mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v1 0/5] ntfs: support O_TMPFILE
@ 2026-10-27  9:35 Baolin Liu
  2026-10-27  9:35 ` [PATCH v1 1/5] ntfs: initialize inherited ACLs after allocating the MFT record Baolin Liu
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Baolin Liu @ 2026-10-27  9:35 UTC (permalink / raw)
  To: linkinjeon, hyc.lee; +Cc: ntfs, linux-kernel, Baolin Liu

From: Baolin Liu <liubaolin@kylinos.cn>

Hi all,

This series adds O_TMPFILE support to fs/ntfs. Applications can prepare a
regular file's contents and metadata without exposing a directory entry,
then publish the file with linkat(). Unpublished files are reclaimed when
their last reference is released. O_EXCL prevents publication.

O_TMPFILE reuses the existing inode creation, EA update and hard-link
paths. The first four patches fix existing bugs in those paths, so that
anonymous files can inherit ACLs, prepare metadata and be published
correctly:

  1. ACLs must be initialized after allocating the MFT record. Otherwise,
     an inherited ACL can appear correct in memory but disappear after
     inode eviction. Anonymous creation uses the same initialization
     path as ordinary file creation.

  2. Publication through linkat() must preserve the file's atime and
     mtime. The existing hard-link path resets both; the fix updates only
     the required timestamps and marks the affected inodes dirty.

  3. Files may already have ACLs or other EAs before publication. The
     hard-link path currently leaves the new FILE_NAME's packed EA size
     at zero. Initialize it from EA_INFORMATION when adding the link.

  4. Attribute inodes initialized while the base file has zero links
     also have zero links. They are evicted at the final iput(), discarding
     dirty pages from nonresident EA writes. Synchronous nonresident
     writes keep a successful EA update readable after that cache is dropped.

These bugs also affect ordinary creation, hard links and open-unlinked
files. Keeping the fixes separate allows independent review and use
without O_TMPFILE. Patch 5 builds on them to add anonymous creation,
VFS-controlled publication and documentation.

Named-stream ioctls can create or open streams after publication, but
not while the temporary file remains unpublished.

Tested this five-patch series in QEMU with four CPUs, NTFS debug and
lockdep. Tests cover creation, publication, permissions, idmapped mounts,
direct I/O, mmap, allocation, concurrent publication and ENOSPC. Checks
after cache eviction/remount/reboot covered file contents, EA values,
access ACL readability, mode and explicitly set atime/mtime. Injected
ENOSPC at five creation/publication/open sites and checked failure
behavior and release of the recorded base MFT records. Ordinary file
operations were also exercised. Individual patch notes describe the tests.

Based on ntfs-next commit e845077350df3862c733ed10a950d49ef4d64b0c.

Thanks,
Baolin

Baolin Liu (2):
  ntfs: initialize inherited ACLs after allocating the MFT record
  ntfs: support O_TMPFILE

Xuewen Wang (3):
  ntfs: preserve timestamps when creating hard links
  ntfs: initialize packed EA size when creating hard links
  ntfs: write unlinked file attributes before dropping their cache

 Documentation/filesystems/ntfs.rst |  15 ++++
 fs/ntfs/inode.c                    |   8 ++
 fs/ntfs/namei.c                    | 123 ++++++++++++++++++++++++-----
 3 files changed, 126 insertions(+), 20 deletions(-)


base-commit: e845077350df3862c733ed10a950d49ef4d64b0c


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v1 1/5] ntfs: initialize inherited ACLs after allocating the MFT record
  2026-10-27  9:35 [PATCH v1 0/5] ntfs: support O_TMPFILE Baolin Liu
@ 2026-10-27  9:35 ` Baolin Liu
  2026-10-27  9:35 ` [PATCH v1 2/5] ntfs: preserve timestamps when creating hard links Baolin Liu
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Baolin Liu @ 2026-10-27  9:35 UTC (permalink / raw)
  To: linkinjeon, hyc.lee; +Cc: ntfs, linux-kernel, Baolin Liu

From: Baolin Liu <liubaolin@kylinos.cn>

ntfs_init_acl() can write EAs, but __ntfs_create() calls it before
allocating the new inode's MFT record. An inherited access ACL can appear
correct in the inode cache and disappear after eviction.

Initialize the ACL after allocating the MFT record and adding the standard
attributes. Release the parent and child record locks around the ACL
helpers to avoid recursive locking, then refresh the local mode from the
inode. On creation failure, remove any EA and EA_INFORMATION attributes
before freeing the MFT record.

Signed-off-by: Baolin Liu <liubaolin@kylinos.cn>
---
QEMU tests used the complete five-patch series with NTFS debug and
lockdep enabled. An ordinary new file inherits an access ACL from a
parent default ACL containing a named-user entry; that access ACL remains
readable after inode eviction. An anonymous file's inherited access ACL
also remains readable after publication, cache eviction, read-only and
read-write remounts, unmount/mount and a fresh boot. Offline inspection
confirmed that the recorded base MFT record was freed after an injected
creation failure following ACL and WSL EA initialization.

 fs/ntfs/namei.c | 44 +++++++++++++++++++++++++++++++++-----------
 1 file changed, 33 insertions(+), 11 deletions(-)

diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
index 0db7da0..0c84a5c 100644
--- a/fs/ntfs/namei.c
+++ b/fs/ntfs/namei.c
@@ -422,6 +422,7 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
 	struct ntfs_volume *vol = dir_ni->vol;
 	struct ntfs_inode *ni;
 	bool rollback_data = false, rollback_sd = false, rollback_reparse = false;
+	bool rollback_ea = false;
 	struct file_name_attr *fn = NULL;
 	struct standard_information *si = NULL;
 	int err = 0, fn_len, si_len;
@@ -429,6 +430,7 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
 	struct mft_record *ni_mrec, *dni_mrec;
 	struct super_block *sb = dir_ni->vol->sb;
 	struct inode *rollback_data_vi = NULL, *rollback_sd_vi = NULL;
+	struct inode *rollback_ea_vi = NULL, *rollback_eai_vi = NULL;
 	__le64 parent_mft_ref;
 	u64 child_mft_ref;
 	__le16 ea_size;
@@ -469,17 +471,6 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
 
 	mode = vi->i_mode;
 
-#ifdef CONFIG_NTFS_FS_POSIX_ACL
-	if (!S_ISLNK(mode) && (sb->s_flags & SB_POSIXACL)) {
-		err = ntfs_init_acl(idmap, vi, dir);
-		if (err)
-			goto err_out;
-	} else
-#endif
-	{
-		vi->i_flags |= S_NOSEC;
-	}
-
 	if (uid_valid(vol->uid))
 		vi->i_uid = vol->uid;
 
@@ -644,6 +635,24 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
 			goto err_out;
 	}
 
+	/* ACL EAs need the allocated MFT record and initialized attributes. */
+	rollback_ea = true;
+#ifdef CONFIG_NTFS_FS_POSIX_ACL
+	if (!S_ISLNK(mode) && (sb->s_flags & SB_POSIXACL)) {
+		/* The ACL helpers acquire both the parent's and child's locks. */
+		mutex_unlock(&dir_ni->mrec_lock);
+		mutex_unlock(&ni->mrec_lock);
+		err = ntfs_init_acl(idmap, vi, dir);
+		mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL);
+		mutex_lock_nested(&dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT);
+		if (err)
+			goto err_out;
+		mode = vi->i_mode;
+	}
+#endif
+	if (S_ISLNK(mode) || !(sb->s_flags & SB_POSIXACL))
+		vi->i_flags |= S_NOSEC;
+
 	err = ntfs_ea_set_wsl_inode(vi, dev, &ea_size,
 			NTFS_EA_UID | NTFS_EA_GID | NTFS_EA_MODE);
 	if (err)
@@ -718,6 +727,15 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
 	return ni;
 
 err_out:
+	if (rollback_ea) {
+		if (ntfs_attr_exist(ni, AT_EA, AT_UNNAMED, 0) &&
+		    ntfs_attr_remove(ni, AT_EA, AT_UNNAMED, 0, &rollback_ea_vi))
+			ntfs_error(sb, "Failed to roll back EA attribute.");
+		if (ntfs_attr_exist(ni, AT_EA_INFORMATION, AT_UNNAMED, 0) &&
+		    ntfs_attr_remove(ni, AT_EA_INFORMATION, AT_UNNAMED, 0,
+				     &rollback_eai_vi))
+			ntfs_error(sb, "Failed to roll back EA_INFORMATION attribute.");
+	}
 	if (rollback_sd) {
 		int rollback_err;
 
@@ -764,6 +782,10 @@ err_out:
 	mutex_unlock(&dir_ni->mrec_lock);
 	mutex_unlock(&ni->mrec_lock);
 
+	if (rollback_ea_vi)
+		iput(rollback_ea_vi);
+	if (rollback_eai_vi)
+		iput(rollback_eai_vi);
 	if (rollback_data_vi)
 		iput(rollback_data_vi);
 	if (rollback_sd_vi)


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v1 2/5] ntfs: preserve timestamps when creating hard links
  2026-10-27  9:35 [PATCH v1 0/5] ntfs: support O_TMPFILE Baolin Liu
  2026-10-27  9:35 ` [PATCH v1 1/5] ntfs: initialize inherited ACLs after allocating the MFT record Baolin Liu
