aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r--fs/fuse/dir.c70
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 */
184static 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
196static int invalid_nodeid(u64 nodeid) 180static 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 */
199static 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
211static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, 213static 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;