Read this as a study guide instead

Filesystems

How bytes get organized; inodes and journaling.

Check your own machine

These commands only read information — they change nothing. Run the block for your OS, then use the table to read your own numbers against the ideas above.

Windows

Volumes and free space on Windows PowerShell
Get-Volume

What you should see Get-Volume lists each volume with its drive letter, its file system type such as NTFS, its total size, and its remaining free space.

Note NTFS records files in a master file table rather than Unix-style inodes, so there is no inode number to list and no inode column appears on Windows.

macOS

Mounts, space, and inode numbers on macOS zsh
df -h
ls -li

What you should see df -h lists each mounted volume with its capacity and free space in human-readable units; ls -li prints a long listing of the current directory with each file's inode number in the first column.

Note macOS ships the BSD versions of df and ls, so the column headings differ slightly from Linux; df -h reports base-1024 sizes (use df -H for base-1000).

Linux

Mounts, space, and inodes on Linux bash
df -h
df -i
ls -li

What you should see df -h lists each mounted filesystem with its total size and how much space is used and available, in human-readable units; df -i lists the same filesystems by inode count, showing total, used, and free inodes; ls -li prints a long listing of the current directory with each file's inode number in the first column.

Where each idea shows up in your output
ConceptWindowsmacOSLinux
Mounted filesystems and free spaceGet-Volumedf -hdf -h
Inode number of a file—ls -lils -li

The same ideas, as prose

These are the exact fragments the model serves — also available as an ordered study guide.

From numbered blocks to named files

A storage device does not know what a file is. It offers something far more primitive: a long row of numbered slots, each holding a fixed number of bytes, and the ability to read or write any slot by its number. That is all. Turning that into the tree of named files you actually work with is a job someone has to do.

The filesystem is the layer that does it. It decides which numbered slots hold a given file's bytes, keeps a record of that file's size and permissions and timestamps, and maps human-readable names, arranged in a tree of directories, onto those records. The paths you type are the surface; underneath, everything is numbered slots and careful bookkeeping.

Everything here is that gap and its consequences: how a file's bytes land in blocks, what the record behind a file holds, why a name is separable from the file it names, how several devices become one tree, and why a disk that is out of room and a disk that is out of records can be two different problems.

Storage is fixed-size blocks

one file is scattered across non-contiguous blocks the file threads through its blocks in order file file file file 0 1 2 3 4 5 6 7 8 9 10 11 numbered blocks on the device blocks holding the file free block
A file's bytes occupy several non-contiguous numbered blocks on a device while the remaining blocks stay free.

A filesystem does not hand the device one byte at a time. It works in blocks: fixed-size chunks that are the smallest unit it will allocate. A block is commonly 4 KiB on today's filesystems, but that size is chosen when the filesystem is created rather than fixed for all time — it is the common default, not a law. Underneath the block sits the device's own smallest unit, the sector, which is 512 bytes on traditional drives and 4096 bytes on newer ones.

When you save a file, its bytes are spread across as many blocks as they need, and those blocks are not necessarily next to each other. The filesystem takes whatever free blocks are available, wherever they happen to sit like a wall of identical numbered bins where one delivery is tucked into whichever bins happen to be free, rather than onto a single long shelf. A large file can end up scattered the length of the device, its pieces interleaved with everyone else's.

So a file is a set of blocks, not one continuous stretch. That has a cost that shapes the rest of the design: something, somewhere, has to record exactly which blocks belong to which file, or the scattered pieces are just so much noise.

The record behind a file

report.txt the directory entry names the inode inode mode / permissions owner and group timestamps size in bytes link count block pointers ptr 0 ptr 1 ptr 2 data block data block data block each pointer names a data block the filename lives in the directory, not in the inode
An inode holds a file's metadata and a list of block pointers to its data blocks, while the filename is stored separately in the directory.

Every file has a record that describes it, and on a Unix filesystem that record is called an inode. The inode holds the file's metadata: its size in bytes, its permissions, its owner, timestamps for when it was last changed and read, a count of how many names refer to it, and the list of block pointers saying exactly which blocks on the device hold the file's contents like a packing list that records an item's details and exactly which bins hold its contents, kept separate from the contents themselves. The pointers are what pull the scattered pieces back into one file.

One thing the inode does not hold is the file's name. The inode is pure bookkeeping about the bytes and who may touch them; the name lives somewhere else entirely. That separation is easy to overlook, and it quietly explains a great deal of how a filesystem behaves.

