aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse
diff options
context:
space:
mode:
authorJohn Muir <john@jmuir.com>2011-12-06 15:50:06 -0500
committerMiklos Szeredi <mszeredi@suse.cz>2011-12-13 05:58:49 -0500
commit451d0f599934fd97faf54a5d7954b518e66192cb (patch)
tree6dd7a6fcdc8ff3bc50dec37b114c447b3b1d7ba1 /fs/fuse
parentb18da0c56e9ff43a007b6c8e302c62e720964151 (diff)
FUSE: Notifying the kernel of deletion.
Allows a FUSE file-system to tell the kernel when a file or directory is deleted. If the specified dentry has the specified inode number, the kernel will unhash it. The current 'fuse_notify_inval_entry' does not cause the kernel to clean up directories that are in use properly, and as a result the users of those directories see incorrect semantics from the file-system. The error condition seen when 'fuse_notify_inval_entry' is used to notify of a deleted directory is avoided when 'fuse_notify_delete' is used instead. The following scenario demonstrates the difference: 1. User A chdirs into 'testdir' and starts reading 'testfile'. 2. User B rm -rf 'testdir'. 3. User B creates 'testdir'. 4. User C chdirs into 'testdir'. If you run the above within the same machine on any file-system (including fuse file-systems), there is no problem: user C is able to chdir into the new testdir. The old testdir is removed from the dentry tree, but still open by user A. If operations 2 and 3 are performed via the network such that the fuse file-system uses one of the notify functions to tell the kernel that the nodes are gone, then the following error occurs for user C while user A holds the original directory open: muirj@empacher:~> ls /test/testdir ls: cannot access /test/testdir: No such file or directory The issue here is that the kernel still has a dentry for testdir, and so it is requesting the attributes for the old directory, while the file-system is responding that the directory no longer exists. If on the other hand, if the file-system can notify the kernel that the directory is deleted using the new 'fuse_notify_delete' function, then the above ls will find the new directory as expected. Signed-off-by: John Muir <john@jmuir.com> Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs/fuse')
-rw-r--r--fs/fuse/dev.c57
-rw-r--r--fs/fuse/dir.c32
-rw-r--r--fs/fuse/fuse_i.h8
3 files changed, 93 insertions, 4 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 2aaf3eaaf13d..5f3368ab0fa9 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1378,7 +1378,59 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,
1378 down_read(&fc->killsb); 1378 down_read(&fc->killsb);
1379 err = -ENOENT; 1379 err = -ENOENT;
1380 if (fc->sb) 1380 if (fc->sb)
1381 err = fuse_reverse_inval_entry(fc->sb, outarg.parent, &name); 1381 err = fuse_reverse_inval_entry(fc->sb, outarg.parent, 0, &name);
1382 up_read(&fc->killsb);
1383 kfree(buf);
1384 return err;
1385
1386err:
1387 kfree(buf);
1388 fuse_copy_finish(cs);
1389 return err;
1390}
1391
1392static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size,
1393 struct fuse_copy_state *cs)
1394{
1395 struct fuse_notify_delete_out outarg;
1396 int err = -ENOMEM;
1397 char *buf;
1398 struct qstr name;
1399
1400 buf = kzalloc(FUSE_NAME_MAX + 1, GFP_KERNEL);
1401 if (!buf)
1402 goto err;
1403
1404 err = -EINVAL;
1405 if (size < sizeof(outarg))
1406 goto err;
1407
1408 err = fuse_copy_one(cs, &outarg, sizeof(outarg));
1409 if (err)
1410 goto err;
1411
1412 err = -ENAMETOOLONG;
1413 if (outarg.namelen > FUSE_NAME_MAX)
1414 goto err;
1415
1416 err = -EINVAL;
1417 if (size != sizeof(outarg) + outarg.namelen + 1)
1418 goto err;
1419
1420 name.name = buf;
1421 name.len = outarg.namelen;
1422 err = fuse_copy_one(cs, buf, outarg.namelen + 1);
1423 if (err)
1424 goto err;
1425 fuse_copy_finish(cs);
1426 buf[outarg.namelen] = 0;
1427 name.hash = full_name_hash(name.name, name.len);
1428
1429 down_read(&fc->killsb);
1430 err = -ENOENT;
1431 if (fc->sb)
1432 err = fuse_reverse_inval_entry(fc->sb, outarg.parent,
1433 outarg.child, &name);
1382 up_read(&fc->killsb); 1434 up_read(&fc->killsb);
1383 kfree(buf); 1435 kfree(buf);
1384 return err; 1436 return err;
@@ -1597,6 +1649,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
1597 case FUSE_NOTIFY_RETRIEVE: 1649 case FUSE_NOTIFY_RETRIEVE:
1598 return fuse_notify_retrieve(fc, size, cs); 1650 return fuse_notify_retrieve(fc, size, cs);
1599 1651
1652 case FUSE_NOTIFY_DELETE:
1653 return fuse_notify_delete(fc, size, cs);
1654
1600 default: 1655 default:
1601 fuse_copy_finish(cs); 1656 fuse_copy_finish(cs);
1602 return -EINVAL; 1657 return -EINVAL;
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 344577933f62..bef8c3011d31 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -868,7 +868,7 @@ int fuse_update_attributes(struct inode *inode, struct kstat *stat,
868} 868}
869 869
870int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, 870int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
871 struct qstr *name) 871 u64 child_nodeid, struct qstr *name)
872{ 872{
873 int err = -ENOTDIR; 873 int err = -ENOTDIR;
874 struct inode *parent; 874 struct inode *parent;
@@ -895,8 +895,36 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
895 895
896 fuse_invalidate_attr(parent); 896 fuse_invalidate_attr(parent);
897 fuse_invalidate_entry(entry); 897 fuse_invalidate_entry(entry);
898
899 if (child_nodeid != 0 && entry->d_inode) {
900 mutex_lock(&entry->d_inode->i_mutex);
901 if (get_node_id(entry->d_inode) != child_nodeid) {
902 err = -ENOENT;
903 goto badentry;
904 }
905 if (d_mountpoint(entry)) {
906 err = -EBUSY;
907 goto badentry;
908 }
909 if (S_ISDIR(entry->d_inode->i_mode)) {
910 shrink_dcache_parent(entry);
911 if (!simple_empty(entry)) {
912 err = -ENOTEMPTY;
913 goto badentry;
914 }
915 entry->d_inode->i_flags |= S_DEAD;
916 }
917 dont_mount(entry);
918 clear_nlink(entry->d_inode);
919 err = 0;
920 badentry:
921 mutex_unlock(&entry->d_inode->i_mutex);
922 if (!err)
923 d_delete(entry);
924 } else {
925 err = 0;
926 }
898 dput(entry); 927 dput(entry);
899 err = 0;
900 928
901 unlock: 929 unlock:
902 mutex_unlock(&parent->i_mutex); 930 mutex_unlock(&parent->i_mutex);
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 09337bcc2554..a571584a091a 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -755,9 +755,15 @@ int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid,
755/** 755/**
756 * File-system tells the kernel to invalidate parent attributes and 756 * File-system tells the kernel to invalidate parent attributes and
757 * the dentry matching parent/name. 757 * the dentry matching parent/name.
758 *
759 * If the child_nodeid is non-zero and:
760 * - matches the inode number for the dentry matching parent/name,
761 * - is not a mount point
762 * - is a file or oan empty directory
763 * then the dentry is unhashed (d_delete()).
758 */ 764 */
759int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, 765int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
760 struct qstr *name); 766 u64 child_nodeid, struct qstr *name);
761 767
762int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file, 768int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
763 bool isdir); 769 bool isdir);