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 | ||
f7a4f689 | 8 | int kernel_read_file(struct file *file, void **buf, |
5287b07f KC |
9 | loff_t max_size, enum kernel_read_file_id id) |
10 | { | |
11 | loff_t i_size, pos; | |
12 | ssize_t bytes = 0; | |
13 | void *allocated = NULL; | |
14 | int ret; | |
15 | ||
16 | if (!S_ISREG(file_inode(file)->i_mode) || max_size < 0) | |
17 | return -EINVAL; | |
18 | ||
19 | ret = deny_write_access(file); | |
20 | if (ret) | |
21 | return ret; | |
22 | ||
23 | ret = security_kernel_read_file(file, id); | |
24 | if (ret) | |
25 | goto out; | |
26 | ||
27 | i_size = i_size_read(file_inode(file)); | |
28 | if (i_size <= 0) { | |
29 | ret = -EINVAL; | |
30 | goto out; | |
31 | } | |
f7a4f689 | 32 | if (i_size > INT_MAX || (max_size > 0 && i_size > max_size)) { |
5287b07f KC |
33 | ret = -EFBIG; |
34 | goto out; | |
35 | } | |
36 | ||
37 | if (!*buf) | |
38 | *buf = allocated = vmalloc(i_size); | |
39 | if (!*buf) { | |
40 | ret = -ENOMEM; | |
41 | goto out; | |
42 | } | |
43 | ||
44 | pos = 0; | |
45 | while (pos < i_size) { | |
46 | bytes = kernel_read(file, *buf + pos, i_size - pos, &pos); | |
47 | if (bytes < 0) { | |
48 | ret = bytes; | |
49 | goto out_free; | |
50 | } | |
51 | ||
52 | if (bytes == 0) | |
53 | break; | |
54 | } | |
55 | ||
56 | if (pos != i_size) { | |
57 | ret = -EIO; | |
58 | goto out_free; | |
59 | } | |
60 | ||
61 | ret = security_kernel_post_read_file(file, *buf, i_size, id); | |
5287b07f KC |
62 | |
63 | out_free: | |
64 | if (ret < 0) { | |
65 | if (allocated) { | |
66 | vfree(*buf); | |
67 | *buf = NULL; | |
68 | } | |
69 | } | |
70 | ||
71 | out: | |
72 | allow_write_access(file); | |
f7a4f689 | 73 | return ret == 0 ? pos : ret; |
5287b07f KC |
74 | } |
75 | EXPORT_SYMBOL_GPL(kernel_read_file); | |
76 | ||
f7a4f689 | 77 | int kernel_read_file_from_path(const char *path, void **buf, |
5287b07f KC |
78 | loff_t max_size, enum kernel_read_file_id id) |
79 | { | |
80 | struct file *file; | |
81 | int ret; | |
82 | ||
83 | if (!path || !*path) | |
84 | return -EINVAL; | |
85 | ||
86 | file = filp_open(path, O_RDONLY, 0); | |
87 | if (IS_ERR(file)) | |
88 | return PTR_ERR(file); | |
89 | ||
f7a4f689 | 90 | ret = kernel_read_file(file, buf, max_size, id); |
5287b07f KC |
91 | fput(file); |
92 | return ret; | |
93 | } | |
94 | EXPORT_SYMBOL_GPL(kernel_read_file_from_path); | |
95 | ||
96 | int kernel_read_file_from_path_initns(const char *path, void **buf, | |
f7a4f689 | 97 | loff_t max_size, |
5287b07f KC |
98 | enum kernel_read_file_id id) |
99 | { | |
100 | struct file *file; | |
101 | struct path root; | |
102 | int ret; | |
103 | ||
104 | if (!path || !*path) | |
105 | return -EINVAL; | |
106 | ||
107 | task_lock(&init_task); | |
108 | get_fs_root(init_task.fs, &root); | |
109 | task_unlock(&init_task); | |
110 | ||
111 | file = file_open_root(root.dentry, root.mnt, path, O_RDONLY, 0); | |
112 | path_put(&root); | |
113 | if (IS_ERR(file)) | |
114 | return PTR_ERR(file); | |
115 | ||
f7a4f689 | 116 | ret = kernel_read_file(file, buf, max_size, id); |
5287b07f KC |
117 | fput(file); |
118 | return ret; | |
119 | } | |
120 | EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns); | |
121 | ||
f7a4f689 | 122 | int kernel_read_file_from_fd(int fd, void **buf, loff_t max_size, |
5287b07f KC |
123 | enum kernel_read_file_id id) |
124 | { | |
125 | struct fd f = fdget(fd); | |
126 | int ret = -EBADF; | |
127 | ||
128 | if (!f.file) | |
129 | goto out; | |
130 | ||
f7a4f689 | 131 | ret = kernel_read_file(f.file, buf, max_size, id); |
5287b07f KC |
132 | out: |
133 | fdput(f); | |
134 | return ret; | |
135 | } | |
136 | EXPORT_SYMBOL_GPL(kernel_read_file_from_fd); |