OSDN Git Service

Merge tag 'fuse-update-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/mszered...
[tomoyo/tomoyo-test1.git] / fs / fuse / file.c
index e8aa533..09ef2a4 100644 (file)
@@ -802,21 +802,12 @@ static void fuse_short_read(struct inode *inode, u64 attr_ver, size_t num_read,
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
 
-       if (fc->writeback_cache) {
-               /*
-                * A hole in a file. Some data after the hole are in page cache,
-                * but have not reached the client fs yet. So, the hole is not
-                * present there.
-                */
-               int i;
-               int start_idx = num_read >> PAGE_SHIFT;
-               size_t off = num_read & (PAGE_SIZE - 1);
-
-               for (i = start_idx; i < ap->num_pages; i++) {
-                       zero_user_segment(ap->pages[i], off, PAGE_SIZE);
-                       off = 0;
-               }
-       } else {
+       /*
+        * If writeback_cache is enabled, a short read means there's a hole in
+        * the file.  Some data after the hole is in page cache, but has not
+        * reached the client fs yet.  So the hole is not present there.
+        */
+       if (!fc->writeback_cache) {
                loff_t pos = page_offset(ap->pages[0]) + num_read;
                fuse_read_update_size(inode, pos, attr_ver);
        }
@@ -1103,6 +1094,7 @@ static ssize_t fuse_send_write_pages(struct fuse_io_args *ia,
        struct fuse_file *ff = file->private_data;
        struct fuse_mount *fm = ff->fm;
        unsigned int offset, i;
+       bool short_write;
        int err;
 
        for (i = 0; i < ap->num_pages; i++)
@@ -1117,32 +1109,38 @@ static ssize_t fuse_send_write_pages(struct fuse_io_args *ia,
        if (!err && ia->write.out.size > count)
                err = -EIO;
 
+       short_write = ia->write.out.size < count;
        offset = ap->descs[0].offset;
        count = ia->write.out.size;
        for (i = 0; i < ap->num_pages; i++) {
                struct page *page = ap->pages[i];
 
-               if (!err && !offset && count >= PAGE_SIZE)
-                       SetPageUptodate(page);
-
-               if (count > PAGE_SIZE - offset)
-                       count -= PAGE_SIZE - offset;
-               else
-                       count = 0;
-               offset = 0;
-
-               unlock_page(page);
+               if (err) {
+                       ClearPageUptodate(page);
+               } else {
+                       if (count >= PAGE_SIZE - offset)
+                               count -= PAGE_SIZE - offset;
+                       else {
+                               if (short_write)
+                                       ClearPageUptodate(page);
+                               count = 0;
+                       }
+                       offset = 0;
+               }
+               if (ia->write.page_locked && (i == ap->num_pages - 1))
+                       unlock_page(page);
                put_page(page);
        }
 
        return err;
 }
 
-static ssize_t fuse_fill_write_pages(struct fuse_args_pages *ap,
+static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia,
                                     struct address_space *mapping,
                                     struct iov_iter *ii, loff_t pos,
                                     unsigned int max_pages)
 {
+       struct fuse_args_pages *ap = &ia->ap;
        struct fuse_conn *fc = get_fuse_conn(mapping->host);
        unsigned offset = pos & (PAGE_SIZE - 1);
        size_t count = 0;
@@ -1195,6 +1193,16 @@ static ssize_t fuse_fill_write_pages(struct fuse_args_pages *ap,
                if (offset == PAGE_SIZE)
                        offset = 0;
 
+               /* If we copied full page, mark it uptodate */
+               if (tmp == PAGE_SIZE)
+                       SetPageUptodate(page);
+
+               if (PageUptodate(page)) {
+                       unlock_page(page);
+               } else {
+                       ia->write.page_locked = true;
+                       break;
+               }
                if (!fc->big_writes)
                        break;
        } while (iov_iter_count(ii) && count < fc->max_write &&
@@ -1238,7 +1246,7 @@ static ssize_t fuse_perform_write(struct kiocb *iocb,
                        break;
                }
 
-               count = fuse_fill_write_pages(ap, mapping, ii, pos, nr_pages);
+               count = fuse_fill_write_pages(&ia, mapping, ii, pos, nr_pages);
                if (count <= 0) {
                        err = count;
                } else {
@@ -1753,8 +1761,17 @@ static void fuse_writepage_end(struct fuse_mount *fm, struct fuse_args *args,
                container_of(args, typeof(*wpa), ia.ap.args);
        struct inode *inode = wpa->inode;
        struct fuse_inode *fi = get_fuse_inode(inode);
+       struct fuse_conn *fc = get_fuse_conn(inode);
 
        mapping_set_error(inode->i_mapping, error);
+       /*
+        * A writeback finished and this might have updated mtime/ctime on
+        * server making local mtime/ctime stale.  Hence invalidate attrs.
+        * Do this only if writeback_cache is not enabled.  If writeback_cache
+        * is enabled, we trust local ctime/mtime.
+        */
+       if (!fc->writeback_cache)
+               fuse_invalidate_attr(inode);
        spin_lock(&fi->lock);
        rb_erase(&wpa->writepages_entry, &fi->writepages);
        while (wpa->next) {