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