Links and File Types: Flexible Usage

Links and File Types: Flexible Usage

Hard links vs. symbolic links (soft links). Special files in /dev as device interfaces. The inode concept and when each link type is appropriate.

Unix supports different file types—regular files, directories, links, and device files. Each type has specific characteristics and use cases. Links provide multiple names for the same file or references to files in other locations. Device files in /dev represent hardware as files—hard disks, USB drives, and terminals are accessed through standard file operations.

The inode concept separates file names from file contents. An inode stores the file metadata and storage locations, while the file name is only a reference to it. This separation enables hard links—multiple names point to the same inode and therefore to the same data.

The Inode Concept

What Is an Inode

An inode (Index Node) is a data structure that describes a file. Every file has exactly one inode, identified by a unique number. The inode contains:

  • File size
  • Permissions (rwx)
  • Owner and group
  • Timestamps (creation, modification, last access)
  • Number of hard links
  • Pointers to the data blocks on disk

The inode does not contain the file name. The file name exists in the parent directory, which associates the name with the inode number. Technically, a directory is a list of file names and their corresponding inode numbers.

$ ls -i file.txt
12345678 file.txt

The -i option displays the inode number—12345678 in this example. This number is unique within a filesystem. Two files on the same partition cannot have the same inode number.

$ stat file.txt
  File: file.txt
  Size: 2048            Blocks: 8          IO Block: 4096   regular file
Device: 801h/2049d      Inode: 12345678    Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/ michael)   Gid: ( 1000/ michael)

The stat command displays all inode information. The inode number is 12345678, the file has one link (its file name), the permissions are 0644, and the owner is michael. The data blocks on disk are referenced through the inode—not through the file name.

Directories as Lists of Inodes

A directory is a special file containing name-to-inode mappings. When you run ls /home, the system reads the /home directory file, finds the inode numbers for all entries, reads the corresponding inodes for details (size, permissions), and displays the information.

$ ls -id /home/michael
1048577 /home/michael

The directory itself also has an inode (here 1048577). Directories always contain at least two entries: . (the current directory, pointing to its own inode) and .. (the parent directory, pointing to the parent directory’s inode).

Multiple Names for One File

A hard link is an additional directory entry for an existing inode. Both names point to the same data—there is no “original” file and “link,” but rather two equivalent names for the same content.

$ echo "Test data" > original.txt
$ ln original.txt copy.txt
$ ls -li original.txt copy.txt
12345678 -rw-r--r-- 2 michael michael 10 Oct  6 18:30 copy.txt
12345678 -rw-r--r-- 2 michael michael 10 Oct  6 18:30 original.txt

Both files have the same inode number (12345678). The 2 after the permissions indicates the link count—two names refer to this inode. Changes made to copy.txt appear in original.txt and vice versa—they are not two copies, but two names for the same file.

$ echo "New line" >> copy.txt
$ cat original.txt
Test data
New line

The change made through copy.txt is immediately visible in original.txt. The inode stores the data, and both names access it.

$ rm original.txt
$ ls -li copy.txt
12345678 -rw-r--r-- 1 michael michael 21 Oct  6 18:32 copy.txt

After rm original.txt, copy.txt still exists—the link counter has dropped from 2 to 1. The inode remains, the data remains, only the name original.txt has been removed from the directory. The filesystem releases the inode and its data blocks only when the link counter reaches 0.

$ rm copy.txt
$ ls -li copy.txt
ls: cannot access 'copy.txt': No such file or directory

Now the link counter is 0—the inode is released and the data is deleted. As long as at least one hard link exists, the data remains available. This makes hard links useful for backups—even if one name is deleted accidentally, the data is still accessible through the other link.

Hard links work only within a single filesystem. A link between /home (on partition sda1) and /var (on partition sda2) is not possible because each filesystem has its own inode numbers.

$ ln /home/michael/file.txt /var/backup/file.txt
ln: failed to create hard link '/var/backup/file.txt' => '/home/michael/file.txt': Invalid cross-device link

The “cross-device link” error indicates the problem—different filesystems have different inode tables. Symbolic links are used for links across filesystem boundaries.

