1/* 2 * linux/fs/adfs/dir_fplus.c 3 * 4 * Copyright (C) 1997-1999 Russell King 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10#include <linux/buffer_head.h> 11#include <linux/slab.h> 12#include "adfs.h" 13#include "dir_fplus.h" 14 15static int 16adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir) 17{ 18 struct adfs_bigdirheader *h; 19 struct adfs_bigdirtail *t; 20 unsigned long block; 21 unsigned int blk, size; 22 int i, ret = -EIO; 23 24 dir->nr_buffers = 0; 25 26 /* start off using fixed bh set - only alloc for big dirs */ 27 dir->bh_fplus = &dir->bh[0]; 28 29 block = __adfs_block_map(sb, id, 0); 30 if (!block) { 31 adfs_error(sb, "dir object %X has a hole at offset 0", id); 32 goto out; 33 } 34 35 dir->bh_fplus[0] = sb_bread(sb, block); 36 if (!dir->bh_fplus[0]) 37 goto out; 38 dir->nr_buffers += 1; 39 40 h = (struct adfs_bigdirheader *)dir->bh_fplus[0]->b_data; 41 size = le32_to_cpu(h->bigdirsize); 42 if (size != sz) { 43 printk(KERN_WARNING "adfs: adfs_fplus_read:" 44 " directory header size %X\n" 45 " does not match directory size %X\n", 46 size, sz); 47 } 48 49 if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 || 50 h->bigdirversion[2] != 0 || size & 2047 || 51 h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) { 52 printk(KERN_WARNING "adfs: dir object %X has" 53 " malformed dir header\n", id); 54 goto out; 55 } 56 57 size >>= sb->s_blocksize_bits; 58 if (size > ARRAY_SIZE(dir->bh)) { 59 /* this directory is too big for fixed bh set, must allocate */ 60 struct buffer_head **bh_fplus = 61 kcalloc(size, sizeof(struct buffer_head *), 62 GFP_KERNEL); 63 if (!bh_fplus) { 64 ret = -ENOMEM; 65 adfs_error(sb, "not enough memory for" 66 " dir object %X (%d blocks)", id, size); 67 goto out; 68 } 69 dir->bh_fplus = bh_fplus; 70 /* copy over the pointer to the block that we've already read */ 71 dir->bh_fplus[0] = dir->bh[0]; 72 } 73 74 for (blk = 1; blk < size; blk++) { 75 block = __adfs_block_map(sb, id, blk); 76 if (!block) { 77 adfs_error(sb, "dir object %X has a hole at offset %d", id, blk); 78 goto out; 79 } 80 81 dir->bh_fplus[blk] = sb_bread(sb, block); 82 if (!dir->bh_fplus[blk]) { 83 adfs_error(sb, "dir object %x failed read for offset %d, mapped block %lX", 84 id, blk, block); 85 goto out; 86 } 87 88 dir->nr_buffers += 1; 89 } 90 91 t = (struct adfs_bigdirtail *) 92 (dir->bh_fplus[size - 1]->b_data + (sb->s_blocksize - 8)); 93 94 if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) || 95 t->bigdirendmasseq != h->startmasseq || 96 t->reserved[0] != 0 || t->reserved[1] != 0) { 97 printk(KERN_WARNING "adfs: dir object %X has " 98 "malformed dir end\n", id); 99 goto out; 100 } 101 102 dir->parent_id = le32_to_cpu(h->bigdirparent); 103 dir->sb = sb; 104 return 0; 105 106out: 107 if (dir->bh_fplus) { 108 for (i = 0; i < dir->nr_buffers; i++) 109 brelse(dir->bh_fplus[i]); 110 111 if (&dir->bh[0] != dir->bh_fplus) 112 kfree(dir->bh_fplus); 113 114 dir->bh_fplus = NULL; 115 } 116 117 dir->nr_buffers = 0; 118 dir->sb = NULL; 119 return ret; 120} 121 122static int 123adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos) 124{ 125 struct adfs_bigdirheader *h = 126 (struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data; 127 int ret = -ENOENT; 128 129 if (fpos <= le32_to_cpu(h->bigdirentries)) { 130 dir->pos = fpos; 131 ret = 0; 132 } 133 134 return ret; 135} 136 137static void 138dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len) 139{ 140 struct super_block *sb = dir->sb; 141 unsigned int buffer, partial, remainder; 142 143 buffer = offset >> sb->s_blocksize_bits; 144 offset &= sb->s_blocksize - 1; 145 146 partial = sb->s_blocksize - offset; 147 148 if (partial >= len) 149 memcpy(to, dir->bh_fplus[buffer]->b_data + offset, len); 150 else { 151 char *c = (char *)to; 152 153 remainder = len - partial; 154 155 memcpy(c, 156 dir->bh_fplus[buffer]->b_data + offset, 157 partial); 158 159 memcpy(c + partial, 160 dir->bh_fplus[buffer + 1]->b_data, 161 remainder); 162 } 163} 164 165static int 166adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj) 167{ 168 struct adfs_bigdirheader *h = 169 (struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data; 170 struct adfs_bigdirentry bde; 171 unsigned int offset; 172 int i, ret = -ENOENT; 173 174 if (dir->pos >= le32_to_cpu(h->bigdirentries)) 175 goto out; 176 177 offset = offsetof(struct adfs_bigdirheader, bigdirname); 178 offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3); 179 offset += dir->pos * sizeof(struct adfs_bigdirentry); 180 181 dir_memcpy(dir, offset, &bde, sizeof(struct adfs_bigdirentry)); 182 183 obj->loadaddr = le32_to_cpu(bde.bigdirload); 184 obj->execaddr = le32_to_cpu(bde.bigdirexec); 185 obj->size = le32_to_cpu(bde.bigdirlen); 186 obj->file_id = le32_to_cpu(bde.bigdirindaddr); 187 obj->attr = le32_to_cpu(bde.bigdirattr); 188 obj->name_len = le32_to_cpu(bde.bigdirobnamelen); 189 190 offset = offsetof(struct adfs_bigdirheader, bigdirname); 191 offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3); 192 offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry); 193 offset += le32_to_cpu(bde.bigdirobnameptr); 194 195 dir_memcpy(dir, offset, obj->name, obj->name_len); 196 for (i = 0; i < obj->name_len; i++) 197 if (obj->name[i] == '/') 198 obj->name[i] = '.'; 199 200 obj->filetype = -1; 201 202 /* 203 * object is a file and is filetyped and timestamped? 204 * RISC OS 12-bit filetype is stored in load_address[19:8] 205 */ 206 if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) && 207 (0xfff00000 == (0xfff00000 & obj->loadaddr))) { 208 obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8); 209 210 /* optionally append the ,xyz hex filetype suffix */ 211 if (ADFS_SB(dir->sb)->s_ftsuffix) 212 obj->name_len += 213 append_filetype_suffix( 214 &obj->name[obj->name_len], 215 obj->filetype); 216 } 217 218 dir->pos += 1; 219 ret = 0; 220out: 221 return ret; 222} 223 224static int 225adfs_fplus_sync(struct adfs_dir *dir) 226{ 227 int err = 0; 228 int i; 229 230 for (i = dir->nr_buffers - 1; i >= 0; i--) { 231 struct buffer_head *bh = dir->bh_fplus[i]; 232 sync_dirty_buffer(bh); 233 if (buffer_req(bh) && !buffer_uptodate(bh)) 234 err = -EIO; 235 } 236 237 return err; 238} 239 240static void 241adfs_fplus_free(struct adfs_dir *dir) 242{ 243 int i; 244 245 if (dir->bh_fplus) { 246 for (i = 0; i < dir->nr_buffers; i++) 247 brelse(dir->bh_fplus[i]); 248 249 if (&dir->bh[0] != dir->bh_fplus) 250 kfree(dir->bh_fplus); 251 252 dir->bh_fplus = NULL; 253 } 254 255 dir->nr_buffers = 0; 256 dir->sb = NULL; 257} 258 259struct adfs_dir_ops adfs_fplus_dir_ops = { 260 .read = adfs_fplus_read, 261 .setpos = adfs_fplus_setpos, 262 .getnext = adfs_fplus_getnext, 263 .sync = adfs_fplus_sync, 264 .free = adfs_fplus_free 265}; 266