* [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* 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
* [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