FreeBSD Basics



[ Home | FreeBSD | Samba | CMPP | Mail me ]


Understanding Filesystem Inodes
Dru Lavigne
03/07/2001

We've spent the last few articles looking at partition tables and file systems. We've discovered that your PC finds your FreeBSD slice by reading the BIOS partition table. That FreeBSD slice has a Unix partition table that contains the "disk packing label," which describes the layout of the filesystems on that slice. This week, we can finally take a look at inodes: what they are and what information about them is available to you on your FreeBSD system.

Let's start by taking another look at the output of the "disklabel" command from one of my FreeBSD systems; I've snipped the output to just show the layout of the partitions:

disklabel ad0
<snip>

8 partitions:
#     size offset fstype [fsize bsize bps/cpg]
a:  102400      0 4.2BSD    0    0     0 # (Cyl.  0 - 6*)
b:  270976 102400   swap                 # (Cyl.  6*- 23*)
c: 6538455      0 unused    0    0       # (Cyl.  0 - 406)
e:   40960 373376 4.2BSD    0    0     0 # (Cyl. 23*- 25*)
f: 6124119 414336 4.2BSD    0    0     0 # (Cyl. 25*- 406*)

Notice that partitions "a", "e", and "f" are to be formatted with the filesystem type 4.2BSD, meaning the Berkeley fast file system (FFS). What is interesting to note is that each partition has been defined by its cylinders. That is, partition "a" uses cylinders 0-6, partition "e" uses cylinders 23-25, and partition "f" uses cylinders 25-406. The "*" means that the partition didn't begin or end exactly on a cylinder boundary.

What exactly is a cylinder? If you're a bit rusty on how hard drives work, you may find this article and its figures useful.

Basically, a hard drive is composed of a number of circular disks called platters. Each platter has been divided into circular tracks; a cylinder is the same track on all the platters. If you could separate all the cylinders on your hard drive, you would end up with a series of increasingly smaller sized rings with each ring being the thickness of your hard drive.

A partition is simply a cylinder group, or a group of adjacent cylinders logically joined into a wider, doughnut-shaped ring. If a partition is formatted with a filesystem, that filesystem will maintain one inode table to keep track of any data placed on that cylinder group. To summarize:

Each partition that has been formatted with a filesystem has three distinct areas:

The superblock describes the parameters of the filesystem, such as the number of blocks, the size of the blocks, the size of the fragments, and the number of inodes. (If you're curious as to what else is defined in the superblock, you'll find all the parameters in man 5 fs.) These parameters were determined by the newfs command and any switches you may have passed to that command when you created the filesystem. This means that if at a later date you discover that you will run out of inodes before you run out of disk blocks, you will have to recreate the filesystem in order to change these parameters. You'll need to back up your data and test your backup first, as recreating the filesystem with the newfs utility will destroy all of the existing data on that cylinder group.

After the superblock area is the area that contains all of the inode entries. Each inode entry is 128 bytes in size and contains information about the file that it represents; this information is known as the file's "metadata." You can find out for yourself what this metadata is by reading the file /usr/include/ufs/ufs/dinode.h; even though this is a C file, it is well commented and not too hard to figure out. I've summarized its contents by listing the metadata that an inode keeps track of:

Notice that the name of the file is not part of the inode's metadata. The filesystem doesn't care what the name of the file is; it only needs to know what inode number is associated with that file.

Much of a file's metadata can be viewed by doing a long directory listing. Let's take a look at the long listing for the root directory by using the ls command with the l switch:

ls -l /

total 6429
-r--r--r--   1 root  wheel     4735 Nov 20 07:03 COPYRIGHT
drwxr-xr-x   2 root  wheel     1024 Dec 21 19:09 bin
drwxr-xr-x   3 root  wheel      512 Dec 21 12:27 boot
drwxr-xr-x   2 root  wheel      512 Dec 21 10:19 cdrom
lrwxr-xr-x   1 root  wheel       11 Dec 21 12:27 compat -> /usr/compat
drwxr-xr-x   3 root  wheel    13824 Feb 25 09:15 dev
drwxr-xr-x  15 root  wheel     2048 Dec 22 18:20 etc
drwxr-xr-x   1 root  wheel     7168 Jan  1  1980 floppy
lrwxrwxrwx   1 root  wheel        9 Dec 21 12:29 home -> /usr/home
-r-xr-xr-x   1 root  wheel  3258128 Nov 20 08:02 kernel
-r-xr-xr-x   1 root  wheel  3258128 Nov 20 08:02 kernel.GENERIC
drwxr-xr-x   2 root  wheel      512 Nov 20 06:56 mnt
drwxr-xr-x   2 root  wheel     2560 Dec 21 10:33 modules
dr-xr-xr-x   1 root  wheel      512 Feb 25 10:32 proc
drwxr-xr-x   2 root  wheel      512 Dec 23 08:30 root
drwxr-xr-x   2 root  wheel     2048 Dec 21 19:09 sbin
drwxr-xr-x   4 root  wheel     1024 Dec 21 10:19 stand
lrwxrwxrwx   1 root  wheel       11 Dec 21 12:22 sys -> usr/src/sys
drwxrwxrwt   2 root  wheel      512 Feb 25 09:15 tmp
drwxr-xr-x  19 root  wheel      512 Feb 11 19:04 usr
drwxr-xr-x  18 root  wheel      512 Nov 20 08:13 var

