18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#include <linux/fs.h> 38c2ecf20Sopenharmony_ci#include <linux/fs_struct.h> 48c2ecf20Sopenharmony_ci#include <linux/kernel_read_file.h> 58c2ecf20Sopenharmony_ci#include <linux/security.h> 68c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/** 98c2ecf20Sopenharmony_ci * kernel_read_file() - read file contents into a kernel buffer 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * @file file to read from 128c2ecf20Sopenharmony_ci * @offset where to start reading from (see below). 138c2ecf20Sopenharmony_ci * @buf pointer to a "void *" buffer for reading into (if 148c2ecf20Sopenharmony_ci * *@buf is NULL, a buffer will be allocated, and 158c2ecf20Sopenharmony_ci * @buf_size will be ignored) 168c2ecf20Sopenharmony_ci * @buf_size size of buf, if already allocated. If @buf not 178c2ecf20Sopenharmony_ci * allocated, this is the largest size to allocate. 188c2ecf20Sopenharmony_ci * @file_size if non-NULL, the full size of @file will be 198c2ecf20Sopenharmony_ci * written here. 208c2ecf20Sopenharmony_ci * @id the kernel_read_file_id identifying the type of 218c2ecf20Sopenharmony_ci * file contents being read (for LSMs to examine) 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * @offset must be 0 unless both @buf and @file_size are non-NULL 248c2ecf20Sopenharmony_ci * (i.e. the caller must be expecting to read partial file contents 258c2ecf20Sopenharmony_ci * via an already-allocated @buf, in at most @buf_size chunks, and 268c2ecf20Sopenharmony_ci * will be able to determine when the entire file was read by 278c2ecf20Sopenharmony_ci * checking @file_size). This isn't a recommended way to read a 288c2ecf20Sopenharmony_ci * file, though, since it is possible that the contents might 298c2ecf20Sopenharmony_ci * change between calls to kernel_read_file(). 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Returns number of bytes read (no single read will be bigger 328c2ecf20Sopenharmony_ci * than INT_MAX), or negative on error. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ciint kernel_read_file(struct file *file, loff_t offset, void **buf, 368c2ecf20Sopenharmony_ci size_t buf_size, size_t *file_size, 378c2ecf20Sopenharmony_ci enum kernel_read_file_id id) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci loff_t i_size, pos; 408c2ecf20Sopenharmony_ci size_t copied; 418c2ecf20Sopenharmony_ci void *allocated = NULL; 428c2ecf20Sopenharmony_ci bool whole_file; 438c2ecf20Sopenharmony_ci int ret; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (offset != 0 && (!*buf || !file_size)) 468c2ecf20Sopenharmony_ci return -EINVAL; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (!S_ISREG(file_inode(file)->i_mode)) 498c2ecf20Sopenharmony_ci return -EINVAL; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ret = deny_write_access(file); 528c2ecf20Sopenharmony_ci if (ret) 538c2ecf20Sopenharmony_ci return ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci i_size = i_size_read(file_inode(file)); 568c2ecf20Sopenharmony_ci if (i_size <= 0) { 578c2ecf20Sopenharmony_ci ret = -EINVAL; 588c2ecf20Sopenharmony_ci goto out; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci /* The file is too big for sane activities. */ 618c2ecf20Sopenharmony_ci if (i_size > INT_MAX) { 628c2ecf20Sopenharmony_ci ret = -EFBIG; 638c2ecf20Sopenharmony_ci goto out; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci /* The entire file cannot be read in one buffer. */ 668c2ecf20Sopenharmony_ci if (!file_size && offset == 0 && i_size > buf_size) { 678c2ecf20Sopenharmony_ci ret = -EFBIG; 688c2ecf20Sopenharmony_ci goto out; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci whole_file = (offset == 0 && i_size <= buf_size); 728c2ecf20Sopenharmony_ci ret = security_kernel_read_file(file, id, whole_file); 738c2ecf20Sopenharmony_ci if (ret) 748c2ecf20Sopenharmony_ci goto out; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (file_size) 778c2ecf20Sopenharmony_ci *file_size = i_size; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (!*buf) 808c2ecf20Sopenharmony_ci *buf = allocated = vmalloc(i_size); 818c2ecf20Sopenharmony_ci if (!*buf) { 828c2ecf20Sopenharmony_ci ret = -ENOMEM; 838c2ecf20Sopenharmony_ci goto out; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci pos = offset; 878c2ecf20Sopenharmony_ci copied = 0; 888c2ecf20Sopenharmony_ci while (copied < buf_size) { 898c2ecf20Sopenharmony_ci ssize_t bytes; 908c2ecf20Sopenharmony_ci size_t wanted = min_t(size_t, buf_size - copied, 918c2ecf20Sopenharmony_ci i_size - pos); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci bytes = kernel_read(file, *buf + copied, wanted, &pos); 948c2ecf20Sopenharmony_ci if (bytes < 0) { 958c2ecf20Sopenharmony_ci ret = bytes; 968c2ecf20Sopenharmony_ci goto out_free; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (bytes == 0) 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci copied += bytes; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (whole_file) { 1058c2ecf20Sopenharmony_ci if (pos != i_size) { 1068c2ecf20Sopenharmony_ci ret = -EIO; 1078c2ecf20Sopenharmony_ci goto out_free; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci ret = security_kernel_post_read_file(file, *buf, i_size, id); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ciout_free: 1148c2ecf20Sopenharmony_ci if (ret < 0) { 1158c2ecf20Sopenharmony_ci if (allocated) { 1168c2ecf20Sopenharmony_ci vfree(*buf); 1178c2ecf20Sopenharmony_ci *buf = NULL; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ciout: 1228c2ecf20Sopenharmony_ci allow_write_access(file); 1238c2ecf20Sopenharmony_ci return ret == 0 ? copied : ret; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(kernel_read_file); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ciint kernel_read_file_from_path(const char *path, loff_t offset, void **buf, 1288c2ecf20Sopenharmony_ci size_t buf_size, size_t *file_size, 1298c2ecf20Sopenharmony_ci enum kernel_read_file_id id) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct file *file; 1328c2ecf20Sopenharmony_ci int ret; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (!path || !*path) 1358c2ecf20Sopenharmony_ci return -EINVAL; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci file = filp_open(path, O_RDONLY, 0); 1388c2ecf20Sopenharmony_ci if (IS_ERR(file)) 1398c2ecf20Sopenharmony_ci return PTR_ERR(file); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci ret = kernel_read_file(file, offset, buf, buf_size, file_size, id); 1428c2ecf20Sopenharmony_ci fput(file); 1438c2ecf20Sopenharmony_ci return ret; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(kernel_read_file_from_path); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ciint kernel_read_file_from_path_initns(const char *path, loff_t offset, 1488c2ecf20Sopenharmony_ci void **buf, size_t buf_size, 1498c2ecf20Sopenharmony_ci size_t *file_size, 1508c2ecf20Sopenharmony_ci enum kernel_read_file_id id) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct file *file; 1538c2ecf20Sopenharmony_ci struct path root; 1548c2ecf20Sopenharmony_ci int ret; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (!path || !*path) 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci task_lock(&init_task); 1608c2ecf20Sopenharmony_ci get_fs_root(init_task.fs, &root); 1618c2ecf20Sopenharmony_ci task_unlock(&init_task); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci file = file_open_root(&root, path, O_RDONLY, 0); 1648c2ecf20Sopenharmony_ci path_put(&root); 1658c2ecf20Sopenharmony_ci if (IS_ERR(file)) 1668c2ecf20Sopenharmony_ci return PTR_ERR(file); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci ret = kernel_read_file(file, offset, buf, buf_size, file_size, id); 1698c2ecf20Sopenharmony_ci fput(file); 1708c2ecf20Sopenharmony_ci return ret; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ciint kernel_read_file_from_fd(int fd, loff_t offset, void **buf, 1758c2ecf20Sopenharmony_ci size_t buf_size, size_t *file_size, 1768c2ecf20Sopenharmony_ci enum kernel_read_file_id id) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct fd f = fdget(fd); 1798c2ecf20Sopenharmony_ci int ret = -EBADF; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (!f.file || !(f.file->f_mode & FMODE_READ)) 1828c2ecf20Sopenharmony_ci goto out; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ret = kernel_read_file(f.file, offset, buf, buf_size, file_size, id); 1858c2ecf20Sopenharmony_ciout: 1868c2ecf20Sopenharmony_ci fdput(f); 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(kernel_read_file_from_fd); 190