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 15 static int 16 adfs_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 adfs_error(sb, "not enough memory for" 65 " dir object %X (%d blocks)", id, size); 66 goto out; 67 } 68 dir->bh_fplus = bh_fplus; 69 /* copy over the pointer to the block that we've already read */ 70 dir->bh_fplus[0] = dir->bh[0]; 71 } 72 73 for (blk = 1; blk < size; blk++) { 74 block = __adfs_block_map(sb, id, blk); 75 if (!block) { 76 adfs_error(sb, "dir object %X has a hole at offset %d", id, blk); 77 goto out; 78 } 79 80 dir->bh_fplus[blk] = sb_bread(sb, block); 81 if (!dir->bh_fplus[blk]) { 82 adfs_error(sb, "dir object %x failed read for offset %d, mapped block %lX", 83 id, blk, block); 84 goto out; 85 } 86 87 dir->nr_buffers += 1; 88 } 89 90 t = (struct adfs_bigdirtail *) 91 (dir->bh_fplus[size - 1]->b_data + (sb->s_blocksize - 8)); 92 93 if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) || 94 t->bigdirendmasseq != h->startmasseq || 95 t->reserved[0] != 0 || t->reserved[1] != 0) { 96 printk(KERN_WARNING "adfs: dir object %X has " 97 "malformed dir end\n", id); 98 goto out; 99 } 100 101 dir->parent_id = le32_to_cpu(h->bigdirparent); 102 dir->sb = sb; 103 return 0; 104 105 out: 106 if (dir->bh_fplus) { 107 for (i = 0; i < dir->nr_buffers; i++) 108 brelse(dir->bh_fplus[i]); 109 110 if (&dir->bh[0] != dir->bh_fplus) 111 kfree(dir->bh_fplus); 112 113 dir->bh_fplus = NULL; 114 } 115 116 dir->nr_buffers = 0; 117 dir->sb = NULL; 118 return ret; 119 } 120 121 static int 122 adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos) 123 { 124 struct adfs_bigdirheader *h = 125 (struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data; 126 int ret = -ENOENT; 127 128 if (fpos <= le32_to_cpu(h->bigdirentries)) { 129 dir->pos = fpos; 130 ret = 0; 131 } 132 133 return ret; 134 } 135 136 static void 137 dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len) 138 { 139 struct super_block *sb = dir->sb; 140 unsigned int buffer, partial, remainder; 141 142 buffer = offset >> sb->s_blocksize_bits; 143 offset &= sb->s_blocksize - 1; 144 145 partial = sb->s_blocksize - offset; 146 147 if (partial >= len) 148 memcpy(to, dir->bh_fplus[buffer]->b_data + offset, len); 149 else { 150 char *c = (char *)to; 151 152 remainder = len - partial; 153 154 memcpy(c, 155 dir->bh_fplus[buffer]->b_data + offset, 156 partial); 157 158 memcpy(c + partial, 159 dir->bh_fplus[buffer + 1]->b_data, 160 remainder); 161 } 162 } 163 164 static int 165 adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj) 166 { 167 struct adfs_bigdirheader *h = 168 (struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data; 169 struct adfs_bigdirentry bde; 170 unsigned int offset; 171 int i, ret = -ENOENT; 172 173 if (dir->pos >= le32_to_cpu(h->bigdirentries)) 174 goto out; 175 176 offset = offsetof(struct adfs_bigdirheader, bigdirname); 177 offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3); 178 offset += dir->pos * sizeof(struct adfs_bigdirentry); 179 180 dir_memcpy(dir, offset, &bde, sizeof(struct adfs_bigdirentry)); 181 182 obj->loadaddr = le32_to_cpu(bde.bigdirload); 183 obj->execaddr = le32_to_cpu(bde.bigdirexec); 184 obj->size = le32_to_cpu(bde.bigdirlen); 185 obj->file_id = le32_to_cpu(bde.bigdirindaddr); 186 obj->attr = le32_to_cpu(bde.bigdirattr); 187 obj->name_len = le32_to_cpu(bde.bigdirobnamelen); 188 189 offset = offsetof(struct adfs_bigdirheader, bigdirname); 190 offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3); 191 offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry); 192 offset += le32_to_cpu(bde.bigdirobnameptr); 193 194 dir_memcpy(dir, offset, obj->name, obj->name_len); 195 for (i = 0; i < obj->name_len; i++) 196 if (obj->name[i] == '/') 197 obj->name[i] = '.'; 198 199 obj->filetype = -1; 200 201 /* 202 * object is a file and is filetyped and timestamped? 203 * RISC OS 12-bit filetype is stored in load_address[19:8] 204 */ 205 if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) && 206 (0xfff00000 == (0xfff00000 & obj->loadaddr))) { 207 obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8); 208 209 /* optionally append the ,xyz hex filetype suffix */ 210 if (ADFS_SB(dir->sb)->s_ftsuffix) 211 obj->name_len += 212 append_filetype_suffix( 213 &obj->name[obj->name_len], 214 obj->filetype); 215 } 216 217 dir->pos += 1; 218 ret = 0; 219 out: 220 return ret; 221 } 222 223 static int 224 adfs_fplus_sync(struct adfs_dir *dir) 225 { 226 int err = 0; 227 int i; 228 229 for (i = dir->nr_buffers - 1; i >= 0; i--) { 230 struct buffer_head *bh = dir->bh_fplus[i]; 231 sync_dirty_buffer(bh); 232 if (buffer_req(bh) && !buffer_uptodate(bh)) 233 err = -EIO; 234 } 235 236 return err; 237 } 238 239 static void 240 adfs_fplus_free(struct adfs_dir *dir) 241 { 242 int i; 243 244 if (dir->bh_fplus) { 245 for (i = 0; i < dir->nr_buffers; i++) 246 brelse(dir->bh_fplus[i]); 247 248 if (&dir->bh[0] != dir->bh_fplus) 249 kfree(dir->bh_fplus); 250 251 dir->bh_fplus = NULL; 252 } 253 254 dir->nr_buffers = 0; 255 dir->sb = NULL; 256 } 257 258 struct adfs_dir_ops adfs_fplus_dir_ops = { 259 .read = adfs_fplus_read, 260 .setpos = adfs_fplus_setpos, 261 .getnext = adfs_fplus_getnext, 262 .sync = adfs_fplus_sync, 263 .free = adfs_fplus_free 264 }; 265