Linux stat Command Deep Dive: Complete Metadata Extraction from Inodes to Timestamps#

The stat command is one of the most underrated tools in Linux. Most people rely on ls -l to check file information, but stat reveals the underlying truths that ls can’t show.

The Core of stat: inode Metadata#

The essence of stat is reading a file’s inode (index node). In Linux filesystems, each file has a unique inode that stores all information except the filename and actual data.

stat example.txt

Output looks like this:

File: example.txt
Size: 1024       Blocks: 8          IO Block: 4096   regular file
Device: 801h/2049d      Inode: 131074      Links: 1
Access: (0644/-rw-r--r--)  Uid: (1000/user)   Gid: (1000/user)
Access: 2025-05-12 10:30:00.123456789 +0800
Modify: 2025-05-12 09:15:00.987654321 +0800
Change: 2025-05-12 09:15:00.987654321 +0800
Birth: -

Key Fields Explained#

  • Size: Actual file size in bytes
  • Blocks: Number of 512-byte blocks allocated (not simply file size / 512, includes metadata and preallocation)
  • IO Block: Optimal I/O block size for the filesystem (usually 4096 bytes)
  • Inode: Inode number, unique within the filesystem
  • Links: Hard link count

Three Types of Timestamps: atime, mtime, ctime#

This is the most valuable part of stat, and many people confuse these three:

Access Time (atime)#

The time the file was last read. Note these pitfalls:

# Reading a file updates atime
cat file.txt

# But modern systems use relatime by default, only updating when mtime is earlier than atime
# This is a performance optimization to avoid frequent disk writes

Performance tuning in /etc/fstab:

  • noatime: Completely disable atime updates (better performance)
  • relatime: Relative time updates (default)
  • strictatime: Update on every access (poor performance)

Modify Time (mtime)#

The time the file content was last modified. This is what ls -l shows by default.

echo "new content" >> file.txt  # Updates mtime and ctime

Change Time (ctime)#

The time the file metadata was last changed. Note it’s “changed” not “modified”:

chmod 755 file.txt   # Only updates ctime
chown user file.txt  # Only updates ctime
ln file.txt hardlink # Updates ctime (link count changed)

Key differences:

  • Modifying file content: both mtime and ctime update
  • Changing permissions/ownership: only ctime updates
  • You cannot directly modify ctime, except at the filesystem level

Birth Time (Creation Time)#

Most Linux filesystems (ext4, xfs) support creation time, but stat shows - because GNU coreutils’ stat version is older. Use debugfs instead:

debugfs -R 'stat <inode_number>' /dev/sda1

Under the Hood: System Calls#

The stat command calls the stat() system call:

#include <sys/stat.h>
#include <unistd.h>

int stat(const char *pathname, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);  // Don't follow symlinks
int fstat(int fd, struct stat *statbuf);  // Via file descriptor

Key fields in struct stat:

struct stat {
    dev_t     st_dev;      // Device ID
    ino_t     st_ino;      // Inode number
    mode_t    st_mode;     // File type and permissions
    nlink_t   st_nlink;    // Number of hard links
    uid_t     st_uid;      // User ID
    gid_t     st_gid;      // Group ID
    dev_t     st_rdev;     // Special device ID
    off_t     st_size;     // File size (bytes)
    blksize_t st_blksize;  // I/O block size
    blkcnt_t  st_blocks;   // Number of 512-byte blocks allocated
    time_t    st_atime;    // Access time
    time_t    st_mtime;    // Modification time
    time_t    st_ctime;    // Status change time
};

Formatted Output: The Art of -c Flag#

stat -c allows custom output formatting, essential for script automation:

# Show only file size
stat -c "%s" file.txt

# Show inode and filename
stat -c "%i %n" file.txt

# Show permissions (octal)
stat -c "%a %n" file.txt

# Show owner username
stat -c "%U %G %n" file.txt

# Custom time format
stat -c "%y %n" file.txt  # ISO format mtime
stat -c "%.10y %n" file.txt  # Only date part (first 10 chars)

Practical Format Specifiers#

