xref: /openbmc/linux/fs/btrfs/export.c (revision 30237d67)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2c1d7c514SDavid Sterba 
3be6e8dc0SBalaji Rao #include <linux/fs.h>
4be6e8dc0SBalaji Rao #include <linux/types.h>
5be6e8dc0SBalaji Rao #include "ctree.h"
6be6e8dc0SBalaji Rao #include "disk-io.h"
7be6e8dc0SBalaji Rao #include "btrfs_inode.h"
8be6e8dc0SBalaji Rao #include "print-tree.h"
9be6e8dc0SBalaji Rao #include "export.h"
1007e81dc9SJosef Bacik #include "accessors.h"
117f0add25SJosef Bacik #include "super.h"
12be6e8dc0SBalaji Rao 
13d397712bSChris Mason #define BTRFS_FID_SIZE_NON_CONNECTABLE (offsetof(struct btrfs_fid, \
14d397712bSChris Mason 						 parent_objectid) / 4)
15d397712bSChris Mason #define BTRFS_FID_SIZE_CONNECTABLE (offsetof(struct btrfs_fid, \
16d397712bSChris Mason 					     parent_root_objectid) / 4)
17be6e8dc0SBalaji Rao #define BTRFS_FID_SIZE_CONNECTABLE_ROOT (sizeof(struct btrfs_fid) / 4)
18be6e8dc0SBalaji Rao 
btrfs_encode_fh(struct inode * inode,u32 * fh,int * max_len,struct inode * parent)19b0b0382bSAl Viro static int btrfs_encode_fh(struct inode *inode, u32 *fh, int *max_len,
20b0b0382bSAl Viro 			   struct inode *parent)
21be6e8dc0SBalaji Rao {
22be6e8dc0SBalaji Rao 	struct btrfs_fid *fid = (struct btrfs_fid *)fh;
23be6e8dc0SBalaji Rao 	int len = *max_len;
24be6e8dc0SBalaji Rao 	int type;
25be6e8dc0SBalaji Rao 
26b0b0382bSAl Viro 	if (parent && (len < BTRFS_FID_SIZE_CONNECTABLE)) {
275fe0c237SAneesh Kumar K.V 		*max_len = BTRFS_FID_SIZE_CONNECTABLE;
2894e07a75SNamjae Jeon 		return FILEID_INVALID;
295fe0c237SAneesh Kumar K.V 	} else if (len < BTRFS_FID_SIZE_NON_CONNECTABLE) {
305fe0c237SAneesh Kumar K.V 		*max_len = BTRFS_FID_SIZE_NON_CONNECTABLE;
3194e07a75SNamjae Jeon 		return FILEID_INVALID;
325fe0c237SAneesh Kumar K.V 	}
33be6e8dc0SBalaji Rao 
34be6e8dc0SBalaji Rao 	len  = BTRFS_FID_SIZE_NON_CONNECTABLE;
35be6e8dc0SBalaji Rao 	type = FILEID_BTRFS_WITHOUT_PARENT;
36be6e8dc0SBalaji Rao 
374a0cc7caSNikolay Borisov 	fid->objectid = btrfs_ino(BTRFS_I(inode));
384fd786e6SMisono Tomohiro 	fid->root_objectid = BTRFS_I(inode)->root->root_key.objectid;
39be6e8dc0SBalaji Rao 	fid->gen = inode->i_generation;
40be6e8dc0SBalaji Rao 
41b0b0382bSAl Viro 	if (parent) {
42be6e8dc0SBalaji Rao 		u64 parent_root_id;
43be6e8dc0SBalaji Rao 
44be6e8dc0SBalaji Rao 		fid->parent_objectid = BTRFS_I(parent)->location.objectid;
45be6e8dc0SBalaji Rao 		fid->parent_gen = parent->i_generation;
464fd786e6SMisono Tomohiro 		parent_root_id = BTRFS_I(parent)->root->root_key.objectid;
47be6e8dc0SBalaji Rao 
48be6e8dc0SBalaji Rao 		if (parent_root_id != fid->root_objectid) {
49be6e8dc0SBalaji Rao 			fid->parent_root_objectid = parent_root_id;
50be6e8dc0SBalaji Rao 			len = BTRFS_FID_SIZE_CONNECTABLE_ROOT;
51be6e8dc0SBalaji Rao 			type = FILEID_BTRFS_WITH_PARENT_ROOT;
52be6e8dc0SBalaji Rao 		} else {
53be6e8dc0SBalaji Rao 			len = BTRFS_FID_SIZE_CONNECTABLE;
54be6e8dc0SBalaji Rao 			type = FILEID_BTRFS_WITH_PARENT;
55be6e8dc0SBalaji Rao 		}
56be6e8dc0SBalaji Rao 	}
57be6e8dc0SBalaji Rao 
58be6e8dc0SBalaji Rao 	*max_len = len;
59be6e8dc0SBalaji Rao 	return type;
60be6e8dc0SBalaji Rao }
61be6e8dc0SBalaji Rao 
62b307f06dSDavid Sterba /*
63b307f06dSDavid Sterba  * Read dentry of inode with @objectid from filesystem root @root_objectid.
64b307f06dSDavid Sterba  *
65b307f06dSDavid Sterba  * @sb:             the filesystem super block
66b307f06dSDavid Sterba  * @objectid:       inode objectid
67b307f06dSDavid Sterba  * @root_objectid:  object id of the subvolume root where to look up the inode
68b307f06dSDavid Sterba  * @generation:     optional, if not zero, verify that the found inode
69b307f06dSDavid Sterba  *                  generation matches
70b307f06dSDavid Sterba  *
71b307f06dSDavid Sterba  * Return dentry alias for the inode, otherwise an error. In case the
72b307f06dSDavid Sterba  * generation does not match return ESTALE.
73b307f06dSDavid Sterba  */
btrfs_get_dentry(struct super_block * sb,u64 objectid,u64 root_objectid,u64 generation)74c0c907a4SMarcos Paulo de Souza struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,
75b307f06dSDavid Sterba 				u64 root_objectid, u64 generation)
76be6e8dc0SBalaji Rao {
77815745cfSAl Viro 	struct btrfs_fs_info *fs_info = btrfs_sb(sb);
78be6e8dc0SBalaji Rao 	struct btrfs_root *root;
79be6e8dc0SBalaji Rao 	struct inode *inode;
8076dda93cSYan, Zheng 
8176dda93cSYan, Zheng 	if (objectid < BTRFS_FIRST_FREE_OBJECTID)
8276dda93cSYan, Zheng 		return ERR_PTR(-ESTALE);
83be6e8dc0SBalaji Rao 
8456e9357aSDavid Sterba 	root = btrfs_get_fs_root(fs_info, root_objectid, true);
85c75e8394SJosef Bacik 	if (IS_ERR(root))
86c75e8394SJosef Bacik 		return ERR_CAST(root);
8776dda93cSYan, Zheng 
880202e83fSDavid Sterba 	inode = btrfs_iget(sb, objectid, root);
8900246528SJosef Bacik 	btrfs_put_root(root);
90c75e8394SJosef Bacik 	if (IS_ERR(inode))
91c75e8394SJosef Bacik 		return ERR_CAST(inode);
9276dda93cSYan, Zheng 
93b307f06dSDavid Sterba 	if (generation != 0 && generation != inode->i_generation) {
94be6e8dc0SBalaji Rao 		iput(inode);
95be6e8dc0SBalaji Rao 		return ERR_PTR(-ESTALE);
96be6e8dc0SBalaji Rao 	}
97be6e8dc0SBalaji Rao 
98af53d29aSAl Viro 	return d_obtain_alias(inode);
99be6e8dc0SBalaji Rao }
100be6e8dc0SBalaji Rao 
btrfs_fh_to_parent(struct super_block * sb,struct fid * fh,int fh_len,int fh_type)101be6e8dc0SBalaji Rao static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh,
102be6e8dc0SBalaji Rao 					 int fh_len, int fh_type)
103be6e8dc0SBalaji Rao {
104be6e8dc0SBalaji Rao 	struct btrfs_fid *fid = (struct btrfs_fid *) fh;
105be6e8dc0SBalaji Rao 	u64 objectid, root_objectid;
106be6e8dc0SBalaji Rao 	u32 generation;
107be6e8dc0SBalaji Rao 
108be6e8dc0SBalaji Rao 	if (fh_type == FILEID_BTRFS_WITH_PARENT) {
1097d35199eSNeilBrown 		if (fh_len <  BTRFS_FID_SIZE_CONNECTABLE)
110be6e8dc0SBalaji Rao 			return NULL;
111be6e8dc0SBalaji Rao 		root_objectid = fid->root_objectid;
112be6e8dc0SBalaji Rao 	} else if (fh_type == FILEID_BTRFS_WITH_PARENT_ROOT) {
1137d35199eSNeilBrown 		if (fh_len < BTRFS_FID_SIZE_CONNECTABLE_ROOT)
114be6e8dc0SBalaji Rao 			return NULL;
115be6e8dc0SBalaji Rao 		root_objectid = fid->parent_root_objectid;
116be6e8dc0SBalaji Rao 	} else
117be6e8dc0SBalaji Rao 		return NULL;
118be6e8dc0SBalaji Rao 
119be6e8dc0SBalaji Rao 	objectid = fid->parent_objectid;
120be6e8dc0SBalaji Rao 	generation = fid->parent_gen;
121be6e8dc0SBalaji Rao 
122b307f06dSDavid Sterba 	return btrfs_get_dentry(sb, objectid, root_objectid, generation);
123be6e8dc0SBalaji Rao }
124be6e8dc0SBalaji Rao 
btrfs_fh_to_dentry(struct super_block * sb,struct fid * fh,int fh_len,int fh_type)125be6e8dc0SBalaji Rao static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
126be6e8dc0SBalaji Rao 					 int fh_len, int fh_type)
127be6e8dc0SBalaji Rao {
128be6e8dc0SBalaji Rao 	struct btrfs_fid *fid = (struct btrfs_fid *) fh;
129be6e8dc0SBalaji Rao 	u64 objectid, root_objectid;
130be6e8dc0SBalaji Rao 	u32 generation;
131be6e8dc0SBalaji Rao 
132be6e8dc0SBalaji Rao 	if ((fh_type != FILEID_BTRFS_WITH_PARENT ||
1337d35199eSNeilBrown 	     fh_len < BTRFS_FID_SIZE_CONNECTABLE) &&
134be6e8dc0SBalaji Rao 	    (fh_type != FILEID_BTRFS_WITH_PARENT_ROOT ||
1357d35199eSNeilBrown 	     fh_len < BTRFS_FID_SIZE_CONNECTABLE_ROOT) &&
136be6e8dc0SBalaji Rao 	    (fh_type != FILEID_BTRFS_WITHOUT_PARENT ||
1377d35199eSNeilBrown 	     fh_len < BTRFS_FID_SIZE_NON_CONNECTABLE))
138be6e8dc0SBalaji Rao 		return NULL;
139be6e8dc0SBalaji Rao 
140be6e8dc0SBalaji Rao 	objectid = fid->objectid;
141be6e8dc0SBalaji Rao 	root_objectid = fid->root_objectid;
142be6e8dc0SBalaji Rao 	generation = fid->gen;
143be6e8dc0SBalaji Rao 
144b307f06dSDavid Sterba 	return btrfs_get_dentry(sb, objectid, root_objectid, generation);
145be6e8dc0SBalaji Rao }
146be6e8dc0SBalaji Rao 
btrfs_get_parent(struct dentry * child)147c0c907a4SMarcos Paulo de Souza struct dentry *btrfs_get_parent(struct dentry *child)
148be6e8dc0SBalaji Rao {
1492b0143b5SDavid Howells 	struct inode *dir = d_inode(child);
1500b246afaSJeff Mahoney 	struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb);
151be6e8dc0SBalaji Rao 	struct btrfs_root *root = BTRFS_I(dir)->root;
152be6e8dc0SBalaji Rao 	struct btrfs_path *path;
153be6e8dc0SBalaji Rao 	struct extent_buffer *leaf;
15476dda93cSYan, Zheng 	struct btrfs_root_ref *ref;
15576dda93cSYan, Zheng 	struct btrfs_key key;
15676dda93cSYan, Zheng 	struct btrfs_key found_key;
157be6e8dc0SBalaji Rao 	int ret;
158be6e8dc0SBalaji Rao 
159be6e8dc0SBalaji Rao 	path = btrfs_alloc_path();
1602a29edc6Sliubo 	if (!path)
1612a29edc6Sliubo 		return ERR_PTR(-ENOMEM);
162be6e8dc0SBalaji Rao 
1634a0cc7caSNikolay Borisov 	if (btrfs_ino(BTRFS_I(dir)) == BTRFS_FIRST_FREE_OBJECTID) {
16476dda93cSYan, Zheng 		key.objectid = root->root_key.objectid;
16576dda93cSYan, Zheng 		key.type = BTRFS_ROOT_BACKREF_KEY;
16687acb4efSDavid Woodhouse 		key.offset = (u64)-1;
1670b246afaSJeff Mahoney 		root = fs_info->tree_root;
16876dda93cSYan, Zheng 	} else {
1694a0cc7caSNikolay Borisov 		key.objectid = btrfs_ino(BTRFS_I(dir));
17076dda93cSYan, Zheng 		key.type = BTRFS_INODE_REF_KEY;
17176dda93cSYan, Zheng 		key.offset = (u64)-1;
17276dda93cSYan, Zheng 	}
173be6e8dc0SBalaji Rao 
17487acb4efSDavid Woodhouse 	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
17576dda93cSYan, Zheng 	if (ret < 0)
17676dda93cSYan, Zheng 		goto fail;
17730237d67SDavid Sterba 	if (ret == 0) {
17830237d67SDavid Sterba 		/*
17930237d67SDavid Sterba 		 * Key with offset of -1 found, there would have to exist an
18030237d67SDavid Sterba 		 * inode with such number or a root with such id.
18130237d67SDavid Sterba 		 */
18230237d67SDavid Sterba 		ret = -EUCLEAN;
18330237d67SDavid Sterba 		goto fail;
18430237d67SDavid Sterba 	}
18576dda93cSYan, Zheng 
18676dda93cSYan, Zheng 	if (path->slots[0] == 0) {
18776dda93cSYan, Zheng 		ret = -ENOENT;
18876dda93cSYan, Zheng 		goto fail;
18976dda93cSYan, Zheng 	}
19076dda93cSYan, Zheng 
19176dda93cSYan, Zheng 	path->slots[0]--;
19276dda93cSYan, Zheng 	leaf = path->nodes[0];
19376dda93cSYan, Zheng 
19476dda93cSYan, Zheng 	btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
19576dda93cSYan, Zheng 	if (found_key.objectid != key.objectid || found_key.type != key.type) {
19676dda93cSYan, Zheng 		ret = -ENOENT;
19776dda93cSYan, Zheng 		goto fail;
19876dda93cSYan, Zheng 	}
19976dda93cSYan, Zheng 
20076dda93cSYan, Zheng 	if (found_key.type == BTRFS_ROOT_BACKREF_KEY) {
20176dda93cSYan, Zheng 		ref = btrfs_item_ptr(leaf, path->slots[0],
20276dda93cSYan, Zheng 				     struct btrfs_root_ref);
20376dda93cSYan, Zheng 		key.objectid = btrfs_root_ref_dirid(leaf, ref);
20476dda93cSYan, Zheng 	} else {
20576dda93cSYan, Zheng 		key.objectid = found_key.offset;
20676dda93cSYan, Zheng 	}
20776dda93cSYan, Zheng 	btrfs_free_path(path);
20876dda93cSYan, Zheng 
20976dda93cSYan, Zheng 	if (found_key.type == BTRFS_ROOT_BACKREF_KEY) {
2100b246afaSJeff Mahoney 		return btrfs_get_dentry(fs_info->sb, key.objectid,
211b307f06dSDavid Sterba 					found_key.offset, 0);
21276dda93cSYan, Zheng 	}
21376dda93cSYan, Zheng 
2140202e83fSDavid Sterba 	return d_obtain_alias(btrfs_iget(fs_info->sb, key.objectid, root));
21576dda93cSYan, Zheng fail:
216d54a8390SDavid Woodhouse 	btrfs_free_path(path);
217d54a8390SDavid Woodhouse 	return ERR_PTR(ret);
218d54a8390SDavid Woodhouse }
219be6e8dc0SBalaji Rao 
btrfs_get_name(struct dentry * parent,char * name,struct dentry * child)2202ede0dafSJosef Bacik static int btrfs_get_name(struct dentry *parent, char *name,
2212ede0dafSJosef Bacik 			  struct dentry *child)
2222ede0dafSJosef Bacik {
2232b0143b5SDavid Howells 	struct inode *inode = d_inode(child);
2242b0143b5SDavid Howells 	struct inode *dir = d_inode(parent);
2250b246afaSJeff Mahoney 	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
2262ede0dafSJosef Bacik 	struct btrfs_path *path;
2272ede0dafSJosef Bacik 	struct btrfs_root *root = BTRFS_I(dir)->root;
2282ede0dafSJosef Bacik 	struct btrfs_inode_ref *iref;
2292ede0dafSJosef Bacik 	struct btrfs_root_ref *rref;
2302ede0dafSJosef Bacik 	struct extent_buffer *leaf;
2312ede0dafSJosef Bacik 	unsigned long name_ptr;
2322ede0dafSJosef Bacik 	struct btrfs_key key;
2332ede0dafSJosef Bacik 	int name_len;
2342ede0dafSJosef Bacik 	int ret;
23533345d01SLi Zefan 	u64 ino;
2362ede0dafSJosef Bacik 
2372ede0dafSJosef Bacik 	if (!S_ISDIR(dir->i_mode))
2382ede0dafSJosef Bacik 		return -EINVAL;
2392ede0dafSJosef Bacik 
2404a0cc7caSNikolay Borisov 	ino = btrfs_ino(BTRFS_I(inode));
24133345d01SLi Zefan 
2422ede0dafSJosef Bacik 	path = btrfs_alloc_path();
2432ede0dafSJosef Bacik 	if (!path)
2442ede0dafSJosef Bacik 		return -ENOMEM;
2452ede0dafSJosef Bacik 
24633345d01SLi Zefan 	if (ino == BTRFS_FIRST_FREE_OBJECTID) {
2472ede0dafSJosef Bacik 		key.objectid = BTRFS_I(inode)->root->root_key.objectid;
2482ede0dafSJosef Bacik 		key.type = BTRFS_ROOT_BACKREF_KEY;
2492ede0dafSJosef Bacik 		key.offset = (u64)-1;
2500b246afaSJeff Mahoney 		root = fs_info->tree_root;
2512ede0dafSJosef Bacik 	} else {
25233345d01SLi Zefan 		key.objectid = ino;
2534a0cc7caSNikolay Borisov 		key.offset = btrfs_ino(BTRFS_I(dir));
2542ede0dafSJosef Bacik 		key.type = BTRFS_INODE_REF_KEY;
2552ede0dafSJosef Bacik 	}
2562ede0dafSJosef Bacik 
2572ede0dafSJosef Bacik 	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
2582ede0dafSJosef Bacik 	if (ret < 0) {
2592ede0dafSJosef Bacik 		btrfs_free_path(path);
2602ede0dafSJosef Bacik 		return ret;
2612ede0dafSJosef Bacik 	} else if (ret > 0) {
26233345d01SLi Zefan 		if (ino == BTRFS_FIRST_FREE_OBJECTID) {
2632ede0dafSJosef Bacik 			path->slots[0]--;
2642ede0dafSJosef Bacik 		} else {
2652ede0dafSJosef Bacik 			btrfs_free_path(path);
2662ede0dafSJosef Bacik 			return -ENOENT;
2672ede0dafSJosef Bacik 		}
2682ede0dafSJosef Bacik 	}
2692ede0dafSJosef Bacik 	leaf = path->nodes[0];
2702ede0dafSJosef Bacik 
27133345d01SLi Zefan 	if (ino == BTRFS_FIRST_FREE_OBJECTID) {
2722ede0dafSJosef Bacik 		rref = btrfs_item_ptr(leaf, path->slots[0],
2732ede0dafSJosef Bacik 				     struct btrfs_root_ref);
2742ede0dafSJosef Bacik 		name_ptr = (unsigned long)(rref + 1);
2752ede0dafSJosef Bacik 		name_len = btrfs_root_ref_name_len(leaf, rref);
2762ede0dafSJosef Bacik 	} else {
2772ede0dafSJosef Bacik 		iref = btrfs_item_ptr(leaf, path->slots[0],
2782ede0dafSJosef Bacik 				      struct btrfs_inode_ref);
2792ede0dafSJosef Bacik 		name_ptr = (unsigned long)(iref + 1);
2802ede0dafSJosef Bacik 		name_len = btrfs_inode_ref_name_len(leaf, iref);
2812ede0dafSJosef Bacik 	}
2822ede0dafSJosef Bacik 
2832ede0dafSJosef Bacik 	read_extent_buffer(leaf, name, name_ptr, name_len);
2842ede0dafSJosef Bacik 	btrfs_free_path(path);
2852ede0dafSJosef Bacik 
2862ede0dafSJosef Bacik 	/*
2872ede0dafSJosef Bacik 	 * have to add the null termination to make sure that reconnect_path
2882ede0dafSJosef Bacik 	 * gets the right len for strlen
2892ede0dafSJosef Bacik 	 */
2902ede0dafSJosef Bacik 	name[name_len] = '\0';
2912ede0dafSJosef Bacik 
2922ede0dafSJosef Bacik 	return 0;
2932ede0dafSJosef Bacik }
2942ede0dafSJosef Bacik 
295be6e8dc0SBalaji Rao const struct export_operations btrfs_export_ops = {
296be6e8dc0SBalaji Rao 	.encode_fh	= btrfs_encode_fh,
297be6e8dc0SBalaji Rao 	.fh_to_dentry	= btrfs_fh_to_dentry,
298be6e8dc0SBalaji Rao 	.fh_to_parent	= btrfs_fh_to_parent,
299be6e8dc0SBalaji Rao 	.get_parent	= btrfs_get_parent,
3002ede0dafSJosef Bacik 	.get_name	= btrfs_get_name,
301be6e8dc0SBalaji Rao };
302