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; 241da177e4SLinus Torvalds 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 721da177e4SLinus Torvalds 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 1031da177e4SLinus Torvalds static int 1041da177e4SLinus Torvalds adfs_match(struct qstr *name, struct object_info *obj) 1051da177e4SLinus Torvalds { 1061da177e4SLinus Torvalds int i; 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds if (name->len != obj->name_len) 1091da177e4SLinus Torvalds return 0; 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds for (i = 0; i < name->len; i++) { 1121da177e4SLinus Torvalds char c1, c2; 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds c1 = name->name[i]; 1151da177e4SLinus Torvalds c2 = obj->name[i]; 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds if (c1 >= 'A' && c1 <= 'Z') 1181da177e4SLinus Torvalds c1 += 'a' - 'A'; 1191da177e4SLinus Torvalds if (c2 >= 'A' && c2 <= 'Z') 1201da177e4SLinus Torvalds c2 += 'a' - 'A'; 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds if (c1 != c2) 1231da177e4SLinus Torvalds return 0; 1241da177e4SLinus Torvalds } 1251da177e4SLinus Torvalds return 1; 1261da177e4SLinus Torvalds } 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds static int 1291da177e4SLinus Torvalds adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj) 1301da177e4SLinus Torvalds { 1311da177e4SLinus Torvalds struct super_block *sb = inode->i_sb; 1321da177e4SLinus Torvalds struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 1331da177e4SLinus Torvalds struct adfs_dir dir; 1341da177e4SLinus Torvalds int ret; 1351da177e4SLinus Torvalds 1361da177e4SLinus Torvalds ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); 1371da177e4SLinus Torvalds if (ret) 1381da177e4SLinus Torvalds goto out; 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds if (ADFS_I(inode)->parent_id != dir.parent_id) { 141*19bdd41aSJoe Perches adfs_error(sb, "parent directory changed under me! (%lx but got %x)\n", 1421da177e4SLinus Torvalds ADFS_I(inode)->parent_id, dir.parent_id); 1431da177e4SLinus Torvalds ret = -EIO; 1441da177e4SLinus Torvalds goto free_out; 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds obj->parent_id = inode->i_ino; 1481da177e4SLinus Torvalds 1491da177e4SLinus Torvalds /* 1501da177e4SLinus Torvalds * '.' is handled by reserved_lookup() in fs/namei.c 1511da177e4SLinus Torvalds */ 1521da177e4SLinus Torvalds if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') { 1531da177e4SLinus Torvalds /* 1541da177e4SLinus Torvalds * Currently unable to fill in the rest of 'obj', 1551da177e4SLinus Torvalds * but this is better than nothing. We need to 1561da177e4SLinus Torvalds * ascend one level to find it's parent. 1571da177e4SLinus Torvalds */ 1581da177e4SLinus Torvalds obj->name_len = 0; 1591da177e4SLinus Torvalds obj->file_id = obj->parent_id; 1601da177e4SLinus Torvalds goto free_out; 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds read_lock(&adfs_dir_lock); 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds ret = ops->setpos(&dir, 0); 1661da177e4SLinus Torvalds if (ret) 1671da177e4SLinus Torvalds goto unlock_out; 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds ret = -ENOENT; 1701da177e4SLinus Torvalds while (ops->getnext(&dir, obj) == 0) { 1711da177e4SLinus Torvalds if (adfs_match(name, obj)) { 1721da177e4SLinus Torvalds ret = 0; 1731da177e4SLinus Torvalds break; 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds unlock_out: 1781da177e4SLinus Torvalds read_unlock(&adfs_dir_lock); 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds free_out: 1811da177e4SLinus Torvalds ops->free(&dir); 1821da177e4SLinus Torvalds out: 1831da177e4SLinus Torvalds return ret; 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds 1864b6f5d20SArjan van de Ven const struct file_operations adfs_dir_operations = { 1871da177e4SLinus Torvalds .read = generic_read_dir, 18859af1584SAl Viro .llseek = generic_file_llseek, 1892638ffbaSAl Viro .iterate = adfs_readdir, 1901b061d92SChristoph Hellwig .fsync = generic_file_fsync, 1911da177e4SLinus Torvalds }; 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds static int 194da53be12SLinus Torvalds adfs_hash(const struct dentry *parent, struct qstr *qstr) 1951da177e4SLinus Torvalds { 1961da177e4SLinus Torvalds const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen; 1971da177e4SLinus Torvalds const unsigned char *name; 1981da177e4SLinus Torvalds unsigned long hash; 1991da177e4SLinus Torvalds int i; 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds if (qstr->len < name_len) 2021da177e4SLinus Torvalds return 0; 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds /* 2051da177e4SLinus Torvalds * Truncate the name in place, avoids 2061da177e4SLinus Torvalds * having to define a compare function. 2071da177e4SLinus Torvalds */ 2081da177e4SLinus Torvalds qstr->len = i = name_len; 2091da177e4SLinus Torvalds name = qstr->name; 2101da177e4SLinus Torvalds hash = init_name_hash(); 2111da177e4SLinus Torvalds while (i--) { 2121da177e4SLinus Torvalds char c; 2131da177e4SLinus Torvalds 2141da177e4SLinus Torvalds c = *name++; 2151da177e4SLinus Torvalds if (c >= 'A' && c <= 'Z') 2161da177e4SLinus Torvalds c += 'a' - 'A'; 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds hash = partial_name_hash(c, hash); 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds qstr->hash = end_name_hash(hash); 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds return 0; 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds /* 2261da177e4SLinus Torvalds * Compare two names, taking note of the name length 2271da177e4SLinus Torvalds * requirements of the underlying filesystem. 2281da177e4SLinus Torvalds */ 2291da177e4SLinus Torvalds static int 230da53be12SLinus Torvalds adfs_compare(const struct dentry *parent, const struct dentry *dentry, 231621e155aSNick Piggin unsigned int len, const char *str, const struct qstr *name) 2321da177e4SLinus Torvalds { 2331da177e4SLinus Torvalds int i; 2341da177e4SLinus Torvalds 235621e155aSNick Piggin if (len != name->len) 2361da177e4SLinus Torvalds return 1; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds for (i = 0; i < name->len; i++) { 2391da177e4SLinus Torvalds char a, b; 2401da177e4SLinus Torvalds 241621e155aSNick Piggin a = str[i]; 2421da177e4SLinus Torvalds b = name->name[i]; 2431da177e4SLinus Torvalds 2441da177e4SLinus Torvalds if (a >= 'A' && a <= 'Z') 2451da177e4SLinus Torvalds a += 'a' - 'A'; 2461da177e4SLinus Torvalds if (b >= 'A' && b <= 'Z') 2471da177e4SLinus Torvalds b += 'a' - 'A'; 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds if (a != b) 2501da177e4SLinus Torvalds return 1; 2511da177e4SLinus Torvalds } 2521da177e4SLinus Torvalds return 0; 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds 255e16404edSAl Viro const struct dentry_operations adfs_dentry_operations = { 2561da177e4SLinus Torvalds .d_hash = adfs_hash, 2571da177e4SLinus Torvalds .d_compare = adfs_compare, 2581da177e4SLinus Torvalds }; 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds static struct dentry * 26100cd8dd3SAl Viro adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 2621da177e4SLinus Torvalds { 2631da177e4SLinus Torvalds struct inode *inode = NULL; 2641da177e4SLinus Torvalds struct object_info obj; 2651da177e4SLinus Torvalds int error; 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj); 2681da177e4SLinus Torvalds if (error == 0) { 2691da177e4SLinus Torvalds error = -EACCES; 2701da177e4SLinus Torvalds /* 2711da177e4SLinus Torvalds * This only returns NULL if get_empty_inode 2721da177e4SLinus Torvalds * fails. 2731da177e4SLinus Torvalds */ 2741da177e4SLinus Torvalds inode = adfs_iget(dir->i_sb, &obj); 2751da177e4SLinus Torvalds if (inode) 2761da177e4SLinus Torvalds error = 0; 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds d_add(dentry, inode); 2791da177e4SLinus Torvalds return ERR_PTR(error); 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds /* 2831da177e4SLinus Torvalds * directories can handle most operations... 2841da177e4SLinus Torvalds */ 285754661f1SArjan van de Ven const struct inode_operations adfs_dir_inode_operations = { 2861da177e4SLinus Torvalds .lookup = adfs_lookup, 2871da177e4SLinus Torvalds .setattr = adfs_notify_change, 2881da177e4SLinus Torvalds }; 289