diff options
Diffstat (limited to 'fs/btrfs/export.c')
| -rw-r--r-- | fs/btrfs/export.c | 132 | 
1 files changed, 98 insertions, 34 deletions
diff --git a/fs/btrfs/export.c b/fs/btrfs/export.c index 951ef09b82f..41422a3de8e 100644 --- a/fs/btrfs/export.c +++ b/fs/btrfs/export.c @@ -5,7 +5,6 @@  #include "btrfs_inode.h"  #include "print-tree.h"  #include "export.h" -#include "compat.h"  #define BTRFS_FID_SIZE_NON_CONNECTABLE (offsetof(struct btrfs_fid, \  						 parent_objectid) / 4) @@ -13,38 +12,35 @@  					     parent_root_objectid) / 4)  #define BTRFS_FID_SIZE_CONNECTABLE_ROOT (sizeof(struct btrfs_fid) / 4) -static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len, -			   int connectable) +static int btrfs_encode_fh(struct inode *inode, u32 *fh, int *max_len, +			   struct inode *parent)  {  	struct btrfs_fid *fid = (struct btrfs_fid *)fh; -	struct inode *inode = dentry->d_inode;  	int len = *max_len;  	int type; -	if ((len < BTRFS_FID_SIZE_NON_CONNECTABLE) || -	    (connectable && len < BTRFS_FID_SIZE_CONNECTABLE)) -		return 255; +	if (parent && (len < BTRFS_FID_SIZE_CONNECTABLE)) { +		*max_len = BTRFS_FID_SIZE_CONNECTABLE; +		return FILEID_INVALID; +	} else if (len < BTRFS_FID_SIZE_NON_CONNECTABLE) { +		*max_len = BTRFS_FID_SIZE_NON_CONNECTABLE; +		return FILEID_INVALID; +	}  	len  = BTRFS_FID_SIZE_NON_CONNECTABLE;  	type = FILEID_BTRFS_WITHOUT_PARENT; -	fid->objectid = inode->i_ino; +	fid->objectid = btrfs_ino(inode);  	fid->root_objectid = BTRFS_I(inode)->root->objectid;  	fid->gen = inode->i_generation; -	if (connectable && !S_ISDIR(inode->i_mode)) { -		struct inode *parent; +	if (parent) {  		u64 parent_root_id; -		spin_lock(&dentry->d_lock); - -		parent = dentry->d_parent->d_inode;  		fid->parent_objectid = BTRFS_I(parent)->location.objectid;  		fid->parent_gen = parent->i_generation;  		parent_root_id = BTRFS_I(parent)->root->objectid; -		spin_unlock(&dentry->d_lock); -  		if (parent_root_id != fid->root_objectid) {  			fid->parent_root_objectid = parent_root_id;  			len = BTRFS_FID_SIZE_CONNECTABLE_ROOT; @@ -63,9 +59,8 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,  				       u64 root_objectid, u32 generation,  				       int check_generation)  { -	struct btrfs_fs_info *fs_info = btrfs_sb(sb)->fs_info; +	struct btrfs_fs_info *fs_info = btrfs_sb(sb);  	struct btrfs_root *root; -	struct dentry *dentry;  	struct inode *inode;  	struct btrfs_key key;  	int index; @@ -86,11 +81,6 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,  		goto fail;  	} -	if (btrfs_root_refs(&root->root_item) == 0) { -		err = -ENOENT; -		goto fail; -	} -  	key.objectid = objectid;  	btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);  	key.offset = 0; @@ -108,10 +98,7 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,  		return ERR_PTR(-ESTALE);  	} -	dentry = d_obtain_alias(inode); -	if (!IS_ERR(dentry)) -		dentry->d_op = &btrfs_dentry_operations; -	return dentry; +	return d_obtain_alias(inode);  fail:  	srcu_read_unlock(&fs_info->subvol_srcu, index);  	return ERR_PTR(err); @@ -166,7 +153,6 @@ static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh,  static struct dentry *btrfs_get_parent(struct dentry *child)  {  	struct inode *dir = child->d_inode; -	static struct dentry *dentry;  	struct btrfs_root *root = BTRFS_I(dir)->root;  	struct btrfs_path *path;  	struct extent_buffer *leaf; @@ -176,14 +162,16 @@ static struct dentry *btrfs_get_parent(struct dentry *child)  	int ret;  	path = btrfs_alloc_path(); +	if (!path) +		return ERR_PTR(-ENOMEM); -	if (dir->i_ino == BTRFS_FIRST_FREE_OBJECTID) { +	if (btrfs_ino(dir) == BTRFS_FIRST_FREE_OBJECTID) {  		key.objectid = root->root_key.objectid;  		key.type = BTRFS_ROOT_BACKREF_KEY;  		key.offset = (u64)-1;  		root = root->fs_info->tree_root;  	} else { -		key.objectid = dir->i_ino; +		key.objectid = btrfs_ino(dir);  		key.type = BTRFS_INODE_REF_KEY;  		key.offset = (u64)-1;  	} @@ -192,7 +180,7 @@ static struct dentry *btrfs_get_parent(struct dentry *child)  	if (ret < 0)  		goto fail; -	BUG_ON(ret == 0); +	BUG_ON(ret == 0); /* Key with offset of -1 found */  	if (path->slots[0] == 0) {  		ret = -ENOENT;  		goto fail; @@ -223,18 +211,94 @@ static struct dentry *btrfs_get_parent(struct dentry *child)  	key.type = BTRFS_INODE_ITEM_KEY;  	key.offset = 0; -	dentry = d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root, NULL)); -	if (!IS_ERR(dentry)) -		dentry->d_op = &btrfs_dentry_operations; -	return dentry; +	return d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root, NULL));  fail:  	btrfs_free_path(path);  	return ERR_PTR(ret);  } +static int btrfs_get_name(struct dentry *parent, char *name, +			  struct dentry *child) +{ +	struct inode *inode = child->d_inode; +	struct inode *dir = parent->d_inode; +	struct btrfs_path *path; +	struct btrfs_root *root = BTRFS_I(dir)->root; +	struct btrfs_inode_ref *iref; +	struct btrfs_root_ref *rref; +	struct extent_buffer *leaf; +	unsigned long name_ptr; +	struct btrfs_key key; +	int name_len; +	int ret; +	u64 ino; + +	if (!dir || !inode) +		return -EINVAL; + +	if (!S_ISDIR(dir->i_mode)) +		return -EINVAL; + +	ino = btrfs_ino(inode); + +	path = btrfs_alloc_path(); +	if (!path) +		return -ENOMEM; +	path->leave_spinning = 1; + +	if (ino == BTRFS_FIRST_FREE_OBJECTID) { +		key.objectid = BTRFS_I(inode)->root->root_key.objectid; +		key.type = BTRFS_ROOT_BACKREF_KEY; +		key.offset = (u64)-1; +		root = root->fs_info->tree_root; +	} else { +		key.objectid = ino; +		key.offset = btrfs_ino(dir); +		key.type = BTRFS_INODE_REF_KEY; +	} + +	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); +	if (ret < 0) { +		btrfs_free_path(path); +		return ret; +	} else if (ret > 0) { +		if (ino == BTRFS_FIRST_FREE_OBJECTID) { +			path->slots[0]--; +		} else { +			btrfs_free_path(path); +			return -ENOENT; +		} +	} +	leaf = path->nodes[0]; + +	if (ino == BTRFS_FIRST_FREE_OBJECTID) { +		rref = btrfs_item_ptr(leaf, path->slots[0], +				     struct btrfs_root_ref); +		name_ptr = (unsigned long)(rref + 1); +		name_len = btrfs_root_ref_name_len(leaf, rref); +	} else { +		iref = btrfs_item_ptr(leaf, path->slots[0], +				      struct btrfs_inode_ref); +		name_ptr = (unsigned long)(iref + 1); +		name_len = btrfs_inode_ref_name_len(leaf, iref); +	} + +	read_extent_buffer(leaf, name, name_ptr, name_len); +	btrfs_free_path(path); + +	/* +	 * have to add the null termination to make sure that reconnect_path +	 * gets the right len for strlen +	 */ +	name[name_len] = '\0'; + +	return 0; +} +  const struct export_operations btrfs_export_ops = {  	.encode_fh	= btrfs_encode_fh,  	.fh_to_dentry	= btrfs_fh_to_dentry,  	.fh_to_parent	= btrfs_fh_to_parent,  	.get_parent	= btrfs_get_parent, +	.get_name	= btrfs_get_name,  };  | 
