11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/fs/adfs/dir.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 1999-2000 Russell King 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4SLinus Torvalds * it under the terms of the GNU General Public License version 2 as 81da177e4SLinus Torvalds * published by the Free Software Foundation. 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * Common directory handling for ADFS 111da177e4SLinus Torvalds */ 121da177e4SLinus Torvalds #include "adfs.h" 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds /* 151da177e4SLinus Torvalds * For future. This should probably be per-directory. 161da177e4SLinus Torvalds */ 171da177e4SLinus Torvalds static DEFINE_RWLOCK(adfs_dir_lock); 181da177e4SLinus Torvalds 191da177e4SLinus Torvalds static int 202638ffbaSAl Viro adfs_readdir(struct file *file, struct dir_context *ctx) 211da177e4SLinus Torvalds { 222638ffbaSAl Viro struct inode *inode = file_inode(file); 231da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 240125f504SJulia Lawall const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 251da177e4SLinus Torvalds struct object_info obj; 261da177e4SLinus Torvalds struct adfs_dir dir; 271da177e4SLinus Torvalds int ret = 0; 281da177e4SLinus Torvalds 292638ffbaSAl Viro if (ctx->pos >> 32) 302638ffbaSAl Viro return 0; 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); 331da177e4SLinus Torvalds if (ret) 342638ffbaSAl Viro return ret; 351da177e4SLinus Torvalds 362638ffbaSAl Viro if (ctx->pos == 0) { 372638ffbaSAl Viro if (!dir_emit_dot(file, ctx)) 381da177e4SLinus Torvalds goto free_out; 392638ffbaSAl Viro ctx->pos = 1; 402638ffbaSAl Viro } 412638ffbaSAl Viro if (ctx->pos == 1) { 422638ffbaSAl Viro if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR)) 431da177e4SLinus Torvalds goto free_out; 442638ffbaSAl Viro ctx->pos = 2; 451da177e4SLinus Torvalds } 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds read_lock(&adfs_dir_lock); 481da177e4SLinus Torvalds 492638ffbaSAl Viro ret = ops->setpos(&dir, ctx->pos - 2); 501da177e4SLinus Torvalds if (ret) 511da177e4SLinus Torvalds goto unlock_out; 521da177e4SLinus Torvalds while (ops->getnext(&dir, &obj) == 0) { 532638ffbaSAl Viro if (!dir_emit(ctx, obj.name, obj.name_len, 542638ffbaSAl Viro obj.file_id, DT_UNKNOWN)) 552638ffbaSAl Viro break; 562638ffbaSAl Viro ctx->pos++; 571da177e4SLinus Torvalds } 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds unlock_out: 601da177e4SLinus Torvalds read_unlock(&adfs_dir_lock); 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds free_out: 631da177e4SLinus Torvalds ops->free(&dir); 641da177e4SLinus Torvalds return ret; 651da177e4SLinus Torvalds } 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds int 68ffdc9064SAl Viro adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) 691da177e4SLinus Torvalds { 701da177e4SLinus Torvalds int ret = -EINVAL; 711da177e4SLinus Torvalds #ifdef CONFIG_ADFS_FS_RW 720125f504SJulia Lawall const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 731da177e4SLinus Torvalds struct adfs_dir dir; 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n", 761da177e4SLinus Torvalds obj->file_id, obj->parent_id); 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds if (!ops->update) { 791da177e4SLinus Torvalds ret = -EINVAL; 801da177e4SLinus Torvalds goto out; 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds ret = ops->read(sb, obj->parent_id, 0, &dir); 841da177e4SLinus Torvalds if (ret) 851da177e4SLinus Torvalds goto out; 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds write_lock(&adfs_dir_lock); 881da177e4SLinus Torvalds ret = ops->update(&dir, obj); 891da177e4SLinus Torvalds write_unlock(&adfs_dir_lock); 901da177e4SLinus Torvalds 91ffdc9064SAl Viro if (wait) { 92ffdc9064SAl Viro int err = ops->sync(&dir); 93ffdc9064SAl Viro if (!ret) 94ffdc9064SAl Viro ret = err; 95ffdc9064SAl Viro } 96ffdc9064SAl Viro 971da177e4SLinus Torvalds ops->free(&dir); 981da177e4SLinus Torvalds out: 991da177e4SLinus Torvalds #endif 1001da177e4SLinus Torvalds return ret; 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 103*525715d0SRussell King static unsigned char adfs_tolower(unsigned char c) 104*525715d0SRussell King { 105*525715d0SRussell King if (c >= 'A' && c <= 'Z') 106*525715d0SRussell King c += 'a' - 'A'; 107*525715d0SRussell King return c; 108*525715d0SRussell King } 109*525715d0SRussell King 1101e504cf8SRussell King static int __adfs_compare(const unsigned char *qstr, u32 qlen, 1111e504cf8SRussell King const char *str, u32 len) 1121da177e4SLinus Torvalds { 1131e504cf8SRussell King u32 i; 1141da177e4SLinus Torvalds 1151e504cf8SRussell King if (qlen != len) 1161e504cf8SRussell King return 1; 1171da177e4SLinus Torvalds 118*525715d0SRussell King for (i = 0; i < qlen; i++) 119*525715d0SRussell King if (adfs_tolower(qstr[i]) != adfs_tolower(str[i])) 1201da177e4SLinus Torvalds return 1; 121*525715d0SRussell King 1221e504cf8SRussell King return 0; 1231e504cf8SRussell King } 1241da177e4SLinus Torvalds 1251e504cf8SRussell King static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr, 1261e504cf8SRussell King struct object_info *obj) 1271da177e4SLinus Torvalds { 1281da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 1290125f504SJulia Lawall const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 1301e504cf8SRussell King const unsigned char *name; 1311da177e4SLinus Torvalds struct adfs_dir dir; 1321e504cf8SRussell King u32 name_len; 1331da177e4SLinus Torvalds int ret; 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); 1361da177e4SLinus Torvalds if (ret) 1371da177e4SLinus Torvalds goto out; 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds if (ADFS_I(inode)->parent_id != dir.parent_id) { 14019bdd41aSJoe Perches adfs_error(sb, "parent directory changed under me! (%lx but got %x)\n", 1411da177e4SLinus Torvalds ADFS_I(inode)->parent_id, dir.parent_id); 1421da177e4SLinus Torvalds ret = -EIO; 1431da177e4SLinus Torvalds goto free_out; 1441da177e4SLinus Torvalds } 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds obj->parent_id = inode->i_ino; 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds read_lock(&adfs_dir_lock); 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds ret = ops->setpos(&dir, 0); 1511da177e4SLinus Torvalds if (ret) 1521da177e4SLinus Torvalds goto unlock_out; 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds ret = -ENOENT; 1551e504cf8SRussell King name = qstr->name; 1561e504cf8SRussell King name_len = qstr->len; 1571da177e4SLinus Torvalds while (ops->getnext(&dir, obj) == 0) { 1581e504cf8SRussell King if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) { 1591da177e4SLinus Torvalds ret = 0; 1601da177e4SLinus Torvalds break; 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds } 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds unlock_out: 1651da177e4SLinus Torvalds read_unlock(&adfs_dir_lock); 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds free_out: 1681da177e4SLinus Torvalds ops->free(&dir); 1691da177e4SLinus Torvalds out: 1701da177e4SLinus Torvalds return ret; 1711da177e4SLinus Torvalds } 1721da177e4SLinus Torvalds 1734b6f5d20SArjan van de Ven const struct file_operations adfs_dir_operations = { 1741da177e4SLinus Torvalds .read = generic_read_dir, 17559af1584SAl Viro .llseek = generic_file_llseek, 1762638ffbaSAl Viro .iterate = adfs_readdir, 1771b061d92SChristoph Hellwig .fsync = generic_file_fsync, 1781da177e4SLinus Torvalds }; 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds static int 181da53be12SLinus Torvalds adfs_hash(const struct dentry *parent, struct qstr *qstr) 1821da177e4SLinus Torvalds { 1831da177e4SLinus Torvalds const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen; 1841da177e4SLinus Torvalds const unsigned char *name; 1851da177e4SLinus Torvalds unsigned long hash; 1861da177e4SLinus Torvalds int i; 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds if (qstr->len < name_len) 1891da177e4SLinus Torvalds return 0; 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds /* 1921da177e4SLinus Torvalds * Truncate the name in place, avoids 1931da177e4SLinus Torvalds * having to define a compare function. 1941da177e4SLinus Torvalds */ 1951da177e4SLinus Torvalds qstr->len = i = name_len; 1961da177e4SLinus Torvalds name = qstr->name; 1978387ff25SLinus Torvalds hash = init_name_hash(parent); 198*525715d0SRussell King while (i--) 199*525715d0SRussell King hash = partial_name_hash(adfs_tolower(*name++), hash); 2001da177e4SLinus Torvalds qstr->hash = end_name_hash(hash); 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds return 0; 2031da177e4SLinus Torvalds } 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds /* 2061da177e4SLinus Torvalds * Compare two names, taking note of the name length 2071da177e4SLinus Torvalds * requirements of the underlying filesystem. 2081da177e4SLinus Torvalds */ 2091e504cf8SRussell King static int adfs_compare(const struct dentry *dentry, unsigned int len, 2101e504cf8SRussell King const char *str, const struct qstr *qstr) 2111da177e4SLinus Torvalds { 2121e504cf8SRussell King return __adfs_compare(qstr->name, qstr->len, str, len); 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 215e16404edSAl Viro const struct dentry_operations adfs_dentry_operations = { 2161da177e4SLinus Torvalds .d_hash = adfs_hash, 2171da177e4SLinus Torvalds .d_compare = adfs_compare, 2181da177e4SLinus Torvalds }; 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds static struct dentry * 22100cd8dd3SAl Viro adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 2221da177e4SLinus Torvalds { 2231da177e4SLinus Torvalds struct inode *inode = NULL; 2241da177e4SLinus Torvalds struct object_info obj; 2251da177e4SLinus Torvalds int error; 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj); 2281da177e4SLinus Torvalds if (error == 0) { 2291da177e4SLinus Torvalds /* 2301da177e4SLinus Torvalds * This only returns NULL if get_empty_inode 2311da177e4SLinus Torvalds * fails. 2321da177e4SLinus Torvalds */ 2331da177e4SLinus Torvalds inode = adfs_iget(dir->i_sb, &obj); 2349a7dddcaSAl Viro if (!inode) 2359a7dddcaSAl Viro inode = ERR_PTR(-EACCES); 2369a7dddcaSAl Viro } else if (error != -ENOENT) { 2379a7dddcaSAl Viro inode = ERR_PTR(error); 2381da177e4SLinus Torvalds } 2399a7dddcaSAl Viro return d_splice_alias(inode, dentry); 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds /* 2431da177e4SLinus Torvalds * directories can handle most operations... 2441da177e4SLinus Torvalds */ 245754661f1SArjan van de Ven const struct inode_operations adfs_dir_inode_operations = { 2461da177e4SLinus Torvalds .lookup = adfs_lookup, 2471da177e4SLinus Torvalds .setattr = adfs_notify_change, 2481da177e4SLinus Torvalds }; 249