Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ feat_common_core = [
"lsmem",
"mcookie",
"mesg",
"mount",
"mountpoint",
"nologin",
"renice",
Expand Down Expand Up @@ -108,6 +109,7 @@ lslocks = { optional = true, version = "0.0.1", package = "uu_lslocks", path = "
lsmem = { optional = true, version = "0.0.1", package = "uu_lsmem", path = "src/uu/lsmem" }
mcookie = { optional = true, version = "0.0.1", package = "uu_mcookie", path = "src/uu/mcookie" }
mesg = { optional = true, version = "0.0.1", package = "uu_mesg", path = "src/uu/mesg" }
mount = { optional = true, version = "0.0.1", package = "uu_mount", path = "src/uu/mount" }
mountpoint = { optional = true, version = "0.0.1", package = "uu_mountpoint", path = "src/uu/mountpoint" }
nologin = { optional = true, version = "0.0.1", package = "uu_nologin", path = "src/uu/nologin" }
renice = { optional = true, version = "0.0.1", package = "uu_renice", path = "src/uu/renice" }
Expand All @@ -121,6 +123,7 @@ ctor = "0.6.0"
# dmesg test require fixed-boot-time feature turned on.
dmesg = { version = "0.0.1", package = "uu_dmesg", path = "src/uu/dmesg", features = ["fixed-boot-time"] }
libc = { workspace = true }
mount = { version = "0.0.1", package = "uu_mount", path = "src/uu/mount" }
pretty_assertions = "1"
rand = { workspace = true }
regex = { workspace = true }
Expand Down
17 changes: 17 additions & 0 deletions src/uu/mount/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "uu_mount"
version = "0.0.1"
edition = "2021"

[lib]
path = "src/mount.rs"

[[bin]]
name = "mount"
path = "src/main.rs"

[dependencies]
clap = { workspace = true }
libc = { workspace = true }
thiserror = { workspace = true }
uucore = { workspace = true }
109 changes: 109 additions & 0 deletions src/uu/mount/mount.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# mount

```
mount [options]
mount [options] <source> <directory>
mount [options] <source> | <directory>
mount [options] --source <source> [--target <directory>]
mount [options] [--source <source>] --target <directory>
mount [options] --make-{shared,slave,private,unbindable} <mountpoint>
mount [options] --make-{rshared,rslave,rprivate,runbindable} <mountpoint>
```

Mount a filesystem, or show all currently mounted filesystems.

When called without arguments, or with `-t` but no source and target, mount
prints all currently mounted filesystems (read from `/proc/mounts` on Linux).
The `-t` option filters the listing to entries of the given type. `-l` requests
labels in listing output when they can be discovered from `/dev/disk/by-label`.

When called with a source device and a target directory, mount attaches the
filesystem found on the source to the target directory.

When called with a single positional argument, mount resolves that argument via
`/etc/fstab`. The argument may be either a source specifier or a mount point.
`--source` and `--target` can be used to disambiguate that lookup.

When called with `--make-*`, mount applies mount-propagation changes to the
specified mountpoint. If a normal mount is also requested in the same command,
the propagation changes are applied after the mount succeeds.

## Supported contract

This implementation currently targets **Linux** and is intended to provide a
strong baseline `mount` with the core operational semantics in place:

- no-argument listing from `/proc/mounts`
- direct mounts plus `--bind`, `--rbind`, and `--move`
- `--all` with already-mounted skipping, `-t` filtering, `-O` filtering, and
optional `--fork`
- `/etc/fstab`-driven single-argument resolution, including alternate files via
`-T`
- label/UUID/PARTLABEL/PARTUUID resolution
- merged `fstab` + CLI `-o` options for fstab-derived mounts
- optional mountpoint creation via `-m`
- propagation changes via `--make-*`

This command is **not yet a full upstream-compatible replacement** for every
advanced `mount(8)` feature. The supported behavior is intentionally explicit so
reviewers can evaluate the current contract clearly.

## Options

- `-a`, `--all` — mount all filesystems listed in `/etc/fstab` (respects `noauto`
and `-t` / `-O` filters)
- `-B`, `--bind` — bind-mount a subtree at another location (`MS_BIND`)
- `-R`, `--rbind` — recursively bind-mount a subtree (`MS_BIND | MS_REC`)
- `-M`, `--move` — atomically move a mounted subtree to a new location
(`MS_MOVE`)
- `--make-shared` — mark a subtree as shared
- `--make-slave` — mark a subtree as slave
- `--make-private` — mark a subtree as private
- `--make-unbindable` — mark a subtree as unbindable
- `--make-rshared` — recursively mark a whole subtree as shared
- `--make-rslave` — recursively mark a whole subtree as slave
- `--make-rprivate` — recursively mark a whole subtree as private
- `--make-runbindable` — recursively mark a whole subtree as unbindable
- `-f`, `--fake` — dry run; parse arguments and resolve devices but skip the
actual `mount(2)` syscall
- `-F`, `--fork` — with `--all`, mount matching filesystems in separate worker
processes
- `-T`, `--fstab PATH` — use an alternate fstab file instead of `/etc/fstab`
- `-l`, `--show-labels` — show filesystem labels in listing output when
available
- `-m`, `--mkdir` — create the target mountpoint if it does not already exist
- `-n`, `--no-mtab` — do not write an entry to `/etc/mtab`
- `-o`, `--options LIST` — comma-separated list of mount options (e.g.
`ro,noatime,uid=1000`); for `/etc/fstab`-resolved mounts, CLI options are
appended after `fstab` options so later values win
- `-O`, `--test-opts LIST` — with `--all`, limit mounts to fstab entries whose
option field matches `LIST`
- `-r`, `--read-only` — mount read-only (same as `-o ro`)
- `-w`, `--read-write` — mount read-write, overriding a `ro` option from fstab
- `-t`, `--types LIST` — filesystem type filter; prefix a type with `no` to
exclude it (e.g. `-t noext4`)
- `-v`, `--verbose` — print a diagnostic line for each mount operation
- `-L`, `--label LABEL` — mount the device with the given filesystem label
- `-U`, `--uuid UUID` — mount the device with the given filesystem UUID
- `--partlabel LABEL` — mount the partition with the given partition label
(`PARTLABEL=`)
- `--partuuid UUID` — mount the partition with the given partition UUID
(`PARTUUID=`)
- `--source SOURCE` — explicitly specify the source side of the mount or the
single-argument fstab lookup key
- `--target DIRECTORY` — explicitly specify the target side of the mount or the
single-argument fstab lookup key

## Notes

- `--make-*` propagation operations are not combined with `--all`.
- Propagation changes do not read `/etc/fstab`; provide the target mountpoint
explicitly when using them directly.

## Deferred features

Notable items that remain outside the current supported contract include:

- alternate `--options-mode` handling beyond the current append-style merge
- helper-specific behaviors outside this in-process Linux implementation
- additional advanced `mount(8)` compatibility options not yet implemented
66 changes: 66 additions & 0 deletions src/uu/mount/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// This file is part of the uutils util-linux package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

use thiserror::Error;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add the license header

use uucore::error::UError;

pub const EXIT_MOUNT_FAILED: i32 = 32;

#[derive(Error, Debug)]
pub enum MountError {
#[error("cannot open /proc/mounts: {0}")]
ProcMounts(std::io::Error),

#[error("cannot read {0}: {1}")]
FstabRead(String, std::io::Error),

#[error("I/O error: {0}")]
Fstab(#[from] std::io::Error),

#[error("cannot find device with label {0:?}")]
LabelNotFound(String),

#[error("cannot find device with UUID {0:?}")]
UuidNotFound(String),

#[error("cannot find mount entry for {0:?} in fstab")]
FstabEntryNotFound(String),

#[error("no mount point specified and none found in fstab for {0}")]
NoMountPoint(String),

#[error("cannot create mount point {0}: {1}")]
CreateMountPoint(String, std::io::Error),

#[error("cannot fork mount worker: {0}")]
Fork(std::io::Error),

#[error("cannot wait for mount worker: {0}")]
Wait(std::io::Error),

#[error("invalid source path: {0}")]
InvalidSource(std::ffi::NulError),

#[error("invalid target path: {0}")]
InvalidTarget(std::ffi::NulError),

#[error("invalid filesystem type: {0}")]
InvalidFSType(std::ffi::NulError),

#[error("invalid mount options: {0}")]
InvalidOptions(std::ffi::NulError),

#[error("mount: {1} on {2}: {0}")]
MountFailed(std::io::Error, String, String),
}

impl UError for MountError {
fn code(&self) -> i32 {
match self {
MountError::MountFailed(_, _, _) => EXIT_MOUNT_FAILED,
_ => 1,
}
}
}
32 changes: 32 additions & 0 deletions src/uu/mount/src/escape.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// This file is part of the uutils util-linux package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

/// Expand octal escape sequences of the form `\NNN` used in mount-table style
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/// files to encode whitespace and other special characters.
pub(crate) fn unescape_octal(s: &str) -> String {
let mut result: Vec<u8> = Vec::with_capacity(s.len());
let bytes = s.as_bytes();
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'\\' && i + 3 < bytes.len() {
let (a, b, c) = (bytes[i + 1], bytes[i + 2], bytes[i + 3]);
if a.is_ascii_digit()
&& a < b'8'
&& b.is_ascii_digit()
&& b < b'8'
&& c.is_ascii_digit()
&& c < b'8'
{
let value = (a - b'0') * 64 + (b - b'0') * 8 + (c - b'0');
result.push(value);
i += 4;
continue;
}
}
result.push(bytes[i]);
i += 1;
}
String::from_utf8_lossy(&result).into_owned()
}
Loading