From bfe30ffbefd9fd8fec0acb391aace370251e21b4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 12 Oct 2026 21:43:19 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Sync=202026-10-12=2021:43:19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clashoo/Makefile | 4 +- ksmbd-tools/Makefile | 119 ++ ksmbd-tools/files/ksmbd.conf.template | 15 + ksmbd-tools/files/ksmbd.config | 9 + ksmbd-tools/files/ksmbd.config.example | 11 + ksmbd-tools/files/ksmbd.hotplug | 18 + ksmbd-tools/files/ksmbd.init | 211 ++++ ksmbd-tools/files/ksmbd.sysctl | 5 + ksmbd-tools/files/shares.sh | 127 ++ ksmbd-tools/files/smb.service | 9 + ksmbd-tools/patches/030-glib.patch | 14 + luci-app-clashoo/Makefile | 2 +- .../root/usr/share/rpcd/ucode/luci.clashoo | 64 +- luci-app-diskman/Makefile | 4 +- .../luasrc/model/cbi/diskman/disks.lua | 2 +- .../luasrc/model/cbi/diskman/partition.lua | 56 +- luci-app-diskman/luasrc/model/diskman.lua | 55 +- .../luasrc/view/diskman/disk_info.htm | 123 +- luci-app-diskman/po/pl/diskman.po | 10 +- luci-app-diskman/po/templates/diskman.pot | 17 +- luci-app-diskman/po/zh_Hans/diskman.po | 21 +- luci-app-diskman/po/zh_Hant/diskman.po | 21 +- luci-app-modem-manager-next/Makefile | 49 + luci-app-modem-manager-next/README.md | 147 +++ .../luci-static/resources/view/modem/log.js | 96 ++ .../resources/view/modem/overview.js | 430 +++++++ .../resources/view/modem/settings.js | 565 +++++++++ .../luci-static/resources/view/modem/sms.js | 503 ++++++++ .../resources/view/modem/templates.js | 109 ++ .../resources/view/modem/terminal.js | 643 ++++++++++ .../po/templates/modem-manager.pot | 614 +++++++++ .../po/templates/modem.pot | 614 +++++++++ luci-app-modem-manager-next/po/zh-cn | 1 + .../po/zh_Hans/modem-manager.po | 1017 +++++++++++++++ .../po/zh_Hans/modem.po | 1017 +++++++++++++++ .../root/etc/config/modem-manager | 11 + .../root/etc/hotplug.d/usb/20-modem-hotplug | 18 + .../root/etc/init.d/modem-manager | 44 + .../etc/uci-defaults/99_modem-manager-init | 6 + .../root/usr/libexec/modem-at-executor | 164 +++ .../root/usr/libexec/modem-collector | 60 + .../root/usr/libexec/modem-detect | 189 +++ .../luci/menu.d/luci-app-modem-manager.json | 60 + .../modem-manager/templates/fibocom.json | 40 + .../modem-manager/templates/generic.json | 29 + .../modem-manager/templates/quectel.json | 43 + .../share/modem-manager/templates/zte.json | 35 + .../rpcd/acl.d/luci-app-modem-manager.json | 56 + .../root/usr/share/rpcd/ucode/luci.modem | 1106 +++++++++++++++++ luci-app-passwall/Makefile | 2 +- .../root/usr/share/passwall/lease2hosts.sh | 87 +- 51 files changed, 8523 insertions(+), 149 deletions(-) create mode 100644 ksmbd-tools/Makefile create mode 100644 ksmbd-tools/files/ksmbd.conf.template create mode 100644 ksmbd-tools/files/ksmbd.config create mode 100644 ksmbd-tools/files/ksmbd.config.example create mode 100644 ksmbd-tools/files/ksmbd.hotplug create mode 100644 ksmbd-tools/files/ksmbd.init create mode 100644 ksmbd-tools/files/ksmbd.sysctl create mode 100644 ksmbd-tools/files/shares.sh create mode 100644 ksmbd-tools/files/smb.service create mode 100644 ksmbd-tools/patches/030-glib.patch create mode 100644 luci-app-modem-manager-next/Makefile create mode 100644 luci-app-modem-manager-next/README.md create mode 100644 luci-app-modem-manager-next/htdocs/luci-static/resources/view/modem/log.js create mode 100644 luci-app-modem-manager-next/htdocs/luci-static/resources/view/modem/overview.js create mode 100644 luci-app-modem-manager-next/htdocs/luci-static/resources/view/modem/settings.js create mode 100644 luci-app-modem-manager-next/htdocs/luci-static/resources/view/modem/sms.js create mode 100644 luci-app-modem-manager-next/htdocs/luci-static/resources/view/modem/templates.js create mode 100644 luci-app-modem-manager-next/htdocs/luci-static/resources/view/modem/terminal.js create mode 100644 luci-app-modem-manager-next/po/templates/modem-manager.pot create mode 100644 luci-app-modem-manager-next/po/templates/modem.pot create mode 120000 luci-app-modem-manager-next/po/zh-cn create mode 100644 luci-app-modem-manager-next/po/zh_Hans/modem-manager.po create mode 100644 luci-app-modem-manager-next/po/zh_Hans/modem.po create mode 100644 luci-app-modem-manager-next/root/etc/config/modem-manager create mode 100755 luci-app-modem-manager-next/root/etc/hotplug.d/usb/20-modem-hotplug create mode 100755 luci-app-modem-manager-next/root/etc/init.d/modem-manager create mode 100755 luci-app-modem-manager-next/root/etc/uci-defaults/99_modem-manager-init create mode 100755 luci-app-modem-manager-next/root/usr/libexec/modem-at-executor create mode 100755 luci-app-modem-manager-next/root/usr/libexec/modem-collector create mode 100755 luci-app-modem-manager-next/root/usr/libexec/modem-detect create mode 100644 luci-app-modem-manager-next/root/usr/share/luci/menu.d/luci-app-modem-manager.json create mode 100644 luci-app-modem-manager-next/root/usr/share/modem-manager/templates/fibocom.json create mode 100644 luci-app-modem-manager-next/root/usr/share/modem-manager/templates/generic.json create mode 100644 luci-app-modem-manager-next/root/usr/share/modem-manager/templates/quectel.json create mode 100644 luci-app-modem-manager-next/root/usr/share/modem-manager/templates/zte.json create mode 100644 luci-app-modem-manager-next/root/usr/share/rpcd/acl.d/luci-app-modem-manager.json create mode 100644 luci-app-modem-manager-next/root/usr/share/rpcd/ucode/luci.modem diff --git a/clashoo/Makefile b/clashoo/Makefile index b9b4c60ad..77c66b06d 100644 --- a/clashoo/Makefile +++ b/clashoo/Makefile @@ -5,13 +5,13 @@ PKG_SHORT_SHA:=bc76c13 PKG_COMMIT_DATE:=2026.10.11 PKG_VERSION:=$(PKG_COMMIT_DATE)~$(PKG_SHORT_SHA) -PKG_RELEASE:=10 +PKG_RELEASE:=11 PKG_SOURCE:=$(PKG_NAME)-alpha-$(PKG_SHORT_SHA).tar.gz PKG_SOURCE_SUBDIR:=$(PKG_NAME)-alpha-$(PKG_SHORT_SHA) PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-alpha-$(PKG_SHORT_SHA) PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-clashoo/releases/download/mihomo-src -PKG_SOURCE_VERSION:=29c93e5739479dc14c0dea398465f3bc17026944 +PKG_SOURCE_VERSION:=fac50d4bfa20e397dc765616578d92522b208e3a PKG_HASH:=skip PKG_BUILD_VERSION:=alpha-$(PKG_SHORT_SHA) diff --git a/ksmbd-tools/Makefile b/ksmbd-tools/Makefile new file mode 100644 index 000000000..777a7ab43 --- /dev/null +++ b/ksmbd-tools/Makefile @@ -0,0 +1,119 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=ksmbd-tools +PKG_VERSION:=3.5.7 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://github.com/cifsd-team/ksmbd-tools/releases/download/$(PKG_VERSION) +PKG_HASH:=skip + +PKG_LICENSE:=GPL-2.0-or-later +PKG_LICENSE_FILES:=COPYING + +PKG_BUILD_DEPENDS:=glib2 +PKG_BUILD_FLAGS:=gc-sections + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/nls.mk +include $(INCLUDE_DIR)/meson.mk + +define Package/ksmbd-tools/Default + SECTION:=net + CATEGORY:=Network + SUBMENU:=Filesystem + TITLE:=Kernel SMB + URL:=https://github.com/cifsd-team/ksmbd-tools + DEPENDS:= $(ICONV_DEPENDS) $(INTL_DEPENDS) +endef + +define Package/ksmbd-tools/Default/description + Userspace tools to manage the SMB kernel fileserver (ksmbd.ko). + The config file location is /etc/ksmbd/ksmbd.conf +endef + +define Package/ksmbd-server + $(call Package/ksmbd-tools/Default) + TITLE+= server + DEPENDS+= +(LINUX_5_4||LINUX_5_10||LINUX_6_6):kmod-fs-cifsd +!(LINUX_5_4||LINUX_5_10||LINUX_6_6):kmod-fs-ksmbd +libnl-core +libnl-genl +umdns +flock +endef + +define Package/ksmbd-server/description + installs: ksmbd.mountd + + This provides the basic fileserver service and is the minimum needed to serve 'guest only' file shares or use a existing user database file ksmbdpwd.db. +endef + +define Package/ksmbd-server/config + select PACKAGE_wsdd2 +endef + +define Package/ksmbd-avahi-service + $(call Package/ksmbd-tools/Default) + TITLE+= (Avahi service) + DEPENDS:= +avahi-daemon +endef + +define Package/ksmbd-avahi-service/description + installs: smb.service + + This package contains the service definition for announcing the + Ksmbd (smb/445) Daemon service via mDNS/DNS-SD. +endef + +define Package/ksmbd-hotplug + $(call Package/ksmbd-tools/Default) + TITLE+= hotplug script for auto sharing + DEPENDS:=+blockd +endef + +MESON_ARGS += \ + -Db_lto=true \ + -Dkrb5=disabled + +TARGET_LDFLAGS += -Wl,--as-needed + +define Package/ksmbd-server/install + $(INSTALL_DIR) $(1)/usr/libexec + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/libexec/ksmbd.tools $(1)/usr/libexec/ + $(INSTALL_DIR) $(1)/usr/sbin + $(CP) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.addshare $(1)/usr/sbin/ + $(CP) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.adduser $(1)/usr/sbin/ + $(CP) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.control $(1)/usr/sbin/ + $(CP) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.mountd $(1)/usr/sbin/ + $(INSTALL_DIR) $(1)/etc/config $(1)/etc/ksmbd $(1)/etc/init.d + $(INSTALL_CONF) ./files/ksmbd.config $(1)/etc/config/ksmbd + $(INSTALL_DATA) ./files/ksmbd.conf.template $(1)/etc/ksmbd/ + $(INSTALL_BIN) ./files/ksmbd.init $(1)/etc/init.d/ksmbd + $(INSTALL_DIR) $(1)/etc/sysctl.d + $(INSTALL_CONF) ./files/ksmbd.sysctl $(1)/etc/sysctl.d/90-ksmbd.conf + $(INSTALL_DATA) ./files/shares.sh $(1)/etc/ksmbd/ + # copy examples until we have a wiki page + $(INSTALL_DATA) ./files/ksmbd.config.example $(1)/etc/ksmbd/ +endef + +define Package/ksmbd-avahi-service/install + $(INSTALL_DIR) $(1)/etc/avahi/services + $(INSTALL_DATA) ./files/smb.service $(1)/etc/avahi/services/ +endef + +define Package/ksmbd-hotplug/install + $(INSTALL_DIR) $(1)/etc/hotplug.d/mount + $(INSTALL_CONF) ./files/ksmbd.hotplug $(1)/etc/hotplug.d/mount/60-ksmbd +endef + +define Package/ksmbd-server/conffiles +/etc/config/ksmbd +/etc/ksmbd/ksmbd.conf.template +/etc/ksmbd/ksmbd.conf +/etc/ksmbd/ksmbdpwd.db +/etc/sysctl.d/90-ksmbd.conf +endef + +define Package/ksmbd-avahi-service/conffiles +/etc/avahi/services/smb.service +endef + +$(eval $(call BuildPackage,ksmbd-server)) +$(eval $(call BuildPackage,ksmbd-avahi-service)) +$(eval $(call BuildPackage,ksmbd-hotplug)) diff --git a/ksmbd-tools/files/ksmbd.conf.template b/ksmbd-tools/files/ksmbd.conf.template new file mode 100644 index 000000000..9daf4258a --- /dev/null +++ b/ksmbd-tools/files/ksmbd.conf.template @@ -0,0 +1,15 @@ +[global] + netbios name = |NAME| + server string = |DESCRIPTION| + workgroup = |WORKGROUP| + interfaces = |INTERFACES| + bind interfaces only = yes + ipc timeout = 20 + deadtime = 15 + map to guest = Bad User + server max protocol = SMB3_11 + smb2 max read = 1MB + smb2 max write = 1MB + smb2 max trans = 1MB + cache read buffers = no + cache trans buffers = no diff --git a/ksmbd-tools/files/ksmbd.config b/ksmbd-tools/files/ksmbd.config new file mode 100644 index 000000000..33d8bc771 --- /dev/null +++ b/ksmbd-tools/files/ksmbd.config @@ -0,0 +1,9 @@ +config globals + option enabled '0' + option 'workgroup' 'WORKGROUP' + option 'description' 'Ksmbd on OpenWrt' + option interface 'lan' + option allow_legacy_protocols '1' + option allow_guest_ipc '1' + option homes '0' + option autoshare '1' diff --git a/ksmbd-tools/files/ksmbd.config.example b/ksmbd-tools/files/ksmbd.config.example new file mode 100644 index 000000000..9a457e03b --- /dev/null +++ b/ksmbd-tools/files/ksmbd.config.example @@ -0,0 +1,11 @@ +config globals + option 'description' 'Ksmbd on OpenWrt' + +config share + option name 'testshare' + option path '/tmp' + option read_only 'no' + option guest_ok 'yes' + option create_mask '0666' + option dir_mask '0777' + option force_root '1' diff --git a/ksmbd-tools/files/ksmbd.hotplug b/ksmbd-tools/files/ksmbd.hotplug new file mode 100644 index 000000000..56b68b300 --- /dev/null +++ b/ksmbd-tools/files/ksmbd.hotplug @@ -0,0 +1,18 @@ +#!/bin/sh + +. /lib/functions.sh +case "$ACTION" in add|remove) ;; *) exit 0;; esac +/etc/init.d/ksmbd enabled || exit 0 + +enabled=0 +autoshare=1 +ksmbd_settings() { + config_get_bool enabled "$1" enabled 0 + config_get_bool autoshare "$1" autoshare 1 +} +config_load ksmbd +config_foreach ksmbd_settings globals +[ "$enabled" -eq 1 ] && [ "$autoshare" -eq 1 ] || exit 0 + +# Synchronize automatic UCI shares and rebuild from current mounts. +/etc/init.d/ksmbd reload diff --git a/ksmbd-tools/files/ksmbd.init b/ksmbd-tools/files/ksmbd.init new file mode 100644 index 000000000..14f049786 --- /dev/null +++ b/ksmbd-tools/files/ksmbd.init @@ -0,0 +1,211 @@ +#!/bin/sh /etc/rc.common + +START=98 +STOP=05 +USE_PROCD=1 + +SMBD_IFACE="" +KSMBD_ENABLED=0 +KSMBD_HOMES=0 +KSMBD_AUTOSHARE=1 + +config_get_sane() +{ + config_get "$@" + set -- "$(echo "$1" | tr -d '<>[]{};%?=#\n')" +} + +smb_header() +{ + config_get_bool KSMBD_ENABLED "$1" enabled 0 + config_get_bool KSMBD_HOMES "$1" homes 0 + config_get_bool KSMBD_AUTOSHARE "$1" autoshare 1 + config_get_sane SMBD_IFACE "$1" interface "lan" + + # resolve interfaces + interfaces=$( + . /lib/functions/network.sh + + for net in $SMBD_IFACE; do + network_is_up "$net" || continue + network_get_device device "$net" + printf "%s " "${device:-$net}" + done + ) + + config_get hostname "$1" name + case "$hostname" in + ''|*[!a-zA-Z0-9_.-]*) + hostname="$(sed 's/\..*//' /proc/sys/kernel/hostname | tr -cd 'a-zA-Z0-9_-')" + ;; + esac + + config_get_sane workgroup "$1" workgroup "WORKGROUP" + config_get_sane description "$1" description "Ksmbd on OpenWrt" + config_get_bool ALLOW_LEGACY_PROTOCOLS "$1" allow_legacy_protocols 1 + config_get_bool ALLOW_GUEST_IPC "$1" allow_guest_ipc 1 + + sed -e "s#|NAME|#$hostname#g" \ + -e "s#|WORKGROUP|#$workgroup#g" \ + -e "s#|DESCRIPTION|#$description#g" \ + -e "s#|INTERFACES|#$interfaces#g" \ + /etc/ksmbd/ksmbd.conf.template > /var/etc/ksmbd/ksmbd.conf + + { + printf "\n######### Dynamic written config options #########\n" + + if [ "$ALLOW_LEGACY_PROTOCOLS" -eq 1 ]; then + logger -p daemon.info -t 'ksmbd' "Legacy Protocols allowed, don't use this option for secure environments!" + printf "\tserver min protocol = NT1\n" + printf "\tserver signing = disabled\n" + fi + printf "\n[IPC$]\n\tguest ok = %s\n" "$( [ "$ALLOW_GUEST_IPC" -eq 1 ] && echo yes || echo no )" + } >> /var/etc/ksmbd/ksmbd.conf + + [ -e /etc/ksmbd/ksmbd.conf ] || ln -nsf /var/etc/ksmbd/ksmbd.conf /etc/ksmbd/ksmbd.conf + + if [ ! -L /etc/ksmbd/ksmbd.conf ]; then + logger -p daemon.warn -t 'ksmbd' "Local custom /etc/ksmbd/ksmbd.conf file detected, all UCI/Luci config settings are ignored!" + fi +} + +smb_add_share() +{ + config_get_sane name "$1" name + config_get_sane path "$1" path + config_get_sane comment "$1" comment + config_get_sane users "$1" users + config_get_sane create_mask "$1" create_mask 0666 + config_get_sane dir_mask "$1" dir_mask 0777 + config_get_sane browseable "$1" browseable yes + config_get_sane read_only "$1" read_only no + config_get_sane writeable "$1" writeable + config_get_sane guest_ok "$1" guest_ok yes + config_get_bool force_root "$1" force_root 1 + config_get_sane write_list "$1" write_list + config_get_sane read_list "$1" read_list + config_get_sane hide_dot_files "$1" hide_dot_files yes + config_get_sane veto_files "$1" veto_files + config_get_sane inherit_owner "$1" inherit_owner + config_get_sane force_create_mode "$1" force_create_mode + config_get_sane force_directory_mode "$1" force_directory_mode + + [ -z "$name" ] || [ -z "$path" ] && return + + { + printf "\n[%s]\n\tpath = %s\n" "$name" "$path" + [ -n "$comment" ] && printf "\tcomment = %s\n" "$comment" + + if [ "$force_root" -eq 1 ]; then + printf "\tforce user = %s\n" "root" + printf "\tforce group = %s\n" "root" + fi + [ -n "$users" ] && printf "\tvalid users = %s\n" "$users" + + [ -n "$create_mask" ] && printf "\tcreate mask = %s\n" "$create_mask" + [ -n "$dir_mask" ] && printf "\tdirectory mask = %s\n" "$dir_mask" + [ -n "$force_create_mode" ] && printf "\tforce create mode = %s\n" "$force_create_mode" + [ -n "$force_directory_mode" ] && printf "\tforce directory mode = %s\n" "$force_directory_mode" + + [ -n "$browseable" ] && printf "\tbrowseable = %s\n" "$browseable" + [ -n "$read_only" ] && printf "\tread only = %s\n" "$read_only" + [ -n "$writeable" ] && printf "\twriteable = %s\n" "$writeable" + + [ -n "$guest_ok" ] && printf "\tguest ok = %s\n" "$guest_ok" + [ -n "$inherit_owner" ] && printf "\tinherit owner = %s\n" "$inherit_owner" + + [ -n "$write_list" ] && printf "\twrite list = %s\n" "$write_list" + [ -n "$read_list" ] && printf "\tread list = %s\n" "$read_list" + + [ -n "$hide_dot_files" ] && printf "\thide dot files = %s\n" "$hide_dot_files" + [ -n "$veto_files" ] && printf "\tveto files = %s\n" "$veto_files" + } >> /var/etc/ksmbd/ksmbd.conf +} + +init_config() +{ + flock -x 9 || return 1 + mkdir -p /var/etc/ksmbd + + config_load ksmbd + config_foreach smb_header globals + . /etc/ksmbd/shares.sh + ksmbd_sync_mounts || return 1 + config_load ksmbd + config_foreach smb_add_share share + [ "$KSMBD_HOMES" -eq 1 ] && ksmbd_add_homes + return 0 +} 9>/var/lock/ksmbd-config.lock + +service_triggers() +{ + # PROCD_RELOAD_DELAY=1000 + + procd_add_reload_trigger "dhcp" "system" "ksmbd" + + for i in $SMBD_IFACE; do + procd_add_reload_interface_trigger "$i" + done +} + +kill_server() +{ + if [ -e /sys/module/ksmbd ]; then + if [ -e /sys/class/ksmbd-control/kill_server ]; then + logger -p daemon.info -t 'ksmbd' "triggering kill_server" + echo hard > /sys/class/ksmbd-control/kill_server + fi + fi +} + +start_service() +{ + init_config || return 1 + if [ "$KSMBD_ENABLED" -ne 1 ]; then + stop_service + return 0 + fi + + if [ ! -e /etc/ksmbd/ksmbd.conf ]; then + logger -p daemon.error -t 'ksmbd' "missing config /etc/ksmbd/ksmbd.conf!" + exit 1 + fi + + # NOTE: We don't do a soft-reload via signal, since [global] ksmbd.conf setting changes will be ignored, so always reset hard. + [ -e /sys/module/ksmbd ] && stop_service + + [ ! -e /sys/module/ksmbd ] && modprobe ksmbd 2> /dev/null + if [ ! -e /sys/module/ksmbd ]; then + logger -p daemon.error -t 'ksmbd' "modprobe of ksmbd module failed, can\'t start ksmbd!" + exit 1 + fi + + logger -p daemon.notice -t 'ksmbd' "Starting Ksmbd userspace service." + procd_open_instance + procd_add_mdns "smb" "tcp" "445" "daemon=ksmbd" + procd_set_param command /usr/sbin/ksmbd.mountd --n + procd_set_param file /etc/ksmbd/ksmbd.conf + procd_set_param limits nofile=16384 + procd_close_instance +} + +stop_service() +{ + logger -p daemon.notice -t 'ksmbd' "Stopping Ksmbd userspace service." + killall ksmbd.mountd > /dev/null 2>&1 + + [ -e /sys/module/ksmbd ] && rmmod ksmbd > /dev/null 2>&1 + # kill server if we cant rmmod + [ -e /sys/module/ksmbd ] && kill_server + # next try + [ -e /sys/module/ksmbd ] && rmmod ksmbd > /dev/null 2>&1 + + if [ -e /sys/module/ksmbd ]; then + logger -p daemon.error -t 'ksmbd' "module still loaded after kill_server?" + fi + [ -f /tmp/ksmbd.lock ] && rm /tmp/ksmbd.lock +} + +# reload_service() { + # restart "$@" +# } diff --git a/ksmbd-tools/files/ksmbd.sysctl b/ksmbd-tools/files/ksmbd.sysctl new file mode 100644 index 000000000..cf7b2d569 --- /dev/null +++ b/ksmbd-tools/files/ksmbd.sysctl @@ -0,0 +1,5 @@ +# Bound buffered-write backlog to smooth sustained uploads on flash storage. +# These VM limits apply system-wide, including writers other than ksmbd. +# /etc/sysctl.conf is loaded afterwards and can override these values. +vm.dirty_background_bytes=4194304 +vm.dirty_bytes=16777216 diff --git a/ksmbd-tools/files/shares.sh b/ksmbd-tools/files/shares.sh new file mode 100644 index 000000000..ba552048e --- /dev/null +++ b/ksmbd-tools/files/shares.sh @@ -0,0 +1,127 @@ +#!/bin/sh +# Home shares remain private runtime entries; mount shares are stored in UCI. +ksmbd_share_exists() +{ + KSMBD_SHARE_NAME="$1" KSMBD_SHARE_PATH="$2" awk ' + /^\[/ { name = substr($0, 2, length($0)-2); if (tolower(name) == tolower(ENVIRON["KSMBD_SHARE_NAME"])) found=1 } + /^[ \t]*path[ \t]*=/ { path=$0; sub(/^[ \t]*path[ \t]*=[ \t]*/, "", path); if (path == ENVIRON["KSMBD_SHARE_PATH"]) found=1 } + END { exit !found }' /var/etc/ksmbd/ksmbd.conf +} + +ksmbd_add_homes() +{ + local user password uid gid gecos home shell + [ -r /etc/ksmbd/ksmbdpwd.db ] || return 0 + while IFS=: read -r user password uid gid gecos home shell; do + case "$user" in ''|*[!a-zA-Z0-9_.-]*) continue;; esac + case "$home" in /|/var|/tmp|/dev|/proc|/sys|'') continue;; /*) ;; *) continue;; esac + [ -d "$home" ] || continue + # Private homes are only generated for registered SMB users. + awk -F: -v user="$user" '$1 == user { found=1 } END { exit !found }' /etc/ksmbd/ksmbdpwd.db || continue + ksmbd_share_exists "$user" "$home" && continue + { + printf '\n[%s]\n\tpath = %s\n' "$user" "$home" + printf '\tcomment = Home Directories\n\tvalid users = %s\n' "$user" + printf '\tbrowseable = no\n\tguest ok = no\n\tread only = no\n' + printf '\tcreate mask = 0750\n\tdirectory mask = 0750\n' + } >> /var/etc/ksmbd/ksmbd.conf + done < /etc/passwd +} + +# Use a private delta directory so commits do not include pending LuCI edits. +ksmbd_mount_uci() +{ + uci ${UCI_CONFIG_DIR:+-c "$UCI_CONFIG_DIR"} -t "$ksmbd_delta" -q "$@" +} + +ksmbd_prune_mount() +{ + local section="$1" path auto_path fingerprint + config_get path "$section" path + config_get auto_path "$section" auto_path + [ -n "$auto_path" ] || return 0 + if [ "$path" != "$auto_path" ]; then + # Editing the path converts an automatic share into a manual share. + ksmbd_mount_uci delete "ksmbd.$section.auto_path" + elif [ "$KSMBD_AUTOSHARE" -ne 1 ] || ! grep -Fxq "$path" "$ksmbd_mounts"; then + ksmbd_mount_uci delete "ksmbd.$section" + else + # Only remove identical generated entries; differing permissions are + # intentional configuration and must not be silently discarded. + fingerprint="$(ksmbd_mount_uci show "ksmbd.$section" | + sed '1d; s/^ksmbd\.[^.]*\.//' | LC_ALL=C sort | md5sum | cut -d ' ' -f 1)" + if grep -Fxq "$fingerprint" "$ksmbd_delta/seen"; then + ksmbd_mount_uci delete "ksmbd.$section" + else + printf '%s\n' "$fingerprint" >> "$ksmbd_delta/seen" + fi + fi + return 0 +} + +ksmbd_find_mount() +{ + local section="$1" share_name share_path + config_get share_name "$section" name + config_get share_path "$section" path + if [ "$share_path" = "$target" ] || + [ "$(printf '%s' "$share_name" | tr 'A-Z' 'a-z')" = "$(printf '%s' "$name" | tr 'A-Z' 'a-z')" ]; then + ksmbd_found=1 + fi +} + +ksmbd_scan_mounts() +{ + local device target fstype options rest name + while read -r device target fstype options rest; do + case "$device" in /dev/sd*|/dev/hd*|/dev/mmcblk*|/dev/nvme*|/dev/md*) ;; *) continue;; esac + # Firmware images and other read-only mounts are not automatic shares. + case "$fstype" in squashfs|erofs|cramfs|romfs|iso9660) continue;; esac + case ",$options," in *,ro,*) continue;; esac + target="$(printf '%b' "$target")" + case "$target" in /|/rom|/overlay|/boot|/boot/*|/tmp|/tmp/*|/dev|/dev/*|/proc|/proc/*|/sys|/sys/*) continue;; esac + case "$target" in *' +'*|*' '*) continue;; esac + [ -d "$target" ] || continue + name="${target##*/}" + case "$name" in ''|*'['*|*']'*|*';'*|*'%'*) name="${device##*/}";; esac + printf '%s\t%s\tno\n' "$target" "$name" + done < /proc/mounts +} + +ksmbd_sync_mounts() +( + local ksmbd_delta ksmbd_mounts target name readonly section ksmbd_found + flock -x 8 || return 1 + # The caller's snapshot can predate another hotplug worker's commit. + config_load ksmbd + ksmbd_delta="$(mktemp -d /tmp/ksmbd-uci.XXXXXX)" || return 1 + trap 'rm -rf "$ksmbd_delta"' EXIT + : > "$ksmbd_delta/seen" + ksmbd_mounts="$ksmbd_delta/paths" + ksmbd_scan_mounts > "$ksmbd_delta/mounts" + cut -f1 "$ksmbd_delta/mounts" > "$ksmbd_mounts" + config_foreach ksmbd_prune_mount share + if [ -n "$(ksmbd_mount_uci changes ksmbd)" ]; then + ksmbd_mount_uci commit ksmbd || return 1 + fi + [ "$KSMBD_AUTOSHARE" -eq 1 ] || return 0 + config_load ksmbd + while IFS="$(printf '\t')" read -r target name readonly; do + ksmbd_found=0 + config_foreach ksmbd_find_mount share + [ "$ksmbd_found" -eq 0 ] || continue + section="$(ksmbd_mount_uci add ksmbd share)" || return 1 + ksmbd_mount_uci set "ksmbd.$section.name=$name" + ksmbd_mount_uci set "ksmbd.$section.path=$target" + ksmbd_mount_uci set "ksmbd.$section.auto_path=$target" + ksmbd_mount_uci set "ksmbd.$section.browseable=yes" + ksmbd_mount_uci set "ksmbd.$section.guest_ok=yes" + ksmbd_mount_uci set "ksmbd.$section.read_only=$readonly" + ksmbd_mount_uci set "ksmbd.$section.force_root=1" + ksmbd_mount_uci set "ksmbd.$section.create_mask=0666" + ksmbd_mount_uci set "ksmbd.$section.dir_mask=0777" + ksmbd_mount_uci commit ksmbd || return 1 + config_load ksmbd + done < "$ksmbd_delta/mounts" +) 8>/var/lock/ksmbd-autoshare.lock diff --git a/ksmbd-tools/files/smb.service b/ksmbd-tools/files/smb.service new file mode 100644 index 000000000..24e643036 --- /dev/null +++ b/ksmbd-tools/files/smb.service @@ -0,0 +1,9 @@ + + + + %h + + _smb._tcp + 445 + + diff --git a/ksmbd-tools/patches/030-glib.patch b/ksmbd-tools/patches/030-glib.patch new file mode 100644 index 000000000..9e3fa2c62 --- /dev/null +++ b/ksmbd-tools/patches/030-glib.patch @@ -0,0 +1,14 @@ +Subject: [PATCH] Link glib statically for OpenWrt + +Signed-off-by: coolsnowwolf + +--- a/meson.build ++++ b/meson.build +@@ -21,6 +21,7 @@ + glib_dep = dependency( + 'glib-2.0', + version: '>= 2.58', ++ static: true, + ) + libnl_dep = dependency( + 'libnl-genl-3.0', diff --git a/luci-app-clashoo/Makefile b/luci-app-clashoo/Makefile index cbd221359..38181dd30 100644 --- a/luci-app-clashoo/Makefile +++ b/luci-app-clashoo/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-clashoo PKG_VERSION:=1.31.0 -PKG_RELEASE:=7 +PKG_RELEASE:=8 PKG_MAINTAINER:=kenzok8 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) diff --git a/luci-app-clashoo/root/usr/share/rpcd/ucode/luci.clashoo b/luci-app-clashoo/root/usr/share/rpcd/ucode/luci.clashoo index d89be1a98..52a2ceae5 100755 --- a/luci-app-clashoo/root/usr/share/rpcd/ucode/luci.clashoo +++ b/luci-app-clashoo/root/usr/share/rpcd/ucode/luci.clashoo @@ -2,9 +2,18 @@ 'use strict'; -import { access, popen, glob, readfile, writefile, stat, lstat, unlink, open, mkdtemp, rename, rmdir } from 'fs'; +import { access, popen, glob, readfile, writefile, stat, lstat, unlink, open, rename, rmdir } from 'fs'; import { cursor } from 'uci'; +// 兼容 ucode < 2026-10-10:原生缺少 mkdtemp,改用 mktemp -d +function mkdtemp(tpl) { + let p = popen("mktemp -d '" + tpl + "' 2>/dev/null"); + if (!p) return null; + let r = trim(p.read('all') || ''); + p.close(); + return r || null; +} + const BINARY_PATHS = [ '/usr/bin/mihomo-stable', '/usr/bin/mihomo', @@ -1219,6 +1228,10 @@ if (!t) t = 'template'; return '_merged_' + s + '__' + t + '.yaml'; } +function is_generated_yaml(path) { +return system("grep -q '^x-clashoo:' " + shell_quote(path) + " 2>/dev/null") == 0; +} + function template_search_dirs() { return [TEMPLATE_DIR, '/usr/share/clashoo/config/template']; } @@ -1228,7 +1241,7 @@ name = ensure_yaml_name(name || ''); if (!name) return ''; for (let dir in template_search_dirs()) { let p = dir + '/' + name; -if (access(p, 'r')) return p; +if (access(p, 'r') && !is_generated_yaml(p)) return p; } return ''; } @@ -2907,6 +2920,7 @@ for (let dir in template_search_dirs()) { for (let f in (glob(dir + '/*.yaml') || [])) { let name = replace(f, dir + '/', ''); if (index(name, '_merged_') == 0 || seen[name]) continue; +if (is_generated_yaml(f)) continue; seen[name] = 1; let fi = stat(f); push(files, { name, mtime: fi ? ('' + fi.mtime) : null, size: fi ? (int(fi.size / 1024) + ' KB') : null }); @@ -2914,6 +2928,7 @@ push(files, { name, mtime: fi ? ('' + fi.mtime) : null, size: fi ? (int(fi.size for (let f in (glob(dir + '/*.yml') || [])) { let name = replace(f, dir + '/', ''); if (index(name, '_merged_') == 0 || seen[name]) continue; +if (is_generated_yaml(f)) continue; seen[name] = 1; let fi = stat(f); push(files, { name, mtime: fi ? ('' + fi.mtime) : null, size: fi ? (int(fi.size / 1024) + ' KB') : null }); @@ -3477,13 +3492,22 @@ if (!tpl_src) return { success: false, error: 'invalid_template', message: '模板来源不能为空' }; let pairs = []; +let names = {}; try { let parsed = json(pairs_raw || '[]'); -if (type(parsed) == 'array') { +if (type(parsed) != 'array') +return { success: false, error: 'invalid_pairs', message: '订阅参数格式错误' }; for (let p in parsed) { -if (p && p.name && p.url && match(p.url, /^https?:\/\//)) -push(pairs, { name: trim(p.name), url: trim(p.url) }); -} +if (!p || type(p.name) != 'string' || type(p.url) != 'string') +return { success: false, error: 'invalid_pairs', message: '订阅名称和链接不能为空' }; +let name = trim(p.name); +let url = trim(p.url); +if (!name || !match(url, /^https?:\/\//)) +return { success: false, error: 'invalid_pairs', message: '订阅名称不能为空,链接必须以 http:// 或 https:// 开头' }; +if (names[name]) +return { success: false, error: 'duplicate_provider_name', message: '订阅名称不能重复' }; +names[name] = 1; +push(pairs, { name, url }); } } catch (err) { return { success: false, error: 'invalid_pairs', message: '订阅参数格式错误' }; @@ -3507,11 +3531,7 @@ if (system(dl_cmd) != 0 || !access(tmp_tpl, 'r')) return { success: false, error: 'download_failed', message: '远程模板下载失败' }; } else { let safe = safe_name(tpl_src); -let local_path = null; -let search_dirs = template_search_dirs(); -for (let i = 0; i < length(search_dirs); i++) { -if (access(search_dirs[i] + '/' + safe, 'r')) { local_path = search_dirs[i] + '/' + safe; break; } -} +let local_path = find_template_path(safe); if (!local_path) return { success: false, error: 'template_not_found', message: '本地模板文件不存在' }; if (system('cp -f ' + shell_quote(local_path) + ' ' + shell_quote(tmp_tpl)) != 0) @@ -3528,17 +3548,27 @@ return { success: false, error: 'missing_yq', message: '缺少 yq,无法注入 system("cp -f " + shell_quote(tmp_tpl) + " " + shell_quote(exploded)); } + /* 模板必须有 proxy-providers 段 */ + if (system("yq -e 'has(\"proxy-providers\")' " + shell_quote(exploded) + " >/dev/null 2>&1") != 0) { + system("rm -f " + shell_quote(exploded)); + return { success: false, error: 'no_proxy_providers', message: '模板缺少 proxy-providers 段' }; + } + if (system("yq -e '(.\"proxy-providers\" | tag) == \"!!map\" and (.\"proxy-providers\" | length) == 1 and (.\"proxy-providers\" | to_entries[0].value | tag) == \"!!map\" and (.\"proxy-providers\" | to_entries[0].value.type) == \"http\"' " + shell_quote(exploded) + " >/dev/null 2>&1") != 0) { + system("rm -f " + shell_quote(exploded)); + return { success: false, error: 'invalid_proxy_providers', message: '面板模板只支持一个 type: http 的样板 provider;多 provider 配置请直接上传使用' }; + } + /* pairs 写到临时 JSON 文件 */ writefile(tmp_pairs, sprintf('%.J', pairs)); let tpl_label = match(tpl_src, /^https?:\/\//) ? tpl_src : replace(tpl_src, /^.*\//, ""); + let tpl_expr = length(pairs) > 1 ? '($entry.value | del(.path))' : '$entry.value'; let yq_expr = '(load("' + tmp_pairs + '")) as $pairs | ' - + '(.["proxy-providers"] | keys) as $oldkeys | ' - + '($oldkeys | to_entries | map(.key as $i | .value as $k | {"key": $k, "value": ($pairs[$i].name // $k)}) | from_entries) as $ren | ' - + '($oldkeys | to_entries | map(select(.key < ($pairs | length)) | .key as $i | .value as $k | {"key": $k, "value": $pairs[$i].url}) | from_entries) as $reurl | ' - + '.["proxy-providers"] |= with_entries(.key as $old | .key = ($ren[$old] // $old) | .value.url = ($reurl[$old] // .value.url)) | ' - + '(.["proxy-groups"][] | select(has("use")) | .use) |= map($ren[.] // .) | ' + + '(.["proxy-providers"] | to_entries[0]) as $entry | ' + + tpl_expr + ' as $tpl | ' + + '.["proxy-providers"] = ($pairs | map({"key": .name, "value": ($tpl + {"url": .url})}) | from_entries) | ' + + '(.["proxy-groups"][] | select(has("use")) | .use) |= (map(. as $name | ([$pairs[].name] | select($name == $entry.key)) // [$name]) | flatten) | ' + '."x-clashoo" = {"template": strenv(TPL_LABEL)}'; let inject_cmd = "TPL_LABEL=" + shell_quote(tpl_label) @@ -3584,7 +3614,7 @@ system('rm -f ' + shell_quote(tmp_tpl)); } } if (_core_bin) { - let _ret = system(_core_bin + " -t -d /etc/clashoo -f " + shell_quote(out_path) + " 2>/tmp/clashoo_tpl_verify.txt"); + let _ret = system(_core_bin + " -t -d /etc/clashoo -f " + shell_quote(out_path) + " >/dev/null 2>/tmp/clashoo_tpl_verify.txt"); if (_ret != 0) { let _err = trim(readfile('/tmp/clashoo_tpl_verify.txt') || '配置校验失败'); system('rm -f ' + shell_quote(out_path) + ' /tmp/clashoo_tpl_verify.txt 2>/dev/null'); diff --git a/luci-app-diskman/Makefile b/luci-app-diskman/Makefile index 5e0f7d5bc..20abf875d 100644 --- a/luci-app-diskman/Makefile +++ b/luci-app-diskman/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-diskman PKG_VERSION:=0.2.13 -PKG_RELEASE:=2 +PKG_RELEASE:=3 PKG_MAINTAINER:=lisaac PKG_LICENSE:=AGPL-3.0 @@ -24,7 +24,7 @@ define Package/luci-app-diskman/config config PACKAGE_$(PKG_NAME)_INCLUDE_btrfs_progs depends on PACKAGE_$(PKG_NAME) bool "Include btrfs-progs" - default y + default n config PACKAGE_$(PKG_NAME)_INCLUDE_lsblk depends on PACKAGE_$(PKG_NAME) diff --git a/luci-app-diskman/luasrc/model/cbi/diskman/disks.lua b/luci-app-diskman/luasrc/model/cbi/diskman/disks.lua index 3382afff8..a5b2a7e15 100644 --- a/luci-app-diskman/luasrc/model/cbi/diskman/disks.lua +++ b/luci-app-diskman/luasrc/model/cbi/diskman/disks.lua @@ -43,7 +43,7 @@ d:option(DummyValue, "temp", translate("Temp")) d:option(DummyValue, "p_table", translate("Partition Table")) d:option(DummyValue, "sata_ver", translate("SATA Version")) -- d:option(DummyValue, "rota_rate", translate("Rotation Rate")) -d:option(DummyValue, "health_status", translate("Health") .. "
" .. translate("Status")) +d:option(DummyValue, "health_status", translate("Health") .. " / " .. translate("Status")) -- d:option(DummyValue, "status", translate("Status")) -- local btn_eject = d:option(Button, "_eject") diff --git a/luci-app-diskman/luasrc/model/cbi/diskman/partition.lua b/luci-app-diskman/luasrc/model/cbi/diskman/partition.lua index 348a617a6..179fd90f2 100644 --- a/luci-app-diskman/luasrc/model/cbi/diskman/partition.lua +++ b/luci-app-diskman/luasrc/model/cbi/diskman/partition.lua @@ -25,6 +25,25 @@ m.reset = false local disk_info = dm.get_disk_info(dev, true) local format_cmd = dm.get_format_cmd() +local function is_firmware_partition(part) + local name = part.partition_name or "" + return name:match("^0:") or name:find("rootfs", 1, true) +end + +local first_partition_start +for _, part in ipairs(disk_info.partitions) do + local start = tonumber(part.sec_start) + if part.number > 0 and start then + first_partition_start = math.min(first_partition_start or start, start) + end +end + +local function is_leading_free_space(part) + -- Bootloader data may live in the gap before the first real partition. + return part.number == -1 and first_partition_start and + tonumber(part.sec_end) < first_partition_start +end + s = m:section(Table, {disk_info}, translate("Device Info")) -- s:option(DummyValue, "key") -- s:option(DummyValue, "value") @@ -118,14 +137,15 @@ end -- partitions info if not disk_info.p_table:match("Raid") then - s_partition_table = m:section(Table, disk_info.partitions, translate("Partitions Info"), translate("Default 2048 sector alignment, support +size{b,k,m,g,t} in End Sector")) + s_partition_table = m:section(Table, disk_info.partitions, translate("Partitions Info"), translate("Default 1 MiB alignment, support +size{b,k,m,g,t} in End Sector")) -- s_partition_table:option(DummyValue, "number", translate("Number")) s_partition_table:option(DummyValue, "name", translate("Name")) + s_partition_table:option(DummyValue, "partition_name", translate("Partition Name")) local val_sec_start = s_partition_table:option(Value, "sec_start", translate("Start Sector")) val_sec_start.render = function(self, section, scope) -- could create new partition - if disk_info.partitions[section].number == -1 and disk_info.partitions[section].size > 1 * 1024 * 1024 then + if disk_info.partitions[section].number == -1 and not is_leading_free_space(disk_info.partitions[section]) and disk_info.partitions[section].size > 1 * 1024 * 1024 then self.template = "cbi/value" Value.render(self, section, scope) else @@ -136,7 +156,7 @@ if not disk_info.p_table:match("Raid") then local val_sec_end = s_partition_table:option(Value, "sec_end", translate("End Sector")) val_sec_end.render = function(self, section, scope) -- could create new partition - if disk_info.partitions[section].number == -1 and disk_info.partitions[section].size > 1 * 1024 * 1024 then + if disk_info.partitions[section].number == -1 and not is_leading_free_space(disk_info.partitions[section]) and disk_info.partitions[section].size > 1 * 1024 * 1024 then self.template = "cbi/value" Value.render(self, section, scope) else @@ -192,8 +212,9 @@ if not disk_info.p_table:match("Raid") then disk_info.partitions[section]._fs = value end val_fs.render = function(self, section, scope) - -- use listvalue when partition not mounted - if disk_info.partitions[section].mount_point == "-" and disk_info.partitions[section].number ~= -1 and disk_info.partitions[section].type ~= "extended" then + local part = disk_info.partitions[section] + -- Keep firmware partitions read-only in the filesystem column. + if not is_firmware_partition(part) and part.mount_point == "-" and part.number ~= -1 and part.type ~= "extended" then self.template = "diskman/cbi/format_button" self.inputstyle = "reset" self.inputtitle = disk_info.partitions[section].fs == "raw" and translate("Format") or disk_info.partitions[section].fs @@ -261,6 +282,12 @@ if not disk_info.p_table:match("Raid") then btn_action.forcewrite = true btn_action.template = "diskman/cbi/disabled_button" btn_action.render = function(self, section, scope) + if is_firmware_partition(disk_info.partitions[section]) or is_leading_free_space(disk_info.partitions[section]) then + self.template = "cbi/dvalue" + DummyValue.render(self, section, scope) + return + end + self.template = "diskman/cbi/disabled_button" -- if partition is mounted or the size < 1mb, then disable the add action if disk_info.partitions[section].mount_point ~= "-" or (disk_info.partitions[section].type ~= "extended" and disk_info.partitions[section].number == -1 and disk_info.partitions[section].size <= 1 * 1024 * 1024) then self.view_disabled = true @@ -282,16 +309,17 @@ if not disk_info.p_table:match("Raid") then Button.render(self, section, scope) end btn_action.write = function(self, section, value) + if is_firmware_partition(disk_info.partitions[section]) or is_leading_free_space(disk_info.partitions[section]) then return end if value == translate("New") then local start_sec = disk_info.partitions[section]._sec_start and tonumber(disk_info.partitions[section]._sec_start) or tonumber(disk_info.partitions[section].sec_start) local end_sec = disk_info.partitions[section]._sec_end + local logic_sec = tonumber(disk_info.logic_sec) + local align = math.max(math.ceil(tonumber(disk_info.phy_sec) / logic_sec), math.ceil(1048576 / logic_sec)) if start_sec then -- for sector alignment - local align = tonumber(disk_info.phy_sec) / tonumber(disk_info.logic_sec) - align = (align < 2048) and 2048 - if start_sec < 2048 then - start_sec = "2048" .. "s" + if start_sec < align then + start_sec = tostring(align) .. "s" elseif math.fmod( start_sec, align ) ~= 0 then start_sec = tostring(start_sec + align - math.fmod( start_sec, align )) .. "s" else @@ -306,14 +334,14 @@ if not disk_info.p_table:match("Raid") then if tonumber(end_size) and end_unit then local unit ={ B=1, - S=512, + S=tonumber(disk_info.logic_sec), K=1024, M=1048576, G=1073741824, T=1099511627776 } end_unit = end_unit:upper() - end_sec = tostring(tonumber(end_size) * unit[end_unit] / unit["S"] + tonumber(start_sec:sub(1,-2)) - 1 ) .. "s" + end_sec = tostring(math.ceil(tonumber(end_size) * unit[end_unit] / logic_sec) + tonumber(start_sec:sub(1,-2)) - 1) .. "s" elseif tonumber(end_sec) then end_sec = end_sec .. "s" else @@ -325,8 +353,8 @@ if not disk_info.p_table:match("Raid") then if disk_info.p_table == "MBR" and disk_info["extended_partition_index"] then if tonumber(disk_info.partitions[disk_info["extended_partition_index"]].sec_start) <= tonumber(start_sec:sub(1,-2)) and tonumber(disk_info.partitions[disk_info["extended_partition_index"]].sec_end) >= tonumber(end_sec:sub(1,-2)) then part_type = "logical" - if tonumber(start_sec:sub(1,-2)) - tonumber(disk_info.partitions[section].sec_start) < 2048 then - start_sec = tonumber(start_sec:sub(1,-2)) + 2048 + if tonumber(start_sec:sub(1,-2)) - tonumber(disk_info.partitions[section].sec_start) < align then + start_sec = tonumber(start_sec:sub(1,-2)) + align start_sec = start_sec .."s" end end @@ -363,4 +391,4 @@ if not disk_info.p_table:match("Raid") then end end -return m \ No newline at end of file +return m diff --git a/luci-app-diskman/luasrc/model/diskman.lua b/luci-app-diskman/luasrc/model/diskman.lua index ec3d60cd2..96e85f52b 100644 --- a/luci-app-diskman/luasrc/model/diskman.lua +++ b/luci-app-diskman/luasrc/model/diskman.lua @@ -9,6 +9,7 @@ local ver = require "luci.version" local CMD = {"parted", "mdadm", "blkid", "smartctl", "df", "btrfs", "lsblk"} local d = {command ={}} +local SYSFS_SECTOR_SIZE = 512 for _, cmd in ipairs(CMD) do local command = luci.sys.exec("/usr/bin/which " .. cmd) d.command[cmd] = command:match("^.+"..cmd) or nil @@ -33,7 +34,42 @@ function byte_format(byte) end end +local function get_emmc_health(device) + if not device:match("^mmcblk%d+$") then return end + local path = "/sys/class/block/" .. device .. "/device/" + if not (nixio.fs.readfile(path .. "type") or ""):match("^MMC%s*$") then return end + local translate = require("luci.i18n").translate + local info = {} + local lifetime + -- Use the more worn of the supported A/B estimates; zero means undefined. + for value in (nixio.fs.readfile(path .. "life_time") or ""):gmatch("%S+") do + local estimate = tonumber(value) + if estimate and estimate >= 1 and estimate <= 11 then + lifetime = math.max(lifetime or 0, estimate) + end + end + if lifetime then + if lifetime == 11 then + info.health = translate("Estimated lifetime exceeded") + else + -- Report estimated remaining life as a range, not an exact percentage. + info.health = string.format("%d-%d%%", 100 - lifetime * 10, 110 - lifetime * 10) + end + end + local eol = tonumber(nixio.fs.readfile(path .. "pre_eol_info") or "") + if eol == 1 then + info.status = translate("Normal") + elseif eol == 2 then + info.status = translate("Warning") + elseif eol == 3 then + info.status = translate("Urgent") + end + return info +end + local get_smart_info = function(device) + local emmc_info = get_emmc_health(device) + if emmc_info then return emmc_info end local section local smart_info = {} for _, line in ipairs(luci.util.execl(d.command.smartctl .. " -H -A -i -n standby -f brief /dev/" .. device)) do @@ -188,6 +224,10 @@ local get_parted_info = function(device) elseif device:match("mmcblk") or device:match("md") or device:match("nvme") then partition_temp["name"] = device.."p"..partition_temp["number"] end + -- Read the kernel partition name: older parted output does not escape colons. + local uevent = partition_temp["name"] ~= "-" and + nixio.fs.readfile("/sys/class/block/" .. partition_temp["name"] .. "/uevent") or "" + partition_temp["partition_name"] = (uevent or ""):match("\nPARTNAME=([^\n]+)") or "-" if partition_temp["number"] > 0 and partition_temp["fs"] == "" and d.command.lsblk then partition_temp["fs"] = (luci.util.exec(d.command.lsblk .. " /dev/" .. partition_temp["name"] .. " -no fstype") or ""):match("([^%s]+)") or "" end @@ -230,7 +270,7 @@ local get_parted_info = function(device) elseif (p["number"] <= 4) and (p["number"] > 0) then local s = nixio.fs.readfile("/sys/block/"..device.."/"..p["name"].."/size") if s then - local real_size_sec = tonumber(s) * tonumber(disk_temp.logic_sec) + local real_size_sec = tonumber(s) * SYSFS_SECTOR_SIZE -- if size not equal, it's an extended if real_size_sec ~= p["size"] then disk_temp["extended_partition_index"] = i @@ -313,7 +353,7 @@ d.get_disk_info = function(device, wakeup) -- if only weakup == true, weakup the disk and check parted_info. if smart_info.status ~= "STANDBY" or wakeup or (smart_info["p_table"] and not smart_info["p_table"]:match("Raid")) or device:match("^md") then disk_info = get_parted_info(device) - disk_info["sec_size"] = disk_info["logic_sec"] .. "/" .. disk_info["phy_sec"] + disk_info["sec_size"] = tostring(disk_info["logic_sec"] or "-") .. "/" .. tostring(disk_info["phy_sec"] or "-") disk_info["size_formated"] = byte_format(tonumber(disk_info["size"])) -- if status is standby, after get part info, the disk is weakuped, then get smart_info again for more informations if smart_info.status ~= "ACTIVE" then smart_info = get_smart_info(device) end @@ -362,11 +402,9 @@ d.list_raid_devices = function() end local size = tonumber(fs.readfile(string.format("/sys/class/block/%s/size", mdpath))) - local ss = tonumber(fs.readfile(string.format("/sys/class/block/%s/queue/logical_block_size", mdpath))) - device_info["path"] = "/dev/"..mdpath - device_info["size"] = size*ss - device_info["size_formated"] = byte_format(size*ss) + device_info["size"] = size * SYSFS_SECTOR_SIZE + device_info["size_formated"] = byte_format(device_info["size"]) device_info["active"] = active:upper() device_info["level"] = level device_info["members"] = members @@ -418,19 +456,18 @@ d.list_devices = function() local device_info = {} local device = "/dev/" .. bname local size = tonumber(fs.readfile(string.format("/sys/class/block/%s/size", bname)) or "0") - local ss = tonumber(fs.readfile(string.format("/sys/class/block/%s/queue/logical_block_size", bname)) or "0") local model = fs.readfile(string.format("/sys/class/block/%s/device/model", bname)) local partitions = {} for part in nixio.fs.glob("/sys/block/" .. bname .."/" .. bname .. "*") do local pname = nixio.fs.basename(part) - local psize = byte_format(tonumber(nixio.fs.readfile(part .. "/size"))*ss) + local psize = byte_format(tonumber(nixio.fs.readfile(part .. "/size")) * SYSFS_SECTOR_SIZE) local mount_point = get_mount_point(pname) if mount_point then device_info["inuse"] = true end table.insert(partitions, {name = pname, size_formated = psize, inuse = mount_point}) end device_info["path"] = device - device_info["size_formated"] = byte_format(size*ss) + device_info["size_formated"] = byte_format(size * SYSFS_SECTOR_SIZE) device_info["model"] = model device_info["partitions"] = partitions -- true or false diff --git a/luci-app-diskman/luasrc/view/diskman/disk_info.htm b/luci-app-diskman/luasrc/view/diskman/disk_info.htm index 10ba3e7c2..287c78fcc 100644 --- a/luci-app-diskman/luasrc/view/diskman/disk_info.htm +++ b/luci-app-diskman/luasrc/view/diskman/disk_info.htm @@ -1,7 +1,66 @@ +