From: "J. Bruce Fields" <bfields@fieldses.org>
To: John Muir <john@jmuir.com>
Cc: fuse-devel <fuse-devel@lists.sourceforge.net>,
Miklos Szeredi <miklos@szeredi.hu>,
Linux-Fsdevel <linux-fsdevel@vger.kernel.org>
Subject: Re: [PATCH] FUSE: Notifying the kernel of deletion.
Date: Tue, 6 Dec 2011 15:03:01 -0500 [thread overview]
Message-ID: <20111206200301.GC11788@fieldses.org> (raw)
In-Reply-To: <1F29A426-4358-4AEE-A774-149614B759F5@jmuir.com>
On Tue, Dec 06, 2011 at 07:50:33PM +0100, John Muir wrote:
> 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.
>
> Signed-off-by: John Muir <john@jmuir.com>
> ---
> Please find below a patch that add notification of deletion to the FUSE kernel interface. These patches allow the file-system to tell the kernel when a file (and more particularly) a directory is deleted. This is needed because using the current '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 'notify_inval_entry' is used to notify of a deleted directory is avoided when 'notify_delete' is used instead.
You've put all this extra text after the ---, meaning it would be
discarded before going into git. But this sort of "why we're doing
this" information is exactly what you want in a changelog.
--b.
>
> I'll demonstrate below with the following scenario:
> 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, on the other hand, the 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 my 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 'notify_delete' function, then the above ls will find the new directory as expected.
>
>
> diff -updr orig/fs/fuse/dev.c new/fs/fuse/dev.c
> --- orig/fs/fuse/dev.c 2011-12-01 23:56:01.000000000 +0100
> +++ new/fs/fuse/dev.c 2011-12-06 19:30:50.682000123 +0100
> @@ -1378,7 +1378,59 @@ static int fuse_notify_inval_entry(struc
> down_read(&fc->killsb);
> err = -ENOENT;
> if (fc->sb)
> - err = fuse_reverse_inval_entry(fc->sb, outarg.parent, &name);
> + err = fuse_reverse_inval_entry(fc->sb, outarg.parent, 0, &name);
> + up_read(&fc->killsb);
> + kfree(buf);
> + return err;
> +
> +err:
> + kfree(buf);
> + fuse_copy_finish(cs);
> + return err;
> +}
> +
> +static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size,
> + struct fuse_copy_state *cs)
> +{
> + struct fuse_notify_delete_out outarg;
> + int err = -ENOMEM;
> + char *buf;
> + struct qstr name;
> +
> + buf = kzalloc(FUSE_NAME_MAX + 1, GFP_KERNEL);
> + if (!buf)
> + goto err;
> +
> + err = -EINVAL;
> + if (size < sizeof(outarg))
> + goto err;
> +
> + err = fuse_copy_one(cs, &outarg, sizeof(outarg));
> + if (err)
> + goto err;
> +
> + err = -ENAMETOOLONG;
> + if (outarg.namelen > FUSE_NAME_MAX)
> + goto err;
> +
> + err = -EINVAL;
> + if (size != sizeof(outarg) + outarg.namelen + 1)
> + goto err;
> +
> + name.name = buf;
> + name.len = outarg.namelen;
> + err = fuse_copy_one(cs, buf, outarg.namelen + 1);
> + if (err)
> + goto err;
> + fuse_copy_finish(cs);
> + buf[outarg.namelen] = 0;
> + name.hash = full_name_hash(name.name, name.len);
> +
> + down_read(&fc->killsb);
> + err = -ENOENT;
> + if (fc->sb)
> + err = fuse_reverse_inval_entry(fc->sb, outarg.parent,
> + outarg.child, &name);
> up_read(&fc->killsb);
> kfree(buf);
> return err;
> @@ -1596,6 +1648,9 @@ static int fuse_notify(struct fuse_conn
> case FUSE_NOTIFY_RETRIEVE:
> return fuse_notify_retrieve(fc, size, cs);
>
> + case FUSE_NOTIFY_DELETE:
> + return fuse_notify_delete(fc, size, cs);
> +
> default:
> fuse_copy_finish(cs);
> return -EINVAL;
> diff -updr orig/fs/fuse/dir.c new/fs/fuse/dir.c
> --- orig/fs/fuse/dir.c 2011-12-01 23:56:01.000000000 +0100
> +++ new/fs/fuse/dir.c 2011-12-06 19:30:50.683000127 +0100
> @@ -868,7 +868,7 @@ int fuse_update_attributes(struct inode
> }
>
> int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
> - struct qstr *name)
> + u64 child_nodeid, struct qstr *name)
> {
> int err = -ENOTDIR;
> struct inode *parent;
> @@ -895,8 +895,36 @@ int fuse_reverse_inval_entry(struct supe
>
> fuse_invalidate_attr(parent);
> fuse_invalidate_entry(entry);
> +
> + if (child_nodeid != 0 && entry->d_inode) {
> + mutex_lock(&entry->d_inode->i_mutex);
> + if (get_node_id(entry->d_inode) != child_nodeid) {
> + err = -ENOENT;
> + goto badentry;
> + }
> + if (d_mountpoint(entry)) {
> + err = -EBUSY;
> + goto badentry;
> + }
> + if (S_ISDIR(entry->d_inode->i_mode)) {
> + shrink_dcache_parent(entry);
> + if (!simple_empty(entry)) {
> + err = -ENOTEMPTY;
> + goto badentry;
> + }
> + entry->d_inode->i_flags |= S_DEAD;
> + }
> + dont_mount(entry);
> + clear_nlink(entry->d_inode);
> + err = 0;
> + badentry:
> + mutex_unlock(&entry->d_inode->i_mutex);
> + if (!err)
> + d_delete(entry);
> + } else {
> + err = 0;
> + }
> dput(entry);
> - err = 0;
>
> unlock:
> mutex_unlock(&parent->i_mutex);
> diff -updr orig/fs/fuse/fuse_i.h new/fs/fuse/fuse_i.h
> --- orig/fs/fuse/fuse_i.h 2011-12-01 23:56:01.000000000 +0100
> +++ new/fs/fuse/fuse_i.h 2011-12-06 19:30:50.691000142 +0100
> @@ -755,9 +755,15 @@ int fuse_reverse_inval_inode(struct supe
> /**
> * File-system tells the kernel to invalidate parent attributes and
> * the dentry matching parent/name.
> + *
> + * If the child_nodeid is non-zero and:
> + * - matches the inode number for the dentry matching parent/name,
> + * - is not a mount point
> + * - is a file or oan empty directory
> + * then the dentry is unhashed (d_delete()).
> */
> int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
> - struct qstr *name);
> + u64 child_nodeid, struct qstr *name);
>
> int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
> bool isdir);
> diff -updr orig/include/linux/fuse.h new/include/linux/fuse.h
> --- orig/include/linux/fuse.h 2011-12-01 23:56:01.000000000 +0100
> +++ new/include/linux/fuse.h 2011-12-06 19:31:05.686000162 +0100
> @@ -283,6 +283,7 @@ enum fuse_notify_code {
> FUSE_NOTIFY_INVAL_ENTRY = 3,
> FUSE_NOTIFY_STORE = 4,
> FUSE_NOTIFY_RETRIEVE = 5,
> + FUSE_NOTIFY_DELETE = 6,
> FUSE_NOTIFY_CODE_MAX,
> };
>
> @@ -605,6 +606,13 @@ struct fuse_notify_inval_entry_out {
> __u32 namelen;
> __u32 padding;
> };
> +
> +struct fuse_notify_delete_out {
> + __u64 parent;
> + __u64 child;
> + __u32 namelen;
> + __u32 padding;
> +};
>
> struct fuse_notify_store_out {
> __u64 nodeid;
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
next prev parent reply other threads:[~2011-12-06 20:03 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-12-06 18:50 [PATCH] FUSE: Notifying the kernel of deletion John Muir
2011-12-06 20:03 ` J. Bruce Fields [this message]
2011-12-06 20:08 ` John Muir
2011-12-06 20:42 ` J. Bruce Fields
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20111206200301.GC11788@fieldses.org \
--to=bfields@fieldses.org \
--cc=fuse-devel@lists.sourceforge.net \
--cc=john@jmuir.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=miklos@szeredi.hu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).