[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20251226173935.15695-1-almaz.alexandrovich@paragon-software.com>
Date: Fri, 26 Dec 2025 18:39:35 +0100
From: Konstantin Komarov <almaz.alexandrovich@...agon-software.com>
To: <ntfs3@...ts.linux.dev>
CC: <linux-kernel@...r.kernel.org>, <linux-fsdevel@...r.kernel.org>,
Konstantin Komarov <almaz.alexandrovich@...agon-software.com>
Subject: [PATCH] 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@...agon-software.com>
---
fs/ntfs3/dir.c | 2 +-
fs/ntfs3/file.c | 30 ++++++++++++++++++++++++---
fs/ntfs3/frecord.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++
fs/ntfs3/ntfs_fs.h | 2 ++
4 files changed, 81 insertions(+), 4 deletions(-)
diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c
index 24cb64d5521a..001773b4514b 100644
--- a/fs/ntfs3/dir.c
+++ b/fs/ntfs3/dir.c
@@ -668,7 +668,7 @@ const struct file_operations ntfs_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate_shared = ntfs_readdir,
- .fsync = generic_file_fsync,
+ .fsync = ntfs_file_fsync,
.open = ntfs_file_open,
.unlocked_ioctl = ntfs_ioctl,
#ifdef CONFIG_COMPAT
diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index 732260087066..b48cdd77efae 100644
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@ -1443,13 +1443,37 @@ static ssize_t ntfs_file_splice_write(struct pipe_inode_info *pipe,
/*
* ntfs_file_fsync - file_operations::fsync
*/
-static int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
struct inode *inode = file_inode(file);
- if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
+ struct super_block *sb = inode->i_sb;
+ struct ntfs_sb_info *sbi = sb->s_fs_info;
+ int err, ret;
+
+ if (unlikely(ntfs3_forced_shutdown(sb)))
return -EIO;
- return generic_file_fsync(file, start, end, datasync);
+ ret = file_write_and_wait_range(file, start, end);
+ if (ret)
+ return ret;
+
+ ret = write_inode_now(inode, !datasync);
+
+ if (!ret) {
+ ret = ni_write_parents(ntfs_i(inode), !datasync);
+ }
+
+ if (!ret) {
+ ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
+ ntfs_update_mftmirr(sbi, false);
+ }
+
+ err = sync_blockdev(sb->s_bdev);
+ if (unlikely(err && !ret))
+ ret = err;
+ if (!ret)
+ blkdev_issue_flush(sb->s_bdev);
+ return ret;
}
// clang-format off
diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
index 7e3d61de2f8f..a123e3f0acde 100644
--- a/fs/ntfs3/frecord.c
+++ b/fs/ntfs3/frecord.c
@@ -3001,6 +3001,57 @@ bool ni_is_dirty(struct inode *inode)
return false;
}
+/*
+ * ni_write_parents
+ *
+ * Helper function for ntfs_file_fsync.
+ */
+int ni_write_parents(struct ntfs_inode *ni, int sync)
+{
+ int err = 0;
+ struct ATTRIB *attr = NULL;
+ struct ATTR_LIST_ENTRY *le = NULL;
+ struct ntfs_sb_info *sbi = ni->mi.sbi;
+ struct super_block *sb = sbi->sb;
+
+ while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL,
+ NULL))) {
+ struct inode *dir;
+ struct ATTR_FILE_NAME *fname;
+
+ fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
+ if (!fname)
+ continue;
+
+ /* Check simple case when parent inode equals current inode. */
+ if (ino_get(&fname->home) == ni->vfs_inode.i_ino) {
+ if (MFT_REC_ROOT != ni->vfs_inode.i_ino) {
+ ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
+ err = -EINVAL;
+ }
+ continue;
+ }
+
+ dir = ntfs_iget5(sb, &fname->home, NULL);
+ if (IS_ERR(dir)) {
+ ntfs_inode_warn(
+ &ni->vfs_inode,
+ "failed to open parent directory r=%lx to write",
+ (long)ino_get(&fname->home));
+ continue;
+ }
+
+ if (!is_bad_inode(dir)) {
+ int err2 = write_inode_now(dir, sync);
+ if (!err)
+ err = err2;
+ }
+ iput(dir);
+ }
+
+ return err;
+}
+
/*
* ni_update_parent
*
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index cee7b73b9670..482722438bd9 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -512,6 +512,7 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
int ntfs_file_open(struct inode *inode, struct file *file);
int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len);
+int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg);
long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg);
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,
struct NTFS_DE *new_de);
bool ni_is_dirty(struct inode *inode);
+int ni_write_parents(struct ntfs_inode *ni, int sync);
/* Globals from fslog.c */
bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes);
--
2.43.0
Powered by blists - more mailing lists