February 17, 2025 by Daniel F Dickinson5 minutes
Due to an OpenWrt issue, the author needed a version of the 'mountpoint' command. Since the author wanted to learn ucode, they coded a version of the command using it.
It was
OpenWrt issue 13873 that led to the author’s need for the moundpoint
command for
OpenWrt. As an opportunity to learn
ucode, an interesting
ECMAscript-like language1, the author translated a C-based 'mountpoint
command into
ucode. This may be moot as enabling the busybox
mountpoint
applet could be more space-efficient, but the author enjoyed the exercise.
The following code is a fairly quick and dirty translation of
mountpoint from util-linux,
written in ‘C’, to this code written in ucode
.
Comments, suggestions and discussion of the following code should be done on the mountpoint-ucode Gist.
With the code in place, you can issue mountpoint /path/to/check
which returns 0
(POSIX shell true
) if /path/to/check
is a mounted mountpoint, 1 if is not a
mounted mountpoint, unless an error occurs, in which case the script returns -1 (255).
mountpoint -v /path/to/check
will show a written message such as:
/path/to/check is a mountpoint
.
mountpoint -vv /path/to/check
will additionally output a message like
Could not stat '/path/to/check'
if there is no file or directory at
path/to/check
.
Place the following script as /usr/bin/mountpoint
on a device with
ucode installed,
and make it executable (chmod 0755 /usr/bin/mountpoint
).
#!/usr/bin/env ucode
import { print_is_mountpoint } from "mountpoint";
let args = ARGV;
const arg1 = shift(args);
let do_message = false;
let show_errors = false;
if (arg1 == "-v") {
do_message = true;
} else if (arg1 == "-vv") {
do_message = true;
show_errors = true;
} else {
unshift(args, arg1);
}
exit(print_is_mountpoint(join(" ", args), do_message, show_errors));
And place the following module as /usr/share/ucode/mountpoint.uc
.
// based on https://github.com/util-linux/util-linux/blob/master/sys-utils/mountpoint.c
import { access, dirname, open, realpath, stat } from 'fs';
function _is_dm_devname(canonical) {
let dmpath = null;
const spos = rindex(canonical, "/");
if (spos != null) {
dmpath = substr(canonical, spos + 1);
} else {
return {
error: true,
dmpath: null,
error_message: "Not a device path"
};
}
const can_stat = stat(canonical);
if (can_stat == null) {
return {
error: true,
dmpath: null,
error_message: sprintf("Unable to access path %s", canonical)
};
}
if (
(length(dmpath) < 5) ||
(substr(dmpath, 0, 4) != "/dm-") ||
(int(substr(dmpath, 4)) == NaN) ||
(can_stat.type != "block")
) {
return {
error: false,
dmpath: null,
error_message: null
};
}
return {
error: false,
dmpath: dmpath,
error_message: null
};
}
function _canonicalize_dm_name(path) {
if (path == null) {
return {
error: true,
dmpath: null,
error_message: "No input path"
};
}
const fpath = sprintf("/sys/block/%s/dm/name", path);
const f = open(fpath, "r");
if (f == null) {
return {
error: true,
dmpath: null,
error_message: sprintf("Unable to open '%s'", fpath)
};
}
const dmname = f.read("\n");
f.close();
if (dmname == null) {
return {
error: true,
dmpath: null,
error_message: sprintf("Failed to read '%s'", fpath)
};
}
const dmpath = sprintf("/dev/mapper/%s", dmname);
if (!access(dmpath)) {
return {
error: true,
dmpath: null,
error_message: sprintf("Unable to access '%s'", dmpath)
};
}
return {
error: false,
dmpath: dmpath,
error_message: null
};
}
function _canonicalize_device_path(path) {
if (!path) {
return {
error: true,
canonical: null,
error_message: "No input path"
};
}
let canonical = realpath(path);
if (!canonical) {
return {
error: true,
canonical: null,
error_message: sprintf("'%s' has no canonical path", path)
};
}
const dmname_result = _is_dm_devname(canonical);
if (!dmname_result.error) {
if (dmname_result.dmname != null) {
const dmcanonical_result = _canonicalize_dm_name(dmname_result.dmname);
if (dmcanonical_result.dmname != null) {
canonical = dmcanonical_result.dmname;
} else if (dmcanonical_result.error) {
return {
error: true,
canonical: null,
error_message: sprintf("'%s' could not be canonicalized: %s", path, dmcanonical_result.error_message)
};
}
}
} else if (dmname_result.error_message != "Not a device path") {
return {
error: true,
canonical: null,
error_message: sprintf("'%s' could not be read: %s", canonical, dmname_result.error_message)
};
}
return {
error: false,
canonical: canonical,
error_message: null
};
}
function _dir_to_device(in_stat, in_path) {
if (in_path == null) {
return {
error: true,
device: null,
error_message: "No input path"
};
}
const canonical_result = _canonicalize_device_path(in_path);
if (canonical_result.error) {
return {
error: true,
device: null,
error_message: canonical_result.error_message
};
}
const canstat_result = stat(canonical_result.canonical);
if (canstat_result == null) {
return {
error: true,
device: null,
error_message: sprintf("Could not stat '%s' (canonical path for '%s')", canonical_result.canonical, in_path)
};
}
let parent_path = sprintf("%s/..", canonical_result.canonical);
if (canstat_result.type != "directory") {
parent_path = dirname(canonical_result.canonical);
}
const parent_stat = stat(parent_path);
if (parent_stat == null) {
return {
error: true,
device: null,
error_message: sprintf("Could not stat parent directory of '%s' (canonical path for '%s')", canonical_result.canonical, in_path)
};
}
if ((in_stat.dev.major != parent_stat.dev.major) || (in_stat.dev.minor != parent_stat.dev.minor) || (in_stat.inode == parent_stat.inode)) {
return {
error: false,
device: in_stat.dev,
error_message: null
};
}
return {
error: false,
device: null,
error_message: null
};
}
function _is_mountpoint(path) {
const path_stat = stat(path);
if (path_stat == null) {
return {
error: true,
is_mountpoint: null,
device: null,
error_message: sprintf("Could not stat '%s'", path)
};
}
const path_device_result = _dir_to_device(path_stat, path);
if (path_device_result.error) {
return {
error: true,
is_mountpoint: null,
device: null,
error_message: sprintf("Could not get device for '%s': %s", path, path_device_result.error_message)
};
} else {
return {
error: false,
is_mountpoint: (path_device_result.device != null),
device: path_device_result.device,
error_message: null
}
}
}
function is_mountpoint(path, show_error) {
const is_mp_result = _is_mountpoint(path);
if (is_mp_result.error) {
if (show_error) {
printf("%s\n", is_mp_result.error_message);
}
return -1;
} else {
return (is_mp_result.is_mountpoint ? 0 : 1);
}
}
function print_is_mountpoint(path, show_message, show_error) {
const is_mp = is_mountpoint(path, show_error);
if (is_mp == -1) {
exit(-1);
}
if (is_mp == 0) {
if (show_message) {
printf("%s is a mountpoint\n", path);
}
exit(0);
} else {
if (show_message) {
printf("%s is not a mountpoint\n", path);
}
exit(1);
}
}
export { is_mountpoint, print_is_mountpoint };
Javascript is an ECMAscript language ↩︎