ntfs: support persistent noatime attribute

NTFS currently rejects FS_NOATIME_FL in its fileattr setter. This prevents
users from disabling automatic access-time updates on individual files or
directories with chattr +A.

Store noatime in bit 3 of the private $LINUXFLAGS EA and restore S_NOATIME
when loading an inode. Report and accept FS_NOATIME_FL in the fileattr
callbacks, updating the in-memory flag only after the EA update succeeds.
The VFS then suppresses automatic atime updates for the inode, while
chattr -A restores the mount's atime policy.

Document the new on-disk bit and that newly created children do not inherit
the attribute.

Suggested-by: Baolin Liu <liubaolin@kylinos.cn>
Reviewed-by: Baolin Liu <liubaolin@kylinos.cn>
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Xuewen Wang <wangxuewen@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Xuewen Wang 2026-10-16 11:09:57 +08:00 committed by Namjae Jeon
parent 89467388e8
commit b715537e26
4 changed files with 24 additions and 8 deletions

View File

@ -38,15 +38,20 @@ The project is available at:
Linux file attributes
=====================
The driver supports lsattr(1) and chattr(1). Immutable, append-only and
nodump are stored in a private ``$LINUXFLAGS`` extended attribute within the
The driver supports lsattr(1) and chattr(1). Immutable, append-only, nodump
and noatime are stored in a private ``$LINUXFLAGS`` extended attribute within the
standard NTFS ``$EA``/``$EA_INFORMATION`` attributes. Its value is a
32-bit little-endian bitmask: bit 0 is immutable, bit 1 is append-only,
and bit 2 is nodump. Unknown bits are preserved when updating the flags;
the entry is removed when the entire value becomes zero. These settings
bit 2 is nodump, and bit 3 is noatime. Unknown bits are preserved when updating
the flags; the entry is removed when the entire value becomes zero. These settings
survive inode eviction, unmount and reboot. Direct writes to this EA
through setxattr(2) or removexattr(2) are rejected; use chattr(1) instead.
The noatime attribute (``chattr +A``) suppresses automatic access-time
updates for the inode, including when mounted with ``strictatime``. It
applies to directories as well as files, but is not inherited by newly
created children. Use ``chattr -A`` to restore the mount's access-time policy.
System metadata files and files protected by the ``sys_immutable`` mount
option cannot have their immutable protection cleared with chattr(1).
Protection derived solely from the mount option is not stored in the EA.
@ -55,7 +60,7 @@ configured per file. Compression and encryption are reported but cannot
be changed through chattr(1).
These Linux flags are enforced by Linux. Storing them in an NTFS EA does
not make Windows enforce immutable, append-only or nodump semantics.
not make Windows enforce immutable, append-only, nodump or noatime semantics.
Supported mount options

View File

@ -501,6 +501,8 @@ int ntfs_ea_get_linuxflags(struct inode *inode)
inode->i_flags |= S_IMMUTABLE;
if (ni->linuxflags & NTFS_LINUXFLAGS_APPEND)
inode->i_flags |= S_APPEND;
if (ni->linuxflags & NTFS_LINUXFLAGS_NOATIME)
inode->i_flags |= S_NOATIME;
return 0;
}

View File

@ -11,8 +11,10 @@
#define NTFS_LINUXFLAGS_IMMUTABLE BIT(0)
#define NTFS_LINUXFLAGS_APPEND BIT(1)
#define NTFS_LINUXFLAGS_NODUMP BIT(2)
#define NTFS_LINUXFLAGS_NOATIME BIT(3)
#define NTFS_LINUXFLAGS_MASK (NTFS_LINUXFLAGS_IMMUTABLE | \
NTFS_LINUXFLAGS_APPEND | NTFS_LINUXFLAGS_NODUMP)
NTFS_LINUXFLAGS_APPEND | NTFS_LINUXFLAGS_NODUMP | \
NTFS_LINUXFLAGS_NOATIME)
extern const struct xattr_handler *const ntfs_xattr_handlers[];

View File

@ -167,6 +167,8 @@ int ntfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
flags |= FS_IMMUTABLE_FL;
if (vi->i_flags & S_APPEND)
flags |= FS_APPEND_FL;
if (vi->i_flags & S_NOATIME)
flags |= FS_NOATIME_FL;
if (ni->linuxflags & NTFS_LINUXFLAGS_NODUMP)
flags |= FS_NODUMP_FL;
if (!NVolCaseSensitive(ni->vol))
@ -200,7 +202,8 @@ int ntfs_fileattr_set(const struct mnt_idmap *idmap, struct dentry *dentry,
{
struct inode *vi = d_inode(dentry);
struct ntfs_inode *ni = NTFS_I(vi);
u32 allowed = FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL;
u32 allowed = FS_IMMUTABLE_FL | FS_APPEND_FL | FS_NODUMP_FL |
FS_NOATIME_FL;
u32 readonly = 0;
u32 linuxflags = ni->linuxflags & ~NTFS_LINUXFLAGS_MASK;
unsigned int new_fl = 0;
@ -245,6 +248,10 @@ int ntfs_fileattr_set(const struct mnt_idmap *idmap, struct dentry *dentry,
}
if (fa->flags & FS_NODUMP_FL)
linuxflags |= NTFS_LINUXFLAGS_NODUMP;
if (fa->flags & FS_NOATIME_FL) {
new_fl |= S_NOATIME;
linuxflags |= NTFS_LINUXFLAGS_NOATIME;
}
mutex_lock(&ni->mrec_lock);
err = ntfs_ea_set_linuxflags(vi, linuxflags);
@ -252,7 +259,7 @@ int ntfs_fileattr_set(const struct mnt_idmap *idmap, struct dentry *dentry,
if (err)
return err;
inode_set_flags(vi, new_fl, S_IMMUTABLE | S_APPEND);
inode_set_flags(vi, new_fl, S_IMMUTABLE | S_APPEND | S_NOATIME);
inode_set_ctime_current(vi);
mark_inode_dirty(vi);
return 0;