diff options
-rw-r--r-- | fs/fuse/dir.c | 70 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 3 | ||||
-rw-r--r-- | fs/fuse/inode.c | 5 |
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 | */ | ||
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; |
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 | ||
399 | void fuse_conn_put(struct fuse_conn *fc) | 400 | void 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 | ||
405 | struct fuse_conn *fuse_conn_get(struct fuse_conn *fc) | 408 | struct fuse_conn *fuse_conn_get(struct fuse_conn *fc) |