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