Linux Basic β€” Linux File System Practice Exercises

7 minute read

Linux File System β€” Practice Exercises

These exercises reinforce everything covered in Chapter 6: the FHS, paths, file types, inodes, links, and disk usage. Work through them in order on a real Linux system or a VM.


Exercise 1 β€” FHS Exploration

Goal: Navigate the Filesystem Hierarchy Standard and locate key files for three installed services.

Task:

  1. Find the main configuration file for ssh (the SSH daemon)
  2. Find where ssh logs are written
  3. Find the sshd binary itself
  4. Find the sshd systemd unit file
  5. Find the runtime PID file (if any) for sshd

Bonus: Repeat the same five steps for cron and nginx (if installed).

πŸ’‘ Show Answer
# 1. SSH configuration file
cat /etc/ssh/sshd_config | head -20
# Location: /etc/ssh/sshd_config

# 2. SSH logs β€” check in /var/log
grep -r sshd /etc/rsyslog.conf 2>/dev/null || echo "check journalctl"
sudo journalctl -u ssh --no-pager | tail -10
# On older distros: /var/log/auth.log or /var/log/secure

# 3. sshd binary location
which sshd
# /usr/sbin/sshd
ls -l /usr/sbin/sshd

# 4. Systemd unit file
systemctl show ssh --property=FragmentPath
# Or:
find /lib/systemd /etc/systemd -name "ssh*" 2>/dev/null
# /lib/systemd/system/ssh.service

# 5. Runtime PID file
cat /run/sshd.pid 2>/dev/null || echo "No PID file β€” check /run"
ls /run/ | grep ssh

# Bonus β€” cron
ls /etc/cron* /var/spool/cron/
which cron crond 2>/dev/null
find /lib/systemd -name "cron*" 2>/dev/null

# Bonus β€” nginx (if installed)
ls /etc/nginx/
ls /var/log/nginx/
which nginx

Exercise 2 β€” Path Navigation Lab

Goal: Practice converting between absolute and relative paths without a GUI.

Setup:

mkdir -p /tmp/pathlab/{alpha/{a1,a2},beta/{b1,b2},gamma}
touch /tmp/pathlab/alpha/a1/file1.txt
touch /tmp/pathlab/beta/b2/file2.txt
touch /tmp/pathlab/gamma/file3.txt
cd /tmp/pathlab/alpha/a1

Tasks (you are in /tmp/pathlab/alpha/a1/):

  1. Print the absolute path to file2.txt without using realpath
  2. Copy file1.txt to beta/b1/ using only a relative path
  3. Create a symlink in /tmp/pathlab/gamma/ pointing to file1.txt using a relative target path
  4. Use realpath to confirm the symlink resolves correctly
  5. Move to /tmp/pathlab/beta/b1/ and cat file1.txt using a relative path that goes up two directories
πŸ’‘ Show Answer
# 1. Absolute path to file2.txt
echo /tmp/pathlab/beta/b2/file2.txt
# (manually constructed β€” absolute paths start from /)

# 2. Copy using relative path (from /tmp/pathlab/alpha/a1/)
cp file1.txt ../../beta/b1/
ls /tmp/pathlab/beta/b1/
# file1.txt

# 3. Symlink in gamma using a relative target
# From gamma's perspective, file1.txt is at ../alpha/a1/file1.txt
ln -s ../alpha/a1/file1.txt /tmp/pathlab/gamma/link1.txt
ls -la /tmp/pathlab/gamma/
# link1.txt -> ../alpha/a1/file1.txt

# 4. Verify with realpath
realpath /tmp/pathlab/gamma/link1.txt
# /tmp/pathlab/alpha/a1/file1.txt

# 5. Cat from /tmp/pathlab/beta/b1/
cd /tmp/pathlab/beta/b1/
cat ../../alpha/a1/file1.txt
# (empty file, but no error)

# Cleanup
rm -rf /tmp/pathlab

Exercise 3 β€” Inode Investigation

Goal: Use stat and ls -i to compare regular files, directories, and symlinks.

Tasks:

  1. Create three files and compare their inode numbers β€” are they consecutive?
  2. Create a directory and note how stat shows its link count β€” why is it at least 2?
  3. Compare stat output for a regular file vs a symlink to that file β€” note the differences
πŸ’‘ Show Answer
# 1. Create files and compare inodes
touch /tmp/inode_a /tmp/inode_b /tmp/inode_c
ls -i /tmp/inode_a /tmp/inode_b /tmp/inode_c
# Inode numbers are assigned by the filesystem β€” not always consecutive
# They may or may not be adjacent depending on filesystem state

# 2. Directory link count
mkdir /tmp/testdir
stat /tmp/testdir | grep Links
# Links: 2
# Why 2? Every directory has:
# - One entry in its parent (the dirname itself)
# - One '.' entry inside itself (pointing back to the same inode)
# Each subdirectory adds another '..' entry, increasing the parent's link count by 1

mkdir /tmp/testdir/subA /tmp/testdir/subB
stat /tmp/testdir | grep Links
# Links: 4   (testdir itself = 1, . inside = 1, subA/.., subB/..)

# 3. Regular file vs symlink inode comparison
echo "hello inode" > /tmp/original.txt
ln -s /tmp/original.txt /tmp/symlink.txt

stat /tmp/original.txt
# Inode: XXXXX    Size: 12    Links: 1    regular file
stat /tmp/symlink.txt
# Inode: YYYYY    Size: 16    Links: 1    symbolic link -> /tmp/original.txt
# - DIFFERENT inode numbers (symlink is its own filesystem object)
# - symlink Size = length of the path string "/tmp/original.txt"

# Cleanup
rm -rf /tmp/testdir /tmp/inode_a /tmp/inode_b /tmp/inode_c /tmp/original.txt /tmp/symlink.txt

Goal: Create and test hard links and symlinks, observe the cross-filesystem limitation, and find dangling symlinks.

Tasks:

  1. Create a file and a hard link to it. Prove they share an inode. Delete the original β€” does the content survive?
  2. Attempt a hard link to /etc/hostname from /tmp/ β€” note the error
  3. Create a symlink and then remove its target β€” observe the dangling state with ls and find
  4. Atomically switch a symlink from version 1 to version 2 using the deployment pattern
πŸ’‘ Show Answer
# 1. Hard link exercise
echo "content to keep" > /tmp/original.txt
ln /tmp/original.txt /tmp/hardlink.txt

ls -li /tmp/original.txt /tmp/hardlink.txt
# Both show same inode number, Link count = 2

rm /tmp/original.txt
cat /tmp/hardlink.txt
# content to keep   ← data survives, inode still has 1 link

ls -li /tmp/hardlink.txt
# Link count is now 1

# 2. Cross-filesystem hard link (will fail)
ln /etc/hostname /tmp/hostname_link
# ln: failed to create hard link '/tmp/hostname_link' => '/etc/hostname':
# Invalid cross-device link
# /etc is on the root filesystem; /tmp may be on tmpfs β€” different devices

# 3. Dangling symlink
echo "I exist" > /tmp/real.txt
ln -s /tmp/real.txt /tmp/mylink.txt
rm /tmp/real.txt

ls -la /tmp/mylink.txt
# lrwxrwxrwx 1 ubuntu ubuntu 12 Apr  1 10:00 /tmp/mylink.txt -> /tmp/real.txt
# (shown in red in most terminals)

cat /tmp/mylink.txt
# cat: /tmp/mylink.txt: No such file or directory

find /tmp -xtype l
# /tmp/mylink.txt   ← found by -xtype l (type is symlink but target missing)

# 4. Deployment pattern
mkdir -p /tmp/deploy/app-v1 /tmp/deploy/app-v2
echo "version 1" > /tmp/deploy/app-v1/main.sh
echo "version 2" > /tmp/deploy/app-v2/main.sh

ln -s /tmp/deploy/app-v1 /tmp/deploy/current
cat /tmp/deploy/current/main.sh
# version 1

ln -sfn /tmp/deploy/app-v2 /tmp/deploy/current
cat /tmp/deploy/current/main.sh
# version 2

# Rollback
ln -sfn /tmp/deploy/app-v1 /tmp/deploy/current
cat /tmp/deploy/current/main.sh
# version 1

# Cleanup
rm -rf /tmp/deploy /tmp/mylink.txt /tmp/hardlink.txt

Exercise 5 β€” Disk Detective

Goal: Use df and du to identify where disk space is being consumed.

Tasks:

  1. Check overall disk space with df -h β€” identify the filesystem with the highest Use%
  2. Check inode usage with df -i β€” are any filesystems at risk of running out of inodes?
  3. Find the top 5 directories consuming the most space under /var
  4. Find any files over 50 MB anywhere on the system (excluding /proc and /sys)
  5. Check if any deleted-but-open files are consuming space
πŸ’‘ Show Answer
# 1. Overall disk space
df -h
# Look for Use% near 80% or higher β€” those need attention

# 2. Inode usage
df -i
# IUse% > 80% indicates risk
# IUse% at 100% means no new files can be created on that filesystem

# 3. Top directories under /var
du -h --max-depth=2 /var 2>/dev/null | sort -rh | head -10
# Common space consumers:
# /var/lib/docker   β€” Docker images and containers
# /var/log/journal  β€” systemd logs
# /var/cache/apt    β€” apt package cache
# /var/lib/mysql    β€” MySQL data files (if installed)

# Drill deeper into the biggest one found:
du -sh /var/log/* | sort -rh | head -5

# 4. Large files (excluding /proc and /sys)
find / -xdev -type f -size +50M \
  -not -path "/proc/*" \
  -not -path "/sys/*" \
  -printf '%s\t%p\n' 2>/dev/null \
  | sort -rn \
  | awk '{printf "%.0fMB\t%s\n", $1/1024/1024, $2}' \
  | head -10

# 5. Deleted-but-open files (consuming space even though unlinked)
sudo lsof +L1 2>/dev/null | grep DEL
# If output is long, check total space they consume:
sudo lsof +L1 2>/dev/null | grep DEL | awk '{print $7}' | \
  awk '{sum += $1} END {print "Total deleted-but-open:", sum/1024/1024, "MB"}'

# Fix: restart the process that has the file open
# Example: if nginx has a deleted log open:
# sudo systemctl restart nginx

Challenge: Full Disk Simulation

Advanced β€” simulate and resolve a disk-full situation on a practice VM (do NOT do this on a production system):

# WARNING: This will fill /tmp β€” do on a throwaway VM only
# Fill up /tmp with a large file
dd if=/dev/zero of=/tmp/bigfile bs=1M count=1000   # 1 GB

# Observe:
df -h /tmp
# Now try to create a file in /tmp
touch /tmp/newfile   # May fail if /tmp is its own partition

# Resolution:
rm /tmp/bigfile
df -h /tmp

Summary Checklist

After completing these exercises you should be comfortable with:

  • βœ… Locating configuration, logs, and binaries for any installed service using FHS knowledge
  • βœ… Constructing and converting between absolute and relative paths
  • βœ… Using stat and ls -i to inspect inode metadata
  • βœ… Creating and managing hard links and symlinks
  • βœ… Implementing the versioned deployment symlink pattern
  • βœ… Finding dangling symlinks with find -xtype l
  • βœ… Using df -h and df -i to assess disk and inode health
  • βœ… Using du -sh * and sort -rh to find space consumers
  • βœ… Detecting deleted-but-open files with lsof +L1

← Back to Disk Usage: df and du ↑ Chapter Home: Linux File System

Updated: