| 1 | |
| 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 | * @offset: where to start reading from (see below).
|
| 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.
|
| 18 | * @file_size: if non-NULL, the full size of @file will be
|
| 19 | * written here.
|
| 20 | * @id: the kernel_read_file_id identifying the type of
|
| 21 | * file contents being read (for LSMs to examine)
|
| 22 | *
|
| 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 | *
|
| 31 | * Returns number of bytes read (no single read will be bigger
|
| 32 | * than SSIZE_MAX), or negative on error.
|
| 33 | *
|
| 34 |
|
| 35 | ssize_t kernel_read_file(struct file *file, loff_t offset, void **buf,
|
| 36 | size_t buf_size, size_t *file_size,
|
| 37 | enum kernel_read_file_id id)
|
| 38 | {
|
| 39 | loff_t i_size, pos;
|
| 40 | ssize_t copied;
|
| 41 | void *allocated = NULL;
|
| 42 | bool whole_file;
|
| 43 | int ret;
|
| 44 |
|
| 45 | if (offset != 0 && (!*buf || !file_size))
|
| 46 | return -EINVAL;
|
| 47 |
|
| 48 | if (!S_ISREG(file_inode(file)->i_mode))
|
| 49 | return -EINVAL;
|
| 50 |
|
| 51 | ret = deny_write_access(file);
|
| 52 | if (ret)
|
| 53 | return ret;
|
| 54 |
|
| 55 | i_size = i_size_read(file_inode(file));
|
| 56 | if (i_size <= 0) {
|
| 57 | ret = -EINVAL;
|
| 58 | goto out;
|
| 59 | }
|
| 60 |
|
| 61 | if (i_size > SSIZE_MAX) {
|
| 62 | ret = -EFBIG;
|
| 63 | goto out;
|
| 64 | }
|
| 65 |
|
| 66 | if (!file_size && offset == 0 && i_size > buf_size) {
|
| 67 | ret = -EFBIG;
|
| 68 | goto out;
|
| 69 | }
|
| 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 |
|
| 76 | if (file_size)
|
| 77 | *file_size = i_size;
|
| 78 |
|
| 79 | if (!*buf)
|
| 80 | *buf = allocated = vmalloc(i_size);
|
| 81 | if (!*buf) {
|
| 82 | ret = -ENOMEM;
|
| 83 | goto out;
|
| 84 | }
|
| 85 |
|
| 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);
|
| 94 | if (bytes < 0) {
|
| 95 | ret = bytes;
|
| 96 | goto out_free;
|
| 97 | }
|
| 98 |
|
| 99 | if (bytes == 0)
|
| 100 | break;
|
| 101 | copied += bytes;
|
| 102 | }
|
| 103 |
|
| 104 | if (whole_file) {
|
| 105 | if (pos != i_size) {
|
| 106 | ret = -EIO;
|
| 107 | goto out_free;
|
| 108 | }
|
| 109 |
|
| 110 | ret = security_kernel_post_read_file(file, *buf, i_size, id);
|
| 111 | }
|
| 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);
|
| 123 | return ret == 0 ? copied : ret;
|
| 124 | }
|
| 125 | EXPORT_SYMBOL_GPL(kernel_read_file);
|
| 126 |
|
| 127 | ssize_t kernel_read_file_from_path(const char *path, loff_t offset, void **buf,
|
| 128 | size_t buf_size, size_t *file_size,
|
| 129 | enum kernel_read_file_id id)
|
| 130 | {
|
| 131 | struct file *file;
|
| 132 | ssize_t 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 |
|
| 141 | ret = kernel_read_file(file, offset, buf, buf_size, file_size, id);
|
| 142 | fput(file);
|
| 143 | return ret;
|
| 144 | }
|
| 145 | EXPORT_SYMBOL_GPL(kernel_read_file_from_path);
|
| 146 |
|
| 147 | ssize_t kernel_read_file_from_path_initns(const char *path, loff_t offset,
|
| 148 | void **buf, size_t buf_size,
|
| 149 | size_t *file_size,
|
| 150 | enum kernel_read_file_id id)
|
| 151 | {
|
| 152 | struct file *file;
|
| 153 | ssize_t ret;
|
| 154 |
|
| 155 | if (!path || !*path)
|
| 156 | return -EINVAL;
|
| 157 |
|
| 158 | scoped_with_init_fs()
|
| 159 | file = filp_open(path, O_RDONLY, 0);
|
| 160 | if (IS_ERR(file))
|
| 161 | return PTR_ERR(file);
|
| 162 |
|
| 163 | ret = kernel_read_file(file, offset, buf, buf_size, file_size, id);
|
| 164 | fput(file);
|
| 165 | return ret;
|
| 166 | }
|
| 167 | EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns);
|
| 168 |
|
| 169 | ssize_t kernel_read_file_from_fd(int fd, loff_t offset, void **buf,
|
| 170 | size_t buf_size, size_t *file_size,
|
| 171 | enum kernel_read_file_id id)
|
| 172 | {
|
| 173 | CLASS(fd, f)(fd);
|
| 174 |
|
| 175 | if (fd_empty(f) || !(fd_file(f)->f_mode & FMODE_READ))
|
| 176 | return -EBADF;
|
| 177 |
|
| 178 | return kernel_read_file(fd_file(f), offset, buf, buf_size, file_size, id);
|
| 179 | }
|
| 180 | EXPORT_SYMBOL_GPL(kernel_read_file_from_fd);
|