Hard links to directories are prohibited (except for . and ..). They would allow cycles in the filesystem tree—for example, directory A containing B while B contains A. Tools such as find or du would then recurse indefinitely.

References to Paths

A symbolic link (symlink, soft link) is a special file that contains the path to another file. Unlike hard links, symlinks do not point to inodes but to file names. They work across filesystem boundaries and can point to directories.

$ ln -s /home/michael/documents/important.txt shortcut.txt
$ ls -l shortcut.txt
lrwxrwxrwx 1 michael michael 35 Oct  6 18:45 shortcut.txt -> /home/michael/documents/important.txt

The l at the beginning (lrwxrwxrwx) indicates a symbolic link. The -> arrow shows the target. The permissions rwxrwxrwx are standard for symlinks—the actual access permissions come from the target file, not from the link itself.

$ cat shortcut.txt
Important content

Reading shortcut.txt follows the link and reads /home/michael/documents/important.txt. For the user, the difference is transparent—the file behaves like a regular file.

Symlinks can contain either absolute or relative paths:

$ ln -s /usr/bin/python3 python
$ ls -l python
lrwxrwxrwx 1 michael michael 16 Oct  6 18:50 python -> /usr/bin/python3

This link contains an absolute path (/usr/bin/python3). It works from any location—./python runs Python regardless of the current directory.

$ cd /home/michael/projects
$ ln -s ../documents/file.txt link.txt
$ ls -l link.txt
lrwxrwxrwx 1 michael michael 23 Oct  6 18:52 link.txt -> ../documents/file.txt

This link contains a relative path (../documents/file.txt). It works from /home/michael/projectslink.txt points to /home/michael/documents/file.txt. If the directory is moved, the relative link remains functional as long as the directory structure is preserved.

Absolute links point to fixed paths—they become invalid if the target is moved. Relative links remain functional when the source and target are moved together.

$ ln -s nonexistent.txt link.txt
$ ls -l link.txt
lrwxrwxrwx 1 michael michael 17 Oct  6 18:55 link.txt -> nonexistent.txt
$ cat link.txt
cat: link.txt: No such file or directory

Symlinks can point to non-existent targets—these are known as “broken links.” The system creates the link without verifying whether the target exists. Only when the link is accessed does it fail. ls -l displays the link correctly, while cat fails with “No such file or directory.”

$ find . -type l -! -exec test -e {} \; -print
./link.txt

This command finds all broken links in the current directory. -type l filters symlinks, -! -exec test -e {} \; checks whether the target exists (and negates the result), and -print displays the broken links.

$ ln -s /var/log logs
$ cd logs
$ pwd
/home/michael/logs
$ pwd -P
/var/log

The logs symlink points to /var/log. Running cd logs works because symlinks to directories are allowed. pwd displays the symbolic path (/home/michael/logs), while pwd -P displays the physical path (/var/log).

Directory symlinks are practical for frequently used paths. /usr/bin/X11 is often a symlink to /usr/bin for historical compatibility. Many tools expect programs in /usr/bin/X11, and the symlink redirects them to /usr/bin.

Use hard links when:

  • Backup protection is required (data remains available if one link is deleted)
  • The files are on the same partition (hard links cannot cross filesystem boundaries)
  • Working with regular files (not directories)
  • Completely transparent behavior is desired (no distinction between the original and the link)

Use symbolic links when:

  • Linking across filesystem boundaries
  • Creating links to directories
  • A visible distinction between the link and the target is desired
  • Flexible path redirection is needed (the target may change)

Comparison Table

Property Hard Link Symbolic Link
Works across filesystems No Yes
Works with directories No Yes
Recognizable as a link No (same inode) Yes (own inode, type l)
Target deleted Data remains available Link becomes invalid
Overhead Minimal (directory entry only) Minimal (small file containing a path)
Transparency Completely transparent Visible with ls -l

Practical Examples

Backups with Hard Links:

$ cp -al /data/project/ /backup/project-2025-10-06/

