1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * linux/fs/adfs/dir.c 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Copyright (C) 1999-2000 Russell King 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Common directory handling for ADFS 81da177e4SLinus Torvalds */ 9*1dd9f5baSRussell King #include <linux/slab.h> 101da177e4SLinus Torvalds #include "adfs.h" 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds /* 131da177e4SLinus Torvalds * For future. This should probably be per-directory. 141da177e4SLinus Torvalds */ 151da177e4SLinus Torvalds static DEFINE_RWLOCK(adfs_dir_lock); 161da177e4SLinus Torvalds 17*1dd9f5baSRussell King void adfs_dir_relse(struct adfs_dir *dir) 18*1dd9f5baSRussell King { 19*1dd9f5baSRussell King unsigned int i; 20*1dd9f5baSRussell King 21*1dd9f5baSRussell King for (i = 0; i < dir->nr_buffers; i++) 22*1dd9f5baSRussell King brelse(dir->bhs[i]); 23*1dd9f5baSRussell King dir->nr_buffers = 0; 24*1dd9f5baSRussell King 25*1dd9f5baSRussell King if (dir->bhs != dir->bh) 26*1dd9f5baSRussell King kfree(dir->bhs); 27*1dd9f5baSRussell King dir->bhs = NULL; 28*1dd9f5baSRussell King dir->sb = NULL; 29*1dd9f5baSRussell King } 30*1dd9f5baSRussell King 3195fbadbbSRussell King static int adfs_dir_read(struct super_block *sb, u32 indaddr, 3295fbadbbSRussell King unsigned int size, struct adfs_dir *dir) 3395fbadbbSRussell King { 3495fbadbbSRussell King dir->sb = sb; 3595fbadbbSRussell King dir->bhs = dir->bh; 3695fbadbbSRussell King dir->nr_buffers = 0; 3795fbadbbSRussell King 3895fbadbbSRussell King return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir); 3995fbadbbSRussell King } 4095fbadbbSRussell King 41411c49bcSRussell King void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj) 42411c49bcSRussell King { 43fc722a04SRussell King unsigned int dots, i; 44adb514a4SRussell King 45adb514a4SRussell King /* 46adb514a4SRussell King * RISC OS allows the use of '/' in directory entry names, so we need 47adb514a4SRussell King * to fix these up. '/' is typically used for FAT compatibility to 48adb514a4SRussell King * represent '.', so do the same conversion here. In any case, '.' 49adb514a4SRussell King * will never be in a RISC OS name since it is used as the pathname 50fc722a04SRussell King * separator. Handle the case where we may generate a '.' or '..' 51fc722a04SRussell King * name, replacing the first character with '^' (the RISC OS "parent 52fc722a04SRussell King * directory" character.) 53adb514a4SRussell King */ 54fc722a04SRussell King for (i = dots = 0; i < obj->name_len; i++) 55fc722a04SRussell King if (obj->name[i] == '/') { 56adb514a4SRussell King obj->name[i] = '.'; 57fc722a04SRussell King dots++; 58fc722a04SRussell King } 59fc722a04SRussell King 60fc722a04SRussell King if (obj->name_len <= 2 && dots == obj->name_len) 61fc722a04SRussell King obj->name[0] = '^'; 62adb514a4SRussell King 63411c49bcSRussell King /* 64b4ed8f75SRussell King * If the object is a file, and the user requested the ,xyz hex 65b4ed8f75SRussell King * filetype suffix to the name, check the filetype and append. 66411c49bcSRussell King */ 67b4ed8f75SRussell King if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) { 68b4ed8f75SRussell King u16 filetype = adfs_filetype(obj->loadaddr); 69411c49bcSRussell King 70b4ed8f75SRussell King if (filetype != ADFS_FILETYPE_NONE) { 715f8de487SRussell King obj->name[obj->name_len++] = ','; 725f8de487SRussell King obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8); 735f8de487SRussell King obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4); 745f8de487SRussell King obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0); 755f8de487SRussell King } 76411c49bcSRussell King } 77411c49bcSRussell King } 78411c49bcSRussell King 791da177e4SLinus Torvalds static int 802638ffbaSAl Viro adfs_readdir(struct file *file, struct dir_context *ctx) 811da177e4SLinus Torvalds { 822638ffbaSAl Viro struct inode *inode = file_inode(file); 831da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 840125f504SJulia Lawall const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 851da177e4SLinus Torvalds struct object_info obj; 861da177e4SLinus Torvalds struct adfs_dir dir; 871da177e4SLinus Torvalds int ret = 0; 881da177e4SLinus Torvalds 892638ffbaSAl Viro if (ctx->pos >> 32) 902638ffbaSAl Viro return 0; 911da177e4SLinus Torvalds 9295fbadbbSRussell King ret = adfs_dir_read(sb, inode->i_ino, inode->i_size, &dir); 931da177e4SLinus Torvalds if (ret) 942638ffbaSAl Viro return ret; 951da177e4SLinus Torvalds 962638ffbaSAl Viro if (ctx->pos == 0) { 972638ffbaSAl Viro if (!dir_emit_dot(file, ctx)) 981da177e4SLinus Torvalds goto free_out; 992638ffbaSAl Viro ctx->pos = 1; 1002638ffbaSAl Viro } 1012638ffbaSAl Viro if (ctx->pos == 1) { 1022638ffbaSAl Viro if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR)) 1031da177e4SLinus Torvalds goto free_out; 1042638ffbaSAl Viro ctx->pos = 2; 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds read_lock(&adfs_dir_lock); 1081da177e4SLinus Torvalds 1092638ffbaSAl Viro ret = ops->setpos(&dir, ctx->pos - 2); 1101da177e4SLinus Torvalds if (ret) 1111da177e4SLinus Torvalds goto unlock_out; 1121da177e4SLinus Torvalds while (ops->getnext(&dir, &obj) == 0) { 1132638ffbaSAl Viro if (!dir_emit(ctx, obj.name, obj.name_len, 1145ed70bb4SRussell King obj.indaddr, DT_UNKNOWN)) 1152638ffbaSAl Viro break; 1162638ffbaSAl Viro ctx->pos++; 1171da177e4SLinus Torvalds } 1181da177e4SLinus Torvalds 1191da177e4SLinus Torvalds unlock_out: 1201da177e4SLinus Torvalds read_unlock(&adfs_dir_lock); 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds free_out: 123*1dd9f5baSRussell King adfs_dir_relse(&dir); 1241da177e4SLinus Torvalds return ret; 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds int 128ffdc9064SAl Viro adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) 1291da177e4SLinus Torvalds { 1301da177e4SLinus Torvalds int ret = -EINVAL; 1311da177e4SLinus Torvalds #ifdef CONFIG_ADFS_FS_RW 1320125f504SJulia Lawall const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 1331da177e4SLinus Torvalds struct adfs_dir dir; 1341da177e4SLinus Torvalds 1355ed70bb4SRussell King printk(KERN_INFO "adfs_dir_update: object %06x in dir %06x\n", 1365ed70bb4SRussell King obj->indaddr, obj->parent_id); 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds if (!ops->update) { 1391da177e4SLinus Torvalds ret = -EINVAL; 1401da177e4SLinus Torvalds goto out; 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds 14395fbadbbSRussell King ret = adfs_dir_read(sb, obj->parent_id, 0, &dir); 1441da177e4SLinus Torvalds if (ret) 1451da177e4SLinus Torvalds goto out; 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds write_lock(&adfs_dir_lock); 1481da177e4SLinus Torvalds ret = ops->update(&dir, obj); 1491da177e4SLinus Torvalds write_unlock(&adfs_dir_lock); 1501da177e4SLinus Torvalds 151ffdc9064SAl Viro if (wait) { 152ffdc9064SAl Viro int err = ops->sync(&dir); 153ffdc9064SAl Viro if (!ret) 154ffdc9064SAl Viro ret = err; 155ffdc9064SAl Viro } 156ffdc9064SAl Viro 157*1dd9f5baSRussell King adfs_dir_relse(&dir); 1581da177e4SLinus Torvalds out: 1591da177e4SLinus Torvalds #endif 1601da177e4SLinus Torvalds return ret; 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds 163525715d0SRussell King static unsigned char adfs_tolower(unsigned char c) 164525715d0SRussell King { 165525715d0SRussell King if (c >= 'A' && c <= 'Z') 166525715d0SRussell King c += 'a' - 'A'; 167525715d0SRussell King return c; 168525715d0SRussell King } 169525715d0SRussell King 1701e504cf8SRussell King static int __adfs_compare(const unsigned char *qstr, u32 qlen, 1711e504cf8SRussell King const char *str, u32 len) 1721da177e4SLinus Torvalds { 1731e504cf8SRussell King u32 i; 1741da177e4SLinus Torvalds 1751e504cf8SRussell King if (qlen != len) 1761e504cf8SRussell King return 1; 1771da177e4SLinus Torvalds 178525715d0SRussell King for (i = 0; i < qlen; i++) 179525715d0SRussell King if (adfs_tolower(qstr[i]) != adfs_tolower(str[i])) 1801da177e4SLinus Torvalds return 1; 181525715d0SRussell King 1821e504cf8SRussell King return 0; 1831e504cf8SRussell King } 1841da177e4SLinus Torvalds 1851e504cf8SRussell King static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr, 1861e504cf8SRussell King struct object_info *obj) 1871da177e4SLinus Torvalds { 1881da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 1890125f504SJulia Lawall const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 1901e504cf8SRussell King const unsigned char *name; 1911da177e4SLinus Torvalds struct adfs_dir dir; 1921e504cf8SRussell King u32 name_len; 1931da177e4SLinus Torvalds int ret; 1941da177e4SLinus Torvalds 19595fbadbbSRussell King ret = adfs_dir_read(sb, inode->i_ino, inode->i_size, &dir); 1961da177e4SLinus Torvalds if (ret) 1971da177e4SLinus Torvalds goto out; 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds if (ADFS_I(inode)->parent_id != dir.parent_id) { 2005ed70bb4SRussell King adfs_error(sb, 2015ed70bb4SRussell King "parent directory changed under me! (%06x but got %06x)\n", 2021da177e4SLinus Torvalds ADFS_I(inode)->parent_id, dir.parent_id); 2031da177e4SLinus Torvalds ret = -EIO; 2041da177e4SLinus Torvalds goto free_out; 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds obj->parent_id = inode->i_ino; 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds read_lock(&adfs_dir_lock); 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds ret = ops->setpos(&dir, 0); 2121da177e4SLinus Torvalds if (ret) 2131da177e4SLinus Torvalds goto unlock_out; 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds ret = -ENOENT; 2161e504cf8SRussell King name = qstr->name; 2171e504cf8SRussell King name_len = qstr->len; 2181da177e4SLinus Torvalds while (ops->getnext(&dir, obj) == 0) { 2191e504cf8SRussell King if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) { 2201da177e4SLinus Torvalds ret = 0; 2211da177e4SLinus Torvalds break; 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds unlock_out: 2261da177e4SLinus Torvalds read_unlock(&adfs_dir_lock); 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds free_out: 229*1dd9f5baSRussell King adfs_dir_relse(&dir); 2301da177e4SLinus Torvalds out: 2311da177e4SLinus Torvalds return ret; 2321da177e4SLinus Torvalds } 2331da177e4SLinus Torvalds 2344b6f5d20SArjan van de Ven const struct file_operations adfs_dir_operations = { 2351da177e4SLinus Torvalds .read = generic_read_dir, 23659af1584SAl Viro .llseek = generic_file_llseek, 2372638ffbaSAl Viro .iterate = adfs_readdir, 2381b061d92SChristoph Hellwig .fsync = generic_file_fsync, 2391da177e4SLinus Torvalds }; 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds static int 242da53be12SLinus Torvalds adfs_hash(const struct dentry *parent, struct qstr *qstr) 2431da177e4SLinus Torvalds { 2441da177e4SLinus Torvalds const unsigned char *name; 2451da177e4SLinus Torvalds unsigned long hash; 2462eb0684fSRussell King u32 len; 2471da177e4SLinus Torvalds 2482eb0684fSRussell King if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen) 2492eb0684fSRussell King return -ENAMETOOLONG; 2501da177e4SLinus Torvalds 2512eb0684fSRussell King len = qstr->len; 2521da177e4SLinus Torvalds name = qstr->name; 2538387ff25SLinus Torvalds hash = init_name_hash(parent); 2542eb0684fSRussell King while (len--) 255525715d0SRussell King hash = partial_name_hash(adfs_tolower(*name++), hash); 2561da177e4SLinus Torvalds qstr->hash = end_name_hash(hash); 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds return 0; 2591da177e4SLinus Torvalds } 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds /* 2621da177e4SLinus Torvalds * Compare two names, taking note of the name length 2631da177e4SLinus Torvalds * requirements of the underlying filesystem. 2641da177e4SLinus Torvalds */ 2651e504cf8SRussell King static int adfs_compare(const struct dentry *dentry, unsigned int len, 2661e504cf8SRussell King const char *str, const struct qstr *qstr) 2671da177e4SLinus Torvalds { 2681e504cf8SRussell King return __adfs_compare(qstr->name, qstr->len, str, len); 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 271e16404edSAl Viro const struct dentry_operations adfs_dentry_operations = { 2721da177e4SLinus Torvalds .d_hash = adfs_hash, 2731da177e4SLinus Torvalds .d_compare = adfs_compare, 2741da177e4SLinus Torvalds }; 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds static struct dentry * 27700cd8dd3SAl Viro adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 2781da177e4SLinus Torvalds { 2791da177e4SLinus Torvalds struct inode *inode = NULL; 2801da177e4SLinus Torvalds struct object_info obj; 2811da177e4SLinus Torvalds int error; 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj); 2841da177e4SLinus Torvalds if (error == 0) { 2851da177e4SLinus Torvalds /* 2861da177e4SLinus Torvalds * This only returns NULL if get_empty_inode 2871da177e4SLinus Torvalds * fails. 2881da177e4SLinus Torvalds */ 2891da177e4SLinus Torvalds inode = adfs_iget(dir->i_sb, &obj); 2909a7dddcaSAl Viro if (!inode) 2919a7dddcaSAl Viro inode = ERR_PTR(-EACCES); 2929a7dddcaSAl Viro } else if (error != -ENOENT) { 2939a7dddcaSAl Viro inode = ERR_PTR(error); 2941da177e4SLinus Torvalds } 2959a7dddcaSAl Viro return d_splice_alias(inode, dentry); 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds /* 2991da177e4SLinus Torvalds * directories can handle most operations... 3001da177e4SLinus Torvalds */ 301754661f1SArjan van de Ven const struct inode_operations adfs_dir_inode_operations = { 3021da177e4SLinus Torvalds .lookup = adfs_lookup, 3031da177e4SLinus Torvalds .setattr = adfs_notify_change, 3041da177e4SLinus Torvalds }; 305