18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/adfs/dir.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1999-2000 Russell King 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Common directory handling for ADFS 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include "adfs.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* 138c2ecf20Sopenharmony_ci * For future. This should probably be per-directory. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(adfs_dir_rwsem); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ciint adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset, 188c2ecf20Sopenharmony_ci size_t len) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci struct super_block *sb = dir->sb; 218c2ecf20Sopenharmony_ci unsigned int index, remain; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci index = offset >> sb->s_blocksize_bits; 248c2ecf20Sopenharmony_ci offset &= sb->s_blocksize - 1; 258c2ecf20Sopenharmony_ci remain = sb->s_blocksize - offset; 268c2ecf20Sopenharmony_ci if (index + (remain < len) >= dir->nr_buffers) 278c2ecf20Sopenharmony_ci return -EINVAL; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci if (remain < len) { 308c2ecf20Sopenharmony_ci memcpy(dst, dir->bhs[index]->b_data + offset, remain); 318c2ecf20Sopenharmony_ci dst += remain; 328c2ecf20Sopenharmony_ci len -= remain; 338c2ecf20Sopenharmony_ci index += 1; 348c2ecf20Sopenharmony_ci offset = 0; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci memcpy(dst, dir->bhs[index]->b_data + offset, len); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return 0; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ciint adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src, 438c2ecf20Sopenharmony_ci size_t len) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct super_block *sb = dir->sb; 468c2ecf20Sopenharmony_ci unsigned int index, remain; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci index = offset >> sb->s_blocksize_bits; 498c2ecf20Sopenharmony_ci offset &= sb->s_blocksize - 1; 508c2ecf20Sopenharmony_ci remain = sb->s_blocksize - offset; 518c2ecf20Sopenharmony_ci if (index + (remain < len) >= dir->nr_buffers) 528c2ecf20Sopenharmony_ci return -EINVAL; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (remain < len) { 558c2ecf20Sopenharmony_ci memcpy(dir->bhs[index]->b_data + offset, src, remain); 568c2ecf20Sopenharmony_ci src += remain; 578c2ecf20Sopenharmony_ci len -= remain; 588c2ecf20Sopenharmony_ci index += 1; 598c2ecf20Sopenharmony_ci offset = 0; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci memcpy(dir->bhs[index]->b_data + offset, src, len); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return 0; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void __adfs_dir_cleanup(struct adfs_dir *dir) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci dir->nr_buffers = 0; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (dir->bhs != dir->bh) 728c2ecf20Sopenharmony_ci kfree(dir->bhs); 738c2ecf20Sopenharmony_ci dir->bhs = NULL; 748c2ecf20Sopenharmony_ci dir->sb = NULL; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_civoid adfs_dir_relse(struct adfs_dir *dir) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci unsigned int i; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci for (i = 0; i < dir->nr_buffers; i++) 828c2ecf20Sopenharmony_ci brelse(dir->bhs[i]); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci __adfs_dir_cleanup(dir); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void adfs_dir_forget(struct adfs_dir *dir) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci unsigned int i; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci for (i = 0; i < dir->nr_buffers; i++) 928c2ecf20Sopenharmony_ci bforget(dir->bhs[i]); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci __adfs_dir_cleanup(dir); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ciint adfs_dir_read_buffers(struct super_block *sb, u32 indaddr, 988c2ecf20Sopenharmony_ci unsigned int size, struct adfs_dir *dir) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct buffer_head **bhs; 1018c2ecf20Sopenharmony_ci unsigned int i, num; 1028c2ecf20Sopenharmony_ci int block; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits; 1058c2ecf20Sopenharmony_ci if (num > ARRAY_SIZE(dir->bh)) { 1068c2ecf20Sopenharmony_ci /* We only allow one extension */ 1078c2ecf20Sopenharmony_ci if (dir->bhs != dir->bh) 1088c2ecf20Sopenharmony_ci return -EINVAL; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL); 1118c2ecf20Sopenharmony_ci if (!bhs) 1128c2ecf20Sopenharmony_ci return -ENOMEM; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (dir->nr_buffers) 1158c2ecf20Sopenharmony_ci memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs)); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci dir->bhs = bhs; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci for (i = dir->nr_buffers; i < num; i++) { 1218c2ecf20Sopenharmony_ci block = __adfs_block_map(sb, indaddr, i); 1228c2ecf20Sopenharmony_ci if (!block) { 1238c2ecf20Sopenharmony_ci adfs_error(sb, "dir %06x has a hole at offset %u", 1248c2ecf20Sopenharmony_ci indaddr, i); 1258c2ecf20Sopenharmony_ci goto error; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci dir->bhs[i] = sb_bread(sb, block); 1298c2ecf20Sopenharmony_ci if (!dir->bhs[i]) { 1308c2ecf20Sopenharmony_ci adfs_error(sb, 1318c2ecf20Sopenharmony_ci "dir %06x failed read at offset %u, mapped block 0x%08x", 1328c2ecf20Sopenharmony_ci indaddr, i, block); 1338c2ecf20Sopenharmony_ci goto error; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci dir->nr_buffers++; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cierror: 1418c2ecf20Sopenharmony_ci adfs_dir_relse(dir); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return -EIO; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int adfs_dir_read(struct super_block *sb, u32 indaddr, 1478c2ecf20Sopenharmony_ci unsigned int size, struct adfs_dir *dir) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci dir->sb = sb; 1508c2ecf20Sopenharmony_ci dir->bhs = dir->bh; 1518c2ecf20Sopenharmony_ci dir->nr_buffers = 0; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int adfs_dir_read_inode(struct super_block *sb, struct inode *inode, 1578c2ecf20Sopenharmony_ci struct adfs_dir *dir) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int ret; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir); 1628c2ecf20Sopenharmony_ci if (ret) 1638c2ecf20Sopenharmony_ci return ret; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (ADFS_I(inode)->parent_id != dir->parent_id) { 1668c2ecf20Sopenharmony_ci adfs_error(sb, 1678c2ecf20Sopenharmony_ci "parent directory id changed under me! (%06x but got %06x)\n", 1688c2ecf20Sopenharmony_ci ADFS_I(inode)->parent_id, dir->parent_id); 1698c2ecf20Sopenharmony_ci adfs_dir_relse(dir); 1708c2ecf20Sopenharmony_ci ret = -EIO; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void adfs_dir_mark_dirty(struct adfs_dir *dir) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci unsigned int i; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Mark the buffers dirty */ 1818c2ecf20Sopenharmony_ci for (i = 0; i < dir->nr_buffers; i++) 1828c2ecf20Sopenharmony_ci mark_buffer_dirty(dir->bhs[i]); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int adfs_dir_sync(struct adfs_dir *dir) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci int err = 0; 1888c2ecf20Sopenharmony_ci int i; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci for (i = dir->nr_buffers - 1; i >= 0; i--) { 1918c2ecf20Sopenharmony_ci struct buffer_head *bh = dir->bhs[i]; 1928c2ecf20Sopenharmony_ci sync_dirty_buffer(bh); 1938c2ecf20Sopenharmony_ci if (buffer_req(bh) && !buffer_uptodate(bh)) 1948c2ecf20Sopenharmony_ci err = -EIO; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return err; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_civoid adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci unsigned int dots, i; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* 2058c2ecf20Sopenharmony_ci * RISC OS allows the use of '/' in directory entry names, so we need 2068c2ecf20Sopenharmony_ci * to fix these up. '/' is typically used for FAT compatibility to 2078c2ecf20Sopenharmony_ci * represent '.', so do the same conversion here. In any case, '.' 2088c2ecf20Sopenharmony_ci * will never be in a RISC OS name since it is used as the pathname 2098c2ecf20Sopenharmony_ci * separator. Handle the case where we may generate a '.' or '..' 2108c2ecf20Sopenharmony_ci * name, replacing the first character with '^' (the RISC OS "parent 2118c2ecf20Sopenharmony_ci * directory" character.) 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci for (i = dots = 0; i < obj->name_len; i++) 2148c2ecf20Sopenharmony_ci if (obj->name[i] == '/') { 2158c2ecf20Sopenharmony_ci obj->name[i] = '.'; 2168c2ecf20Sopenharmony_ci dots++; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (obj->name_len <= 2 && dots == obj->name_len) 2208c2ecf20Sopenharmony_ci obj->name[0] = '^'; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* 2238c2ecf20Sopenharmony_ci * If the object is a file, and the user requested the ,xyz hex 2248c2ecf20Sopenharmony_ci * filetype suffix to the name, check the filetype and append. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_ci if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) { 2278c2ecf20Sopenharmony_ci u16 filetype = adfs_filetype(obj->loadaddr); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (filetype != ADFS_FILETYPE_NONE) { 2308c2ecf20Sopenharmony_ci obj->name[obj->name_len++] = ','; 2318c2ecf20Sopenharmony_ci obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8); 2328c2ecf20Sopenharmony_ci obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4); 2338c2ecf20Sopenharmony_ci obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int adfs_iterate(struct file *file, struct dir_context *ctx) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 2418c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 2428c2ecf20Sopenharmony_ci const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 2438c2ecf20Sopenharmony_ci struct adfs_dir dir; 2448c2ecf20Sopenharmony_ci int ret; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci down_read(&adfs_dir_rwsem); 2478c2ecf20Sopenharmony_ci ret = adfs_dir_read_inode(sb, inode, &dir); 2488c2ecf20Sopenharmony_ci if (ret) 2498c2ecf20Sopenharmony_ci goto unlock; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (ctx->pos == 0) { 2528c2ecf20Sopenharmony_ci if (!dir_emit_dot(file, ctx)) 2538c2ecf20Sopenharmony_ci goto unlock_relse; 2548c2ecf20Sopenharmony_ci ctx->pos = 1; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci if (ctx->pos == 1) { 2578c2ecf20Sopenharmony_ci if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR)) 2588c2ecf20Sopenharmony_ci goto unlock_relse; 2598c2ecf20Sopenharmony_ci ctx->pos = 2; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ret = ops->iterate(&dir, ctx); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ciunlock_relse: 2658c2ecf20Sopenharmony_ci up_read(&adfs_dir_rwsem); 2668c2ecf20Sopenharmony_ci adfs_dir_relse(&dir); 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ciunlock: 2708c2ecf20Sopenharmony_ci up_read(&adfs_dir_rwsem); 2718c2ecf20Sopenharmony_ci return ret; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ciint 2758c2ecf20Sopenharmony_ciadfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 2788c2ecf20Sopenharmony_ci struct adfs_dir dir; 2798c2ecf20Sopenharmony_ci int ret; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_ADFS_FS_RW)) 2828c2ecf20Sopenharmony_ci return -EINVAL; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (!ops->update) 2858c2ecf20Sopenharmony_ci return -EINVAL; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci down_write(&adfs_dir_rwsem); 2888c2ecf20Sopenharmony_ci ret = adfs_dir_read(sb, obj->parent_id, 0, &dir); 2898c2ecf20Sopenharmony_ci if (ret) 2908c2ecf20Sopenharmony_ci goto unlock; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci ret = ops->update(&dir, obj); 2938c2ecf20Sopenharmony_ci if (ret) 2948c2ecf20Sopenharmony_ci goto forget; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci ret = ops->commit(&dir); 2978c2ecf20Sopenharmony_ci if (ret) 2988c2ecf20Sopenharmony_ci goto forget; 2998c2ecf20Sopenharmony_ci up_write(&adfs_dir_rwsem); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci adfs_dir_mark_dirty(&dir); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (wait) 3048c2ecf20Sopenharmony_ci ret = adfs_dir_sync(&dir); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci adfs_dir_relse(&dir); 3078c2ecf20Sopenharmony_ci return ret; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* 3108c2ecf20Sopenharmony_ci * If the updated failed because the entry wasn't found, we can 3118c2ecf20Sopenharmony_ci * just release the buffers. If it was any other error, forget 3128c2ecf20Sopenharmony_ci * the dirtied buffers so they aren't written back to the media. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ciforget: 3158c2ecf20Sopenharmony_ci if (ret == -ENOENT) 3168c2ecf20Sopenharmony_ci adfs_dir_relse(&dir); 3178c2ecf20Sopenharmony_ci else 3188c2ecf20Sopenharmony_ci adfs_dir_forget(&dir); 3198c2ecf20Sopenharmony_ciunlock: 3208c2ecf20Sopenharmony_ci up_write(&adfs_dir_rwsem); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return ret; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic unsigned char adfs_tolower(unsigned char c) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci if (c >= 'A' && c <= 'Z') 3288c2ecf20Sopenharmony_ci c += 'a' - 'A'; 3298c2ecf20Sopenharmony_ci return c; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int __adfs_compare(const unsigned char *qstr, u32 qlen, 3338c2ecf20Sopenharmony_ci const char *str, u32 len) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci u32 i; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (qlen != len) 3388c2ecf20Sopenharmony_ci return 1; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci for (i = 0; i < qlen; i++) 3418c2ecf20Sopenharmony_ci if (adfs_tolower(qstr[i]) != adfs_tolower(str[i])) 3428c2ecf20Sopenharmony_ci return 1; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr, 3488c2ecf20Sopenharmony_ci struct object_info *obj) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 3518c2ecf20Sopenharmony_ci const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 3528c2ecf20Sopenharmony_ci const unsigned char *name; 3538c2ecf20Sopenharmony_ci struct adfs_dir dir; 3548c2ecf20Sopenharmony_ci u32 name_len; 3558c2ecf20Sopenharmony_ci int ret; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci down_read(&adfs_dir_rwsem); 3588c2ecf20Sopenharmony_ci ret = adfs_dir_read_inode(sb, inode, &dir); 3598c2ecf20Sopenharmony_ci if (ret) 3608c2ecf20Sopenharmony_ci goto unlock; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ret = ops->setpos(&dir, 0); 3638c2ecf20Sopenharmony_ci if (ret) 3648c2ecf20Sopenharmony_ci goto unlock_relse; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = -ENOENT; 3678c2ecf20Sopenharmony_ci name = qstr->name; 3688c2ecf20Sopenharmony_ci name_len = qstr->len; 3698c2ecf20Sopenharmony_ci while (ops->getnext(&dir, obj) == 0) { 3708c2ecf20Sopenharmony_ci if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) { 3718c2ecf20Sopenharmony_ci ret = 0; 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci obj->parent_id = ADFS_I(inode)->indaddr; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ciunlock_relse: 3788c2ecf20Sopenharmony_ci up_read(&adfs_dir_rwsem); 3798c2ecf20Sopenharmony_ci adfs_dir_relse(&dir); 3808c2ecf20Sopenharmony_ci return ret; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ciunlock: 3838c2ecf20Sopenharmony_ci up_read(&adfs_dir_rwsem); 3848c2ecf20Sopenharmony_ci return ret; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ciconst struct file_operations adfs_dir_operations = { 3888c2ecf20Sopenharmony_ci .read = generic_read_dir, 3898c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 3908c2ecf20Sopenharmony_ci .iterate_shared = adfs_iterate, 3918c2ecf20Sopenharmony_ci .fsync = generic_file_fsync, 3928c2ecf20Sopenharmony_ci}; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int 3958c2ecf20Sopenharmony_ciadfs_hash(const struct dentry *parent, struct qstr *qstr) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci const unsigned char *name; 3988c2ecf20Sopenharmony_ci unsigned long hash; 3998c2ecf20Sopenharmony_ci u32 len; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen) 4028c2ecf20Sopenharmony_ci return -ENAMETOOLONG; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci len = qstr->len; 4058c2ecf20Sopenharmony_ci name = qstr->name; 4068c2ecf20Sopenharmony_ci hash = init_name_hash(parent); 4078c2ecf20Sopenharmony_ci while (len--) 4088c2ecf20Sopenharmony_ci hash = partial_name_hash(adfs_tolower(*name++), hash); 4098c2ecf20Sopenharmony_ci qstr->hash = end_name_hash(hash); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/* 4158c2ecf20Sopenharmony_ci * Compare two names, taking note of the name length 4168c2ecf20Sopenharmony_ci * requirements of the underlying filesystem. 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_cistatic int adfs_compare(const struct dentry *dentry, unsigned int len, 4198c2ecf20Sopenharmony_ci const char *str, const struct qstr *qstr) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci return __adfs_compare(qstr->name, qstr->len, str, len); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ciconst struct dentry_operations adfs_dentry_operations = { 4258c2ecf20Sopenharmony_ci .d_hash = adfs_hash, 4268c2ecf20Sopenharmony_ci .d_compare = adfs_compare, 4278c2ecf20Sopenharmony_ci}; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic struct dentry * 4308c2ecf20Sopenharmony_ciadfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct inode *inode = NULL; 4338c2ecf20Sopenharmony_ci struct object_info obj; 4348c2ecf20Sopenharmony_ci int error; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj); 4378c2ecf20Sopenharmony_ci if (error == 0) { 4388c2ecf20Sopenharmony_ci /* 4398c2ecf20Sopenharmony_ci * This only returns NULL if get_empty_inode 4408c2ecf20Sopenharmony_ci * fails. 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_ci inode = adfs_iget(dir->i_sb, &obj); 4438c2ecf20Sopenharmony_ci if (!inode) 4448c2ecf20Sopenharmony_ci inode = ERR_PTR(-EACCES); 4458c2ecf20Sopenharmony_ci } else if (error != -ENOENT) { 4468c2ecf20Sopenharmony_ci inode = ERR_PTR(error); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci return d_splice_alias(inode, dentry); 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci/* 4528c2ecf20Sopenharmony_ci * directories can handle most operations... 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ciconst struct inode_operations adfs_dir_inode_operations = { 4558c2ecf20Sopenharmony_ci .lookup = adfs_lookup, 4568c2ecf20Sopenharmony_ci .setattr = adfs_notify_change, 4578c2ecf20Sopenharmony_ci}; 458