Skip to content

Commit dcd9d6a

Browse files
fs/ntfs3: fsync files by syncing parent inodes
Some xfstests expect fsync() on a file or directory to also persist directory metadata up the parent chain. Using generic_file_fsync() is not sufficient for ntfs, because parent directories are not explicitly written out. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
1 parent f7edab0 commit dcd9d6a

4 files changed

Lines changed: 81 additions & 4 deletions

File tree

fs/ntfs3/dir.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ const struct file_operations ntfs_dir_operations = {
668668
.llseek = generic_file_llseek,
669669
.read = generic_read_dir,
670670
.iterate_shared = ntfs_readdir,
671-
.fsync = generic_file_fsync,
671+
.fsync = ntfs_file_fsync,
672672
.open = ntfs_file_open,
673673
.unlocked_ioctl = ntfs_ioctl,
674674
#ifdef CONFIG_COMPAT

fs/ntfs3/file.c

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,13 +1443,37 @@ static ssize_t ntfs_file_splice_write(struct pipe_inode_info *pipe,
14431443
/*
14441444
* ntfs_file_fsync - file_operations::fsync
14451445
*/
1446-
static int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
1446+
int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
14471447
{
14481448
struct inode *inode = file_inode(file);
1449-
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
1449+
struct super_block *sb = inode->i_sb;
1450+
struct ntfs_sb_info *sbi = sb->s_fs_info;
1451+
int err, ret;
1452+
1453+
if (unlikely(ntfs3_forced_shutdown(sb)))
14501454
return -EIO;
14511455

1452-
return generic_file_fsync(file, start, end, datasync);
1456+
ret = file_write_and_wait_range(file, start, end);
1457+
if (ret)
1458+
return ret;
1459+
1460+
ret = write_inode_now(inode, !datasync);
1461+
1462+
if (!ret) {
1463+
ret = ni_write_parents(ntfs_i(inode), !datasync);
1464+
}
1465+
1466+
if (!ret) {
1467+
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
1468+
ntfs_update_mftmirr(sbi, false);
1469+
}
1470+
1471+
err = sync_blockdev(sb->s_bdev);
1472+
if (unlikely(err && !ret))
1473+
ret = err;
1474+
if (!ret)
1475+
blkdev_issue_flush(sb->s_bdev);
1476+
return ret;
14531477
}
14541478

14551479
// clang-format off

fs/ntfs3/frecord.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3001,6 +3001,57 @@ bool ni_is_dirty(struct inode *inode)
30013001
return false;
30023002
}
30033003

3004+
/*
3005+
* ni_write_parents
3006+
*
3007+
* Helper function for ntfs_file_fsync.
3008+
*/
3009+
int ni_write_parents(struct ntfs_inode *ni, int sync)
3010+
{
3011+
int err = 0;
3012+
struct ATTRIB *attr = NULL;
3013+
struct ATTR_LIST_ENTRY *le = NULL;
3014+
struct ntfs_sb_info *sbi = ni->mi.sbi;
3015+
struct super_block *sb = sbi->sb;
3016+
3017+
while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL,
3018+
NULL))) {
3019+
struct inode *dir;
3020+
struct ATTR_FILE_NAME *fname;
3021+
3022+
fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
3023+
if (!fname)
3024+
continue;
3025+
3026+
/* Check simple case when parent inode equals current inode. */
3027+
if (ino_get(&fname->home) == ni->vfs_inode.i_ino) {
3028+
if (MFT_REC_ROOT != ni->vfs_inode.i_ino) {
3029+
ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
3030+
err = -EINVAL;
3031+
}
3032+
continue;
3033+
}
3034+
3035+
dir = ntfs_iget5(sb, &fname->home, NULL);
3036+
if (IS_ERR(dir)) {
3037+
ntfs_inode_warn(
3038+
&ni->vfs_inode,
3039+
"failed to open parent directory r=%lx to write",
3040+
(long)ino_get(&fname->home));
3041+
continue;
3042+
}
3043+
3044+
if (!is_bad_inode(dir)) {
3045+
int err2 = write_inode_now(dir, sync);
3046+
if (!err)
3047+
err = err2;
3048+
}
3049+
iput(dir);
3050+
}
3051+
3052+
return err;
3053+
}
3054+
30043055
/*
30053056
* ni_update_parent
30063057
*

fs/ntfs3/ntfs_fs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
512512
int ntfs_file_open(struct inode *inode, struct file *file);
513513
int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
514514
__u64 start, __u64 len);
515+
int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
515516
long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg);
516517
long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg);
517518
extern const struct inode_operations ntfs_special_inode_operations;
@@ -590,6 +591,7 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
590591
struct NTFS_DE *new_de);
591592

592593
bool ni_is_dirty(struct inode *inode);
594+
int ni_write_parents(struct ntfs_inode *ni, int sync);
593595

594596
/* Globals from fslog.c */
595597
bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes);

0 commit comments

Comments
 (0)