cachefiles: Fix __cachefiles_prepare_write()
authorDavid Howells <dhowells@redhat.com>
Tue, 2 Jan 2024 16:28:04 +0000 (16:28 +0000)
committerDavid Howells <dhowells@redhat.com>
Wed, 3 Jan 2024 14:52:53 +0000 (14:52 +0000)
Fix __cachefiles_prepare_write() to correctly determine whether the
requested write will fit correctly with the DIO alignment.

Reported-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Yiqun Leng <yqleng@linux.alibaba.com>
Tested-by: Jia Zhu <zhujia.zj@bytedance.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: linux-cachefs@redhat.com
cc: linux-erofs@lists.ozlabs.org
cc: linux-fsdevel@vger.kernel.org
cc: linux-mm@kvack.org

fs/cachefiles/io.c

index bffffedce4a93936aab4298815510f5cb3c79d11..7529b40bc95a98e22efd5284676b5f564bf53825 100644 (file)
@@ -522,16 +522,22 @@ int __cachefiles_prepare_write(struct cachefiles_object *object,
                               bool no_space_allocated_yet)
 {
        struct cachefiles_cache *cache = object->volume->cache;
-       loff_t start = *_start, pos;
-       size_t len = *_len, down;
+       unsigned long long start = *_start, pos;
+       size_t len = *_len;
        int ret;
 
        /* Round to DIO size */
-       down = start - round_down(start, PAGE_SIZE);
-       *_start = start - down;
-       *_len = round_up(down + len, PAGE_SIZE);
-       if (down < start || *_len > upper_len)
+       start = round_down(*_start, PAGE_SIZE);
+       if (start != *_start) {
+               kleave(" = -ENOBUFS [down]");
+               return -ENOBUFS;
+       }
+       if (*_len > upper_len) {
+               kleave(" = -ENOBUFS [up]");
                return -ENOBUFS;
+       }
+
+       *_len = round_up(len, PAGE_SIZE);
 
        /* We need to work out whether there's sufficient disk space to perform
         * the write - but we can skip that check if we have space already
@@ -542,7 +548,7 @@ int __cachefiles_prepare_write(struct cachefiles_object *object,
 
        pos = cachefiles_inject_read_error();
        if (pos == 0)
-               pos = vfs_llseek(file, *_start, SEEK_DATA);
+               pos = vfs_llseek(file, start, SEEK_DATA);
        if (pos < 0 && pos >= (loff_t)-MAX_ERRNO) {
                if (pos == -ENXIO)
                        goto check_space; /* Unallocated tail */
@@ -550,7 +556,7 @@ int __cachefiles_prepare_write(struct cachefiles_object *object,
                                          cachefiles_trace_seek_error);
                return pos;
        }
-       if ((u64)pos >= (u64)*_start + *_len)
+       if (pos >= start + *_len)
                goto check_space; /* Unallocated region */
 
        /* We have a block that's at least partially filled - if we're low on
@@ -563,13 +569,13 @@ int __cachefiles_prepare_write(struct cachefiles_object *object,
 
        pos = cachefiles_inject_read_error();
        if (pos == 0)
-               pos = vfs_llseek(file, *_start, SEEK_HOLE);
+               pos = vfs_llseek(file, start, SEEK_HOLE);
        if (pos < 0 && pos >= (loff_t)-MAX_ERRNO) {
                trace_cachefiles_io_error(object, file_inode(file), pos,
                                          cachefiles_trace_seek_error);
                return pos;
        }
-       if ((u64)pos >= (u64)*_start + *_len)
+       if (pos >= start + *_len)
                return 0; /* Fully allocated */
 
        /* Partially allocated, but insufficient space: cull. */
@@ -577,7 +583,7 @@ int __cachefiles_prepare_write(struct cachefiles_object *object,
        ret = cachefiles_inject_remove_error();
        if (ret == 0)
                ret = vfs_fallocate(file, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
-                                   *_start, *_len);
+                                   start, *_len);
        if (ret < 0) {
                trace_cachefiles_io_error(object, file_inode(file), ret,
                                          cachefiles_trace_fallocate_error);