@ 2026-10-27  9:35 ` Baolin Liu
  2026-10-29  0:15   ` Hyunchul Lee
  2026-10-27  9:35 ` [PATCH v1 3/5] ntfs: initialize packed EA size " Baolin Liu
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Baolin Liu @ 2026-10-27  9:35 UTC (permalink / raw)
  To: linkinjeon, hyc.lee; +Cc: ntfs, linux-kernel, Xuewen Wang

From: Xuewen Wang <wangxuewen@kylinos.cn>

ntfs_link() resets atime, mtime and ctime on both the source file and
the destination directory after creating a hard link. This overwrites
the source file's atime and mtime and the directory's atime.

Update only the source ctime and the destination directory mtime/ctime.
Mark both inodes dirty so these changes are queued for writeback instead
of relying on the earlier FILE_NAME write.

Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn>
---
Tested with the complete five-patch series in QEMU. Explicitly assigned
atime and mtime are preserved across ordinary hard-link creation and
O_TMPFILE publication. A separate persistence check set and synced
atime/mtime on the published file, then verified those values after
cache eviction, remount and a fresh boot.

 fs/ntfs/namei.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
index 0c84a5c..4442e1d 100644
--- a/fs/ntfs/namei.c
+++ b/fs/ntfs/namei.c
@@ -1663,12 +1663,13 @@ static int ntfs_link(struct dentry *old_dentry, struct inode *dir,
 	}
 
 	inode_inc_iversion(dir);
-	simple_inode_init_ts(dir);
+	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
+	mark_inode_dirty(dir);
 
 	inode_inc_iversion(vi);
-	simple_inode_init_ts(vi);
+	inode_set_ctime_current(vi);
+	mark_inode_dirty(vi);
 
-	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
 	d_instantiate(dentry, vi);
 	mutex_unlock(&dir_ni->mrec_lock);
 	mutex_unlock(&ni->mrec_lock);


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v1 3/5] ntfs: initialize packed EA size when creating hard links
  2026-10-27  9:35 [PATCH v1 0/5] ntfs: support O_TMPFILE Baolin Liu
  2026-10-27  9:35 ` [PATCH v1 1/5] ntfs: initialize inherited ACLs after allocating the MFT record Baolin Liu
  2026-10-27  9:35 ` [PATCH v1 2/5] ntfs: preserve timestamps when creating hard links Baolin Liu
@ 2026-10-27  9:35 ` Baolin Liu
  2026-10-27  9:35 ` [PATCH v1 4/5] ntfs: write unlinked file attributes before dropping their cache Baolin Liu
  2026-10-27  9:35 ` [PATCH v1 5/5] ntfs: support O_TMPFILE Baolin Liu
  4 siblings, 0 replies; 9+ messages in thread
