Files
wlan-ap/backports/0024-uvol-backport-package.patch
John Crispin 1e7efc68a8 config.yml: update 21.02 baseline from RC2->GA
Signed-off-by: John Crispin <john@phrozen.org>
2021-09-04 08:14:58 +02:00

1214 lines
27 KiB
Diff

From 08809a60a8f2c065a38c24fcdbd69b939e5c29d9 Mon Sep 17 00:00:00 2001
From: John Crispin <john@phrozen.org>
Date: Fri, 13 Aug 2021 08:46:57 +0200
Subject: [PATCH 24/27] uvol: backport package
Signed-off-by: John Crispin <john@phrozen.org>
---
package/system/uvol/Makefile | 77 ++++
package/system/uvol/files/autopart.defaults | 127 ++++++
package/system/uvol/files/common.sh | 83 ++++
package/system/uvol/files/lvm.sh | 435 ++++++++++++++++++++
package/system/uvol/files/ubi.sh | 337 +++++++++++++++
package/system/uvol/files/uvol | 52 +++
package/system/uvol/files/uvol.defaults | 3 +
package/system/uvol/files/uvol.init | 23 ++
8 files changed, 1137 insertions(+)
create mode 100644 package/system/uvol/Makefile
create mode 100644 package/system/uvol/files/autopart.defaults
create mode 100644 package/system/uvol/files/common.sh
create mode 100644 package/system/uvol/files/lvm.sh
create mode 100644 package/system/uvol/files/ubi.sh
create mode 100644 package/system/uvol/files/uvol
create mode 100644 package/system/uvol/files/uvol.defaults
create mode 100644 package/system/uvol/files/uvol.init
diff --git a/package/system/uvol/Makefile b/package/system/uvol/Makefile
new file mode 100644
index 0000000000..bd70410c5e
--- /dev/null
+++ b/package/system/uvol/Makefile
@@ -0,0 +1,77 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=uvol
+PKG_VERSION:=0.4
+PKG_RELEASE:=$(AUTORELEASE)
+
+PKG_MAINTAINER:=Daniel Golle <daniel@makrotopia.org>
+PKG_LICENSE:=GPL-2.0-or-later
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/autopart
+ SECTION:=utils
+ CATEGORY:=Utilities
+ SUBMENU:=Disc
+ TITLE:=Automatically initialize LVM partition
+ DEPENDS:=+partx-utils +sfdisk
+ PKGARCH=all
+endef
+
+define Package/autopart/description
+ Automatically allocate the GPT partition for LVM and initialize it
+ on first boot.
+endef
+
+define Package/uvol
+ SECTION:=utils
+ CATEGORY:=Utilities
+ SUBMENU:=Disc
+ TITLE:=OpenWrt UBI/LVM volume abstraction
+ DEPENDS:=+blockd
+ PKGARCH=all
+endef
+
+define Package/uvol/description
+ 'uvol' is tool to automate storage volume handling on embedded
+ devices in a generic way.
+ 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 up example_volume_1
+ uvol device example_volume_1
+
+ uvol create example_volume_2 9812733 ro
+ cat example_volume_2.squashfs | uvol write example_volume_2 9812733
+ uvol up example_volume_2
+ uvol device example_volume_2
+endef
+
+define Build/Prepare
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/autopart/install
+ $(INSTALL_DIR) $(1)/etc/uci-defaults
+ $(INSTALL_BIN) ./files/autopart.defaults $(1)/etc/uci-defaults/30-autopart
+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_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_BIN) ./files/uvol $(1)/usr/sbin
+ $(INSTALL_BIN) ./files/uvol.defaults $(1)/etc/uci-defaults/90-uvol-restore-uci
+endef
+
+$(eval $(call BuildPackage,autopart))
+$(eval $(call BuildPackage,uvol))
diff --git a/package/system/uvol/files/autopart.defaults b/package/system/uvol/files/autopart.defaults
new file mode 100644
index 0000000000..870cd44156
--- /dev/null
+++ b/package/system/uvol/files/autopart.defaults
@@ -0,0 +1,127 @@
+#!/bin/sh
+
+. /lib/functions.sh
+. /lib/upgrade/common.sh
+. /usr/share/libubox/jshn.sh
+
+OWRT_VOLUMES=owrt-volumes
+
+load_partitions() {
+ local dev="$1"
+ json_init
+ json_load "$(sfdisk -J "$dev" 2>/dev/null)"
+ json_select "partitiontable" || return 1
+ return 0
+}
+
+get_partition_by_name_gpt() {
+ local label part parts node name
+ json_get_vars label
+ [ "$label" = "gpt" ] || return
+ json_select "partitions" || return
+ json_get_keys parts
+ for part in $parts; do
+ json_select "$part"
+ json_get_vars node name
+ if [ "$1" = "$name" ]; then
+ echo "$node"
+ break
+ fi
+ json_select ..
+ done
+ json_select ..
+}
+
+get_partition_by_type_mbr() {
+ local label part parts node type
+ json_get_vars label
+ [ "$label" = "dos" ] || return
+ json_select "partitions" || return
+ json_get_keys parts
+ for part in $parts; do
+ json_select "$part"
+ json_get_vars node type
+ if [ "$1" = "$type" ]; then
+ echo "$node"
+ break
+ fi
+ json_select ..
+ done
+ json_select ..
+}
+
+part_fixup() {
+ echo "write" | sfdisk --force -q -w never "$1" 1>/dev/null 2>/dev/null
+}
+
+get_free_area() {
+ local found=
+ sfdisk -q -F "$1" 2>/dev/null | while read -r start end sectors size; do
+ case $start in
+ *"Unpartitioned"* | *"Units:"* | *"Sector"* | *"Start"* )
+ continue
+ ;;
+ [0-9]*)
+ case "$size" in
+ *"M")
+ [ "${size%%M}" -lt 100 ] && continue
+ ;;
+ *"G" | *"T")
+ ;;
+ *"k" | *"b")
+ continue
+ ;;
+ esac
+ [ "$found" ] || echo "start=$start, size=$((end - start))"
+ found=1
+ ;;
+ esac
+ done
+}
+
+create_lvm_part() {
+ local disk="$1"
+ local freepart
+
+ freepart="$(get_free_area "$disk")"
+ if [ "$freepart" ]; then
+ echo "$freepart, type=lvm, name=$OWRT_VOLUMES" | sfdisk --force -w never -a "$disk" || return 1
+ partx -a "$disk" 1>/dev/null 2>/dev/null || true
+ return 0
+ else
+ return 1
+ fi
+}
+
+lvm_init() {
+ lvm pvcreate -f "$1"
+ lvm vgcreate "$2" "$1"
+ lvm vgs
+}
+
+autopart_init() {
+ local diskdev
+ local lvmpart
+ local diskserial diskhash
+
+ export_bootdevice && export_partdevice diskdev 0
+
+ [ "$diskdev" ] || return
+
+ [ -e "/sys/class/block/$diskdev/device/serial" ] && diskserial="$(cat "/sys/class/block/$diskdev/device/serial")"
+ [ -e "/sys/class/block/$diskdev/device/cid" ] && diskserial="$diskserial$(cat "/sys/class/block/$diskdev/device/cid")"
+ [ "$diskserial" ] || diskserial="$(cat /proc/sys/kernel/random/uuid)"
+ diskhash="$(echo "$diskserial" | sha256sum | cut -d' ' -f1)"
+
+ part_fixup "/dev/$diskdev"
+ create_lvm_part "/dev/$diskdev" || return
+ load_partitions "/dev/$diskdev" || return
+ lvmpart="$(get_partition_by_name_gpt "$OWRT_VOLUMES")"
+ [ "$lvmpart" ] || lvmpart="$(get_partition_by_type_mbr "8e")"
+ [ "$lvmpart" ] || return
+
+ lvm_init "$lvmpart" "${OWRT_VOLUMES}-${diskhash:0:16}"
+}
+
+autopart_init
+exit 0
diff --git a/package/system/uvol/files/common.sh b/package/system/uvol/files/common.sh
new file mode 100644
index 0000000000..e3b554e180
--- /dev/null
+++ b/package/system/uvol/files/common.sh
@@ -0,0 +1,83 @@
+#!/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="/var/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
+ "_uxc")
+ target="/var/state/uxc"
+ ;;
+ "_"*)
+ 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
+}
diff --git a/package/system/uvol/files/lvm.sh b/package/system/uvol/files/lvm.sh
new file mode 100644
index 0000000000..95281194ba
--- /dev/null
+++ b/package/system/uvol/files/lvm.sh
@@ -0,0 +1,435 @@
+#!/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() {
+ local reports rep pv pvs
+ vg_name=
+ 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" ] && echo "$(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" ] && /sbin/block umount "$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
+ 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}
+ echo "$lv_name $lv_mode $lv_size"
+ json_select ..
+ done
+ json_select ..
+ break
+ done
+}
+
+
+detect() {
+ local reports rep lv lvs lv_name lv_full_name lv_mode volname devname lv_skip_activation
+ 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
diff --git a/package/system/uvol/files/ubi.sh b/package/system/uvol/files/ubi.sh
new file mode 100644
index 0000000000..b0b363d7ed
--- /dev/null
+++ b/package/system/uvol/files/ubi.sh
@@ -0,0 +1,337 @@
+#!/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
+ /sbin/block umount "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
+ /sbin/block umount "$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
+ 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}"
+ echo "$volname $volmode $volsize"
+ done
+}
+
+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
diff --git a/package/system/uvol/files/uvol b/package/system/uvol/files/uvol
new file mode 100644
index 0000000000..4ecd2e165a
--- /dev/null
+++ b/package/system/uvol/files/uvol
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+# 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
+uvol storage volume manager
+
+syntax: uvol command ...
+
+commands:
+ boot get active volumes ready (called on boot)
+ free show number of bytes available
+ total show total number of bytes
+ align show sector size in bytes
+ list [volname] list volumes
+ create volname size type create new volume
+ size: in bytes
+ type: 'ro' or 'rw'
+ remove volname delete volume
+ device volname show block device for mounting
+ size volname show size of volume
+ up volname get volume ready for mounting
+ down volname take volume down after unmounting
+ status volname return status of volume
+ return code: 0 - volume is ready for use
+ 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=
+
+for backend in /usr/libexec/uvol/*.sh; do
+ total=$($backend total)
+ backends_tried="$backends_tried $($backend name)"
+ [ "$total" ] && uvol_backend=$backend
+done
+
+if [ -z "$uvol_backend" ]; then
+ echo "No backend available. (tried:$backends_tried)"
+ echo "To setup devices with block storage install 'autopart'."
+ return 2
+fi
+
+flock -x /tmp/run/uvol.lock "$uvol_backend" "$@"
diff --git a/package/system/uvol/files/uvol.defaults b/package/system/uvol/files/uvol.defaults
new file mode 100644
index 0000000000..cbd53a3e4e
--- /dev/null
+++ b/package/system/uvol/files/uvol.defaults
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+uci -q get fstab.@uvol[0].initialized >/dev/null || uvol detect || true
diff --git a/package/system/uvol/files/uvol.init b/package/system/uvol/files/uvol.init
new file mode 100644
index 0000000000..1f6e2aac08
--- /dev/null
+++ b/package/system/uvol/files/uvol.init
@@ -0,0 +1,23 @@
+#!/bin/sh /etc/rc.common
+
+START=99
+USE_PROCD=1
+NAME=uvol
+PROG=/usr/sbin/uvol
+
+start_service() {
+ [ "${__BOOT_UVOL}" = "1" ] && return 0
+ procd_open_instance "$NAME"
+ procd_set_param command "$PROG" boot
+ procd_close_instance
+}
+
+boot() {
+ __BOOT_UVOL=1
+ start
+}
+
+service_triggers() {
+ procd_add_raw_trigger "mount.ready" 200 /etc/init.d/uvol start
+}
+
--
2.25.1