Flag Types
System Call
Shell Tool
What are I-node Flags?
Beyond the standard 12 permission bits, Linux allows you to set special i-node flags on files and directories. These are additional attributes that control how the kernel treats the file — things like making it immutable, enabling append-only mode, or skipping atime updates.
Originally an ext2 feature, i-node flags are now supported on ext3, ext4, XFS, Btrfs, JFS, and Reiserfs. They are a Linux-specific extension (not in POSIX), accessed via the ioctl() system call from programs, or via the chattr and lsattr shell commands.
Key Terms:
| Constant (linux/fs.h) | chattr | Purpose | Implemented? |
|---|---|---|---|
| FS_IMMUTABLE_FL | i | File data and metadata cannot be changed. Even root cannot modify it. | ✓ Yes |
| FS_APPEND_FL | a | File can only be opened with O_APPEND. Data can only be added, never overwritten. | ✓ Yes |
| FS_NOATIME_FL | A | Skip atime updates on access. Improves I/O performance for read-heavy files. | ✓ Yes |
| FS_SYNC_FL | S | Writes are synchronous (like O_SYNC). Data goes to disk immediately. | ✓ Yes |
| FS_DIRSYNC_FL | D | Directory updates are synchronous. Only for directories. | ✓ Yes (2.6+) |
| FS_JOURNAL_DATA_FL | j | Journal file data as well as metadata. ext3/ext4 only. | ✓ Yes |
| FS_NODUMP_FL | d | Exclude from dump(8) backups. | ✓ Yes |
| FS_NOTAIL_FL | t | Disable tail packing. Reiserfs only. | ✓ Yes |
| FS_TOPDIR_FL | T | Mark as top-level directory for Orlov block allocator. ext2/3/4. | ✓ Yes (2.6+) |
| FS_COMPR_FL | c | Compress file on disk. (API exists but not in any major fs.) | ✗ Not implemented |
| FS_SECRM_FL | s | Securely wipe file on delete. (Not implemented.) | ✗ Not implemented |
| FS_UNRM_FL | u | Allow undelete. (Not implemented.) | ✗ Not implemented |
FS_IMMUTABLE_FL (i), FS_APPEND_FL (a), and FS_NOATIME_FL (A). These are real-world relevant.The easiest way to work with i-node flags is via shell commands:
# View current flags on a file
$ lsattr myfile
--------------e---- myfile (e = uses extents, default on ext4)
# Set a flag using chattr (+flag adds, -flag removes, =flag sets exactly)
$ chattr +i myfile # Make immutable
$ lsattr myfile
----i---------e---- myfile
# Try to delete it — even root fails!
$ rm myfile
rm: cannot remove 'myfile': Operation not permitted
# Remove immutable flag first
$ chattr -i myfile
$ rm myfile # Now it works
# Set append-only (useful for log files)
$ chattr +a /var/log/myapp.log
# Now: open(log, O_WRONLY) fails, but open(log, O_WRONLY|O_APPEND) succeeds
# Set both append-only and immutable
$ chattr +ai myfile
# Skip atime updates for performance
$ chattr +A myfile
# Apply flags recursively to a directory tree
$ chattr -R +A /var/www/html # Skip atime on all web files
From C programs, use ioctl() with FS_IOC_GETFLAGS and FS_IOC_SETFLAGS:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fs.h> /* FS_IOC_GETFLAGS, FS_IOC_SETFLAGS, FS_*_FL */
#include <stdlib.h>
Example 1: Read current i-node flags
int get_inode_flags(const char *path) {
int fd, flags;
fd = open(path, O_RDONLY);
if (fd == -1) { perror("open"); return -1; }
if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) {
perror("ioctl GETFLAGS");
close(fd);
return -1;
}
close(fd);
return flags;
}
int main(void) {
int flags = get_inode_flags("myfile");
if (flags == -1) return 1;
printf("Flags: 0x%x\n", flags);
if (flags & FS_IMMUTABLE_FL) printf(" Immutable (i)\n");
if (flags & FS_APPEND_FL) printf(" Append-only (a)\n");
if (flags & FS_NOATIME_FL) printf(" No-atime (A)\n");
if (flags & FS_SYNC_FL) printf(" Sync (S)\n");
if (flags & FS_NODUMP_FL) printf(" No-dump (d)\n");
if (flags & FS_JOURNAL_DATA_FL) printf(" Journal data (j)\n");
return 0;
}
Example 2: Set the FS_NOATIME_FL flag (skip atime updates)
int set_noatime_flag(const char *path) {
int fd, flags;
/* Must open with write access to set flags (or be owner/root) */
fd = open(path, O_RDONLY);
if (fd == -1) { perror("open"); return -1; }
/* Step 1: Get current flags */
if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) {
perror("ioctl GETFLAGS");
close(fd);
return -1;
}
/* Step 2: Add the flag using OR */
flags |= FS_NOATIME_FL;
/* Step 3: Set the updated flags */
if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1) {
perror("ioctl SETFLAGS");
close(fd);
return -1;
}
printf("FS_NOATIME_FL set on %s\n", path);
close(fd);
return 0;
}
int main(void) {
return set_noatime_flag("myfile");
}
Example 3: Clear (remove) a specific flag
int clear_flag(const char *path, int flag_to_clear) {
int fd, flags;
fd = open(path, O_RDONLY);
if (fd == -1) { perror("open"); return -1; }
if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) {
close(fd); return -1;
}
/* Remove the flag using AND NOT */
flags &= ~flag_to_clear;
if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1) {
close(fd); return -1;
}
close(fd);
return 0;
}
int main(void) {
/* Remove noatime flag */
if (clear_flag("myfile", FS_NOATIME_FL) == 0)
printf("Cleared FS_NOATIME_FL\n");
return 0;
}
Example 4: Make a file immutable (needs CAP_LINUX_IMMUTABLE / root)
/* WARNING: Run as root. Once immutable, even root cannot modify/delete.
Must clear immutable flag first to make any changes. */
int make_immutable(const char *path) {
int fd, flags;
fd = open(path, O_RDONLY);
if (fd == -1) { perror("open"); return -1; }
if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) {
close(fd); return -1;
}
flags |= FS_IMMUTABLE_FL;
if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1) {
perror("ioctl SETFLAGS (need root and CAP_LINUX_IMMUTABLE)");
close(fd);
return -1;
}
printf("File is now immutable. Cannot be changed or deleted.\n");
close(fd);
return 0;
}
/* To reverse it: clear_flag(path, FS_IMMUTABLE_FL)
Use case: protect critical system config files from accidental deletion
e.g. make_immutable("/etc/hosts"); */
Example 5: Set append-only on a log file
/* Append-only: processes can only add data, never overwrite.
Good for log files — even if program is compromised, it can't
falsify old log entries. */
int make_append_only(const char *path) {
int fd, flags;
fd = open(path, O_RDONLY);
if (fd == -1) { perror("open"); return -1; }
if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) {
close(fd); return -1;
}
flags |= FS_APPEND_FL;
if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1) {
perror("ioctl (need root)");
close(fd); return -1;
}
printf("Log file is now append-only\n");
close(fd);
return 0;
}
/* After this:
open(path, O_WRONLY) → EPERM
open(path, O_WRONLY|O_APPEND) → OK
write(fd, ...) → data appended to end only */
Example 6: Utility — print all set flags as a string
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
struct { int flag; char letter; } flag_map[] = {
{ FS_APPEND_FL, 'a' },
{ FS_NODUMP_FL, 'd' },
{ FS_IMMUTABLE_FL, 'i' },
{ FS_JOURNAL_DATA_FL,'j' },
{ FS_NOATIME_FL, 'A' },
{ FS_SYNC_FL, 'S' },
{ FS_DIRSYNC_FL, 'D' },
{ FS_NOTAIL_FL, 't' },
{ FS_TOPDIR_FL, 'T' },
{ 0, 0 }
};
void lsattr_for_file(const char *path) {
int fd, flags, i;
char flag_str[20] = "-------------"; /* 13 dashes = no flags */
fd = open(path, O_RDONLY);
if (fd == -1) { perror("open"); return; }
if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == 0) {
for (i = 0; flag_map[i].flag; i++) {
if (flags & flag_map[i].flag)
flag_str[i] = flag_map[i].letter;
}
printf("%s %s\n", flag_str, path);
}
close(fd);
}
int main(int argc, char *argv[]) {
if (argc < 2) { fprintf(stderr, "Usage: %s file...\n", argv[0]); return 1; }
for (int i = 1; i < argc; i++)
lsattr_for_file(argv[i]);
return 0;
}
When you set i-node flags on a directory, those flags are generally inherited by new files and subdirectories created inside. But there are exceptions:
| Flag on Directory | Inherited by new files? | Inherited by new subdirs? |
|---|---|---|
| Most flags (A, S, etc.) | Yes | Yes |
| FS_DIRSYNC_FL (D) | No (files only) | Yes (subdirs only) |
| FS_IMMUTABLE_FL (i) | Not inherited | Not inherited |
FS_IMMUTABLE_FL is not inherited because an immutable directory can’t have new entries added to it — so inheritance is impossible.| Filesystem | I-node flags support | Notes |
|---|---|---|
| ext2 | ✓ Origin | Original home of this feature |
| ext3 | ✓ Full | Adds FS_JOURNAL_DATA_FL support |
| ext4 | ✓ Full | Most commonly used today |
| Btrfs | ✓ Partial | Immutable, noatime, and others |
| XFS | ✓ Partial | Since Linux 2.4.25 |
| Reiserfs | ✓ Partial | Needs -o attrs mount option |
| JFS | ✓ Partial | Since Linux 2.6.17 |
| FAT / NTFS | ✗ Not supported | No i-node concept |
Q1 What system call is used to get and set i-node flags from a C program?
A ioctl() with the operations FS_IOC_GETFLAGS (to read flags into an int) and FS_IOC_SETFLAGS (to write flags). Requires an open file descriptor and the headers <sys/ioctl.h> and <linux/fs.h>.
Q2 What is the difference between the immutable flag and the append-only flag?
A Immutable (FS_IMMUTABLE_FL) prevents all changes — no writes, no chmod, no chown, no rename, no delete. Even root is blocked. Append-only (FS_APPEND_FL) allows data to be added at the end (with O_APPEND), but existing data cannot be overwritten and the file cannot be deleted.
Q3 What privilege is needed to set the immutable or append-only flag?
A The CAP_LINUX_IMMUTABLE capability (which root has by default). Normal unprivileged users cannot set these flags.
Q4 What is the practical use case for FS_NOATIME_FL?
A Disabling atime updates avoids a disk write on every file read. For read-heavy workloads (web servers serving static files, database systems, mail servers), this significantly reduces disk I/O. It’s equivalent to the noatime mount option but per-file.
Q5 Shell commands: how do you make a file append-only and verify it?
A
$ chattr +a logfile.log # Set append-only
$ lsattr logfile.log # Verify: shows 'a' in flag string
# -----a--------e---- logfile.log
$ echo "new line" >> logfile.log # OK (append)
$ echo "overwrite" > logfile.log # FAILS: -bash: logfile.log: Operation not permitted
Q6 Are i-node flags part of POSIX?
A No. I-node flags are a Linux-specific (non-standard) extension, originally from ext2. They are accessed via ioctl() rather than a dedicated POSIX system call. BSDs have a similar feature called “file flags” via chflags(), but the API is different.
Q7 Write the correct ioctl call pattern to enable FS_SYNC_FL on a file.
A
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <fcntl.h>
int attr;
int fd = open("myfile", O_RDONLY);
ioctl(fd, FS_IOC_GETFLAGS, &attr); /* get current flags */
attr |= FS_SYNC_FL; /* add the flag */
ioctl(fd, FS_IOC_SETFLAGS, &attr); /* write back */
close(fd);
You have completed Chapter 15 — File Attributes!
