Understanding the Linux inodes

Linux makes a clear distinction between the contents of a file and the information about a file. All information needed by the filesystem to handle a file is included in a data structure called an inode. Each file has its own inode, which the filesystem uses to identify the file.

File types

Linux files may be one of the following types.

  • Regular file
  • Directory
  • Symbolic link
  • Block device file
  • Character device file
  • Pipe(FIFO)
  • Socket

The first three file types are constituents of any Linux filesystem. Device files are related both to I/O devices, and to device drivers integrated into the kernel. When a program accesses a device file, it acts directly on the I/O device associated with that file. Pipes and sockets are special files used for interprocess communication.

File inodes

Linux system must provide the following inode attributes, which are specified in the POSIX standard:

  • File type
  • Link count: Number of hard links associated with the file
  • File size in bytes
  • Device ID(i.e., the identifier of the device containing the file)
  • Inode number that identifies the file in the filesystem
  • UID of the file owner
  • User group ID of the file
  • Timestamps which specify the inode change time, the last access time and the last modify time.
  • Access rights/permissions(read, write, and execute) and file mode

The following is inode struct definition in the Linux source code.

 * Keep mostly read-only and often accessed (especially for
 * the RCU path lookup and 'stat' data) fields at the beginning
 * of the 'struct inode'
struct inode {
    umode_t			i_mode;
    unsigned short		i_opflags;
    kuid_t			i_uid;
    kgid_t			i_gid;
    unsigned int		i_flags;

    struct posix_acl	*i_acl;
    struct posix_acl	*i_default_acl;

    const struct inode_operations	*i_op;
    struct super_block	*i_sb;
    struct address_space	*i_mapping;

    void			*i_security;

    /* Stat data, not accessed from path walking */
    unsigned long		i_ino;
     * Filesystems may only read i_nlink directly.  They shall use the
     * following functions for modification:
     *    (set|clear|inc|drop)_nlink
     *    inode_(inc|dec)_link_count
    union {
        const unsigned int i_nlink;
        unsigned int __i_nlink;
    dev_t			i_rdev;
    loff_t			i_size;
    struct timespec64	i_atime;
    struct timespec64	i_mtime;
    struct timespec64	i_ctime;
    spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
    unsigned short          i_bytes;
    u8			i_blkbits;
    u8			i_write_hint;
    blkcnt_t		i_blocks;

    seqcount_t		i_size_seqcount;

    /* Misc */
    unsigned long		i_state;
    struct rw_semaphore	i_rwsem;

    unsigned long		dirtied_when;	/* jiffies of first dirtying */
    unsigned long		dirtied_time_when;

    struct hlist_node	i_hash;
    struct list_head	i_io_list;	/* backing dev IO list */
    struct bdi_writeback	*i_wb;		/* the associated cgroup wb */

    /* foreign inode detection, see wbc_detach_inode() */
    int			i_wb_frn_winner;
    u16			i_wb_frn_avg_time;
    u16			i_wb_frn_history;
    struct list_head	i_lru;		/* inode LRU list */
    struct list_head	i_sb_list;
    struct list_head	i_wb_list;	/* backing dev writeback list */
    union {
        struct hlist_head	i_dentry;
        struct rcu_head		i_rcu;
    atomic64_t		i_version;
    atomic64_t		i_sequence; /* see futex */
    atomic_t		i_count;
    atomic_t		i_dio_count;
    atomic_t		i_writecount;
#if defined(CONFIG_IMA) || defined(CONFIG_FILE_LOCKING)
    atomic_t		i_readcount; /* struct files open RO */
    union {
        const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
        void (*free_inode)(struct inode *);
    struct file_lock_context	*i_flctx;
    struct address_space	i_data;
    struct list_head	i_devices;
    union {
        struct pipe_inode_info	*i_pipe;
        struct cdev		*i_cdev;
        char			*i_link;
        unsigned		i_dir_seq;

    __u32			i_generation;

    __u32			i_fsnotify_mask; /* all events this inode cares about */
    struct fsnotify_mark_connector __rcu	*i_fsnotify_marks;

    struct fscrypt_info	*i_crypt_info;

    struct fsverity_info	*i_verity_info;

    void			*i_private; /* fs or device private pointer */
} __randomize_layout;

Display inode information

The inode data can be displayed with the stat command.

$ echo "hello inode" > testfile

$ debugfs /dev/mapper/vgroot-lvroot
debugfs 1.42.9 (28-Dec-2013)
debugfs:  q
[root@init500-d1 ~]# stat testfile
  File: ‘testfile’
  Size: 12        	Blocks: 8          IO Block: 4096   regular file
Device: fd00h/64768d	Inode: 27787306    Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-04-30 20:40:38.496696650 +0000
Modify: 2022-04-30 20:40:38.496696650 +0000
Change: 2022-04-30 20:40:38.496696650 +0000
 Birth: -

$ stat --format=%i testfile

The inode number can be also displayed with ls -i command. With ls -l command, it displays file permissions for the owner, group and others. In the following example, the owner has read and write permission. The group and others only have read permission.

$ ls -il testfile
27787306 -rw-r--r-- 1 root root 12 Apr 30 20:40 testfile

The inode space information can be displayed with df -i command.

$ df -i
Filesystem                   Inodes  IUsed     IFree IUse% Mounted on
devtmpfs                  132058034    953 132057081    1% /dev
tmpfs                     132061520      4 132061516    1% /dev/shm
tmpfs                     132061520   1270 132060250    1% /run
tmpfs                     132061520     17 132061503    1% /sys/fs/cgroup
/dev/mapper/vgroot-lvroot  97320960 188567  97132393    1% /
/dev/nvme0n1p2               128016     31    127985    1% /boot
/dev/nvme0n1p1                    0      0         0     - /boot/efi
tmpfs                     132061520      1 132061519    1% /run/user/0
tmpfs                     132061520      1 132061519    1% /var/lib/osd/lttng

The content of filesystem superblock can be listed with tune2fs command. The inode related information can be grepped.

$ tune2fs -l /dev/mapper/vgroot-lvroot | grep -i inode
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize
Inode count:              97320960
Free inodes:              97167388
Inodes per group:         8192
Inode blocks per group:   512
First inode:              11
Inode size:	          256
Journal inode:            8
First orphan inode:       22157990
Journal backup:           inode blocks

The debugfs program is an interactive file system debugger. It can be used to examine and change the state of an ext2, ext3, or ext4 file system.

$ debugfs /dev/mapper/vgroot-lvroot
debugfs 1.42.9 (28-Dec-2013)

debugfs:  stat <27787306>
Inode: 27787306   Type: regular    Mode:  0644   Flags: 0x80000
Generation: 269876152    Version: 0x00000000:00000001
User:     0   Group:     0   Size: 12
File ACL: 0    Directory ACL: 0
Links: 1   Blockcount: 8
Fragment:  Address: 0    Number: 0    Size: 0
 ctime: 0x626d9ec6:766bf528 -- Sat Apr 30 20:40:38 2022
 atime: 0x626d9ec6:766bf528 -- Sat Apr 30 20:40:38 2022
 mtime: 0x626d9ec6:766bf528 -- Sat Apr 30 20:40:38 2022
crtime: 0x626d9ec6:766bf528 -- Sat Apr 30 20:40:38 2022
Size of extra inode fields: 32

Inode structure for the directory can be displayed as below.

$ stat /
  File: ‘/’
  Size: 4096      	Blocks: 8          IO Block: 4096   directory
Device: fd00h/64768d	Inode: 2           Links: 18
Access: (0555/dr-xr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-04-14 23:50:13.812053511 +0000
Modify: 2022-04-30 20:42:56.468650708 +0000
Change: 2022-04-30 20:42:56.468650708 +0000
 Birth: -