The -l option creates hard links instead of copies. The backup consumes very little additional disk space—only new or modified files are actually copied. Unchanged files are hard links to the originals. Incremental backup tools such as rsync use this technique.

Flexible Versions with Symlinks:

$ ls -l /usr/bin/python*
lrwxrwxrwx 1 root root 9 Jun 15 2024 /usr/bin/python -> python3.11
lrwxrwxrwx 1 root root 9 Jun 15 2024 /usr/bin/python3 -> python3.11
-rwxr-xr-x 1 root root 5894184 Jun 15 2024 /usr/bin/python3.11

/usr/bin/python is a symlink to python3, which in turn is a symlink to python3.11. Scripts can use #!/usr/bin/python, and the system redirects them to the configured Python version. When Python is upgraded, only the symlink changes—not every script.

Shared Configuration:

$ ln -s /opt/config/app.conf ~/.config/app/app.conf

Multiple users can share the same central configuration through symlinks. Changes made to /opt/config/app.conf take effect for every user. Individual customization is still possible through additional configuration files while the shared base configuration remains unchanged.

Special Files in /dev

Block Devices and Character Devices

The /dev directory contains device files that represent hardware. Unix treats hardware as files—read and write operations access devices instead of data stored on disk.

$ ls -l /dev/sda
brw-rw---- 1 root disk 8, 0 Oct  6 08:15 /dev/sda

The leading b indicates a block device. Block devices operate with fixed block sizes (typically 512 bytes or 4 KB) and support random access. Hard disks, SSDs, and USB drives are block devices. The numbers 8, 0 are the major and minor numbers—8 identifies the device driver (SCSI/SATA), while 0 identifies the specific instance (the first disk).

$ ls -l /dev/tty0
crw------- 1 root tty 4, 0 Oct  6 08:15 /dev/tty0

The leading c indicates a character device. Character devices operate sequentially, one character at a time. Serial ports, terminals, and sound cards are character devices. /dev/tty0 is the first virtual console—writes appear on the screen, while reads wait for keyboard input.

Useful Special Devices

/dev/null — The Black Hole:

$ echo "Unimportant output" > /dev/null
$ ls nonexistent.txt 2>/dev/null

Anything written to /dev/null disappears. This is useful for output that is not needed. The redirection 2>/dev/null sends error messages into the void so that only normal output appears on the terminal.

/dev/zero — Endless Zeros:

$ dd if=/dev/zero of=file.bin bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 0.123456 s, 849 MB/s

/dev/zero provides an endless stream of zero bytes. The dd command reads 100 MB from /dev/zero and writes it to file.bin, creating a file filled with zeros. This is useful for generating test files or overwriting sensitive data.

/dev/random and /dev/urandom — Random Data:

$ head -c 32 /dev/urandom | base64
5K7j8mP3nQ9xL2vW1R4tY8zN6A0sH5fB

/dev/urandom provides cryptographically secure random data. This command reads 32 bytes of random data and encodes them as Base64, producing a random password. /dev/random is similar but blocks when the entropy pool is empty. /dev/urandom never blocks and is sufficient for most applications.

/dev/loop* — Loop Devices:

$ sudo losetup -f
/dev/loop0
$ sudo losetup /dev/loop0 disk.img
$ sudo mount /dev/loop0 /mnt

Loop devices make image files available as block devices. An ISO image or disk image can be mounted as if it were a physical drive. losetup associates the image with a loop device, and mount makes it accessible within the filesystem.

Understanding Device Numbers

The major number identifies the device driver in the kernel, while the minor number identifies the specific device instance:

$ ls -l /dev/sd*
brw-rw---- 1 root disk 8, 0 Oct  6 08:15 /dev/sda
brw-rw---- 1 root disk 8, 1 Oct  6 08:15 /dev/sda1
brw-rw---- 1 root disk 8, 2 Oct  6 08:15 /dev/sda2
brw-rw---- 1 root disk 8,16 Oct  6 08:15 /dev/sdb

Major number 8 represents SCSI/SATA devices. Minor 0 is the first disk (sda), minor 1 is the first partition (sda1), minor 2 is the second partition (sda2), and minor 16 is the second disk (sdb). The kernel uses these numbers to communicate directly with the appropriate device driver.

