From 7d9ebf33d85317f3f258c627de51701e2bf7642d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20Hsiao=20=E8=95=AD=E6=B3=95=E5=AE=A3?= Date: Fri, 17 May 2024 10:09:55 +0000 Subject: [PATCH 01/15] ecryptfs: Set s_time_gran to get correct time granularity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set the eCryptfs superblock time granularity, using the lower filesystem's s_time_gran value, to prevent unnecessary inode timestamp truncation to the granularity of a full second. The use of utimensat(2) to set a timestamp with nanosecond precision would trigger this bug. That occurred when using the following utilities to update timestamps of a file: * cp -p: copy a file and preserve its atime and mtime * touch -r: touch a file and use a reference file's timestamps Closes: https://bugs.launchpad.net/ecryptfs/+bug/1890486 Signed-off-by: Frank Hsiao 蕭法宣 [tyhicks: Partially rewrite the commit message] Signed-off-by: Tyler Hicks --- fs/ecryptfs/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index f4ab387eb4ed..5f37cddb956f 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -531,6 +531,7 @@ static int ecryptfs_get_tree(struct fs_context *fc) s->s_blocksize = path.dentry->d_sb->s_blocksize; s->s_magic = ECRYPTFS_SUPER_MAGIC; s->s_stack_depth = path.dentry->d_sb->s_stack_depth + 1; + s->s_time_gran = path.dentry->d_sb->s_time_gran; rc = -EINVAL; if (s->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { From f7a1c0283f135833db164cc0fac8d422fed2079e Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 5 Feb 2026 13:51:00 +0100 Subject: [PATCH 02/15] ecryptfs: Replace memcpy + manual NUL termination with strscpy Use strscpy() to copy the NUL-terminated '->token.password.signature' and 'sig' to the destination buffers instead of using memcpy() followed by manual NUL terminations. Signed-off-by: Thorsten Blum Signed-off-by: Tyler Hicks --- fs/ecryptfs/debug.c | 5 ++--- fs/ecryptfs/keystore.c | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/fs/ecryptfs/debug.c b/fs/ecryptfs/debug.c index c185a8cb5fe2..42643702457c 100644 --- a/fs/ecryptfs/debug.c +++ b/fs/ecryptfs/debug.c @@ -7,6 +7,7 @@ * Author(s): Michael A. Halcrow */ +#include #include "ecryptfs_kernel.h" /* @@ -33,9 +34,7 @@ void ecryptfs_dump_auth_tok(struct ecryptfs_auth_tok *auth_tok) ECRYPTFS_PERSISTENT_PASSWORD) { ecryptfs_printk(KERN_DEBUG, " * persistent\n"); } - memcpy(sig, auth_tok->token.password.signature, - ECRYPTFS_SIG_SIZE_HEX); - sig[ECRYPTFS_SIG_SIZE_HEX] = '\0'; + strscpy(sig, auth_tok->token.password.signature); ecryptfs_printk(KERN_DEBUG, " * signature = [%s]\n", sig); } ecryptfs_printk(KERN_DEBUG, " * session_key.flags = [0x%x]\n", diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index e8494903bb42..0be746493e56 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -2458,8 +2458,7 @@ int ecryptfs_add_keysig(struct ecryptfs_crypt_stat *crypt_stat, char *sig) if (!new_key_sig) return -ENOMEM; - memcpy(new_key_sig->keysig, sig, ECRYPTFS_SIG_SIZE_HEX); - new_key_sig->keysig[ECRYPTFS_SIG_SIZE_HEX] = '\0'; + strscpy(new_key_sig->keysig, sig); /* Caller must hold keysig_list_mutex */ list_add(&new_key_sig->crypt_stat_list, &crypt_stat->keysig_list); @@ -2479,9 +2478,8 @@ ecryptfs_add_global_auth_tok(struct ecryptfs_mount_crypt_stat *mount_crypt_stat, if (!new_auth_tok) return -ENOMEM; - memcpy(new_auth_tok->sig, sig, ECRYPTFS_SIG_SIZE_HEX); + strscpy(new_auth_tok->sig, sig); new_auth_tok->flags = global_auth_tok_flags; - new_auth_tok->sig[ECRYPTFS_SIG_SIZE_HEX] = '\0'; mutex_lock(&mount_crypt_stat->global_auth_tok_list_mutex); list_add(&new_auth_tok->mount_crypt_stat_list, &mount_crypt_stat->global_auth_tok_list); From 8b9bf58bc3a6f148d990bb697a3b6dbb11672f86 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 5 Feb 2026 14:24:51 +0100 Subject: [PATCH 03/15] ecryptfs: Use struct_size to improve process_response + send_miscdev Use struct_size(), which provides additional compile-time checks for structures with flexible array members (e.g., __must_be_array()), to determine the allocation size for a new 'struct ecryptfs_message'. In send_miscdev(), reuse 'msg_size' instead of recalculating it. Signed-off-by: Thorsten Blum Signed-off-by: Tyler Hicks --- fs/ecryptfs/messaging.c | 3 ++- fs/ecryptfs/miscdev.c | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c index 30c8e15d87b5..03c60f0850ca 100644 --- a/fs/ecryptfs/messaging.c +++ b/fs/ecryptfs/messaging.c @@ -6,6 +6,7 @@ * Author(s): Michael A. Halcrow * Tyler Hicks */ +#include #include #include #include @@ -232,7 +233,7 @@ int ecryptfs_process_response(struct ecryptfs_daemon *daemon, msg_ctx->counter, seq); goto unlock; } - msg_size = (sizeof(*msg) + msg->data_len); + msg_size = struct_size(msg, data, msg->data_len); msg_ctx->msg = kmemdup(msg, msg_size, GFP_KERNEL); if (!msg_ctx->msg) { rc = -ENOMEM; diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c index 4e62c3cef70f..5a7d08149922 100644 --- a/fs/ecryptfs/miscdev.c +++ b/fs/ecryptfs/miscdev.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -148,8 +149,10 @@ int ecryptfs_send_miscdev(char *data, size_t data_size, u16 msg_flags, struct ecryptfs_daemon *daemon) { struct ecryptfs_message *msg; + size_t msg_size; - msg = kmalloc((sizeof(*msg) + data_size), GFP_KERNEL); + msg_size = struct_size(msg, data, data_size); + msg = kmalloc(msg_size, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -159,7 +162,7 @@ int ecryptfs_send_miscdev(char *data, size_t data_size, msg_ctx->msg->data_len = data_size; msg_ctx->type = msg_type; memcpy(msg_ctx->msg->data, data, data_size); - msg_ctx->msg_size = (sizeof(*msg_ctx->msg) + data_size); + msg_ctx->msg_size = msg_size; list_add_tail(&msg_ctx->daemon_out_list, &daemon->msg_ctx_out_queue); mutex_unlock(&msg_ctx->mux); From 3b7f363b7bba203318c77d91c131123cf059cdbb Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 5 Feb 2026 14:25:32 +0100 Subject: [PATCH 04/15] ecryptfs: Fix tag number in encrypt_filename() error message Report the correct tag number (70) instead of tag 72. Use ecryptfs_printk() and reformat the string to silence the checkpatch warning: "WARNING: quoted string split across lines". Signed-off-by: Thorsten Blum Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 3b59346d68c5..ff001d9b4582 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1376,9 +1376,9 @@ ecryptfs_encrypt_filename(struct ecryptfs_filename *filename, mount_crypt_stat, NULL, filename->filename_size); if (rc) { - printk(KERN_ERR "%s: Error attempting to get packet " - "size for tag 72; rc = [%d]\n", __func__, - rc); + ecryptfs_printk(KERN_ERR, + "Error attempting to get packet size for tag 70; rc = [%d]\n", + rc); filename->encrypted_filename_size = 0; goto out; } From fb1b02dc02da0ff63a5965056db0c9f77842bd19 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 9 Mar 2026 23:48:24 +0100 Subject: [PATCH 05/15] ecryptfs: Remove redundant if checks in encrypt_and_encode_filename The outer if already checks if 'mount_crypt_stat' is true. Drop checking 'mount_crypt_stat' again. Use ecryptfs_printk() while we're at it. Signed-off-by: Thorsten Blum Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index ff001d9b4582..c10a2d2d9947 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1802,8 +1802,9 @@ int ecryptfs_encrypt_and_encode_filename( filename->filename_size = name_size; rc = ecryptfs_encrypt_filename(filename, mount_crypt_stat); if (rc) { - printk(KERN_ERR "%s: Error attempting to encrypt " - "filename; rc = [%d]\n", __func__, rc); + ecryptfs_printk(KERN_ERR, + "Error attempting to encrypt filename; rc = [%d]\n", + rc); kfree(filename); goto out; } @@ -1811,9 +1812,8 @@ int ecryptfs_encrypt_and_encode_filename( NULL, &encoded_name_no_prefix_size, filename->encrypted_filename, filename->encrypted_filename_size); - if (mount_crypt_stat - && (mount_crypt_stat->flags - & ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK)) + if (mount_crypt_stat->flags + & ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK) (*encoded_name_size) = (ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE + encoded_name_no_prefix_size); @@ -1828,9 +1828,8 @@ int ecryptfs_encrypt_and_encode_filename( kfree(filename); goto out; } - if (mount_crypt_stat - && (mount_crypt_stat->flags - & ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK)) { + if (mount_crypt_stat->flags + & ECRYPTFS_GLOBAL_ENCFN_USE_MOUNT_FNEK) { memcpy((*encoded_name), ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX, ECRYPTFS_FNEK_ENCRYPTED_FILENAME_PREFIX_SIZE); @@ -1848,9 +1847,9 @@ int ecryptfs_encrypt_and_encode_filename( rc = -EOPNOTSUPP; } if (rc) { - printk(KERN_ERR "%s: Error attempting to encode " - "encrypted filename; rc = [%d]\n", __func__, - rc); + ecryptfs_printk(KERN_ERR, + "Error attempting to encode encrypted filename; rc = [%d]\n", + rc); kfree((*encoded_name)); (*encoded_name) = NULL; (*encoded_name_size) = 0; From 1601fe9e0423d813b1158a52e051bd3059f74197 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 10 Mar 2026 11:26:53 +0100 Subject: [PATCH 06/15] ecryptfs: Log function name only once in decode_and_decrypt_filename ecryptfs_printk() already prints the function name using %s and __func__. Drop the redundant function name from the debug log message. Signed-off-by: Thorsten Blum Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index c10a2d2d9947..3cc4afb8b10d 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1924,8 +1924,7 @@ int ecryptfs_decode_and_decrypt_filename(char **plaintext_name, decoded_name_size); if (rc) { ecryptfs_printk(KERN_DEBUG, - "%s: Could not parse tag 70 packet from filename\n", - __func__); + "Could not parse tag 70 packet from filename\n"); goto out_free; } } else { From c8717a7fa5d2ea9048f398c16ab61d8e4c8f83b8 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 30 Mar 2026 12:35:15 +0200 Subject: [PATCH 07/15] ecryptfs: Fix typo in ecryptfs_derive_iv function comment s/vale/value/ Signed-off-by: Thorsten Blum Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 3cc4afb8b10d..a7511acc593e 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -72,7 +72,7 @@ out: /** * ecryptfs_derive_iv - * @iv: destination for the derived iv vale + * @iv: destination for the derived iv value * @crypt_stat: Pointer to crypt_stat struct for the current inode * @offset: Offset of the extent whose IV we are to derive * From cd3b3094df0ee0f147957e7a7a1103990fdd6641 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 30 Mar 2026 12:35:17 +0200 Subject: [PATCH 08/15] ecryptfs: Drop TODO comment in ecryptfs_derive_iv Remove the TODO from 2006. eCryptfs is generally not receiving new features and changing the IV derivation is only likely to happen to address security concerns in the future. Signed-off-by: Thorsten Blum [tyhicks: Add the reasoning to the commit message] Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index a7511acc593e..dd3e2f1e2544 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -89,10 +89,6 @@ void ecryptfs_derive_iv(char *iv, struct ecryptfs_crypt_stat *crypt_stat, ecryptfs_printk(KERN_DEBUG, "root iv:\n"); ecryptfs_dump_hex(crypt_stat->root_iv, crypt_stat->iv_bytes); } - /* TODO: It is probably secure to just cast the least - * significant bits of the root IV into an unsigned long and - * add the offset to that rather than go through all this - * hashing business. -Halcrow */ memcpy(src, crypt_stat->root_iv, crypt_stat->iv_bytes); memset((src + crypt_stat->iv_bytes), 0, 16); snprintf((src + crypt_stat->iv_bytes), 16, "%lld", offset); From 8f61364322a07ff6c35691b575d6fbda8e71e29d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 8 Apr 2026 08:06:36 +0200 Subject: [PATCH 09/15] ecryptfs: cleanup ecryptfs_setattr Initialize variables at declaration time where applicable and reformat conditionals to match the kernel coding style. Signed-off-by: Christoph Hellwig Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 8ab014db3e03..81cf42d01ec5 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -886,25 +886,23 @@ ecryptfs_permission(struct mnt_idmap *idmap, struct inode *inode, static int ecryptfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *ia) { - int rc = 0; - struct dentry *lower_dentry; + struct inode *inode = d_inode(dentry); + struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); + struct inode *lower_inode = ecryptfs_inode_to_lower(inode); struct iattr lower_ia; - struct inode *inode; - struct inode *lower_inode; struct ecryptfs_crypt_stat *crypt_stat; + int rc; crypt_stat = &ecryptfs_inode_to_private(d_inode(dentry))->crypt_stat; if (!(crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED)) ecryptfs_init_crypt_stat(crypt_stat); - inode = d_inode(dentry); - lower_inode = ecryptfs_inode_to_lower(inode); - lower_dentry = ecryptfs_dentry_to_lower(dentry); + mutex_lock(&crypt_stat->cs_mutex); if (d_is_dir(dentry)) crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED); - else if (d_is_reg(dentry) - && (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED) - || !(crypt_stat->flags & ECRYPTFS_KEY_VALID))) { + else if (d_is_reg(dentry) && + (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED) || + !(crypt_stat->flags & ECRYPTFS_KEY_VALID))) { struct ecryptfs_mount_crypt_stat *mount_crypt_stat; mount_crypt_stat = &ecryptfs_superblock_to_private( @@ -917,8 +915,8 @@ static int ecryptfs_setattr(struct mnt_idmap *idmap, rc = ecryptfs_read_metadata(dentry); ecryptfs_put_lower_file(inode); if (rc) { - if (!(mount_crypt_stat->flags - & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { + if (!(mount_crypt_stat->flags & + ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) { rc = -EIO; printk(KERN_WARNING "Either the lower file " "is not in a valid eCryptfs format, " From b109187378615e683d8d8a24f4bc246bd3fb7b26 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 8 Apr 2026 08:06:37 +0200 Subject: [PATCH 10/15] ecryptfs: streamline truncate_upper Use a few strategic gotos to reduce indentation and keep the main flow outside of branches. Switch all touched comments to normal kernel style and avoid breaks in printed strings for all the code touched. Signed-off-by: Christoph Hellwig Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 117 +++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 81cf42d01ec5..695573850569 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -725,83 +725,88 @@ upper_size_to_lower_size(struct ecryptfs_crypt_stat *crypt_stat, static int truncate_upper(struct dentry *dentry, struct iattr *ia, struct iattr *lower_ia) { - int rc = 0; struct inode *inode = d_inode(dentry); struct ecryptfs_crypt_stat *crypt_stat; loff_t i_size = i_size_read(inode); loff_t lower_size_before_truncate; loff_t lower_size_after_truncate; + size_t num_zeros; + int rc; if (unlikely((ia->ia_size == i_size))) { lower_ia->ia_valid &= ~ATTR_SIZE; return 0; } + rc = ecryptfs_get_lower_file(dentry, inode); if (rc) return rc; - crypt_stat = &ecryptfs_inode_to_private(d_inode(dentry))->crypt_stat; - /* Switch on growing or shrinking file */ + if (ia->ia_size > i_size) { char zero[] = { 0x00 }; + /* + * Write a single 0 at the last position of the file; this + * triggers code that will fill in 0's throughout the + * intermediate portion of the previous end of the file and the + * new end of the file. + */ + rc = ecryptfs_write(inode, zero, ia->ia_size - 1, 1); lower_ia->ia_valid &= ~ATTR_SIZE; - /* Write a single 0 at the last position of the file; - * this triggers code that will fill in 0's throughout - * the intermediate portion of the previous end of the - * file and the new and of the file */ - rc = ecryptfs_write(inode, zero, - (ia->ia_size - 1), 1); - } else { /* ia->ia_size < i_size_read(inode) */ - /* We're chopping off all the pages down to the page - * in which ia->ia_size is located. Fill in the end of - * that page from (ia->ia_size & ~PAGE_MASK) to - * PAGE_SIZE with zeros. */ - size_t num_zeros = (PAGE_SIZE - - (ia->ia_size & ~PAGE_MASK)); + goto out; + } - if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { - truncate_setsize(inode, ia->ia_size); - lower_ia->ia_size = ia->ia_size; - lower_ia->ia_valid |= ATTR_SIZE; - goto out; - } - if (num_zeros) { - char *zeros_virt; - - zeros_virt = kzalloc(num_zeros, GFP_KERNEL); - if (!zeros_virt) { - rc = -ENOMEM; - goto out; - } - rc = ecryptfs_write(inode, zeros_virt, - ia->ia_size, num_zeros); - kfree(zeros_virt); - if (rc) { - printk(KERN_ERR "Error attempting to zero out " - "the remainder of the end page on " - "reducing truncate; rc = [%d]\n", rc); - goto out; - } - } + crypt_stat = &ecryptfs_inode_to_private(d_inode(dentry))->crypt_stat; + if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { truncate_setsize(inode, ia->ia_size); - rc = ecryptfs_write_inode_size_to_metadata(inode); - if (rc) { - printk(KERN_ERR "Problem with " - "ecryptfs_write_inode_size_to_metadata; " - "rc = [%d]\n", rc); + lower_ia->ia_size = ia->ia_size; + lower_ia->ia_valid |= ATTR_SIZE; + goto out; + } + + /* + * We're chopping off all the pages down to the page in which + * ia->ia_size is located. Fill in the end of that page from + * (ia->ia_size & ~PAGE_MASK) to PAGE_SIZE with zeros. + */ + num_zeros = PAGE_SIZE - (ia->ia_size & ~PAGE_MASK); + if (num_zeros) { + char *zeros_virt; + + zeros_virt = kzalloc(num_zeros, GFP_KERNEL); + if (!zeros_virt) { + rc = -ENOMEM; goto out; } - /* We are reducing the size of the ecryptfs file, and need to - * know if we need to reduce the size of the lower file. */ - lower_size_before_truncate = - upper_size_to_lower_size(crypt_stat, i_size); - lower_size_after_truncate = - upper_size_to_lower_size(crypt_stat, ia->ia_size); - if (lower_size_after_truncate < lower_size_before_truncate) { - lower_ia->ia_size = lower_size_after_truncate; - lower_ia->ia_valid |= ATTR_SIZE; - } else - lower_ia->ia_valid &= ~ATTR_SIZE; + rc = ecryptfs_write(inode, zeros_virt, ia->ia_size, num_zeros); + kfree(zeros_virt); + if (rc) { + pr_err("Error attempting to zero out the remainder of the end page on reducing truncate; rc = [%d]\n", + rc); + goto out; + } + } + truncate_setsize(inode, ia->ia_size); + rc = ecryptfs_write_inode_size_to_metadata(inode); + if (rc) { + pr_err("Problem with ecryptfs_write_inode_size_to_metadata; rc = [%d]\n", + rc); + goto out; + } + + /* + * We are reducing the size of the ecryptfs file, and need to know if we + * need to reduce the size of the lower file. + */ + lower_size_before_truncate = + upper_size_to_lower_size(crypt_stat, i_size); + lower_size_after_truncate = + upper_size_to_lower_size(crypt_stat, ia->ia_size); + if (lower_size_after_truncate < lower_size_before_truncate) { + lower_ia->ia_size = lower_size_after_truncate; + lower_ia->ia_valid |= ATTR_SIZE; + } else { + lower_ia->ia_valid &= ~ATTR_SIZE; } out: ecryptfs_put_lower_file(inode); From b19fe74e0fc970cef90bb78ddb473ae0356bce94 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 8 Apr 2026 08:06:38 +0200 Subject: [PATCH 11/15] ecryptfs: use ZERO_PAGE instead of allocating zeroed memory in truncate_upper Use the existing pre-zeroed memory instead of allocating a new chunk. Signed-off-by: Christoph Hellwig Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 695573850569..daa63b7dd015 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -771,15 +771,8 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, */ num_zeros = PAGE_SIZE - (ia->ia_size & ~PAGE_MASK); if (num_zeros) { - char *zeros_virt; - - zeros_virt = kzalloc(num_zeros, GFP_KERNEL); - if (!zeros_virt) { - rc = -ENOMEM; - goto out; - } - rc = ecryptfs_write(inode, zeros_virt, ia->ia_size, num_zeros); - kfree(zeros_virt); + rc = ecryptfs_write(inode, page_address(ZERO_PAGE(0)), + ia->ia_size, num_zeros); if (rc) { pr_err("Error attempting to zero out the remainder of the end page on reducing truncate; rc = [%d]\n", rc); From 472dea1d2235439c0c25850d53deffc517cc8c61 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 8 Apr 2026 08:06:39 +0200 Subject: [PATCH 12/15] ecryptfs: combine the two ATTR_SIZE blocks in ecryptfs_setattr Simplify the logic in ecryptfs_setattr by combining the two ATTR_SIZE blocks. This initializes lower_ia before the size check, which is obviously correct as the size check doesn't look at it. Signed-off-by: Christoph Hellwig Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index daa63b7dd015..ec6aae5af1f8 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -934,16 +934,15 @@ static int ecryptfs_setattr(struct mnt_idmap *idmap, rc = setattr_prepare(&nop_mnt_idmap, dentry, ia); if (rc) goto out; - if (ia->ia_valid & ATTR_SIZE) { - rc = ecryptfs_inode_newsize_ok(inode, ia->ia_size); - if (rc) - goto out; - } memcpy(&lower_ia, ia, sizeof(lower_ia)); if (ia->ia_valid & ATTR_FILE) lower_ia.ia_file = ecryptfs_file_to_lower(ia->ia_file); if (ia->ia_valid & ATTR_SIZE) { + rc = ecryptfs_inode_newsize_ok(inode, ia->ia_size); + if (rc) + goto out; + rc = truncate_upper(dentry, ia, &lower_ia); if (rc < 0) goto out; From 081447ecfc255cb63b6e392cd01d9f684d4df5b8 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 8 Apr 2026 08:06:40 +0200 Subject: [PATCH 13/15] ecryptfs: merge ecryptfs_inode_newsize_ok into truncate_upper Both callers of ecryptfs_inode_newsize_ok call truncate_upper right after. Merge ecryptfs_inode_newsize_ok into truncate_upper to simplify the logic. Signed-off-by: Christoph Hellwig Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 52 +++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index ec6aae5af1f8..4ec3e76f0562 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -738,6 +738,23 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, return 0; } + crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; + lower_size_before_truncate = + upper_size_to_lower_size(crypt_stat, i_size); + lower_size_after_truncate = + upper_size_to_lower_size(crypt_stat, ia->ia_size); + if (lower_size_after_truncate > lower_size_before_truncate) { + /* + * The eCryptfs inode and the new *lower* size are mixed here + * because we may not have the lower i_mutex held and/or it may + * not be appropriate to call inode_newsize_ok() with inodes + * from other filesystems. + */ + rc = inode_newsize_ok(inode, lower_size_after_truncate); + if (rc) + return rc; + } + rc = ecryptfs_get_lower_file(dentry, inode); if (rc) return rc; @@ -756,7 +773,6 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, goto out; } - crypt_stat = &ecryptfs_inode_to_private(d_inode(dentry))->crypt_stat; if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { truncate_setsize(inode, ia->ia_size); lower_ia->ia_size = ia->ia_size; @@ -791,10 +807,6 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, * We are reducing the size of the ecryptfs file, and need to know if we * need to reduce the size of the lower file. */ - lower_size_before_truncate = - upper_size_to_lower_size(crypt_stat, i_size); - lower_size_after_truncate = - upper_size_to_lower_size(crypt_stat, ia->ia_size); if (lower_size_after_truncate < lower_size_before_truncate) { lower_ia->ia_size = lower_size_after_truncate; lower_ia->ia_valid |= ATTR_SIZE; @@ -806,28 +818,6 @@ out: return rc; } -static int ecryptfs_inode_newsize_ok(struct inode *inode, loff_t offset) -{ - struct ecryptfs_crypt_stat *crypt_stat; - loff_t lower_oldsize, lower_newsize; - - crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; - lower_oldsize = upper_size_to_lower_size(crypt_stat, - i_size_read(inode)); - lower_newsize = upper_size_to_lower_size(crypt_stat, offset); - if (lower_newsize > lower_oldsize) { - /* - * The eCryptfs inode and the new *lower* size are mixed here - * because we may not have the lower i_mutex held and/or it may - * not be appropriate to call inode_newsize_ok() with inodes - * from other filesystems. - */ - return inode_newsize_ok(inode, lower_newsize); - } - - return 0; -} - /** * ecryptfs_truncate * @dentry: The ecryptfs layer dentry @@ -844,10 +834,6 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) struct iattr lower_ia = { .ia_valid = 0 }; int rc; - rc = ecryptfs_inode_newsize_ok(d_inode(dentry), new_length); - if (rc) - return rc; - rc = truncate_upper(dentry, &ia, &lower_ia); if (!rc && lower_ia.ia_valid & ATTR_SIZE) { struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); @@ -939,10 +925,6 @@ static int ecryptfs_setattr(struct mnt_idmap *idmap, if (ia->ia_valid & ATTR_FILE) lower_ia.ia_file = ecryptfs_file_to_lower(ia->ia_file); if (ia->ia_valid & ATTR_SIZE) { - rc = ecryptfs_inode_newsize_ok(inode, ia->ia_size); - if (rc) - goto out; - rc = truncate_upper(dentry, ia, &lower_ia); if (rc < 0) goto out; From 5d1f0e8cd9482ddb5318f765f7ca508ce707cf83 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 8 Apr 2026 08:06:41 +0200 Subject: [PATCH 14/15] ecryptfs: factor out a ecryptfs_iattr_to_lower helper Prepare for using the code to create a lower struct iattr in multiple places. Signed-off-by: Christoph Hellwig Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 4ec3e76f0562..a06b84033ff3 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -677,6 +677,20 @@ static const char *ecryptfs_get_link(struct dentry *dentry, return buf; } +static void ecryptfs_iattr_to_lower(struct iattr *lower_ia, + const struct iattr *ia) +{ + memcpy(lower_ia, ia, sizeof(*lower_ia)); + if (ia->ia_valid & ATTR_FILE) + lower_ia->ia_file = ecryptfs_file_to_lower(ia->ia_file); + /* + * If the mode change is for clearing setuid/setgid bits, allow the lower + * file system to interpret this in its own way. + */ + if (lower_ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) + lower_ia->ia_valid &= ~ATTR_MODE; +} + /** * upper_size_to_lower_size * @crypt_stat: Crypt_stat associated with file @@ -921,21 +935,13 @@ static int ecryptfs_setattr(struct mnt_idmap *idmap, if (rc) goto out; - memcpy(&lower_ia, ia, sizeof(lower_ia)); - if (ia->ia_valid & ATTR_FILE) - lower_ia.ia_file = ecryptfs_file_to_lower(ia->ia_file); + ecryptfs_iattr_to_lower(&lower_ia, ia); if (ia->ia_valid & ATTR_SIZE) { rc = truncate_upper(dentry, ia, &lower_ia); if (rc < 0) goto out; } - /* - * mode change is for clearing setuid/setgid bits. Allow lower fs - * to interpret this in its own way. - */ - if (lower_ia.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) - lower_ia.ia_valid &= ~ATTR_MODE; inode_lock(d_inode(lower_dentry)); rc = notify_change(&nop_mnt_idmap, lower_dentry, &lower_ia, NULL); From e836ec1819b0cc50e0b45a53b0bdce6c596f0207 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 8 Apr 2026 08:06:42 +0200 Subject: [PATCH 15/15] ecryptfs: keep the lower iattr contained in truncate_upper Currently the two callers of truncate_upper handle passing information very differently. ecryptfs_truncate passes a zeroed lower_ia and expects truncate_upper to fill it in from the upper ia created just for that, while ecryptfs_setattr passes a fully initialized lower_ia copied from the upper one. Both of them then call notify_change on the lower_ia. Switch to only passing the upper ia, and derive the lower ia from it inside truncate_upper, and call notify_change inside the function itself. Because the old name is misleading now, rename the resulting function to __ecryptfs_truncate as it deals with both the lower and upper inodes. Signed-off-by: Christoph Hellwig Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 84 ++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 47 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index a06b84033ff3..546c1fe692c0 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -721,36 +721,33 @@ upper_size_to_lower_size(struct ecryptfs_crypt_stat *crypt_stat, } /** - * truncate_upper + * __ecryptfs_truncate * @dentry: The ecryptfs layer dentry * @ia: Address of the ecryptfs inode's attributes - * @lower_ia: Address of the lower inode's attributes * - * Function to handle truncations modifying the size of the file. Note - * that the file sizes are interpolated. When expanding, we are simply - * writing strings of 0's out. When truncating, we truncate the upper - * inode and update the lower_ia according to the page index - * interpolations. If ATTR_SIZE is set in lower_ia->ia_valid upon return, - * the caller must use lower_ia in a call to notify_change() to perform - * the truncation of the lower inode. + * Handle truncations modifying the size of the file. Note that the file sizes + * are interpolated. When expanding, we are simply writing strings of 0's out. + * When truncating, we truncate the upper inode and update the lower_ia + * according to the page index interpolations. * * Returns zero on success; non-zero otherwise */ -static int truncate_upper(struct dentry *dentry, struct iattr *ia, - struct iattr *lower_ia) +static int __ecryptfs_truncate(struct dentry *dentry, const struct iattr *ia) { + struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); struct inode *inode = d_inode(dentry); struct ecryptfs_crypt_stat *crypt_stat; loff_t i_size = i_size_read(inode); loff_t lower_size_before_truncate; loff_t lower_size_after_truncate; + struct iattr lower_ia; size_t num_zeros; int rc; - if (unlikely((ia->ia_size == i_size))) { - lower_ia->ia_valid &= ~ATTR_SIZE; + ecryptfs_iattr_to_lower(&lower_ia, ia); + + if (unlikely((ia->ia_size == i_size))) return 0; - } crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; lower_size_before_truncate = @@ -783,15 +780,13 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, * new end of the file. */ rc = ecryptfs_write(inode, zero, ia->ia_size - 1, 1); - lower_ia->ia_valid &= ~ATTR_SIZE; goto out; } if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { truncate_setsize(inode, ia->ia_size); - lower_ia->ia_size = ia->ia_size; - lower_ia->ia_valid |= ATTR_SIZE; - goto out; + lower_ia.ia_size = ia->ia_size; + goto set_size; } /* @@ -821,12 +816,15 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, * We are reducing the size of the ecryptfs file, and need to know if we * need to reduce the size of the lower file. */ - if (lower_size_after_truncate < lower_size_before_truncate) { - lower_ia->ia_size = lower_size_after_truncate; - lower_ia->ia_valid |= ATTR_SIZE; - } else { - lower_ia->ia_valid &= ~ATTR_SIZE; - } + if (lower_size_after_truncate >= lower_size_before_truncate) + goto out; + + lower_ia.ia_size = lower_size_after_truncate; +set_size: + lower_ia.ia_valid |= ATTR_SIZE; + inode_lock(d_inode(lower_dentry)); + rc = notify_change(&nop_mnt_idmap, lower_dentry, &lower_ia, NULL); + inode_unlock(d_inode(lower_dentry)); out: ecryptfs_put_lower_file(inode); return rc; @@ -844,20 +842,12 @@ out: */ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length) { - struct iattr ia = { .ia_valid = ATTR_SIZE, .ia_size = new_length }; - struct iattr lower_ia = { .ia_valid = 0 }; - int rc; + const struct iattr ia = { + .ia_valid = ATTR_SIZE, + .ia_size = new_length, + }; - rc = truncate_upper(dentry, &ia, &lower_ia); - if (!rc && lower_ia.ia_valid & ATTR_SIZE) { - struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); - - inode_lock(d_inode(lower_dentry)); - rc = notify_change(&nop_mnt_idmap, lower_dentry, - &lower_ia, NULL); - inode_unlock(d_inode(lower_dentry)); - } - return rc; + return __ecryptfs_truncate(dentry, &ia); } static int @@ -887,7 +877,6 @@ static int ecryptfs_setattr(struct mnt_idmap *idmap, struct inode *inode = d_inode(dentry); struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); struct inode *lower_inode = ecryptfs_inode_to_lower(inode); - struct iattr lower_ia; struct ecryptfs_crypt_stat *crypt_stat; int rc; @@ -935,17 +924,18 @@ static int ecryptfs_setattr(struct mnt_idmap *idmap, if (rc) goto out; - ecryptfs_iattr_to_lower(&lower_ia, ia); if (ia->ia_valid & ATTR_SIZE) { - rc = truncate_upper(dentry, ia, &lower_ia); - if (rc < 0) - goto out; + rc = __ecryptfs_truncate(dentry, ia); + } else { + struct iattr lower_ia; + + ecryptfs_iattr_to_lower(&lower_ia, ia); + + inode_lock(d_inode(lower_dentry)); + rc = notify_change(&nop_mnt_idmap, lower_dentry, &lower_ia, + NULL); + inode_unlock(d_inode(lower_dentry)); } - - - inode_lock(d_inode(lower_dentry)); - rc = notify_change(&nop_mnt_idmap, lower_dentry, &lower_ia, NULL); - inode_unlock(d_inode(lower_dentry)); out: fsstack_copy_attr_all(inode, lower_inode); return rc;