uvol: replace with re-write in ucode
Replace previous Shell draft-quality implementation of uvol with a rewrite in ucode[1]. While the new code is slightly larger, it performs much better (as we no longer fork() for parsing strings like in Shell with grep, sed and friends). Before: time uvol list -j [ ... ] real 0m 0.82s user 0m 0.13s sys 0m 0.10s After: time uvol list -j [ ... ] real 0m 0.47s user 0m 0.05s sys 0m 0.05s [1]: https://github.com/jow-/ucode Signed-off-by: Daniel Golle <daniel@makrotopia.org>
This commit is contained in:
parent
73b304bf0c
commit
6350c7bc63
9 changed files with 1222 additions and 934 deletions
|
@ -1,7 +1,7 @@
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=uvol
|
PKG_NAME:=uvol
|
||||||
PKG_VERSION:=0.6
|
PKG_VERSION:=0.7
|
||||||
PKG_RELEASE:=$(AUTORELEASE)
|
PKG_RELEASE:=$(AUTORELEASE)
|
||||||
|
|
||||||
PKG_MAINTAINER:=Daniel Golle <daniel@makrotopia.org>
|
PKG_MAINTAINER:=Daniel Golle <daniel@makrotopia.org>
|
||||||
|
@ -19,8 +19,7 @@ define Package/autopart
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/autopart/description
|
define Package/autopart/description
|
||||||
Automatically allocate the GPT partition for LVM and initialize it
|
Automatically allocate and initialize a partition for LVM on first boot.
|
||||||
on first boot.
|
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/uvol
|
define Package/uvol
|
||||||
|
@ -28,18 +27,22 @@ define Package/uvol
|
||||||
CATEGORY:=Utilities
|
CATEGORY:=Utilities
|
||||||
SUBMENU:=Disc
|
SUBMENU:=Disc
|
||||||
TITLE:=OpenWrt UBI/LVM volume abstraction
|
TITLE:=OpenWrt UBI/LVM volume abstraction
|
||||||
DEPENDS:=+blockd
|
DEPENDS:=+blockd +ucode +ucode-mod-fs +ucode-mod-uci
|
||||||
PKGARCH=all
|
PKGARCH=all
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/uvol/description
|
define Package/uvol/description
|
||||||
'uvol' is tool to automate storage volume handling on embedded
|
'uvol' is tool to automate storage volume handling on embedded
|
||||||
devices in a generic way.
|
devices in a generic way.
|
||||||
|
Depending on what is available, 'uvol' will use either UBI or LVM2
|
||||||
|
as storage backends and transparently offer identical operations on
|
||||||
|
top of them.
|
||||||
|
|
||||||
Also install the 'autopart' package to easily make use of 'uvol' on
|
Also install the 'autopart' package to easily make use of 'uvol' on
|
||||||
block-storage based devices.
|
block-storage based devices.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
uvol create example_volume_1 256MiB rw
|
uvol create example_volume_1 268435456 rw
|
||||||
uvol up example_volume_1
|
uvol up example_volume_1
|
||||||
uvol device example_volume_1
|
uvol device example_volume_1
|
||||||
|
|
||||||
|
@ -64,11 +67,12 @@ define Package/autopart/install
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/uvol/install
|
define Package/uvol/install
|
||||||
$(INSTALL_DIR) $(1)/etc/init.d $(1)/usr/libexec/uvol $(1)/usr/sbin $(1)/lib/functions $(1)/etc/uci-defaults
|
$(INSTALL_DIR) $(1)/etc/init.d $(1)/usr/uvol/backends $(1)/usr/sbin $(1)/etc/uci-defaults
|
||||||
$(INSTALL_BIN) ./files/uvol.init $(1)/etc/init.d/uvol
|
$(INSTALL_BIN) ./files/uvol.init $(1)/etc/init.d/uvol
|
||||||
$(INSTALL_BIN) ./files/common.sh $(1)/lib/functions/uvol.sh
|
$(INSTALL_DATA) ./files/blockdev_common.uc $(1)/usr/lib/uvol/
|
||||||
$(INSTALL_BIN) ./files/ubi.sh $(1)/usr/libexec/uvol/20-ubi.sh
|
$(INSTALL_DATA) ./files/uci.uc $(1)/usr/lib/uvol/
|
||||||
$(INSTALL_BIN) ./files/lvm.sh $(1)/usr/libexec/uvol/50-lvm.sh
|
$(INSTALL_DATA) ./files/lvm.uc $(1)/usr/lib/uvol/backends/
|
||||||
|
$(INSTALL_DATA) ./files/ubi.uc $(1)/usr/lib/uvol/backends/
|
||||||
$(INSTALL_BIN) ./files/uvol $(1)/usr/sbin
|
$(INSTALL_BIN) ./files/uvol $(1)/usr/sbin
|
||||||
$(INSTALL_BIN) ./files/uvol.defaults $(1)/etc/uci-defaults/90-uvol-init
|
$(INSTALL_BIN) ./files/uvol.defaults $(1)/etc/uci-defaults/90-uvol-init
|
||||||
endef
|
endef
|
||||||
|
|
123
utils/uvol/files/blockdev_common.uc
Normal file
123
utils/uvol/files/blockdev_common.uc
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
{%
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
// Helper functions used to identify the boot device
|
||||||
|
// adapted from /lib/functions.sh
|
||||||
|
let cmdline_get_var = function(var) {
|
||||||
|
let cmdline = fs.open("/proc/cmdline", "r");
|
||||||
|
let allargs = cmdline.read("all");
|
||||||
|
cmdline.close();
|
||||||
|
let ret = null;
|
||||||
|
for (let arg in split(allargs, /[ \t\n]/)) {
|
||||||
|
let el = split(arg, "=");
|
||||||
|
if (shift(el) == var)
|
||||||
|
return join("=", el);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
// adapted from /lib/upgrade/common.sh
|
||||||
|
let get_blockdevs = function() {
|
||||||
|
let devs = [];
|
||||||
|
for (let dev in fs.glob('/dev/*'))
|
||||||
|
if (fs.stat(dev).type == "block")
|
||||||
|
push(devs, split(dev, '/')[-1]);
|
||||||
|
|
||||||
|
return devs;
|
||||||
|
};
|
||||||
|
|
||||||
|
// adapted from /lib/upgrade/common.sh
|
||||||
|
let get_uevent_major_minor = function(file) {
|
||||||
|
let uevf = fs.open(file, "r");
|
||||||
|
if (!uevf)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let r = {};
|
||||||
|
let evl;
|
||||||
|
while ((evl = uevf.read("line"))) {
|
||||||
|
let ev = split(evl, '=');
|
||||||
|
if (ev[0] == "MAJOR")
|
||||||
|
r.major = +ev[1];
|
||||||
|
if (ev[0] == "MINOR")
|
||||||
|
r.minor = +ev[1];
|
||||||
|
}
|
||||||
|
uevf.close();
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
|
||||||
|
// adapted from /lib/upgrade/common.sh
|
||||||
|
let get_bootdev = function(void) {
|
||||||
|
let rootpart = cmdline_get_var("root");
|
||||||
|
let uevent = null;
|
||||||
|
|
||||||
|
if (wildcard(rootpart, "PARTUUID=[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]-[a-f0-9][a-f0-9]")) {
|
||||||
|
let uuidarg = split(substr(rootpart, 9), '-')[0];
|
||||||
|
for (let bd in get_blockdevs()) {
|
||||||
|
let bdf = fs.open(sprintf("/dev/%s", bd), "r");
|
||||||
|
bdf.seek(440);
|
||||||
|
let bduuid = bdf.read(4);
|
||||||
|
bdf.close();
|
||||||
|
if (uuidarg == sprintf("%x%x%x%x", ord(bduuid, 3), ord(bduuid, 2), ord(bduuid, 1), ord(bduuid, 0))) {
|
||||||
|
uevent = sprintf("/sys/class/block/%s/uevent", bd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (wildcard(rootpart, "PARTUUID=????????-????-????-????-??????????0?/PARTNROFF=*") ||
|
||||||
|
wildcard(rootpart, "PARTUUID=????????-????-????-????-??????????02")) {
|
||||||
|
let uuidarg = substr(split(substr(rootpart, 9), '/')[0], 0, -2) + "00";
|
||||||
|
for (let bd in get_blockdevs()) {
|
||||||
|
let bdf = fs.open(sprintf("/dev/%s", bd), "r");
|
||||||
|
bdf.seek(568);
|
||||||
|
let bduuid = bdf.read(16);
|
||||||
|
bdf.close();
|
||||||
|
if (!bduuid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let uuid = sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||||
|
ord(bduuid, 3), ord(bduuid, 2), ord(bduuid, 1), ord(bduuid, 0),
|
||||||
|
ord(bduuid, 5), ord(bduuid, 4),
|
||||||
|
ord(bduuid, 7), ord(bduuid, 6),
|
||||||
|
ord(bduuid, 8), ord(bduuid, 9),
|
||||||
|
ord(bduuid, 10), ord(bduuid, 11), ord(bduuid, 12), ord(bduuid, 13), ord(bduuid, 14), ord(bduuid, 15));
|
||||||
|
if (uuidarg == uuid) {
|
||||||
|
uevent = sprintf("/sys/class/block/%s/uevent", bd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (wildcard(rootpart, "0x[a-f0-9][a-f0-9][a-f0-9]") ||
|
||||||
|
wildcard(rootpart, "0x[a-f0-9][a-f0-9][a-f0-9][a-f0-9]") ||
|
||||||
|
wildcard(rootpart, "[a-f0-9][a-f0-9][a-f0-9]") ||
|
||||||
|
wildcard(rootpart, "[a-f0-9][a-f0-9][a-f0-9][a-f0-9]")) {
|
||||||
|
let devid = rootpart;
|
||||||
|
if (substr(devid, 0, 2) == "0x")
|
||||||
|
devid = substr(devid, 2);
|
||||||
|
|
||||||
|
devid = hex(devid);
|
||||||
|
for (let bd in get_blockdevs()) {
|
||||||
|
let r = get_uevent_major_minor(sprintf("/sys/class/block/%s/uevent", bd));
|
||||||
|
if (r && (r.major == devid / 256) && (r.minor == devid % 256)) {
|
||||||
|
uevent = sprintf("/sys/class/block/%s/../uevent", bd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (wildcard(rootpart, "/dev/*")) {
|
||||||
|
uevent = sprintf("/sys/class/block/%s/../uevent", split(rootpart, '/')[-1]);
|
||||||
|
}
|
||||||
|
return get_uevent_major_minor(uevent);
|
||||||
|
};
|
||||||
|
|
||||||
|
// adapted from /lib/upgrade/common.sh
|
||||||
|
let get_partition = function(dev, num) {
|
||||||
|
for (let bd in get_blockdevs()) {
|
||||||
|
let r = get_uevent_major_minor(sprintf("/sys/class/block/%s/uevent", bd));
|
||||||
|
if (r.major == dev.major && r.minor == dev.minor + num) {
|
||||||
|
return bd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
blockdev_common = {};
|
||||||
|
blockdev_common.get_partition = get_partition;
|
||||||
|
blockdev_common.get_bootdev = get_bootdev;
|
||||||
|
%}
|
|
@ -1,83 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
UCI_SPOOLDIR="/var/spool/uvol"
|
|
||||||
|
|
||||||
_uvol_init_spooldir() {
|
|
||||||
[ ! -d "$(dirname "$UCI_SPOOLDIR")" ] && mkdir -p "$(dirname "$UCI_SPOOLDIR")"
|
|
||||||
mkdir -m 0700 -p "$UCI_SPOOLDIR"
|
|
||||||
}
|
|
||||||
|
|
||||||
uvol_uci_add() {
|
|
||||||
local volname="$1"
|
|
||||||
local devname="$2"
|
|
||||||
local mode="$3"
|
|
||||||
local autofs=0
|
|
||||||
local target="/tmp/run/uvol/$volname"
|
|
||||||
local uuid uciname
|
|
||||||
|
|
||||||
[ "$mode" = "ro" ] && autofs=1
|
|
||||||
uciname="${volname//[-.]/_}"
|
|
||||||
uciname="${uciname//[!([:alnum:]_)]}"
|
|
||||||
uuid="$(/sbin/block info | grep "^$2" | xargs -n 1 echo | grep "^UUID=.*")"
|
|
||||||
[ "$uuid" ] || return 22
|
|
||||||
uuid="${uuid:5}"
|
|
||||||
|
|
||||||
case "$uciname" in
|
|
||||||
"_meta")
|
|
||||||
target="/tmp/run/uvol/.meta"
|
|
||||||
;;
|
|
||||||
"_"*)
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
_uvol_init_spooldir
|
|
||||||
if [ -e "${UCI_SPOOLDIR}/remove-$1" ]; then
|
|
||||||
rm "${UCI_SPOOLDIR}/remove-$1"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat >"${UCI_SPOOLDIR}/add-$1" <<EOF
|
|
||||||
set fstab.$uciname=mount
|
|
||||||
set fstab.$uciname.uuid=$uuid
|
|
||||||
set fstab.$uciname.target=$target
|
|
||||||
set fstab.$uciname.options=$mode
|
|
||||||
set fstab.$uciname.autofs=$autofs
|
|
||||||
set fstab.$uciname.enabled=1
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
uvol_uci_remove() {
|
|
||||||
local volname="$1"
|
|
||||||
local uciname
|
|
||||||
|
|
||||||
uciname="${volname//[-.]/_}"
|
|
||||||
uciname="${uciname//[!([:alnum:]_)]}"
|
|
||||||
if [ -e "${UCI_SPOOLDIR}/add-$1" ]; then
|
|
||||||
rm "${UCI_SPOOLDIR}/add-$1"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
_uvol_init_spooldir
|
|
||||||
cat >"${UCI_SPOOLDIR}/remove-$1" <<EOF
|
|
||||||
delete fstab.$uciname
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
uvol_uci_commit() {
|
|
||||||
local volname="$1"
|
|
||||||
local ucibatch
|
|
||||||
|
|
||||||
for ucibatch in "${UCI_SPOOLDIR}/"*"-$volname"${volname+*} ; do
|
|
||||||
[ -e "$ucibatch" ] || break
|
|
||||||
uci batch < "$ucibatch"
|
|
||||||
[ $? -eq 0 ] && rm "$ucibatch"
|
|
||||||
done
|
|
||||||
|
|
||||||
uci commit fstab
|
|
||||||
return $?
|
|
||||||
}
|
|
||||||
|
|
||||||
uvol_uci_init() {
|
|
||||||
uci -q get fstab.@uvol[0] && return
|
|
||||||
uci add fstab uvol
|
|
||||||
uci set fstab.@uvol[-1].initialized=1
|
|
||||||
}
|
|
|
@ -1,460 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
cmd="$1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
if [ "$cmd" = "name" ]; then
|
|
||||||
echo "LVM"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
command -v lvm >/dev/null || return 1
|
|
||||||
|
|
||||||
. /lib/functions.sh
|
|
||||||
. /lib/functions/uvol.sh
|
|
||||||
. /lib/upgrade/common.sh
|
|
||||||
. /usr/share/libubox/jshn.sh
|
|
||||||
|
|
||||||
export_bootdevice
|
|
||||||
[ "$BOOTDEV_MAJOR" ] || return 1
|
|
||||||
export_partdevice rootdev 0
|
|
||||||
[ "$rootdev" ] || return 1
|
|
||||||
|
|
||||||
case "$rootdev" in
|
|
||||||
mtd*|\
|
|
||||||
ram*|\
|
|
||||||
ubi*)
|
|
||||||
return 1
|
|
||||||
esac
|
|
||||||
|
|
||||||
lvm_cmd() {
|
|
||||||
local cmd="$1"
|
|
||||||
shift
|
|
||||||
LVM_SUPPRESS_FD_WARNINGS=1 lvm "$cmd" "$@"
|
|
||||||
return $?
|
|
||||||
}
|
|
||||||
|
|
||||||
pvs() {
|
|
||||||
lvm_cmd pvs --reportformat json --units b "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
vgs() {
|
|
||||||
lvm_cmd vgs --reportformat json --units b "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
lvs() {
|
|
||||||
lvm_cmd lvs --reportformat json --units b "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
freebytes() {
|
|
||||||
echo $((vg_free_count * vg_extent_size))
|
|
||||||
}
|
|
||||||
|
|
||||||
totalbytes() {
|
|
||||||
echo $((vg_extent_count * vg_extent_size))
|
|
||||||
}
|
|
||||||
|
|
||||||
existvol() {
|
|
||||||
[ "$1" ] || return 1
|
|
||||||
test -e "/dev/$vg_name/ro_$1" || test -e "/dev/$vg_name/rw_$1"
|
|
||||||
return $?
|
|
||||||
}
|
|
||||||
|
|
||||||
vg_name=
|
|
||||||
exportpv() {
|
|
||||||
vg_name=
|
|
||||||
config_load fstab
|
|
||||||
local uvolsect="$(config_foreach echo uvol)"
|
|
||||||
[ -n "$uvolsect" ] && config_get vg_name "$uvolsect" vg_name
|
|
||||||
[ -n "$vg_name" ] && return
|
|
||||||
local reports rep pv pvs
|
|
||||||
json_init
|
|
||||||
json_load "$(pvs -o vg_name -S "pv_name=~^/dev/$rootdev.*\$")"
|
|
||||||
json_select report
|
|
||||||
json_get_keys reports
|
|
||||||
for rep in $reports; do
|
|
||||||
json_select "$rep"
|
|
||||||
json_select pv
|
|
||||||
json_get_keys pvs
|
|
||||||
for pv in $pvs; do
|
|
||||||
json_select "$pv"
|
|
||||||
json_get_vars vg_name
|
|
||||||
json_select ..
|
|
||||||
break
|
|
||||||
done
|
|
||||||
json_select ..
|
|
||||||
break
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
vg_extent_size=
|
|
||||||
vg_extent_count=
|
|
||||||
vg_free_count=
|
|
||||||
exportvg() {
|
|
||||||
local reports rep vg vgs
|
|
||||||
vg_extent_size=
|
|
||||||
vg_extent_count=
|
|
||||||
vg_free_count=
|
|
||||||
json_init
|
|
||||||
json_load "$(vgs -o vg_extent_size,vg_extent_count,vg_free_count -S "vg_name=$vg_name")"
|
|
||||||
json_select report
|
|
||||||
json_get_keys reports
|
|
||||||
for rep in $reports; do
|
|
||||||
json_select "$rep"
|
|
||||||
json_select vg
|
|
||||||
json_get_keys vgs
|
|
||||||
for vg in $vgs; do
|
|
||||||
json_select "$vg"
|
|
||||||
json_get_vars vg_extent_size vg_extent_count vg_free_count
|
|
||||||
vg_extent_size=${vg_extent_size%B}
|
|
||||||
json_select ..
|
|
||||||
break
|
|
||||||
done
|
|
||||||
json_select ..
|
|
||||||
break
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_active=
|
|
||||||
lv_name=
|
|
||||||
lv_full_name=
|
|
||||||
lv_path=
|
|
||||||
lv_dm_path=
|
|
||||||
lv_size=
|
|
||||||
exportlv() {
|
|
||||||
local reports rep lv lvs
|
|
||||||
lv_active=
|
|
||||||
lv_name=
|
|
||||||
lv_full_name=
|
|
||||||
lv_path=
|
|
||||||
lv_dm_path=
|
|
||||||
lv_size=
|
|
||||||
json_init
|
|
||||||
|
|
||||||
json_load "$(lvs -o lv_active,lv_name,lv_full_name,lv_size,lv_path,lv_dm_path -S "lv_name=~^[rw][owp]_$1\$ && vg_name=$vg_name")"
|
|
||||||
json_select report
|
|
||||||
json_get_keys reports
|
|
||||||
for rep in $reports; do
|
|
||||||
json_select "$rep"
|
|
||||||
json_select lv
|
|
||||||
json_get_keys lvs
|
|
||||||
for lv in $lvs; do
|
|
||||||
json_select "$lv"
|
|
||||||
json_get_vars lv_active lv_name lv_full_name lv_size lv_path lv_dm_path
|
|
||||||
lv_size=${lv_size%B}
|
|
||||||
json_select ..
|
|
||||||
break
|
|
||||||
done
|
|
||||||
json_select ..
|
|
||||||
break
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
getdev() {
|
|
||||||
local dms dm_name
|
|
||||||
|
|
||||||
for dms in /sys/devices/virtual/block/dm-* ; do
|
|
||||||
[ "$dms" = "/sys/devices/virtual/block/dm-*" ] && break
|
|
||||||
read -r dm_name < "$dms/dm/name"
|
|
||||||
[ "$(basename "$lv_dm_path")" = "$dm_name" ] && basename "$dms"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
getuserdev() {
|
|
||||||
local dms dm_name
|
|
||||||
existvol "$1" || return 1
|
|
||||||
exportlv "$1"
|
|
||||||
getdev "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
getsize() {
|
|
||||||
exportlv "$1"
|
|
||||||
[ "$lv_size" ] && echo "$lv_size"
|
|
||||||
}
|
|
||||||
|
|
||||||
activatevol() {
|
|
||||||
exportlv "$1"
|
|
||||||
[ "$lv_path" ] || return 2
|
|
||||||
case "$lv_path" in
|
|
||||||
/dev/*/wo_*|\
|
|
||||||
/dev/*/wp_*)
|
|
||||||
return 22
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
uvol_uci_commit "$1"
|
|
||||||
[ "$lv_active" = "active" ] && return 0
|
|
||||||
lvm_cmd lvchange -k n "$lv_full_name" || return $?
|
|
||||||
lvm_cmd lvchange -a y "$lv_full_name" || return $?
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
disactivatevol() {
|
|
||||||
exportlv "$1"
|
|
||||||
local devname
|
|
||||||
[ "$lv_path" ] || return 2
|
|
||||||
case "$lv_path" in
|
|
||||||
/dev/*/wo_*|\
|
|
||||||
/dev/*/wp_*)
|
|
||||||
return 22
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
[ "$lv_active" = "active" ] || return 0
|
|
||||||
devname="$(getdev "$1")"
|
|
||||||
[ "$devname" ] && umount "/dev/$devname"
|
|
||||||
lvm_cmd lvchange -a n "$lv_full_name"
|
|
||||||
lvm_cmd lvchange -k y "$lv_full_name" || return $?
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
getstatus() {
|
|
||||||
exportlv "$1"
|
|
||||||
[ "$lv_full_name" ] || return 2
|
|
||||||
existvol "$1" || return 1
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
createvol() {
|
|
||||||
local mode lvmode ret
|
|
||||||
local volsize=$(($2))
|
|
||||||
[ "$volsize" ] || return 22
|
|
||||||
exportlv "$1"
|
|
||||||
[ "$lv_size" ] && return 17
|
|
||||||
size_ext=$((volsize / vg_extent_size))
|
|
||||||
[ $((size_ext * vg_extent_size)) -lt $volsize ] && size_ext=$((size_ext + 1))
|
|
||||||
|
|
||||||
case "$3" in
|
|
||||||
ro|wo)
|
|
||||||
lvmode=r
|
|
||||||
mode=wo
|
|
||||||
;;
|
|
||||||
rw)
|
|
||||||
lvmode=rw
|
|
||||||
mode=wp
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
return 22
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
lvm_cmd lvcreate -p "$lvmode" -a n -y -W n -Z n -n "${mode}_$1" -l "$size_ext" "$vg_name" || return $?
|
|
||||||
ret=$?
|
|
||||||
if [ ! $ret -eq 0 ] || [ "$lvmode" = "r" ]; then
|
|
||||||
return $ret
|
|
||||||
fi
|
|
||||||
exportlv "$1"
|
|
||||||
[ "$lv_full_name" ] || return 22
|
|
||||||
lvm_cmd lvchange -a y "$lv_full_name" || return $?
|
|
||||||
if [ "$lv_size" -gt $(( 100 * 1024 * 1024 )) ]; then
|
|
||||||
mkfs.f2fs -f -l "$1" "$lv_path"
|
|
||||||
ret=$?
|
|
||||||
[ $ret != 0 ] && [ $ret != 134 ] && {
|
|
||||||
lvm_cmd lvchange -a n "$lv_full_name" || return $?
|
|
||||||
return $ret
|
|
||||||
}
|
|
||||||
else
|
|
||||||
mke2fs -F -L "$1" "$lv_path" || {
|
|
||||||
ret=$?
|
|
||||||
lvm_cmd lvchange -a n "$lv_full_name" || return $?
|
|
||||||
return $ret
|
|
||||||
}
|
|
||||||
fi
|
|
||||||
uvol_uci_add "$1" "/dev/$(getdev "$1")" "rw"
|
|
||||||
lvm_cmd lvchange -a n "$lv_full_name" || return $?
|
|
||||||
lvm_cmd lvrename "$vg_name" "wp_$1" "rw_$1" || return $?
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
removevol() {
|
|
||||||
exportlv "$1"
|
|
||||||
[ "$lv_full_name" ] || return 2
|
|
||||||
[ "$lv_active" = "active" ] && return 16
|
|
||||||
lvm_cmd lvremove -y "$lv_full_name" || return $?
|
|
||||||
uvol_uci_remove "$1"
|
|
||||||
uvol_uci_commit "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
updatevol() {
|
|
||||||
exportlv "$1"
|
|
||||||
[ "$lv_full_name" ] || return 2
|
|
||||||
[ "$lv_size" -ge "$2" ] || return 27
|
|
||||||
case "$lv_path" in
|
|
||||||
/dev/*/wo_*)
|
|
||||||
lvm_cmd lvchange -p rw "$lv_full_name" || return $?
|
|
||||||
lvm_cmd lvchange -a y "$lv_full_name" || return $?
|
|
||||||
dd of="$lv_path"
|
|
||||||
uvol_uci_add "$1" "/dev/$(getdev "$1")" "ro"
|
|
||||||
lvm_cmd lvchange -a n "$lv_full_name" || return $?
|
|
||||||
lvm_cmd lvchange -p r "$lv_full_name" || return $?
|
|
||||||
lvm_cmd lvrename "$lv_full_name" "${lv_full_name%%/*}/ro_$1" || return $?
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
default)
|
|
||||||
return 22
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
listvols() {
|
|
||||||
local reports rep lv lvs lv_name lv_size lv_mode volname json_output json_notfirst
|
|
||||||
if [ "$1" = "-j" ]; then
|
|
||||||
json_output=1
|
|
||||||
echo "["
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
volname=${1:-.*}
|
|
||||||
json_init
|
|
||||||
json_load "$(lvs -o lv_name,lv_size -S "lv_name=~^[rw][owp]_$volname\$ && vg_name=$vg_name")"
|
|
||||||
json_select report
|
|
||||||
json_get_keys reports
|
|
||||||
for rep in $reports; do
|
|
||||||
json_select "$rep"
|
|
||||||
json_select lv
|
|
||||||
json_get_keys lvs
|
|
||||||
for lv in $lvs; do
|
|
||||||
json_select "$lv"
|
|
||||||
json_get_vars lv_name lv_size
|
|
||||||
lv_mode="${lv_name:0:2}"
|
|
||||||
lv_name="${lv_name:3}"
|
|
||||||
lv_size=${lv_size%B}
|
|
||||||
if [ "${lv_name:0:1}" != "." ]; then
|
|
||||||
if [ "$json_output" = "1" ]; then
|
|
||||||
[ "$json_notfirst" = "1" ] && echo ","
|
|
||||||
echo -e "\t{"
|
|
||||||
echo -e "\t\t\"name\": \"$lv_name\","
|
|
||||||
echo -e "\t\t\"mode\": \"$lv_mode\","
|
|
||||||
echo -e "\t\t\"size\": $lv_size"
|
|
||||||
echo -n -e "\t}"
|
|
||||||
json_notfirst=1
|
|
||||||
else
|
|
||||||
echo "$lv_name $lv_mode $lv_size"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
json_select ..
|
|
||||||
done
|
|
||||||
json_select ..
|
|
||||||
break
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$json_output" = "1" ]; then
|
|
||||||
[ "$json_notfirst" = "1" ] && echo
|
|
||||||
echo "]"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
detect() {
|
|
||||||
local reports rep lv lvs lv_name lv_full_name lv_mode volname devname
|
|
||||||
local temp_up=""
|
|
||||||
|
|
||||||
json_init
|
|
||||||
json_load "$(lvs -o lv_full_name -S "lv_name=~^[rw][owp]_.*\$ && vg_name=$vg_name && lv_skip_activation!=0")"
|
|
||||||
json_select report
|
|
||||||
json_get_keys reports
|
|
||||||
for rep in $reports; do
|
|
||||||
json_select "$rep"
|
|
||||||
json_select lv
|
|
||||||
json_get_keys lvs
|
|
||||||
for lv in $lvs; do
|
|
||||||
json_select "$lv"
|
|
||||||
json_get_vars lv_full_name
|
|
||||||
echo "lvchange -a y $lv_full_name"
|
|
||||||
lvm_cmd lvchange -k n "$lv_full_name"
|
|
||||||
lvm_cmd lvchange -a y "$lv_full_name"
|
|
||||||
temp_up="$temp_up $lv_full_name"
|
|
||||||
json_select ..
|
|
||||||
done
|
|
||||||
json_select ..
|
|
||||||
break
|
|
||||||
done
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
uvol_uci_init
|
|
||||||
|
|
||||||
json_init
|
|
||||||
json_load "$(lvs -o lv_name,lv_dm_path -S "lv_name=~^[rw][owp]_.*\$ && vg_name=$vg_name")"
|
|
||||||
json_select report
|
|
||||||
json_get_keys reports
|
|
||||||
for rep in $reports; do
|
|
||||||
json_select "$rep"
|
|
||||||
json_select lv
|
|
||||||
json_get_keys lvs
|
|
||||||
for lv in $lvs; do
|
|
||||||
json_select "$lv"
|
|
||||||
json_get_vars lv_name lv_dm_path
|
|
||||||
lv_mode="${lv_name:0:2}"
|
|
||||||
lv_name="${lv_name:3}"
|
|
||||||
echo uvol_uci_add "$lv_name" "/dev/$(getdev "$lv_name")" "$lv_mode"
|
|
||||||
uvol_uci_add "$lv_name" "/dev/$(getdev "$lv_name")" "$lv_mode"
|
|
||||||
json_select ..
|
|
||||||
done
|
|
||||||
json_select ..
|
|
||||||
break
|
|
||||||
done
|
|
||||||
|
|
||||||
uvol_uci_commit
|
|
||||||
|
|
||||||
for lv_full_name in $temp_up; do
|
|
||||||
echo "lvchange -a n $lv_full_name"
|
|
||||||
lvm_cmd lvchange -a n "$lv_full_name"
|
|
||||||
lvm_cmd lvchange -k y "$lv_full_name"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
boot() {
|
|
||||||
true ; # nothing to do, lvm does it all for us
|
|
||||||
}
|
|
||||||
|
|
||||||
exportpv
|
|
||||||
exportvg
|
|
||||||
|
|
||||||
case "$cmd" in
|
|
||||||
align)
|
|
||||||
echo "$vg_extent_size"
|
|
||||||
;;
|
|
||||||
free)
|
|
||||||
freebytes
|
|
||||||
;;
|
|
||||||
total)
|
|
||||||
totalbytes
|
|
||||||
;;
|
|
||||||
detect)
|
|
||||||
detect
|
|
||||||
;;
|
|
||||||
boot)
|
|
||||||
boot
|
|
||||||
;;
|
|
||||||
list)
|
|
||||||
listvols "$@"
|
|
||||||
;;
|
|
||||||
create)
|
|
||||||
createvol "$@"
|
|
||||||
;;
|
|
||||||
remove)
|
|
||||||
removevol "$@"
|
|
||||||
;;
|
|
||||||
device)
|
|
||||||
getuserdev "$@"
|
|
||||||
;;
|
|
||||||
size)
|
|
||||||
getsize "$@"
|
|
||||||
;;
|
|
||||||
up)
|
|
||||||
activatevol "$@"
|
|
||||||
;;
|
|
||||||
down)
|
|
||||||
disactivatevol "$@"
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
getstatus "$@"
|
|
||||||
;;
|
|
||||||
write)
|
|
||||||
updatevol "$@"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "unknown command"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
467
utils/uvol/files/lvm.uc
Normal file
467
utils/uvol/files/lvm.uc
Normal file
|
@ -0,0 +1,467 @@
|
||||||
|
{%
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
// LVM2 backend for uvol
|
||||||
|
// (c) 2022 Daniel Golle <daniel@makrotopia.org>
|
||||||
|
//
|
||||||
|
// This plugin uses LVM2 as a storage backend for uvol.
|
||||||
|
//
|
||||||
|
// By default, volumes are allocated on the physical device used for booting,
|
||||||
|
// the LVM2 PV and VG are initialized auto-magically by the 'autopart' script.
|
||||||
|
// By setting the UCI option 'vg_name' in the 'uvol' section in /etc/config/fstab
|
||||||
|
// you may set an arbitrary LVM2 volume group to back uvol instad.
|
||||||
|
|
||||||
|
let lvm_exec = "/sbin/lvm";
|
||||||
|
|
||||||
|
function lvm(cmd, ...args) {
|
||||||
|
let lvm_json_cmds = [ "lvs", "pvs", "vgs" ];
|
||||||
|
try {
|
||||||
|
let json_param = "";
|
||||||
|
if (cmd in lvm_json_cmds)
|
||||||
|
json_param = "--reportformat json --units b ";
|
||||||
|
let stdout = fs.popen(sprintf("LVM_SUPPRESS_FD_WARNINGS=1 %s %s %s%s", lvm_exec, cmd, json_param, join(" ", args)));
|
||||||
|
let tmp;
|
||||||
|
if (stdout) {
|
||||||
|
tmp = stdout.read("all");
|
||||||
|
let ret = {};
|
||||||
|
ret.retval = stdout.close();
|
||||||
|
if (json_param) {
|
||||||
|
let data = json(tmp);
|
||||||
|
if (data.report)
|
||||||
|
ret.report = data.report[0];
|
||||||
|
} else {
|
||||||
|
ret.stdout = trim(tmp);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
printf("lvm cli command failed: %s\n", fs.error());
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
printf("Failed to parse lvm cli output: %s\n%s\n", e, e.stacktrace[0].context);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pvs() {
|
||||||
|
let fstab = cursor.get_all('fstab');
|
||||||
|
for (let k, section in fstab) {
|
||||||
|
if (section['.type'] != 'uvol' || !section.vg_name)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return section.vg_name;
|
||||||
|
}
|
||||||
|
include("/usr/lib/uvol/blockdev_common.uc");
|
||||||
|
let rootdev = blockdev_common.get_partition(blockdev_common.get_bootdev(), 0);
|
||||||
|
let tmp = lvm("pvs", "-o", "vg_name", "-S", sprintf("\"pv_name=~^/dev/%s.*\$\"", rootdev));
|
||||||
|
if (tmp.report.pv)
|
||||||
|
return tmp.report.pv[0].vg_name;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function vgs(vg_name) {
|
||||||
|
let tmp = lvm("vgs", "-o", "vg_extent_size,vg_extent_count,vg_free_count", "-S", sprintf("\"vg_name=%s\"", vg_name));
|
||||||
|
let ret = null;
|
||||||
|
if (tmp && tmp.report.vg) {
|
||||||
|
ret = tmp.report.vg;
|
||||||
|
for (let r in ret) {
|
||||||
|
r.vg_extent_size = +(rtrim(r.vg_extent_size, "B"));
|
||||||
|
r.vg_extent_count = +r.vg_extent_count;
|
||||||
|
r.vg_free_count = +r.vg_free_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret)
|
||||||
|
return ret[0];
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvs(vg_name, vol_name, extra_exp) {
|
||||||
|
let ret = [];
|
||||||
|
if (!vol_name)
|
||||||
|
vol_name = ".*";
|
||||||
|
|
||||||
|
let lvexpr = sprintf("\"lvname=~^[rw][owp]_%s\$ && vg_name=%s%s%s\"",
|
||||||
|
vol_name, vg_name, extra_exp?" && ":"", extra_exp?extra_exp:"");
|
||||||
|
let tmp = lvm("lvs", "-o", "lv_active,lv_name,lv_full_name,lv_size,lv_path,lv_dm_path", "-S", lvexpr);
|
||||||
|
if (tmp && tmp.report.lv) {
|
||||||
|
ret = tmp.report.lv;
|
||||||
|
for (let r in ret) {
|
||||||
|
r.lv_size = +(rtrim(r.lv_size, "B"));
|
||||||
|
r.lv_active = (r.lv_active == "active");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getdev(lv) {
|
||||||
|
if (!lv)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
for (let dms in fs.glob("/sys/devices/virtual/block/dm-*")) {
|
||||||
|
let f = fs.open(sprintf("%s/dm/name", dms), "r");
|
||||||
|
if (!f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let dm_name = trim(f.read("all"));
|
||||||
|
f.close();
|
||||||
|
if ( split(lv.lv_dm_path, '/')[-1] == dm_name )
|
||||||
|
return split(dms, '/')[-1]
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_init(ctx) {
|
||||||
|
cursor = ctx.cursor;
|
||||||
|
fs = ctx.fs;
|
||||||
|
if (!fs.access(lvm_exec, "x"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
vg_name = pvs();
|
||||||
|
if (!vg_name)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
vg = vgs(vg_name);
|
||||||
|
uvol_uci_add = ctx.uci_add;
|
||||||
|
uvol_uci_commit = ctx.uci_commit;
|
||||||
|
uvol_uci_remove = ctx.uci_remove;
|
||||||
|
uvol_uci_init = ctx.uci_init;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_free() {
|
||||||
|
if (!vg || !vg.vg_free_count || !vg.vg_extent_size)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
return sprintf("%d", vg.vg_free_count * vg.vg_extent_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_total() {
|
||||||
|
if (!vg || !vg.vg_extent_count || !vg.vg_extent_size)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
return sprintf("%d", vg.vg_extent_count * vg.vg_extent_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_align() {
|
||||||
|
if (!vg || !vg.vg_extent_size)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
return sprintf("%d", vg.vg_extent_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_list(vol_name) {
|
||||||
|
let vols = [];
|
||||||
|
|
||||||
|
if (!vg_name)
|
||||||
|
return vols;
|
||||||
|
|
||||||
|
let res = lvs(vg_name, vol_name);
|
||||||
|
for (let lv in res) {
|
||||||
|
let vol = {};
|
||||||
|
if (substr(lv.lv_name, 3, 1) == ".")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vol.name = substr(lv.lv_name, 3);
|
||||||
|
vol.mode = substr(lv.lv_name, 0, 2);
|
||||||
|
if (!lv.lv_active) {
|
||||||
|
if (vol.mode == "ro")
|
||||||
|
vol.mode = "rd";
|
||||||
|
if (vol.mode == "rw")
|
||||||
|
vol.mode = "wd";
|
||||||
|
}
|
||||||
|
vol.size = lv.lv_size;
|
||||||
|
push(vols, vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vols;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_size(vol_name) {
|
||||||
|
if (!vol_name || !vg_name)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
let res = lvs(vg_name, vol_name);
|
||||||
|
if (!res[0])
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
return sprintf("%d", res[0].lv_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_status(vol_name) {
|
||||||
|
if (!vol_name || !vg_name)
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
let res = lvs(vg_name, vol_name);
|
||||||
|
if (!res[0])
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
let mode = substr(res[0].lv_name, 0, 2);
|
||||||
|
if ((mode != "ro" && mode != "rw") || !res[0].lv_active)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_device(vol_name) {
|
||||||
|
if (!vol_name || !vg_name)
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
let res = lvs(vg_name, vol_name);
|
||||||
|
if (!res[0])
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
let mode = substr(res[0].lv_name, 0, 2);
|
||||||
|
if ((mode != "ro" && mode != "rw") || !res[0].lv_active)
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
return getdev(res[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_updown(vol_name, up) {
|
||||||
|
if (!vol_name || !vg_name)
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
let res = lvs(vg_name, vol_name);
|
||||||
|
if (!res[0])
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
let lv = res[0];
|
||||||
|
if (!lv.lv_path)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
if (up && (wildcard(lv.lv_path, "/dev/*/wo_*") ||
|
||||||
|
wildcard(lv.lv_path, "/dev/*/wp_*")))
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
if (up)
|
||||||
|
uvol_uci_commit(vol_name);
|
||||||
|
|
||||||
|
if (lv.lv_active == up)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!up) {
|
||||||
|
let devname = getdev(lv);
|
||||||
|
if (devname)
|
||||||
|
system(sprintf("umount /dev/%s", devname));
|
||||||
|
}
|
||||||
|
|
||||||
|
let lvchange_r = lvm("lvchange", up?"-k":"-a", "n", lv.lv_full_name);
|
||||||
|
if (up && lvchange_r.retval != 0)
|
||||||
|
return lvchange_r.retval;
|
||||||
|
|
||||||
|
lvchange_r = lvm("lvchange", up?"-a":"-k", "y", lv.lv_full_name);
|
||||||
|
if (lvchange_r.retval != 0)
|
||||||
|
return lvchange_r.retval;
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_up(vol_name) {
|
||||||
|
return lvm_updown(vol_name, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_down(vol_name) {
|
||||||
|
return lvm_updown(vol_name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_create(vol_name, vol_size, vol_mode) {
|
||||||
|
if (!vol_name || !vg_name)
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
vol_size = +vol_size;
|
||||||
|
if (vol_size <= 0)
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
let res = lvs(vg_name, vol_name);
|
||||||
|
if (res[0])
|
||||||
|
return 17;
|
||||||
|
|
||||||
|
let size_ext = vol_size / vg.vg_extent_size;
|
||||||
|
if (vol_size % vg.vg_extent_size)
|
||||||
|
++size_ext;
|
||||||
|
let lvmode, mode;
|
||||||
|
if (vol_mode == "ro" || vol_mode == "wo") {
|
||||||
|
lvmode = "r";
|
||||||
|
mode = "wo";
|
||||||
|
} else if (vol_mode == "rw") {
|
||||||
|
lvmode = "rw";
|
||||||
|
mode = "wp";
|
||||||
|
} else {
|
||||||
|
return 22;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = lvm("lvcreate", "-p", lvmode, "-a", "n", "-y", "-W", "n", "-Z", "n", "-n", sprintf("%s_%s", mode, vol_name), "-l", size_ext, vg_name);
|
||||||
|
if (ret.retval != 0 || lvmode == "r")
|
||||||
|
return ret.retval;
|
||||||
|
|
||||||
|
let lv = lvs(vg_name, vol_name);
|
||||||
|
if (!lv[0] || !lv[0].lv_full_name)
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
lv = lv[0];
|
||||||
|
let ret = lvm("lvchange", "-a", "y", lv.lv_full_name);
|
||||||
|
if (ret.retval != 0)
|
||||||
|
return ret.retval;
|
||||||
|
|
||||||
|
let use_f2fs = (lv.lv_size > (100 * 1024 * 1024));
|
||||||
|
if (use_f2fs) {
|
||||||
|
let mkfs_ret = system(sprintf("/usr/sbin/mkfs.f2fs -f -l \"%s\" \"%s\"", vol_name, lv.lv_path));
|
||||||
|
if (mkfs_ret != 0 && mkfs_ret != 134) {
|
||||||
|
lvchange_r = lvm("lvchange", "-a", "n", lv.lv_full_name);
|
||||||
|
if (lvchange_r.retval != 0)
|
||||||
|
return lvchange_r.retval;
|
||||||
|
return mkfs_ret;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mkfs_ret = system(sprintf("/usr/sbin/mke2fs -F -L \"%s\" \"%s\"", vol_name, lv.lv_path));
|
||||||
|
if (mkfs_ret != 0) {
|
||||||
|
lvchange_r = lvm("lvchange", "-a", "n", lv.lv_full_name);
|
||||||
|
if (lvchange_r.retval != 0)
|
||||||
|
return lvchange_r.retval;
|
||||||
|
return mkfs_ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uvol_uci_add(vol_name, sprintf("/dev/%s", getdev(lv)), "rw");
|
||||||
|
|
||||||
|
ret = lvm("lvchange", "-a", "n", lv.lv_full_name);
|
||||||
|
if (ret.retval != 0)
|
||||||
|
return ret.retval;
|
||||||
|
|
||||||
|
ret = lvm("lvrename", vg_name, sprintf("wp_%s", vol_name), sprintf("rw_%s", vol_name));
|
||||||
|
if (ret.retval != 0)
|
||||||
|
return ret.retval;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_remove(vol_name) {
|
||||||
|
if (!vol_name || !vg_name)
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
let res = lvs(vg_name, vol_name);
|
||||||
|
if (!res[0])
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
if (res[0].lv_active)
|
||||||
|
return 16;
|
||||||
|
|
||||||
|
let ret = lvm("lvremove", "-y", res[0].lv_full_name);
|
||||||
|
if (ret.retval != 0)
|
||||||
|
return ret.retval;
|
||||||
|
|
||||||
|
uvol_uci_remove(vol_name);
|
||||||
|
uvol_uci_commit(vol_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_dd(in_fd, out_fd, vol_size) {
|
||||||
|
let rem = vol_size;
|
||||||
|
let buf;
|
||||||
|
while ((buf = in_fd.read(vg.vg_extent_size)) && (rem > 0)) {
|
||||||
|
rem -= length(buf);
|
||||||
|
if (rem < 0) {
|
||||||
|
buf = substr(buf, 0, rem);
|
||||||
|
}
|
||||||
|
out_fd.write(buf);
|
||||||
|
}
|
||||||
|
return rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_write(vol_name, vol_size) {
|
||||||
|
if (!vol_name || !vg_name)
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
let lv = lvs(vg_name, vol_name);
|
||||||
|
if (!lv[0] || !lv[0].lv_full_name)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
lv = lv[0];
|
||||||
|
vol_size = +vol_size;
|
||||||
|
if (vol_size > lv.lv_size)
|
||||||
|
return 27;
|
||||||
|
|
||||||
|
if (wildcard(lv.lv_path, "/dev/*/wo_*")) {
|
||||||
|
let ret = lvm("lvchange", "-p", "rw", lv.lv_full_name);
|
||||||
|
if (ret.retval != 0)
|
||||||
|
return ret.retval;
|
||||||
|
|
||||||
|
let ret = lvm("lvchange", "-a", "y", lv.lv_full_name);
|
||||||
|
if (ret.retval != 0)
|
||||||
|
return ret.retval;
|
||||||
|
|
||||||
|
let volfile = fs.open(lv.lv_path, "w");
|
||||||
|
let ret = lvm_dd(fs.stdin, volfile, vol_size);
|
||||||
|
volfile.close();
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("more %d bytes data than given size!\n", -ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret > 0) {
|
||||||
|
printf("reading finished %d bytes before given size!\n", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
uvol_uci_add(vol_name, sprintf("/dev/%s", getdev(lv)), "ro");
|
||||||
|
|
||||||
|
let ret = lvm("lvchange", "-a", "n", lv.lv_full_name);
|
||||||
|
if (ret.retval != 0)
|
||||||
|
return ret.retval;
|
||||||
|
|
||||||
|
let ret = lvm("lvchange", "-p", "r", lv.lv_full_name);
|
||||||
|
if (ret.retval != 0)
|
||||||
|
return ret.retval;
|
||||||
|
|
||||||
|
let ret = lvm("lvrename", vg_name, sprintf("wo_%s", vol_name), sprintf("ro_%s", vol_name));
|
||||||
|
if (ret.retval != 0)
|
||||||
|
return ret.retval;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return 22;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_detect() {
|
||||||
|
let temp_up = [];
|
||||||
|
let inactive_lv = lvs(vg_name, null, "lv_skip_activation!=0");
|
||||||
|
for (let lv in inactive_lv) {
|
||||||
|
lvm("lvchange", "-k", "n", lv.lv_full_name);
|
||||||
|
lvm("lvchange", "-a", "y", lv.lv_full_name);
|
||||||
|
push(temp_up, lv.lv_full_name);
|
||||||
|
}
|
||||||
|
sleep(1000);
|
||||||
|
uvol_uci_init();
|
||||||
|
for (let lv in lvs(vg_name)) {
|
||||||
|
let vol_name = substr(lv.lv_name, 3);
|
||||||
|
let vol_mode = substr(lv.lv_name, 0, 2);
|
||||||
|
uvol_uci_add(vol_name, sprintf("/dev/%s", getdev(lv)), vol_mode);
|
||||||
|
}
|
||||||
|
uvol_uci_commit();
|
||||||
|
for (let lv_full_name in temp_up) {
|
||||||
|
lvm("lvchange", "-a", "n", lv_full_name);
|
||||||
|
lvm("lvchange", "-k", "y", lv_full_name);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lvm_boot() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
backend.backend = "LVM";
|
||||||
|
backend.priority = 50;
|
||||||
|
backend.init = lvm_init;
|
||||||
|
backend.boot = lvm_boot;
|
||||||
|
backend.detect = lvm_detect;
|
||||||
|
backend.free = lvm_free;
|
||||||
|
backend.align = lvm_align;
|
||||||
|
backend.total = lvm_total;
|
||||||
|
backend.list = lvm_list;
|
||||||
|
backend.size = lvm_size;
|
||||||
|
backend.status = lvm_status;
|
||||||
|
backend.device = lvm_device;
|
||||||
|
backend.up = lvm_up;
|
||||||
|
backend.down = lvm_down;
|
||||||
|
backend.create = lvm_create;
|
||||||
|
backend.remove = lvm_remove;
|
||||||
|
backend.write = lvm_write;
|
||||||
|
%}
|
|
@ -1,358 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
cmd="$1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
if [ "$cmd" = "name" ]; then
|
|
||||||
echo "UBI"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
test -e /sys/class/ubi/version || return 0
|
|
||||||
read -r ubiver < /sys/class/ubi/version
|
|
||||||
[ "$ubiver" = "1" ] || return 1
|
|
||||||
test -e /sys/devices/virtual/ubi || return 0
|
|
||||||
|
|
||||||
ubidev=$(ls -1 /sys/devices/virtual/ubi | head -n 1)
|
|
||||||
|
|
||||||
read -r ebsize < "/sys/devices/virtual/ubi/${ubidev}/eraseblock_size"
|
|
||||||
|
|
||||||
. /lib/functions/uvol.sh
|
|
||||||
|
|
||||||
freebytes() {
|
|
||||||
read -r availeb < "/sys/devices/virtual/ubi/${ubidev}/avail_eraseblocks"
|
|
||||||
echo $((availeb * ebsize))
|
|
||||||
}
|
|
||||||
|
|
||||||
totalbytes() {
|
|
||||||
read -r totaleb < "/sys/devices/virtual/ubi/${ubidev}/total_eraseblocks"
|
|
||||||
echo $((totaleb * ebsize))
|
|
||||||
}
|
|
||||||
|
|
||||||
getdev() {
|
|
||||||
local voldir volname
|
|
||||||
for voldir in "/sys/devices/virtual/ubi/${ubidev}/${ubidev}_"*; do
|
|
||||||
read -r volname < "${voldir}/name"
|
|
||||||
case "$volname" in
|
|
||||||
uvol-[rw][owpd]-$1)
|
|
||||||
basename "$voldir"
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
continue
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
vol_is_mode() {
|
|
||||||
local voldev="$1"
|
|
||||||
local volname
|
|
||||||
read -r volname < "/sys/devices/virtual/ubi/${ubidev}/${voldev}/name"
|
|
||||||
case "$volname" in
|
|
||||||
uvol-$2-*)
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
getstatus() {
|
|
||||||
local voldev
|
|
||||||
voldev="$(getdev "$@")"
|
|
||||||
[ "$voldev" ] || return 2
|
|
||||||
vol_is_mode "$voldev" wo && return 22
|
|
||||||
vol_is_mode "$voldev" wp && return 16
|
|
||||||
vol_is_mode "$voldev" wd && return 1
|
|
||||||
vol_is_mode "$voldev" ro && [ ! -e "/dev/ubiblock${voldev:3}" ] && return 1
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
getsize() {
|
|
||||||
local voldev
|
|
||||||
voldev="$(getdev "$@")"
|
|
||||||
[ "$voldev" ] || return 2
|
|
||||||
cat "/sys/devices/virtual/ubi/${ubidev}/${voldev}/data_bytes"
|
|
||||||
}
|
|
||||||
|
|
||||||
getuserdev() {
|
|
||||||
local voldev
|
|
||||||
voldev="$(getdev "$@")"
|
|
||||||
[ "$voldev" ] || return 2
|
|
||||||
if vol_is_mode "$voldev" ro ; then
|
|
||||||
echo "/dev/ubiblock${voldev:3}"
|
|
||||||
elif vol_is_mode "$voldev" rw ; then
|
|
||||||
echo "/dev/$voldev"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
mkubifs() {
|
|
||||||
local tmp_mp
|
|
||||||
tmp_mp="$(mktemp -d)"
|
|
||||||
mount -t ubifs "$1" "$tmp_mp" || return $?
|
|
||||||
umount "$tmp_mp" || return $?
|
|
||||||
rmdir "$tmp_mp" || return $?
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
createvol() {
|
|
||||||
local mode ret voldev
|
|
||||||
voldev=$(getdev "$@")
|
|
||||||
[ "$voldev" ] && return 17
|
|
||||||
case "$3" in
|
|
||||||
ro|wo)
|
|
||||||
mode=wo
|
|
||||||
;;
|
|
||||||
rw)
|
|
||||||
mode=wp
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
return 22
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
ubimkvol "/dev/$ubidev" -N "uvol-$mode-$1" -s "$2" || return $?
|
|
||||||
ret=$?
|
|
||||||
[ $ret -eq 0 ] || return $ret
|
|
||||||
voldev="$(getdev "$@")"
|
|
||||||
ubiupdatevol -t "/dev/$voldev" || return $?
|
|
||||||
[ "$mode" = "wp" ] || return 0
|
|
||||||
mkubifs "/dev/$voldev" || return $?
|
|
||||||
uvol_uci_add "$1" "/dev/$voldev" "rw"
|
|
||||||
ubirename "/dev/$ubidev" "uvol-wp-$1" "uvol-wd-$1" || return $?
|
|
||||||
}
|
|
||||||
|
|
||||||
removevol() {
|
|
||||||
local voldev volnum
|
|
||||||
voldev=$(getdev "$@")
|
|
||||||
[ "$voldev" ] || return 2
|
|
||||||
vol_is_mode "$voldev" rw && return 16
|
|
||||||
vol_is_mode "$voldev" ro && return 16
|
|
||||||
volnum="${voldev#${ubidev}_}"
|
|
||||||
ubirmvol "/dev/$ubidev" -n "$volnum" || return $?
|
|
||||||
uvol_uci_remove "$1"
|
|
||||||
uvol_uci_commit "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
block_hotplug() {
|
|
||||||
export ACTION="$1"
|
|
||||||
export DEVNAME="$2"
|
|
||||||
/sbin/block hotplug
|
|
||||||
}
|
|
||||||
|
|
||||||
activatevol() {
|
|
||||||
local voldev
|
|
||||||
voldev="$(getdev "$@")"
|
|
||||||
[ "$voldev" ] || return 2
|
|
||||||
vol_is_mode "$voldev" rw && return 0
|
|
||||||
vol_is_mode "$voldev" ro && return 0
|
|
||||||
vol_is_mode "$voldev" wo && return 22
|
|
||||||
vol_is_mode "$voldev" wp && return 16
|
|
||||||
uvol_uci_commit "$1"
|
|
||||||
if vol_is_mode "$voldev" rd; then
|
|
||||||
ubirename "/dev/$ubidev" "uvol-rd-$1" "uvol-ro-$1" || return $?
|
|
||||||
ubiblock --create "/dev/$voldev" || return $?
|
|
||||||
return 0
|
|
||||||
elif vol_is_mode "$voldev" wd; then
|
|
||||||
ubirename "/dev/$ubidev" "uvol-wd-$1" "uvol-rw-$1" || return $?
|
|
||||||
block_hotplug add "$voldev"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
disactivatevol() {
|
|
||||||
local voldev
|
|
||||||
voldev="$(getdev "$@")"
|
|
||||||
[ "$voldev" ] || return 2
|
|
||||||
vol_is_mode "$voldev" rd && return 0
|
|
||||||
vol_is_mode "$voldev" wd && return 0
|
|
||||||
vol_is_mode "$voldev" wo && return 22
|
|
||||||
vol_is_mode "$voldev" wp && return 16
|
|
||||||
if vol_is_mode "$voldev" ro; then
|
|
||||||
grep -q "^/dev/ubiblock${voldev:3}" /proc/self/mounts && umount "/dev/ubiblock${voldev:3}"
|
|
||||||
ubiblock --remove "/dev/$voldev"
|
|
||||||
ubirename "/dev/$ubidev" "uvol-ro-$1" "uvol-rd-$1" || return $?
|
|
||||||
return 0
|
|
||||||
elif vol_is_mode "$voldev" rw; then
|
|
||||||
umount "/dev/$voldev"
|
|
||||||
ubirename "/dev/$ubidev" "uvol-rw-$1" "uvol-wd-$1" || return $?
|
|
||||||
block_hotplug remove "$voldev"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
updatevol() {
|
|
||||||
local voldev
|
|
||||||
voldev="$(getdev "$@")"
|
|
||||||
[ "$voldev" ] || return 2
|
|
||||||
[ "$2" ] || return 22
|
|
||||||
vol_is_mode "$voldev" wo || return 22
|
|
||||||
ubiupdatevol -s "$2" "/dev/$voldev" -
|
|
||||||
ubiblock --create "/dev/$voldev"
|
|
||||||
uvol_uci_add "$1" "/dev/ubiblock${voldev:3}" "ro"
|
|
||||||
ubiblock --remove "/dev/$voldev"
|
|
||||||
ubirename "/dev/$ubidev" "uvol-wo-$1" "uvol-rd-$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
listvols() {
|
|
||||||
local volname volmode volsize json_output json_notfirst
|
|
||||||
if [ "$1" = "-j" ]; then
|
|
||||||
json_output=1
|
|
||||||
shift
|
|
||||||
echo "["
|
|
||||||
fi
|
|
||||||
for voldir in "/sys/devices/virtual/ubi/${ubidev}/${ubidev}_"*; do
|
|
||||||
read -r volname < "$voldir/name"
|
|
||||||
case "$volname" in
|
|
||||||
uvol-[rw][wod]*)
|
|
||||||
read -r volsize < "$voldir/data_bytes"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
continue
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
volmode="${volname:5:2}"
|
|
||||||
volname="${volname:8}"
|
|
||||||
[ "${volname:0:1}" = "." ] && continue
|
|
||||||
if [ "$json_output" = "1" ]; then
|
|
||||||
[ "$json_notfirst" = "1" ] && echo ","
|
|
||||||
echo -e "\t{"
|
|
||||||
echo -e "\t\t\"name\": \"$volname\","
|
|
||||||
echo -e "\t\t\"mode\": \"$volmode\","
|
|
||||||
echo -e "\t\t\"size\": $volsize"
|
|
||||||
echo -n -e "\t}"
|
|
||||||
json_notfirst=1
|
|
||||||
else
|
|
||||||
echo "$volname $volmode $volsize"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$json_output" = "1" ]; then
|
|
||||||
[ "$json_notfirst" = "1" ] && echo
|
|
||||||
echo "]"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
bootvols() {
|
|
||||||
local volname volmode volsize voldev fstype
|
|
||||||
for voldir in "/sys/devices/virtual/ubi/${ubidev}/${ubidev}_"*; do
|
|
||||||
read -r volname < "$voldir/name"
|
|
||||||
voldev="$(basename "$voldir")"
|
|
||||||
fstype=
|
|
||||||
case "$volname" in
|
|
||||||
uvol-ro-*)
|
|
||||||
ubiblock --create "/dev/$voldev" || return $?
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
continue
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
volmode="${volname:5:2}"
|
|
||||||
volname="${volname:8}"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
detect() {
|
|
||||||
local volname voldev volmode voldev fstype tmpdev=""
|
|
||||||
for voldir in "/sys/devices/virtual/ubi/${ubidev}/${ubidev}_"*; do
|
|
||||||
read -r volname < "$voldir/name"
|
|
||||||
voldev="$(basename "$voldir")"
|
|
||||||
fstype=
|
|
||||||
case "$volname" in
|
|
||||||
uvol-r[od]-*)
|
|
||||||
if ! [ -e "/dev/ubiblock${voldev:3}" ]; then
|
|
||||||
ubiblock --create "/dev/$voldev" || return $?
|
|
||||||
fi
|
|
||||||
case "$volname" in
|
|
||||||
uvol-rd-*)
|
|
||||||
tmpdev="$tmpdev $voldev"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
continue
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
volmode="${volname:5:2}"
|
|
||||||
volname="${volname:8}"
|
|
||||||
done
|
|
||||||
|
|
||||||
uvol_uci_init
|
|
||||||
|
|
||||||
for voldir in "/sys/devices/virtual/ubi/${ubidev}/${ubidev}_"*; do
|
|
||||||
read -r volname < "$voldir/name"
|
|
||||||
voldev="$(basename "$voldir")"
|
|
||||||
case "$volname" in
|
|
||||||
uvol-[rw][wod]*)
|
|
||||||
true
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
continue
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
volmode="${volname:5:2}"
|
|
||||||
volname="${volname:8}"
|
|
||||||
case "$volmode" in
|
|
||||||
"ro" | "rd")
|
|
||||||
uvol_uci_add "$volname" "/dev/ubiblock${voldev:3}" "ro"
|
|
||||||
;;
|
|
||||||
"rw" | "wd")
|
|
||||||
uvol_uci_add "$volname" "/dev/${voldev}" "rw"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
uvol_uci_commit
|
|
||||||
|
|
||||||
for voldev in $tmpdev ; do
|
|
||||||
ubiblock --remove "/dev/$voldev" || return $?
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$cmd" in
|
|
||||||
align)
|
|
||||||
echo "$ebsize"
|
|
||||||
;;
|
|
||||||
free)
|
|
||||||
freebytes
|
|
||||||
;;
|
|
||||||
total)
|
|
||||||
totalbytes
|
|
||||||
;;
|
|
||||||
detect)
|
|
||||||
detect
|
|
||||||
;;
|
|
||||||
boot)
|
|
||||||
bootvols
|
|
||||||
;;
|
|
||||||
list)
|
|
||||||
listvols "$@"
|
|
||||||
;;
|
|
||||||
create)
|
|
||||||
createvol "$@"
|
|
||||||
;;
|
|
||||||
remove)
|
|
||||||
removevol "$@"
|
|
||||||
;;
|
|
||||||
device)
|
|
||||||
getuserdev "$@"
|
|
||||||
;;
|
|
||||||
size)
|
|
||||||
getsize "$@"
|
|
||||||
;;
|
|
||||||
up)
|
|
||||||
activatevol "$@"
|
|
||||||
;;
|
|
||||||
down)
|
|
||||||
disactivatevol "$@"
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
getstatus "$@"
|
|
||||||
;;
|
|
||||||
write)
|
|
||||||
updatevol "$@"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "unknown command"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
378
utils/uvol/files/ubi.uc
Normal file
378
utils/uvol/files/ubi.uc
Normal file
|
@ -0,0 +1,378 @@
|
||||||
|
{%
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
// UBI backend for uvol
|
||||||
|
// (c) 2022 Daniel Golle <daniel@makrotopia.org>
|
||||||
|
//
|
||||||
|
// This plugin uses UBI on NAND flash as a storage backend for uvol.
|
||||||
|
|
||||||
|
function read_file(file) {
|
||||||
|
let fp = fs.open(file);
|
||||||
|
if (!fp)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
let var = rtrim(fp.read("all"));
|
||||||
|
fp.close();
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mkdtemp() {
|
||||||
|
math = require("math");
|
||||||
|
let r1 = math.rand();
|
||||||
|
let r2 = math.rand();
|
||||||
|
let randbytes = chr((r1 >> 24) & 0xff, (r1 >> 16) & 0xff, (r1 >> 8) & 0xff, r1 & 0xff,
|
||||||
|
(r2 >> 24) & 0xff, (r2 >> 16) & 0xff, (r2 >> 8) & 0xff, r2 & 0xff);
|
||||||
|
|
||||||
|
let randstr = replace(b64enc(randbytes), /[\/-_.=]/g, "");
|
||||||
|
let dirname = sprintf("/tmp/uvol-%s", randstr);
|
||||||
|
fs.mkdir(dirname, 0700);
|
||||||
|
return dirname;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_get_dev(vol_name) {
|
||||||
|
let wcstring = sprintf("uvol-[rw][owpd]-%s", vol_name);
|
||||||
|
for (vol_dir in fs.glob(sprintf("/sys/devices/virtual/ubi/%s/%s_*", ubidev, ubidev))) {
|
||||||
|
let vol_ubiname = read_file(sprintf("%s/name", vol_dir));
|
||||||
|
if (wildcard(vol_ubiname, wcstring))
|
||||||
|
return fs.basename(vol_dir);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function vol_get_mode(vol_dev, mode) {
|
||||||
|
let vol_name = read_file(sprintf("/sys/devices/virtual/ubi/%s/%s/name", ubidev, vol_dev));
|
||||||
|
return substr(vol_name, 5, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mkubifs(vol_dev) {
|
||||||
|
let temp_mp = mkdtemp();
|
||||||
|
system(sprintf("mount -t ubifs /dev/%s %s", vol_dev, temp_mp));
|
||||||
|
system(sprintf("umount %s", temp_mp));
|
||||||
|
fs.rmdir(temp_mp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function block_hotplug(action, devname) {
|
||||||
|
return system(sprintf("ACTION=%s DEVNAME=%s /sbin/block hotplug", action, devname));
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_init(ctx) {
|
||||||
|
cursor = ctx.cursor;
|
||||||
|
fs = ctx.fs;
|
||||||
|
|
||||||
|
let ubiver = read_file("/sys/class/ubi/version");
|
||||||
|
if (ubiver != 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let ubidevpath = null;
|
||||||
|
for (ubidevpath in fs.glob("/sys/devices/virtual/ubi/*"))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!ubidevpath)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ubidev = fs.basename(ubidevpath);
|
||||||
|
ebsize = read_file(sprintf("%s/eraseblock_size", ubidevpath));
|
||||||
|
|
||||||
|
uvol_uci_add = ctx.uci_add;
|
||||||
|
uvol_uci_commit = ctx.uci_commit;
|
||||||
|
uvol_uci_remove = ctx.uci_remove;
|
||||||
|
uvol_uci_init = ctx.uci_init;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_free() {
|
||||||
|
let availeb = read_file(sprintf("/sys/devices/virtual/ubi/%s/avail_eraseblocks", ubidev));
|
||||||
|
return sprintf("%d", availeb * ebsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_align() {
|
||||||
|
return sprintf("%d", ebsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_total() {
|
||||||
|
let totaleb = read_file(sprintf("/sys/devices/virtual/ubi/%s/total_eraseblocks", ubidev));
|
||||||
|
return sprintf("%d", totaleb * ebsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_status(vol_name) {
|
||||||
|
let vol_dev = ubi_get_dev(vol_name);
|
||||||
|
if (!vol_dev)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
let vol_mode = vol_get_mode(vol_dev);
|
||||||
|
if (vol_mode == "wo") return 22;
|
||||||
|
if (vol_mode == "wp") return 16;
|
||||||
|
if (vol_mode == "wd") return 1;
|
||||||
|
if (vol_mode == "ro" &&
|
||||||
|
!fs.access(sprintf("/dev/ubiblock%s", substr(vol_dev, 3)), "r")) return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_size(vol_name) {
|
||||||
|
let vol_dev = ubi_get_dev(vol_name);
|
||||||
|
if (!vol_dev)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
let vol_size = read_file(sprintf("/sys/devices/virtual/ubi/%s/%s/data_bytes", ubidev, vol_dev));
|
||||||
|
return sprintf("%d", vol_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_device(vol_name) {
|
||||||
|
let vol_dev = ubi_get_dev(vol_name);
|
||||||
|
if (!vol_dev)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
let vol_mode = vol_get_mode(vol_dev);
|
||||||
|
if (vol_mode == "ro")
|
||||||
|
return sprintf("/dev/ubiblock%s", substr(vol_dev, 3));
|
||||||
|
else if (vol_mode == "rw")
|
||||||
|
return sprintf("/dev/%s", vol_dev);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_create(vol_name, vol_size, vol_mode) {
|
||||||
|
let vol_dev = ubi_get_dev(vol_name);
|
||||||
|
if (vol_dev)
|
||||||
|
return 17;
|
||||||
|
|
||||||
|
let mode;
|
||||||
|
if (vol_mode == "ro" || vol_mode == "wo")
|
||||||
|
mode = "wo";
|
||||||
|
else if (vol_mode == "rw")
|
||||||
|
mode = "wp";
|
||||||
|
else
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
let vol_size = +vol_size;
|
||||||
|
if (vol_size <= 0)
|
||||||
|
return 22;
|
||||||
|
let ret = system(sprintf("ubimkvol /dev/%s -N \"uvol-%s-%s\" -s %d", ubidev, mode, vol_name, vol_size));
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
let vol_dev = ubi_get_dev(vol_name);
|
||||||
|
if (!vol_dev)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
let ret = system(sprintf("ubiupdatevol -t /dev/%s", vol_dev));
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (mode != "wp")
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
let ret = mkubifs(vol_dev);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
uvol_uci_add(vol_name, sprintf("/dev/%s", vol_dev), "rw");
|
||||||
|
|
||||||
|
let ret = system(sprintf("ubirename /dev/%s \"uvol-wp-%s\" \"uvol-wd-%s\"", ubidev, vol_name, vol_name));
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_remove(vol_name) {
|
||||||
|
let vol_dev = ubi_get_dev(vol_name);
|
||||||
|
if (!vol_dev)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
let vol_mode = vol_get_mode(vol_dev);
|
||||||
|
if (vol_mode == "rw" || vol_mode == "ro")
|
||||||
|
return 16;
|
||||||
|
|
||||||
|
let volnum = split(vol_dev, "_")[1];
|
||||||
|
|
||||||
|
let ret = system(sprintf("ubirmvol /dev/%s -n %d", ubidev, volnum));
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
uvol_uci_remove(vol_name);
|
||||||
|
uvol_uci_commit(vol_name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_up(vol_name) {
|
||||||
|
let vol_dev = ubi_get_dev(vol_name);
|
||||||
|
if (!vol_dev)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
let vol_mode = vol_get_mode(vol_dev);
|
||||||
|
if (vol_mode == "rw" || vol_mode == "ro")
|
||||||
|
return 0;
|
||||||
|
else if (vol_mode == "wo")
|
||||||
|
return 22;
|
||||||
|
else if (vol_mode == "wp")
|
||||||
|
return 16;
|
||||||
|
|
||||||
|
uvol_uci_commit(vol_name);
|
||||||
|
if (vol_mode == "rd") {
|
||||||
|
let ret = system(sprintf("ubirename /dev/%s \"uvol-rd-%s\" \"uvol-ro-%s\"", ubidev, vol_name, vol_name));
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return system(sprintf("ubiblock --create /dev/%s", vol_dev));
|
||||||
|
} else if (vol_mode == "wd") {
|
||||||
|
let ret = system(sprintf("ubirename /dev/%s \"uvol-wd-%s\" \"uvol-rw-%s\"", ubidev, vol_name, vol_name));
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return block_hotplug("add", vol_dev);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_down(vol_name) {
|
||||||
|
let vol_dev = ubi_get_dev(vol_name);
|
||||||
|
if (!vol_dev)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
let vol_mode = vol_get_mode(vol_dev);
|
||||||
|
if (vol_mode == "rd" || vol_mode == "wd")
|
||||||
|
return 0;
|
||||||
|
else if (vol_mode == "wo")
|
||||||
|
return 22;
|
||||||
|
else if (vol_mode == "wp")
|
||||||
|
return 16;
|
||||||
|
else if (vol_mode == "ro") {
|
||||||
|
system(sprintf("umount /dev/ubiblock%s 2>&1 >/dev/null", substr(vol_dev, 3)));
|
||||||
|
system(sprintf("ubiblock --remove /dev/%s", vol_dev));
|
||||||
|
let ret = system(sprintf("ubirename /dev/%s \"uvol-ro-%s\" \"uvol-rd-%s\"", ubidev, vol_name, vol_name));
|
||||||
|
return ret;
|
||||||
|
} else if (vol_mode == "rw") {
|
||||||
|
system(sprintf("umount /dev/%s 2>&1 >/dev/null", vol_dev));
|
||||||
|
let ret = system(sprintf("ubirename /dev/%s \"uvol-rw-%s\" \"uvol-wd-%s\"", ubidev, vol_name, vol_name));
|
||||||
|
block_hotplug("remove", vol_dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_list(search_name) {
|
||||||
|
let volumes = [];
|
||||||
|
for (vol_dir in fs.glob(sprintf("/sys/devices/virtual/ubi/%s/%s_*", ubidev, ubidev))) {
|
||||||
|
let vol = {};
|
||||||
|
let vol_ubiname = read_file(sprintf("%s/name", vol_dir));
|
||||||
|
if (!wildcard(vol_ubiname, "uvol-[rw][wod]-*"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let vol_mode = substr(vol_ubiname, 5, 2);
|
||||||
|
let vol_name = substr(vol_ubiname, 8);
|
||||||
|
let vol_size = read_file(sprintf("%s/data_bytes", vol_dir));
|
||||||
|
if (substr(vol_name, 0, 1) == ".")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vol.name = vol_name;
|
||||||
|
vol.mode = vol_mode;
|
||||||
|
vol.size = vol_size;
|
||||||
|
push(volumes, vol);
|
||||||
|
}
|
||||||
|
return volumes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_detect() {
|
||||||
|
let tmpdev = [];
|
||||||
|
for (vol_dir in fs.glob(sprintf("/sys/devices/virtual/ubi/%s/%s_*", ubidev, ubidev))) {
|
||||||
|
let vol_ubiname = read_file(sprintf("%s/name", vol_dir));
|
||||||
|
|
||||||
|
if (!wildcard(vol_ubiname, "uvol-r[od]-*"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let vol_name = substr(vol_ubiname, 8);
|
||||||
|
let vol_mode = substr(vol_ubiname, 5, 2);
|
||||||
|
let vol_dev = fs.basename(vol_dir);
|
||||||
|
|
||||||
|
ret = system(sprintf("ubiblock --create /dev/%s", vol_dev));
|
||||||
|
if (ret)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (vol_mode == "rd")
|
||||||
|
push(tmpdev, vol_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
uvol_uci_init();
|
||||||
|
|
||||||
|
for (vol_dir in fs.glob(sprintf("/sys/devices/virtual/ubi/%s/%s_*", ubidev, ubidev))) {
|
||||||
|
let vol_ubiname = read_file(sprintf("%s/name", vol_dir));
|
||||||
|
if (!wildcard(vol_ubiname, "uvol-[rw][wod]-*"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let vol_dev = fs.basename(vol_dir);
|
||||||
|
let vol_name = substr(vol_ubiname, 8);
|
||||||
|
let vol_mode = substr(vol_ubiname, 5, 2);
|
||||||
|
|
||||||
|
if (vol_mode == "ro" || vol_mode == "rd")
|
||||||
|
uvol_uci_add(vol_name, sprintf("/dev/ubiblock%s", substr(vol_dev, 3)), "ro");
|
||||||
|
else if (vol_mode == "rw" || vol_mode == "wd")
|
||||||
|
uvol_uci_add(vol_name, sprintf("/dev/%s", vol_dev), "rw");
|
||||||
|
}
|
||||||
|
|
||||||
|
uvol_uci_commit();
|
||||||
|
|
||||||
|
for (vol_dev in tmpdev)
|
||||||
|
system(sprintf("ubiblock --remove /dev/%s", vol_dev));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_boot() {
|
||||||
|
for (vol_dir in fs.glob(sprintf("/sys/devices/virtual/ubi/%s/%s_*", ubidev, ubidev))) {
|
||||||
|
let vol_dev = fs.basename(vol_dir);
|
||||||
|
let vol_ubiname = read_file(sprintf("%s/name", vol_dir));
|
||||||
|
|
||||||
|
if (!wildcard(vol_ubiname, "uvol-ro-*"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
system(sprintf("ubiblock --create /dev/%s", vol_dev));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ubi_write(vol_name, write_size) {
|
||||||
|
let vol_dev = ubi_get_dev(vol_name);
|
||||||
|
if (!vol_dev)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
write_size = +write_size;
|
||||||
|
if (write_size <= 0)
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
let vol_mode = vol_get_mode(vol_dev);
|
||||||
|
if (vol_mode != "wo")
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
let ret = system(sprintf("ubiupdatevol -s %s /dev/%s -", write_size, vol_dev));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
system(sprintf("ubiblock --create /dev/%s", vol_dev));
|
||||||
|
uvol_uci_add(vol_name, sprintf("/dev/ubiblock%s", substr(vol_dev, 3)), "ro");
|
||||||
|
system(sprintf("ubiblock --remove /dev/%s", vol_dev));
|
||||||
|
system(sprintf("ubirename /dev/%s \"uvol-wo-%s\" \"uvol-rd-%s\"", ubidev, vol_name, vol_name));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
backend.backend = "UBI";
|
||||||
|
backend.priority = 20;
|
||||||
|
backend.init = ubi_init;
|
||||||
|
backend.boot = ubi_boot;
|
||||||
|
backend.detect = ubi_detect;
|
||||||
|
backend.free = ubi_free;
|
||||||
|
backend.align = ubi_align;
|
||||||
|
backend.total = ubi_total;
|
||||||
|
backend.list = ubi_list;
|
||||||
|
backend.size = ubi_size;
|
||||||
|
backend.status = ubi_status;
|
||||||
|
backend.device = ubi_device;
|
||||||
|
backend.up = ubi_up;
|
||||||
|
backend.down = ubi_down;
|
||||||
|
backend.create = ubi_create;
|
||||||
|
backend.remove = ubi_remove;
|
||||||
|
backend.write = ubi_write;
|
||||||
|
%}
|
139
utils/uvol/files/uci.uc
Normal file
139
utils/uvol/files/uci.uc
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
{%
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
// UCI tools for uvol
|
||||||
|
// (c) 2022 Daniel Golle <daniel@makrotopia.org>
|
||||||
|
|
||||||
|
let uci_spooldir = "/var/spool/uvol";
|
||||||
|
let init_spooldir = function(void) {
|
||||||
|
parentdir = fs.stat(fs.dirname(uci_spooldir));
|
||||||
|
if (!parentdir || parentdir.type != "directory")
|
||||||
|
fs.mkdir(fs.dirname(uci_spooldir), 0755);
|
||||||
|
fs.mkdir(uci_spooldir, 0700);
|
||||||
|
};
|
||||||
|
|
||||||
|
uvol_uci = {
|
||||||
|
uvol_uci_add: function(vol_name, dev_name, mode) {
|
||||||
|
try {
|
||||||
|
let autofs = false;
|
||||||
|
let uuid;
|
||||||
|
let target;
|
||||||
|
if (mode == "ro")
|
||||||
|
autofs = true;
|
||||||
|
|
||||||
|
let uciname = replace(vol_name, /[-.]/g, "_");
|
||||||
|
uciname = replace(uciname, /!([:alnum:]_)/g, "");
|
||||||
|
let bdinfo_p = fs.popen("/sbin/block info");
|
||||||
|
let bdinfo_l;
|
||||||
|
while (bdinfo_l = bdinfo_p.read("line")) {
|
||||||
|
if (substr(bdinfo_l, 0, length(dev_name) + 1) != dev_name + ":")
|
||||||
|
continue;
|
||||||
|
let bdinfo_e = split(bdinfo_l, " ");
|
||||||
|
shift(bdinfo_e);
|
||||||
|
for (let bdinfo_a in bdinfo_e) {
|
||||||
|
let bdinfo_v = split(bdinfo_a, "=");
|
||||||
|
if (bdinfo_v[0] && bdinfo_v[0] == "UUID") {
|
||||||
|
uuid = trim(bdinfo_v[1], "\"");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!uuid)
|
||||||
|
return 22;
|
||||||
|
|
||||||
|
if (uciname == "_meta")
|
||||||
|
target = "/tmp/run/uvol/.meta";
|
||||||
|
else if (substr(uciname, 0, 1) == "_")
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
target = sprintf("/tmp/run/uvol/%s", vol_name);
|
||||||
|
|
||||||
|
init_spooldir();
|
||||||
|
let remspool = sprintf("%s/remove-%s", uci_spooldir, uciname);
|
||||||
|
if (fs.stat(remspool))
|
||||||
|
fs.unlink(remspool);
|
||||||
|
|
||||||
|
let addobj = {};
|
||||||
|
addobj.name=uciname;
|
||||||
|
addobj.uuid=uuid;
|
||||||
|
addobj.target=target;
|
||||||
|
addobj.options=mode;
|
||||||
|
addobj.autofs=autofs;
|
||||||
|
addobj.enabled=true;
|
||||||
|
|
||||||
|
let spoolfile = fs.open(sprintf("%s/add-%s", uci_spooldir, uciname), "w");
|
||||||
|
spoolfile.write(addobj);
|
||||||
|
spoolfile.close();
|
||||||
|
} catch(e) {
|
||||||
|
printf("adding UCI section to spool failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
uvol_uci_remove: function(vol_name) {
|
||||||
|
let uciname = replace(vol_name, /[-.]/g, "_");
|
||||||
|
uciname = replace(uciname, /!([:alnum:]_)/g, "");
|
||||||
|
|
||||||
|
let addspool = sprintf("%s/add-%s", uci_spooldir, uciname);
|
||||||
|
if (fs.stat(addspool)) {
|
||||||
|
fs.unlink(addspool);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
init_spooldir();
|
||||||
|
let spoolfile = fs.open(sprintf("%s/remove-%s", uci_spooldir, uciname), "w");
|
||||||
|
spoolfile.write(uciname);
|
||||||
|
spoolfile.close();
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
uvol_uci_commit: function(vol_name) {
|
||||||
|
try {
|
||||||
|
let uciname = null;
|
||||||
|
if (vol_name) {
|
||||||
|
uciname = replace(vol_name, /[-.]/g, "_");
|
||||||
|
uciname = replace(uciname, /!([:alnum:]_)/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let file in fs.glob(sprintf("%s/*-%s", uci_spooldir, uciname?uciname:"*"))) {
|
||||||
|
let action = split(fs.basename(file), "-")[0];
|
||||||
|
let spoolfd = fs.open(file, "r");
|
||||||
|
let spoolstr = spoolfd.read("all");
|
||||||
|
spoolfd.close();
|
||||||
|
fs.unlink(file);
|
||||||
|
if (action == "remove") {
|
||||||
|
cursor.delete("fstab", spoolstr);
|
||||||
|
} else if (action == "add") {
|
||||||
|
let spoolobj = json(spoolstr);
|
||||||
|
cursor.set("fstab", spoolobj.name, "mount");
|
||||||
|
for (key in keys(spoolobj)) {
|
||||||
|
if (key == "name")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cursor.set("fstab", spoolobj.name, key, spoolobj[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor.commit();
|
||||||
|
} catch(e) {
|
||||||
|
printf("committing UCI spool failed");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
uvol_uci_init: function () {
|
||||||
|
cursor.load("fstab");
|
||||||
|
let f = cursor.get("fstab", "@uvol[0]", "initialized");
|
||||||
|
if (f == 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cursor.add("fstab", "uvol");
|
||||||
|
cursor.set("fstab", "@uvol[-1]", "initialized", true);
|
||||||
|
cursor.commit();
|
||||||
|
cursor.unload("fstab");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
%}
|
|
@ -1,12 +1,11 @@
|
||||||
#!/bin/sh
|
#!/usr/bin/ucode
|
||||||
|
{%
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
// uvol - storage volume manager for OpenWrt
|
||||||
|
// (c) 2022 Daniel Golle <daniel@makrotopia.org>
|
||||||
|
|
||||||
# uvol prototype
|
function help() {
|
||||||
# future development roadmap (aka. to-do):
|
%}
|
||||||
# * re-implement in C (use libubox, execve lvm/ubi*)
|
|
||||||
# * hash to validate volume while writing
|
|
||||||
# * add atomic batch processing for use by container/package manager
|
|
||||||
|
|
||||||
if [ -z "$1" ]; then cat <<EOF
|
|
||||||
uvol storage volume manager
|
uvol storage volume manager
|
||||||
|
|
||||||
syntax: uvol command ...
|
syntax: uvol command ...
|
||||||
|
@ -30,23 +29,102 @@ commands:
|
||||||
1 - volume is not ready for use
|
1 - volume is not ready for use
|
||||||
2 - volume doesn'y exist
|
2 - volume doesn'y exist
|
||||||
write volname size write to volume from stdin, size in bytes
|
write volname size write to volume from stdin, size in bytes
|
||||||
EOF
|
{%
|
||||||
return 22
|
}
|
||||||
fi
|
|
||||||
|
|
||||||
uvol_backend=
|
let fs = require("fs");
|
||||||
backends_tried=
|
let uci = require("uci");
|
||||||
|
let cursor = uci ? uci.cursor() : null;
|
||||||
|
|
||||||
for backend in /usr/libexec/uvol/*.sh; do
|
let ctx = {};
|
||||||
total=$($backend total)
|
ctx.cursor = cursor;
|
||||||
backends_tried="$backends_tried $($backend name)"
|
ctx.fs = fs;
|
||||||
[ "$total" ] && uvol_backend=$backend
|
include("/usr/lib/uvol/uci.uc");
|
||||||
done
|
ctx.uci_add = uvol_uci.uvol_uci_add;
|
||||||
|
ctx.uci_remove = uvol_uci.uvol_uci_remove;
|
||||||
|
ctx.uci_commit = uvol_uci.uvol_uci_commit;
|
||||||
|
ctx.uci_init = uvol_uci.uvol_uci_init;
|
||||||
|
|
||||||
if [ -z "$uvol_backend" ]; then
|
let backend = null;
|
||||||
echo "No backend available. (tried:$backends_tried)"
|
let tried_backends = [];
|
||||||
echo "To setup devices with block storage install 'autopart'."
|
for (plugin in fs.glob("/usr/lib/uvol/backends/*.uc")) {
|
||||||
return 2
|
let current_backend = {};
|
||||||
fi
|
include(plugin, { backend: current_backend });
|
||||||
|
push(tried_backends, current_backend.backend);
|
||||||
|
if (type(backend) == "object" &&
|
||||||
|
type(backend.priority) == "int" &&
|
||||||
|
type(current_backend.priority) == "int" &&
|
||||||
|
backend.priority > current_backend.priority)
|
||||||
|
continue;
|
||||||
|
|
||||||
flock -x /tmp/run/uvol.lock "$uvol_backend" "$@"
|
if (type(current_backend.init) == "function" &&
|
||||||
|
current_backend.init(ctx)) {
|
||||||
|
backend = current_backend;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!backend) {
|
||||||
|
printf("No backend available. (tried: %s)\n", join(" ", tried_backends));
|
||||||
|
printf("To setup devices with block storage install 'autopart'.\n");
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
shift(ARGV);
|
||||||
|
shift(ARGV);
|
||||||
|
let cmd = shift(ARGV);
|
||||||
|
|
||||||
|
if (!cmd || cmd == "-h" || cmd == "help") {
|
||||||
|
help();
|
||||||
|
return cmd?0:22;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cmd in keys(backend))) {
|
||||||
|
printf("command %s not found\n", cmd);
|
||||||
|
return 22;
|
||||||
|
}
|
||||||
|
|
||||||
|
let json_output = false;
|
||||||
|
if (ARGV[0] == "-j") {
|
||||||
|
json_output = true;
|
||||||
|
shift(ARGV);
|
||||||
|
}
|
||||||
|
|
||||||
|
let legacy_output = function(var) {
|
||||||
|
let out = "";
|
||||||
|
if (type(var) == "array") {
|
||||||
|
for (let line in var) {
|
||||||
|
out += join(" ", values(line));
|
||||||
|
out += "\n";
|
||||||
|
}
|
||||||
|
} else if (type(var) == "object") {
|
||||||
|
out += join(" ", values(line));
|
||||||
|
out += "\n";
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type(backend[cmd]) == "string") {
|
||||||
|
printf("%s\n", backend[cmd]);
|
||||||
|
} else if (type(backend[cmd]) == "function") {
|
||||||
|
let ret = backend[cmd](...ARGV);
|
||||||
|
if (type(ret) == "int")
|
||||||
|
exit(ret);
|
||||||
|
|
||||||
|
if (type(ret) == "string") {
|
||||||
|
printf("%s\n", ret);
|
||||||
|
} else {
|
||||||
|
if (json_output)
|
||||||
|
printf("%.J\n", ret);
|
||||||
|
else
|
||||||
|
printf("%s", legacy_output(ret));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (json_output)
|
||||||
|
printf("%.J\n", backend[cmd]);
|
||||||
|
else
|
||||||
|
printf("%s\n", legacy_output(backend[cmd]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
%}
|
||||||
|
|
Loading…
Reference in a new issue