From: Baolin Liu @ 2026-10-27  9:35 UTC (permalink / raw)
  To: linkinjeon, hyc.lee; +Cc: ntfs, linux-kernel, Xuewen Wang, Baolin Liu

From: Xuewen Wang <wangxuewen@kylinos.cn>

A new hard link has a zero packed EA size in its FILE_NAME even when
the source file already has extended attributes.

For files with EAs and no reparse point, read EA_INFORMATION and check
its attribute length before adding the name. Copy its ea_length into
the new FILE_NAME's packed_ea_size. Skip this assignment for reparse
points, where the same storage holds the reparse-point tag.

Suggested-by: Baolin Liu <liubaolin@kylinos.cn>
Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn>
---
Tested with the complete five-patch series in QEMU. Published an
O_TMPFILE after inheriting an access ACL and setting a 12000-byte EA.
Offline ntfsinfo inspection confirms that the FILE_NAME attribute in
the file's MFT record has the same packed EA length as EA_INFORMATION
(12134 bytes). The 12000-byte EA contents also match after cache eviction,
remount and a fresh boot.

 fs/ntfs/namei.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
index 4442e1d..b26fd9c 100644
--- a/fs/ntfs/namei.c
+++ b/fs/ntfs/namei.c
@@ -1316,6 +1316,24 @@ static int __ntfs_link(struct ntfs_inode *ni, struct ntfs_inode *dir_ni,
 	fn->last_access_time = utc2ntfs(inode_get_atime(vi));
 	memcpy(fn->file_name, name, name_len * sizeof(__le16));
 
+	if (NInoHasEA(ni) && !(ni->flags & FILE_ATTR_REPARSE_POINT)) {
+		struct ea_information *info;
+		s64 size;
+
+		info = ntfs_attr_readall(ni, AT_EA_INFORMATION, AT_UNNAMED, 0, &size);
+		if (IS_ERR(info)) {
+			err = PTR_ERR(info);
+			goto err_out;
+		}
+		if (size != sizeof(*info))
+			err = -EUCLEAN;
+		else
+			fn->type.ea.packed_ea_size = info->ea_length;
+		kvfree(info);
+		if (err)
+			goto err_out;
+	}
+
 	/* Add FILE_NAME attribute to index. */
 	err = ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no,
 					le16_to_cpu(ni_mrec->sequence_number)));


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v1 4/5] ntfs: write unlinked file attributes before dropping their cache
  2026-10-27  9:35 [PATCH v1 0/5] ntfs: support O_TMPFILE Baolin Liu
                   ` (2 preceding siblings ...)
  2026-10-27  9:35 ` [PATCH v1 3/5] ntfs: initialize packed EA size " Baolin Liu
