Commit | Line | Data |
---|---|---|
be6e8dc0 BR |
1 | #include <linux/fs.h> |
2 | #include <linux/types.h> | |
3 | #include "ctree.h" | |
4 | #include "disk-io.h" | |
5 | #include "btrfs_inode.h" | |
6 | #include "print-tree.h" | |
7 | #include "export.h" | |
8 | #include "compat.h" | |
9 | ||
d397712b CM |
10 | #define BTRFS_FID_SIZE_NON_CONNECTABLE (offsetof(struct btrfs_fid, \ |
11 | parent_objectid) / 4) | |
12 | #define BTRFS_FID_SIZE_CONNECTABLE (offsetof(struct btrfs_fid, \ | |
13 | parent_root_objectid) / 4) | |
14 | #define BTRFS_FID_SIZE_CONNECTABLE_ROOT (sizeof(struct btrfs_fid) / 4) | |
be6e8dc0 BR |
15 | |
16 | static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len, | |
17 | int connectable) | |
18 | { | |
19 | struct btrfs_fid *fid = (struct btrfs_fid *)fh; | |
20 | struct inode *inode = dentry->d_inode; | |
21 | int len = *max_len; | |
22 | int type; | |
23 | ||
24 | if ((len < BTRFS_FID_SIZE_NON_CONNECTABLE) || | |
25 | (connectable && len < BTRFS_FID_SIZE_CONNECTABLE)) | |
26 | return 255; | |
27 | ||
28 | len = BTRFS_FID_SIZE_NON_CONNECTABLE; | |
29 | type = FILEID_BTRFS_WITHOUT_PARENT; | |
30 | ||
76dda93c | 31 | fid->objectid = inode->i_ino; |
be6e8dc0 BR |
32 | fid->root_objectid = BTRFS_I(inode)->root->objectid; |
33 | fid->gen = inode->i_generation; | |
34 | ||
35 | if (connectable && !S_ISDIR(inode->i_mode)) { | |
36 | struct inode *parent; | |
37 | u64 parent_root_id; | |
38 | ||
39 | spin_lock(&dentry->d_lock); | |
40 | ||
41 | parent = dentry->d_parent->d_inode; | |
42 | fid->parent_objectid = BTRFS_I(parent)->location.objectid; | |
43 | fid->parent_gen = parent->i_generation; | |
44 | parent_root_id = BTRFS_I(parent)->root->objectid; | |
45 | ||
46 | spin_unlock(&dentry->d_lock); | |
47 | ||
48 | if (parent_root_id != fid->root_objectid) { | |
49 | fid->parent_root_objectid = parent_root_id; | |
50 | len = BTRFS_FID_SIZE_CONNECTABLE_ROOT; | |
51 | type = FILEID_BTRFS_WITH_PARENT_ROOT; | |
52 | } else { | |
53 | len = BTRFS_FID_SIZE_CONNECTABLE; | |
54 | type = FILEID_BTRFS_WITH_PARENT; | |
55 | } | |
56 | } | |
57 | ||
58 | *max_len = len; | |
59 | return type; | |
60 | } | |
61 | ||
62 | static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid, | |
76dda93c YZ |
63 | u64 root_objectid, u32 generation, |
64 | int check_generation) | |
be6e8dc0 | 65 | { |
76dda93c | 66 | struct btrfs_fs_info *fs_info = btrfs_sb(sb)->fs_info; |
be6e8dc0 | 67 | struct btrfs_root *root; |
76dda93c | 68 | struct dentry *dentry; |
be6e8dc0 | 69 | struct inode *inode; |
be6e8dc0 | 70 | struct btrfs_key key; |
76dda93c YZ |
71 | int index; |
72 | int err = 0; | |
73 | ||
74 | if (objectid < BTRFS_FIRST_FREE_OBJECTID) | |
75 | return ERR_PTR(-ESTALE); | |
be6e8dc0 | 76 | |
2d4d9fbd DW |
77 | key.objectid = root_objectid; |
78 | btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); | |
79 | key.offset = (u64)-1; | |
80 | ||
76dda93c YZ |
81 | index = srcu_read_lock(&fs_info->subvol_srcu); |
82 | ||
83 | root = btrfs_read_fs_root_no_name(fs_info, &key); | |
84 | if (IS_ERR(root)) { | |
85 | err = PTR_ERR(root); | |
86 | goto fail; | |
87 | } | |
88 | ||
89 | if (btrfs_root_refs(&root->root_item) == 0) { | |
90 | err = -ENOENT; | |
91 | goto fail; | |
92 | } | |
2d4d9fbd | 93 | |
be6e8dc0 BR |
94 | key.objectid = objectid; |
95 | btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); | |
96 | key.offset = 0; | |
97 | ||
73f73415 | 98 | inode = btrfs_iget(sb, &key, root, NULL); |
76dda93c YZ |
99 | if (IS_ERR(inode)) { |
100 | err = PTR_ERR(inode); | |
101 | goto fail; | |
102 | } | |
103 | ||
104 | srcu_read_unlock(&fs_info->subvol_srcu, index); | |
be6e8dc0 | 105 | |
76dda93c | 106 | if (check_generation && generation != inode->i_generation) { |
be6e8dc0 BR |
107 | iput(inode); |
108 | return ERR_PTR(-ESTALE); | |
109 | } | |
110 | ||
76dda93c YZ |
111 | dentry = d_obtain_alias(inode); |
112 | if (!IS_ERR(dentry)) | |
113 | dentry->d_op = &btrfs_dentry_operations; | |
114 | return dentry; | |
115 | fail: | |
116 | srcu_read_unlock(&fs_info->subvol_srcu, index); | |
117 | return ERR_PTR(err); | |
be6e8dc0 BR |
118 | } |
119 | ||
120 | static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh, | |
121 | int fh_len, int fh_type) | |
122 | { | |
123 | struct btrfs_fid *fid = (struct btrfs_fid *) fh; | |
124 | u64 objectid, root_objectid; | |
125 | u32 generation; | |
126 | ||
127 | if (fh_type == FILEID_BTRFS_WITH_PARENT) { | |
128 | if (fh_len != BTRFS_FID_SIZE_CONNECTABLE) | |
129 | return NULL; | |
130 | root_objectid = fid->root_objectid; | |
131 | } else if (fh_type == FILEID_BTRFS_WITH_PARENT_ROOT) { | |
132 | if (fh_len != BTRFS_FID_SIZE_CONNECTABLE_ROOT) | |
133 | return NULL; | |
134 | root_objectid = fid->parent_root_objectid; | |
135 | } else | |
136 | return NULL; | |
137 | ||
138 | objectid = fid->parent_objectid; | |
139 | generation = fid->parent_gen; | |
140 | ||
76dda93c | 141 | return btrfs_get_dentry(sb, objectid, root_objectid, generation, 1); |
be6e8dc0 BR |
142 | } |
143 | ||
144 | static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh, | |
145 | int fh_len, int fh_type) | |
146 | { | |
147 | struct btrfs_fid *fid = (struct btrfs_fid *) fh; | |
148 | u64 objectid, root_objectid; | |
149 | u32 generation; | |
150 | ||
151 | if ((fh_type != FILEID_BTRFS_WITH_PARENT || | |
152 | fh_len != BTRFS_FID_SIZE_CONNECTABLE) && | |
153 | (fh_type != FILEID_BTRFS_WITH_PARENT_ROOT || | |
154 | fh_len != BTRFS_FID_SIZE_CONNECTABLE_ROOT) && | |
155 | (fh_type != FILEID_BTRFS_WITHOUT_PARENT || | |
156 | fh_len != BTRFS_FID_SIZE_NON_CONNECTABLE)) | |
157 | return NULL; | |
158 | ||
159 | objectid = fid->objectid; | |
160 | root_objectid = fid->root_objectid; | |
161 | generation = fid->gen; | |
162 | ||
76dda93c | 163 | return btrfs_get_dentry(sb, objectid, root_objectid, generation, 1); |
be6e8dc0 BR |
164 | } |
165 | ||
166 | static struct dentry *btrfs_get_parent(struct dentry *child) | |
167 | { | |
168 | struct inode *dir = child->d_inode; | |
76dda93c | 169 | static struct dentry *dentry; |
be6e8dc0 | 170 | struct btrfs_root *root = BTRFS_I(dir)->root; |
be6e8dc0 BR |
171 | struct btrfs_path *path; |
172 | struct extent_buffer *leaf; | |
76dda93c YZ |
173 | struct btrfs_root_ref *ref; |
174 | struct btrfs_key key; | |
175 | struct btrfs_key found_key; | |
be6e8dc0 BR |
176 | int ret; |
177 | ||
178 | path = btrfs_alloc_path(); | |
179 | ||
76dda93c YZ |
180 | if (dir->i_ino == BTRFS_FIRST_FREE_OBJECTID) { |
181 | key.objectid = root->root_key.objectid; | |
182 | key.type = BTRFS_ROOT_BACKREF_KEY; | |
183 | key.offset = (u64)-1; | |
184 | root = root->fs_info->tree_root; | |
185 | } else { | |
186 | key.objectid = dir->i_ino; | |
187 | key.type = BTRFS_INODE_REF_KEY; | |
188 | key.offset = (u64)-1; | |
189 | } | |
be6e8dc0 | 190 | |
87acb4ef | 191 | ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); |
76dda93c YZ |
192 | if (ret < 0) |
193 | goto fail; | |
194 | ||
195 | BUG_ON(ret == 0); | |
196 | if (path->slots[0] == 0) { | |
197 | ret = -ENOENT; | |
198 | goto fail; | |
d54a8390 | 199 | } |
76dda93c YZ |
200 | |
201 | path->slots[0]--; | |
be6e8dc0 | 202 | leaf = path->nodes[0]; |
76dda93c YZ |
203 | |
204 | btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); | |
205 | if (found_key.objectid != key.objectid || found_key.type != key.type) { | |
206 | ret = -ENOENT; | |
207 | goto fail; | |
be6e8dc0 BR |
208 | } |
209 | ||
76dda93c YZ |
210 | if (found_key.type == BTRFS_ROOT_BACKREF_KEY) { |
211 | ref = btrfs_item_ptr(leaf, path->slots[0], | |
212 | struct btrfs_root_ref); | |
213 | key.objectid = btrfs_root_ref_dirid(leaf, ref); | |
214 | } else { | |
215 | key.objectid = found_key.offset; | |
216 | } | |
be6e8dc0 BR |
217 | btrfs_free_path(path); |
218 | ||
76dda93c YZ |
219 | if (found_key.type == BTRFS_ROOT_BACKREF_KEY) { |
220 | return btrfs_get_dentry(root->fs_info->sb, key.objectid, | |
221 | found_key.offset, 0, 0); | |
222 | } | |
2d4d9fbd | 223 | |
76dda93c | 224 | key.type = BTRFS_INODE_ITEM_KEY; |
be6e8dc0 | 225 | key.offset = 0; |
73f73415 | 226 | dentry = d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root, NULL)); |
76dda93c YZ |
227 | if (!IS_ERR(dentry)) |
228 | dentry->d_op = &btrfs_dentry_operations; | |
229 | return dentry; | |
230 | fail: | |
231 | btrfs_free_path(path); | |
232 | return ERR_PTR(ret); | |
be6e8dc0 BR |
233 | } |
234 | ||
235 | const struct export_operations btrfs_export_ops = { | |
236 | .encode_fh = btrfs_encode_fh, | |
237 | .fh_to_dentry = btrfs_fh_to_dentry, | |
238 | .fh_to_parent = btrfs_fh_to_parent, | |
239 | .get_parent = btrfs_get_parent, | |
240 | }; |