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