Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

maskvol

繁體中文 | License

maskvol creates writable development masks over read-only source volumes. It is designed for workflows where containers need realistic production-like data, but every development write must stay isolated until an operator reviews and explicitly merges it.

The tool is a single Bash CLI built on Linux overlayfs. It works with NFS shares, local directories, Docker bind mounts, Docker volumes, and other mounted filesystems.

What It Does

SOURCE -- read-only bind -->  WORK_ROOT/<id>/lower --+
                              WORK_ROOT/<id>/upper --+--> overlayfs --> TARGET
                              WORK_ROOT/<id>/work  --+
  • SOURCE is the original data directory.
  • TARGET is the writable merged view used by containers or developers.
  • WORK_ROOT stores mask-local state for one TARGET: lower, upper, and work.
  • WORK_ROOT/<id>/lower is a read-only bind mount of SOURCE. It is the baseline view that overlayfs reads from.
  • WORK_ROOT/<id>/upper stores every new file, modified file, copied-up file, and overlay delete marker created through TARGET.
  • WORK_ROOT/<id>/work is overlayfs scratch space required by the kernel. It must be on the same filesystem as upper and is not user data.
  • The original SOURCE is read-only bound and is not written by overlay use.
  • Changes can be reviewed with diff, exported with export, or merged with merge.

How It Works

maskvol up creates the three overlay inputs under WORK_ROOT/<id> and mounts the merged filesystem at TARGET. Containers and users interact only with TARGET; overlayfs decides whether each operation reads from lower, writes to upper, or uses work internally.

Operation through TARGET SOURCE WORK_ROOT/<id>/lower WORK_ROOT/<id>/upper WORK_ROOT/<id>/work Result seen at TARGET
Read an unchanged file Provides the original file. Read-only bind view of SOURCE; overlayfs reads from here. Not used for that path. Not used. Original file appears.
Create a new file Unchanged. No new file appears. New file is written here. May be used internally by overlayfs. New file appears.
Modify an existing lower file Unchanged. Original file remains read-only. File is first copied up, then modified here. Used by overlayfs during copy-up/metadata operations. Modified version shadows lower.
Modify a file already in upper Unchanged. Not used for that path. Existing upper file is modified in place. May be used internally. Modified upper version appears.
Delete a lower file Unchanged. Original file still exists. A whiteout/delete marker is written here. May be used internally. File disappears from merged view.
Delete an upper-only file Unchanged. Not used for that path. Upper file is removed. May be used internally. File disappears.
Delete or replace a lower directory Unchanged. Original directory still exists. Overlayfs may create whiteouts and/or opaque directory metadata here. Used for directory metadata operations. Lower directory entries are hidden or replaced.
Rename within the mask Unchanged. Original lower paths remain read-only. Renamed/copied-up entries and whiteouts are stored here as needed. Used by overlayfs for rename bookkeeping. Rename appears in merged view.
Run maskvol diff Read for comparison only. Not modified. Scanned for changed files and delete markers. Not modified. Prints the pending change plan.
Run maskvol export Unchanged. Not exported. Additions/modifications are copied to OUT_DIR; delete markers become a manifest. Not exported. Produces a change-only export.
Run maskvol merge Receives additions/modifications from upper. Not modified by merge. Used as the merge source. Not used. SOURCE now includes merged changes.
Run maskvol merge --delete Also deletes paths represented by upper whiteouts/opaque metadata. Not modified by merge. Used as the source of delete intent. Not used. SOURCE reflects additions, modifications, and explicit deletes.

Important details:

  • WORK_ROOT/<id>/lower is a mountpoint, not a copy of SOURCE.
  • WORK_ROOT/<id>/upper is the only user-meaningful change layer.
  • WORK_ROOT/<id>/work is required by overlayfs and should not be inspected, copied, edited, exported, or backed up as user data.
  • TARGET is a merged view. Do not use rsync --delete from TARGET back to SOURCE; use maskvol merge --delete so delete intent comes only from overlayfs markers in upper.

Requirements

  • Linux with overlayfs
  • bash
  • mount, findmnt, stat, rsync, python3
  • Root privileges for commands that mount, unmount, export, or merge

Optional:

  • docker, for reporting containers that currently use a mask
  • fuser, for reporting active local processes

Acknowledgements

maskvol is a small orchestration layer around established open source and Linux system components. The project depends on these tools and communities:

Dependency Used for
Linux overlayfs The kernel merged filesystem that provides lower, upper, work, and TARGET semantics.
GNU Bash The CLI implementation language.
util-linux System utilities such as mount, findmnt, and mountpoint.
rsync Change preview, export, and merge copy operations.
Python Portable parsing of overlayfs whiteouts and opaque-directory metadata.
Docker Optional container usage reporting and a common runtime for masked volumes.

These components were not created by this project. maskvol exists because they provide reliable primitives that can be combined into a safer development workflow.

Installation

From this repository:

sudo install -m 0755 maskvol /usr/local/bin/maskvol

Then verify:

maskvol doctor

