162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * eCryptfs: Linux filesystem encryption layer
462306a36Sopenharmony_ci * This is where eCryptfs coordinates the symmetric encryption and
562306a36Sopenharmony_ci * decryption of the file data as it passes between the lower
662306a36Sopenharmony_ci * encrypted file and the upper decrypted file.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 1997-2003 Erez Zadok
962306a36Sopenharmony_ci * Copyright (C) 2001-2003 Stony Brook University
1062306a36Sopenharmony_ci * Copyright (C) 2004-2007 International Business Machines Corp.
1162306a36Sopenharmony_ci *   Author(s): Michael A. Halcrow <mahalcro@us.ibm.com>
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/pagemap.h>
1562306a36Sopenharmony_ci#include <linux/writeback.h>
1662306a36Sopenharmony_ci#include <linux/page-flags.h>
1762306a36Sopenharmony_ci#include <linux/mount.h>
1862306a36Sopenharmony_ci#include <linux/file.h>
1962306a36Sopenharmony_ci#include <linux/scatterlist.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/xattr.h>
2262306a36Sopenharmony_ci#include <asm/unaligned.h>
2362306a36Sopenharmony_ci#include "ecryptfs_kernel.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * ecryptfs_get_locked_page
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * Get one page from cache or lower f/s, return error otherwise.
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * Returns locked and up-to-date page (if ok), with increased
3162306a36Sopenharmony_ci * refcnt.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_cistruct page *ecryptfs_get_locked_page(struct inode *inode, loff_t index)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct page *page = read_mapping_page(inode->i_mapping, index, NULL);
3662306a36Sopenharmony_ci	if (!IS_ERR(page))
3762306a36Sopenharmony_ci		lock_page(page);
3862306a36Sopenharmony_ci	return page;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/**
4262306a36Sopenharmony_ci * ecryptfs_writepage
4362306a36Sopenharmony_ci * @page: Page that is locked before this call is made
4462306a36Sopenharmony_ci * @wbc: Write-back control structure
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * Returns zero on success; non-zero otherwise
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci * This is where we encrypt the data and pass the encrypted data to
4962306a36Sopenharmony_ci * the lower filesystem.  In OpenPGP-compatible mode, we operate on
5062306a36Sopenharmony_ci * entire underlying packets.
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_cistatic int ecryptfs_writepage(struct page *page, struct writeback_control *wbc)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	int rc;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	rc = ecryptfs_encrypt_page(page);
5762306a36Sopenharmony_ci	if (rc) {
5862306a36Sopenharmony_ci		ecryptfs_printk(KERN_WARNING, "Error encrypting "
5962306a36Sopenharmony_ci				"page (upper index [0x%.16lx])\n", page->index);
6062306a36Sopenharmony_ci		ClearPageUptodate(page);
6162306a36Sopenharmony_ci		goto out;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci	SetPageUptodate(page);
6462306a36Sopenharmony_ciout:
6562306a36Sopenharmony_ci	unlock_page(page);
6662306a36Sopenharmony_ci	return rc;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void strip_xattr_flag(char *page_virt,
7062306a36Sopenharmony_ci			     struct ecryptfs_crypt_stat *crypt_stat)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) {
7362306a36Sopenharmony_ci		size_t written;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci		crypt_stat->flags &= ~ECRYPTFS_METADATA_IN_XATTR;
7662306a36Sopenharmony_ci		ecryptfs_write_crypt_stat_flags(page_virt, crypt_stat,
7762306a36Sopenharmony_ci						&written);
7862306a36Sopenharmony_ci		crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/*
8362306a36Sopenharmony_ci *   Header Extent:
8462306a36Sopenharmony_ci *     Octets 0-7:        Unencrypted file size (big-endian)
8562306a36Sopenharmony_ci *     Octets 8-15:       eCryptfs special marker
8662306a36Sopenharmony_ci *     Octets 16-19:      Flags
8762306a36Sopenharmony_ci *      Octet 16:         File format version number (between 0 and 255)
8862306a36Sopenharmony_ci *      Octets 17-18:     Reserved
8962306a36Sopenharmony_ci *      Octet 19:         Bit 1 (lsb): Reserved
9062306a36Sopenharmony_ci *                        Bit 2: Encrypted?
9162306a36Sopenharmony_ci *                        Bits 3-8: Reserved
9262306a36Sopenharmony_ci *     Octets 20-23:      Header extent size (big-endian)
9362306a36Sopenharmony_ci *     Octets 24-25:      Number of header extents at front of file
9462306a36Sopenharmony_ci *                        (big-endian)
9562306a36Sopenharmony_ci *     Octet  26:         Begin RFC 2440 authentication token packet set
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/**
9962306a36Sopenharmony_ci * ecryptfs_copy_up_encrypted_with_header
10062306a36Sopenharmony_ci * @page: Sort of a ``virtual'' representation of the encrypted lower
10162306a36Sopenharmony_ci *        file. The actual lower file does not have the metadata in
10262306a36Sopenharmony_ci *        the header. This is locked.
10362306a36Sopenharmony_ci * @crypt_stat: The eCryptfs inode's cryptographic context
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci * The ``view'' is the version of the file that userspace winds up
10662306a36Sopenharmony_ci * seeing, with the header information inserted.
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_cistatic int
10962306a36Sopenharmony_ciecryptfs_copy_up_encrypted_with_header(struct page *page,
11062306a36Sopenharmony_ci				       struct ecryptfs_crypt_stat *crypt_stat)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	loff_t extent_num_in_page = 0;
11362306a36Sopenharmony_ci	loff_t num_extents_per_page = (PAGE_SIZE
11462306a36Sopenharmony_ci				       / crypt_stat->extent_size);
11562306a36Sopenharmony_ci	int rc = 0;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	while (extent_num_in_page < num_extents_per_page) {
11862306a36Sopenharmony_ci		loff_t view_extent_num = ((((loff_t)page->index)
11962306a36Sopenharmony_ci					   * num_extents_per_page)
12062306a36Sopenharmony_ci					  + extent_num_in_page);
12162306a36Sopenharmony_ci		size_t num_header_extents_at_front =
12262306a36Sopenharmony_ci			(crypt_stat->metadata_size / crypt_stat->extent_size);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		if (view_extent_num < num_header_extents_at_front) {
12562306a36Sopenharmony_ci			/* This is a header extent */
12662306a36Sopenharmony_ci			char *page_virt;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci			page_virt = kmap_local_page(page);
12962306a36Sopenharmony_ci			memset(page_virt, 0, PAGE_SIZE);
13062306a36Sopenharmony_ci			/* TODO: Support more than one header extent */
13162306a36Sopenharmony_ci			if (view_extent_num == 0) {
13262306a36Sopenharmony_ci				size_t written;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci				rc = ecryptfs_read_xattr_region(
13562306a36Sopenharmony_ci					page_virt, page->mapping->host);
13662306a36Sopenharmony_ci				strip_xattr_flag(page_virt + 16, crypt_stat);
13762306a36Sopenharmony_ci				ecryptfs_write_header_metadata(page_virt + 20,
13862306a36Sopenharmony_ci							       crypt_stat,
13962306a36Sopenharmony_ci							       &written);
14062306a36Sopenharmony_ci			}
14162306a36Sopenharmony_ci			kunmap_local(page_virt);
14262306a36Sopenharmony_ci			flush_dcache_page(page);
14362306a36Sopenharmony_ci			if (rc) {
14462306a36Sopenharmony_ci				printk(KERN_ERR "%s: Error reading xattr "
14562306a36Sopenharmony_ci				       "region; rc = [%d]\n", __func__, rc);
14662306a36Sopenharmony_ci				goto out;
14762306a36Sopenharmony_ci			}
14862306a36Sopenharmony_ci		} else {
14962306a36Sopenharmony_ci			/* This is an encrypted data extent */
15062306a36Sopenharmony_ci			loff_t lower_offset =
15162306a36Sopenharmony_ci				((view_extent_num * crypt_stat->extent_size)
15262306a36Sopenharmony_ci				 - crypt_stat->metadata_size);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci			rc = ecryptfs_read_lower_page_segment(
15562306a36Sopenharmony_ci				page, (lower_offset >> PAGE_SHIFT),
15662306a36Sopenharmony_ci				(lower_offset & ~PAGE_MASK),
15762306a36Sopenharmony_ci				crypt_stat->extent_size, page->mapping->host);
15862306a36Sopenharmony_ci			if (rc) {
15962306a36Sopenharmony_ci				printk(KERN_ERR "%s: Error attempting to read "
16062306a36Sopenharmony_ci				       "extent at offset [%lld] in the lower "
16162306a36Sopenharmony_ci				       "file; rc = [%d]\n", __func__,
16262306a36Sopenharmony_ci				       lower_offset, rc);
16362306a36Sopenharmony_ci				goto out;
16462306a36Sopenharmony_ci			}
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci		extent_num_in_page++;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ciout:
16962306a36Sopenharmony_ci	return rc;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci/**
17362306a36Sopenharmony_ci * ecryptfs_read_folio
17462306a36Sopenharmony_ci * @file: An eCryptfs file
17562306a36Sopenharmony_ci * @folio: Folio from eCryptfs inode mapping into which to stick the read data
17662306a36Sopenharmony_ci *
17762306a36Sopenharmony_ci * Read in a folio, decrypting if necessary.
17862306a36Sopenharmony_ci *
17962306a36Sopenharmony_ci * Returns zero on success; non-zero on error.
18062306a36Sopenharmony_ci */
18162306a36Sopenharmony_cistatic int ecryptfs_read_folio(struct file *file, struct folio *folio)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct page *page = &folio->page;
18462306a36Sopenharmony_ci	struct ecryptfs_crypt_stat *crypt_stat =
18562306a36Sopenharmony_ci		&ecryptfs_inode_to_private(page->mapping->host)->crypt_stat;
18662306a36Sopenharmony_ci	int rc = 0;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (!crypt_stat || !(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
18962306a36Sopenharmony_ci		rc = ecryptfs_read_lower_page_segment(page, page->index, 0,
19062306a36Sopenharmony_ci						      PAGE_SIZE,
19162306a36Sopenharmony_ci						      page->mapping->host);
19262306a36Sopenharmony_ci	} else if (crypt_stat->flags & ECRYPTFS_VIEW_AS_ENCRYPTED) {
19362306a36Sopenharmony_ci		if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) {
19462306a36Sopenharmony_ci			rc = ecryptfs_copy_up_encrypted_with_header(page,
19562306a36Sopenharmony_ci								    crypt_stat);
19662306a36Sopenharmony_ci			if (rc) {
19762306a36Sopenharmony_ci				printk(KERN_ERR "%s: Error attempting to copy "
19862306a36Sopenharmony_ci				       "the encrypted content from the lower "
19962306a36Sopenharmony_ci				       "file whilst inserting the metadata "
20062306a36Sopenharmony_ci				       "from the xattr into the header; rc = "
20162306a36Sopenharmony_ci				       "[%d]\n", __func__, rc);
20262306a36Sopenharmony_ci				goto out;
20362306a36Sopenharmony_ci			}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		} else {
20662306a36Sopenharmony_ci			rc = ecryptfs_read_lower_page_segment(
20762306a36Sopenharmony_ci				page, page->index, 0, PAGE_SIZE,
20862306a36Sopenharmony_ci				page->mapping->host);
20962306a36Sopenharmony_ci			if (rc) {
21062306a36Sopenharmony_ci				printk(KERN_ERR "Error reading page; rc = "
21162306a36Sopenharmony_ci				       "[%d]\n", rc);
21262306a36Sopenharmony_ci				goto out;
21362306a36Sopenharmony_ci			}
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci	} else {
21662306a36Sopenharmony_ci		rc = ecryptfs_decrypt_page(page);
21762306a36Sopenharmony_ci		if (rc) {
21862306a36Sopenharmony_ci			ecryptfs_printk(KERN_ERR, "Error decrypting page; "
21962306a36Sopenharmony_ci					"rc = [%d]\n", rc);
22062306a36Sopenharmony_ci			goto out;
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ciout:
22462306a36Sopenharmony_ci	if (rc)
22562306a36Sopenharmony_ci		ClearPageUptodate(page);
22662306a36Sopenharmony_ci	else
22762306a36Sopenharmony_ci		SetPageUptodate(page);
22862306a36Sopenharmony_ci	ecryptfs_printk(KERN_DEBUG, "Unlocking page with index = [0x%.16lx]\n",
22962306a36Sopenharmony_ci			page->index);
23062306a36Sopenharmony_ci	unlock_page(page);
23162306a36Sopenharmony_ci	return rc;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/*
23562306a36Sopenharmony_ci * Called with lower inode mutex held.
23662306a36Sopenharmony_ci */
23762306a36Sopenharmony_cistatic int fill_zeros_to_end_of_page(struct page *page, unsigned int to)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct inode *inode = page->mapping->host;
24062306a36Sopenharmony_ci	int end_byte_in_page;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if ((i_size_read(inode) / PAGE_SIZE) != page->index)
24362306a36Sopenharmony_ci		goto out;
24462306a36Sopenharmony_ci	end_byte_in_page = i_size_read(inode) % PAGE_SIZE;
24562306a36Sopenharmony_ci	if (to > end_byte_in_page)
24662306a36Sopenharmony_ci		end_byte_in_page = to;
24762306a36Sopenharmony_ci	zero_user_segment(page, end_byte_in_page, PAGE_SIZE);
24862306a36Sopenharmony_ciout:
24962306a36Sopenharmony_ci	return 0;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/**
25362306a36Sopenharmony_ci * ecryptfs_write_begin
25462306a36Sopenharmony_ci * @file: The eCryptfs file
25562306a36Sopenharmony_ci * @mapping: The eCryptfs object
25662306a36Sopenharmony_ci * @pos: The file offset at which to start writing
25762306a36Sopenharmony_ci * @len: Length of the write
25862306a36Sopenharmony_ci * @pagep: Pointer to return the page
25962306a36Sopenharmony_ci * @fsdata: Pointer to return fs data (unused)
26062306a36Sopenharmony_ci *
26162306a36Sopenharmony_ci * This function must zero any hole we create
26262306a36Sopenharmony_ci *
26362306a36Sopenharmony_ci * Returns zero on success; non-zero otherwise
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_cistatic int ecryptfs_write_begin(struct file *file,
26662306a36Sopenharmony_ci			struct address_space *mapping,
26762306a36Sopenharmony_ci			loff_t pos, unsigned len,
26862306a36Sopenharmony_ci			struct page **pagep, void **fsdata)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	pgoff_t index = pos >> PAGE_SHIFT;
27162306a36Sopenharmony_ci	struct page *page;
27262306a36Sopenharmony_ci	loff_t prev_page_end_size;
27362306a36Sopenharmony_ci	int rc = 0;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	page = grab_cache_page_write_begin(mapping, index);
27662306a36Sopenharmony_ci	if (!page)
27762306a36Sopenharmony_ci		return -ENOMEM;
27862306a36Sopenharmony_ci	*pagep = page;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	prev_page_end_size = ((loff_t)index << PAGE_SHIFT);
28162306a36Sopenharmony_ci	if (!PageUptodate(page)) {
28262306a36Sopenharmony_ci		struct ecryptfs_crypt_stat *crypt_stat =
28362306a36Sopenharmony_ci			&ecryptfs_inode_to_private(mapping->host)->crypt_stat;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
28662306a36Sopenharmony_ci			rc = ecryptfs_read_lower_page_segment(
28762306a36Sopenharmony_ci				page, index, 0, PAGE_SIZE, mapping->host);
28862306a36Sopenharmony_ci			if (rc) {
28962306a36Sopenharmony_ci				printk(KERN_ERR "%s: Error attempting to read "
29062306a36Sopenharmony_ci				       "lower page segment; rc = [%d]\n",
29162306a36Sopenharmony_ci				       __func__, rc);
29262306a36Sopenharmony_ci				ClearPageUptodate(page);
29362306a36Sopenharmony_ci				goto out;
29462306a36Sopenharmony_ci			} else
29562306a36Sopenharmony_ci				SetPageUptodate(page);
29662306a36Sopenharmony_ci		} else if (crypt_stat->flags & ECRYPTFS_VIEW_AS_ENCRYPTED) {
29762306a36Sopenharmony_ci			if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) {
29862306a36Sopenharmony_ci				rc = ecryptfs_copy_up_encrypted_with_header(
29962306a36Sopenharmony_ci					page, crypt_stat);
30062306a36Sopenharmony_ci				if (rc) {
30162306a36Sopenharmony_ci					printk(KERN_ERR "%s: Error attempting "
30262306a36Sopenharmony_ci					       "to copy the encrypted content "
30362306a36Sopenharmony_ci					       "from the lower file whilst "
30462306a36Sopenharmony_ci					       "inserting the metadata from "
30562306a36Sopenharmony_ci					       "the xattr into the header; rc "
30662306a36Sopenharmony_ci					       "= [%d]\n", __func__, rc);
30762306a36Sopenharmony_ci					ClearPageUptodate(page);
30862306a36Sopenharmony_ci					goto out;
30962306a36Sopenharmony_ci				}
31062306a36Sopenharmony_ci				SetPageUptodate(page);
31162306a36Sopenharmony_ci			} else {
31262306a36Sopenharmony_ci				rc = ecryptfs_read_lower_page_segment(
31362306a36Sopenharmony_ci					page, index, 0, PAGE_SIZE,
31462306a36Sopenharmony_ci					mapping->host);
31562306a36Sopenharmony_ci				if (rc) {
31662306a36Sopenharmony_ci					printk(KERN_ERR "%s: Error reading "
31762306a36Sopenharmony_ci					       "page; rc = [%d]\n",
31862306a36Sopenharmony_ci					       __func__, rc);
31962306a36Sopenharmony_ci					ClearPageUptodate(page);
32062306a36Sopenharmony_ci					goto out;
32162306a36Sopenharmony_ci				}
32262306a36Sopenharmony_ci				SetPageUptodate(page);
32362306a36Sopenharmony_ci			}
32462306a36Sopenharmony_ci		} else {
32562306a36Sopenharmony_ci			if (prev_page_end_size
32662306a36Sopenharmony_ci			    >= i_size_read(page->mapping->host)) {
32762306a36Sopenharmony_ci				zero_user(page, 0, PAGE_SIZE);
32862306a36Sopenharmony_ci				SetPageUptodate(page);
32962306a36Sopenharmony_ci			} else if (len < PAGE_SIZE) {
33062306a36Sopenharmony_ci				rc = ecryptfs_decrypt_page(page);
33162306a36Sopenharmony_ci				if (rc) {
33262306a36Sopenharmony_ci					printk(KERN_ERR "%s: Error decrypting "
33362306a36Sopenharmony_ci					       "page at index [%ld]; "
33462306a36Sopenharmony_ci					       "rc = [%d]\n",
33562306a36Sopenharmony_ci					       __func__, page->index, rc);
33662306a36Sopenharmony_ci					ClearPageUptodate(page);
33762306a36Sopenharmony_ci					goto out;
33862306a36Sopenharmony_ci				}
33962306a36Sopenharmony_ci				SetPageUptodate(page);
34062306a36Sopenharmony_ci			}
34162306a36Sopenharmony_ci		}
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci	/* If creating a page or more of holes, zero them out via truncate.
34462306a36Sopenharmony_ci	 * Note, this will increase i_size. */
34562306a36Sopenharmony_ci	if (index != 0) {
34662306a36Sopenharmony_ci		if (prev_page_end_size > i_size_read(page->mapping->host)) {
34762306a36Sopenharmony_ci			rc = ecryptfs_truncate(file->f_path.dentry,
34862306a36Sopenharmony_ci					       prev_page_end_size);
34962306a36Sopenharmony_ci			if (rc) {
35062306a36Sopenharmony_ci				printk(KERN_ERR "%s: Error on attempt to "
35162306a36Sopenharmony_ci				       "truncate to (higher) offset [%lld];"
35262306a36Sopenharmony_ci				       " rc = [%d]\n", __func__,
35362306a36Sopenharmony_ci				       prev_page_end_size, rc);
35462306a36Sopenharmony_ci				goto out;
35562306a36Sopenharmony_ci			}
35662306a36Sopenharmony_ci		}
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci	/* Writing to a new page, and creating a small hole from start
35962306a36Sopenharmony_ci	 * of page?  Zero it out. */
36062306a36Sopenharmony_ci	if ((i_size_read(mapping->host) == prev_page_end_size)
36162306a36Sopenharmony_ci	    && (pos != 0))
36262306a36Sopenharmony_ci		zero_user(page, 0, PAGE_SIZE);
36362306a36Sopenharmony_ciout:
36462306a36Sopenharmony_ci	if (unlikely(rc)) {
36562306a36Sopenharmony_ci		unlock_page(page);
36662306a36Sopenharmony_ci		put_page(page);
36762306a36Sopenharmony_ci		*pagep = NULL;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci	return rc;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/*
37362306a36Sopenharmony_ci * ecryptfs_write_inode_size_to_header
37462306a36Sopenharmony_ci *
37562306a36Sopenharmony_ci * Writes the lower file size to the first 8 bytes of the header.
37662306a36Sopenharmony_ci *
37762306a36Sopenharmony_ci * Returns zero on success; non-zero on error.
37862306a36Sopenharmony_ci */
37962306a36Sopenharmony_cistatic int ecryptfs_write_inode_size_to_header(struct inode *ecryptfs_inode)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	char *file_size_virt;
38262306a36Sopenharmony_ci	int rc;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	file_size_virt = kmalloc(sizeof(u64), GFP_KERNEL);
38562306a36Sopenharmony_ci	if (!file_size_virt) {
38662306a36Sopenharmony_ci		rc = -ENOMEM;
38762306a36Sopenharmony_ci		goto out;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci	put_unaligned_be64(i_size_read(ecryptfs_inode), file_size_virt);
39062306a36Sopenharmony_ci	rc = ecryptfs_write_lower(ecryptfs_inode, file_size_virt, 0,
39162306a36Sopenharmony_ci				  sizeof(u64));
39262306a36Sopenharmony_ci	kfree(file_size_virt);
39362306a36Sopenharmony_ci	if (rc < 0)
39462306a36Sopenharmony_ci		printk(KERN_ERR "%s: Error writing file size to header; "
39562306a36Sopenharmony_ci		       "rc = [%d]\n", __func__, rc);
39662306a36Sopenharmony_ci	else
39762306a36Sopenharmony_ci		rc = 0;
39862306a36Sopenharmony_ciout:
39962306a36Sopenharmony_ci	return rc;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistruct kmem_cache *ecryptfs_xattr_cache;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int ecryptfs_write_inode_size_to_xattr(struct inode *ecryptfs_inode)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	ssize_t size;
40762306a36Sopenharmony_ci	void *xattr_virt;
40862306a36Sopenharmony_ci	struct dentry *lower_dentry =
40962306a36Sopenharmony_ci		ecryptfs_inode_to_private(ecryptfs_inode)->lower_file->f_path.dentry;
41062306a36Sopenharmony_ci	struct inode *lower_inode = d_inode(lower_dentry);
41162306a36Sopenharmony_ci	int rc;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (!(lower_inode->i_opflags & IOP_XATTR)) {
41462306a36Sopenharmony_ci		printk(KERN_WARNING
41562306a36Sopenharmony_ci		       "No support for setting xattr in lower filesystem\n");
41662306a36Sopenharmony_ci		rc = -ENOSYS;
41762306a36Sopenharmony_ci		goto out;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci	xattr_virt = kmem_cache_alloc(ecryptfs_xattr_cache, GFP_KERNEL);
42062306a36Sopenharmony_ci	if (!xattr_virt) {
42162306a36Sopenharmony_ci		rc = -ENOMEM;
42262306a36Sopenharmony_ci		goto out;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci	inode_lock(lower_inode);
42562306a36Sopenharmony_ci	size = __vfs_getxattr(lower_dentry, lower_inode, ECRYPTFS_XATTR_NAME,
42662306a36Sopenharmony_ci			      xattr_virt, PAGE_SIZE);
42762306a36Sopenharmony_ci	if (size < 0)
42862306a36Sopenharmony_ci		size = 8;
42962306a36Sopenharmony_ci	put_unaligned_be64(i_size_read(ecryptfs_inode), xattr_virt);
43062306a36Sopenharmony_ci	rc = __vfs_setxattr(&nop_mnt_idmap, lower_dentry, lower_inode,
43162306a36Sopenharmony_ci			    ECRYPTFS_XATTR_NAME, xattr_virt, size, 0);
43262306a36Sopenharmony_ci	inode_unlock(lower_inode);
43362306a36Sopenharmony_ci	if (rc)
43462306a36Sopenharmony_ci		printk(KERN_ERR "Error whilst attempting to write inode size "
43562306a36Sopenharmony_ci		       "to lower file xattr; rc = [%d]\n", rc);
43662306a36Sopenharmony_ci	kmem_cache_free(ecryptfs_xattr_cache, xattr_virt);
43762306a36Sopenharmony_ciout:
43862306a36Sopenharmony_ci	return rc;
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ciint ecryptfs_write_inode_size_to_metadata(struct inode *ecryptfs_inode)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	struct ecryptfs_crypt_stat *crypt_stat;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	crypt_stat = &ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat;
44662306a36Sopenharmony_ci	BUG_ON(!(crypt_stat->flags & ECRYPTFS_ENCRYPTED));
44762306a36Sopenharmony_ci	if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
44862306a36Sopenharmony_ci		return ecryptfs_write_inode_size_to_xattr(ecryptfs_inode);
44962306a36Sopenharmony_ci	else
45062306a36Sopenharmony_ci		return ecryptfs_write_inode_size_to_header(ecryptfs_inode);
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci/**
45462306a36Sopenharmony_ci * ecryptfs_write_end
45562306a36Sopenharmony_ci * @file: The eCryptfs file object
45662306a36Sopenharmony_ci * @mapping: The eCryptfs object
45762306a36Sopenharmony_ci * @pos: The file position
45862306a36Sopenharmony_ci * @len: The length of the data (unused)
45962306a36Sopenharmony_ci * @copied: The amount of data copied
46062306a36Sopenharmony_ci * @page: The eCryptfs page
46162306a36Sopenharmony_ci * @fsdata: The fsdata (unused)
46262306a36Sopenharmony_ci */
46362306a36Sopenharmony_cistatic int ecryptfs_write_end(struct file *file,
46462306a36Sopenharmony_ci			struct address_space *mapping,
46562306a36Sopenharmony_ci			loff_t pos, unsigned len, unsigned copied,
46662306a36Sopenharmony_ci			struct page *page, void *fsdata)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	pgoff_t index = pos >> PAGE_SHIFT;
46962306a36Sopenharmony_ci	unsigned from = pos & (PAGE_SIZE - 1);
47062306a36Sopenharmony_ci	unsigned to = from + copied;
47162306a36Sopenharmony_ci	struct inode *ecryptfs_inode = mapping->host;
47262306a36Sopenharmony_ci	struct ecryptfs_crypt_stat *crypt_stat =
47362306a36Sopenharmony_ci		&ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat;
47462306a36Sopenharmony_ci	int rc;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	ecryptfs_printk(KERN_DEBUG, "Calling fill_zeros_to_end_of_page"
47762306a36Sopenharmony_ci			"(page w/ index = [0x%.16lx], to = [%d])\n", index, to);
47862306a36Sopenharmony_ci	if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
47962306a36Sopenharmony_ci		rc = ecryptfs_write_lower_page_segment(ecryptfs_inode, page, 0,
48062306a36Sopenharmony_ci						       to);
48162306a36Sopenharmony_ci		if (!rc) {
48262306a36Sopenharmony_ci			rc = copied;
48362306a36Sopenharmony_ci			fsstack_copy_inode_size(ecryptfs_inode,
48462306a36Sopenharmony_ci				ecryptfs_inode_to_lower(ecryptfs_inode));
48562306a36Sopenharmony_ci		}
48662306a36Sopenharmony_ci		goto out;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci	if (!PageUptodate(page)) {
48962306a36Sopenharmony_ci		if (copied < PAGE_SIZE) {
49062306a36Sopenharmony_ci			rc = 0;
49162306a36Sopenharmony_ci			goto out;
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci		SetPageUptodate(page);
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci	/* Fills in zeros if 'to' goes beyond inode size */
49662306a36Sopenharmony_ci	rc = fill_zeros_to_end_of_page(page, to);
49762306a36Sopenharmony_ci	if (rc) {
49862306a36Sopenharmony_ci		ecryptfs_printk(KERN_WARNING, "Error attempting to fill "
49962306a36Sopenharmony_ci			"zeros in page with index = [0x%.16lx]\n", index);
50062306a36Sopenharmony_ci		goto out;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci	rc = ecryptfs_encrypt_page(page);
50362306a36Sopenharmony_ci	if (rc) {
50462306a36Sopenharmony_ci		ecryptfs_printk(KERN_WARNING, "Error encrypting page (upper "
50562306a36Sopenharmony_ci				"index [0x%.16lx])\n", index);
50662306a36Sopenharmony_ci		goto out;
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci	if (pos + copied > i_size_read(ecryptfs_inode)) {
50962306a36Sopenharmony_ci		i_size_write(ecryptfs_inode, pos + copied);
51062306a36Sopenharmony_ci		ecryptfs_printk(KERN_DEBUG, "Expanded file size to "
51162306a36Sopenharmony_ci			"[0x%.16llx]\n",
51262306a36Sopenharmony_ci			(unsigned long long)i_size_read(ecryptfs_inode));
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci	rc = ecryptfs_write_inode_size_to_metadata(ecryptfs_inode);
51562306a36Sopenharmony_ci	if (rc)
51662306a36Sopenharmony_ci		printk(KERN_ERR "Error writing inode size to metadata; "
51762306a36Sopenharmony_ci		       "rc = [%d]\n", rc);
51862306a36Sopenharmony_ci	else
51962306a36Sopenharmony_ci		rc = copied;
52062306a36Sopenharmony_ciout:
52162306a36Sopenharmony_ci	unlock_page(page);
52262306a36Sopenharmony_ci	put_page(page);
52362306a36Sopenharmony_ci	return rc;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic sector_t ecryptfs_bmap(struct address_space *mapping, sector_t block)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct inode *lower_inode = ecryptfs_inode_to_lower(mapping->host);
52962306a36Sopenharmony_ci	int ret = bmap(lower_inode, &block);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (ret)
53262306a36Sopenharmony_ci		return 0;
53362306a36Sopenharmony_ci	return block;
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci#include <linux/buffer_head.h>
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ciconst struct address_space_operations ecryptfs_aops = {
53962306a36Sopenharmony_ci	/*
54062306a36Sopenharmony_ci	 * XXX: This is pretty broken for multiple reasons: ecryptfs does not
54162306a36Sopenharmony_ci	 * actually use buffer_heads, and ecryptfs will crash without
54262306a36Sopenharmony_ci	 * CONFIG_BLOCK.  But it matches the behavior before the default for
54362306a36Sopenharmony_ci	 * address_space_operations without the ->dirty_folio method was
54462306a36Sopenharmony_ci	 * cleaned up, so this is the best we can do without maintainer
54562306a36Sopenharmony_ci	 * feedback.
54662306a36Sopenharmony_ci	 */
54762306a36Sopenharmony_ci#ifdef CONFIG_BLOCK
54862306a36Sopenharmony_ci	.dirty_folio	= block_dirty_folio,
54962306a36Sopenharmony_ci	.invalidate_folio = block_invalidate_folio,
55062306a36Sopenharmony_ci#endif
55162306a36Sopenharmony_ci	.writepage = ecryptfs_writepage,
55262306a36Sopenharmony_ci	.read_folio = ecryptfs_read_folio,
55362306a36Sopenharmony_ci	.write_begin = ecryptfs_write_begin,
55462306a36Sopenharmony_ci	.write_end = ecryptfs_write_end,
55562306a36Sopenharmony_ci	.bmap = ecryptfs_bmap,
55662306a36Sopenharmony_ci};
557