18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OMFS (as used by RIO Karma) directory operations. 48c2ecf20Sopenharmony_ci * Copyright (C) 2005 Bob Copeland <me@bobcopeland.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/fs.h> 88c2ecf20Sopenharmony_ci#include <linux/ctype.h> 98c2ecf20Sopenharmony_ci#include <linux/buffer_head.h> 108c2ecf20Sopenharmony_ci#include "omfs.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic int omfs_hash(const char *name, int namelen, int mod) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci int i, hash = 0; 158c2ecf20Sopenharmony_ci for (i = 0; i < namelen; i++) 168c2ecf20Sopenharmony_ci hash ^= tolower(name[i]) << (i % 24); 178c2ecf20Sopenharmony_ci return hash % mod; 188c2ecf20Sopenharmony_ci} 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * Finds the bucket for a given name and reads the containing block; 228c2ecf20Sopenharmony_ci * *ofs is set to the offset of the first list entry. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_cistatic struct buffer_head *omfs_get_bucket(struct inode *dir, 258c2ecf20Sopenharmony_ci const char *name, int namelen, int *ofs) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci int nbuckets = (dir->i_size - OMFS_DIR_START)/8; 288c2ecf20Sopenharmony_ci int bucket = omfs_hash(name, namelen, nbuckets); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci *ofs = OMFS_DIR_START + bucket * 8; 318c2ecf20Sopenharmony_ci return omfs_bread(dir->i_sb, dir->i_ino); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic struct buffer_head *omfs_scan_list(struct inode *dir, u64 block, 358c2ecf20Sopenharmony_ci const char *name, int namelen, 368c2ecf20Sopenharmony_ci u64 *prev_block) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci struct buffer_head *bh; 398c2ecf20Sopenharmony_ci struct omfs_inode *oi; 408c2ecf20Sopenharmony_ci int err = -ENOENT; 418c2ecf20Sopenharmony_ci *prev_block = ~0; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci while (block != ~0) { 448c2ecf20Sopenharmony_ci bh = omfs_bread(dir->i_sb, block); 458c2ecf20Sopenharmony_ci if (!bh) { 468c2ecf20Sopenharmony_ci err = -EIO; 478c2ecf20Sopenharmony_ci goto err; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci oi = (struct omfs_inode *) bh->b_data; 518c2ecf20Sopenharmony_ci if (omfs_is_bad(OMFS_SB(dir->i_sb), &oi->i_head, block)) { 528c2ecf20Sopenharmony_ci brelse(bh); 538c2ecf20Sopenharmony_ci goto err; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (strncmp(oi->i_name, name, namelen) == 0) 578c2ecf20Sopenharmony_ci return bh; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci *prev_block = block; 608c2ecf20Sopenharmony_ci block = be64_to_cpu(oi->i_sibling); 618c2ecf20Sopenharmony_ci brelse(bh); 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_cierr: 648c2ecf20Sopenharmony_ci return ERR_PTR(err); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic struct buffer_head *omfs_find_entry(struct inode *dir, 688c2ecf20Sopenharmony_ci const char *name, int namelen) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct buffer_head *bh; 718c2ecf20Sopenharmony_ci int ofs; 728c2ecf20Sopenharmony_ci u64 block, dummy; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci bh = omfs_get_bucket(dir, name, namelen, &ofs); 758c2ecf20Sopenharmony_ci if (!bh) 768c2ecf20Sopenharmony_ci return ERR_PTR(-EIO); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci block = be64_to_cpu(*((__be64 *) &bh->b_data[ofs])); 798c2ecf20Sopenharmony_ci brelse(bh); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return omfs_scan_list(dir, block, name, namelen, &dummy); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ciint omfs_make_empty(struct inode *inode, struct super_block *sb) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct omfs_sb_info *sbi = OMFS_SB(sb); 878c2ecf20Sopenharmony_ci struct buffer_head *bh; 888c2ecf20Sopenharmony_ci struct omfs_inode *oi; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci bh = omfs_bread(sb, inode->i_ino); 918c2ecf20Sopenharmony_ci if (!bh) 928c2ecf20Sopenharmony_ci return -ENOMEM; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci memset(bh->b_data, 0, sizeof(struct omfs_inode)); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (S_ISDIR(inode->i_mode)) { 978c2ecf20Sopenharmony_ci memset(&bh->b_data[OMFS_DIR_START], 0xff, 988c2ecf20Sopenharmony_ci sbi->s_sys_blocksize - OMFS_DIR_START); 998c2ecf20Sopenharmony_ci } else 1008c2ecf20Sopenharmony_ci omfs_make_empty_table(bh, OMFS_EXTENT_START); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci oi = (struct omfs_inode *) bh->b_data; 1038c2ecf20Sopenharmony_ci oi->i_head.h_self = cpu_to_be64(inode->i_ino); 1048c2ecf20Sopenharmony_ci oi->i_sibling = ~cpu_to_be64(0ULL); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci mark_buffer_dirty(bh); 1078c2ecf20Sopenharmony_ci brelse(bh); 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int omfs_add_link(struct dentry *dentry, struct inode *inode) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct inode *dir = d_inode(dentry->d_parent); 1148c2ecf20Sopenharmony_ci const char *name = dentry->d_name.name; 1158c2ecf20Sopenharmony_ci int namelen = dentry->d_name.len; 1168c2ecf20Sopenharmony_ci struct omfs_inode *oi; 1178c2ecf20Sopenharmony_ci struct buffer_head *bh; 1188c2ecf20Sopenharmony_ci u64 block; 1198c2ecf20Sopenharmony_ci __be64 *entry; 1208c2ecf20Sopenharmony_ci int ofs; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* just prepend to head of queue in proper bucket */ 1238c2ecf20Sopenharmony_ci bh = omfs_get_bucket(dir, name, namelen, &ofs); 1248c2ecf20Sopenharmony_ci if (!bh) 1258c2ecf20Sopenharmony_ci goto out; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci entry = (__be64 *) &bh->b_data[ofs]; 1288c2ecf20Sopenharmony_ci block = be64_to_cpu(*entry); 1298c2ecf20Sopenharmony_ci *entry = cpu_to_be64(inode->i_ino); 1308c2ecf20Sopenharmony_ci mark_buffer_dirty(bh); 1318c2ecf20Sopenharmony_ci brelse(bh); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* now set the sibling and parent pointers on the new inode */ 1348c2ecf20Sopenharmony_ci bh = omfs_bread(dir->i_sb, inode->i_ino); 1358c2ecf20Sopenharmony_ci if (!bh) 1368c2ecf20Sopenharmony_ci goto out; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci oi = (struct omfs_inode *) bh->b_data; 1398c2ecf20Sopenharmony_ci memcpy(oi->i_name, name, namelen); 1408c2ecf20Sopenharmony_ci memset(oi->i_name + namelen, 0, OMFS_NAMELEN - namelen); 1418c2ecf20Sopenharmony_ci oi->i_sibling = cpu_to_be64(block); 1428c2ecf20Sopenharmony_ci oi->i_parent = cpu_to_be64(dir->i_ino); 1438c2ecf20Sopenharmony_ci mark_buffer_dirty(bh); 1448c2ecf20Sopenharmony_ci brelse(bh); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci dir->i_ctime = current_time(dir); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* mark affected inodes dirty to rebuild checksums */ 1498c2ecf20Sopenharmony_ci mark_inode_dirty(dir); 1508c2ecf20Sopenharmony_ci mark_inode_dirty(inode); 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ciout: 1538c2ecf20Sopenharmony_ci return -ENOMEM; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int omfs_delete_entry(struct dentry *dentry) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct inode *dir = d_inode(dentry->d_parent); 1598c2ecf20Sopenharmony_ci struct inode *dirty; 1608c2ecf20Sopenharmony_ci const char *name = dentry->d_name.name; 1618c2ecf20Sopenharmony_ci int namelen = dentry->d_name.len; 1628c2ecf20Sopenharmony_ci struct omfs_inode *oi; 1638c2ecf20Sopenharmony_ci struct buffer_head *bh, *bh2; 1648c2ecf20Sopenharmony_ci __be64 *entry, next; 1658c2ecf20Sopenharmony_ci u64 block, prev; 1668c2ecf20Sopenharmony_ci int ofs; 1678c2ecf20Sopenharmony_ci int err = -ENOMEM; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* delete the proper node in the bucket's linked list */ 1708c2ecf20Sopenharmony_ci bh = omfs_get_bucket(dir, name, namelen, &ofs); 1718c2ecf20Sopenharmony_ci if (!bh) 1728c2ecf20Sopenharmony_ci goto out; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci entry = (__be64 *) &bh->b_data[ofs]; 1758c2ecf20Sopenharmony_ci block = be64_to_cpu(*entry); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci bh2 = omfs_scan_list(dir, block, name, namelen, &prev); 1788c2ecf20Sopenharmony_ci if (IS_ERR(bh2)) { 1798c2ecf20Sopenharmony_ci err = PTR_ERR(bh2); 1808c2ecf20Sopenharmony_ci goto out_free_bh; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci oi = (struct omfs_inode *) bh2->b_data; 1848c2ecf20Sopenharmony_ci next = oi->i_sibling; 1858c2ecf20Sopenharmony_ci brelse(bh2); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (prev != ~0) { 1888c2ecf20Sopenharmony_ci /* found in middle of list, get list ptr */ 1898c2ecf20Sopenharmony_ci brelse(bh); 1908c2ecf20Sopenharmony_ci bh = omfs_bread(dir->i_sb, prev); 1918c2ecf20Sopenharmony_ci if (!bh) 1928c2ecf20Sopenharmony_ci goto out; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci oi = (struct omfs_inode *) bh->b_data; 1958c2ecf20Sopenharmony_ci entry = &oi->i_sibling; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci *entry = next; 1998c2ecf20Sopenharmony_ci mark_buffer_dirty(bh); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (prev != ~0) { 2028c2ecf20Sopenharmony_ci dirty = omfs_iget(dir->i_sb, prev); 2038c2ecf20Sopenharmony_ci if (!IS_ERR(dirty)) { 2048c2ecf20Sopenharmony_ci mark_inode_dirty(dirty); 2058c2ecf20Sopenharmony_ci iput(dirty); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci err = 0; 2108c2ecf20Sopenharmony_ciout_free_bh: 2118c2ecf20Sopenharmony_ci brelse(bh); 2128c2ecf20Sopenharmony_ciout: 2138c2ecf20Sopenharmony_ci return err; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int omfs_dir_is_empty(struct inode *inode) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci int nbuckets = (inode->i_size - OMFS_DIR_START) / 8; 2198c2ecf20Sopenharmony_ci struct buffer_head *bh; 2208c2ecf20Sopenharmony_ci u64 *ptr; 2218c2ecf20Sopenharmony_ci int i; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci bh = omfs_bread(inode->i_sb, inode->i_ino); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (!bh) 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci ptr = (u64 *) &bh->b_data[OMFS_DIR_START]; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci for (i = 0; i < nbuckets; i++, ptr++) 2318c2ecf20Sopenharmony_ci if (*ptr != ~0) 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci brelse(bh); 2358c2ecf20Sopenharmony_ci return *ptr != ~0; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int omfs_remove(struct inode *dir, struct dentry *dentry) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct inode *inode = d_inode(dentry); 2418c2ecf20Sopenharmony_ci int ret; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (S_ISDIR(inode->i_mode) && 2458c2ecf20Sopenharmony_ci !omfs_dir_is_empty(inode)) 2468c2ecf20Sopenharmony_ci return -ENOTEMPTY; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ret = omfs_delete_entry(dentry); 2498c2ecf20Sopenharmony_ci if (ret) 2508c2ecf20Sopenharmony_ci return ret; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci clear_nlink(inode); 2538c2ecf20Sopenharmony_ci mark_inode_dirty(inode); 2548c2ecf20Sopenharmony_ci mark_inode_dirty(dir); 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int omfs_add_node(struct inode *dir, struct dentry *dentry, umode_t mode) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci int err; 2618c2ecf20Sopenharmony_ci struct inode *inode = omfs_new_inode(dir, mode); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (IS_ERR(inode)) 2648c2ecf20Sopenharmony_ci return PTR_ERR(inode); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci err = omfs_make_empty(inode, dir->i_sb); 2678c2ecf20Sopenharmony_ci if (err) 2688c2ecf20Sopenharmony_ci goto out_free_inode; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci err = omfs_add_link(dentry, inode); 2718c2ecf20Sopenharmony_ci if (err) 2728c2ecf20Sopenharmony_ci goto out_free_inode; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci d_instantiate(dentry, inode); 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ciout_free_inode: 2788c2ecf20Sopenharmony_ci iput(inode); 2798c2ecf20Sopenharmony_ci return err; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int omfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci return omfs_add_node(dir, dentry, mode | S_IFDIR); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int omfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, 2888c2ecf20Sopenharmony_ci bool excl) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci return omfs_add_node(dir, dentry, mode | S_IFREG); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic struct dentry *omfs_lookup(struct inode *dir, struct dentry *dentry, 2948c2ecf20Sopenharmony_ci unsigned int flags) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct buffer_head *bh; 2978c2ecf20Sopenharmony_ci struct inode *inode = NULL; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (dentry->d_name.len > OMFS_NAMELEN) 3008c2ecf20Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci bh = omfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len); 3038c2ecf20Sopenharmony_ci if (!IS_ERR(bh)) { 3048c2ecf20Sopenharmony_ci struct omfs_inode *oi = (struct omfs_inode *)bh->b_data; 3058c2ecf20Sopenharmony_ci ino_t ino = be64_to_cpu(oi->i_head.h_self); 3068c2ecf20Sopenharmony_ci brelse(bh); 3078c2ecf20Sopenharmony_ci inode = omfs_iget(dir->i_sb, ino); 3088c2ecf20Sopenharmony_ci } else if (bh != ERR_PTR(-ENOENT)) { 3098c2ecf20Sopenharmony_ci inode = ERR_CAST(bh); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci return d_splice_alias(inode, dentry); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* sanity check block's self pointer */ 3158c2ecf20Sopenharmony_ciint omfs_is_bad(struct omfs_sb_info *sbi, struct omfs_header *header, 3168c2ecf20Sopenharmony_ci u64 fsblock) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci int is_bad; 3198c2ecf20Sopenharmony_ci u64 ino = be64_to_cpu(header->h_self); 3208c2ecf20Sopenharmony_ci is_bad = ((ino != fsblock) || (ino < sbi->s_root_ino) || 3218c2ecf20Sopenharmony_ci (ino > sbi->s_num_blocks)); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (is_bad) 3248c2ecf20Sopenharmony_ci printk(KERN_WARNING "omfs: bad hash chain detected\n"); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return is_bad; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic bool omfs_fill_chain(struct inode *dir, struct dir_context *ctx, 3308c2ecf20Sopenharmony_ci u64 fsblock, int hindex) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci /* follow chain in this bucket */ 3338c2ecf20Sopenharmony_ci while (fsblock != ~0) { 3348c2ecf20Sopenharmony_ci struct buffer_head *bh = omfs_bread(dir->i_sb, fsblock); 3358c2ecf20Sopenharmony_ci struct omfs_inode *oi; 3368c2ecf20Sopenharmony_ci u64 self; 3378c2ecf20Sopenharmony_ci unsigned char d_type; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (!bh) 3408c2ecf20Sopenharmony_ci return true; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci oi = (struct omfs_inode *) bh->b_data; 3438c2ecf20Sopenharmony_ci if (omfs_is_bad(OMFS_SB(dir->i_sb), &oi->i_head, fsblock)) { 3448c2ecf20Sopenharmony_ci brelse(bh); 3458c2ecf20Sopenharmony_ci return true; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci self = fsblock; 3498c2ecf20Sopenharmony_ci fsblock = be64_to_cpu(oi->i_sibling); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* skip visited nodes */ 3528c2ecf20Sopenharmony_ci if (hindex) { 3538c2ecf20Sopenharmony_ci hindex--; 3548c2ecf20Sopenharmony_ci brelse(bh); 3558c2ecf20Sopenharmony_ci continue; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci d_type = (oi->i_type == OMFS_DIR) ? DT_DIR : DT_REG; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (!dir_emit(ctx, oi->i_name, 3618c2ecf20Sopenharmony_ci strnlen(oi->i_name, OMFS_NAMELEN), 3628c2ecf20Sopenharmony_ci self, d_type)) { 3638c2ecf20Sopenharmony_ci brelse(bh); 3648c2ecf20Sopenharmony_ci return false; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci brelse(bh); 3678c2ecf20Sopenharmony_ci ctx->pos++; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci return true; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic int omfs_rename(struct inode *old_dir, struct dentry *old_dentry, 3738c2ecf20Sopenharmony_ci struct inode *new_dir, struct dentry *new_dentry, 3748c2ecf20Sopenharmony_ci unsigned int flags) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct inode *new_inode = d_inode(new_dentry); 3778c2ecf20Sopenharmony_ci struct inode *old_inode = d_inode(old_dentry); 3788c2ecf20Sopenharmony_ci int err; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (flags & ~RENAME_NOREPLACE) 3818c2ecf20Sopenharmony_ci return -EINVAL; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (new_inode) { 3848c2ecf20Sopenharmony_ci /* overwriting existing file/dir */ 3858c2ecf20Sopenharmony_ci err = omfs_remove(new_dir, new_dentry); 3868c2ecf20Sopenharmony_ci if (err) 3878c2ecf20Sopenharmony_ci goto out; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* since omfs locates files by name, we need to unlink _before_ 3918c2ecf20Sopenharmony_ci * adding the new link or we won't find the old one */ 3928c2ecf20Sopenharmony_ci err = omfs_delete_entry(old_dentry); 3938c2ecf20Sopenharmony_ci if (err) 3948c2ecf20Sopenharmony_ci goto out; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci mark_inode_dirty(old_dir); 3978c2ecf20Sopenharmony_ci err = omfs_add_link(new_dentry, old_inode); 3988c2ecf20Sopenharmony_ci if (err) 3998c2ecf20Sopenharmony_ci goto out; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci old_inode->i_ctime = current_time(old_inode); 4028c2ecf20Sopenharmony_ci mark_inode_dirty(old_inode); 4038c2ecf20Sopenharmony_ciout: 4048c2ecf20Sopenharmony_ci return err; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int omfs_readdir(struct file *file, struct dir_context *ctx) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct inode *dir = file_inode(file); 4108c2ecf20Sopenharmony_ci struct buffer_head *bh; 4118c2ecf20Sopenharmony_ci __be64 *p; 4128c2ecf20Sopenharmony_ci unsigned int hchain, hindex; 4138c2ecf20Sopenharmony_ci int nbuckets; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (ctx->pos >> 32) 4168c2ecf20Sopenharmony_ci return -EINVAL; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (ctx->pos < 1 << 20) { 4198c2ecf20Sopenharmony_ci if (!dir_emit_dots(file, ctx)) 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci ctx->pos = 1 << 20; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci nbuckets = (dir->i_size - OMFS_DIR_START) / 8; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* high 12 bits store bucket + 1 and low 20 bits store hash index */ 4278c2ecf20Sopenharmony_ci hchain = (ctx->pos >> 20) - 1; 4288c2ecf20Sopenharmony_ci hindex = ctx->pos & 0xfffff; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci bh = omfs_bread(dir->i_sb, dir->i_ino); 4318c2ecf20Sopenharmony_ci if (!bh) 4328c2ecf20Sopenharmony_ci return -EINVAL; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci p = (__be64 *)(bh->b_data + OMFS_DIR_START) + hchain; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci for (; hchain < nbuckets; hchain++) { 4378c2ecf20Sopenharmony_ci __u64 fsblock = be64_to_cpu(*p++); 4388c2ecf20Sopenharmony_ci if (!omfs_fill_chain(dir, ctx, fsblock, hindex)) 4398c2ecf20Sopenharmony_ci break; 4408c2ecf20Sopenharmony_ci hindex = 0; 4418c2ecf20Sopenharmony_ci ctx->pos = (hchain+2) << 20; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci brelse(bh); 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ciconst struct inode_operations omfs_dir_inops = { 4488c2ecf20Sopenharmony_ci .lookup = omfs_lookup, 4498c2ecf20Sopenharmony_ci .mkdir = omfs_mkdir, 4508c2ecf20Sopenharmony_ci .rename = omfs_rename, 4518c2ecf20Sopenharmony_ci .create = omfs_create, 4528c2ecf20Sopenharmony_ci .unlink = omfs_remove, 4538c2ecf20Sopenharmony_ci .rmdir = omfs_remove, 4548c2ecf20Sopenharmony_ci}; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ciconst struct file_operations omfs_dir_operations = { 4578c2ecf20Sopenharmony_ci .read = generic_read_dir, 4588c2ecf20Sopenharmony_ci .iterate_shared = omfs_readdir, 4598c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 4608c2ecf20Sopenharmony_ci}; 461