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