We've been given seven columns of output that represent each file's:

  • permissions
  • link count
  • owner
  • group
  • size in bytes
  • mtime
  • pathname
  • You should be able to recognize the first six as part of the metadata contained in each file's inode.

    You can also find out the inode number of each file by adding the i switch to the ls command:

    ls -li /
    
    total 6429
     441 -r--r--r--   1 root  wheel     4735 Nov 20 07:03 COPYRIGHT
      46 drwxr-xr-x   2 root  wheel     1024 Dec 21 19:09 bin
    6323 drwxr-xr-x   3 root  wheel      512 Dec 21 12:27 boot
      44 drwxr-xr-x   2 root  wheel      512 Dec 21 10:19 cdrom
     444 lrwxr-xr-x   1 root  wheel       11 Dec 21 12:27 compat -> /usr/compat
    6272 drwxr-xr-x   3 root  wheel    13824 Feb 25 09:15 dev
      36 drwxr-xr-x  15 root  wheel     2048 Dec 22 18:20 etc
     236 drwxr-xr-x   1 root  wheel     7168 Jan  1  1980 floppy
     451 lrwxrwxrwx   1 root  wheel        9 Dec 21 12:29 home -> /usr/home
     443 -r-xr-xr-x   1 root  wheel  3258128 Nov 20 08:02 kernel
     442 -r-xr-xr-x   1 root  wheel  3258128 Nov 20 08:02 kernel.GENERIC
     241 drwxr-xr-x   2 root  wheel      512 Nov 20 06:56 mnt
     242 drwxr-xr-x   2 root  wheel     2560 Dec 21 10:33 modules
       2 dr-xr-xr-x   1 root  wheel      512 Feb 25 10:32 proc
     358 drwxr-xr-x   2 root  wheel      512 Dec 23 08:30 root
     363 drwxr-xr-x   2 root  wheel     2048 Dec 21 19:09 sbin
       5 drwxr-xr-x   4 root  wheel     1024 Dec 21 10:19 stand
     440 lrwxrwxrwx   1 root  wheel       11 Dec 21 12:22 sys -> usr/src/sys
       2 drwxrwxrwt   2 root  wheel      512 Feb 25 09:15 tmp
       2 drwxr-xr-x  19 root  wheel      512 Feb 11 19:04 usr
       2 drwxr-xr-x  18 root  wheel      512 Nov 20 08:13 var

    Notice that this output added an extra column to the long listing; the number in the first column is the inode number for that file. Inode number "2" has been mentioned four times; once for proc, tmp, usr, and var. Inode 2 is always the first inode of a filesystem and represents the root or starting point of that filesystem. We can use the df command to see which filesystems have been mounted on this system:

    df
    
    Filesystem  1K-blocks    Used    Avail Capacity  Mounted on
    /dev/ad0s2a     49583   27729    17888    61%    /
    /dev/ad0s2f   2967289  737169  1992737    27%    /usr
    /dev/ad0s2e     19815    3647    14583    20%    /var
    procfs              4       4        0   100%    /proc
    mfs:27         131231       1   120732     0%    /tmp

    Not surprisingly, usr, var, proc and tmp show up as mounted filesystems. Don't forget that each filesystem maintains its own inode table; that is, inode 2 for usr is a different inode entry in a completely different inode table than the inode 2 entry for "var." The only thing these inode entries share in common is the number 2, as they are both the root entry for their respective filesystems.

    The df or disk free utility has a switch that will tell you how many inodes are on each filesystem. Let's run the df utility again with the i switch. View the output:

    df -i
    
    Filesystem  1K-blocks     Used    Avail Capacity iused   ifree  %iused  Mounted on
    /dev/ad0s2a     49583    27729    17888    61%    1074   11468     9%   /
    /dev/ad0s2f   2967289   739993  1989913    27%   90852  655130    12%   /usr
    /dev/ad0s2e     19815     3645    14585    20%     391    4663     8%   /var
    procfs              4        4        0   100%      25     507     5%   /proc
    mfs:27         131231        1   120732     0%       1   33277     0%   /tmp

    It's a good idea to run this command on a regular basis to ensure that your filesystems are not running out of either disk storage blocks or inode entries. Unless you are creating a large number of very small files, you will probably run out of disk blocks long before you run out of inodes. Knowing whether or not you've created enough inodes while leaving enough storage blocks is a matter of experience, as it depends upon what your FreeBSD system is used for and what types of files are created by your users. If you run this command often, you'll get an idea of what is normal for your system; you'll also learn if the defaults are not appropriate for your system.

    The last thing I'd like you to notice about an inode is that it keeps track of three different times: the file's mtime, atime, and ctime.

    A file's "mtime" is its last modification time; that is, when the actual contents of the file were last changed. For example, if you open a file with your favourite text editor and add or delete some text, you have modified the contents of that file. When you save your changes, the inode will update the "mtime" of that file. Remember that ls -l will show the mtime of the file.

    A file's "atime" is the last time that file was accessed. For example, if you read a file using a pager, you will access the file and the inode will update the "atime" for that file. You can view a directory listing by atime instead of the default mtime by using the switches lut as shown:

    ls -lut /
    
    total 6429
    dr-xr-xr-x   1 root  wheel      512 Feb 26 14:06 proc
    drwxr-xr-x   2 root  wheel     1024 Feb 26 14:03 bin
    drwxr-xr-x   2 root  wheel     2048 Feb 26 14:03 sbin
    drwxr-xr-x   3 root  wheel    13824 Feb 26 13:54 dev
    drwxrwxrwt   2 root  wheel      512 Feb 26 13:54 tmp
    drwxr-xr-x  19 root  wheel      512 Feb 25 14:02 usr
    drwxr-xr-x  18 root  wheel      512 Feb 20 02:01 var
    drwxr-xr-x   2 root  wheel      512 Feb 20 01:59 root
    drwxr-xr-x   4 root  wheel     1024 Feb 20 01:59 stand
    drwxr-xr-x  15 root  wheel     2048 Feb 20 01:59 etc
    drwxr-xr-x   2 root  wheel      512 Feb 20 01:59 cdrom
    drwxr-xr-x   3 root  wheel      512 Feb 20 01:59 boot
    drwxr-xr-x   2 root  wheel      512 Feb 20 01:59 mnt
    drwxr-xr-x   2 root  wheel     2560 Feb 20 01:59 modules
    lrwxrwxrwx   1 root  wheel        9 Dec 21 12:29 home -> /usr/home
    lrwxr-xr-x   1 root  wheel       11 Dec 21 12:27 compat -> /usr/compat
    lrwxrwxrwx   1 root  wheel       11 Dec 21 12:22 sys -> usr/src/sys
    -r-xr-xr-x   1 root  wheel  3258128 Nov 20 08:02 kernel
    -r-xr-xr-x   1 root  wheel  3258128 Nov 20 08:02 kernel.GENERIC
    -r--r--r--   1 root  wheel     4735 Nov 20 07:03 COPYRIGHT
    drwxr-xr-x   1 root  wheel     7168 Jan  1  1980 floppy

    The "ctime" is updated whenever the inode itself is changed. For example, if you change the permissions, owner, or group of a file you are actually making changes to that file's inode, so the "ctime" will also be updated. To see a listing by ctime, use the switches lc with the ls utility, as shown:

    ls -lc /
    
    total 6429
    -r--r--r--   1 root  wheel     4735 Dec 21 12:22 COPYRIGHT
    drwxr-xr-x   2 root  wheel     1024 Dec 21 19:09 bin
    drwxr-xr-x   3 root  wheel      512 Dec 21 12:27 boot
    drwxr-xr-x   2 root  wheel      512 Dec 21 10:19 cdrom
    lrwxr-xr-x   1 root  wheel       11 Dec 21 12:27 compat -> /usr/compat
    drwxr-xr-x   3 root  wheel    13824 Feb 26 13:54 dev
    drwxr-xr-x  15 root  wheel     2048 Dec 22 18:20 etc
    drwxr-xr-x   1 root  wheel     7168 Jan  1  1980 floppy
    lrwxrwxrwx   1 root  wheel        9 Dec 21 12:29 home -> /usr/home
    -r-xr-xr-x   1 root  wheel  3258128 Dec 21 12:27 kernel
    -r-xr-xr-x   1 root  wheel  3258128 Feb 11 19:04 kernel.GENERIC
    drwxr-xr-x   2 root  wheel      512 Dec 21 10:27 mnt
    drwxr-xr-x   2 root  wheel     2560 Dec 21 10:33 modules
    dr-xr-xr-x   1 root  wheel      512 Feb 26 14:07 proc
    drwxr-xr-x   2 root  wheel      512 Dec 23 08:30 root
    drwxr-xr-x   2 root  wheel     2048 Dec 21 19:09 sbin
    drwxr-xr-x   4 root  wheel     1024 Dec 21 10:19 stand
    lrwxrwxrwx   1 root  wheel       11 Dec 21 12:22 sys -> usr/src/sys
    drwxrwxrwt   2 root  wheel      512 Feb 26 13:54 tmp
    drwxr-xr-x  19 root  wheel      512 Feb 11 19:04 usr
    drwxr-xr-x  18 root  wheel      512 Dec 21 19:15 var

    If you take a close look at those last three ls listings, you'll note that the times are indeed different and reflect the three types of times recorded by each file's inode.

    Next week, I'd like to start taking a closer look at IP packets and headers so you'll have the grounding necessary to understand packet filters and firewalls.

    Read more from FreeBSD Basics.




    [ Home | FreeBSD | Samba | CMPP | Mail me ]

    Copyright © 2000, 2001 it's meaculpa. All rights reserved.