Skip to content

Commit 1ec9307

Browse files
Darrick J. WongChandan Babu R
authored andcommitted
xfs: allow unlinked symlinks and dirs with zero size
For a very very long time, inode inactivation has set the inode size to zero before unmapping the extents associated with the data fork. Unfortunately, commit 3c6f46e changed the inode verifier to prohibit zero-length symlinks and directories. If an inode happens to get logged in this state and the system crashes before freeing the inode, log recovery will also fail on the broken inode. Therefore, allow zero-size symlinks and directories as long as the link count is zero; nobody will be able to open these files by handle so there isn't any risk of data exposure. Fixes: 3c6f46e ("xfs: sanity check directory inode di_size") Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Signed-off-by: Chandan Babu R <[email protected]>
1 parent 288e1f6 commit 1ec9307

File tree

1 file changed

+18
-5
lines changed

1 file changed

+18
-5
lines changed

fs/xfs/libxfs/xfs_inode_buf.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,13 @@ xfs_dinode_verify_fork(
379379
/*
380380
* A directory small enough to fit in the inode must be stored
381381
* in local format. The directory sf <-> extents conversion
382-
* code updates the directory size accordingly.
382+
* code updates the directory size accordingly. Directories
383+
* being truncated have zero size and are not subject to this
384+
* check.
383385
*/
384386
if (S_ISDIR(mode)) {
385-
if (be64_to_cpu(dip->di_size) <= fork_size &&
387+
if (dip->di_size &&
388+
be64_to_cpu(dip->di_size) <= fork_size &&
386389
fork_format != XFS_DINODE_FMT_LOCAL)
387390
return __this_address;
388391
}
@@ -528,9 +531,19 @@ xfs_dinode_verify(
528531
if (mode && xfs_mode_to_ftype(mode) == XFS_DIR3_FT_UNKNOWN)
529532
return __this_address;
530533

531-
/* No zero-length symlinks/dirs. */
532-
if ((S_ISLNK(mode) || S_ISDIR(mode)) && di_size == 0)
533-
return __this_address;
534+
/*
535+
* No zero-length symlinks/dirs unless they're unlinked and hence being
536+
* inactivated.
537+
*/
538+
if ((S_ISLNK(mode) || S_ISDIR(mode)) && di_size == 0) {
539+
if (dip->di_version > 1) {
540+
if (dip->di_nlink)
541+
return __this_address;
542+
} else {
543+
if (dip->di_onlink)
544+
return __this_address;
545+
}
546+
}
534547

535548
fa = xfs_dinode_verify_nrext64(mp, dip);
536549
if (fa)

0 commit comments

Comments
 (0)