162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * eCryptfs: Linux filesystem encryption layer 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007 International Business Machines Corp. 662306a36Sopenharmony_ci * Author(s): Michael A. Halcrow <mahalcro@us.ibm.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/fs.h> 1062306a36Sopenharmony_ci#include <linux/pagemap.h> 1162306a36Sopenharmony_ci#include <linux/sched/signal.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "ecryptfs_kernel.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/** 1662306a36Sopenharmony_ci * ecryptfs_write_lower 1762306a36Sopenharmony_ci * @ecryptfs_inode: The eCryptfs inode 1862306a36Sopenharmony_ci * @data: Data to write 1962306a36Sopenharmony_ci * @offset: Byte offset in the lower file to which to write the data 2062306a36Sopenharmony_ci * @size: Number of bytes from @data to write at @offset in the lower 2162306a36Sopenharmony_ci * file 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Write data to the lower file. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * Returns bytes written on success; less than zero on error 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ciint ecryptfs_write_lower(struct inode *ecryptfs_inode, char *data, 2862306a36Sopenharmony_ci loff_t offset, size_t size) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct file *lower_file; 3162306a36Sopenharmony_ci ssize_t rc; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci lower_file = ecryptfs_inode_to_private(ecryptfs_inode)->lower_file; 3462306a36Sopenharmony_ci if (!lower_file) 3562306a36Sopenharmony_ci return -EIO; 3662306a36Sopenharmony_ci rc = kernel_write(lower_file, data, size, &offset); 3762306a36Sopenharmony_ci mark_inode_dirty_sync(ecryptfs_inode); 3862306a36Sopenharmony_ci return rc; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/** 4262306a36Sopenharmony_ci * ecryptfs_write_lower_page_segment 4362306a36Sopenharmony_ci * @ecryptfs_inode: The eCryptfs inode 4462306a36Sopenharmony_ci * @page_for_lower: The page containing the data to be written to the 4562306a36Sopenharmony_ci * lower file 4662306a36Sopenharmony_ci * @offset_in_page: The offset in the @page_for_lower from which to 4762306a36Sopenharmony_ci * start writing the data 4862306a36Sopenharmony_ci * @size: The amount of data from @page_for_lower to write to the 4962306a36Sopenharmony_ci * lower file 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * Determines the byte offset in the file for the given page and 5262306a36Sopenharmony_ci * offset within the page, maps the page, and makes the call to write 5362306a36Sopenharmony_ci * the contents of @page_for_lower to the lower inode. 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * Returns zero on success; non-zero otherwise 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ciint ecryptfs_write_lower_page_segment(struct inode *ecryptfs_inode, 5862306a36Sopenharmony_ci struct page *page_for_lower, 5962306a36Sopenharmony_ci size_t offset_in_page, size_t size) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci char *virt; 6262306a36Sopenharmony_ci loff_t offset; 6362306a36Sopenharmony_ci int rc; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci offset = ((((loff_t)page_for_lower->index) << PAGE_SHIFT) 6662306a36Sopenharmony_ci + offset_in_page); 6762306a36Sopenharmony_ci virt = kmap_local_page(page_for_lower); 6862306a36Sopenharmony_ci rc = ecryptfs_write_lower(ecryptfs_inode, virt, offset, size); 6962306a36Sopenharmony_ci if (rc > 0) 7062306a36Sopenharmony_ci rc = 0; 7162306a36Sopenharmony_ci kunmap_local(virt); 7262306a36Sopenharmony_ci return rc; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/** 7662306a36Sopenharmony_ci * ecryptfs_write 7762306a36Sopenharmony_ci * @ecryptfs_inode: The eCryptfs file into which to write 7862306a36Sopenharmony_ci * @data: Virtual address where data to write is located 7962306a36Sopenharmony_ci * @offset: Offset in the eCryptfs file at which to begin writing the 8062306a36Sopenharmony_ci * data from @data 8162306a36Sopenharmony_ci * @size: The number of bytes to write from @data 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * Write an arbitrary amount of data to an arbitrary location in the 8462306a36Sopenharmony_ci * eCryptfs inode page cache. This is done on a page-by-page, and then 8562306a36Sopenharmony_ci * by an extent-by-extent, basis; individual extents are encrypted and 8662306a36Sopenharmony_ci * written to the lower page cache (via VFS writes). This function 8762306a36Sopenharmony_ci * takes care of all the address translation to locations in the lower 8862306a36Sopenharmony_ci * filesystem; it also handles truncate events, writing out zeros 8962306a36Sopenharmony_ci * where necessary. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * Returns zero on success; non-zero otherwise 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ciint ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset, 9462306a36Sopenharmony_ci size_t size) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct page *ecryptfs_page; 9762306a36Sopenharmony_ci struct ecryptfs_crypt_stat *crypt_stat; 9862306a36Sopenharmony_ci char *ecryptfs_page_virt; 9962306a36Sopenharmony_ci loff_t ecryptfs_file_size = i_size_read(ecryptfs_inode); 10062306a36Sopenharmony_ci loff_t data_offset = 0; 10162306a36Sopenharmony_ci loff_t pos; 10262306a36Sopenharmony_ci int rc = 0; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci crypt_stat = &ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat; 10562306a36Sopenharmony_ci /* 10662306a36Sopenharmony_ci * if we are writing beyond current size, then start pos 10762306a36Sopenharmony_ci * at the current size - we'll fill in zeros from there. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci if (offset > ecryptfs_file_size) 11062306a36Sopenharmony_ci pos = ecryptfs_file_size; 11162306a36Sopenharmony_ci else 11262306a36Sopenharmony_ci pos = offset; 11362306a36Sopenharmony_ci while (pos < (offset + size)) { 11462306a36Sopenharmony_ci pgoff_t ecryptfs_page_idx = (pos >> PAGE_SHIFT); 11562306a36Sopenharmony_ci size_t start_offset_in_page = (pos & ~PAGE_MASK); 11662306a36Sopenharmony_ci size_t num_bytes = (PAGE_SIZE - start_offset_in_page); 11762306a36Sopenharmony_ci loff_t total_remaining_bytes = ((offset + size) - pos); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (fatal_signal_pending(current)) { 12062306a36Sopenharmony_ci rc = -EINTR; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (num_bytes > total_remaining_bytes) 12562306a36Sopenharmony_ci num_bytes = total_remaining_bytes; 12662306a36Sopenharmony_ci if (pos < offset) { 12762306a36Sopenharmony_ci /* remaining zeros to write, up to destination offset */ 12862306a36Sopenharmony_ci loff_t total_remaining_zeros = (offset - pos); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (num_bytes > total_remaining_zeros) 13162306a36Sopenharmony_ci num_bytes = total_remaining_zeros; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci ecryptfs_page = ecryptfs_get_locked_page(ecryptfs_inode, 13462306a36Sopenharmony_ci ecryptfs_page_idx); 13562306a36Sopenharmony_ci if (IS_ERR(ecryptfs_page)) { 13662306a36Sopenharmony_ci rc = PTR_ERR(ecryptfs_page); 13762306a36Sopenharmony_ci printk(KERN_ERR "%s: Error getting page at " 13862306a36Sopenharmony_ci "index [%ld] from eCryptfs inode " 13962306a36Sopenharmony_ci "mapping; rc = [%d]\n", __func__, 14062306a36Sopenharmony_ci ecryptfs_page_idx, rc); 14162306a36Sopenharmony_ci goto out; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci ecryptfs_page_virt = kmap_local_page(ecryptfs_page); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * pos: where we're now writing, offset: where the request was 14762306a36Sopenharmony_ci * If current pos is before request, we are filling zeros 14862306a36Sopenharmony_ci * If we are at or beyond request, we are writing the *data* 14962306a36Sopenharmony_ci * If we're in a fresh page beyond eof, zero it in either case 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci if (pos < offset || !start_offset_in_page) { 15262306a36Sopenharmony_ci /* We are extending past the previous end of the file. 15362306a36Sopenharmony_ci * Fill in zero values to the end of the page */ 15462306a36Sopenharmony_ci memset(((char *)ecryptfs_page_virt 15562306a36Sopenharmony_ci + start_offset_in_page), 0, 15662306a36Sopenharmony_ci PAGE_SIZE - start_offset_in_page); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* pos >= offset, we are now writing the data request */ 16062306a36Sopenharmony_ci if (pos >= offset) { 16162306a36Sopenharmony_ci memcpy(((char *)ecryptfs_page_virt 16262306a36Sopenharmony_ci + start_offset_in_page), 16362306a36Sopenharmony_ci (data + data_offset), num_bytes); 16462306a36Sopenharmony_ci data_offset += num_bytes; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci kunmap_local(ecryptfs_page_virt); 16762306a36Sopenharmony_ci flush_dcache_page(ecryptfs_page); 16862306a36Sopenharmony_ci SetPageUptodate(ecryptfs_page); 16962306a36Sopenharmony_ci unlock_page(ecryptfs_page); 17062306a36Sopenharmony_ci if (crypt_stat->flags & ECRYPTFS_ENCRYPTED) 17162306a36Sopenharmony_ci rc = ecryptfs_encrypt_page(ecryptfs_page); 17262306a36Sopenharmony_ci else 17362306a36Sopenharmony_ci rc = ecryptfs_write_lower_page_segment(ecryptfs_inode, 17462306a36Sopenharmony_ci ecryptfs_page, 17562306a36Sopenharmony_ci start_offset_in_page, 17662306a36Sopenharmony_ci data_offset); 17762306a36Sopenharmony_ci put_page(ecryptfs_page); 17862306a36Sopenharmony_ci if (rc) { 17962306a36Sopenharmony_ci printk(KERN_ERR "%s: Error encrypting " 18062306a36Sopenharmony_ci "page; rc = [%d]\n", __func__, rc); 18162306a36Sopenharmony_ci goto out; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci pos += num_bytes; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci if (pos > ecryptfs_file_size) { 18662306a36Sopenharmony_ci i_size_write(ecryptfs_inode, pos); 18762306a36Sopenharmony_ci if (crypt_stat->flags & ECRYPTFS_ENCRYPTED) { 18862306a36Sopenharmony_ci int rc2; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci rc2 = ecryptfs_write_inode_size_to_metadata( 19162306a36Sopenharmony_ci ecryptfs_inode); 19262306a36Sopenharmony_ci if (rc2) { 19362306a36Sopenharmony_ci printk(KERN_ERR "Problem with " 19462306a36Sopenharmony_ci "ecryptfs_write_inode_size_to_metadata; " 19562306a36Sopenharmony_ci "rc = [%d]\n", rc2); 19662306a36Sopenharmony_ci if (!rc) 19762306a36Sopenharmony_ci rc = rc2; 19862306a36Sopenharmony_ci goto out; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ciout: 20362306a36Sopenharmony_ci return rc; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/** 20762306a36Sopenharmony_ci * ecryptfs_read_lower 20862306a36Sopenharmony_ci * @data: The read data is stored here by this function 20962306a36Sopenharmony_ci * @offset: Byte offset in the lower file from which to read the data 21062306a36Sopenharmony_ci * @size: Number of bytes to read from @offset of the lower file and 21162306a36Sopenharmony_ci * store into @data 21262306a36Sopenharmony_ci * @ecryptfs_inode: The eCryptfs inode 21362306a36Sopenharmony_ci * 21462306a36Sopenharmony_ci * Read @size bytes of data at byte offset @offset from the lower 21562306a36Sopenharmony_ci * inode into memory location @data. 21662306a36Sopenharmony_ci * 21762306a36Sopenharmony_ci * Returns bytes read on success; 0 on EOF; less than zero on error 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ciint ecryptfs_read_lower(char *data, loff_t offset, size_t size, 22062306a36Sopenharmony_ci struct inode *ecryptfs_inode) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct file *lower_file; 22362306a36Sopenharmony_ci lower_file = ecryptfs_inode_to_private(ecryptfs_inode)->lower_file; 22462306a36Sopenharmony_ci if (!lower_file) 22562306a36Sopenharmony_ci return -EIO; 22662306a36Sopenharmony_ci return kernel_read(lower_file, data, size, &offset); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/** 23062306a36Sopenharmony_ci * ecryptfs_read_lower_page_segment 23162306a36Sopenharmony_ci * @page_for_ecryptfs: The page into which data for eCryptfs will be 23262306a36Sopenharmony_ci * written 23362306a36Sopenharmony_ci * @page_index: Page index in @page_for_ecryptfs from which to start 23462306a36Sopenharmony_ci * writing 23562306a36Sopenharmony_ci * @offset_in_page: Offset in @page_for_ecryptfs from which to start 23662306a36Sopenharmony_ci * writing 23762306a36Sopenharmony_ci * @size: The number of bytes to write into @page_for_ecryptfs 23862306a36Sopenharmony_ci * @ecryptfs_inode: The eCryptfs inode 23962306a36Sopenharmony_ci * 24062306a36Sopenharmony_ci * Determines the byte offset in the file for the given page and 24162306a36Sopenharmony_ci * offset within the page, maps the page, and makes the call to read 24262306a36Sopenharmony_ci * the contents of @page_for_ecryptfs from the lower inode. 24362306a36Sopenharmony_ci * 24462306a36Sopenharmony_ci * Returns zero on success; non-zero otherwise 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_ciint ecryptfs_read_lower_page_segment(struct page *page_for_ecryptfs, 24762306a36Sopenharmony_ci pgoff_t page_index, 24862306a36Sopenharmony_ci size_t offset_in_page, size_t size, 24962306a36Sopenharmony_ci struct inode *ecryptfs_inode) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci char *virt; 25262306a36Sopenharmony_ci loff_t offset; 25362306a36Sopenharmony_ci int rc; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci offset = ((((loff_t)page_index) << PAGE_SHIFT) + offset_in_page); 25662306a36Sopenharmony_ci virt = kmap_local_page(page_for_ecryptfs); 25762306a36Sopenharmony_ci rc = ecryptfs_read_lower(virt, offset, size, ecryptfs_inode); 25862306a36Sopenharmony_ci if (rc > 0) 25962306a36Sopenharmony_ci rc = 0; 26062306a36Sopenharmony_ci kunmap_local(virt); 26162306a36Sopenharmony_ci flush_dcache_page(page_for_ecryptfs); 26262306a36Sopenharmony_ci return rc; 26362306a36Sopenharmony_ci} 264