Merge tag 'input-for-v6.1-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor...
[platform/kernel/linux-starfive.git] / fs / squashfs / file_direct.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2013
4  * Phillip Lougher <phillip@squashfs.org.uk>
5  */
6
7 #include <linux/fs.h>
8 #include <linux/vfs.h>
9 #include <linux/kernel.h>
10 #include <linux/slab.h>
11 #include <linux/string.h>
12 #include <linux/pagemap.h>
13 #include <linux/mutex.h>
14
15 #include "squashfs_fs.h"
16 #include "squashfs_fs_sb.h"
17 #include "squashfs_fs_i.h"
18 #include "squashfs.h"
19 #include "page_actor.h"
20
21 /* Read separately compressed datablock directly into page cache */
22 int squashfs_readpage_block(struct page *target_page, u64 block, int bsize,
23         int expected)
24
25 {
26         struct inode *inode = target_page->mapping->host;
27         struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
28
29         int file_end = (i_size_read(inode) - 1) >> PAGE_SHIFT;
30         int mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1;
31         int start_index = target_page->index & ~mask;
32         int end_index = start_index | mask;
33         int i, n, pages, bytes, res = -ENOMEM;
34         struct page **page;
35         struct squashfs_page_actor *actor;
36         void *pageaddr;
37
38         if (end_index > file_end)
39                 end_index = file_end;
40
41         pages = end_index - start_index + 1;
42
43         page = kmalloc_array(pages, sizeof(void *), GFP_KERNEL);
44         if (page == NULL)
45                 return res;
46
47         /* Try to grab all the pages covered by the Squashfs block */
48         for (i = 0, n = start_index; n <= end_index; n++) {
49                 page[i] = (n == target_page->index) ? target_page :
50                         grab_cache_page_nowait(target_page->mapping, n);
51
52                 if (page[i] == NULL)
53                         continue;
54
55                 if (PageUptodate(page[i])) {
56                         unlock_page(page[i]);
57                         put_page(page[i]);
58                         continue;
59                 }
60
61                 i++;
62         }
63
64         pages = i;
65
66         /*
67          * Create a "page actor" which will kmap and kunmap the
68          * page cache pages appropriately within the decompressor
69          */
70         actor = squashfs_page_actor_init_special(msblk, page, pages, expected);
71         if (actor == NULL)
72                 goto out;
73
74         /* Decompress directly into the page cache buffers */
75         res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
76
77         squashfs_page_actor_free(actor);
78
79         if (res < 0)
80                 goto mark_errored;
81
82         if (res != expected) {
83                 res = -EIO;
84                 goto mark_errored;
85         }
86
87         /* Last page (if present) may have trailing bytes not filled */
88         bytes = res % PAGE_SIZE;
89         if (page[pages - 1]->index == end_index && bytes) {
90                 pageaddr = kmap_local_page(page[pages - 1]);
91                 memset(pageaddr + bytes, 0, PAGE_SIZE - bytes);
92                 kunmap_local(pageaddr);
93         }
94
95         /* Mark pages as uptodate, unlock and release */
96         for (i = 0; i < pages; i++) {
97                 flush_dcache_page(page[i]);
98                 SetPageUptodate(page[i]);
99                 unlock_page(page[i]);
100                 if (page[i] != target_page)
101                         put_page(page[i]);
102         }
103
104         kfree(page);
105
106         return 0;
107
108 mark_errored:
109         /* Decompression failed, mark pages as errored.  Target_page is
110          * dealt with by the caller
111          */
112         for (i = 0; i < pages; i++) {
113                 if (page[i] == NULL || page[i] == target_page)
114                         continue;
115                 flush_dcache_page(page[i]);
116                 SetPageError(page[i]);
117                 unlock_page(page[i]);
118                 put_page(page[i]);
119         }
120
121 out:
122         kfree(page);
123         return res;
124 }