]>
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 | |
12 | * @buf pointer to a "void *" buffer for reading into (if | |
13 | * *@buf is NULL, a buffer will be allocated, and | |
14 | * @buf_size will be ignored) | |
15 | * @buf_size size of buf, if already allocated. If @buf not | |
16 | * allocated, this is the largest size to allocate. | |
88535288 KC |
17 | * @file_size if non-NULL, the full size of @file will be |
18 | * written here. | |
113eeb51 KC |
19 | * @id the kernel_read_file_id identifying the type of |
20 | * file contents being read (for LSMs to examine) | |
21 | * | |
22 | * Returns number of bytes read (no single read will be bigger | |
23 | * than INT_MAX), or negative on error. | |
24 | * | |
25 | */ | |
f7a4f689 | 26 | int kernel_read_file(struct file *file, void **buf, |
88535288 KC |
27 | size_t buf_size, size_t *file_size, |
28 | enum kernel_read_file_id id) | |
5287b07f KC |
29 | { |
30 | loff_t i_size, pos; | |
31 | ssize_t bytes = 0; | |
32 | void *allocated = NULL; | |
33 | int ret; | |
34 | ||
113eeb51 | 35 | if (!S_ISREG(file_inode(file)->i_mode)) |
5287b07f KC |
36 | return -EINVAL; |
37 | ||
38 | ret = deny_write_access(file); | |
39 | if (ret) | |
40 | return ret; | |
41 | ||
42 | ret = security_kernel_read_file(file, id); | |
43 | if (ret) | |
44 | goto out; | |
45 | ||
46 | i_size = i_size_read(file_inode(file)); | |
47 | if (i_size <= 0) { | |
48 | ret = -EINVAL; | |
49 | goto out; | |
50 | } | |
113eeb51 | 51 | if (i_size > INT_MAX || i_size > buf_size) { |
5287b07f KC |
52 | ret = -EFBIG; |
53 | goto out; | |
54 | } | |
88535288 KC |
55 | if (file_size) |
56 | *file_size = i_size; | |
5287b07f KC |
57 | |
58 | if (!*buf) | |
59 | *buf = allocated = vmalloc(i_size); | |
60 | if (!*buf) { | |
61 | ret = -ENOMEM; | |
62 | goto out; | |
63 | } | |
64 | ||
65 | pos = 0; | |
66 | while (pos < i_size) { | |
67 | bytes = kernel_read(file, *buf + pos, i_size - pos, &pos); | |
68 | if (bytes < 0) { | |
69 | ret = bytes; | |
70 | goto out_free; | |
71 | } | |
72 | ||
73 | if (bytes == 0) | |
74 | break; | |
75 | } | |
76 | ||
77 | if (pos != i_size) { | |
78 | ret = -EIO; | |
79 | goto out_free; | |
80 | } | |
81 | ||
82 | ret = security_kernel_post_read_file(file, *buf, i_size, id); | |
5287b07f KC |
83 | |
84 | out_free: | |
85 | if (ret < 0) { | |
86 | if (allocated) { | |
87 | vfree(*buf); | |
88 | *buf = NULL; | |
89 | } | |
90 | } | |
91 | ||
92 | out: | |
93 | allow_write_access(file); | |
f7a4f689 | 94 | return ret == 0 ? pos : ret; |
5287b07f KC |
95 | } |
96 | EXPORT_SYMBOL_GPL(kernel_read_file); | |
97 | ||
f7a4f689 | 98 | int kernel_read_file_from_path(const char *path, void **buf, |
88535288 KC |
99 | size_t buf_size, size_t *file_size, |
100 | enum kernel_read_file_id id) | |
5287b07f KC |
101 | { |
102 | struct file *file; | |
103 | int ret; | |
104 | ||
105 | if (!path || !*path) | |
106 | return -EINVAL; | |
107 | ||
108 | file = filp_open(path, O_RDONLY, 0); | |
109 | if (IS_ERR(file)) | |
110 | return PTR_ERR(file); | |
111 | ||
88535288 | 112 | ret = kernel_read_file(file, buf, buf_size, file_size, id); |
5287b07f KC |
113 | fput(file); |
114 | return ret; | |
115 | } | |
116 | EXPORT_SYMBOL_GPL(kernel_read_file_from_path); | |
117 | ||
118 | int kernel_read_file_from_path_initns(const char *path, void **buf, | |
88535288 | 119 | size_t buf_size, size_t *file_size, |
5287b07f KC |
120 | enum kernel_read_file_id id) |
121 | { | |
122 | struct file *file; | |
123 | struct path root; | |
124 | int ret; | |
125 | ||
126 | if (!path || !*path) | |
127 | return -EINVAL; | |
128 | ||
129 | task_lock(&init_task); | |
130 | get_fs_root(init_task.fs, &root); | |
131 | task_unlock(&init_task); | |
132 | ||
133 | file = file_open_root(root.dentry, root.mnt, path, O_RDONLY, 0); | |
134 | path_put(&root); | |
135 | if (IS_ERR(file)) | |
136 | return PTR_ERR(file); | |
137 | ||
88535288 | 138 | ret = kernel_read_file(file, buf, buf_size, file_size, id); |
5287b07f KC |
139 | fput(file); |
140 | return ret; | |
141 | } | |
142 | EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns); | |
143 | ||
113eeb51 | 144 | int kernel_read_file_from_fd(int fd, void **buf, size_t buf_size, |
88535288 | 145 | size_t *file_size, |
5287b07f KC |
146 | enum kernel_read_file_id id) |
147 | { | |
148 | struct fd f = fdget(fd); | |
149 | int ret = -EBADF; | |
150 | ||
151 | if (!f.file) | |
152 | goto out; | |
153 | ||
88535288 | 154 | ret = kernel_read_file(f.file, buf, buf_size, file_size, id); |
5287b07f KC |
155 | out: |
156 | fdput(f); | |
157 | return ret; | |
158 | } | |
159 | EXPORT_SYMBOL_GPL(kernel_read_file_from_fd); |