Mounting and Unmounting File Systems in Linux: Complete Guide

 

🔌Mounting and Unmounting File Systems in Linux: Complete Guide
mount(), umount(), /etc/fstab — attaching and detaching file systems in the directory tree
Topic 7 of 9
Mount/Unmount
Key Calls
mount(), umount2()
Files
/etc/fstab, /proc/mounts

Key Terms:

Mount Point mount() umount() umount2() mountflags MS_RDONLY MS_BIND /etc/fstab /proc/mounts MNT_DETACH

What is Mounting?

Linux has a single directory tree rooted at /. All filesystems — whether on your hard disk, USB, or over the network — must be attached into this tree to be accessible. This attachment is called mounting.

A mount point is any directory in the existing tree where a new filesystem is attached. After mounting, the filesystem’s root appears at that directory.

# Mount filesystem on /dev/sdb1 at /mnt/usb:
mount /dev/sdb1 /mnt/usb

# Now access files:
ls /mnt/usb            # shows files on USB

# Unmount when done:
umount /mnt/usb

📊 Single Directory Hierarchy with Mount Points
/ (root) — sda6 ext4 filesystem
bin etc usr home ← sda8 ext3 proc ← procfs sys ← sysfs windows ← sda1 vfat tmp
avr/
mtk/
→ test/ ← sda9 reiserfs
C/
explorer.exe

Highlighted cells with ← are mount points where different filesystems are attached into the tree.

⚙️ The mount() System Call
#include <sys/mount.h>

int mount(const char *source,   /* device or special source */
          const char *target,   /* mount point directory */
          const char *fstype,   /* "ext4", "vfat", "tmpfs" ... */
          unsigned long mountflags,  /* OR'd flags from table below */
          const void *data);    /* fs-specific options string */
/* Returns 0 on success, -1 on error */

Key mountflags:

Flag Purpose
MS_RDONLY Read-only mount — no new files can be created
MS_NOSUID Disable setuid/setgid programs — security feature
MS_NODEV Disallow access to device files on this FS
MS_NOEXEC Don’t allow programs to execute from this FS
MS_SYNCHRONOUS All writes synchronous (like O_SYNC on every file)
MS_REMOUNT Change flags on already-mounted FS (e.g., ro to rw)
MS_BIND Bind mount: make a directory visible at another location
MS_NOATIME Don’t update access time — improves read performance
MS_RELATIME Update atime only if older than mtime (default since 2.6.30)
MS_MOVE Atomically move a mount point to a new location
MS_REC Apply recursively to all submounts in a subtree
MS_DIRSYNC Directory updates synchronous (create/rename/link)

💻 Code Example 1: mount() System Call Usage
#include <stdio.h>
#include <sys/mount.h>

int main(void) {
    /* Example 1: Mount ext4 filesystem read-write */
    if (mount("/dev/sdb1",      /* source: block device */
              "/mnt/data",       /* target: mount point */
              "ext4",            /* filesystem type */
              0,                 /* flags: none = read-write */
              NULL)              /* no extra options */
        == -1) {
        perror("mount ext4");
        return 1;
    }
    printf("Mounted /dev/sdb1 at /mnt/data\n");

    /* Example 2: Mount read-only */
    if (mount("/dev/sdb1", "/mnt/data_ro", "ext4",
              MS_RDONLY,    /* read-only flag */
              NULL) == -1) {
        perror("mount read-only");
    }

    /* Example 3: Remount existing mount as read-write */
    /* (e.g., root fs is initially mounted ro, then remounted rw) */
    if (mount("/dev/sda1", "/", NULL,
              MS_REMOUNT,   /* keep target=/,  */
              NULL) == -1) {
        /* MS_REMOUNT ignores fstype, uses existing */
        perror("remount");
    }

    /* Example 4: Mount with options string */
    if (mount("/dev/sdb1", "/mnt/fat", "vfat",
              MS_NOEXEC | MS_NOSUID,
              "iocharset=utf8,umask=022") == -1) {
        perror("mount vfat");
    }

    /* Example 5: Mount tmpfs (no device needed) */
    if (mount("tmpfs", "/tmp/ramdisk", "tmpfs",
              0,
              "size=64m") == -1) {  /* 64MB size limit */
        perror("mount tmpfs");
    }

    return 0;
}
/* Must be run as root (CAP_SYS_ADMIN) */

💻 Code Example 2: Parse /proc/mounts to List All Mounted Filesystems
#include <stdio.h>
#include <string.h>
#include <mntent.h>   /* getmntent(), setmntent() */

/* /proc/mounts format (6 fields):
   device  mountpoint  fstype  options  dump  fsck_order */

void list_mounts_manual(void) {
    FILE *fp;
    char line[512];
    char device[128], mountpoint[128], fstype[32];
    char options[256];
    int dump, fsck;

    fp = fopen("/proc/mounts", "r");
    if (!fp) { perror("fopen /proc/mounts"); return; }

    printf("%-20s %-20s %-10s %s\n",
           "Device", "Mount Point", "FS Type", "Options");
    printf("%-20s %-20s %-10s %s\n",
           "------", "-----------", "-------", "-------");

    while (fgets(line, sizeof(line), fp)) {
        sscanf(line, "%127s %127s %31s %255s %d %d",
               device, mountpoint, fstype, options, &dump, &fsck);
        printf("%-20s %-20s %-10s %s\n",
               device, mountpoint, fstype, options);
    }
    fclose(fp);
}

/* Better: use mntent library (reads /etc/fstab or /proc/mounts) */
void list_mounts_mntent(void) {
    FILE *fp;
    struct mntent *mnt;

    fp = setmntent("/proc/mounts", "r");
    if (!fp) { perror("setmntent"); return; }

    printf("\n=== Via getmntent() ===\n");
    while ((mnt = getmntent(fp)) != NULL) {
        printf("%-15s on %-20s type %-10s (%s)\n",
               mnt->mnt_fsname,   /* device */
               mnt->mnt_dir,      /* mount point */
               mnt->mnt_type,     /* fs type */
               mnt->mnt_opts);    /* options */
    }

    endmntent(fp);
}

int main(void) {
    list_mounts_manual();
    list_mounts_mntent();
    return 0;
}

/* Sample output:
Device               Mount Point          FS Type    Options
------               -----------          -------    -------
/dev/sda1            /                    ext4       rw,relatime
tmpfs                /tmp                 tmpfs      rw,nosuid
/dev/sdb1            /mnt/usb             vfat       rw,uid=1000
*/

⚙️ umount() and umount2()
#include <sys/mount.h>

int umount(const char *target);  /* target = mount point */

int umount2(const char *target, int flags);
/* flags: MNT_DETACH, MNT_EXPIRE, MNT_FORCE, UMOUNT_NOFOLLOW */
MNT_DETACH
Lazy unmount. Marks the mount so no new accesses allowed, but existing processes continue using it. Actually unmounts when all processes close their files.
MNT_EXPIRE
Mark as “expirable”. First call fails with EAGAIN but marks it. If not used, a second call unmounts it. Good for automounting.
MNT_FORCE
Force unmount even if busy (NFS only). May cause data loss. Use with caution.
⚠️ You cannot unmount a “busy” filesystem — if any process has open files on it or its cwd is within it, umount() returns EBUSY.

💻 Code Example 3: umount() and umount2()
#include <stdio.h>
#include <sys/mount.h>
#include <errno.h>
#include <string.h>

int safe_unmount(const char *mountpoint) {
    /* Try normal unmount first */
    if (umount(mountpoint) == 0) {
        printf("Unmounted %s successfully\n", mountpoint);
        return 0;
    }

    if (errno == EBUSY) {
        printf("%s is busy. Trying lazy unmount...\n", mountpoint);

        /* MNT_DETACH: stop new access, let existing finish */
        if (umount2(mountpoint, MNT_DETACH) == 0) {
            printf("Lazy unmount done — will complete when idle\n");
            return 0;
        }
    }

    fprintf(stderr, "umount failed: %s\n", strerror(errno));
    return -1;
}

int main(void) {
    /* Normal unmount */
    safe_unmount("/mnt/usb");

    /* Check if anything is keeping fs busy before unmounting:
       lsof +D /mnt/usb        shows open files
       fuser -mv /mnt/usb      shows processes using it
    */

    /* Remount read-only before unmounting
       (useful for safely removing storage) */
    if (mount("", "/mnt/data", "",
              MS_REMOUNT | MS_RDONLY, NULL) == 0) {
        printf("Remounted read-only, safe to remove\n");
        umount("/mnt/data");
    }

    return 0;
}

/* Shell equivalents:
   umount /mnt/usb
   umount -l /mnt/usb      # lazy
   umount -f /mnt/nfs      # force (NFS only)
   
   Find what's using a mount:
   fuser -mv /mnt/usb
   lsof +D /mnt/usb
*/

💻 Code Example 4: Parse /etc/fstab
#include <stdio.h>
#include <mntent.h>

/* /etc/fstab: static table of filesystems to mount at boot.
   Same format as /proc/mounts but maintained by admin, not kernel. */

int main(void) {
    FILE *fp;
    struct mntent *mnt;

    fp = setmntent("/etc/fstab", "r");
    if (!fp) { perror("setmntent /etc/fstab"); return 1; }

    printf("Filesystems defined in /etc/fstab:\n\n");
    printf("%-20s %-15s %-8s %-20s %s %s\n",
           "Device", "MountPoint", "FSType", "Options",
           "Dump", "Pass");

    while ((mnt = getmntent(fp)) != NULL) {
        /* Skip comments (lines starting with #) — handled by getmntent */
        printf("%-20s %-15s %-8s %-20s %d    %d\n",
               mnt->mnt_fsname,
               mnt->mnt_dir,
               mnt->mnt_type,
               mnt->mnt_opts,
               mnt->mnt_freq,    /* dump frequency */
               mnt->mnt_passno); /* fsck pass order */

        /* Check for specific options */
        if (hasmntopt(mnt, "ro"))
            printf("   ^ Note: mounted READ-ONLY\n");
        if (hasmntopt(mnt, "noauto"))
            printf("   ^ Note: not auto-mounted at boot\n");
    }

    endmntent(fp);

    /* Show how to find specific entry */
    printf("\n--- Looking for swap entries ---\n");
    fp = setmntent("/etc/fstab", "r");
    while ((mnt = getmntent(fp)) != NULL) {
        if (strcmp(mnt->mnt_type, "swap") == 0)
            printf("Swap: %s\n", mnt->mnt_fsname);
    }
    endmntent(fp);

    return 0;
}

/* /etc/fstab example:
# <device>         <mount>  <type>  <options>           <dump> <pass>
UUID=abc-123       /        ext4    errors=remount-ro    0       1
UUID=def-456       /home    ext4    defaults             0       2
UUID=ghi-789       none     swap    sw                   0       0
tmpfs              /tmp     tmpfs   defaults,size=512m   0       0
*/

🎯 Interview Questions — Mounting and Unmounting

Q1. What is a mount point? What happens to files in a directory when a filesystem is mounted on it?

A mount point is a directory in the existing filesystem tree where a new filesystem is attached. Files that were in the mount point directory before mounting become temporarily hidden — only the new filesystem’s contents are visible. After unmounting, the original contents reappear. This is called mount stacking (since Linux 2.4).

Q2. What privilege is required to call mount()? Why?

CAP_SYS_ADMIN (effectively root). Mounting allows arbitrary filesystem code to run in kernel space and gives access to raw block devices, which could be used to bypass filesystem permissions. Unprivileged users can mount using FUSE or user-mountable entries in /etc/fstab.

Q3. What is the difference between MS_NOATIME and MS_RELATIME?

MS_NOATIME never updates the access timestamp (atime) — best performance. MS_RELATIME (default since Linux 2.6.30) updates atime only if it’s older than mtime or ctime, or older than 24 hours. MS_RELATIME gives most of the performance benefit while still letting programs detect recently-read files.

Q4. What is a lazy unmount (MNT_DETACH)?

With MNT_DETACH, the mount point is immediately marked so no new accesses are allowed, but processes that already have the filesystem open can continue using it. The filesystem is fully unmounted when all processes close their files. Useful for graceful migration off a busy filesystem without taking the system offline.

Q5. What is the difference between /proc/mounts, /etc/mtab, and /etc/fstab?

/proc/mounts is a kernel interface (always accurate, reflects real mount state). /etc/mtab is maintained by mount(8) commands and may be slightly more detailed but can become stale if mounts are done programmatically without updating it. /etc/fstab is manually edited by the admin and defines what should be mounted at boot; it’s not automatically updated by mounting/unmounting.

Q6. What does MS_REMOUNT do and when would you use it?

MS_REMOUNT changes the mount flags for an already-mounted filesystem without unmounting and remounting it. Use case: the root filesystem is mounted read-only by the kernel early in boot; init scripts use MS_REMOUNT to make it read-write. Another use: safely switching a filesystem read-only before maintenance without unmounting.

Leave a Reply

Your email address will not be published. Required fields are marked *