1 /* 2 * linux/fs/adfs/dir_f.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 * E and F format directory handling 11 */ 12 #include <linux/buffer_head.h> 13 #include "adfs.h" 14 #include "dir_f.h" 15 16 static void adfs_f_free(struct adfs_dir *dir); 17 18 /* 19 * Read an (unaligned) value of length 1..4 bytes 20 */ 21 static inline unsigned int adfs_readval(unsigned char *p, int len) 22 { 23 unsigned int val = 0; 24 25 switch (len) { 26 case 4: val |= p[3] << 24; 27 case 3: val |= p[2] << 16; 28 case 2: val |= p[1] << 8; 29 default: val |= p[0]; 30 } 31 return val; 32 } 33 34 static inline void adfs_writeval(unsigned char *p, int len, unsigned int val) 35 { 36 switch (len) { 37 case 4: p[3] = val >> 24; 38 case 3: p[2] = val >> 16; 39 case 2: p[1] = val >> 8; 40 default: p[0] = val; 41 } 42 } 43 44 static inline int adfs_readname(char *buf, char *ptr, int maxlen) 45 { 46 char *old_buf = buf; 47 48 while ((unsigned char)*ptr >= ' ' && maxlen--) { 49 if (*ptr == '/') 50 *buf++ = '.'; 51 else 52 *buf++ = *ptr; 53 ptr++; 54 } 55 *buf = '\0'; 56 57 return buf - old_buf; 58 } 59 60 #define ror13(v) ((v >> 13) | (v << 19)) 61 62 #define dir_u8(idx) \ 63 ({ int _buf = idx >> blocksize_bits; \ 64 int _off = idx - (_buf << blocksize_bits);\ 65 *(u8 *)(bh[_buf]->b_data + _off); \ 66 }) 67 68 #define dir_u32(idx) \ 69 ({ int _buf = idx >> blocksize_bits; \ 70 int _off = idx - (_buf << blocksize_bits);\ 71 *(__le32 *)(bh[_buf]->b_data + _off); \ 72 }) 73 74 #define bufoff(_bh,_idx) \ 75 ({ int _buf = _idx >> blocksize_bits; \ 76 int _off = _idx - (_buf << blocksize_bits);\ 77 (u8 *)(_bh[_buf]->b_data + _off); \ 78 }) 79 80 /* 81 * There are some algorithms that are nice in 82 * assembler, but a bitch in C... This is one 83 * of them. 84 */ 85 static u8 86 adfs_dir_checkbyte(const struct adfs_dir *dir) 87 { 88 struct buffer_head * const *bh = dir->bh; 89 const int blocksize_bits = dir->sb->s_blocksize_bits; 90 union { __le32 *ptr32; u8 *ptr8; } ptr, end; 91 u32 dircheck = 0; 92 int last = 5 - 26; 93 int i = 0; 94 95 /* 96 * Accumulate each word up to the last whole 97 * word of the last directory entry. This 98 * can spread across several buffer heads. 99 */ 100 do { 101 last += 26; 102 do { 103 dircheck = le32_to_cpu(dir_u32(i)) ^ ror13(dircheck); 104 105 i += sizeof(u32); 106 } while (i < (last & ~3)); 107 } while (dir_u8(last) != 0); 108 109 /* 110 * Accumulate the last few bytes. These 111 * bytes will be within the same bh. 112 */ 113 if (i != last) { 114 ptr.ptr8 = bufoff(bh, i); 115 end.ptr8 = ptr.ptr8 + last - i; 116 117 do { 118 dircheck = *ptr.ptr8++ ^ ror13(dircheck); 119 } while (ptr.ptr8 < end.ptr8); 120 } 121 122 /* 123 * The directory tail is in the final bh 124 * Note that contary to the RISC OS PRMs, 125 * the first few bytes are NOT included 126 * in the check. All bytes are in the 127 * same bh. 128 */ 129 ptr.ptr8 = bufoff(bh, 2008); 130 end.ptr8 = ptr.ptr8 + 36; 131 132 do { 133 __le32 v = *ptr.ptr32++; 134 dircheck = le32_to_cpu(v) ^ ror13(dircheck); 135 } while (ptr.ptr32 < end.ptr32); 136 137 return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff; 138 } 139 140 /* 141 * Read and check that a directory is valid 142 */ 143 static int 144 adfs_dir_read(struct super_block *sb, unsigned long object_id, 145 unsigned int size, struct adfs_dir *dir) 146 { 147 const unsigned int blocksize_bits = sb->s_blocksize_bits; 148 int blk = 0; 149 150 /* 151 * Directories which are not a multiple of 2048 bytes 152 * are considered bad v2 [3.6] 153 */ 154 if (size & 2047) 155 goto bad_dir; 156 157 size >>= blocksize_bits; 158 159 dir->nr_buffers = 0; 160 dir->sb = sb; 161 162 for (blk = 0; blk < size; blk++) { 163 int phys; 164 165 phys = __adfs_block_map(sb, object_id, blk); 166 if (!phys) { 167 adfs_error(sb, "dir object %lX has a hole at offset %d", 168 object_id, blk); 169 goto release_buffers; 170 } 171 172 dir->bh[blk] = sb_bread(sb, phys); 173 if (!dir->bh[blk]) 174 goto release_buffers; 175 } 176 177 memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead)); 178 memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail)); 179 180 if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq || 181 memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4)) 182 goto bad_dir; 183 184 if (memcmp(&dir->dirhead.startname, "Nick", 4) && 185 memcmp(&dir->dirhead.startname, "Hugo", 4)) 186 goto bad_dir; 187 188 if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte) 189 goto bad_dir; 190 191 dir->nr_buffers = blk; 192 193 return 0; 194 195 bad_dir: 196 adfs_error(sb, "corrupted directory fragment %lX", 197 object_id); 198 release_buffers: 199 for (blk -= 1; blk >= 0; blk -= 1) 200 brelse(dir->bh[blk]); 201 202 dir->sb = NULL; 203 204 return -EIO; 205 } 206 207 /* 208 * convert a disk-based directory entry to a Linux ADFS directory entry 209 */ 210 static inline void 211 adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de) 212 { 213 obj->name_len = adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN); 214 obj->file_id = adfs_readval(de->dirinddiscadd, 3); 215 obj->loadaddr = adfs_readval(de->dirload, 4); 216 obj->execaddr = adfs_readval(de->direxec, 4); 217 obj->size = adfs_readval(de->dirlen, 4); 218 obj->attr = de->newdiratts; 219 } 220 221 /* 222 * convert a Linux ADFS directory entry to a disk-based directory entry 223 */ 224 static inline void 225 adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj) 226 { 227 adfs_writeval(de->dirinddiscadd, 3, obj->file_id); 228 adfs_writeval(de->dirload, 4, obj->loadaddr); 229 adfs_writeval(de->direxec, 4, obj->execaddr); 230 adfs_writeval(de->dirlen, 4, obj->size); 231 de->newdiratts = obj->attr; 232 } 233 234 /* 235 * get a directory entry. Note that the caller is responsible 236 * for holding the relevant locks. 237 */ 238 static int 239 __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj) 240 { 241 struct super_block *sb = dir->sb; 242 struct adfs_direntry de; 243 int thissize, buffer, offset; 244 245 buffer = pos >> sb->s_blocksize_bits; 246 247 if (buffer > dir->nr_buffers) 248 return -EINVAL; 249 250 offset = pos & (sb->s_blocksize - 1); 251 thissize = sb->s_blocksize - offset; 252 if (thissize > 26) 253 thissize = 26; 254 255 memcpy(&de, dir->bh[buffer]->b_data + offset, thissize); 256 if (thissize != 26) 257 memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data, 258 26 - thissize); 259 260 if (!de.dirobname[0]) 261 return -ENOENT; 262 263 adfs_dir2obj(obj, &de); 264 265 return 0; 266 } 267 268 static int 269 __adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj) 270 { 271 struct super_block *sb = dir->sb; 272 struct adfs_direntry de; 273 int thissize, buffer, offset; 274 275 buffer = pos >> sb->s_blocksize_bits; 276 277 if (buffer > dir->nr_buffers) 278 return -EINVAL; 279 280 offset = pos & (sb->s_blocksize - 1); 281 thissize = sb->s_blocksize - offset; 282 if (thissize > 26) 283 thissize = 26; 284 285 /* 286 * Get the entry in total 287 */ 288 memcpy(&de, dir->bh[buffer]->b_data + offset, thissize); 289 if (thissize != 26) 290 memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data, 291 26 - thissize); 292 293 /* 294 * update it 295 */ 296 adfs_obj2dir(&de, obj); 297 298 /* 299 * Put the new entry back 300 */ 301 memcpy(dir->bh[buffer]->b_data + offset, &de, thissize); 302 if (thissize != 26) 303 memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize, 304 26 - thissize); 305 306 return 0; 307 } 308 309 /* 310 * the caller is responsible for holding the necessary 311 * locks. 312 */ 313 static int 314 adfs_dir_find_entry(struct adfs_dir *dir, unsigned long object_id) 315 { 316 int pos, ret; 317 318 ret = -ENOENT; 319 320 for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) { 321 struct object_info obj; 322 323 if (!__adfs_dir_get(dir, pos, &obj)) 324 break; 325 326 if (obj.file_id == object_id) { 327 ret = pos; 328 break; 329 } 330 } 331 332 return ret; 333 } 334 335 static int 336 adfs_f_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir) 337 { 338 int ret; 339 340 if (sz != ADFS_NEWDIR_SIZE) 341 return -EIO; 342 343 ret = adfs_dir_read(sb, id, sz, dir); 344 if (ret) 345 adfs_error(sb, "unable to read directory"); 346 else 347 dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3); 348 349 return ret; 350 } 351 352 static int 353 adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos) 354 { 355 if (fpos >= ADFS_NUM_DIR_ENTRIES) 356 return -ENOENT; 357 358 dir->pos = 5 + fpos * 26; 359 return 0; 360 } 361 362 static int 363 adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj) 364 { 365 unsigned int ret; 366 367 ret = __adfs_dir_get(dir, dir->pos, obj); 368 if (ret == 0) 369 dir->pos += 26; 370 371 return ret; 372 } 373 374 static int 375 adfs_f_update(struct adfs_dir *dir, struct object_info *obj) 376 { 377 struct super_block *sb = dir->sb; 378 int ret, i; 379 380 ret = adfs_dir_find_entry(dir, obj->file_id); 381 if (ret < 0) { 382 adfs_error(dir->sb, "unable to locate entry to update"); 383 goto out; 384 } 385 386 __adfs_dir_put(dir, ret, obj); 387 388 /* 389 * Increment directory sequence number 390 */ 391 dir->bh[0]->b_data[0] += 1; 392 dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1; 393 394 ret = adfs_dir_checkbyte(dir); 395 /* 396 * Update directory check byte 397 */ 398 dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret; 399 400 #if 1 401 { 402 const unsigned int blocksize_bits = sb->s_blocksize_bits; 403 404 memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead)); 405 memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail)); 406 407 if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq || 408 memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4)) 409 goto bad_dir; 410 411 if (memcmp(&dir->dirhead.startname, "Nick", 4) && 412 memcmp(&dir->dirhead.startname, "Hugo", 4)) 413 goto bad_dir; 414 415 if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte) 416 goto bad_dir; 417 } 418 #endif 419 for (i = dir->nr_buffers - 1; i >= 0; i--) 420 mark_buffer_dirty(dir->bh[i]); 421 422 ret = 0; 423 out: 424 return ret; 425 #if 1 426 bad_dir: 427 adfs_error(dir->sb, "whoops! I broke a directory!"); 428 return -EIO; 429 #endif 430 } 431 432 static int 433 adfs_f_sync(struct adfs_dir *dir) 434 { 435 int err = 0; 436 int i; 437 438 for (i = dir->nr_buffers - 1; i >= 0; i--) { 439 struct buffer_head *bh = dir->bh[i]; 440 sync_dirty_buffer(bh); 441 if (buffer_req(bh) && !buffer_uptodate(bh)) 442 err = -EIO; 443 } 444 445 return err; 446 } 447 448 static void 449 adfs_f_free(struct adfs_dir *dir) 450 { 451 int i; 452 453 for (i = dir->nr_buffers - 1; i >= 0; i--) { 454 brelse(dir->bh[i]); 455 dir->bh[i] = NULL; 456 } 457 458 dir->nr_buffers = 0; 459 dir->sb = NULL; 460 } 461 462 struct adfs_dir_ops adfs_f_dir_ops = { 463 .read = adfs_f_read, 464 .setpos = adfs_f_setpos, 465 .getnext = adfs_f_getnext, 466 .update = adfs_f_update, 467 .sync = adfs_f_sync, 468 .free = adfs_f_free 469 }; 470