Named Pipes and Sockets

Named Pipes (FIFOs)

Named pipes are special files used for interprocess communication. They allow data to flow between processes through the filesystem.

$ mkfifo mypipe
$ ls -l mypipe
prw-r--r-- 1 michael michael 0 Oct  6 19:30 mypipe

The leading p indicates a named pipe (FIFO—First In, First Out). Process A writes to mypipe, while Process B reads from it. The data flows directly from the writer to the reader without being stored on disk.

# Terminal 1:
$ echo "Message" > mypipe

# Terminal 2:
$ cat mypipe
Message

The echo command blocks until cat reads from the pipe. The data then flows through the pipe, and both commands exit. Named pipes are useful for loosely coupled processes—they do not have to start simultaneously, but one process must wait for the other.

Unix Domain Sockets

Unix domain sockets are special files that provide bidirectional communication between processes.

$ ls -l /var/run/docker.sock
srwxrwxrwx 1 root docker 0 Oct  6 08:15 /var/run/docker.sock

The leading s indicates a socket. The Docker daemon listens on this socket, and the Docker CLI communicates with the daemon through the socket file. Both sides can send and receive data, similar to network sockets but without network overhead.

File Types at a Glance

All File Type Codes

The first character in ls -l identifies the file type:

Code Type Meaning
- Regular file Regular file
d Directory Directory
l Symbolic link Symbolic link
b Block device Block-oriented device (disk)
c Character device Character-oriented device (terminal)
p Named pipe FIFO for interprocess communication
s Socket Unix domain socket

Determining File Types

The file command identifies file types by examining their contents and magic numbers rather than relying on file extensions.

$ file /bin/ls
/bin/ls: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked

$ file document.txt
document.txt: ASCII text

$ file image.jpg
image.jpg: JPEG image data, JFIF standard 1.01

$ file link.txt
link.txt: symbolic link to /home/michael/documents/important.txt

The file command reads the first bytes of a file, searches for known signatures (magic numbers), and identifies the file type. A file named .txt that actually contains JPEG data is correctly recognized as a JPEG image. This approach is more reliable than trusting file extensions.

$ find /home -inum 12345678
/home/michael/original.txt
/home/michael/backup/file.txt

This command finds every path with inode number 12345678—all hard links to the same file. The -inum option searches by inode number instead of file name.

$ find /usr/bin -type l -ls
lrwxrwxrwx 1 root root 9 Jun 15 2024 /usr/bin/python -> python3.11
lrwxrwxrwx 1 root root 9 Jun 15 2024 /usr/bin/python3 -> python3.11

The -type l option filters symbolic links. The -ls option displays detailed information, including the link target. This is useful for reviewing symlink structures in system directories.

$ find /home/michael -xtype l -delete

The -xtype l option finds symbolic links whose targets do not exist (broken links). The -delete option removes them. Use caution: broken links may be intentional because the target is expected to appear later. Always determine why a link is broken before deleting it.

Summary

Hard links provide multiple names for the same data by referencing the same inode. They work only within a single filesystem and protect data from accidental deletion. Symbolic links are flexible references to paths, work across filesystem boundaries, and can point to directories.

Special files in /dev make hardware accessible as files—block devices for disks and character devices for terminals. Named pipes and sockets enable interprocess communication through the filesystem. Together, these file types extend Unix systems far beyond ordinary files.

The next article explains mount points—how filesystems, USB drives, and network shares are attached to the directory tree.


Software and Versions Used

This discussion is based on:

  • Inode system: Standard since Unix Version 7 (1979)
  • Operating systems: All Linux distributions and BSD systems
  • Context: Universal across all Unix-like operating systems
  • As of: October 2025

Next steps:

Michael of the Dragons

develops books, software, and open frameworks around technical systems, digital independence, and durable software architectures.
More about Michael →
Links and File Types: Flexible Usage
← Next Article Mount Points and Filesystem Types
Links and File Types: Flexible Usage
Previous Article → Permissions and Ownership: Access Control