diff options
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 70 |
1 files changed, 44 insertions, 26 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 7ecfe95795cd..9d0ef5e18740 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -177,22 +177,6 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) | |||
177 | return 1; | 177 | return 1; |
178 | } | 178 | } |
179 | 179 | ||
180 | /* | ||
181 | * Check if there's already a hashed alias of this directory inode. | ||
182 | * If yes, then lookup and mkdir must not create a new alias. | ||
183 | */ | ||
184 | static int dir_alias(struct inode *inode) | ||
185 | { | ||
186 | if (S_ISDIR(inode->i_mode)) { | ||
187 | struct dentry *alias = d_find_alias(inode); | ||
188 | if (alias) { | ||
189 | dput(alias); | ||
190 | return 1; | ||
191 | } | ||
192 | } | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static int invalid_nodeid(u64 nodeid) | 180 | static int invalid_nodeid(u64 nodeid) |
197 | { | 181 | { |
198 | return !nodeid || nodeid == FUSE_ROOT_ID; | 182 | return !nodeid || nodeid == FUSE_ROOT_ID; |
@@ -208,6 +192,24 @@ static int valid_mode(int m) | |||
208 | S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m); | 192 | S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m); |
209 | } | 193 | } |
210 | 194 | ||
195 | /* | ||
196 | * Add a directory inode to a dentry, ensuring that no other dentry | ||
197 | * refers to this inode. Called with fc->inst_mutex. | ||
198 | */ | ||
199 | static int fuse_d_add_directory(struct dentry *entry, struct inode *inode) | ||
200 | { | ||
201 | struct dentry *alias = d_find_alias(inode); | ||
202 | if (alias) { | ||
203 | /* This tries to shrink the subtree below alias */ | ||
204 | fuse_invalidate_entry(alias); | ||
205 | dput(alias); | ||
206 | if (!list_empty(&inode->i_dentry)) | ||
207 | return -EBUSY; | ||
208 | } | ||
209 | d_add(entry, inode); | ||
210 | return 0; | ||
211 | } | ||
212 | |||
211 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | 213 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, |
212 | struct nameidata *nd) | 214 | struct nameidata *nd) |
213 | { | 215 | { |
@@ -243,11 +245,17 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
243 | if (err && err != -ENOENT) | 245 | if (err && err != -ENOENT) |
244 | return ERR_PTR(err); | 246 | return ERR_PTR(err); |
245 | 247 | ||
246 | if (inode && dir_alias(inode)) { | 248 | if (inode && S_ISDIR(inode->i_mode)) { |
247 | iput(inode); | 249 | mutex_lock(&fc->inst_mutex); |
248 | return ERR_PTR(-EIO); | 250 | err = fuse_d_add_directory(entry, inode); |
249 | } | 251 | mutex_unlock(&fc->inst_mutex); |
250 | d_add(entry, inode); | 252 | if (err) { |
253 | iput(inode); | ||
254 | return ERR_PTR(err); | ||
255 | } | ||
256 | } else | ||
257 | d_add(entry, inode); | ||
258 | |||
251 | entry->d_op = &fuse_dentry_operations; | 259 | entry->d_op = &fuse_dentry_operations; |
252 | if (!err) | 260 | if (!err) |
253 | fuse_change_timeout(entry, &outarg); | 261 | fuse_change_timeout(entry, &outarg); |
@@ -403,12 +411,22 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, | |||
403 | } | 411 | } |
404 | fuse_put_request(fc, req); | 412 | fuse_put_request(fc, req); |
405 | 413 | ||
406 | if (dir_alias(inode)) { | 414 | if (S_ISDIR(inode->i_mode)) { |
407 | iput(inode); | 415 | struct dentry *alias; |
408 | return -EIO; | 416 | mutex_lock(&fc->inst_mutex); |
409 | } | 417 | alias = d_find_alias(inode); |
418 | if (alias) { | ||
419 | /* New directory must have moved since mkdir */ | ||
420 | mutex_unlock(&fc->inst_mutex); | ||
421 | dput(alias); | ||
422 | iput(inode); | ||
423 | return -EBUSY; | ||
424 | } | ||
425 | d_instantiate(entry, inode); | ||
426 | mutex_unlock(&fc->inst_mutex); | ||
427 | } else | ||
428 | d_instantiate(entry, inode); | ||
410 | 429 | ||
411 | d_instantiate(entry, inode); | ||
412 | fuse_change_timeout(entry, &outarg); | 430 | fuse_change_timeout(entry, &outarg); |
413 | fuse_invalidate_attr(dir); | 431 | fuse_invalidate_attr(dir); |
414 | return 0; | 432 | return 0; |