BSD DevCenter
oreilly.comSafari Books Online.Conferences.


FreeBSD Basics

Understanding Filesystem Inodes


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

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.

Also in FreeBSD Basics:

Fun with Xorg

Sharing Internet Connections

Building a Desktop Firewall

Using DesktopBSD

Using PC-BSD

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:

  • A filesystem is responsible for one cylinder group.
  • A cylinder group has one inode table.

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

  • an area known as the superblock
  • an area that contains the inode entries
  • the remaining area, which is used to store files

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:

  • the permissions of the file
  • the file link count
  • old user and group ids
  • the inode number
  • the size of the file in bytes
  • the last time the file was accessed (atime)
  • the last time the file was modified (mtime)
  • the last inode change time for the file (ctime)
  • direct disk blocks
  • indirect disk blocks
  • status flags (chflags)
  • blocks actually held
  • file generation number
  • the owner of the file
  • the primary group of the owner of the file

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 here.

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 (view sample output here).

    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:

    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 here

    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 here.

    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 here.

    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.

    Dru Lavigne is a network and systems administrator, IT instructor, author and international speaker. She has over a decade of experience administering and teaching Netware, Microsoft, Cisco, Checkpoint, SCO, Solaris, Linux, and BSD systems. A prolific author, she pens the popular FreeBSD Basics column for O'Reilly and is author of BSD Hacks and The Best of FreeBSD Basics.

    Read more FreeBSD Basics columns.

    Discuss this article in the Operating Systems Forum.

    Return to the BSD DevCenter.

    Sponsored by: