Squashfs: Rewrite and update squashfs_readahead_fragment() to not use page->index
authorPhillip Lougher <phillip@squashfs.org.uk>
Sun, 18 Aug 2024 23:58:47 +0000 (00:58 +0100)
committerChristian Brauner <brauner@kernel.org>
Mon, 19 Aug 2024 12:08:21 +0000 (14:08 +0200)
The previous implementation lacked error checking (e.g. the bytes
returned by squashfs_fill_page() is not checked), and the use of
page->index could not be removed without substantially rewriting
the routine to use the page actor abstraction used elsewhere.

Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
Link: https://lore.kernel.org/r/20240818235847.170468-5-phillip@squashfs.org.uk
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/squashfs/file.c

index 50fe5a078b831c21a84a788ab565c9ae5d85e80d..5a3745e5202520ed684fb8c33b8b1820ecca7931 100644 (file)
@@ -494,39 +494,73 @@ out:
 }
 
 static int squashfs_readahead_fragment(struct page **page,
-       unsigned int pages, unsigned int expected)
+       unsigned int pages, unsigned int expected, loff_t start)
 {
        struct inode *inode = page[0]->mapping->host;
        struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb,
                squashfs_i(inode)->fragment_block,
                squashfs_i(inode)->fragment_size);
        struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-       unsigned int n, mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1;
-       int error = buffer->error;
+       int i, bytes, copied;
+       struct squashfs_page_actor *actor;
+       unsigned int offset;
+       void *addr;
+       struct page *last_page;
 
-       if (error)
+       if (buffer->error)
                goto out;
 
-       expected += squashfs_i(inode)->fragment_offset;
+       actor = squashfs_page_actor_init_special(msblk, page, pages,
+                                                       expected, start);
+       if (!actor)
+               goto out;
 
-       for (n = 0; n < pages; n++) {
-               unsigned int base = (page[n]->index & mask) << PAGE_SHIFT;
-               unsigned int offset = base + squashfs_i(inode)->fragment_offset;
+       squashfs_actor_nobuff(actor);
+       addr = squashfs_first_page(actor);
 
-               if (expected > offset) {
-                       unsigned int avail = min_t(unsigned int, expected -
-                               offset, PAGE_SIZE);
+       for (copied = offset = 0; offset < expected; offset += PAGE_SIZE) {
+               int avail = min_t(int, expected - offset, PAGE_SIZE);
 
-                       squashfs_fill_page(page[n], buffer, offset, avail);
+               if (!IS_ERR(addr)) {
+                       bytes = squashfs_copy_data(addr, buffer, offset +
+                                       squashfs_i(inode)->fragment_offset, avail);
+
+                       if (bytes != avail)
+                               goto failed;
                }
 
-               unlock_page(page[n]);
-               put_page(page[n]);
+               copied += avail;
+               addr = squashfs_next_page(actor);
        }
 
+       last_page = squashfs_page_actor_free(actor);
+
+       if (copied == expected) {
+               /* Last page (if present) may have trailing bytes not filled */
+               bytes = copied % PAGE_SIZE;
+               if (bytes && last_page)
+                       memzero_page(last_page, bytes, PAGE_SIZE - bytes);
+
+               for (i = 0; i < pages; i++) {
+                       flush_dcache_page(page[i]);
+                       SetPageUptodate(page[i]);
+               }
+       }
+
+       for (i = 0; i < pages; i++) {
+               unlock_page(page[i]);
+               put_page(page[i]);
+       }
+
+       squashfs_cache_put(buffer);
+       return 0;
+
+failed:
+       squashfs_page_actor_free(actor);
+
 out:
        squashfs_cache_put(buffer);
-       return error;
+       return 1;
 }
 
 static void squashfs_readahead(struct readahead_control *ractl)
@@ -572,7 +606,7 @@ static void squashfs_readahead(struct readahead_control *ractl)
                if (start >> msblk->block_log == file_end &&
                                squashfs_i(inode)->fragment_block != SQUASHFS_INVALID_BLK) {
                        res = squashfs_readahead_fragment(pages, nr_pages,
-                                                         expected);
+                                                         expected, start);
                        if (res)
                                goto skip_pages;
                        continue;