@ 2026-10-27  9:35 ` Baolin Liu
  2026-10-27  9:35 ` [PATCH v1 5/5] ntfs: support O_TMPFILE Baolin Liu
  4 siblings, 0 replies; 9+ messages in thread
From: Baolin Liu @ 2026-10-27  9:35 UTC (permalink / raw)
  To: linkinjeon, hyc.lee; +Cc: ntfs, linux-kernel, Xuewen Wang

From: Xuewen Wang <wangxuewen@kylinos.cn>

Attribute inodes copy the base inode's link count when initialized.
Those initialized after the base file is unlinked have zero links and
are evicted at the final iput(). Nonresident attribute writes can leave
dirty pages in that cache, so an immediately following getxattr can fail
to find a successfully written EA.

Use the synchronous nonresident attribute write path when the base inode
has no links. Keep named data streams on their existing I/O path. This
allows anonymous files to prepare nonresident EAs without retaining
attribute inodes that would pin the base inode and prevent its
reclamation.

Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn>
---
Tested with the complete five-patch series in QEMU. A 12000-byte EA on
both an ordinary open-unlinked file and an unpublished O_TMPFILE can be
read back immediately, matching every written byte. The published file's
EA also matches after cache eviction, remount and a fresh boot.
Offline checks found the recorded base MFT records' in-use flags and
MFT bitmap bits clear after closing unpublished files, including one
with nonresident data and EAs.

 fs/ntfs/inode.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 206db4e..3c2bbbd 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -4037,6 +4037,14 @@ s64 ntfs_inode_attr_pwrite(struct inode *vi, s64 pos, s64 count, u8 *buf, bool s
 
 	WARN_ON(!NInoAttr(ni));
 
+	/*
+	 * Attribute inodes of an unlinked file have no links either and are
+	 * evicted at the final iput(). Do not leave their data in dirty pages
+	 * that eviction would discard while the base file is still open.
+	 */
+	if (!VFS_I(base_ni)->i_nlink && !ntfs_inode_is_named_stream(ni))
+		sync = true;
+
 	if (ntfs_inode_is_named_stream(ni)) {
 		if (NInoEncrypted(ni))
 			return -EACCES;


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v1 5/5] ntfs: support O_TMPFILE
  2026-10-27  9:35 [PATCH v1 0/5] ntfs: support O_TMPFILE Baolin Liu
                   ` (3 preceding siblings ...)
  2026-10-27  9:35 ` [PATCH v1 4/5] ntfs: write unlinked file attributes before dropping their cache Baolin Liu
@ 2026-10-27  9:35 ` Baolin Liu
  4 siblings, 0 replies; 9+ messages in thread
From: Baolin Liu @ 2026-10-27  9:35 UTC (permalink / raw)
  To: linkinjeon, hyc.lee; +Cc: ntfs, linux-kernel, Baolin Liu

From: Baolin Liu <liubaolin@kylinos.cn>

Allow regular files to be created without a FILE_NAME attribute or
a directory index entry. Keep their on-disk link count at zero and use
d_tmpfile() to attach the inode to the anonymous dentry. Clear the base
inode's in-memory link count before initializing EAs so that attribute
inodes inherit zero links and can be evicted rather than pinning an
unpublished file in cache.

Allow the first hard link of a zero-link inode only when the VFS marks
it I_LINKABLE. Existing deletion checks remain in place, and the VFS
controls O_EXCL and clears I_LINKABLE after publication. Reuse the
zero-link eviction path for unpublished files.

Document the interface and the named-stream restriction before
publication.

Signed-off-by: Baolin Liu <liubaolin@kylinos.cn>
---
Tested the complete five-patch series in QEMU (x86-64, four CPUs, NTFS
debug and lockdep enabled) on disposable NTFS images. Checked anonymous
creation, last-reference cleanup, O_EXCL, linkat publication and failed
publication retry, ACLs, nonresident EAs, prepared timestamps, direct I/O,
mmap, fallocate, truncation, permissions and idmapped mounts. Named-stream
writes were rejected before publication; stream writes, reads and removal
worked after publication.

Verified published file contents, a 12000-byte EA, mode and explicitly set
atime/mtime after cache eviction, read-only and read-write remounts,
unmount/mount and a fresh boot. The inherited access ACL remained readable
in those checks.

Created 400 files using four worker threads, including 100 publications.
Also checked concurrent publication of the same inode, competing target
names, last-unlink races and ENOSPC followed by space reuse. Injected
ENOSPC at five sites in creation, publication and final-open paths and
checked the failure behavior. Offline inspection found the recorded base
MFT records' in-use flags and MFT bitmap bits clear after cleanup.

 Documentation/filesystems/ntfs.rst | 15 +++++++++
 fs/ntfs/namei.c                    | 54 ++++++++++++++++++++++++++----
 2 files changed, 63 insertions(+), 6 deletions(-)

diff --git a/Documentation/filesystems/ntfs.rst b/Documentation/filesystems/ntfs.rst
index 699e91f..a0acc01 100644
--- a/Documentation/filesystems/ntfs.rst
+++ b/Documentation/filesystems/ntfs.rst
@@ -10,6 +10,7 @@ The Linux NTFS filesystem driver
    - Overview
    - Utilities support
    - Linux file attributes
+   - Temporary files
    - Supported mount options
 
 
@@ -58,6 +59,20 @@ These Linux flags are enforced by Linux. Storing them in an NTFS EA does
 not make Windows enforce immutable, append-only or nodump semantics.
 
 
+Temporary files
+===============
+
+The driver supports ``O_TMPFILE`` for regular files. The file has no
+directory entry until it is linked with linkat(2); ``O_EXCL`` prevents
+this publication. Ownership and mode follow normal file creation rules.
+With ACLs enabled, the file's access ACL is derived from the creation
+directory's default ACL. Unpublished files are reclaimed when their last
+reference is released.
+
+Named stream ioctls cannot create or open streams on an unpublished
+temporary file. They can be used after the file has been linked.
+
+
 Supported mount options
 =======================
 
diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
index b26fd9c..4f58b80 100644
--- a/fs/ntfs/namei.c
+++ b/fs/ntfs/namei.c
@@ -489,9 +489,11 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
 	simple_inode_init_ts(vi);
 	ni->i_crtime = inode_get_ctime(vi);
 
-	inode_set_mtime_to_ts(dir, ni->i_crtime);
-	inode_set_ctime_to_ts(dir, ni->i_crtime);
-	mark_inode_dirty(dir);
+	if (name) {
+		inode_set_mtime_to_ts(dir, ni->i_crtime);
+		inode_set_ctime_to_ts(dir, ni->i_crtime);
+		mark_inode_dirty(dir);
+	}
 
 	err = ntfs_mft_record_alloc(dir_ni->vol, mode, &ni, NULL,
 			&ni_mrec, -1);
@@ -513,6 +515,10 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
 	inode_set_iversion(vi, 1);
 	insert_inode_hash(vi);
 
+	/* Attribute inodes must not pin an unpublished file in cache. */
+	if (!name)
+		clear_nlink(vi);
+
 	mutex_lock_nested(&ni->mrec_lock, NTFS_INODE_MUTEX_NORMAL);
 	mutex_lock_nested(&dir_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT);
 	if (NInoBeingDeleted(dir_ni)) {
@@ -658,6 +664,12 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
 	if (err)
 		goto err_out;
 
+	if (!name) {
+		ni_mrec->link_count = 0;
+		mark_mft_record_dirty(ni);
+		goto done;
+	}
+
 	/* Create FILE_NAME attribute. */
 	fn_len = sizeof(struct file_name_attr) + name_len * sizeof(__le16);
 	fn = kzalloc(fn_len, GFP_NOFS);
@@ -709,12 +721,13 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
 		goto err_out;
 	}
 
+	ni->flags = fn->file_attributes |
+		    (ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN);
+done:
 	unmap_mft_record(ni);
 	mutex_unlock(&dir_ni->mrec_lock);
 	mutex_unlock(&ni->mrec_lock);
 
-	ni->flags = fn->file_attributes |
-		    (ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN);
 	/* Set the sequence number. */
 	vi->i_generation = ni->seq_no;
 	set_nlink(vi, 1);
@@ -782,6 +795,8 @@ err_out:
 	mutex_unlock(&dir_ni->mrec_lock);
 	mutex_unlock(&ni->mrec_lock);
 
+	/* The MFT record has already been freed above. */
+	set_nlink(vi, 1);
 	if (rollback_ea_vi)
 		iput(rollback_ea_vi);
 	if (rollback_eai_vi)
@@ -856,6 +871,31 @@ out_free_stream_names:
 	return 0;
 }
 
+static int ntfs_tmpfile(struct mnt_idmap *idmap, struct inode *dir,
+			struct file *file, umode_t mode)
+{
+	struct ntfs_volume *vol = NTFS_SB(dir->i_sb);
+	struct ntfs_inode *ni;
+	struct inode *inode;
+	int err;
+
+	if (NVolShutdown(vol))
+		return -EIO;
+
+	err = ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY);
+	if (err)
+		return err;
+
+	ni = __ntfs_create(idmap, dir, NULL, 0, mode, 0, NULL, 0);
+	if (IS_ERR(ni))
+		return PTR_ERR(ni);
+
+	inode = VFS_I(ni);
+	d_tmpfile(file, inode);
+	unlock_new_inode(inode);
+	return finish_open_simple(file, 0);
+}
+
 static int ntfs_check_unlinkable_dir(struct ntfs_attr_search_ctx *ctx, struct file_name_attr *fn)
 {
 	int link_count;
@@ -1270,7 +1310,8 @@ static int __ntfs_link(struct ntfs_inode *ni, struct ntfs_inode *dir_ni,
 		goto err_out;
 	}
 
-	if (le16_to_cpu(ni_mrec->link_count) == 0) {
+	if (!ni_mrec->link_count &&
+	    !(inode_state_read_once(vi) & I_LINKABLE)) {
 		err = -ENOENT;
 		goto err_out;
 	}
@@ -1703,6 +1744,7 @@ out:
 const struct inode_operations ntfs_dir_inode_ops = {
 	.lookup		= ntfs_lookup,	/* VFS: Lookup directory. */
 	.create		= ntfs_create,
+	.tmpfile	= ntfs_tmpfile,
 	.unlink		= ntfs_unlink,
 	.mkdir		= ntfs_mkdir,
 	.rmdir		= ntfs_rmdir,


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v1 2/5] ntfs: preserve timestamps when creating hard links
  2026-10-27  9:35 ` [PATCH v1 2/5] ntfs: preserve timestamps when creating hard links Baolin Liu
@ 2026-10-29  0:15   ` Hyunchul Lee
  2026-10-29  1:44     ` wangxuewen
  2026-10-29  1:49     ` liubaolin
  0 siblings, 2 replies; 9+ messages in thread
From: Hyunchul Lee @ 2026-10-29  0:15 UTC (permalink / raw)
  To: Baolin Liu; +Cc: linkinjeon, ntfs, linux-kernel, Xuewen Wang

Hi Baolin,

On Tue, Oct 27, 2026 at 05:35:12PM +0800, Baolin Liu wrote:
> From: Xuewen Wang <wangxuewen@kylinos.cn>
> 
> ntfs_link() resets atime, mtime and ctime on both the source file and
> the destination directory after creating a hard link. This overwrites
> the source file's atime and mtime and the directory's atime.
> 
> Update only the source ctime and the destination directory mtime/ctime.
> Mark both inodes dirty so these changes are queued for writeback instead
> of relying on the earlier FILE_NAME write.
> 
> Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn>
> ---
> Tested with the complete five-patch series in QEMU. Explicitly assigned
> atime and mtime are preserved across ordinary hard-link creation and
> O_TMPFILE publication. A separate persistence check set and synced
> atime/mtime on the published file, then verified those values after
> cache eviction, remount and a fresh boot.
> 
>  fs/ntfs/namei.c | 7 ++++---
>  1 file changed, 4 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
> index 0c84a5c..4442e1d 100644
> --- a/fs/ntfs/namei.c
> +++ b/fs/ntfs/namei.c
> @@ -1663,12 +1663,13 @@ static int ntfs_link(struct dentry *old_dentry, struct inode *dir,
>  	}
>  
>  	inode_inc_iversion(dir);
> -	simple_inode_init_ts(dir);
> +	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
> +	mark_inode_dirty(dir);
>  
>  	inode_inc_iversion(vi);
> -	simple_inode_init_ts(vi);
> +	inode_set_ctime_current(vi);
> +	mark_inode_dirty(vi);

Could you also update the last_mft_change_time of $FILE_NAME? it still
appears to retains the old ctime.

Additionaly, could you run run xfstests generic/004 and generic/389?

>  
> -	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
>  	d_instantiate(dentry, vi);
>  	mutex_unlock(&dir_ni->mrec_lock);
>  	mutex_unlock(&ni->mrec_lock);
> 

-- 
Thanks,
Hyunchul

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v1 2/5] ntfs: preserve timestamps when creating hard links
  2026-10-29  0:15   ` Hyunchul Lee
@ 2026-10-29  1:44     ` wangxuewen
  2026-10-29  1:49     ` liubaolin
  1 sibling, 0 replies; 9+ messages in thread
From: wangxuewen @ 2026-10-29  1:44 UTC (permalink / raw)
  To: Hyunchul Lee, Baolin Liu; +Cc: linkinjeon, ntfs, linux-kernel



在 2026/10/29 08:15, Hyunchul Lee 写道:
> Hi Baolin,
> 
> On Tue, Oct 27, 2026 at 05:35:12PM +0800, Baolin Liu wrote:
>> From: Xuewen Wang <wangxuewen@kylinos.cn>
>>
>> ntfs_link() resets atime, mtime and ctime on both the source file and
>> the destination directory after creating a hard link. This overwrites
>> the source file's atime and mtime and the directory's atime.
>>
>> Update only the source ctime and the destination directory mtime/ctime.
>> Mark both inodes dirty so these changes are queued for writeback instead
>> of relying on the earlier FILE_NAME write.
>>
>> Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn>
>> ---
>> Tested with the complete five-patch series in QEMU. Explicitly assigned
>> atime and mtime are preserved across ordinary hard-link creation and
>> O_TMPFILE publication. A separate persistence check set and synced
>> atime/mtime on the published file, then verified those values after
>> cache eviction, remount and a fresh boot.
>>
>>   fs/ntfs/namei.c | 7 ++++---
>>   1 file changed, 4 insertions(+), 3 deletions(-)
>>
>> diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
>> index 0c84a5c..4442e1d 100644
>> --- a/fs/ntfs/namei.c
>> +++ b/fs/ntfs/namei.c
>> @@ -1663,12 +1663,13 @@ static int ntfs_link(struct dentry *old_dentry, struct inode *dir,
>>   	}
>>   
>>   	inode_inc_iversion(dir);
>> -	simple_inode_init_ts(dir);
>> +	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
>> +	mark_inode_dirty(dir);
>>   
>>   	inode_inc_iversion(vi);
>> -	simple_inode_init_ts(vi);
>> +	inode_set_ctime_current(vi);
>> +	mark_inode_dirty(vi);
> 
> Could you also update the last_mft_change_time of $FILE_NAME? it still
> appears to retains the old ctime.
> 
> Additionaly, could you run run xfstests generic/004 and generic/389?
Hi Hyunchul,

Thanks for your review. Baolin and I will make modifications and run 
tests per your comments, and submit the v2 patch afterwards.

Regards,
Xuewen>
>>   
>> -	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
>>   	d_instantiate(dentry, vi);
>>   	mutex_unlock(&dir_ni->mrec_lock);
>>   	mutex_unlock(&ni->mrec_lock);
>>
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v1 2/5] ntfs: preserve timestamps when creating hard links
  2026-10-29  0:15   ` Hyunchul Lee
  2026-10-29  1:44     ` wangxuewen
