diff options
Diffstat (limited to 'fs/btrfs/export.c')
-rw-r--r-- | fs/btrfs/export.c | 133 |
1 files changed, 85 insertions, 48 deletions
diff --git a/fs/btrfs/export.c b/fs/btrfs/export.c index 9596b40caa4e..ba5c3fd5ab8c 100644 --- a/fs/btrfs/export.c +++ b/fs/btrfs/export.c | |||
@@ -28,7 +28,7 @@ static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len, | |||
28 | len = BTRFS_FID_SIZE_NON_CONNECTABLE; | 28 | len = BTRFS_FID_SIZE_NON_CONNECTABLE; |
29 | type = FILEID_BTRFS_WITHOUT_PARENT; | 29 | type = FILEID_BTRFS_WITHOUT_PARENT; |
30 | 30 | ||
31 | fid->objectid = BTRFS_I(inode)->location.objectid; | 31 | fid->objectid = inode->i_ino; |
32 | fid->root_objectid = BTRFS_I(inode)->root->objectid; | 32 | fid->root_objectid = BTRFS_I(inode)->root->objectid; |
33 | fid->gen = inode->i_generation; | 33 | fid->gen = inode->i_generation; |
34 | 34 | ||
@@ -60,34 +60,61 @@ static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len, | |||
60 | } | 60 | } |
61 | 61 | ||
62 | static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid, | 62 | static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid, |
63 | u64 root_objectid, u32 generation) | 63 | u64 root_objectid, u32 generation, |
64 | int check_generation) | ||
64 | { | 65 | { |
66 | struct btrfs_fs_info *fs_info = btrfs_sb(sb)->fs_info; | ||
65 | struct btrfs_root *root; | 67 | struct btrfs_root *root; |
68 | struct dentry *dentry; | ||
66 | struct inode *inode; | 69 | struct inode *inode; |
67 | struct btrfs_key key; | 70 | struct btrfs_key key; |
71 | int index; | ||
72 | int err = 0; | ||
73 | |||
74 | if (objectid < BTRFS_FIRST_FREE_OBJECTID) | ||
75 | return ERR_PTR(-ESTALE); | ||
68 | 76 | ||
69 | key.objectid = root_objectid; | 77 | key.objectid = root_objectid; |
70 | btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); | 78 | btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); |
71 | key.offset = (u64)-1; | 79 | key.offset = (u64)-1; |
72 | 80 | ||
73 | root = btrfs_read_fs_root_no_name(btrfs_sb(sb)->fs_info, &key); | 81 | index = srcu_read_lock(&fs_info->subvol_srcu); |
74 | if (IS_ERR(root)) | 82 | |
75 | return ERR_CAST(root); | 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 | } | ||
76 | 93 | ||
77 | key.objectid = objectid; | 94 | key.objectid = objectid; |
78 | btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); | 95 | btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); |
79 | key.offset = 0; | 96 | key.offset = 0; |
80 | 97 | ||
81 | inode = btrfs_iget(sb, &key, root); | 98 | inode = btrfs_iget(sb, &key, root); |
82 | if (IS_ERR(inode)) | 99 | if (IS_ERR(inode)) { |
83 | return (void *)inode; | 100 | err = PTR_ERR(inode); |
101 | goto fail; | ||
102 | } | ||
103 | |||
104 | srcu_read_unlock(&fs_info->subvol_srcu, index); | ||
84 | 105 | ||
85 | if (generation != inode->i_generation) { | 106 | if (check_generation && generation != inode->i_generation) { |
86 | iput(inode); | 107 | iput(inode); |
87 | return ERR_PTR(-ESTALE); | 108 | return ERR_PTR(-ESTALE); |
88 | } | 109 | } |
89 | 110 | ||
90 | return d_obtain_alias(inode); | 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); | ||
91 | } | 118 | } |
92 | 119 | ||
93 | static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh, | 120 | static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh, |
@@ -111,7 +138,7 @@ static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh, | |||
111 | objectid = fid->parent_objectid; | 138 | objectid = fid->parent_objectid; |
112 | generation = fid->parent_gen; | 139 | generation = fid->parent_gen; |
113 | 140 | ||
114 | return btrfs_get_dentry(sb, objectid, root_objectid, generation); | 141 | return btrfs_get_dentry(sb, objectid, root_objectid, generation, 1); |
115 | } | 142 | } |
116 | 143 | ||
117 | static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh, | 144 | static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh, |
@@ -133,66 +160,76 @@ static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh, | |||
133 | root_objectid = fid->root_objectid; | 160 | root_objectid = fid->root_objectid; |
134 | generation = fid->gen; | 161 | generation = fid->gen; |
135 | 162 | ||
136 | return btrfs_get_dentry(sb, objectid, root_objectid, generation); | 163 | return btrfs_get_dentry(sb, objectid, root_objectid, generation, 1); |
137 | } | 164 | } |
138 | 165 | ||
139 | static struct dentry *btrfs_get_parent(struct dentry *child) | 166 | static struct dentry *btrfs_get_parent(struct dentry *child) |
140 | { | 167 | { |
141 | struct inode *dir = child->d_inode; | 168 | struct inode *dir = child->d_inode; |
169 | static struct dentry *dentry; | ||
142 | struct btrfs_root *root = BTRFS_I(dir)->root; | 170 | struct btrfs_root *root = BTRFS_I(dir)->root; |
143 | struct btrfs_key key; | ||
144 | struct btrfs_path *path; | 171 | struct btrfs_path *path; |
145 | struct extent_buffer *leaf; | 172 | struct extent_buffer *leaf; |
146 | int slot; | 173 | struct btrfs_root_ref *ref; |
147 | u64 objectid; | 174 | struct btrfs_key key; |
175 | struct btrfs_key found_key; | ||
148 | int ret; | 176 | int ret; |
149 | 177 | ||
150 | path = btrfs_alloc_path(); | 178 | path = btrfs_alloc_path(); |
151 | 179 | ||
152 | key.objectid = dir->i_ino; | 180 | if (dir->i_ino == BTRFS_FIRST_FREE_OBJECTID) { |
153 | btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY); | 181 | key.objectid = root->root_key.objectid; |
154 | key.offset = (u64)-1; | 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 | } | ||
155 | 190 | ||
156 | ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); | 191 | ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); |
157 | if (ret < 0) { | 192 | if (ret < 0) |
158 | /* Error */ | 193 | goto fail; |
159 | btrfs_free_path(path); | 194 | |
160 | return ERR_PTR(ret); | 195 | BUG_ON(ret == 0); |
196 | if (path->slots[0] == 0) { | ||
197 | ret = -ENOENT; | ||
198 | goto fail; | ||
161 | } | 199 | } |
200 | |||
201 | path->slots[0]--; | ||
162 | leaf = path->nodes[0]; | 202 | leaf = path->nodes[0]; |
163 | slot = path->slots[0]; | 203 | |
164 | if (ret) { | 204 | btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); |
165 | /* btrfs_search_slot() returns the slot where we'd want to | 205 | if (found_key.objectid != key.objectid || found_key.type != key.type) { |
166 | insert a backref for parent inode #0xFFFFFFFFFFFFFFFF. | 206 | ret = -ENOENT; |
167 | The _real_ backref, telling us what the parent inode | 207 | goto fail; |
168 | _actually_ is, will be in the slot _before_ the one | ||
169 | that btrfs_search_slot() returns. */ | ||
170 | if (!slot) { | ||
171 | /* Unless there is _no_ key in the tree before... */ | ||
172 | btrfs_free_path(path); | ||
173 | return ERR_PTR(-EIO); | ||
174 | } | ||
175 | slot--; | ||
176 | } | 208 | } |
177 | 209 | ||
178 | btrfs_item_key_to_cpu(leaf, &key, slot); | 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 | } | ||
179 | btrfs_free_path(path); | 217 | btrfs_free_path(path); |
180 | 218 | ||
181 | if (key.objectid != dir->i_ino || key.type != BTRFS_INODE_REF_KEY) | 219 | if (found_key.type == BTRFS_ROOT_BACKREF_KEY) { |
182 | return ERR_PTR(-EINVAL); | 220 | return btrfs_get_dentry(root->fs_info->sb, key.objectid, |
183 | 221 | found_key.offset, 0, 0); | |
184 | objectid = key.offset; | 222 | } |
185 | |||
186 | /* If we are already at the root of a subvol, return the real root */ | ||
187 | if (objectid == dir->i_ino) | ||
188 | return dget(dir->i_sb->s_root); | ||
189 | 223 | ||
190 | /* Build a new key for the inode item */ | 224 | key.type = BTRFS_INODE_ITEM_KEY; |
191 | key.objectid = objectid; | ||
192 | btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); | ||
193 | key.offset = 0; | 225 | key.offset = 0; |
194 | 226 | dentry = d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root)); | |
195 | return d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root)); | 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); | ||
196 | } | 233 | } |
197 | 234 | ||
198 | const struct export_operations btrfs_export_ops = { | 235 | const struct export_operations btrfs_export_ops = { |