Quick Start

Create a mask:

sudo maskvol up /data/media /srv/masks/media-dev

Run a container against the masked volume:

docker run --rm -v /srv/masks/media-dev:/data:rw ubuntu:latest ls /data

Review changes:

maskvol diff /srv/masks/media-dev

Preview a merge:

sudo maskvol merge /srv/masks/media-dev --dry-run

Merge additions and modifications:

sudo maskvol merge /srv/masks/media-dev

Merge deletions too:

sudo maskvol merge /srv/masks/media-dev --delete

Export only changed files:

sudo maskvol export /srv/masks/media-dev /tmp/media-dev-export

Take the mask down while keeping the upper layer:

sudo maskvol down /srv/masks/media-dev

Remove all local mask state:

sudo maskvol purge /srv/masks/media-dev

Commands

Read-only commands:

Command Purpose
doctor Check kernel, overlayfs, Fuse, registry, configured source roots, and Docker availability.
scan List mounted source candidates and registered mask counts.
ls List all registered masks.
containers List Docker containers and their mounted host sources.
tree Show mounted source to TARGET mapping.
info <TARGET> Show metadata for one mask.
path <TARGET> Print TARGET only if it is registered and mounted.
diff <TARGET> Show additions, modifications, and overlay delete markers.

Root commands:

Command Purpose
up <SOURCE> <TARGET> Create or remount a writable mask.
down <TARGET> Unmount TARGET and lower bind; preserve upper.
purge <TARGET> Unmount and remove local upper/work/registry state.
test <TARGET> Verify writable copy-up and that SOURCE remains clean.
export <TARGET> <OUT_DIR> Export only UPPER additions/modifications.
merge <TARGET> --dry-run [--delete] Preview a merge plan.
merge <TARGET> [--delete] Merge UPPER changes into SOURCE after confirmation.
gc Take down masks whose SOURCE disappeared.

Most read commands support --json. Use --quiet or -q to suppress progress messages where useful.

Merge Model

maskvol merge always uses registered metadata. Operators pass only TARGET; the tool resolves the corresponding SOURCE and UPPER from the registry.

By default, merge copies only additions and modifications from UPPER to SOURCE:

sudo maskvol merge /srv/masks/media-dev --dry-run
sudo maskvol merge /srv/masks/media-dev

Deletion support is explicit:

sudo maskvol merge /srv/masks/media-dev --dry-run --delete
sudo maskvol merge /srv/masks/media-dev --delete

--delete parses overlayfs whiteouts and opaque directories, then deletes the corresponding SOURCE paths one by one. It does not use rsync --delete against the merged TARGET view.

Real merges prompt for confirmation. In non-interactive automation, add --yes after reviewing a previous dry run.

Export Model

maskvol export copies only the local UPPER changes:

sudo maskvol export /srv/masks/media-dev /tmp/media-dev-export

The export contains:

  • changed files and directories from UPPER
  • maskvol-export.manifest
  • maskvol-delete.manifest, when delete markers exist

Untouched lower SOURCE data is not exported.

Configuration

Set variables in the environment or in /etc/maskvol.conf:

Variable Default Description
SOURCE_ROOTS empty Optional space-separated roots searched before mount tables for bare SOURCE lookup.
WORK_ROOT /srv/maskvol Local storage for lower bind, upper, and work directories.
REGISTRY_DIR /var/lib/maskvol Registry metadata, one file per TARGET.
OVERLAY_OPTS redirect_dir=on,index=off,xino=off,metacopy=off Overlay mount options tuned for NFS lowerdirs.
CONFIG_FILE /etc/maskvol.conf Optional config file path.

Safety Properties

  • SOURCE is exposed through a read-only bind mount before overlayfs is mounted.
  • TARGET must be absolute and must not equal SOURCE.
  • test verifies copy-up behavior and checks for SOURCE contamination.
  • purge refuses to delete upper/work paths outside WORK_ROOT.
  • merge and export use registered TARGET metadata; operators do not provide SOURCE manually.
  • merge --delete lists delete paths before applying them.
  • rsync --checksum is used so same-size, same-second modifications are not missed.

Testing

Run the integration suite as root:

sudo tests/maskvol.test.sh

The tests create synthetic data under /tmp, override every runtime root, and clean up their mounts and files on exit.

License

maskvol is released under the MIT License.

Author

Created and maintained by chen21019 chen21019@gmail.com.

Project Notice

This project was originally created by chen21019. Copyright (c) 2026 chen21019. Licensed under the MIT License.

Purpose

Teams often need production-shaped volume data during development, but development containers should not have direct write access to the original data. This project was created to make the safer workflow straightforward: mount a writable mask, inspect exactly what changed, export only the delta when needed, and merge back only after an operator reviews the plan.

It is especially useful as AI agents become part of day-to-day engineering workflows. Combined with Docker or other container runtimes, maskvol lets an agent work against an environment that closely resembles production while remaining isolated from production data. The resulting changes can be exported quickly as changed files, delete manifests, or reviewable merge plans.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages