Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
c1d7c514 | 2 | |
be6e8dc0 BR |
3 | #include <linux/fs.h> |
4 | #include <linux/types.h> | |
5 | #include "ctree.h" | |
6 | #include "disk-io.h" | |
7 | #include "btrfs_inode.h" | |
8 | #include "print-tree.h" | |
9 | #include "export.h" | |
07e81dc9 | 10 | #include "accessors.h" |
7f0add25 | 11 | #include "super.h" |
be6e8dc0 | 12 | |
d397712b CM |
13 | #define BTRFS_FID_SIZE_NON_CONNECTABLE (offsetof(struct btrfs_fid, \ |
14 | parent_objectid) / 4) | |
15 | #define BTRFS_FID_SIZE_CONNECTABLE (offsetof(struct btrfs_fid, \ | |
16 | parent_root_objectid) / 4) | |
17 | #define BTRFS_FID_SIZE_CONNECTABLE_ROOT (sizeof(struct btrfs_fid) / 4) | |
be6e8dc0 | 18 | |
b0b0382b AV |
19 | static int btrfs_encode_fh(struct inode *inode, u32 *fh, int *max_len, |
20 | struct inode *parent) | |
be6e8dc0 BR |
21 | { |
22 | struct btrfs_fid *fid = (struct btrfs_fid *)fh; | |
be6e8dc0 BR |
23 | int len = *max_len; |
24 | int type; | |
25 | ||
b0b0382b | 26 | if (parent && (len < BTRFS_FID_SIZE_CONNECTABLE)) { |
5fe0c237 | 27 | *max_len = BTRFS_FID_SIZE_CONNECTABLE; |
94e07a75 | 28 | return FILEID_INVALID; |
5fe0c237 AK |
29 | } else if (len < BTRFS_FID_SIZE_NON_CONNECTABLE) { |
30 | *max_len = BTRFS_FID_SIZE_NON_CONNECTABLE; | |
94e07a75 | 31 | return FILEID_INVALID; |
5fe0c237 | 32 | } |
be6e8dc0 BR |
33 | |
34 | len = BTRFS_FID_SIZE_NON_CONNECTABLE; | |
35 | type = FILEID_BTRFS_WITHOUT_PARENT; | |
36 | ||
4a0cc7ca | 37 | fid->objectid = btrfs_ino(BTRFS_I(inode)); |
4fd786e6 | 38 | fid->root_objectid = BTRFS_I(inode)->root->root_key.objectid; |
be6e8dc0 BR |
39 | fid->gen = inode->i_generation; |
40 | ||
b0b0382b | 41 | if (parent) { |
be6e8dc0 BR |
42 | u64 parent_root_id; |
43 | ||
be6e8dc0 BR |
44 | fid->parent_objectid = BTRFS_I(parent)->location.objectid; |
45 | fid->parent_gen = parent->i_generation; | |
4fd786e6 | 46 | parent_root_id = BTRFS_I(parent)->root->root_key.objectid; |
be6e8dc0 | 47 | |
be6e8dc0 BR |
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 | ||
b307f06d DS |
62 | /* |
63 | * Read dentry of inode with @objectid from filesystem root @root_objectid. | |
64 | * | |
65 | * @sb: the filesystem super block | |
66 | * @objectid: inode objectid | |
67 | * @root_objectid: object id of the subvolume root where to look up the inode | |
68 | * @generation: optional, if not zero, verify that the found inode | |
69 | * generation matches | |
70 | * | |
71 | * Return dentry alias for the inode, otherwise an error. In case the | |
72 | * generation does not match return ESTALE. | |
73 | */ | |
c0c907a4 | 74 | struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid, |
b307f06d | 75 | u64 root_objectid, u64 generation) |
be6e8dc0 | 76 | { |
815745cf | 77 | struct btrfs_fs_info *fs_info = btrfs_sb(sb); |
be6e8dc0 BR |
78 | struct btrfs_root *root; |
79 | struct inode *inode; | |
76dda93c YZ |
80 | |
81 | if (objectid < BTRFS_FIRST_FREE_OBJECTID) | |
82 | return ERR_PTR(-ESTALE); | |
be6e8dc0 | 83 | |
56e9357a | 84 | root = btrfs_get_fs_root(fs_info, root_objectid, true); |
c75e8394 JB |
85 | if (IS_ERR(root)) |
86 | return ERR_CAST(root); | |
76dda93c | 87 | |
0202e83f | 88 | inode = btrfs_iget(sb, objectid, root); |
00246528 | 89 | btrfs_put_root(root); |
c75e8394 JB |
90 | if (IS_ERR(inode)) |
91 | return ERR_CAST(inode); | |
be6e8dc0 | 92 | |
b307f06d | 93 | if (generation != 0 && generation != inode->i_generation) { |
be6e8dc0 BR |
94 | iput(inode); |
95 | return ERR_PTR(-ESTALE); | |
96 | } | |
97 | ||
af53d29a | 98 | return d_obtain_alias(inode); |
be6e8dc0 BR |
99 | } |
100 | ||
101 | static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh, | |
102 | int fh_len, int fh_type) | |
103 | { | |
104 | struct btrfs_fid *fid = (struct btrfs_fid *) fh; | |
105 | u64 objectid, root_objectid; | |
106 | u32 generation; | |
107 | ||
108 | if (fh_type == FILEID_BTRFS_WITH_PARENT) { | |
7d35199e | 109 | if (fh_len < BTRFS_FID_SIZE_CONNECTABLE) |
be6e8dc0 BR |
110 | return NULL; |
111 | root_objectid = fid->root_objectid; | |
112 | } else if (fh_type == FILEID_BTRFS_WITH_PARENT_ROOT) { | |
7d35199e | 113 | if (fh_len < BTRFS_FID_SIZE_CONNECTABLE_ROOT) |
be6e8dc0 BR |
114 | return NULL; |
115 | root_objectid = fid->parent_root_objectid; | |
116 | } else | |
117 | return NULL; | |
118 | ||
119 | objectid = fid->parent_objectid; | |
120 | generation = fid->parent_gen; | |
121 | ||
b307f06d | 122 | return btrfs_get_dentry(sb, objectid, root_objectid, generation); |
be6e8dc0 BR |
123 | } |
124 | ||
125 | static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh, | |
126 | int fh_len, int fh_type) | |
127 | { | |
128 | struct btrfs_fid *fid = (struct btrfs_fid *) fh; | |
129 | u64 objectid, root_objectid; | |
130 | u32 generation; | |
131 | ||
132 | if ((fh_type != FILEID_BTRFS_WITH_PARENT || | |
7d35199e | 133 | fh_len < BTRFS_FID_SIZE_CONNECTABLE) && |
be6e8dc0 | 134 | (fh_type != FILEID_BTRFS_WITH_PARENT_ROOT || |
7d35199e | 135 | fh_len < BTRFS_FID_SIZE_CONNECTABLE_ROOT) && |
be6e8dc0 | 136 | (fh_type != FILEID_BTRFS_WITHOUT_PARENT || |
7d35199e | 137 | fh_len < BTRFS_FID_SIZE_NON_CONNECTABLE)) |
be6e8dc0 BR |
138 | return NULL; |
139 | ||
140 | objectid = fid->objectid; | |
141 | root_objectid = fid->root_objectid; | |
142 | generation = fid->gen; | |
143 | ||
b307f06d | 144 | return btrfs_get_dentry(sb, objectid, root_objectid, generation); |
be6e8dc0 BR |
145 | } |
146 | ||
c0c907a4 | 147 | struct dentry *btrfs_get_parent(struct dentry *child) |
be6e8dc0 | 148 | { |
2b0143b5 | 149 | struct inode *dir = d_inode(child); |
0b246afa | 150 | struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb); |
be6e8dc0 | 151 | struct btrfs_root *root = BTRFS_I(dir)->root; |
be6e8dc0 BR |
152 | struct btrfs_path *path; |
153 | struct extent_buffer *leaf; | |
76dda93c YZ |
154 | struct btrfs_root_ref *ref; |
155 | struct btrfs_key key; | |
156 | struct btrfs_key found_key; | |
be6e8dc0 BR |
157 | int ret; |
158 | ||
159 | path = btrfs_alloc_path(); | |
2a29edc6 | 160 | if (!path) |
161 | return ERR_PTR(-ENOMEM); | |
be6e8dc0 | 162 | |
4a0cc7ca | 163 | if (btrfs_ino(BTRFS_I(dir)) == BTRFS_FIRST_FREE_OBJECTID) { |
76dda93c YZ |
164 | key.objectid = root->root_key.objectid; |
165 | key.type = BTRFS_ROOT_BACKREF_KEY; | |
166 | key.offset = (u64)-1; | |
0b246afa | 167 | root = fs_info->tree_root; |
76dda93c | 168 | } else { |
4a0cc7ca | 169 | key.objectid = btrfs_ino(BTRFS_I(dir)); |
76dda93c YZ |
170 | key.type = BTRFS_INODE_REF_KEY; |
171 | key.offset = (u64)-1; | |
172 | } | |
be6e8dc0 | 173 | |
87acb4ef | 174 | ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); |
76dda93c YZ |
175 | if (ret < 0) |
176 | goto fail; | |
177 | ||
79787eaa | 178 | BUG_ON(ret == 0); /* Key with offset of -1 found */ |
76dda93c YZ |
179 | if (path->slots[0] == 0) { |
180 | ret = -ENOENT; | |
181 | goto fail; | |
d54a8390 | 182 | } |
76dda93c YZ |
183 | |
184 | path->slots[0]--; | |
be6e8dc0 | 185 | leaf = path->nodes[0]; |
76dda93c YZ |
186 | |
187 | btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); | |
188 | if (found_key.objectid != key.objectid || found_key.type != key.type) { | |
189 | ret = -ENOENT; | |
190 | goto fail; | |
be6e8dc0 BR |
191 | } |
192 | ||
76dda93c YZ |
193 | if (found_key.type == BTRFS_ROOT_BACKREF_KEY) { |
194 | ref = btrfs_item_ptr(leaf, path->slots[0], | |
195 | struct btrfs_root_ref); | |
196 | key.objectid = btrfs_root_ref_dirid(leaf, ref); | |
197 | } else { | |
198 | key.objectid = found_key.offset; | |
199 | } | |
be6e8dc0 BR |
200 | btrfs_free_path(path); |
201 | ||
76dda93c | 202 | if (found_key.type == BTRFS_ROOT_BACKREF_KEY) { |
0b246afa | 203 | return btrfs_get_dentry(fs_info->sb, key.objectid, |
b307f06d | 204 | found_key.offset, 0); |
76dda93c | 205 | } |
2d4d9fbd | 206 | |
0202e83f | 207 | return d_obtain_alias(btrfs_iget(fs_info->sb, key.objectid, root)); |
76dda93c YZ |
208 | fail: |
209 | btrfs_free_path(path); | |
210 | return ERR_PTR(ret); | |
be6e8dc0 BR |
211 | } |
212 | ||
2ede0daf JB |
213 | static int btrfs_get_name(struct dentry *parent, char *name, |
214 | struct dentry *child) | |
215 | { | |
2b0143b5 DH |
216 | struct inode *inode = d_inode(child); |
217 | struct inode *dir = d_inode(parent); | |
0b246afa | 218 | struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); |
2ede0daf JB |
219 | struct btrfs_path *path; |
220 | struct btrfs_root *root = BTRFS_I(dir)->root; | |
221 | struct btrfs_inode_ref *iref; | |
222 | struct btrfs_root_ref *rref; | |
223 | struct extent_buffer *leaf; | |
224 | unsigned long name_ptr; | |
225 | struct btrfs_key key; | |
226 | int name_len; | |
227 | int ret; | |
33345d01 | 228 | u64 ino; |
2ede0daf | 229 | |
2ede0daf JB |
230 | if (!S_ISDIR(dir->i_mode)) |
231 | return -EINVAL; | |
232 | ||
4a0cc7ca | 233 | ino = btrfs_ino(BTRFS_I(inode)); |
33345d01 | 234 | |
2ede0daf JB |
235 | path = btrfs_alloc_path(); |
236 | if (!path) | |
237 | return -ENOMEM; | |
2ede0daf | 238 | |
33345d01 | 239 | if (ino == BTRFS_FIRST_FREE_OBJECTID) { |
2ede0daf JB |
240 | key.objectid = BTRFS_I(inode)->root->root_key.objectid; |
241 | key.type = BTRFS_ROOT_BACKREF_KEY; | |
242 | key.offset = (u64)-1; | |
0b246afa | 243 | root = fs_info->tree_root; |
2ede0daf | 244 | } else { |
33345d01 | 245 | key.objectid = ino; |
4a0cc7ca | 246 | key.offset = btrfs_ino(BTRFS_I(dir)); |
2ede0daf JB |
247 | key.type = BTRFS_INODE_REF_KEY; |
248 | } | |
249 | ||
250 | ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); | |
251 | if (ret < 0) { | |
252 | btrfs_free_path(path); | |
253 | return ret; | |
254 | } else if (ret > 0) { | |
33345d01 | 255 | if (ino == BTRFS_FIRST_FREE_OBJECTID) { |
2ede0daf JB |
256 | path->slots[0]--; |
257 | } else { | |
258 | btrfs_free_path(path); | |
259 | return -ENOENT; | |
260 | } | |
261 | } | |
262 | leaf = path->nodes[0]; | |
263 | ||
33345d01 LZ |
264 | if (ino == BTRFS_FIRST_FREE_OBJECTID) { |
265 | rref = btrfs_item_ptr(leaf, path->slots[0], | |
2ede0daf | 266 | struct btrfs_root_ref); |
33345d01 LZ |
267 | name_ptr = (unsigned long)(rref + 1); |
268 | name_len = btrfs_root_ref_name_len(leaf, rref); | |
2ede0daf JB |
269 | } else { |
270 | iref = btrfs_item_ptr(leaf, path->slots[0], | |
271 | struct btrfs_inode_ref); | |
272 | name_ptr = (unsigned long)(iref + 1); | |
273 | name_len = btrfs_inode_ref_name_len(leaf, iref); | |
274 | } | |
275 | ||
276 | read_extent_buffer(leaf, name, name_ptr, name_len); | |
277 | btrfs_free_path(path); | |
278 | ||
279 | /* | |
280 | * have to add the null termination to make sure that reconnect_path | |
281 | * gets the right len for strlen | |
282 | */ | |
283 | name[name_len] = '\0'; | |
284 | ||
285 | return 0; | |
286 | } | |
287 | ||
be6e8dc0 BR |
288 | const struct export_operations btrfs_export_ops = { |
289 | .encode_fh = btrfs_encode_fh, | |
290 | .fh_to_dentry = btrfs_fh_to_dentry, | |
291 | .fh_to_parent = btrfs_fh_to_parent, | |
292 | .get_parent = btrfs_get_parent, | |
2ede0daf | 293 | .get_name = btrfs_get_name, |
be6e8dc0 | 294 | }; |