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