linux/fs/kernel_read_file.c
<<
>>
Prefs
   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 * @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 INT_MAX), or negative on error.
  33 *
  34 */
  35int 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        size_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        /* The file is too big for sane activities. */
  61        if (i_size > INT_MAX) {
  62                ret = -EFBIG;
  63                goto out;
  64        }
  65        /* The entire file cannot be read in one buffer. */
  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
 113out_free:
 114        if (ret < 0) {
 115                if (allocated) {
 116                        vfree(*buf);
 117                        *buf = NULL;
 118                }
 119        }
 120
 121out:
 122        allow_write_access(file);
 123        return ret == 0 ? copied : ret;
 124}
 125EXPORT_SYMBOL_GPL(kernel_read_file);
 126
 127int 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        int 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}
 145EXPORT_SYMBOL_GPL(kernel_read_file_from_path);
 146
 147int 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        struct path root;
 154        int ret;
 155
 156        if (!path || !*path)
 157                return -EINVAL;
 158
 159        task_lock(&init_task);
 160        get_fs_root(init_task.fs, &root);
 161        task_unlock(&init_task);
 162
 163        file = file_open_root(root.dentry, root.mnt, path, O_RDONLY, 0);
 164        path_put(&root);
 165        if (IS_ERR(file))
 166                return PTR_ERR(file);
 167
 168        ret = kernel_read_file(file, offset, buf, buf_size, file_size, id);
 169        fput(file);
 170        return ret;
 171}
 172EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns);
 173
 174int kernel_read_file_from_fd(int fd, loff_t offset, void **buf,
 175                             size_t buf_size, size_t *file_size,
 176                             enum kernel_read_file_id id)
 177{
 178        struct fd f = fdget(fd);
 179        int ret = -EBADF;
 180
 181        if (!f.file)
 182                goto out;
 183
 184        ret = kernel_read_file(f.file, offset, buf, buf_size, file_size, id);
 185out:
 186        fdput(f);
 187        return ret;
 188}
 189EXPORT_SYMBOL_GPL(kernel_read_file_from_fd);
 190