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