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
)
lilik-openwrt-22.03
@ -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 |
@ -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 |
@ -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; | |||
%} |
@ -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; | |||
} | |||
}; | |||
%} |