From nobody Mon Sep 28 17:49:54 2026 Received: from mail-pj1-f54.google.com (mail-pj1-f54.google.com [209.85.216.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 21F0A353A8B for ; Wed, 19 Aug 2026 13:14:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787145277; cv=none; b=NhwX0Z/bZ2pAVespuiLJLk/TkaYDQPmXeA6baou069/WYR3H+hJeWLwAsk3HGLK7eQuQCun94ZHETgWcTQSUz/DPyMYiKBCd1IAIqznUELwNJRacZyd3fIwWgo5rzdEhzQ94m3UvjhVIeuoeWRnOoghzltA+2I9BIT8Wz1IO9Bc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787145277; c=relaxed/simple; bh=ucoPs6SodoXw0vbdqTt3zLlFNoA03bpanPh2+JBIYxY=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=Qu4HJto7OFirYrbF+HbkAn343LsYjkpsaNfkhUqdvyXKDvgaDfPTrp08B1IjgPuQmqLJm+JbqDeUpahAWS8ktP2rfAOSe/aKakwA5kt9O+Kq9yjj2M6BNDyTbLr48yzS0maWizPzKnkgVz4qfRDCQOlxx3nwt0x4hsnSAnGdL74= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=tHvQvA0t; arc=none smtp.client-ip=209.85.216.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="tHvQvA0t" Received: by mail-pj1-f54.google.com with SMTP id 98e67ed59e1d1-39266382df6so781435a91.3 for ; Wed, 19 Aug 2026 06:14:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787145273; x=1787750073; 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=YkturzlV37zRTwxFEsUoMia3rueHM20fJQJwHe6NoXw=; b=tHvQvA0tGj1ejENNdOuJNU+XewF275kD8jVVvbHO00erdYBr5+zfzeVs16+0uZH4Os ucGiKfrf3FZjZEauafEpXh5wu7LRYNcqXp5D5SFP2F8CY5ys7lfoekMczEnBUnCw1aEG YxEufZ9T1DVAzht01txuTUswZlOJ7q0aZZsJvB4WlP9BX2G7xQOxY7JX25tbNOVIQvHq WBKzAk79Yr3yoH9yYkn0YN/fRPxSsUY3Pf2DVdnihll3X86S+w1sNcHOmtjO+GSYwvUE B0hA5B82QCAiOVRWsDlisaq/Hs0aYo2pe5osxbCF0b+NSDgo1R7jav1GVce9X5WFg1N4 v5zg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787145273; x=1787750073; 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=YkturzlV37zRTwxFEsUoMia3rueHM20fJQJwHe6NoXw=; b=CqBtUT8u3nVK8YRyKcX++M6iWZs3NSfTlUPIMTVCawa+g8SmkyZFiE4s9+q3JYMCAX 37jjPaRSB9GW3rQR4hCwsoS806VXJ89502UgMHpA9/NSOrC9Dxam8FVE5lFck1iJ0v8S fD+NoRs+Yjn+Ft9SXh30DwrAhkFvS+wbrf9bTyjLVxj2Xyb3faE2LWjHRNeP+GhaLy53 wSQozS8dmbOtGVhHp9dwb5OPNQnuVsNslj2E9MGxjgPqPaLfUAEVF7VNQNZFu/Ci9DLs A2/CAtyf3q9jD6eoHrPtiWlag+aIz9klrLiB5UJP4HuusSHLRk1ENxY15hwxylo2VsbH p0FQ== X-Forwarded-Encrypted: i=1; AHgh+RrohYgY/oopVt5W5qqbtSE8sDFs9YZ1NtuFMG5t/3yDO4AmLkBkYT3iO3oYGZOIN/CsV3ac5XHlvtFI+mE=@vger.kernel.org X-Gm-Message-State: AFuF++m1y1FGcyPt+jqBSJLJzIUCoNme+u33rLHZnooHwahyngVT7GHy gmL4DqXrdzOlof20LBYQ/n8NSBN8bHK3FEDbnua4ybxN86AxEkitYMhd X-Gm-Gg: AR+sD13qLiUdkg/Mb+VPvB0vQnhlgpm/GygopqlTTCFUdukd1S8aOlMQGpHIqtnU4dF 22+V0HKXQHTsIua2msDISX5TZUxM9Q6Aw6S0FXrhfmkhK9YoWZaCr/3AJvVWtz0I2atKpaxaLZF ouoFkOsctHxI7LTQrcVwCcYoYa7oIQkn0dQgggvrVw1o01IDJsDVhkFC9i56y0cdiw2UfKDnY8e r0pr0YvBlJ4+zwg518Kk7dqzviMjgc6sxJrm3siBL9GcKEow7cKhDwWWSL/91stT+VCWOl8S9op UPtjVf/HSmmJoHj+nHWeaO3qHwd1X3wtR2cu/+SDsk9pLpiURFZR5v7sLLUZSjMoeXWUeRa91/6 6zv7IkZyVCWR5JflYK8xZJ1/T6lxVJXN91e/+zFe873lkRE70CZbvhbeecq8QCoWRREXmYVWd23 WwQ3go61nBtjisOzQeoa+QmKt48VLMc4PJmOBCd7iUesux1sykrOV4/lD0ImKGBZLvskE8qMVEW /44OmWtKazJVPvTIyD0JsbTqopvWLLaENA= X-Received: by 2002:a17:90b:3cc5:b0:393:194d:5366 with SMTP id 98e67ed59e1d1-39580f3bcdemr9196929a91.10.1787145273058; Wed, 19 Aug 2026 06:14:33 -0700 (PDT) Received: from qiwenjie-ThinkCentre-M760t.mioffice.cn ([43.224.245.241]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3957fb8578asm2659651a91.11.2026.08.19.06.14.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 19 Aug 2026 06:14:32 -0700 (PDT) From: Wenjie Qi X-Google-Original-From: Wenjie Qi To: jaegeuk@kernel.org, chao@kernel.org Cc: viro@zeniv.linux.org.uk, linux-f2fs-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org, qiwenjie@xiaomi.com, qwjhust@gmail.com, stable@kernel.org Subject: [PATCH] f2fs: avoid instantiating failed symlinks Date: Wed, 19 Aug 2026 21:14:25 +0800 Message-ID: <20260819131425.189025-1-qiwenjie@xiaomi.com> X-Mailer: git-send-email 2.43.0 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" f2fs_add_link() initializes inode metadata needed by page_symlink(), so the directory entry is added before the symlink target is set up. If target setup fails, the current error path instantiates the dentry and calls f2fs_unlink() directly. This bypasses the dcache update performed by vfs_unlink() and leaves a hashed positive dentry attached to an unlinked inode. Factor the F2FS unlink work into an internal helper which takes the directory, name and inode, and use it while the creation dentry is still negative. Always unhash the dentry before releasing the inode so an entry left behind by a failed rollback remains discoverable. Return -ENOENT explicitly when f2fs_find_entry() cannot find the directory entry. Successful quota initialization otherwise leaves err set to zero and makes the helper report a successful deletion. Fixes: a6be014e1d28 ("f2fs: fix error path of ->symlink") Cc: stable@kernel.org Reported-by: Al Viro Link: https://lore.kernel.org/linux-f2fs-devel/20260815051820.GA660827@ZenI= V/ Signed-off-by: Wenjie Qi --- fs/f2fs/namei.c | 83 ++++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 784f63624..5438d8ffc 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -560,38 +560,31 @@ static struct dentry *f2fs_lookup(struct inode *dir, = struct dentry *dentry, return ERR_PTR(err); } =20 -static int f2fs_unlink(struct inode *dir, struct dentry *dentry) +static int f2fs_unlink_inode(struct inode *dir, const struct qstr *name, + struct inode *inode) { struct f2fs_sb_info *sbi =3D F2FS_I_SB(dir); - struct inode *inode =3D d_inode(dentry); struct f2fs_dir_entry *de; struct f2fs_lock_context lc; struct folio *folio; int err; =20 - trace_f2fs_unlink_enter(dir, dentry); - if (IS_DEVICE_ALIASING(inode)) return -EPERM; =20 - if (unlikely(f2fs_cp_error(sbi))) { - err =3D -EIO; - goto out; - } + if (unlikely(f2fs_cp_error(sbi))) + return -EIO; =20 err =3D f2fs_dquot_initialize(dir); if (err) - goto out; + return err; err =3D f2fs_dquot_initialize(inode); if (err) - goto out; + return err; =20 - de =3D f2fs_find_entry(dir, &dentry->d_name, &folio); - if (!de) { - if (IS_ERR(folio)) - err =3D PTR_ERR(folio); - goto out; - } + de =3D f2fs_find_entry(dir, name, &folio); + if (!de) + return IS_ERR(folio) ? PTR_ERR(folio) : -ENOENT; =20 if (unlikely(inode->i_nlink =3D=3D 0)) { f2fs_warn(sbi, "%s: inode (ino=3D%llx) has zero i_nlink", @@ -610,29 +603,40 @@ static int f2fs_unlink(struct inode *dir, struct dent= ry *dentry) if (err) { f2fs_unlock_op(sbi, &lc); f2fs_folio_put(folio, false); - goto out; + return err; } f2fs_delete_entry(de, folio, dir, inode); f2fs_unlock_op(sbi, &lc); =20 + return 0; + +corrupted: + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_folio_put(folio, false); + return -EFSCORRUPTED; +} + +static int f2fs_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode =3D d_inode(dentry); + int err; + + trace_f2fs_unlink_enter(dir, dentry); + + err =3D f2fs_unlink_inode(dir, &dentry->d_name, inode); + /* VFS negative dentries are incompatible with Encoding and * Case-insensitiveness. Eventually we'll want avoid * invalidating the dentries here, alongside with returning the * negative dentries at f2fs_lookup(), when it is better * supported by the VFS for the CI case. */ - if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir)) + if (!err && IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir)) d_invalidate(dentry); =20 - if (IS_DIRSYNC(dir)) - f2fs_sync_fs(sbi->sb, 1); + if (!err && IS_DIRSYNC(dir)) + f2fs_sync_fs(F2FS_I_SB(dir)->sb, 1); =20 - goto out; -corrupted: - err =3D -EFSCORRUPTED; - set_sbi_flag(sbi, SBI_NEED_FSCK); - f2fs_folio_put(folio, false); -out: trace_f2fs_unlink_exit(inode, err); return err; } @@ -660,7 +664,7 @@ static int f2fs_symlink(struct mnt_idmap *idmap, struct= inode *dir, struct inode *inode; size_t len =3D strlen(symname); struct fscrypt_str disk_link; - int err; + int err, ret; =20 if (unlikely(f2fs_cp_error(sbi))) return -EIO; @@ -701,6 +705,19 @@ static int f2fs_symlink(struct mnt_idmap *idmap, struc= t inode *dir, err =3D page_symlink(inode, disk_link.name, disk_link.len); =20 err_out: + if (err) { + ret =3D f2fs_unlink_inode(dir, &dentry->d_name, inode); + if (ret) + f2fs_warn(sbi, "failed to rollback symlink inode %llu, err: %d", + inode->i_ino, ret); + d_drop(dentry); + unlock_new_inode(inode); + if (!ret && IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); + iput(inode); + goto out_balance; + } + d_instantiate_new(dentry, inode); =20 /* @@ -712,16 +729,12 @@ static int f2fs_symlink(struct mnt_idmap *idmap, stru= ct inode *dir, * If the symlink path is stored into inline_data, there is no * performance regression. */ - if (!err) { - filemap_write_and_wait_range(inode->i_mapping, 0, - disk_link.len - 1); + filemap_write_and_wait_range(inode->i_mapping, 0, disk_link.len - 1); =20 - if (IS_DIRSYNC(dir)) - f2fs_sync_fs(sbi->sb, 1); - } else { - f2fs_unlink(dir, dentry); - } + if (IS_DIRSYNC(dir)) + f2fs_sync_fs(sbi->sb, 1); =20 +out_balance: f2fs_balance_fs(sbi, true); goto out_free_encrypted_link; =20 --=20 2.43.0