• David Howells's avatar
    cifs: Fix flushing, invalidation and file size with FICLONE · c54fc3a4
    David Howells authored
    Fix a number of issues in the cifs filesystem implementation of the FICLONE
    ioctl in cifs_remap_file_range().  This is analogous to the previously
    fixed bug in cifs_file_copychunk_range() and can share the helper
    functions.
    
    Firstly, the invalidation of the destination range is handled incorrectly:
    We shouldn't just invalidate the whole file as dirty data in the file may
    get lost and we can't just call truncate_inode_pages_range() to invalidate
    the destination range as that will erase parts of a partial folio at each
    end whilst invalidating and discarding all the folios in the middle.  We
    need to force all the folios covering the range to be reloaded, but we
    mustn't lose dirty data in them that's not in the destination range.
    
    Further, we shouldn't simply round out the range to PAGE_SIZE at each end
    as cifs should move to support multipage folios.
    
    Secondly, there's an issue whereby a write may have extended the file
    locally, but not have been written back yet.  This can leaves the local
    idea of the EOF at a later point than the server's EOF.  If a clone request
    is issued, this will fail on the server with STATUS_INVALID_VIEW_SIZE
    (which gets translated to -EIO locally) if the clone source extends past
    the server's EOF.
    
    Fix this by:
    
     (0) Flush the source region (already done).  The flush does nothing and
         the EOF isn't moved if the source region has no dirty data.
    
     (1) Move the EOF to the end of the source region if it isn't already at
         least at this point.  If we can't do this, for instance if the server
         doesn't support it, just flush the entire source file.
    
     (2) Find the folio (if present) at each end of the range, flushing it and
         increasing the region-to-be-invalidated to cover those in their
         entirety.
    
     (3) Fully discard all the folios covering the range as we want them to be
         reloaded.
    
     (4) Then perform the extent duplication.
    
    Thirdly, set i_size after doing the duplicate_extents operation as this
    value may be used by various things internally.  stat() hides the issue
    because setting ->time to 0 causes cifs_getatr() to revalidate the
    attributes.
    
    These were causing the cifs/001 xfstest to fail.
    
    Fixes: 04b38d60 ("vfs: pull btrfs clone API to vfs layer")
    Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
    Cc: stable@vger.kernel.org
    cc: Christoph Hellwig <hch@lst.de>
    cc: Paulo Alcantara <pc@manguebit.com>
    cc: Shyam Prasad N <nspmangalore@gmail.com>
    cc: Rohith Surabattula <rohiths.msft@gmail.com>
    cc: Matthew Wilcox <willy@infradead.org>
    cc: Jeff Layton <jlayton@kernel.org>
    cc: linux-cifs@vger.kernel.org
    cc: linux-mm@kvack.org
    Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
    Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
    c54fc3a4
cifsfs.c 54.4 KB