Format Meaning Example
%s File size (bytes) 1024
%b Blocks allocated 8
%i Inode number 131074
%n Filename file.txt
%N Quoted filename ‘file.txt’
%a Octal permissions 644
%A Symbolic permissions -rw-r–r–
%U Username user
%G Group name user
%y mtime 2025-05-12 10:30:00.123456789 +0800
%z ctime 2025-05-12 10:30:00.123456789 +0800
%x atime 2025-05-12 10:30:00.123456789 +0800

Filesystem Information: -f Flag#

stat -f shows filesystem-level information:

stat -f /home

Output:

File: "/home"
ID: 12345678-90ab-cdef-1234-567890abcdef Namelen: 255
Type: ext2/ext3/ext4
Block size: 4096
Blocks: Total: 26214400   Free: 20971520   Available: 19922944
Inodes: Total: 16777216   Free: 15938355

Key fields:

  • Block size: Filesystem block size (affects storage efficiency)
  • Total/Free/Available: Block statistics
  • Inodes: Total and free inodes (inode exhaustion prevents creating new files)

Real-World Scenarios#

1. Find Oldest Files (by Creation Time)#

find /path -type f -printf "%C@ %p\n" | sort -n | head -5

2. Detect File Tampering#

# Backup critical file's ctime
stat -c "%z" /etc/passwd > /var/log/passwd.ctime

# Periodic check
if [ "$(stat -c "%z" /etc/passwd)" != "$(cat /var/log/passwd.ctime)" ]; then
    echo "Warning: /etc/passwd has been modified!"
fi

3. Batch File Size Calculation#

# Sum all .log files in current directory
find . -name "*.log" -exec stat -c "%s" {} + | awk '{sum+=$1} END {print sum}'
# Only count regular files, ignore symlinks
find . -type f -exec stat -c "%s" {} + | awk '{sum+=$1} END {print sum}'

5. Monitor Inode Usage#

# Check filesystem inode usage
df -i | grep -v Filesystem | while read fs total used free percent mount; do
    echo "Mount: $mount | Inode Usage: $percent"
done

stat vs ls -l: When to Use Which?#

Scenario Recommended Tool Reason
Check permissions ls -l More intuitive
Check file size ls -lh Human readable
Full timestamps stat Nanosecond precision
All three times stat ls only shows mtime
Inode info stat ls -i only shows number
Script field extraction stat -c Easier to parse
Filesystem info stat -f ls doesn’t support

Common Pitfalls#

1. Timezone Issues#

stat shows local time, but scripts may need UTC:

# Get UTC timestamp
stat -c "%Y" file.txt | xargs -I {} date -d @{} -u "+%Y-%m-%d %H:%M:%S"

By default, stat follows symlinks:

ln -s target link
stat link        # Shows target's info
stat -L link     # Same as above (-L is default)

3. Block Count Misconception#

st_blocks is in 512-byte units, not filesystem block size:

# File size 1024 bytes
stat -c "%s %b" file.txt
# Output: 1024 8
# Actual usage: 8 * 512 = 4096 bytes (one 4KB block)

Web Implementation: Node.js File Metadata#

Implementing stat-like functionality in Node.js:

const fs = require('fs').promises;

async function getFileStats(filePath) {
    try {
        const stats = await fs.stat(filePath);

        return {
            size: stats.size,
            mode: stats.mode.toString(8).slice(-4),  // Octal permissions
            uid: stats.uid,
            gid: stats.gid,
            atime: stats.atime.toISOString(),
            mtime: stats.mtime.toISOString(),
            ctime: stats.ctime.toISOString(),
            isDirectory: stats.isDirectory(),
            isFile: stats.isFile(),
            isSymbolicLink: stats.isSymbolicLink(),
            ino: stats.ino,  // Inode number
            blocks: stats.blocks,
            blksize: stats.blksize
        };
    } catch (error) {
        throw new Error(`Cannot stat ${filePath}: ${error.message}`);
    }
}

// Usage example
getFileStats('/path/to/file').then(console.log);

Browser limitations: Browsers can’t directly access filesystem metadata. File API only provides lastModified (similar to mtime).

Summary#

The stat command is a window into the Linux filesystem internals. It exposes the complete inode structure, revealing the truth behind files. Remember the three timestamp types, master the -c format syntax, and you’ll be equipped for script automation and troubleshooting.

Next time you need precise file metadata, don’t rely on ls -l. Use stat to get the complete picture.


Related Tools: