All of lore.kernel.org
 help / color / mirror / Atom feed
From: Maxim Patlasov <mpatlasov@parallels.com>
To: Miklos Szeredi <miklos@szeredi.hu>
Cc: <fuse-devel@lists.sourceforge.net>,
	<linux-fsdevel@vger.kernel.org>, <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH 2/4] fuse: writepages: crop secondary requests
Date: Thu, 3 Oct 2013 17:28:30 +0400	[thread overview]
Message-ID: <524D70FE.5000701@parallels.com> (raw)
In-Reply-To: <20131003095749.GB14242@tucsk.piliscsaba.szeredi.hu>

On 10/03/2013 01:57 PM, Miklos Szeredi wrote:
> On Wed, Oct 02, 2013 at 09:38:32PM +0400, Maxim Patlasov wrote:
>> If writeback happens while fuse is in FUSE_NOWRITE condition, the request
>> will be queued but not processed immediately (see fuse_flush_writepages()).
>> Until FUSE_NOWRITE becomes relaxed, more writebacks can happen. They will
>> be queued as "secondary" requests to that first ("primary") request.
>>
>> Existing implementation crops only primary request. This is not correct
>> because a subsequent extending write(2) may increase i_size and then secondary
>> requests won't be cropped properly. The result would be stale data written to
>> the server to a file offset where zeros must be.
>>
>> Similar problem may happen if secondary requests are attached to an in-flight
>> request that was already cropped.
>>
>> The patch solves the issue by cropping all secondary requests in
>> fuse_writepage_end(). Thanks to Miklos for idea.
> How about this, even simpler, one?

Very cute, but unfortunately it has a flaw. See please inline comment below.

>
> Thanks,
> Miklos
>
>
> Index: linux/fs/fuse/file.c
> ===================================================================
> --- linux.orig/fs/fuse/file.c	2013-10-03 11:27:00.597084704 +0200
> +++ linux/fs/fuse/file.c	2013-10-03 11:53:30.477208467 +0200
> @@ -1436,12 +1436,12 @@ static void fuse_writepage_finish(struct
>   }
>   
>   /* Called under fc->lock, may release and reacquire it */
> -static void fuse_send_writepage(struct fuse_conn *fc, struct fuse_req *req)
> +static void fuse_send_writepage(struct fuse_conn *fc, struct fuse_req *req,
> +				loff_t size)
>   __releases(fc->lock)
>   __acquires(fc->lock)
>   {
>   	struct fuse_inode *fi = get_fuse_inode(req->inode);
> -	loff_t size = i_size_read(req->inode);
>   	struct fuse_write_in *inarg = &req->misc.write.in;
>   	__u64 data_size = req->num_pages * PAGE_CACHE_SIZE;
>   
> @@ -1476,7 +1476,7 @@ __acquires(fc->lock)
>    *
>    * Called with fc->lock
>    */
> -void fuse_flush_writepages(struct inode *inode)
> +void __fuse_flush_writepages(struct inode *inode, loff_t crop)
>   __releases(fc->lock)
>   __acquires(fc->lock)
>   {
> @@ -1487,9 +1487,15 @@ __acquires(fc->lock)
>   	while (fi->writectr >= 0 && !list_empty(&fi->queued_writes)) {
>   		req = list_entry(fi->queued_writes.next, struct fuse_req, list);
>   		list_del_init(&req->list);
> -		fuse_send_writepage(fc, req);
> +		fuse_send_writepage(fc, req, crop);
>   	}
>   }
> +void fuse_flush_writepages(struct inode *inode)
> +__releases(fc->lock)
> +__acquires(fc->lock)
> +{
> +	__fuse_flush_writepages(inode, i_size_read(inode));
> +}
>   
>   static void fuse_writepage_end(struct fuse_conn *fc, struct fuse_req *req)
>   {
> @@ -1499,12 +1505,13 @@ static void fuse_writepage_end(struct fu
>   	mapping_set_error(inode->i_mapping, req->out.h.error);
>   	spin_lock(&fc->lock);
>   	while (req->misc.write.next) {
> +		struct fuse_write_in *inarg = &req->misc.write.in;
>   		struct fuse_req *next = req->misc.write.next;
>   		req->misc.write.next = next->misc.write.next;
>   		next->misc.write.next = NULL;
>   		list_add(&next->writepages_entry, &fi->writepages);
>   		list_add_tail(&next->list, &fi->queued_writes);
> -		fuse_flush_writepages(inode);
> +		__fuse_flush_writepages(inode, inarg->offset + inarg->size);

__fuse_flush_writepages() will ignore its 'crop' arg if fi->writectr is 
below zero. This can easily happen if a request is finalized after 
fuse_set_nowrite(). So in a scenario like this:

1. There is an in-flight primary request with a chain of secondary ones.
2. User calls ftruncate(2) to extend file; fuse_set_nowrite() makes 
fi->writectr negative and starts waiting for completion of that 
in-flight request
3. Userspace fuse daemon ACKs the request and fuse_writepage_end() is 
called; it calls __fuse_flush_writepages(), but the latter does nothing 
because fi->writectr < 0
4. fuse_do_setattr() proceeds extending i_size and calling 
__fuse_release_nowrite(). But now new (increased) i_size will be used as 
'crop' arg of __fuse_flush_writepages()

stale data can leak to the server.

Thanks,
Maxim

  reply	other threads:[~2013-10-03 13:28 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-10-02 17:37 [PATCH 0/4] fuse: fixes for fuse_writepage_in_flight() and friends -v2 Maxim Patlasov
2013-10-02 17:38 ` [PATCH 1/4] fuse: writepages: roll back changes if request not found Maxim Patlasov
2013-10-02 17:38 ` [PATCH 2/4] fuse: writepages: crop secondary requests Maxim Patlasov
2013-10-03  9:57   ` Miklos Szeredi
2013-10-03 13:28     ` Maxim Patlasov [this message]
2013-10-03 15:14       ` Miklos Szeredi
2013-10-03 15:50         ` Maxim Patlasov
2013-10-03 16:09           ` Miklos Szeredi
2013-10-03 16:22             ` Maxim Patlasov
2013-10-09  8:20               ` [fuse-devel] " Maxim Patlasov
2013-10-09 15:37                 ` Maxim Patlasov
2013-10-02 17:38 ` [PATCH 3/4] fuse: writepage: update bdi writeout when deleting secondary request Maxim Patlasov
2013-10-03 10:26   ` Miklos Szeredi
2013-10-03 13:46     ` Maxim Patlasov
2013-10-02 17:38 ` [PATCH 4/4] fuse: writepages: protect secondary requests from fuse file release Maxim Patlasov
2013-10-03 10:33   ` Miklos Szeredi

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=524D70FE.5000701@parallels.com \
    --to=mpatlasov@parallels.com \
    --cc=fuse-devel@lists.sourceforge.net \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.