linuxOS_AP06/external/rkscript/disk-helper
2025-06-03 12:28:32 +08:00

547 lines
11 KiB
Bash
Executable File

#!/bin/sh
# Uncomment below to see more logs
# set -x
BUSYBOX_MOUNT_OPTS="loop (a|)sync (no|)atime (no|)diratime (no|)relatime (no|)dev (no|)exec (no|)suid (r|)shared (r|)slave (r|)private (un|)bindable (r|)bind move remount ro"
NTFS_3G_MOUNT_OPTS="ro uid=[0-9]* gid=[0-9]* umask=[0-9]* fmask=[0-9]* dmask=[0-9]*"
message()
{
echo "${ID:+"[$ID]: "}$@"
}
check_tool()
{
TOOL=${1%% *}
BR2_CONFIG=$2
type $TOOL >/dev/null && return 0
if grep -wq "ID=buildroot" /etc/os-release 2>/dev/null; then
[ -n "$BR2_CONFIG" ] && \
message "You may need to enable $BR2_CONFIG"
else
message "Missing tool: $TOOL"
fi
return 1
}
is_rootfs()
{
[ $MOUNT_POINT = "/" ]
}
is_ro()
{
mountpoint -q $MOUNT_POINT || return 1
touch $MOUNT_POINT 2>/dev/null || return 0
return 1
}
normalize_device()
{
if is_rootfs; then
DEV_NO="$(mountpoint -d /)"
ROOT_DEV="$(find /sys/dev/ -name "$DEV_NO" | tail -n 1)"
echo "/dev/$(basename "$(realpath "$ROOT_DEV")")"
return
fi
case "$DEV" in
""|tmpfs|proc|devtmpfs|devpts|sysfs|configfs|debugfs|pstore)
return
;;
/dev/*)
echo $DEV
return
;;
esac
# Try udev rules symlink
if find /dev/block/by-name/$DEV 2>/dev/null; then
return
fi
if find /dev/disk/*/$DEV 2>/dev/null; then
return
fi
# Check partition name
UEVENT=$(grep -l "^PARTNAME=$DEV$" \
/sys/class/block/*/uevent 2>/dev/null | tail -n 1 || true)
if [ "$UEVENT" ]; then
echo "/dev/$(basename $(dirname $UEVENT))"
return
fi
# Check mtdblock name
for d in $(ls /dev/ | grep mtdblock || true); do
if grep -q "^$DEV$" /sys/block/$d/device/name; then
echo "/dev/$d"
return
fi
done
# Check mtd
if grep -q "\"$DEV\"$" /proc/mtd 2>/dev/null; then
echo "/dev/$(grep "\"$DEV\"$" /proc/mtd | cut -d':' -f1)"
return
fi
# Parse from blkid
if blkid 2>/dev/null | grep -w "$DEV" | tail -n 1 | \
grep -o "^[^:]*"; then
return
fi
# Give up
echo $DEV
}
prepare_ubi()
{
# Only support ubi for mtd device
if echo $DEV | grep -vq /dev/mtd; then
message "$DEV is not a mtd device!"
return 1
fi
[ "$PART_NO" ] || { message "No valid part number!" && return 1; }
MTD_DEV=/dev/mtd${PART_NO}
UBI_DEV=/dev/ubi${PART_NO}
UBIVOL_DEV=/dev/ubi${PART_NO}_0
UBIBLOCK_DEV=/dev/ubiblock${PART_NO}_0
if [ "$FSGROUP" = ubifs ]; then
DEV=$UBIVOL_DEV
else
DEV=$UBIBLOCK_DEV
fi
message "Preparing $DEV from $MTD_DEV"
message "Remove ubi block device"
if echo $DEV | grep -q ubiblock; then
check_tool ubiblock BR2_PACKAGE_MTD_UBIBLOCK || return 1
ubiblock -r $UBIVOL_DEV >/dev/null 2>/dev/null
fi
message "Detach ubi device"
check_tool ubidetach BR2_PACKAGE_MTD_UBIDETACH || return 1
ubidetach -p $MTD_DEV >/dev/null 2>/dev/null
message "Attach ubi device"
check_tool ubiattach BR2_PACKAGE_MTD_UBIATTACH || return 1
ubiattach /dev/ubi_ctrl -m $PART_NO -d $PART_NO || return 1
message "Check for valid volume"
if [ ! -e $UBIVOL_DEV ]; then
message "No valid ubi volume"
return 1
fi
message "Create ubi block device"
if echo $DEV | grep -q ubiblock; then
check_tool ubiblock BR2_PACKAGE_MTD_UBIBLOCK || return 1
ubiblock -c $UBIVOL_DEV || return 1
fi
return 0
}
format_ubifs()
{
message "Formatting $MTD_DEV for $DEV"
message "Remove ubi block device"
if echo $DEV | grep -q ubiblock; then
check_tool ubiblock BR2_PACKAGE_MTD_UBIBLOCK || return 1
ubiblock -r $UBIVOL_DEV >/dev/null 2>/dev/null
fi
message "Detach ubi device"
check_tool ubidetach BR2_PACKAGE_MTD_UBIDETACH || return 1
ubidetach -p $MTD_DEV >/dev/null 2>/dev/null
message "Format device"
check_tool ubiformat BR2_PACKAGE_MTD_UBIFORMAT || return 1
ubiformat -yq $MTD_DEV || return 1
message "Attach ubi device"
ubiattach /dev/ubi_ctrl -m $PART_NO -d $PART_NO || return 1
message "Create ubi volume"
check_tool ubimkvol BR2_PACKAGE_MTD_UBIMKVOL || return 1
ubimkvol $UBI_DEV -N $PART_NAME -m || return 1
message "Create ubi block device"
if echo $DEV | grep -q ubiblock; then
check_tool ubiblock BR2_PACKAGE_MTD_UBIBLOCK || return 1
ubiblock -c $UBIVOL_DEV || return 1
fi
}
remount_part()
{
mountpoint -q $MOUNT_POINT || return
if is_ro; then
[ "$1" != ro ] || return
else
[ "$1" != rw ] || return
fi
mount -o remount,$1 $MOUNT_POINT
}
format_part()
{
message "Formatting $DEV($FSTYPE)"
case $FSGROUP in
ext2)
# Set max-mount-counts to 0, and disable the time-dependent checking.
check_tool mke2fs BR2_PACKAGE_E2FSPROGS && \
mke2fs -F -b 4096 -L $PART_NAME $DEV && \
tune2fs -c 0 -i 0 $DEV
;;
vfat)
# Use fat32 by default
check_tool mkfs.vfat BR2_PACKAGE_DOSFSTOOLS_MKFS_FAT && \
mkfs.vfat -I -F 32 -n $PART_NAME $DEV
;;
exfat)
check_tool mkexfatfs BR2_PACKAGE_EXFAT_UTILS && \
mkexfatfs -n $PART_NAME $DEV
;;
ntfs)
# Enable compression
check_tool mkntfs BR2_PACKAGE_NTFS_3G_NTFSPROGS && \
mkntfs -FCQ -L $PART_NAME $DEV
;;
btrfs)
check_tool mkfs.btrfs BR2_PACKAGE_BTRFS_PROGS && \
mkfs.btrfs -f -L $PART_NAME $DEV
;;
f2fs)
check_tool mkfs.f2fs BR2_PACKAGE_F2FS_TOOLS && \
mkfs.f2fs -f -l $PART_NAME $DEV
;;
ubifs)
format_ubifs
;;
jffs2)
check_tool mkfs.jffs2 BR2_PACKAGE_MTD_MKFSJFFS2 && \
mkfs.jffs2 -o $DEV 0x10000 --pad=0x400000 -s 0x1000 -n
;;
erofs|squashfs)
message "It's pointness to format a read-only partition..."
false
;;
auto)
message "Unable to format a auto partition..."
false
;;
*)
message "Unsupported file system $FSTYPE for $DEV"
false
;;
esac
}
format_resize()
{
BACKUP=$1
SRC=$(realpath $MOUNT_POINT)
message "Format-resizing $DEV($FSTYPE)"
message "Backup original data"
cp -a "$SRC" "$BACKUP/" || return 1
umount "$SRC" || return 1
message "Format and mount rw"
format_part || return 1
mount_part || return 1
remount_part rw
message "Restore backup data"
cp -a "$BACKUP/$SRC" $(dirname "$SRC") || return 1
}
resize_ext2()
{
check_tool resize2fs BR2_PACKAGE_E2FSPROGS_RESIZE2FS || return 1
resize2fs $DEV
}
resize_vfat()
{
check_tool fatresize BR2_PACKAGE_FATRESIZE || return 1
SIZE=$(fatresize -i $DEV | grep "Size:" | grep -o "[0-9]*$")
# Somehow fatresize only works for 256M+ fat
[ "$SIZE" -gt $((256 * 1024 * 1024)) ] && return 1
MAX_SIZE=$(( $(cat $SYS_PATH/size) * 512))
MIN_SIZE=$(($MAX_SIZE - 16 * 1024 * 1024))
[ $MIN_SIZE -lt $SIZE ] && return 0 # Large enough!
while [ $MAX_SIZE -gt $MIN_SIZE ];do
# Somehow fatresize cannot resize to max size
MAX_SIZE=$(($MAX_SIZE - 512 * 1024))
# Try to resize with fatresize, not always work
fatresize -s $MAX_SIZE $DEV && return
done
return 1
}
resize_ntfs()
{
check_tool ntfsresize BR2_PACKAGE_NTFS_3G_NTFSPROGS || return 1
echo y | ntfsresize -f $DEV
}
resize_btrfs()
{
check_tool btrfs BR2_PACKAGE_BTRFS_PROGS || return 1
btrfs filesystem resize max $DEV
}
resize_f2fs()
{
check_tool resize.f2fs BR2_PACKAGE_F2FS_TOOLS || return 1
resize.f2fs $DEV
}
resize_part()
{
# Already resized
if [ -f $MOUNT_POINT/.resized ]; then
message "Already resized $DEV($FSTYPE)"
return
fi
if [ -z "$FSRESIZE" ]; then
message "No resize for $FSTYPE"
return
fi
message "Resizing $DEV($FSTYPE)"
# Online resize needs read-write
remount_part rw
if eval $FSRESIZE; then
touch $MOUNT_POINT/.resized
sync
return
fi
message "Done with rootfs"
is_rootfs && return
message "Fallback to format resize"
TEMP_BACKUP=$(mktemp -d)
if format_resize $TEMP_BACKUP; then
touch $MOUNT_POINT/.resized
sync
fi
rm -rf $TEMP_BACKUP
}
convert_mount_opts()
{
# Accept all opts by default for standard mount tool
if [ -z "$@" ] && ! mount --help 2>&1 | grep -q BusyBox; then
echo $OPTS
return
fi
# Filter out unsupported opts
for opt in ${@:-$BUSYBOX_MOUNT_OPTS}; do
echo ${OPTS//,/ } | xargs -n 1 | grep -oE "^$opt$"
done | tr "\n" ","
}
prepare_part()
{
# Ignore external storages
case "$MOUNT_POINT" in
/mnt/* | /media/*) return 1 ;;
esac
DEV="$(realpath "$(normalize_device | head -n 1)" 2>/dev/null)"
[ "$DEV" ] || return 1
# Only accept block or char device
[ -b "$DEV" -o -c "$DEV" ] || return 1
message "Handling $DEV $MOUNT_POINT $FSTYPE $OPTS $PASS"
SYS_PATH=$(echo /sys/class/*/${DEV##*/})
if [ -f "$SYS_PATH/name" ]; then
PART_NAME=$(cat $SYS_PATH/name)
else
PART_NAME=$(grep PARTNAME ${SYS_PATH}/uevent | cut -d '=' -f 2)
fi
PART_NAME=${PART_NAME:-${DEV##*/}}
PART_NO=$(echo $DEV | grep -oE "[0-9]*$")
case $FSTYPE in
ext[234])
FSGROUP=ext2
FSCK="fsck.$FSTYPE -y"
FSCK_CONFIG=BR2_PACKAGE_E2FSPROGS_FSCK
FSRESIZE=resize_ext2
;;
msdos|fat|vfat)
FSGROUP=vfat
FSCK="fsck.vfat -y"
FSCK_CONFIG=BR2_PACKAGE_DOSFSTOOLS_FSCK_FAT
FSRESIZE=resize_vfat
;;
exfat)
FSGROUP=exfat
FSCK="exfatfsck -y"
FSCK_CONFIG=BR2_PACKAGE_EXFAT_UTILS
unset FSRESIZE
;;
ntfs)
FSGROUP=ntfs
FSCK=ntfsfix
FSCK_CONFIG=BR2_PACKAGE_NTFS_3G_NTFSPROGS
FSRESIZE=resize_ntfs
;;
btrfs)
FSGROUP=btrfs
FSCK="btrfs check --repair"
FSCK_CONFIG=BR2_PACKAGE_BTRFS_PROGS
FSRESIZE=resize_btrfs
;;
f2fs)
FSGROUP=f2fs
FSCK="fsck.f2fs -y"
FSCK_CONFIG=BR2_PACKAGE_F2FS_TOOLS
FSRESIZE=resize_f2fs
;;
ubifs)
FSGROUP=ubifs
unset FSCK
unset FSRESIZE
;;
squashfs)
FSGROUP=squashfs
unset FSCK
unset FSRESIZE
;;
erofs)
FSGROUP=erofs
unset FSCK
unset FSRESIZE
;;
jffs2)
FSGROUP=jffs2
unset FSCK
unset FSRESIZE
;;
auto)
FSGROUP=auto
message "Running fsck on a random fs is dangerous"
unset FSCK
unset FSRESIZE
;;
*)
message "Unsupported file system $FSTYPE for $DEV"
return 1
esac
# Setup mount tool and opts
case $FSGROUP in
ntfs)
if grep -wq ntfs3 /proc/filesystems; then
MOUNT="mount -t ntfs3"
OPTS=$(convert_mount_opts)
else
MOUNT=ntfs-3g
check_tool ntfs-3g BR2_PACKAGE_NTFS_3G || return 1
OPTS=$(convert_mount_opts "$NTFS_3G_MOUNT_OPTS")
fi
;;
auto)
MOUNT=mount
OPTS=$(convert_mount_opts)
;;
*)
MOUNT="mount -t $FSTYPE"
OPTS=$(convert_mount_opts)
;;
esac
MOUNT_OPTS=${OPTS:+" -o ${OPTS%,}"}
# Detect ro/rw
MOUNT_RO_RW=$(echo "$OPTS" | tr ',' ' ' | xargs -n 1 | \
grep -o "^r[ow]$" || true)
if [ -z "$MOUNT_RO_RW" ]; then
if is_rootfs && is_ro; then
MOUNT_RO_RW=ro
else
MOUNT_RO_RW=rw
fi
fi
# Prepare for ubi
if [ "$FSTYPE" = ubifs ] ||
[ "$(dd if=$DEV bs=1 count=3 status=none)" = UBI ]; then
if ! prepare_ubi; then
message "Failed to prepare ubi for $DEV"
if [ "$AUTO_MKFS" ] && [ "$FSGROUP" = ubifs ]; then
message "Auto formatting"
format_ubifs || true
fi
fi
fi
}
check_part()
{
[ "$SKIP_FSCK" -o "$PASS" -eq 0 ] && return
if [ -z "$FSCK" ]; then
message "No fsck for $FSTYPE"
return
fi
message "Checking $DEV($FSTYPE)"
if is_rootfs; then
# Remount ro for checking
remount_part ro
elif mountpoint -q "$MOUNT_POINT"; then
# Unmount for checking
umount "$MOUNT_POINT" || remount_part ro
fi
check_tool "$FSCK" $FSCK_CONFIG || return
$FSCK $DEV
}
mount_part()
{
mountpoint -q "$MOUNT_POINT" && return
message "Mounting $DEV($FSTYPE) on $MOUNT_POINT ${MOUNT_OPTS:+with$MOUNT_OPTS}"
$MOUNT $DEV $MOUNT_POINT $MOUNT_OPTS && return
[ "$AUTO_MKFS" ] || return
message "Failed to mount $DEV, try to format it"
format_part && $MOUNT $DEV $MOUNT_POINT $MOUNT_OPTS
}