Ucode-based mountpoint command for OpenWrt
Due to OpenWrt issue 13873 the author needed a version of the mountpoint
command for OpenWrt. Since they wanted to learn ucode, they coded the command
in this interesting ECMAscript-like language1. 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 tranlsation 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 ↩︎