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