1

How do commands like ls or stat distinguish the file type, whether the object is a file or directory?

For example, I created these two objects, considering the fact that a directory is also a file... with some special rules, I want to know how in the output of the command stat these are labelled as "directory" and "regular empty file".

$ mkdir testdir;touch testfile
$ stat testdir | head -2;stat testfile | head -2
 File: `testdir'
 Size: 4096            Blocks: 8          IO Block: 4096   directory
 File: `testfile'
 Size: 0               Blocks: 0          IO Block: 4096   regular empty file

Later, I did a strace while executing stat of the directory testdir and the file testfile respectively. In the trace output I noticed these

lstat("testdir/", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0

and

lstat("testfile", {st_mode=S_IFREG|0664, st_size=0, ...}) = 0

Someone please tell me how st_mode gets these values S_IFDIR and S_IFREG.

I may sound confused; I am indeed.

2 Answers 2

6

Let us try and un-confuse you. In an inode there is a 16 bit field returned by the stat/lstat (and 64 bit variants) in st_mode. Of this 9 bits are used for the rwxrwxrwx permissions, another 3 for the sticky bit, set group id (sgid) bit and set userid (suid) bit. The other 4 bits are used to encode some type information. This can say it is a regular file, a directory, a block or character device, a named pipe etc etc.

So if you create a directory then these 4 bits say it is a directory. You can see this in the strace output...

lstat("testdir/", {st_mode=S_IFDIR|0775, st_size=4096, ...})
                           ^^^^^^^ 4 bits showing the type is directory
                                   ^ 3 bits (this is octal) for suid/sgid/sticky
                                    ^^^ rwxrwxrwx info.
1

Commands ls, stat or even file uses internally the C stat() system call to gather the details. Here the structures are defined. If you check the stat files:

  • /usr/include/bits/stat.h
  • /usr/include/linux/stat.h

The following bits are defined:

/* Encoding of the file mode.  */

#define __S_IFMT        0170000 /* These bits determine file type.  */

/* File types.  */
#define __S_IFDIR       0040000 /* Directory.  */
#define __S_IFCHR       0020000 /* Character device.  */
#define __S_IFBLK       0060000 /* Block device.  */
#define __S_IFREG       0100000 /* Regular file.  */
#define __S_IFIFO       0010000 /* FIFO.  */
#define __S_IFLNK       0120000 /* Symbolic link.  */
#define __S_IFSOCK      0140000 /* Socket.  */

the stat function will check the POSIX macros and compare to see if it is a regular file/ directory

#define S_ISLNK(m)      (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m)      (((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m)      (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m)     (((m) & S_IFMT) == S_IFIFO)
#define S_ISSOCK(m)     (((m) & S_IFMT) == S_IFSOCK)

That's how st_mode gets these values S_IFDIR and S_IFREG.

Just FYI ... Now to one main question on how inodes identify the file type is:

For the XFS filesystem, an inode data structure is divided into 3 parts:

  • di_core (96 bytes)
  • di_u data fork
  • di_a extended attribute fork

The core contains what the inode represents, stat data and information describing the data and attribute forks. Here the file type is controlled by di_core.di_mode (eg. regular file, directory, link, etc).

The di_u "data fork" contains normal data related to the inode.

The di_a "attribute fork" contains extended attributes.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .