Advanced Mounts and tmpfs in Linux: Complete Guide

 

🔗 Advanced Mounts and tmpfs in Linux: Complete Guide
Bind mounts, mount stacking, recursive mounts, and RAM-based filesystems
Topic 8 of 9
Advanced Mounts
Features
Bind, tmpfs, Stack
Use Cases
Containers, chroot, RAM FS

Key Terms:

Bind Mount MS_BIND Mount Stacking Multiple Mount Points Recursive Bind MS_REC tmpfs Per-Mount Flags chroot jail Container

🔁 Mounting a Filesystem at Multiple Locations

Since Linux 2.4, a single filesystem can be mounted at multiple directories simultaneously. Changes via one mount point are immediately visible via the other — they share the same underlying data.

# Mount same device at two different directories:
mount /dev/sda12 /testfs
mount /dev/sda12 /demo

# Create file via first mount point:
touch /testfs/myfile

# Instantly visible via second mount point:
ls /demo
# → lost+found  myfile  (myfile is there!)

# Both show up in /proc/mounts:
# /dev/sda12 on /testfs type ext3 (rw)
# /dev/sda12 on /demo   type ext3 (rw)
📌 Because a device can now be at multiple mount points, umount() in Linux 2.4+ takes a mount point path, not a device name.

📚 Mount Stacking (Multiple Mounts on Same Point)

Linux also allows multiple filesystems to be mounted on the same mount point. The newest mount hides the older one. Unmounting reveals the previous one.

Directory: /testfs
Top of Stack
sda13 reiserfs
(currently visible)
↓ unmount Now visible
sda12 ext3
(was hidden)
Bottom of Stack
sda12 ext3
(hidden while sda13 is on top)
myfile
(created before sda13 was mounted)
# Example: Stack two filesystems on /testfs
mount /dev/sda12 /testfs     # First mount
touch /testfs/myfile         # Create file in first FS

mount /dev/sda13 /testfs     # Stack second mount on top
# Now /testfs shows sda13 — myfile is hidden!
ls /testfs   # shows files from sda13 only

umount /testfs               # Pop top mount
ls /testfs   # now shows sda12 again — myfile is back!

# Use case: smoothly switch running services to new filesystem
# without downtime (combine with MNT_DETACH)

🔗 Bind Mounts (MS_BIND)

A bind mount makes a directory (or file) visible at another location in the filesystem tree. Not a copy — the same inode, same data, changes in one location appear in the other.

✅ Hard Link vs Bind Mount:

  • Hard links: only for files, same filesystem
  • Bind mounts: works for directories, can cross filesystem boundaries, works across chroot jails

Key use cases:

  • chroot jail setup (bind /lib into jail)
  • Container filesystem isolation
  • Accessing same data from two different paths
  • Read-only view of a directory
# BIND MOUNT EXAMPLES:

# 1. Bind a directory: d1 visible via d2
mkdir d1 d2
mount --bind d1 d2
touch d1/hello
ls d2        # → hello (visible!)
touch d2/world
ls d1        # → hello world (visible in both!)

# 2. Bind a single file
echo "original" > f1
touch f2
mount --bind f1 f2
echo "updated" >> f2
cat f1    # → original\nupdated (same file!)

# 3. Bind mount read-only view of a directory
mount --bind /srv/data /mnt/readonly_view
mount -o remount,bind,ro /mnt/readonly_view
# Now /mnt/readonly_view shows same files but read-only

# 4. Use in chroot jail — instead of copying /lib:
mount --bind /lib /jail/lib     # shares system libs
mount --bind /usr /jail/usr     # shares /usr

💻 Code Example 1: Bind Mount with mount() Syscall
#include <stdio.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <unistd.h>

int main(void) {
    /* Create directories for bind mount demo */
    mkdir("/tmp/source_dir", 0755);
    mkdir("/tmp/target_dir", 0755);

    /* Create a file in source */
    FILE *f = fopen("/tmp/source_dir/test.txt", "w");
    fprintf(f, "Bind mount works!\n");
    fclose(f);

    /* Create bind mount: source_dir visible via target_dir */
    /* MS_BIND: when specified, fstype/data args ignored */
    if (mount("/tmp/source_dir",   /* source */
              "/tmp/target_dir",   /* target (mount point) */
              NULL,                /* fstype: ignored for bind */
              MS_BIND,             /* bind mount flag */
              NULL) == -1) {       /* data: ignored */
        perror("mount --bind (need root)");
        return 1;
    }

    printf("Bind mount created.\n");
    printf("File visible at target? ");

    /* Verify: read file through target path */
    FILE *rf = fopen("/tmp/target_dir/test.txt", "r");
    if (rf) {
        char buf[64];
        fgets(buf, sizeof(buf), rf);
        printf("YES: %s", buf);
        fclose(rf);
    }

    /* Cleanup */
    umount("/tmp/target_dir");
    printf("Unmounted.\n");

    return 0;
}
/* Run as root */

💻 Code Example 2: Recursive Bind Mount (MS_BIND | MS_REC)
/* Recursive bind mount replicates all submounts too.
   Non-recursive only binds the top directory.
   
   Example scenario:
     /top                   (ext4 mounted from sda12)
     /top/sub               (reiserfs mounted from sda13)
   
   mount --bind     top dir1  → dir1/sub is empty (submount NOT copied)
   mount --rbind    top dir2  → dir2/sub has contents (submount copied)
*/

#include <stdio.h>
#include <sys/mount.h>
#include <sys/stat.h>

int main(void) {
    /* Normal bind: no submount replication */
    if (mount("/top", "/dir1", NULL,
              MS_BIND,          /* no MS_REC */
              NULL) == 0) {
        printf("Normal bind: /dir1/sub is EMPTY\n");
        printf("(submount from /top/sub not included)\n");
        umount("/dir1");
    }

    /* Recursive bind: submounts are replicated */
    if (mount("/top", "/dir2", NULL,
              MS_BIND | MS_REC,  /* MS_REC = recursive */
              NULL) == 0) {
        printf("Recursive bind: /dir2/sub has submount\n");
        printf("(all submounts from /top replicated)\n");
        /* Unmounting recursive bind requires unmounting submounts first */
        umount("/dir2/sub");
        umount("/dir2");
    }

    return 0;
}

/* Shell equivalents:
   mount --bind  /top /dir1     # non-recursive
   mount --rbind /top /dir2     # recursive

   Use case: Docker/container rootfs construction
   uses recursive bind mounts extensively
*/

⚡ tmpfs — Virtual Memory Filesystem

tmpfs is a filesystem that lives entirely in memory (RAM + swap if RAM fills up). It’s much faster than disk I/O. All data is lost when unmounted or on system crash.

✅ Use tmpfs for:

  • /tmp (compiler temp files)
  • /run (runtime state files)
  • /dev/shm (POSIX shared memory)
  • Browser cache
  • Build directories

⚠️ Don’t use for:

  • Data that must survive reboots
  • Very large files (eats RAM)
  • Anything requiring persistence
# Create a tmpfs filesystem:
mount -t tmpfs myramdisk /mnt/ramdisk

# With 128MB size limit:
mount -t tmpfs -o size=128m myramdisk /mnt/ramdisk

# /tmp as tmpfs (also allows mount stacking):
mount -t tmpfs newtmp /tmp

# Check it:
cat /proc/mounts | grep tmpfs

# Linux automatically uses tmpfs for:
# /dev/shm    - POSIX shared memory
# Internal    - System V shared memory
# /run        - Runtime data (PID files, sockets)

💻 Code Example 3: Create and Use tmpfs Programmatically
#include <stdio.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>

/* Benchmark: tmpfs vs disk write speed */
double write_test(const char *dir, int num_files, int file_size) {
    char path[256];
    char *buf;
    int i, fd;
    struct timespec t1, t2;

    buf = malloc(file_size);
    if (!buf) return -1;
    memset(buf, 0xAB, file_size);

    clock_gettime(CLOCK_MONOTONIC, &t1);

    for (i = 0; i < num_files; i++) {
        snprintf(path, sizeof(path), "%s/file_%04d.tmp", dir, i);
        fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (fd == -1) break;
        write(fd, buf, file_size);
        close(fd);

        /* Cleanup */
        unlink(path);
    }

    clock_gettime(CLOCK_MONOTONIC, &t2);

    free(buf);

    double elapsed = (t2.tv_sec - t1.tv_sec) +
                     (t2.tv_nsec - t1.tv_nsec) / 1e9;
    return elapsed;
}

