#! /bin/sh . /lib/partman/lib/base.sh . /lib/partman/lib/resize.sh . /lib/partman/lib/recipes.sh # busybox wants mount -o move; util-linux wants mount --move. Sigh. if [ -d /lib/debian-installer ]; then mount_move='-o move' else mount_move='--move' fi modprobe loop >/dev/null 2>&1 || true # Set up working directory. if type mktemp >/dev/null 2>&1; then recipe_dir="$(mktemp -d /tmp/partman-auto-loop.XXXXXX)" trap "rm -rf $recipe_dir" EXIT HUP INT QUIT TERM else recipe_dir=/tmp fi # Fetch parameters. disk="$1" cd $disk if ! db_get partman-auto-loop/partition || [ -z "$RET" ]; then logger -t partman-auto-loop "Error: No partition number specified in partman-auto-loop/partition" exit 1 fi partition="$RET" if ! db_get partman-auto-loop/recipe || [ -z "$RET" ]; then logger -t partman-auto-loop "Error: No recipe specified in partman-auto-loop/recipe" exit 1 fi recipe="$RET" echo "$recipe" >"$recipe_dir/loop_recipe" # Find the requested partition. db_progress START 0 5 partman-auto/text/automatically_partition db_progress INFO partman-auto/progress/info partition_id= partition_fs= open_dialog PARTITIONS while { read_line num id size type fs path name; [ "$id" ]; }; do if [ "$num" = "$partition" ]; then partition_id="$id" partition_fs="$fs" # go ahead and read all remaining input fi done close_dialog if [ -z "$partition_id" ]; then logger -t partman-auto-loop "Error: Partition number $partition not found in $disk" exit 1 fi # Set up the requested partition in partman. existing=no for j in $( for i in /lib/partman/valid_filesystems/*; do [ -x $i ] || continue $i $disk $partition_id existing done ); do if [ "$j" = "$partition_fs" ]; then existing=yes fi done if [ "$existing" = no ]; then logger -t partman-auto-loop "Error: No filesystem on $disk/$partition_id" exit 1 fi echo keep >$partition_id/method rm -f $partition_id/format >$partition_id/use_filesystem echo $partition_fs >$partition_id/filesystem mkdir -p $partition_id/options echo / >$partition_id/mountpoint update_partition $disk $partition_id # Is there enough space to perform the recipe? dev="$disk" oldid="$partition_id" recipe_new= firstword= imagepaths= for word in $(cat "$recipe_dir/loop_recipe"); do case $word in .) recipe_new="${recipe_new:+$recipe_new }\$imagepath{ $firstword } $word" firstword= ;; *) if [ "$firstword" ]; then recipe_new="${recipe_new:+$recipe_new }$word" else firstword="$word" imagepaths="${imagepaths:+$imagepaths }$word" fi ;; esac done echo "$recipe_new" >"$recipe_dir/loop_recipe_new" decode_recipe "$recipe_dir/loop_recipe_new" loop db_progress STEP 1 fstab="$( for i in /lib/partman/fstab.d/*; do [ -x "$i" ] || continue $i done | while read fs mp type options dump pass; do case $mp in (/) echo $fs $mp $type $options $dump $pass ;; esac done )" if [ -z "$fstab" ]; then logger -t partman-auto-loop "Error: No fstab output for $disk/$partition_id" exit 1 fi mkdir -p /target mountpoint="$(grep "^${fstab%% *} [^ ]* [^ ]* [^ ]*rw" /proc/mounts | cut -d ' ' -f2 | head -n1)" || mountpoint= if [ "$mountpoint" = /target ]; then # nothing to do : elif [ "$mountpoint" ]; then if ! mount $mount_move "$mountpoint" /target; then logger -t partman-auto-loop "Error: Failed to move $mountpoint to /target" exit 1 fi unmount_cmd='umount /target' else for m in /lib/partman/mount.d/*; do [ -x "$m" ] || continue unmount_cmd="$($m "$fstab")" if [ "$?" = 0 ]; then break fi done fi if [ -d /run ]; then mkdir -p /run/sendsigs.omit.d pidof mount.ntfs >> /run/sendsigs.omit.d/ntfs-3g pidof mount.ntfs-3g >> /run/sendsigs.omit.d/ntfs-3g fi # TODO: handle errors if no mount succeeded mkdir -p /host mount $mount_move /target /host # TODO error handling # Don't try to mount this again later. rm -f $partition_id/mountpoint # Ensure there is enough free space. check_free_space=false requires_disk_space(){ [ "$1" != 0 ] || return path="$(echo "$*" | sed 's/.*\$imagepath{ *\([^ }]*\) *}.*/\1/')" [ "$path" != "$*" ] || return case $path in /*) ;; *) path="/$path" ;; esac [ -f "/host$path" ] && return check_free_space=true } foreach_partition 'requires_disk_space $*' # Skip resize_range check if images are already created. if [ $check_free_space = true ]; then case $partition_fs in linux-swap|fat16|fat32|hfs|hfs+|hfsx) get_resize_range ;; ext2|ext3|ext4) if ! search-path tune2fs; then logger -t partman-auto-loop "Error: tune2fs not found" exit 1 fi if ! search-path resize2fs; then logger -t partman-auto-loop "Error: resize2fs not found" exit 1 fi if ! get_ext2_resize_range; then logger -t partman-auto-loop "Error: Failed to get ext2 resize range for $disk/$partition_id" exit 1 fi ;; ntfs) if ! search-path ntfsresize; then logger -t partman-auto-loop "Error: ntfsresize not found" exit 1 fi if ! get_ntfs_resize_range; then db_input critical partman-auto-loop/unclean_ntfs || true db_go || true logger -t partman-auto-loop "Error: Failed to get NTFS resize range for $disk/$partition_id" reboot exit 1 fi ;; *) logger -t partman-auto-loop "Cannot calculate free space on filesystems of type $partition_fs" exit 1 ;; esac free_size="$(expr \( "$cursize" - "$minsize" \) \* 9 / 10)" # convert to megabytes free_size="$(expr 0000000"$free_size" : '0*\(..*\)......$')" if [ $(min_size) -gt $free_size ]; then logger -t partman-auto-loop "Error: partman-auto-loop/recipe too large ($(min_size) > $free_size)" exit 1 fi fi # Ensure that no old loop images are present and mountable. found_images= mkdir -p /tmpmountpoint for path in $imagepaths; do case $path in /*) ;; *) path="/$path" ;; esac if [ -e "/host$path" ]; then if mount -t auto -o loop,ro /host$path /tmpmountpoint 2>/dev/null 3>&-; then found_images="${found_images:+$found_images }$path" umount /tmpmountpoint || true rmdir /tmpmountpoint || true fi fi done if [ "$found_images" ]; then db_progress STOP db_subst partman-auto-loop/unclean_host PARTITION "$partition" db_subst partman-auto-loop/unclean_host DISK "$disk" db_subst partman-auto-loop/unclean_host IMAGES "$found_images" db_input critical partman-auto-loop/unclean_host || true db_capb db_go || true db_capb backup umount /host || true exit 1 fi db_progress STEP 1 expand_scheme db_progress STEP 1 clean_method db_progress STEP 1 setup_loop () { [ "$1" != 0 ] || return path="$(echo "$*" | sed 's/.*\$imagepath{ *\([^ }]*\) *}.*/\1/')" [ "$path" != "$*" ] || return case $path in /*) ;; *) path="/$path" ;; esac if [ ! -f "/host$path" ]; then mkdir -p "/host${path%/*}" if [ "$4" = "linux-swap" ]; then # swap requires a file with no holes dd if=/dev/zero of="/host$path" bs="1000000" count="$1" else dd if=/dev/zero of="/host$path" bs="1000000" seek="$1" count=0 fi fi if ! losetup -f "/host$path"; then shift continue fi if [ "$4" = linux-swap ]; then loops="/host$path" else loops="$(echo /dev/loop* /dev/loop/*)" fi for loop in $loops; do [ -e "$loop" ] || continue case $loop in /dev/loop*) loopfile="$(losetup "$loop")" || continue # The following works with both busybox's # losetup and util-linux's losetup. Yes, # this is ugly. loopfile="$(echo "$loopfile" | sed 's,.*\(/host/[^)]*\).*,\1,')" ;; *) loopfile="$loop" ;; esac [ "$loopfile" = "/host$path" ] || continue dirname="$(echo "$loop" | sed 's:/:=:g')" dev="$DEVICES/$dirname" rm -rf "$dev" mkdir "$dev" || autopartitioning_failed printf "%s" "$loop" >"$dev/device" printf "%s" "$1" >"$dev/size" echo "Loopback on $loopfile" >"$dev/model" echo "$loopfile" >"$dev/loop" cd "$dev" open_dialog OPEN "$(cat "$dev/device")" read_line response close_dialog if [ "$response" = failed ]; then cd / rm -rf "$dev" autopartitioning_failed fi open_dialog NEW_LABEL loop close_dialog # find the free space open_dialog PARTITIONS free_space= while { read_line num id size type fs path name; [ "$id" ]; }; do if [ "$fs" = free ]; then free_space=$id free_size=$size fi done close_dialog # create partition in the free space [ "$free_space" ] || autopartitioning_failed open_dialog NEW_PARTITION primary $4 $free_space full ${1}000001 read_line num id size type fs path name close_dialog shift; shift; shift; shift setup_partition $id $* break done } foreach_partition 'setup_loop $*' db_progress STEP 1 update_all apt-install lupin-support db_progress STOP exit 0