Each inode has a number, and that number, not the name, is how the filesystem truly identifies a file. The number leads to the record, the record lists the blocks, and the blocks are the data. A name is only the convenient handle bolted on top.

A name is just a pointer

directory name maps to inode number notes.txt 12 draft.txt 12 todo.txt 47 inode 12 inode 47 data data name maps to inode inode maps to data blocks two names point to one inode: a hard link
A directory maps filenames to inode numbers that lead to data blocks, and two different names map to one inode as a hard link.

A directory is not a box that holds files. It is a table that maps names to inode numbers like a library catalog card that points to a book's record, so two cards can point to one book and discarding a card need not remove the book. When you open a file by name, the filesystem looks that name up in its directory, finds the inode number, and follows it to the record and the blocks. The name and the file are two separate things joined only by that lookup.

Because they are separate, one file can have more than one name. A hard link is simply a second directory entry pointing at the same inode: same inode number, same blocks, same metadata, under a different name. The inode keeps a count of how many names refer to it, and that count rises with each hard link. None of the names is the real one; they are equals, and removing one just lowers the count.

A soft link, or symbolic link, works differently. It is a small file of its own whose contents are simply a path — a note that says look over there. It points at a name rather than at an inode, so it can point across to a different filesystem, and it can dangle uselessly if the thing it names is removed.

Why renaming is instant but copying is not

Renaming a huge file is instant. Copying the same file takes real time, and the bigger it is the longer it takes. The reason is the split between a file's name, its record, and its data.

A rename changes only the directory entry — the one line mapping a name to an inode number. The inode stays where it is, the blocks stay where they are, and not a single byte of the contents is touched; one small table gets rewritten. Moving a file within the same filesystem is the same cheap operation: the entry is unhooked from one directory and hooked into another, and the data never moves.

Copying has no such shortcut. A copy is a genuinely new file, with a new inode and its own fresh set of blocks, so every byte of data must be read and written again. It is also why moving a file onto a different device is slow while moving it within one device is instant: a move that crosses from one filesystem to another cannot be a rename at all, and is really a copy followed by a delete.

Many devices, one tree

A computer usually has more than one filesystem — one on its main drive, others on extra disks, USB sticks, or network shares — yet you reach all of them through a single tree of paths. Mounting is what makes that possible.

To mount a filesystem is to attach the filesystem on a device to the existing tree at a chosen directory, called the mount point. After mounting, that directory becomes the root of the attached filesystem: paths that pass through it lead onto the other device with no visible seam. Walk into the mount point and you have quietly stepped from one filesystem onto another.

So the single tree you see is often several filesystems stitched together at their mount points. That is why a move can be instant in one place and slow in another: staying within one mounted filesystem is a cheap rewrite of a directory entry, while crossing a mount point means copying the data onto a different device.

Surviving a crash mid-write

Writing a file is rarely a single action. Updating even one file can mean changing a data block, updating the inode, and adjusting a directory entry — several separate writes that must all land for the filesystem to stay consistent. A crash or power loss in the middle leaves some done and some not, and a filesystem caught in that half-written state can be corrupt.

A journaling filesystem guards against this by writing down its intentions first. Before making a set of changes, it records in a dedicated area — the journal — what it is about to do. Only then does it apply the changes to the filesystem proper, marking the journal entry complete once they have all landed.

If the machine dies mid-write, the journal is the record of what was supposed to happen. On restart the filesystem replays or discards the pending entries and returns to a consistent state, instead of scanning the whole disk hoping to stumble on the damage. The price is writing some things twice; the payoff is not losing the filesystem to a single bad moment.

Running out of space, or out of inodes

On a laptop a filesystem is mostly invisible until it fills up. On a server or a homelab machine it becomes something you have to watch, and it can fail in two different ways that look identical from a distance: a write is refused because the disk is full.

The obvious kind of full is running out of blocks — no free space left to hold more data. But there is a second kind. The number of inodes is often fixed when a filesystem is created, so a disk carpeted in tiny files can exhaust its inodes while plenty of block space sits unused. Every new file needs an inode, and once there are none left the write fails even though the free-space figure looks generous.

This is why there are two questions to ask, not one. df -h reports how much block space is used and free; df -i reports the same for inodes. When a machine insists it is full and the space says otherwise, the inodes are usually the answer — and knowing to look is the difference between a quick fix and a baffling afternoon.