int main(void) {
    const int NUM_FILES = 1000;
    const int FILE_SIZE = 4096;

    /* Mount tmpfs for testing (as root) */
    mkdir("/tmp/ramdisk_test", 0755);
    mount("bench_tmpfs", "/tmp/ramdisk_test", "tmpfs",
          0, "size=64m");

    printf("Writing %d files of %d bytes each...\n\n",
           NUM_FILES, FILE_SIZE);

    double t_tmpfs = write_test("/tmp/ramdisk_test",
                                NUM_FILES, FILE_SIZE);
    double t_disk  = write_test("/tmp",
                                NUM_FILES, FILE_SIZE);

    printf("tmpfs time:  %.3f seconds\n", t_tmpfs);
    printf("disk time:   %.3f seconds\n", t_disk);
    printf("Speedup:     %.1fx faster with tmpfs\n",
           t_disk / t_tmpfs);

    umount("/tmp/ramdisk_test");
    rmdir("/tmp/ramdisk_test");

    return 0;
}
/* Typical: tmpfs is 5-50x faster than disk for small file I/O */

💻 Code Example 4: Per-Mount Flags (Same Device, Different Permissions)
/* Since Linux 2.4, flags like MS_NOEXEC, MS_NOSUID, MS_RDONLY
   can be set per-mount, not per-filesystem.
   Same device mounted twice can have different flags! */

#include <stdio.h>
#include <sys/mount.h>

int main(void) {
    /* Mount same device at two locations with different flags */

    /* Location 1: normal read-write (developers work here) */
    mount("/dev/sda12", "/work", "ext4", 0, NULL);

    /* Location 2: noexec (untrusted users access this) */
    mount("/dev/sda12", "/public", "ext4",
          MS_NOEXEC | MS_NOSUID,  /* no programs, no setuid */
          NULL);

    /* Now:
       cp /bin/echo /work/echo     # works
       /work/echo "hello"          # works (normal mount)
       /public/echo "hello"        # PERMISSION DENIED (noexec!)
    */

    /* Verify with /proc/mounts:
       /dev/sda12 /work   ext4 rw 0 0
       /dev/sda12 /public ext4 rw,noexec,nosuid 0 0
    */

    umount("/work");
    umount("/public");
    return 0;
}

/* Shell equivalent:
   mount /dev/sda12 /work
   mount -o noexec,nosuid /dev/sda12 /public
*/

🎯 Interview Questions — Advanced Mounts and tmpfs

Q1. What is a bind mount and how does it differ from a hard link?

A bind mount makes a directory (or file) appear at a second location in the filesystem tree. Unlike hard links: (1) bind mounts work for directories, hard links don’t; (2) bind mounts can cross filesystem boundaries; (3) bind mounts work across chroot jails. Both share the same underlying data — changes in one location are visible in the other.

Q2. What is mount stacking and what is it useful for?

Mount stacking means mounting multiple filesystems on the same mount point. The newest mount hides the older one. Unmounting reveals the previous one. Use case: gracefully migrating services to a new filesystem — mount the new one over the old, let processes using the old one finish (via MNT_DETACH), then unmount the old one. No downtime needed.

Q3. What is the difference between MS_BIND and MS_BIND | MS_REC?

MS_BIND creates a bind mount of the directory but does NOT replicate any submounts under it. MS_BIND | MS_REC (recursive bind) replicates the entire mount subtree including all submounts under the source. Used in container setup to bind an entire filesystem hierarchy with all its mounts.

Q4. What is tmpfs? How does it differ from a ramdisk?

tmpfs is a virtual memory filesystem that uses both RAM and swap space. It grows and shrinks dynamically. A traditional ramdisk (ramfs) uses only RAM and has a fixed size. tmpfs is more efficient because: unused space is available to the rest of the system, and it can spill to swap if RAM fills up. Data in tmpfs is lost on unmount or crash.

Q5. What Linux kernel feature allows containers (like Docker) to give each process its own mount namespace?

Per-process mount namespaces, introduced in Linux 2.4.19. Each process can have its own set of mount points. When created with CLONE_NEWNS (via clone() or unshare()), a process gets a copy of the parent’s mount namespace which it can modify independently. Docker/containers use this plus bind mounts to build isolated filesystem views.

Q6. What are the two uses of tmpfs that the kernel creates automatically?

1) An internal, invisible tmpfs for implementing System V shared memory (shmget/shmat) and shared anonymous memory mappings (mmap MAP_SHARED). 2) A tmpfs mounted at /dev/shm for glibc’s implementation of POSIX shared memory (shm_open()) and POSIX semaphores.

Leave a Reply

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