Back

Linux inodes

What are they?

Inode name comes from index node. It is data structure which contains metadata about file, it’s owner, access privileges details etc. i-nodes were created in 1969 as part of Unix filesystem [2]. Full list of file attributes for POSIX which may be retrieved by stat() https://en.wikipedia.org/wiki/Inode :

  • Device ID (this identifies the device containing the file; that is, the scope of uniqueness of the serial number)
  • File serial numbers
  • The file mode which determines the file type and how the file’s owner, its group, and others can access the file
  • A link count telling how many hard links point to the inode
  • The User ID of the file’s owner
  • The Group ID of the file
  • The device ID of the file if it is a device file
  • The size of the file in bytes
  • Timestamps telling when the inode itself was last modified (ctime, inode change time), the file content last modified (mtime, modification time), and last accessed (atime, access time)
  • The preferred I/O block size
  • The number of blocks allocated to this file

From man http://man7.org/linux/man-pages/man2/stat.2.html

   lstat() is identical to stat(), except that if pathname is a symbolic
   link, then it returns information about the link itself, not the file
   that it refers to.
   fstat() is identical to stat(), except that the file about which
   information is to be retrieved is specified by the file descriptor
   fd.

Root filesystem for extfs is accessible by inode 2:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
~# debugfs /dev/sdc2
debugfs 1.44.1 (24-Mar-2018)
debugfs:  stat <2>

Inode: 2   Type: directory    Mode:  0755   Flags: 0x80000
Generation: 0    Version: 0x00000000:0000003d
User:     0   Group:     0   Project:     0   Size: 4096
File ACL: 0
Links: 25   Blockcount: 8
Fragment:  Address: 0    Number: 0    Size: 0
 ctime: 0x5c75c231:0e38d234 -- Wed Feb 27 00:48:17 2019
 atime: 0x5ea73676:d3949e4c -- Mon Apr 27 22:45:58 2020
 mtime: 0x5c75c231:0e38d234 -- Wed Feb 27 00:48:17 2019
crtime: 0x5c44dad9:00000000 -- Sun Jan 20 22:32:25 2019
Size of extra inode fields: 32
Inode checksum: 0xb1297a6d
EXTENTS:
(0):9279

Recent versions of XFS support file inlining - storage of small files in inode itself.

Commands

  1. List inode for the file:
1
2
# ls -i test2.txt
4477762301 test2.txt
  1. Show file details and inode:
1
2
3
4
5
6
7
8
9
# stat test2.txt
  File: test2.txt
  Size: 4         	Blocks: 8          IO Block: 4096   regular file
Device: 811h/2065d	Inode: 4477762301  Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-04-29 23:03:30.383288538 +0300
Modify: 2020-04-29 23:03:30.383288538 +0300
Change: 2020-04-30 09:23:40.971643721 +0300
 Birth: -
  1. Show free inodes:
1
2
3
# df -i /opt/data/
Filesystem        Inodes  IUsed     IFree IUse% Mounted on
/dev/sdb1      280747160 614100 280133060    1% /opt/data
  1. Lets check how mv command works with 2 different scenarios. First - move file within one physical device, second - move file from one device to another. /opt/ is one device, /opt/dta ais another:
1
2
3
# df /opt/
Filesystem     1K-blocks      Used Available Use% Mounted on
/dev/sdc2      238798492 108642928 117955528  48% /
1
2
3
# df /opt/data/
Filesystem      1K-blocks       Used Available Use% Mounted on
/dev/sdb1      3905108984 3725191884 179917100  96% /opt/data

Moving file within one device actually does not move any data, just calling rename syscall [7] which changes inode:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# strace mv test.txt test/
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=11824816, ...}) = 0
mmap(NULL, 11824816, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fdf29c48000
close(3)                                = 0
geteuid()                               = 0
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
stat("test/", {st_mode=S_IFDIR|0755, st_size=6, ...}) = 0
lstat("test.txt", {st_mode=S_IFREG|0644, st_size=5, ...}) = 0
lstat("test/test.txt", 0x7ffc9066a270)  = -1 ENOENT (No such file or directory)
rename("test.txt", "test/test.txt")     = 0
lseek(0, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

Moving file between 2 different moutn points takes more systen calls as you can see below and data is actually copied:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# strace mv test.txt /opt/
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=11824816, ...}) = 0
mmap(NULL, 11824816, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f8856728000
close(3)                                = 0
geteuid()                               = 0
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
stat("/opt/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("test.txt", {st_mode=S_IFREG|0644, st_size=5, ...}) = 0
lstat("/opt/test.txt", 0x7ffc30de9370)  = -1 ENOENT (No such file or directory)
rename("test.txt", "/opt/test.txt")     = -1 EXDEV (Invalid cross-device link)
unlink("/opt/test.txt")                 = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "test.txt", O_RDONLY|O_NOFOLLOW) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=5, ...}) = 0
openat(AT_FDCWD, "/opt/test.txt", O_WRONLY|O_CREAT|O_EXCL, 0600) = 4
fstat(4, {st_mode=S_IFREG|0600, st_size=0, ...}) = 0
ioctl(4, BTRFS_IOC_CLONE or FICLONE, 3) = -1 EXDEV (Invalid cross-device link)
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8858523000
read(3, "2345\n", 131072)               = 5
write(4, "2345\n", 5)                   = 5
read(3, "", 131072)                     = 0
utimensat(4, NULL, [{tv_sec=1588018562, tv_nsec=415094435} /* 2020-04-27T23:16:02.415094435+0300 */, {tv_sec=1588018559, tv_nsec=735171997} /* 2020-04-27T23:15:59.735171997+0300 */], 0) = 0
flistxattr(3, NULL, 0)                  = 0
flistxattr(3, 0x7ffc30de90c0, 0)        = 0
fgetxattr(3, "system.posix_acl_access", 0x7ffc30de8fa0, 132) = -1 ENODATA (No data available)
fstat(3, {st_mode=S_IFREG|0644, st_size=5, ...}) = 0
fsetxattr(4, "system.posix_acl_access", "\2\0\0\0\1\0\6\0\377\377\377\377\4\0\4\0\377\377\377\377 \0\4\0\377\377\377\377", 28, 0) = 0
close(4)                                = 0
close(3)                                = 0
munmap(0x7f8858523000, 139264)          = 0
lstat("/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
newfstatat(AT_FDCWD, "test.txt", {st_mode=S_IFREG|0644, st_size=5, ...}, AT_SYMLINK_NOFOLLOW) = 0
unlinkat(AT_FDCWD, "test.txt", 0)       = 0
lseek(0, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

Links:

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy