aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/fuse/dir.c70
-rw-r--r--fs/fuse/fuse_i.h3
-rw-r--r--fs/fuse/inode.c5
3 files changed, 51 insertions, 27 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;
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 69c7750d55b8..91edb8932d90 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -239,6 +239,9 @@ struct fuse_conn {
239 /** Lock protecting accessess to members of this structure */ 239 /** Lock protecting accessess to members of this structure */
240 spinlock_t lock; 240 spinlock_t lock;
241 241
242 /** Mutex protecting against directory alias creation */
243 struct mutex inst_mutex;
244
242 /** Refcount */ 245 /** Refcount */
243 atomic_t count; 246 atomic_t count;
244 247
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 4ee8f72e6380..fc4203570370 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -379,6 +379,7 @@ static struct fuse_conn *new_conn(void)
379 fc = kzalloc(sizeof(*fc), GFP_KERNEL); 379 fc = kzalloc(sizeof(*fc), GFP_KERNEL);
380 if (fc) { 380 if (fc) {
381 spin_lock_init(&fc->lock); 381 spin_lock_init(&fc->lock);
382 mutex_init(&fc->inst_mutex);
382 atomic_set(&fc->count, 1); 383 atomic_set(&fc->count, 1);
383 init_waitqueue_head(&fc->waitq); 384 init_waitqueue_head(&fc->waitq);
384 init_waitqueue_head(&fc->blocked_waitq); 385 init_waitqueue_head(&fc->blocked_waitq);
@@ -398,8 +399,10 @@ static struct fuse_conn *new_conn(void)
398 399
399void fuse_conn_put(struct fuse_conn *fc) 400void fuse_conn_put(struct fuse_conn *fc)
400{ 401{
401 if (atomic_dec_and_test(&fc->count)) 402 if (atomic_dec_and_test(&fc->count)) {
403 mutex_destroy(&fc->inst_mutex);
402 kfree(fc); 404 kfree(fc);
405 }
403} 406}
404 407
405struct fuse_conn *fuse_conn_get(struct fuse_conn *fc) 408struct fuse_conn *fuse_conn_get(struct fuse_conn *fc)