| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * OMFS (as used by RIO Karma) directory operations. | 
					
						
							|  |  |  |  * Copyright (C) 2005 Bob Copeland <me@bobcopeland.com> | 
					
						
							|  |  |  |  * Released under GPL v2. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/fs.h>
 | 
					
						
							|  |  |  | #include <linux/ctype.h>
 | 
					
						
							|  |  |  | #include <linux/buffer_head.h>
 | 
					
						
							|  |  |  | #include "omfs.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int omfs_hash(const char *name, int namelen, int mod) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i, hash = 0; | 
					
						
							|  |  |  | 	for (i = 0; i < namelen; i++) | 
					
						
							|  |  |  | 		hash ^= tolower(name[i]) << (i % 24); | 
					
						
							|  |  |  | 	return hash % mod; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Finds the bucket for a given name and reads the containing block; | 
					
						
							|  |  |  |  * *ofs is set to the offset of the first list entry. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static struct buffer_head *omfs_get_bucket(struct inode *dir, | 
					
						
							|  |  |  | 		const char *name, int namelen, int *ofs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int nbuckets = (dir->i_size - OMFS_DIR_START)/8; | 
					
						
							|  |  |  | 	int bucket = omfs_hash(name, namelen, nbuckets); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*ofs = OMFS_DIR_START + bucket * 8; | 
					
						
							| 
									
										
										
										
											2008-09-06 17:51:53 -04:00
										 |  |  | 	return omfs_bread(dir->i_sb, dir->i_ino); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct buffer_head *omfs_scan_list(struct inode *dir, u64 block, | 
					
						
							|  |  |  | 				const char *name, int namelen, | 
					
						
							|  |  |  | 				u64 *prev_block) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct buffer_head *bh; | 
					
						
							|  |  |  | 	struct omfs_inode *oi; | 
					
						
							|  |  |  | 	int err = -ENOENT; | 
					
						
							|  |  |  | 	*prev_block = ~0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (block != ~0) { | 
					
						
							| 
									
										
										
										
											2008-09-06 17:51:53 -04:00
										 |  |  | 		bh = omfs_bread(dir->i_sb, block); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 		if (!bh) { | 
					
						
							|  |  |  | 			err = -EIO; | 
					
						
							|  |  |  | 			goto err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		oi = (struct omfs_inode *) bh->b_data; | 
					
						
							|  |  |  | 		if (omfs_is_bad(OMFS_SB(dir->i_sb), &oi->i_head, block)) { | 
					
						
							|  |  |  | 			brelse(bh); | 
					
						
							|  |  |  | 			goto err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (strncmp(oi->i_name, name, namelen) == 0) | 
					
						
							|  |  |  | 			return bh; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		*prev_block = block; | 
					
						
							|  |  |  | 		block = be64_to_cpu(oi->i_sibling); | 
					
						
							|  |  |  | 		brelse(bh); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | err: | 
					
						
							|  |  |  | 	return ERR_PTR(err); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct buffer_head *omfs_find_entry(struct inode *dir, | 
					
						
							|  |  |  | 					   const char *name, int namelen) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct buffer_head *bh; | 
					
						
							|  |  |  | 	int ofs; | 
					
						
							|  |  |  | 	u64 block, dummy; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bh = omfs_get_bucket(dir, name, namelen, &ofs); | 
					
						
							|  |  |  | 	if (!bh) | 
					
						
							|  |  |  | 		return ERR_PTR(-EIO); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	block = be64_to_cpu(*((__be64 *) &bh->b_data[ofs])); | 
					
						
							|  |  |  | 	brelse(bh); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return omfs_scan_list(dir, block, name, namelen, &dummy); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int omfs_make_empty(struct inode *inode, struct super_block *sb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct omfs_sb_info *sbi = OMFS_SB(sb); | 
					
						
							|  |  |  | 	struct buffer_head *bh; | 
					
						
							|  |  |  | 	struct omfs_inode *oi; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-06 17:51:53 -04:00
										 |  |  | 	bh = omfs_bread(sb, inode->i_ino); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 	if (!bh) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memset(bh->b_data, 0, sizeof(struct omfs_inode)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-26 02:34:33 -04:00
										 |  |  | 	if (S_ISDIR(inode->i_mode)) { | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 		memset(&bh->b_data[OMFS_DIR_START], 0xff, | 
					
						
							|  |  |  | 			sbi->s_sys_blocksize - OMFS_DIR_START); | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		omfs_make_empty_table(bh, OMFS_EXTENT_START); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	oi = (struct omfs_inode *) bh->b_data; | 
					
						
							|  |  |  | 	oi->i_head.h_self = cpu_to_be64(inode->i_ino); | 
					
						
							| 
									
										
										
										
											2008-07-29 22:33:46 -07:00
										 |  |  | 	oi->i_sibling = ~cpu_to_be64(0ULL); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	mark_buffer_dirty(bh); | 
					
						
							|  |  |  | 	brelse(bh); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int omfs_add_link(struct dentry *dentry, struct inode *inode) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct inode *dir = dentry->d_parent->d_inode; | 
					
						
							|  |  |  | 	const char *name = dentry->d_name.name; | 
					
						
							|  |  |  | 	int namelen = dentry->d_name.len; | 
					
						
							|  |  |  | 	struct omfs_inode *oi; | 
					
						
							|  |  |  | 	struct buffer_head *bh; | 
					
						
							|  |  |  | 	u64 block; | 
					
						
							|  |  |  | 	__be64 *entry; | 
					
						
							|  |  |  | 	int ofs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* just prepend to head of queue in proper bucket */ | 
					
						
							|  |  |  | 	bh = omfs_get_bucket(dir, name, namelen, &ofs); | 
					
						
							|  |  |  | 	if (!bh) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	entry = (__be64 *) &bh->b_data[ofs]; | 
					
						
							|  |  |  | 	block = be64_to_cpu(*entry); | 
					
						
							|  |  |  | 	*entry = cpu_to_be64(inode->i_ino); | 
					
						
							|  |  |  | 	mark_buffer_dirty(bh); | 
					
						
							|  |  |  | 	brelse(bh); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* now set the sibling and parent pointers on the new inode */ | 
					
						
							| 
									
										
										
										
											2008-09-06 17:51:53 -04:00
										 |  |  | 	bh = omfs_bread(dir->i_sb, inode->i_ino); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 	if (!bh) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	oi = (struct omfs_inode *) bh->b_data; | 
					
						
							|  |  |  | 	memcpy(oi->i_name, name, namelen); | 
					
						
							|  |  |  | 	memset(oi->i_name + namelen, 0, OMFS_NAMELEN - namelen); | 
					
						
							|  |  |  | 	oi->i_sibling = cpu_to_be64(block); | 
					
						
							|  |  |  | 	oi->i_parent = cpu_to_be64(dir->i_ino); | 
					
						
							|  |  |  | 	mark_buffer_dirty(bh); | 
					
						
							|  |  |  | 	brelse(bh); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dir->i_ctime = CURRENT_TIME_SEC; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* mark affected inodes dirty to rebuild checksums */ | 
					
						
							|  |  |  | 	mark_inode_dirty(dir); | 
					
						
							|  |  |  | 	mark_inode_dirty(inode); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return -ENOMEM; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int omfs_delete_entry(struct dentry *dentry) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct inode *dir = dentry->d_parent->d_inode; | 
					
						
							|  |  |  | 	struct inode *dirty; | 
					
						
							|  |  |  | 	const char *name = dentry->d_name.name; | 
					
						
							|  |  |  | 	int namelen = dentry->d_name.len; | 
					
						
							|  |  |  | 	struct omfs_inode *oi; | 
					
						
							|  |  |  | 	struct buffer_head *bh, *bh2; | 
					
						
							|  |  |  | 	__be64 *entry, next; | 
					
						
							|  |  |  | 	u64 block, prev; | 
					
						
							|  |  |  | 	int ofs; | 
					
						
							|  |  |  | 	int err = -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* delete the proper node in the bucket's linked list */ | 
					
						
							|  |  |  | 	bh = omfs_get_bucket(dir, name, namelen, &ofs); | 
					
						
							|  |  |  | 	if (!bh) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	entry = (__be64 *) &bh->b_data[ofs]; | 
					
						
							|  |  |  | 	block = be64_to_cpu(*entry); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bh2 = omfs_scan_list(dir, block, name, namelen, &prev); | 
					
						
							|  |  |  | 	if (IS_ERR(bh2)) { | 
					
						
							|  |  |  | 		err = PTR_ERR(bh2); | 
					
						
							|  |  |  | 		goto out_free_bh; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	oi = (struct omfs_inode *) bh2->b_data; | 
					
						
							|  |  |  | 	next = oi->i_sibling; | 
					
						
							|  |  |  | 	brelse(bh2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (prev != ~0) { | 
					
						
							|  |  |  | 		/* found in middle of list, get list ptr */ | 
					
						
							|  |  |  | 		brelse(bh); | 
					
						
							| 
									
										
										
										
											2008-09-06 17:51:53 -04:00
										 |  |  | 		bh = omfs_bread(dir->i_sb, prev); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 		if (!bh) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		oi = (struct omfs_inode *) bh->b_data; | 
					
						
							|  |  |  | 		entry = &oi->i_sibling; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*entry = next; | 
					
						
							|  |  |  | 	mark_buffer_dirty(bh); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (prev != ~0) { | 
					
						
							|  |  |  | 		dirty = omfs_iget(dir->i_sb, prev); | 
					
						
							|  |  |  | 		if (!IS_ERR(dirty)) { | 
					
						
							|  |  |  | 			mark_inode_dirty(dirty); | 
					
						
							|  |  |  | 			iput(dirty); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = 0; | 
					
						
							|  |  |  | out_free_bh: | 
					
						
							|  |  |  | 	brelse(bh); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int omfs_dir_is_empty(struct inode *inode) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int nbuckets = (inode->i_size - OMFS_DIR_START) / 8; | 
					
						
							|  |  |  | 	struct buffer_head *bh; | 
					
						
							|  |  |  | 	u64 *ptr; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-06 17:51:53 -04:00
										 |  |  | 	bh = omfs_bread(inode->i_sb, inode->i_ino); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!bh) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ptr = (u64 *) &bh->b_data[OMFS_DIR_START]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < nbuckets; i++, ptr++) | 
					
						
							|  |  |  | 		if (*ptr != ~0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brelse(bh); | 
					
						
							|  |  |  | 	return *ptr != ~0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-04 01:31:03 -05:00
										 |  |  | static int omfs_remove(struct inode *dir, struct dentry *dentry) | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct inode *inode = dentry->d_inode; | 
					
						
							| 
									
										
										
										
											2011-03-04 01:31:03 -05:00
										 |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-24 13:06:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-27 13:42:04 -07:00
										 |  |  | 	if (S_ISDIR(inode->i_mode) && | 
					
						
							|  |  |  | 	    !omfs_dir_is_empty(inode)) | 
					
						
							|  |  |  | 		return -ENOTEMPTY; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ret = omfs_delete_entry(dentry); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2011-03-04 01:31:03 -05:00
										 |  |  | 		return ret; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	clear_nlink(inode); | 
					
						
							|  |  |  | 	mark_inode_dirty(inode); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 	mark_inode_dirty(dir); | 
					
						
							| 
									
										
										
										
											2011-03-04 01:31:03 -05:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-24 22:58:10 -04:00
										 |  |  | static int omfs_add_node(struct inode *dir, struct dentry *dentry, umode_t mode) | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 	struct inode *inode = omfs_new_inode(dir, mode); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (IS_ERR(inode)) | 
					
						
							|  |  |  | 		return PTR_ERR(inode); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = omfs_make_empty(inode, dir->i_sb); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		goto out_free_inode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = omfs_add_link(dentry, inode); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		goto out_free_inode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	d_instantiate(dentry, inode); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out_free_inode: | 
					
						
							|  |  |  | 	iput(inode); | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-26 01:41:39 -04:00
										 |  |  | static int omfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	return omfs_add_node(dir, dentry, mode | S_IFDIR); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-07-26 01:42:34 -04:00
										 |  |  | static int omfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, | 
					
						
							| 
									
										
										
										
											2012-06-10 18:05:36 -04:00
										 |  |  | 		bool excl) | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	return omfs_add_node(dir, dentry, mode | S_IFREG); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct dentry *omfs_lookup(struct inode *dir, struct dentry *dentry, | 
					
						
							| 
									
										
										
										
											2012-06-10 17:13:09 -04:00
										 |  |  | 				  unsigned int flags) | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct buffer_head *bh; | 
					
						
							|  |  |  | 	struct inode *inode = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dentry->d_name.len > OMFS_NAMELEN) | 
					
						
							|  |  |  | 		return ERR_PTR(-ENAMETOOLONG); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bh = omfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len); | 
					
						
							|  |  |  | 	if (!IS_ERR(bh)) { | 
					
						
							|  |  |  | 		struct omfs_inode *oi = (struct omfs_inode *)bh->b_data; | 
					
						
							|  |  |  | 		ino_t ino = be64_to_cpu(oi->i_head.h_self); | 
					
						
							|  |  |  | 		brelse(bh); | 
					
						
							|  |  |  | 		inode = omfs_iget(dir->i_sb, ino); | 
					
						
							|  |  |  | 		if (IS_ERR(inode)) | 
					
						
							|  |  |  | 			return ERR_CAST(inode); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	d_add(dentry, inode); | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* sanity check block's self pointer */ | 
					
						
							|  |  |  | int omfs_is_bad(struct omfs_sb_info *sbi, struct omfs_header *header, | 
					
						
							|  |  |  | 	u64 fsblock) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int is_bad; | 
					
						
							|  |  |  | 	u64 ino = be64_to_cpu(header->h_self); | 
					
						
							|  |  |  | 	is_bad = ((ino != fsblock) || (ino < sbi->s_root_ino) || | 
					
						
							|  |  |  | 		(ino > sbi->s_num_blocks)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (is_bad) | 
					
						
							|  |  |  | 		printk(KERN_WARNING "omfs: bad hash chain detected\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return is_bad; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | static bool omfs_fill_chain(struct inode *dir, struct dir_context *ctx, | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 		u64 fsblock, int hindex) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* follow chain in this bucket */ | 
					
						
							|  |  |  | 	while (fsblock != ~0) { | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 		struct buffer_head *bh = omfs_bread(dir->i_sb, fsblock); | 
					
						
							|  |  |  | 		struct omfs_inode *oi; | 
					
						
							|  |  |  | 		u64 self; | 
					
						
							|  |  |  | 		unsigned char d_type; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 		if (!bh) | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 			return true; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		oi = (struct omfs_inode *) bh->b_data; | 
					
						
							|  |  |  | 		if (omfs_is_bad(OMFS_SB(dir->i_sb), &oi->i_head, fsblock)) { | 
					
						
							|  |  |  | 			brelse(bh); | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 			return true; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		self = fsblock; | 
					
						
							|  |  |  | 		fsblock = be64_to_cpu(oi->i_sibling); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* skip visited nodes */ | 
					
						
							|  |  |  | 		if (hindex) { | 
					
						
							|  |  |  | 			hindex--; | 
					
						
							|  |  |  | 			brelse(bh); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		d_type = (oi->i_type == OMFS_DIR) ? DT_DIR : DT_REG; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 		if (!dir_emit(ctx, oi->i_name, | 
					
						
							|  |  |  | 			      strnlen(oi->i_name, OMFS_NAMELEN), | 
					
						
							|  |  |  | 			      self, d_type)) { | 
					
						
							|  |  |  | 			brelse(bh); | 
					
						
							|  |  |  | 			return false; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 		brelse(bh); | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 		ctx->pos++; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 	return true; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int omfs_rename(struct inode *old_dir, struct dentry *old_dentry, | 
					
						
							|  |  |  | 		struct inode *new_dir, struct dentry *new_dentry) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct inode *new_inode = new_dentry->d_inode; | 
					
						
							|  |  |  | 	struct inode *old_inode = old_dentry->d_inode; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (new_inode) { | 
					
						
							|  |  |  | 		/* overwriting existing file/dir */ | 
					
						
							| 
									
										
										
										
											2011-03-04 01:31:03 -05:00
										 |  |  | 		err = omfs_remove(new_dir, new_dentry); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 		if (err) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* since omfs locates files by name, we need to unlink _before_
 | 
					
						
							|  |  |  | 	 * adding the new link or we won't find the old one */ | 
					
						
							| 
									
										
										
										
											2011-03-04 01:18:19 -05:00
										 |  |  | 	err = omfs_delete_entry(old_dentry); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-04 01:18:19 -05:00
										 |  |  | 	mark_inode_dirty(old_dir); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 	err = omfs_add_link(new_dentry, old_inode); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	old_inode->i_ctime = CURRENT_TIME_SEC; | 
					
						
							| 
									
										
										
										
											2011-03-04 01:14:55 -05:00
										 |  |  | 	mark_inode_dirty(old_inode); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | out: | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | static int omfs_readdir(struct file *file, struct dir_context *ctx) | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 	struct inode *dir = file_inode(file); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 	struct buffer_head *bh; | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 	__be64 *p; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 	unsigned int hchain, hindex; | 
					
						
							|  |  |  | 	int nbuckets; | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (ctx->pos >> 32) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ctx->pos < 1 << 20) { | 
					
						
							|  |  |  | 		if (!dir_emit_dots(file, ctx)) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		ctx->pos = 1 << 20; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nbuckets = (dir->i_size - OMFS_DIR_START) / 8; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* high 12 bits store bucket + 1 and low 20 bits store hash index */ | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 	hchain = (ctx->pos >> 20) - 1; | 
					
						
							|  |  |  | 	hindex = ctx->pos & 0xfffff; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-09-06 17:51:53 -04:00
										 |  |  | 	bh = omfs_bread(dir->i_sb, dir->i_ino); | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 	if (!bh) | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 	p = (__be64 *)(bh->b_data + OMFS_DIR_START) + hchain; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 	for (; hchain < nbuckets; hchain++) { | 
					
						
							|  |  |  | 		__u64 fsblock = be64_to_cpu(*p++); | 
					
						
							|  |  |  | 		if (!omfs_fill_chain(dir, ctx, fsblock, hindex)) | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 		hindex = 0; | 
					
						
							|  |  |  | 		ctx->pos = (hchain+2) << 20; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	brelse(bh); | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-21 17:01:11 -07:00
										 |  |  | const struct inode_operations omfs_dir_inops = { | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 	.lookup = omfs_lookup, | 
					
						
							|  |  |  | 	.mkdir = omfs_mkdir, | 
					
						
							|  |  |  | 	.rename = omfs_rename, | 
					
						
							|  |  |  | 	.create = omfs_create, | 
					
						
							| 
									
										
										
										
											2011-03-04 01:31:03 -05:00
										 |  |  | 	.unlink = omfs_remove, | 
					
						
							|  |  |  | 	.rmdir = omfs_remove, | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-01 15:43:56 -07:00
										 |  |  | const struct file_operations omfs_dir_operations = { | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | 	.read = generic_read_dir, | 
					
						
							| 
									
										
										
										
											2013-05-17 15:05:25 -04:00
										 |  |  | 	.iterate = omfs_readdir, | 
					
						
							| 
									
										
										
										
											2008-09-03 21:53:01 +02:00
										 |  |  | 	.llseek = generic_file_llseek, | 
					
						
							| 
									
										
										
										
											2008-07-25 19:45:16 -07:00
										 |  |  | }; |