uvol: update to 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>
(cherry picked from commit 6350c7bc63
)
This commit is contained in:
parent
ff9109ad7c
commit
27b395871d
9 changed files with 1222 additions and 934 deletions
|
@ -1,7 +1,7 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=uvol
|
||||
PKG_VERSION:=0.6
|
||||
PKG_VERSION:=0.7
|
||||
PKG_RELEASE:=$(AUTORELEASE)
|
||||
|
||||
PKG_MAINTAINER:=Daniel Golle <daniel@makrotopia.org>
|
||||
|
@ -19,8 +19,7 @@ define Package/autopart
|
|||
endef
|
||||
|
||||
define Package/autopart/description
|
||||
Automatically allocate the GPT partition for LVM and initialize it
|
||||
on first boot.
|
||||
Automatically allocate and initialize a partition for LVM on first boot.
|
||||
endef
|
||||
|
||||
define Package/uvol
|
||||
|
@ -28,18 +27,22 @@ define Package/uvol
|
|||
CATEGORY:=Utilities
|
||||
SUBMENU:=Disc
|
||||
TITLE:=OpenWrt UBI/LVM volume abstraction
|
||||
DEPENDS:=+blockd
|
||||
DEPENDS:=+blockd +ucode +ucode-mod-fs +ucode-mod-uci
|
||||
PKGARCH=all
|
||||
endef
|
||||
|
||||
define Package/uvol/description
|
||||
'uvol' is tool to automate storage volume handling on embedded
|
||||
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
|
||||
block-storage based devices.
|
||||
|
||||
Examples:
|
||||
uvol create example_volume_1 256MiB rw
|
||||
uvol create example_volume_1 268435456 rw
|
||||
uvol up example_volume_1
|
||||
uvol device example_volume_1
|
||||
|
||||
|
@ -64,11 +67,12 @@ define Package/autopart/install
|
|||
endef
|
||||
|
||||
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/common.sh $(1)/lib/functions/uvol.sh
|
||||
$(INSTALL_BIN) ./files/ubi.sh $(1)/usr/libexec/uvol/20-ubi.sh
|
||||
$(INSTALL_BIN) ./files/lvm.sh $(1)/usr/libexec/uvol/50-lvm.sh
|
||||
$(INSTALL_DATA) ./files/blockdev_common.uc $(1)/usr/lib/uvol/
|
||||
$(INSTALL_DATA) ./files/uci.uc $(1)/usr/lib/uvol/
|
||||
$(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.defaults $(1)/etc/uci-defaults/90-uvol-init
|
||||
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
|
||||
# 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
|
||||
function help() {
|
||||
%}
|
||||
uvol storage volume manager
|
||||
|
||||
syntax: uvol command ...
|
||||
|
@ -30,23 +29,102 @@ commands:
|
|||
1 - volume is not ready for use
|
||||
2 - volume doesn'y exist
|
||||
write volname size write to volume from stdin, size in bytes
|
||||
EOF
|
||||
return 22
|
||||
fi
|
||||
{%
|
||||
}
|
||||
|
||||
uvol_backend=
|
||||
backends_tried=
|
||||
let fs = require("fs");
|
||||
let uci = require("uci");
|
||||
let cursor = uci ? uci.cursor() : null;
|
||||
|
||||
for backend in /usr/libexec/uvol/*.sh; do
|
||||
total=$($backend total)
|
||||
backends_tried="$backends_tried $($backend name)"
|
||||
[ "$total" ] && uvol_backend=$backend
|
||||
done
|
||||
let ctx = {};
|
||||
ctx.cursor = cursor;
|
||||
ctx.fs = fs;
|
||||
include("/usr/lib/uvol/uci.uc");
|
||||
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
|
||||
echo "No backend available. (tried:$backends_tried)"
|
||||
echo "To setup devices with block storage install 'autopart'."
|
||||
return 2
|
||||
fi
|
||||
let backend = null;
|
||||
let tried_backends = [];
|
||||
for (plugin in fs.glob("/usr/lib/uvol/backends/*.uc")) {
|
||||
let current_backend = {};
|
||||
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