@ 2026-10-29  1:49     ` liubaolin
  1 sibling, 0 replies; 9+ messages in thread
From: liubaolin @ 2026-10-29  1:49 UTC (permalink / raw)
  To: Hyunchul Lee; +Cc: linkinjeon, ntfs, linux-kernel, Xuewen Wang



在 2026/10/29 08:15, Hyunchul Lee 写道:
> Hi Baolin,
> 
> On Tue, Oct 27, 2026 at 05:35:12PM +0800, Baolin Liu wrote:
>> From: Xuewen Wang <wangxuewen@kylinos.cn>
>>
>> ntfs_link() resets atime, mtime and ctime on both the source file and
>> the destination directory after creating a hard link. This overwrites
>> the source file's atime and mtime and the directory's atime.
>>
>> Update only the source ctime and the destination directory mtime/ctime.
>> Mark both inodes dirty so these changes are queued for writeback instead
>> of relying on the earlier FILE_NAME write.
>>
>> Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn>
>> ---
>> Tested with the complete five-patch series in QEMU. Explicitly assigned
>> atime and mtime are preserved across ordinary hard-link creation and
>> O_TMPFILE publication. A separate persistence check set and synced
>> atime/mtime on the published file, then verified those values after
>> cache eviction, remount and a fresh boot.
>>
>>   fs/ntfs/namei.c | 7 ++++---
>>   1 file changed, 4 insertions(+), 3 deletions(-)
>>
>> diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
>> index 0c84a5c..4442e1d 100644
>> --- a/fs/ntfs/namei.c
>> +++ b/fs/ntfs/namei.c
>> @@ -1663,12 +1663,13 @@ static int ntfs_link(struct dentry *old_dentry, struct inode *dir,
>>   	}
>>   
>>   	inode_inc_iversion(dir);
>> -	simple_inode_init_ts(dir);
>> +	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
>> +	mark_inode_dirty(dir);
>>   
>>   	inode_inc_iversion(vi);
>> -	simple_inode_init_ts(vi);
>> +	inode_set_ctime_current(vi);
>> +	mark_inode_dirty(vi);
> 
> Could you also update the last_mft_change_time of $FILE_NAME? it still
> appears to retains the old ctime.
> 
> Additionaly, could you run run xfstests generic/004 and generic/389?

 >
Hi Hyunchul,

    Thanks for your review. Xuewen and I will make modifications and run 
tests per your comments, and submit the v2 patch afterwards.

Thanks,
Baolin.

> 
>>   
>> -	/* timestamp is already written, so mark_inode_dirty() is unneeded. */
>>   	d_instantiate(dentry, vi);
>>   	mutex_unlock(&dir_ni->mrec_lock);
>>   	mutex_unlock(&ni->mrec_lock);
>>
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-10-29  1:49 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-27  9:35 [PATCH v1 0/5] ntfs: support O_TMPFILE Baolin Liu
2026-10-27  9:35 ` [PATCH v1 1/5] ntfs: initialize inherited ACLs after allocating the MFT record Baolin Liu
2026-10-27  9:35 ` [PATCH v1 2/5] ntfs: preserve timestamps when creating hard links Baolin Liu
2026-10-29  0:15   ` Hyunchul Lee
2026-10-29  1:44     ` wangxuewen
2026-10-29  1:49     ` liubaolin
2026-10-27  9:35 ` [PATCH v1 3/5] ntfs: initialize packed EA size " Baolin Liu
2026-10-27  9:35 ` [PATCH v1 4/5] ntfs: write unlinked file attributes before dropping their cache Baolin Liu
2026-10-27  9:35 ` [PATCH v1 5/5] ntfs: support O_TMPFILE Baolin Liu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®