]>
Commit | Line | Data |
---|---|---|
5287b07f KC |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | #include <linux/fs.h> | |
3 | #include <linux/fs_struct.h> | |
4 | #include <linux/kernel_read_file.h> | |
5 | #include <linux/security.h> | |
6 | #include <linux/vmalloc.h> | |
7 | ||
113eeb51 KC |
8 | /** |
9 | * kernel_read_file() - read file contents into a kernel buffer | |
10 | * | |
11 | * @file file to read from | |
0fa8e084 | 12 | * @offset where to start reading from (see below). |
113eeb51 KC |
13 | * @buf pointer to a "void *" buffer for reading into (if |
14 | * *@buf is NULL, a buffer will be allocated, and | |
15 | * @buf_size will be ignored) | |
16 | * @buf_size size of buf, if already allocated. If @buf not | |
17 | * allocated, this is the largest size to allocate. | |
88535288 KC |
18 | * @file_size if non-NULL, the full size of @file will be |
19 | * written here. | |
113eeb51 KC |
20 | * @id the kernel_read_file_id identifying the type of |
21 | * file contents being read (for LSMs to examine) | |
22 | * | |
0fa8e084 KC |
23 | * @offset must be 0 unless both @buf and @file_size are non-NULL |
24 | * (i.e. the caller must be expecting to read partial file contents | |
25 | * via an already-allocated @buf, in at most @buf_size chunks, and | |
26 | * will be able to determine when the entire file was read by | |
27 | * checking @file_size). This isn't a recommended way to read a | |
28 | * file, though, since it is possible that the contents might | |
29 | * change between calls to kernel_read_file(). | |
30 | * | |
113eeb51 KC |
31 | * Returns number of bytes read (no single read will be bigger |
32 | * than INT_MAX), or negative on error. | |
33 | * | |
34 | */ | |
0fa8e084 | 35 | int kernel_read_file(struct file *file, loff_t offset, void **buf, |
88535288 KC |
36 | size_t buf_size, size_t *file_size, |
37 | enum kernel_read_file_id id) | |
5287b07f KC |
38 | { |
39 | loff_t i_size, pos; | |
0fa8e084 | 40 | size_t copied; |
5287b07f | 41 | void *allocated = NULL; |
0fa8e084 | 42 | bool whole_file; |
5287b07f KC |
43 | int ret; |
44 | ||
0fa8e084 KC |
45 | if (offset != 0 && (!*buf || !file_size)) |
46 | return -EINVAL; | |
47 | ||
113eeb51 | 48 | if (!S_ISREG(file_inode(file)->i_mode)) |
5287b07f KC |
49 | return -EINVAL; |
50 | ||
51 | ret = deny_write_access(file); | |
52 | if (ret) | |
53 | return ret; | |
54 | ||
5287b07f KC |
55 | i_size = i_size_read(file_inode(file)); |
56 | if (i_size <= 0) { | |
57 | ret = -EINVAL; | |
58 | goto out; | |
59 | } | |
0fa8e084 KC |
60 | /* The file is too big for sane activities. */ |
61 | if (i_size > INT_MAX) { | |
62 | ret = -EFBIG; | |
63 | goto out; | |
64 | } | |
65 | /* The entire file cannot be read in one buffer. */ | |
66 | if (!file_size && offset == 0 && i_size > buf_size) { | |
5287b07f KC |
67 | ret = -EFBIG; |
68 | goto out; | |
69 | } | |
0fa8e084 KC |
70 | |
71 | whole_file = (offset == 0 && i_size <= buf_size); | |
72 | ret = security_kernel_read_file(file, id, whole_file); | |
73 | if (ret) | |
74 | goto out; | |
75 | ||
88535288 KC |
76 | if (file_size) |
77 | *file_size = i_size; | |
5287b07f KC |
78 | |
79 | if (!*buf) | |
80 | *buf = allocated = vmalloc(i_size); | |
81 | if (!*buf) { | |
82 | ret = -ENOMEM; | |
83 | goto out; | |
84 | } | |
85 | ||
0fa8e084 KC |
86 | pos = offset; |
87 | copied = 0; | |
88 | while (copied < buf_size) { | |
89 | ssize_t bytes; | |
90 | size_t wanted = min_t(size_t, buf_size - copied, | |
91 | i_size - pos); | |
92 | ||
93 | bytes = kernel_read(file, *buf + copied, wanted, &pos); | |
5287b07f KC |
94 | if (bytes < 0) { |
95 | ret = bytes; | |
96 | goto out_free; | |
97 | } | |
98 | ||
99 | if (bytes == 0) | |
100 | break; | |
0fa8e084 | 101 | copied += bytes; |
5287b07f KC |
102 | } |
103 | ||
0fa8e084 KC |
104 | if (whole_file) { |
105 | if (pos != i_size) { | |
106 | ret = -EIO; | |
107 | goto out_free; | |
108 | } | |
5287b07f | 109 | |
0fa8e084 KC |
110 | ret = security_kernel_post_read_file(file, *buf, i_size, id); |
111 | } | |
5287b07f KC |
112 | |
113 | out_free: | |
114 | if (ret < 0) { | |
115 | if (allocated) { | |
116 | vfree(*buf); | |
117 | *buf = NULL; | |
118 | } | |
119 | } | |
120 | ||
121 | out: | |
122 | allow_write_access(file); | |
0fa8e084 | 123 | return ret == 0 ? copied : ret; |
5287b07f KC |
124 | } |
125 | EXPORT_SYMBOL_GPL(kernel_read_file); | |
126 | ||
0fa8e084 | 127 | int kernel_read_file_from_path(const char *path, loff_t offset, void **buf, |
88535288 KC |
128 | size_t buf_size, size_t *file_size, |
129 | enum kernel_read_file_id id) | |
5287b07f KC |
130 | { |
131 | struct file *file; | |
132 | int ret; | |
133 | ||
134 | if (!path || !*path) | |
135 | return -EINVAL; | |
136 | ||
137 | file = filp_open(path, O_RDONLY, 0); | |
138 | if (IS_ERR(file)) | |
139 | return PTR_ERR(file); | |
140 | ||
0fa8e084 | 141 | ret = kernel_read_file(file, offset, buf, buf_size, file_size, id); |
5287b07f KC |
142 | fput(file); |
143 | return ret; | |
144 | } | |
145 | EXPORT_SYMBOL_GPL(kernel_read_file_from_path); | |
146 | ||
0fa8e084 KC |
147 | int kernel_read_file_from_path_initns(const char *path, loff_t offset, |
148 | void **buf, size_t buf_size, | |
149 | size_t *file_size, | |
5287b07f KC |
150 | enum kernel_read_file_id id) |
151 | { | |
152 | struct file *file; | |
153 | struct path root; | |
154 | int ret; | |
155 | ||
156 | if (!path || !*path) | |
157 | return -EINVAL; | |
158 | ||
159 | task_lock(&init_task); | |
160 | get_fs_root(init_task.fs, &root); | |
161 | task_unlock(&init_task); | |
162 | ||
163 | file = file_open_root(root.dentry, root.mnt, path, O_RDONLY, 0); | |
164 | path_put(&root); | |
165 | if (IS_ERR(file)) | |
166 | return PTR_ERR(file); | |
167 | ||
0fa8e084 | 168 | ret = kernel_read_file(file, offset, buf, buf_size, file_size, id); |
5287b07f KC |
169 | fput(file); |
170 | return ret; | |
171 | } | |
172 | EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns); | |
173 | ||
0fa8e084 KC |
174 | int kernel_read_file_from_fd(int fd, loff_t offset, void **buf, |
175 | size_t buf_size, size_t *file_size, | |
5287b07f KC |
176 | enum kernel_read_file_id id) |
177 | { | |
178 | struct fd f = fdget(fd); | |
179 | int ret = -EBADF; | |
180 | ||
181 | if (!f.file) | |
182 | goto out; | |
183 | ||
0fa8e084 | 184 | ret = kernel_read_file(f.file, offset, buf, buf_size, file_size, id); |
5287b07f KC |
185 | out: |
186 | fdput(f); | |
187 | return ret; | |
188 | } | |
189 | EXPORT_SYMBOL_GPL(kernel_read_file_from_fd); |