summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2019-02-13 14:15:17 -0500
committerDarrick J. Wong <darrick.wong@oracle.com>2019-02-15 01:42:57 -0500
commitc4a6bf7f6cc7eb4cce120fb7eb1e1fb8b2d65e09 (patch)
treedff307aa199ea8d0c4169b16413e9363810c1ea9
parent15a268d9f263ed3a0601a1296568241a5a3da7aa (diff)
xfs: don't ever put nlink > 0 inodes on the unlinked list
When XFS creates an O_TMPFILE file, the inode is created with nlink = 1, put on the unlinked list, and then the VFS sets nlink = 0 in d_tmpfile. If we crash before anything logs the inode (it's dirty incore but the vfs doesn't tell us it's dirty so we never log that change), the iunlink processing part of recovery will then explode with a pile of: XFS: Assertion failed: VFS_I(ip)->i_nlink == 0, file: fs/xfs/xfs_log_recover.c, line: 5072 Worse yet, since nlink is nonzero, the inodes also don't get cleaned up and they just leak until the next xfs_repair run. Therefore, change xfs_iunlink to require that inodes being put on the unlinked list have nlink == 0, change the tmpfile callers to instantiate nodes that way, and set the nlink to 1 just prior to calling d_tmpfile. Fix the comment for xfs_iunlink while we're at it. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
-rw-r--r--fs/xfs/xfs_inode.c16
-rw-r--r--fs/xfs/xfs_iops.c13
2 files changed, 17 insertions, 12 deletions
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index d1411c168700..f643a9295179 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1332,7 +1332,7 @@ xfs_create_tmpfile(
1332 if (error) 1332 if (error)
1333 goto out_trans_cancel; 1333 goto out_trans_cancel;
1334 1334
1335 error = xfs_dir_ialloc(&tp, dp, mode, 1, 0, prid, &ip); 1335 error = xfs_dir_ialloc(&tp, dp, mode, 0, 0, prid, &ip);
1336 if (error) 1336 if (error)
1337 goto out_trans_cancel; 1337 goto out_trans_cancel;
1338 1338
@@ -2231,11 +2231,8 @@ out:
2231} 2231}
2232 2232
2233/* 2233/*
2234 * This is called when the inode's link count goes to 0 or we are creating a 2234 * This is called when the inode's link count has gone to 0 or we are creating
2235 * tmpfile via O_TMPFILE. In the case of a tmpfile, @ignore_linkcount will be 2235 * a tmpfile via O_TMPFILE. The inode @ip must have nlink == 0.
2236 * set to true as the link count is dropped to zero by the VFS after we've
2237 * created the file successfully, so we have to add it to the unlinked list
2238 * while the link count is non-zero.
2239 * 2236 *
2240 * We place the on-disk inode on a list in the AGI. It will be pulled from this 2237 * We place the on-disk inode on a list in the AGI. It will be pulled from this
2241 * list when the inode is freed. 2238 * list when the inode is freed.
@@ -2254,6 +2251,7 @@ xfs_iunlink(
2254 short bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS; 2251 short bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS;
2255 int error; 2252 int error;
2256 2253
2254 ASSERT(VFS_I(ip)->i_nlink == 0);
2257 ASSERT(VFS_I(ip)->i_mode != 0); 2255 ASSERT(VFS_I(ip)->i_mode != 0);
2258 trace_xfs_iunlink(ip); 2256 trace_xfs_iunlink(ip);
2259 2257
@@ -3184,11 +3182,9 @@ xfs_rename_alloc_whiteout(
3184 3182
3185 /* 3183 /*
3186 * Prepare the tmpfile inode as if it were created through the VFS. 3184 * Prepare the tmpfile inode as if it were created through the VFS.
3187 * Otherwise, the link increment paths will complain about nlink 0->1. 3185 * Complete the inode setup and flag it as linkable. nlink is already
3188 * Drop the link count as done by d_tmpfile(), complete the inode setup 3186 * zero, so we can skip the drop_nlink.
3189 * and flag it as linkable.
3190 */ 3187 */
3191 drop_nlink(VFS_I(tmpfile));
3192 xfs_setup_iops(tmpfile); 3188 xfs_setup_iops(tmpfile);
3193 xfs_finish_inode_setup(tmpfile); 3189 xfs_finish_inode_setup(tmpfile);
3194 VFS_I(tmpfile)->i_state |= I_LINKABLE; 3190 VFS_I(tmpfile)->i_state |= I_LINKABLE;
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index f48ffd7a8d3e..1efef69a7f1c 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -191,9 +191,18 @@ xfs_generic_create(
191 191
192 xfs_setup_iops(ip); 192 xfs_setup_iops(ip);
193 193
194 if (tmpfile) 194 if (tmpfile) {
195 /*
196 * The VFS requires that any inode fed to d_tmpfile must have
197 * nlink == 1 so that it can decrement the nlink in d_tmpfile.
198 * However, we created the temp file with nlink == 0 because
199 * we're not allowed to put an inode with nlink > 0 on the
200 * unlinked list. Therefore we have to set nlink to 1 so that
201 * d_tmpfile can immediately set it back to zero.
202 */
203 set_nlink(inode, 1);
195 d_tmpfile(dentry, inode); 204 d_tmpfile(dentry, inode);
196 else 205 } else
197 d_instantiate(dentry, inode); 206 d_instantiate(dentry, inode);
198 207
199 xfs_finish_inode_setup(ip); 208 xfs_finish_inode_setup(ip);