From 08809a60a8f2c065a38c24fcdbd69b939e5c29d9 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 13 Aug 2021 08:46:57 +0200 Subject: [PATCH 24/27] uvol: backport package Signed-off-by: John Crispin --- 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 +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" <"${UCI_SPOOLDIR}/remove-$1" </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 </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