1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * linux/fs/adfs/dir_f.c 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Copyright (C) 1997-1999 Russell King 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * E and F format directory handling 81da177e4SLinus Torvalds */ 91da177e4SLinus Torvalds #include "adfs.h" 101da177e4SLinus Torvalds #include "dir_f.h" 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds /* 131da177e4SLinus Torvalds * Read an (unaligned) value of length 1..4 bytes 141da177e4SLinus Torvalds */ 151da177e4SLinus Torvalds static inline unsigned int adfs_readval(unsigned char *p, int len) 161da177e4SLinus Torvalds { 171da177e4SLinus Torvalds unsigned int val = 0; 181da177e4SLinus Torvalds 191da177e4SLinus Torvalds switch (len) { 201da177e4SLinus Torvalds case 4: val |= p[3] << 24; 2174f79099SGustavo A. R. Silva /* fall through */ 221da177e4SLinus Torvalds case 3: val |= p[2] << 16; 2374f79099SGustavo A. R. Silva /* fall through */ 241da177e4SLinus Torvalds case 2: val |= p[1] << 8; 2574f79099SGustavo A. R. Silva /* fall through */ 261da177e4SLinus Torvalds default: val |= p[0]; 271da177e4SLinus Torvalds } 281da177e4SLinus Torvalds return val; 291da177e4SLinus Torvalds } 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds static inline void adfs_writeval(unsigned char *p, int len, unsigned int val) 321da177e4SLinus Torvalds { 331da177e4SLinus Torvalds switch (len) { 341da177e4SLinus Torvalds case 4: p[3] = val >> 24; 3574f79099SGustavo A. R. Silva /* fall through */ 361da177e4SLinus Torvalds case 3: p[2] = val >> 16; 3774f79099SGustavo A. R. Silva /* fall through */ 381da177e4SLinus Torvalds case 2: p[1] = val >> 8; 3974f79099SGustavo A. R. Silva /* fall through */ 401da177e4SLinus Torvalds default: p[0] = val; 411da177e4SLinus Torvalds } 421da177e4SLinus Torvalds } 431da177e4SLinus Torvalds 441da177e4SLinus Torvalds #define ror13(v) ((v >> 13) | (v << 19)) 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds #define dir_u8(idx) \ 471da177e4SLinus Torvalds ({ int _buf = idx >> blocksize_bits; \ 481da177e4SLinus Torvalds int _off = idx - (_buf << blocksize_bits);\ 491da177e4SLinus Torvalds *(u8 *)(bh[_buf]->b_data + _off); \ 501da177e4SLinus Torvalds }) 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds #define dir_u32(idx) \ 531da177e4SLinus Torvalds ({ int _buf = idx >> blocksize_bits; \ 541da177e4SLinus Torvalds int _off = idx - (_buf << blocksize_bits);\ 551da177e4SLinus Torvalds *(__le32 *)(bh[_buf]->b_data + _off); \ 561da177e4SLinus Torvalds }) 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds #define bufoff(_bh,_idx) \ 591da177e4SLinus Torvalds ({ int _buf = _idx >> blocksize_bits; \ 601da177e4SLinus Torvalds int _off = _idx - (_buf << blocksize_bits);\ 61016936b3SRussell King (void *)(_bh[_buf]->b_data + _off); \ 621da177e4SLinus Torvalds }) 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds /* 651da177e4SLinus Torvalds * There are some algorithms that are nice in 661da177e4SLinus Torvalds * assembler, but a bitch in C... This is one 671da177e4SLinus Torvalds * of them. 681da177e4SLinus Torvalds */ 691da177e4SLinus Torvalds static u8 701da177e4SLinus Torvalds adfs_dir_checkbyte(const struct adfs_dir *dir) 711da177e4SLinus Torvalds { 721da177e4SLinus Torvalds struct buffer_head * const *bh = dir->bh; 731da177e4SLinus Torvalds const int blocksize_bits = dir->sb->s_blocksize_bits; 741da177e4SLinus Torvalds union { __le32 *ptr32; u8 *ptr8; } ptr, end; 751da177e4SLinus Torvalds u32 dircheck = 0; 761da177e4SLinus Torvalds int last = 5 - 26; 771da177e4SLinus Torvalds int i = 0; 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds /* 801da177e4SLinus Torvalds * Accumulate each word up to the last whole 811da177e4SLinus Torvalds * word of the last directory entry. This 821da177e4SLinus Torvalds * can spread across several buffer heads. 831da177e4SLinus Torvalds */ 841da177e4SLinus Torvalds do { 851da177e4SLinus Torvalds last += 26; 861da177e4SLinus Torvalds do { 871da177e4SLinus Torvalds dircheck = le32_to_cpu(dir_u32(i)) ^ ror13(dircheck); 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds i += sizeof(u32); 901da177e4SLinus Torvalds } while (i < (last & ~3)); 911da177e4SLinus Torvalds } while (dir_u8(last) != 0); 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds /* 941da177e4SLinus Torvalds * Accumulate the last few bytes. These 951da177e4SLinus Torvalds * bytes will be within the same bh. 961da177e4SLinus Torvalds */ 971da177e4SLinus Torvalds if (i != last) { 981da177e4SLinus Torvalds ptr.ptr8 = bufoff(bh, i); 991da177e4SLinus Torvalds end.ptr8 = ptr.ptr8 + last - i; 1001da177e4SLinus Torvalds 101e5949050SHarvey Harrison do { 1021da177e4SLinus Torvalds dircheck = *ptr.ptr8++ ^ ror13(dircheck); 103e5949050SHarvey Harrison } while (ptr.ptr8 < end.ptr8); 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds /* 1071da177e4SLinus Torvalds * The directory tail is in the final bh 1081da177e4SLinus Torvalds * Note that contary to the RISC OS PRMs, 1091da177e4SLinus Torvalds * the first few bytes are NOT included 1101da177e4SLinus Torvalds * in the check. All bytes are in the 1111da177e4SLinus Torvalds * same bh. 1121da177e4SLinus Torvalds */ 1131da177e4SLinus Torvalds ptr.ptr8 = bufoff(bh, 2008); 1141da177e4SLinus Torvalds end.ptr8 = ptr.ptr8 + 36; 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds do { 1171da177e4SLinus Torvalds __le32 v = *ptr.ptr32++; 1181da177e4SLinus Torvalds dircheck = le32_to_cpu(v) ^ ror13(dircheck); 1191da177e4SLinus Torvalds } while (ptr.ptr32 < end.ptr32); 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff; 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 124ffc8df34SRussell King static int adfs_f_validate(struct adfs_dir *dir) 125ffc8df34SRussell King { 126ffc8df34SRussell King struct adfs_dirheader *head = dir->dirhead; 127ffc8df34SRussell King struct adfs_newdirtail *tail = dir->newtail; 128ffc8df34SRussell King 129ffc8df34SRussell King if (head->startmasseq != tail->endmasseq || 1307a0e4048SRussell King tail->dirlastmask || tail->reserved[0] || tail->reserved[1] || 131ffc8df34SRussell King (memcmp(&head->startname, "Nick", 4) && 132ffc8df34SRussell King memcmp(&head->startname, "Hugo", 4)) || 133ffc8df34SRussell King memcmp(&head->startname, &tail->endname, 4) || 134ffc8df34SRussell King adfs_dir_checkbyte(dir) != tail->dircheckbyte) 135ffc8df34SRussell King return -EIO; 136ffc8df34SRussell King 137ffc8df34SRussell King return 0; 138ffc8df34SRussell King } 139ffc8df34SRussell King 1405ed70bb4SRussell King /* Read and check that a directory is valid */ 1419318731bSRussell King static int adfs_f_read(struct super_block *sb, u32 indaddr, unsigned int size, 1429318731bSRussell King struct adfs_dir *dir) 1431da177e4SLinus Torvalds { 1441da177e4SLinus Torvalds const unsigned int blocksize_bits = sb->s_blocksize_bits; 145419a6e5eSRussell King int ret; 1461da177e4SLinus Torvalds 1479318731bSRussell King if (size && size != ADFS_NEWDIR_SIZE) 1489318731bSRussell King return -EIO; 1491da177e4SLinus Torvalds 1509318731bSRussell King ret = adfs_dir_read_buffers(sb, indaddr, ADFS_NEWDIR_SIZE, dir); 151419a6e5eSRussell King if (ret) 152419a6e5eSRussell King return ret; 1531da177e4SLinus Torvalds 154016936b3SRussell King dir->dirhead = bufoff(dir->bh, 0); 155016936b3SRussell King dir->newtail = bufoff(dir->bh, 2007); 1561da177e4SLinus Torvalds 157ffc8df34SRussell King if (adfs_f_validate(dir)) 1581da177e4SLinus Torvalds goto bad_dir; 1591da177e4SLinus Torvalds 1609318731bSRussell King dir->parent_id = adfs_readval(dir->newtail->dirparent, 3); 1619318731bSRussell King 1621da177e4SLinus Torvalds return 0; 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds bad_dir: 1655ed70bb4SRussell King adfs_error(sb, "dir %06x is corrupted", indaddr); 1661dd9f5baSRussell King adfs_dir_relse(dir); 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds return -EIO; 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds /* 1721da177e4SLinus Torvalds * convert a disk-based directory entry to a Linux ADFS directory entry 1731da177e4SLinus Torvalds */ 1741da177e4SLinus Torvalds static inline void 175da23ef05SStuart Swales adfs_dir2obj(struct adfs_dir *dir, struct object_info *obj, 176da23ef05SStuart Swales struct adfs_direntry *de) 1771da177e4SLinus Torvalds { 178adb514a4SRussell King unsigned int name_len; 179adb514a4SRussell King 180adb514a4SRussell King for (name_len = 0; name_len < ADFS_F_NAME_LEN; name_len++) { 181adb514a4SRussell King if (de->dirobname[name_len] < ' ') 182adb514a4SRussell King break; 183adb514a4SRussell King 184adb514a4SRussell King obj->name[name_len] = de->dirobname[name_len]; 185adb514a4SRussell King } 186adb514a4SRussell King 187adb514a4SRussell King obj->name_len = name_len; 1885ed70bb4SRussell King obj->indaddr = adfs_readval(de->dirinddiscadd, 3); 1891da177e4SLinus Torvalds obj->loadaddr = adfs_readval(de->dirload, 4); 1901da177e4SLinus Torvalds obj->execaddr = adfs_readval(de->direxec, 4); 1911da177e4SLinus Torvalds obj->size = adfs_readval(de->dirlen, 4); 1921da177e4SLinus Torvalds obj->attr = de->newdiratts; 193da23ef05SStuart Swales 194411c49bcSRussell King adfs_object_fixup(dir, obj); 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds /* 1981da177e4SLinus Torvalds * convert a Linux ADFS directory entry to a disk-based directory entry 1991da177e4SLinus Torvalds */ 2001da177e4SLinus Torvalds static inline void 2011da177e4SLinus Torvalds adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj) 2021da177e4SLinus Torvalds { 2035ed70bb4SRussell King adfs_writeval(de->dirinddiscadd, 3, obj->indaddr); 2041da177e4SLinus Torvalds adfs_writeval(de->dirload, 4, obj->loadaddr); 2051da177e4SLinus Torvalds adfs_writeval(de->direxec, 4, obj->execaddr); 2061da177e4SLinus Torvalds adfs_writeval(de->dirlen, 4, obj->size); 2071da177e4SLinus Torvalds de->newdiratts = obj->attr; 2081da177e4SLinus Torvalds } 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvalds /* 2111da177e4SLinus Torvalds * get a directory entry. Note that the caller is responsible 2121da177e4SLinus Torvalds * for holding the relevant locks. 2131da177e4SLinus Torvalds */ 2141da177e4SLinus Torvalds static int 2151da177e4SLinus Torvalds __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj) 2161da177e4SLinus Torvalds { 2171da177e4SLinus Torvalds struct adfs_direntry de; 218a317120bSRussell King int ret; 2191da177e4SLinus Torvalds 220a317120bSRussell King ret = adfs_dir_copyfrom(&de, dir, pos, 26); 221a317120bSRussell King if (ret) 222a317120bSRussell King return ret; 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds if (!de.dirobname[0]) 2251da177e4SLinus Torvalds return -ENOENT; 2261da177e4SLinus Torvalds 227da23ef05SStuart Swales adfs_dir2obj(dir, obj, &de); 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds return 0; 2301da177e4SLinus Torvalds } 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds static int 2331da177e4SLinus Torvalds adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos) 2341da177e4SLinus Torvalds { 2351da177e4SLinus Torvalds if (fpos >= ADFS_NUM_DIR_ENTRIES) 2361da177e4SLinus Torvalds return -ENOENT; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds dir->pos = 5 + fpos * 26; 2391da177e4SLinus Torvalds return 0; 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds static int 2431da177e4SLinus Torvalds adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj) 2441da177e4SLinus Torvalds { 2451da177e4SLinus Torvalds unsigned int ret; 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds ret = __adfs_dir_get(dir, dir->pos, obj); 2481da177e4SLinus Torvalds if (ret == 0) 2491da177e4SLinus Torvalds dir->pos += 26; 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds return ret; 2521da177e4SLinus Torvalds } 2531da177e4SLinus Torvalds 2544287e4deSRussell King static int adfs_f_iterate(struct adfs_dir *dir, struct dir_context *ctx) 2554287e4deSRussell King { 2564287e4deSRussell King struct object_info obj; 2574287e4deSRussell King int pos = 5 + (ctx->pos - 2) * 26; 2584287e4deSRussell King 2594287e4deSRussell King while (ctx->pos < 2 + ADFS_NUM_DIR_ENTRIES) { 2604287e4deSRussell King if (__adfs_dir_get(dir, pos, &obj)) 2614287e4deSRussell King break; 2624287e4deSRussell King if (!dir_emit(ctx, obj.name, obj.name_len, 2634287e4deSRussell King obj.indaddr, DT_UNKNOWN)) 2644287e4deSRussell King break; 2654287e4deSRussell King pos += 26; 2664287e4deSRussell King ctx->pos++; 2674287e4deSRussell King } 2684287e4deSRussell King return 0; 2694287e4deSRussell King } 2704287e4deSRussell King 271cc625ccdSRussell King static int adfs_f_update(struct adfs_dir *dir, struct object_info *obj) 2721da177e4SLinus Torvalds { 273cc625ccdSRussell King struct adfs_direntry de; 274cc625ccdSRussell King int offset, ret; 2751da177e4SLinus Torvalds 276cc625ccdSRussell King offset = 5 - (int)sizeof(de); 277cc625ccdSRussell King 278cc625ccdSRussell King do { 279cc625ccdSRussell King offset += sizeof(de); 280cc625ccdSRussell King ret = adfs_dir_copyfrom(&de, dir, offset, sizeof(de)); 281cc625ccdSRussell King if (ret) { 282cc625ccdSRussell King adfs_error(dir->sb, "error reading directory entry"); 283cc625ccdSRussell King return -ENOENT; 2841da177e4SLinus Torvalds } 285cc625ccdSRussell King if (!de.dirobname[0]) { 286cc625ccdSRussell King adfs_error(dir->sb, "unable to locate entry to update"); 287cc625ccdSRussell King return -ENOENT; 288cc625ccdSRussell King } 289cc625ccdSRussell King } while (adfs_readval(de.dirinddiscadd, 3) != obj->indaddr); 2901da177e4SLinus Torvalds 291cc625ccdSRussell King /* Update the directory entry with the new object state */ 292cc625ccdSRussell King adfs_obj2dir(&de, obj); 293cc625ccdSRussell King 294cc625ccdSRussell King /* Write the directory entry back to the directory */ 295cc625ccdSRussell King ret = adfs_dir_copyto(dir, pos, &de, 26); 296cc625ccdSRussell King if (ret) 297cc625ccdSRussell King return ret; 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds /* 3001da177e4SLinus Torvalds * Increment directory sequence number 3011da177e4SLinus Torvalds */ 302016936b3SRussell King dir->dirhead->startmasseq += 1; 303016936b3SRussell King dir->newtail->endmasseq += 1; 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds ret = adfs_dir_checkbyte(dir); 3061da177e4SLinus Torvalds /* 3071da177e4SLinus Torvalds * Update directory check byte 3081da177e4SLinus Torvalds */ 309016936b3SRussell King dir->newtail->dircheckbyte = ret; 3101da177e4SLinus Torvalds 311ffc8df34SRussell King ret = adfs_f_validate(dir); 312ffc8df34SRussell King if (ret) 3131da177e4SLinus Torvalds adfs_error(dir->sb, "whoops! I broke a directory!"); 314ffc8df34SRussell King 315ffc8df34SRussell King return ret; 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 3180125f504SJulia Lawall const struct adfs_dir_ops adfs_f_dir_ops = { 3191da177e4SLinus Torvalds .read = adfs_f_read, 3204287e4deSRussell King .iterate = adfs_f_iterate, 3211da177e4SLinus Torvalds .setpos = adfs_f_setpos, 3221da177e4SLinus Torvalds .getnext = adfs_f_getnext, 3231da177e4SLinus Torvalds .update = adfs_f_update, 3241da177e4SLinus Torvalds }; 325