18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/affs/namei.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) 1996 Hans-Joachim Widmaier - Rewritten 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * (C) 1991 Linus Torvalds - minix filesystem 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "affs.h" 138c2ecf20Sopenharmony_ci#include <linux/exportfs.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_citypedef int (*toupper_t)(int); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* Simple toupper() for DOS\1 */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic int 208c2ecf20Sopenharmony_ciaffs_toupper(int ch) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch; 238c2ecf20Sopenharmony_ci} 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* International toupper() for DOS\3 ("international") */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int 288c2ecf20Sopenharmony_ciaffs_intl_toupper(int ch) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0 318c2ecf20Sopenharmony_ci && ch <= 0xFE && ch != 0xF7) ? 328c2ecf20Sopenharmony_ci ch - ('a' - 'A') : ch; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic inline toupper_t 368c2ecf20Sopenharmony_ciaffs_get_toupper(struct super_block *sb) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci return affs_test_opt(AFFS_SB(sb)->s_flags, SF_INTL) ? 398c2ecf20Sopenharmony_ci affs_intl_toupper : affs_toupper; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * Note: the dentry argument is the parent dentry. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistatic inline int 468c2ecf20Sopenharmony_ci__affs_hash_dentry(const struct dentry *dentry, struct qstr *qstr, toupper_t toupper, bool notruncate) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci const u8 *name = qstr->name; 498c2ecf20Sopenharmony_ci unsigned long hash; 508c2ecf20Sopenharmony_ci int retval; 518c2ecf20Sopenharmony_ci u32 len; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci retval = affs_check_name(qstr->name, qstr->len, notruncate); 548c2ecf20Sopenharmony_ci if (retval) 558c2ecf20Sopenharmony_ci return retval; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci hash = init_name_hash(dentry); 588c2ecf20Sopenharmony_ci len = min(qstr->len, AFFSNAMEMAX); 598c2ecf20Sopenharmony_ci for (; len > 0; name++, len--) 608c2ecf20Sopenharmony_ci hash = partial_name_hash(toupper(*name), hash); 618c2ecf20Sopenharmony_ci qstr->hash = end_name_hash(hash); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return 0; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int 678c2ecf20Sopenharmony_ciaffs_hash_dentry(const struct dentry *dentry, struct qstr *qstr) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci return __affs_hash_dentry(dentry, qstr, affs_toupper, 708c2ecf20Sopenharmony_ci affs_nofilenametruncate(dentry)); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int 758c2ecf20Sopenharmony_ciaffs_intl_hash_dentry(const struct dentry *dentry, struct qstr *qstr) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci return __affs_hash_dentry(dentry, qstr, affs_intl_toupper, 788c2ecf20Sopenharmony_ci affs_nofilenametruncate(dentry)); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic inline int __affs_compare_dentry(unsigned int len, 838c2ecf20Sopenharmony_ci const char *str, const struct qstr *name, toupper_t toupper, 848c2ecf20Sopenharmony_ci bool notruncate) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci const u8 *aname = str; 878c2ecf20Sopenharmony_ci const u8 *bname = name->name; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* 908c2ecf20Sopenharmony_ci * 'str' is the name of an already existing dentry, so the name 918c2ecf20Sopenharmony_ci * must be valid. 'name' must be validated first. 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (affs_check_name(name->name, name->len, notruncate)) 958c2ecf20Sopenharmony_ci return 1; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* 988c2ecf20Sopenharmony_ci * If the names are longer than the allowed 30 chars, 998c2ecf20Sopenharmony_ci * the excess is ignored, so their length may differ. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci if (len >= AFFSNAMEMAX) { 1028c2ecf20Sopenharmony_ci if (name->len < AFFSNAMEMAX) 1038c2ecf20Sopenharmony_ci return 1; 1048c2ecf20Sopenharmony_ci len = AFFSNAMEMAX; 1058c2ecf20Sopenharmony_ci } else if (len != name->len) 1068c2ecf20Sopenharmony_ci return 1; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci for (; len > 0; len--) 1098c2ecf20Sopenharmony_ci if (toupper(*aname++) != toupper(*bname++)) 1108c2ecf20Sopenharmony_ci return 1; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int 1168c2ecf20Sopenharmony_ciaffs_compare_dentry(const struct dentry *dentry, 1178c2ecf20Sopenharmony_ci unsigned int len, const char *str, const struct qstr *name) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return __affs_compare_dentry(len, str, name, affs_toupper, 1218c2ecf20Sopenharmony_ci affs_nofilenametruncate(dentry)); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int 1258c2ecf20Sopenharmony_ciaffs_intl_compare_dentry(const struct dentry *dentry, 1268c2ecf20Sopenharmony_ci unsigned int len, const char *str, const struct qstr *name) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci return __affs_compare_dentry(len, str, name, affs_intl_toupper, 1298c2ecf20Sopenharmony_ci affs_nofilenametruncate(dentry)); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* 1348c2ecf20Sopenharmony_ci * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic inline int 1388c2ecf20Sopenharmony_ciaffs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci const u8 *name = dentry->d_name.name; 1418c2ecf20Sopenharmony_ci int len = dentry->d_name.len; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (len >= AFFSNAMEMAX) { 1448c2ecf20Sopenharmony_ci if (*name2 < AFFSNAMEMAX) 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci len = AFFSNAMEMAX; 1478c2ecf20Sopenharmony_ci } else if (len != *name2) 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci for (name2++; len > 0; len--) 1518c2ecf20Sopenharmony_ci if (toupper(*name++) != toupper(*name2++)) 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci return 1; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ciint 1578c2ecf20Sopenharmony_ciaffs_hash_name(struct super_block *sb, const u8 *name, unsigned int len) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci toupper_t toupper = affs_get_toupper(sb); 1608c2ecf20Sopenharmony_ci u32 hash; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci hash = len = min(len, AFFSNAMEMAX); 1638c2ecf20Sopenharmony_ci for (; len > 0; len--) 1648c2ecf20Sopenharmony_ci hash = (hash * 13 + toupper(*name++)) & 0x7ff; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return hash % AFFS_SB(sb)->s_hashsize; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic struct buffer_head * 1708c2ecf20Sopenharmony_ciaffs_find_entry(struct inode *dir, struct dentry *dentry) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 1738c2ecf20Sopenharmony_ci struct buffer_head *bh; 1748c2ecf20Sopenharmony_ci toupper_t toupper = affs_get_toupper(sb); 1758c2ecf20Sopenharmony_ci u32 key; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci pr_debug("%s(\"%pd\")\n", __func__, dentry); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci bh = affs_bread(sb, dir->i_ino); 1808c2ecf20Sopenharmony_ci if (!bh) 1818c2ecf20Sopenharmony_ci return ERR_PTR(-EIO); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci for (;;) { 1868c2ecf20Sopenharmony_ci affs_brelse(bh); 1878c2ecf20Sopenharmony_ci if (key == 0) 1888c2ecf20Sopenharmony_ci return NULL; 1898c2ecf20Sopenharmony_ci bh = affs_bread(sb, key); 1908c2ecf20Sopenharmony_ci if (!bh) 1918c2ecf20Sopenharmony_ci return ERR_PTR(-EIO); 1928c2ecf20Sopenharmony_ci if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper)) 1938c2ecf20Sopenharmony_ci return bh; 1948c2ecf20Sopenharmony_ci key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistruct dentry * 1998c2ecf20Sopenharmony_ciaffs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 2028c2ecf20Sopenharmony_ci struct buffer_head *bh; 2038c2ecf20Sopenharmony_ci struct inode *inode = NULL; 2048c2ecf20Sopenharmony_ci struct dentry *res; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci pr_debug("%s(\"%pd\")\n", __func__, dentry); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci affs_lock_dir(dir); 2098c2ecf20Sopenharmony_ci bh = affs_find_entry(dir, dentry); 2108c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 2118c2ecf20Sopenharmony_ci affs_unlock_dir(dir); 2128c2ecf20Sopenharmony_ci return ERR_CAST(bh); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci if (bh) { 2158c2ecf20Sopenharmony_ci u32 ino = bh->b_blocknr; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* store the real header ino in d_fsdata for faster lookups */ 2188c2ecf20Sopenharmony_ci dentry->d_fsdata = (void *)(long)ino; 2198c2ecf20Sopenharmony_ci switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { 2208c2ecf20Sopenharmony_ci //link to dirs disabled 2218c2ecf20Sopenharmony_ci //case ST_LINKDIR: 2228c2ecf20Sopenharmony_ci case ST_LINKFILE: 2238c2ecf20Sopenharmony_ci ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci affs_brelse(bh); 2268c2ecf20Sopenharmony_ci inode = affs_iget(sb, ino); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci res = d_splice_alias(inode, dentry); 2298c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(res)) 2308c2ecf20Sopenharmony_ci res->d_fsdata = dentry->d_fsdata; 2318c2ecf20Sopenharmony_ci affs_unlock_dir(dir); 2328c2ecf20Sopenharmony_ci return res; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ciint 2368c2ecf20Sopenharmony_ciaffs_unlink(struct inode *dir, struct dentry *dentry) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino, 2398c2ecf20Sopenharmony_ci d_inode(dentry)->i_ino, dentry); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return affs_remove_header(dentry); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ciint 2458c2ecf20Sopenharmony_ciaffs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 2488c2ecf20Sopenharmony_ci struct inode *inode; 2498c2ecf20Sopenharmony_ci int error; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci pr_debug("%s(%lu,\"%pd\",0%ho)\n", 2528c2ecf20Sopenharmony_ci __func__, dir->i_ino, dentry, mode); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci inode = affs_new_inode(dir); 2558c2ecf20Sopenharmony_ci if (!inode) 2568c2ecf20Sopenharmony_ci return -ENOSPC; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci inode->i_mode = mode; 2598c2ecf20Sopenharmony_ci affs_mode_to_prot(inode); 2608c2ecf20Sopenharmony_ci mark_inode_dirty(inode); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci inode->i_op = &affs_file_inode_operations; 2638c2ecf20Sopenharmony_ci inode->i_fop = &affs_file_operations; 2648c2ecf20Sopenharmony_ci inode->i_mapping->a_ops = affs_test_opt(AFFS_SB(sb)->s_flags, SF_OFS) ? 2658c2ecf20Sopenharmony_ci &affs_aops_ofs : &affs_aops; 2668c2ecf20Sopenharmony_ci error = affs_add_entry(dir, inode, dentry, ST_FILE); 2678c2ecf20Sopenharmony_ci if (error) { 2688c2ecf20Sopenharmony_ci clear_nlink(inode); 2698c2ecf20Sopenharmony_ci iput(inode); 2708c2ecf20Sopenharmony_ci return error; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ciint 2768c2ecf20Sopenharmony_ciaffs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct inode *inode; 2798c2ecf20Sopenharmony_ci int error; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci pr_debug("%s(%lu,\"%pd\",0%ho)\n", 2828c2ecf20Sopenharmony_ci __func__, dir->i_ino, dentry, mode); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci inode = affs_new_inode(dir); 2858c2ecf20Sopenharmony_ci if (!inode) 2868c2ecf20Sopenharmony_ci return -ENOSPC; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci inode->i_mode = S_IFDIR | mode; 2898c2ecf20Sopenharmony_ci affs_mode_to_prot(inode); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci inode->i_op = &affs_dir_inode_operations; 2928c2ecf20Sopenharmony_ci inode->i_fop = &affs_dir_operations; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci error = affs_add_entry(dir, inode, dentry, ST_USERDIR); 2958c2ecf20Sopenharmony_ci if (error) { 2968c2ecf20Sopenharmony_ci clear_nlink(inode); 2978c2ecf20Sopenharmony_ci mark_inode_dirty(inode); 2988c2ecf20Sopenharmony_ci iput(inode); 2998c2ecf20Sopenharmony_ci return error; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ciint 3058c2ecf20Sopenharmony_ciaffs_rmdir(struct inode *dir, struct dentry *dentry) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino, 3088c2ecf20Sopenharmony_ci d_inode(dentry)->i_ino, dentry); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return affs_remove_header(dentry); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ciint 3148c2ecf20Sopenharmony_ciaffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 3178c2ecf20Sopenharmony_ci struct buffer_head *bh; 3188c2ecf20Sopenharmony_ci struct inode *inode; 3198c2ecf20Sopenharmony_ci char *p; 3208c2ecf20Sopenharmony_ci int i, maxlen, error; 3218c2ecf20Sopenharmony_ci char c, lc; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci pr_debug("%s(%lu,\"%pd\" -> \"%s\")\n", 3248c2ecf20Sopenharmony_ci __func__, dir->i_ino, dentry, symname); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1; 3278c2ecf20Sopenharmony_ci inode = affs_new_inode(dir); 3288c2ecf20Sopenharmony_ci if (!inode) 3298c2ecf20Sopenharmony_ci return -ENOSPC; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci inode->i_op = &affs_symlink_inode_operations; 3328c2ecf20Sopenharmony_ci inode_nohighmem(inode); 3338c2ecf20Sopenharmony_ci inode->i_data.a_ops = &affs_symlink_aops; 3348c2ecf20Sopenharmony_ci inode->i_mode = S_IFLNK | 0777; 3358c2ecf20Sopenharmony_ci affs_mode_to_prot(inode); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci error = -EIO; 3388c2ecf20Sopenharmony_ci bh = affs_bread(sb, inode->i_ino); 3398c2ecf20Sopenharmony_ci if (!bh) 3408c2ecf20Sopenharmony_ci goto err; 3418c2ecf20Sopenharmony_ci i = 0; 3428c2ecf20Sopenharmony_ci p = (char *)AFFS_HEAD(bh)->table; 3438c2ecf20Sopenharmony_ci lc = '/'; 3448c2ecf20Sopenharmony_ci if (*symname == '/') { 3458c2ecf20Sopenharmony_ci struct affs_sb_info *sbi = AFFS_SB(sb); 3468c2ecf20Sopenharmony_ci while (*symname == '/') 3478c2ecf20Sopenharmony_ci symname++; 3488c2ecf20Sopenharmony_ci spin_lock(&sbi->symlink_lock); 3498c2ecf20Sopenharmony_ci while (sbi->s_volume[i]) /* Cannot overflow */ 3508c2ecf20Sopenharmony_ci *p++ = sbi->s_volume[i++]; 3518c2ecf20Sopenharmony_ci spin_unlock(&sbi->symlink_lock); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci while (i < maxlen && (c = *symname++)) { 3548c2ecf20Sopenharmony_ci if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') { 3558c2ecf20Sopenharmony_ci *p++ = '/'; 3568c2ecf20Sopenharmony_ci i++; 3578c2ecf20Sopenharmony_ci symname += 2; 3588c2ecf20Sopenharmony_ci lc = '/'; 3598c2ecf20Sopenharmony_ci } else if (c == '.' && lc == '/' && *symname == '/') { 3608c2ecf20Sopenharmony_ci symname++; 3618c2ecf20Sopenharmony_ci lc = '/'; 3628c2ecf20Sopenharmony_ci } else { 3638c2ecf20Sopenharmony_ci *p++ = c; 3648c2ecf20Sopenharmony_ci lc = c; 3658c2ecf20Sopenharmony_ci i++; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci if (lc == '/') 3688c2ecf20Sopenharmony_ci while (*symname == '/') 3698c2ecf20Sopenharmony_ci symname++; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci *p = 0; 3728c2ecf20Sopenharmony_ci inode->i_size = i + 1; 3738c2ecf20Sopenharmony_ci mark_buffer_dirty_inode(bh, inode); 3748c2ecf20Sopenharmony_ci affs_brelse(bh); 3758c2ecf20Sopenharmony_ci mark_inode_dirty(inode); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK); 3788c2ecf20Sopenharmony_ci if (error) 3798c2ecf20Sopenharmony_ci goto err; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cierr: 3848c2ecf20Sopenharmony_ci clear_nlink(inode); 3858c2ecf20Sopenharmony_ci mark_inode_dirty(inode); 3868c2ecf20Sopenharmony_ci iput(inode); 3878c2ecf20Sopenharmony_ci return error; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ciint 3918c2ecf20Sopenharmony_ciaffs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct inode *inode = d_inode(old_dentry); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci pr_debug("%s(%lu, %lu, \"%pd\")\n", __func__, inode->i_ino, dir->i_ino, 3968c2ecf20Sopenharmony_ci dentry); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return affs_add_entry(dir, inode, dentry, ST_LINKFILE); 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int 4028c2ecf20Sopenharmony_ciaffs_rename(struct inode *old_dir, struct dentry *old_dentry, 4038c2ecf20Sopenharmony_ci struct inode *new_dir, struct dentry *new_dentry) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct super_block *sb = old_dir->i_sb; 4068c2ecf20Sopenharmony_ci struct buffer_head *bh = NULL; 4078c2ecf20Sopenharmony_ci int retval; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci retval = affs_check_name(new_dentry->d_name.name, 4108c2ecf20Sopenharmony_ci new_dentry->d_name.len, 4118c2ecf20Sopenharmony_ci affs_nofilenametruncate(old_dentry)); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (retval) 4148c2ecf20Sopenharmony_ci return retval; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* Unlink destination if it already exists */ 4178c2ecf20Sopenharmony_ci if (d_really_is_positive(new_dentry)) { 4188c2ecf20Sopenharmony_ci retval = affs_remove_header(new_dentry); 4198c2ecf20Sopenharmony_ci if (retval) 4208c2ecf20Sopenharmony_ci return retval; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci bh = affs_bread(sb, d_inode(old_dentry)->i_ino); 4248c2ecf20Sopenharmony_ci if (!bh) 4258c2ecf20Sopenharmony_ci return -EIO; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Remove header from its parent directory. */ 4288c2ecf20Sopenharmony_ci affs_lock_dir(old_dir); 4298c2ecf20Sopenharmony_ci retval = affs_remove_hash(old_dir, bh); 4308c2ecf20Sopenharmony_ci affs_unlock_dir(old_dir); 4318c2ecf20Sopenharmony_ci if (retval) 4328c2ecf20Sopenharmony_ci goto done; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* And insert it into the new directory with the new name. */ 4358c2ecf20Sopenharmony_ci affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry); 4368c2ecf20Sopenharmony_ci affs_fix_checksum(sb, bh); 4378c2ecf20Sopenharmony_ci affs_lock_dir(new_dir); 4388c2ecf20Sopenharmony_ci retval = affs_insert_hash(new_dir, bh); 4398c2ecf20Sopenharmony_ci affs_unlock_dir(new_dir); 4408c2ecf20Sopenharmony_ci /* TODO: move it back to old_dir, if error? */ 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cidone: 4438c2ecf20Sopenharmony_ci mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir); 4448c2ecf20Sopenharmony_ci affs_brelse(bh); 4458c2ecf20Sopenharmony_ci return retval; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int 4498c2ecf20Sopenharmony_ciaffs_xrename(struct inode *old_dir, struct dentry *old_dentry, 4508c2ecf20Sopenharmony_ci struct inode *new_dir, struct dentry *new_dentry) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci struct super_block *sb = old_dir->i_sb; 4548c2ecf20Sopenharmony_ci struct buffer_head *bh_old = NULL; 4558c2ecf20Sopenharmony_ci struct buffer_head *bh_new = NULL; 4568c2ecf20Sopenharmony_ci int retval; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci bh_old = affs_bread(sb, d_inode(old_dentry)->i_ino); 4598c2ecf20Sopenharmony_ci if (!bh_old) 4608c2ecf20Sopenharmony_ci return -EIO; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci bh_new = affs_bread(sb, d_inode(new_dentry)->i_ino); 4638c2ecf20Sopenharmony_ci if (!bh_new) { 4648c2ecf20Sopenharmony_ci affs_brelse(bh_old); 4658c2ecf20Sopenharmony_ci return -EIO; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* Remove old header from its parent directory. */ 4698c2ecf20Sopenharmony_ci affs_lock_dir(old_dir); 4708c2ecf20Sopenharmony_ci retval = affs_remove_hash(old_dir, bh_old); 4718c2ecf20Sopenharmony_ci affs_unlock_dir(old_dir); 4728c2ecf20Sopenharmony_ci if (retval) 4738c2ecf20Sopenharmony_ci goto done; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* Remove new header from its parent directory. */ 4768c2ecf20Sopenharmony_ci affs_lock_dir(new_dir); 4778c2ecf20Sopenharmony_ci retval = affs_remove_hash(new_dir, bh_new); 4788c2ecf20Sopenharmony_ci affs_unlock_dir(new_dir); 4798c2ecf20Sopenharmony_ci if (retval) 4808c2ecf20Sopenharmony_ci goto done; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* Insert old into the new directory with the new name. */ 4838c2ecf20Sopenharmony_ci affs_copy_name(AFFS_TAIL(sb, bh_old)->name, new_dentry); 4848c2ecf20Sopenharmony_ci affs_fix_checksum(sb, bh_old); 4858c2ecf20Sopenharmony_ci affs_lock_dir(new_dir); 4868c2ecf20Sopenharmony_ci retval = affs_insert_hash(new_dir, bh_old); 4878c2ecf20Sopenharmony_ci affs_unlock_dir(new_dir); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* Insert new into the old directory with the old name. */ 4908c2ecf20Sopenharmony_ci affs_copy_name(AFFS_TAIL(sb, bh_new)->name, old_dentry); 4918c2ecf20Sopenharmony_ci affs_fix_checksum(sb, bh_new); 4928c2ecf20Sopenharmony_ci affs_lock_dir(old_dir); 4938c2ecf20Sopenharmony_ci retval = affs_insert_hash(old_dir, bh_new); 4948c2ecf20Sopenharmony_ci affs_unlock_dir(old_dir); 4958c2ecf20Sopenharmony_cidone: 4968c2ecf20Sopenharmony_ci mark_buffer_dirty_inode(bh_old, new_dir); 4978c2ecf20Sopenharmony_ci mark_buffer_dirty_inode(bh_new, old_dir); 4988c2ecf20Sopenharmony_ci affs_brelse(bh_old); 4998c2ecf20Sopenharmony_ci affs_brelse(bh_new); 5008c2ecf20Sopenharmony_ci return retval; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ciint affs_rename2(struct inode *old_dir, struct dentry *old_dentry, 5048c2ecf20Sopenharmony_ci struct inode *new_dir, struct dentry *new_dentry, 5058c2ecf20Sopenharmony_ci unsigned int flags) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) 5098c2ecf20Sopenharmony_ci return -EINVAL; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci pr_debug("%s(old=%lu,\"%pd\" to new=%lu,\"%pd\")\n", __func__, 5128c2ecf20Sopenharmony_ci old_dir->i_ino, old_dentry, new_dir->i_ino, new_dentry); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (flags & RENAME_EXCHANGE) 5158c2ecf20Sopenharmony_ci return affs_xrename(old_dir, old_dentry, new_dir, new_dentry); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return affs_rename(old_dir, old_dentry, new_dir, new_dentry); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic struct dentry *affs_get_parent(struct dentry *child) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct inode *parent; 5238c2ecf20Sopenharmony_ci struct buffer_head *bh; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci bh = affs_bread(child->d_sb, d_inode(child)->i_ino); 5268c2ecf20Sopenharmony_ci if (!bh) 5278c2ecf20Sopenharmony_ci return ERR_PTR(-EIO); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci parent = affs_iget(child->d_sb, 5308c2ecf20Sopenharmony_ci be32_to_cpu(AFFS_TAIL(child->d_sb, bh)->parent)); 5318c2ecf20Sopenharmony_ci brelse(bh); 5328c2ecf20Sopenharmony_ci if (IS_ERR(parent)) 5338c2ecf20Sopenharmony_ci return ERR_CAST(parent); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci return d_obtain_alias(parent); 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic struct inode *affs_nfs_get_inode(struct super_block *sb, u64 ino, 5398c2ecf20Sopenharmony_ci u32 generation) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct inode *inode; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (!affs_validblock(sb, ino)) 5448c2ecf20Sopenharmony_ci return ERR_PTR(-ESTALE); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci inode = affs_iget(sb, ino); 5478c2ecf20Sopenharmony_ci if (IS_ERR(inode)) 5488c2ecf20Sopenharmony_ci return ERR_CAST(inode); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return inode; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic struct dentry *affs_fh_to_dentry(struct super_block *sb, struct fid *fid, 5548c2ecf20Sopenharmony_ci int fh_len, int fh_type) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci return generic_fh_to_dentry(sb, fid, fh_len, fh_type, 5578c2ecf20Sopenharmony_ci affs_nfs_get_inode); 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic struct dentry *affs_fh_to_parent(struct super_block *sb, struct fid *fid, 5618c2ecf20Sopenharmony_ci int fh_len, int fh_type) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci return generic_fh_to_parent(sb, fid, fh_len, fh_type, 5648c2ecf20Sopenharmony_ci affs_nfs_get_inode); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ciconst struct export_operations affs_export_ops = { 5688c2ecf20Sopenharmony_ci .fh_to_dentry = affs_fh_to_dentry, 5698c2ecf20Sopenharmony_ci .fh_to_parent = affs_fh_to_parent, 5708c2ecf20Sopenharmony_ci .get_parent = affs_get_parent, 5718c2ecf20Sopenharmony_ci}; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ciconst struct dentry_operations affs_dentry_operations = { 5748c2ecf20Sopenharmony_ci .d_hash = affs_hash_dentry, 5758c2ecf20Sopenharmony_ci .d_compare = affs_compare_dentry, 5768c2ecf20Sopenharmony_ci}; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ciconst struct dentry_operations affs_intl_dentry_operations = { 5798c2ecf20Sopenharmony_ci .d_hash = affs_intl_hash_dentry, 5808c2ecf20Sopenharmony_ci .d_compare = affs_intl_compare_dentry, 5818c2ecf20Sopenharmony_ci}; 582