From nobody Fri Oct 23 03:31:33 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 708773A6F11 for ; Thu, 15 Oct 2026 17:46:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792086401; cv=none; b=R1eyONElACns6tTOUq7LCCMoVgBCRw2ikFRY5ZLxs9Rzec3CcIKTYGJsALPsxqepC/9lhlEJpmgeoJezs5L3BM9t01BHUtv0iYQPE4dXM71Bhda6UeUtxm/g7STlNG8d8elL7anroJLF8iK8Ql9vXUb21WaMSOo11L2i+qZRnkQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1792086401; c=relaxed/simple; bh=3ucozJp1Vb41Q178Z0Uce4IWui2IT+J8Bb+v709B0LE=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=HdKM9eTxTFCnIZl8SFcLTkwUfL5TdbuSzYOcFFMK1dq6okYRiypCJ1yHCb7U9CGV4VjxCbTugVt3re0vHP/dMWonZc+IimbGFxR3ivFHEjUxv2jTMdajAyD9AUKHtkUiLoR3tGMCFxSfGWWSRva3+cNAPT1s9uFSdLkDpOuOfJc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=FGT1PNXB; dkim=pass (2048-bit key) header.d=redhat.com header.i=@redhat.com header.b=fn0/0OMx; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="FGT1PNXB"; dkim=pass (2048-bit key) header.d=redhat.com header.i=@redhat.com header.b="fn0/0OMx" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1792086393; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=LQyA1+wc/ZZYzITRZkdnU2lflpO2io/1YEZU0uiNRh8=; b=FGT1PNXBf7ZBg/r1WSU3ivnVoTcsO7bx+Zq1ckBTst89Y4FopPMbE/hMnUnLO4VGjS6YVV +jHpWSW7vfKwdo31NWwigsAv44Ev2n1cvNZ6FspQ6Z0xkJF3YVPPPPtoT4eZWgNqhu+ug0 HeU9qy1ApfYfQPQEqdafWNpsrVNuobw= Received: from mail-dl1-f71.google.com (mail-dl1-f71.google.com [74.125.82.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-194-R9nj1-DQPC6-1IynOwOc1w-1; Thu, 15 Oct 2026 17:46:31 +0000 X-MC-Unique: R9nj1-DQPC6-1IynOwOc1w-1 X-Mimecast-MFC-AGG-ID: R9nj1-DQPC6-1IynOwOc1w_1792086390 Received: by mail-dl1-f71.google.com with SMTP id a92af1059eb24-151ba226e1bso6754045c88.0 for ; Thu, 15 Oct 2026 10:46:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1792086390; x=1792691190; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=LQyA1+wc/ZZYzITRZkdnU2lflpO2io/1YEZU0uiNRh8=; b=fn0/0OMx0r9SoQ7utO37ulxRK0wjSNj4krFE7fx8RSd1kXghp9nbCZBrGLWbj6/X/M SSDdFdXhqMz83Tv8OKAHOV0F+dnidacdDAJPD/IsHzqsnIIn1MfKqKnk0bxD6Xq+Pfda ik1ljt/KOWtD1kjKLYiQDu3O94nKoD4okE5FiJsTbDbrW9fFiUyEdznPIRfDR7Qjrx/j pSFsImU1dom+KyQQT6V45fLmNygKM5WNLbosUZy3KnSOedDofN7GsFtoy4pPd+D3H87d NYBniJcdveNY9eiFHqcEswbH4zNWUv1SXHvXHpvk1HlvaD0ScivupTK36wXPukP+9KsW zo3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1792086390; x=1792691190; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=LQyA1+wc/ZZYzITRZkdnU2lflpO2io/1YEZU0uiNRh8=; b=PeMIkLKeH6zlCtWlLMWr075Bdaw/UJQQ6J/d3FiqHKXzYpvg6qj+UFw3OiHc3VTl5t OZqePUC3Gah83ACOMtX5HavUtlFQ8p0OyMDtA8v6c5Z8jqjtfCqixSDi7zEggjz6xURa ztlq4VYIl1dn6PMGIWV2OFlHZc5vFWrmXNt2ByAeDShodHOLgRfGvRhe/NOBTPby3eeI Q8VD/QxqNpVbMtc17lmBhOhN5QxvAubbr19SfoO1Yy04Uf/yj1y3TAws13L465Mg9uAw zRCM9S+GIj9d+XJO7V/Wc/+yX60Bt7m5o7rY+3WeZVCwCwXOyFi/tr/3i0nCh9JN+CQn Pfrg== X-Forwarded-Encrypted: i=1; AKwUvBw7HuQ9yj13R1QNLJevhW2LyHu8LxDIyc56t7sRgE4WzOonGnRkGaBOaSCmptJy9f+ifLxCrbFYSYVDpLM=@vger.kernel.org X-Gm-Message-State: AFq9FYL3uuRVeKov8AoBljfajoEuxnr9CXXAvajOW+Oa8COhUssPcPIU eJ+fp2eB4M1M0DykkH5hrdQ5UZaraGGCQv8pDa6eowfbmPF1sFLp5kJ3V571xL4XD04cRRN1+1v SOBTdVzpBeNvab3w/cwSlHUrxqZNqOCMN3lbuOqs9mq65JNeV+6HIMJ5rKfpduCVOBA== X-Gm-Gg: AYBFou1pjd+mm8+gUPl4HCl2SQOTh+gWwf/UYjl7OmsXIL22JUI18JDB8cLv2CkiKiI yjAKMfbSCeao7Wa8szb4A4WK03bQWWWreIDF952sdCZj1RicrHfjGwAsnuXu3uYRWGzE8KIlW3k /cZT3K8bde7t6fxC9e5Dy6rw7ip101dhFqAUESr6BtF/XtaTrU8Q0+EpbOqsWNmDKhspjQP1pD3 GwnMDxsON0SY77H+IVdA1/MDs8ZRXcEOGaqq2Cfbpv5lxPUZJWnp7N/yytkQWK7Y9oxPkxy/rPz 2BEC2/K9/BUF+bN2tqgAbOiMAOEFm3njF2dp18nRAswSWI/ubQ/8EQWgbROehjc4NtAwA9oPjDF oTAUSs833ZUcKfWCFJu2BRsXhhIHPcbTvVSwJ0lzwLTKyouZV6nVS5tGgypXKU0GVzXMD7Xb6+m jA/WcoI7BpnfKoHydUidxTcuv0n7MUlY79RSLl9JBUT++leMjolmn2LyR5yWanIzCkjH0H/QV56 vI= X-Received: by 2002:a05:7022:b0d4:b0:15b:7f7a:730d with SMTP id a92af1059eb24-16a603445b0mr27652552c88.28.1792086390204; Thu, 15 Oct 2026 10:46:30 -0700 (PDT) X-Received: by 2002:a05:7022:b0d4:b0:15b:7f7a:730d with SMTP id a92af1059eb24-16a603445b0mr27652495c88.28.1792086389268; Thu, 15 Oct 2026 10:46:29 -0700 (PDT) Received: from cluster.. (4f.55.790d.ip4.static.sl-reverse.com. [13.121.85.79]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-182a164a7f3sm452719c88.1.2026.10.15.10.46.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 15 Oct 2026 10:46:28 -0700 (PDT) From: Alex Markuze To: linux-fscrypt@vger.kernel.org Cc: Alex Markuze , Eric Biggers , "Theodore Y. Ts'o" , Jaegeuk Kim , Ilya Dryomov , Xiubo Li , Viacheslav Dubeyko , ceph-devel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH] fscrypt: allow filesystems to disable symlink target caching Date: Thu, 15 Oct 2026 17:46:24 +0000 Message-Id: X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" fscrypt stores decrypted symlink targets in inode->i_link. The VFS can use this field without calling the filesystem's ->get_link() callback. The target can remain valid after the access policy changes. The CephFS subvolume quarantine series adds access checks to ->get_link(). Quarantine can revoke access after a target is cached. The VFS cache can bypass these checks. The no_symlink_cache option in fscrypt_operations gives filesystems control of this cache. It applies during target creation, lookup and getattr. With this option set, the helpers do not read or update ->i_link. The filesystem callback can then revalidate access. Uncached targets use the existing delayed-free callback. Each uncached lookup allocates a temporary buffer and decrypts or encodes the target. This lets Ceph reuse fscrypt's target validation and buffer ownership. The default setting keeps the current cache behavior for other filesystems. Ceph selects the option so cached targets cannot bypass its ->get_link() checks. Signed-off-by: Alex Markuze --- fs/ceph/crypto.c | 1 + fs/crypto/hooks.c | 37 ++++++++++++++++++++++--------------- include/linux/fscrypt.h | 7 +++++++ 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c index 9e0ebb071840..de786a0e626c 100644 --- a/fs/ceph/crypto.c +++ b/fs/ceph/crypto.c @@ -92,6 +92,7 @@ static struct fscrypt_operations ceph_fscrypt_ops =3D { .inode_info_offs =3D (int)offsetof(struct ceph_inode_info, i_crypt_info) - (int)offsetof(struct ceph_inode_info, netfs.inode), .needs_bounce_pages =3D 1, + .no_symlink_cache =3D 1, .get_context =3D ceph_crypt_get_context, .set_context =3D ceph_crypt_set_context, .get_dummy_policy =3D ceph_get_dummy_policy, diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index a7a8a3f581a0..e1eb0fce7948 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -321,11 +321,13 @@ int __fscrypt_encrypt_symlink(struct inode *inode, co= nst char *target, */ sd->encrypted_path[ciphertext_len] =3D '\0'; =20 - /* Cache the plaintext symlink target for later use by get_link() */ - err =3D -ENOMEM; - inode->i_link =3D kmemdup(target, len + 1, GFP_NOFS); - if (!inode->i_link) - goto err_free_sd; + /* Cache the plaintext symlink target if the filesystem allows it. */ + if (!inode->i_sb->s_cop->no_symlink_cache) { + err =3D -ENOMEM; + inode->i_link =3D kmemdup(target, len + 1, GFP_NOFS); + if (!inode->i_link) + goto err_free_sd; + } =20 if (!disk_link->name) disk_link->name =3D (unsigned char *)sd; @@ -358,17 +360,21 @@ const char *fscrypt_get_symlink(struct inode *inode, = const void *caddr, { const struct fscrypt_symlink_data *sd; struct fscrypt_str cstr, pstr; - bool has_key; + bool has_key, cache; int err; =20 /* This is for encrypted symlinks only */ if (WARN_ON_ONCE(!IS_ENCRYPTED(inode))) return ERR_PTR(-EINVAL); =20 + cache =3D !inode->i_sb->s_cop->no_symlink_cache; + /* If the decrypted target is already cached, just return it. */ - pstr.name =3D READ_ONCE(inode->i_link); - if (pstr.name) - return pstr.name; + if (cache) { + pstr.name =3D READ_ONCE(inode->i_link); + if (pstr.name) + return pstr.name; + } =20 /* * Try to set up the symlink's encryption key, but we can continue @@ -411,12 +417,12 @@ const char *fscrypt_get_symlink(struct inode *inode, = const void *caddr, pstr.name[pstr.len] =3D '\0'; =20 /* - * Cache decrypted symlink targets in i_link for later use. Don't cache - * symlink targets encoded without the key, since those become outdated - * once the key is added. This pairs with the READ_ONCE() above and in - * the VFS path lookup code. + * Cache decrypted symlink targets in i_link if the filesystem allows it. + * Don't cache symlink targets encoded without the key, since those + * become outdated once the key is added. This pairs with the + * READ_ONCE() above and in the VFS path lookup code. */ - if (!has_key || + if (!cache || !has_key || cmpxchg_release(&inode->i_link, NULL, pstr.name) !=3D NULL) set_delayed_call(done, kfree_link, pstr.name); =20 @@ -460,7 +466,8 @@ int fscrypt_symlink_getattr(const struct path *path, st= ruct kstat *stat) * decrypted target or the no-key encoded target), we can just get it in * the same way the VFS does during path resolution and readlink(). */ - link =3D READ_ONCE(inode->i_link); + link =3D inode->i_sb->s_cop->no_symlink_cache ? + NULL : READ_ONCE(inode->i_link); if (!link) { link =3D inode->i_op->get_link(dentry, inode, &done); if (IS_ERR(link)) diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 28f2108e29f3..faeff5812885 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -107,6 +107,13 @@ struct fscrypt_operations { */ unsigned int supports_subblock_data_units : 1; =20 + /* + * If set, don't cache decrypted symlink targets in ->i_link. This + * ensures that the VFS calls ->get_link() for every access, allowing + * the filesystem to revalidate the symlink before returning its target. + */ + unsigned int no_symlink_cache : 1; + /* * This field exists only for backwards compatibility reasons and should * only be set by the filesystems that are setting it already. It base-commit: ed0502cb11f84fff29f374238e526ae94d9bdfde --=20 2.34.1