summaryrefslogblamecommitdiffstats
path: root/core/modules/disk-partitions/data/opt/openslx/scripts/systemd-setup_partitions
blob: 9ca3ac789fc21a46920036f7ca1cb7e70a0145c8 (plain) (tree)






























                                                                               
        


          


                                                                       

































































































                                                                                                                                                                                 
                                                                                   




















                                                                               
                                   

























                                                                                                         






                                                                                         

  
                 

                                                    




                                              







                                       
          

                                                                          
                                                        


                                                                                                            

                    



                                                                                  
                                                
                    
                                                                                                   
                                                 
                                                                                            


                                                                                           



                                                                           
          





                                                                                
                                            
                    
                                                                                                   













                                                                                                   
                                                  


                                                                             
                                                                                                   















                                                                                                                                             
                                                                                                                                              















































                                                                                                                                                                                                    








                                                                                                
                                                   
                                       


                                                                                                  




                                           

                                        

        







                                                                                                 
                                                                                       

                                                                               











                                                                                                                     
#!/bin/bash
# Arrays etc and $(( )) with big numbers
# -----------------------------------------------------------------------------
#
# Copyright (c) 2018 bwLehrpool-Projektteam
#
# This program/file is free software distributed under the GPL version 2.
# See https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
#
# If you have any feedback please consult https://bwlehrpool.de and
# send your feedback to support@bwlehrpool.de.
#
# General information about bwLehrpool can be found at https://bwlehrpool.de
#
# -----------------------------------------------------------------------------
#
# Local hard disk autodetection script for OpenSLX linux stateless clients,
# detecting swap and special partitions

#############################################################################

# Mount point for persistent scratch partition (type 45)
MOUNT_POINT_45="/opt/openslx/persistent"
PARTITION_FILE="/run/openslx/partitions"
readonly MOUNT_POINT_45 PARTITION_FILE
mkdir -p "/run/openslx"

. /opt/openslx/config
. /opt/openslx/bin/slx-tools
dev_find_partitions &> /dev/null # Preload function

logfile=
HAVE_TEMP=
HAVE_SWAP=

# Check what to do. Currently triggered by:
# setup-partitions.service (Mini and Maxi, for swap, linux, persistent)
# setup-temp.service (Mini only, for ID44 /tmp)
while (( $# > 0 )); do
	case "$1" in
		--tmp) DO_TMP=1 ;;
		--persistent) DO_PERSISTENT=1 ;;
		--swap) DO_SWAP=1 ;;
		--linux) DO_LINUX=1 ;;
		*) echo "Unknown option '$1'" >&2 ;;
	esac
	shift
done

declare -a TMPFILES
gettmp () {
	local vn file
	for vn in "$@"; do
		file=$(mktemp -p /run/openslx) # since we fiddle around with /tmp in this script
		declare -g "${vn}=${file}"
		TMPFILES+=("$file")
	done
}
delalltmp () {
	rm -f -- "${TMPFILES[@]}"
}
trap delalltmp EXIT

# get_mount_options <fstype> <varname>
get_mount_options () {
	case "$1" in
		ext2)
			declare -ag "${2}=(-o nocheck)"
			;;
		ext4)
			declare -ag "${2}=(-o 'errors=remount-ro,data=ordered,relatime,quota')"
			;;
		*)
			declare -ag "${2}=()"
	esac
}

# General formatter for the /tmp partition on a local harddisk
format_disk () {
	declare -ag MOUNT_OPTIONS_SET_BY_FORMAT_DISK=() # Global var!
	local target="$1"
	local fslist="xfs jfs ext3 ext2 ext4"
	local fs
	declare -a fopt
	[ $# -ge 2 ] && fslist="$2"
	for fs in $fslist ; do
		if grep -q "\\b${fs}\\b" "/proc/filesystems"; then
			# Filesystem already supported by running kernel
			:
		elif modprobe "${fs}"; then
			# Filesystem module could be loaded and should be supported now
			:
		else
			# Not supported, try next one
			continue
		fi
		if which "mkfs.$fs" ; then
			case "$fs" in
			reiserfs)
				fopt=("-f")
				;;
			xfs)
				fopt=("-f" "-K")
				;;
			ext2|ext3)
				fopt=("-F")
				;;
			ext4)
				fopt=(-F -b 4096 -O "extent,huge_file,flex_bg,metadata_csum,64bit,dir_nlink,extra_isize,quota" -E "nodiscard,quotatype=usrquota:prjquota" -I 256)
				;;
			jfs)
				fopt=()
				;;
			*)
				fopt=()
				;;
			esac
			get_mount_options "$fs" MOUNT_OPTIONS_SET_BY_FORMAT_DISK
			"mkfs.$fs" "${fopt[@]}" "${target}" && return 0 # Success!
		fi
	done
	return 1
}

# cd to /tmp, mount new /tmp, mv * from cwd (=old temp) to /tmp (=new temp)
# If cd to /tmp fails, call the fallback function below
mount_temp () {
	local PRE=$(pwd)
	if ! cd /tmp; then
		mount_temp_fallback "$@"
		return $?
	fi
	mount "$@" /tmp || return 1
	chmod a+rwxt /tmp
	# Move stuff from working directory, which is old /tmp, to new /tmp just mounted
	mv ./* ./.[!.]* ./..?* /tmp/ 2> /dev/null
	local OLD=$(LANG=C.UTF-8 ls -alh | grep -v -E ' \.\.?$' | grep -v '^total')
	[ -n "$OLD" ] && echo -- "Leftovers:" && echo -- "$OLD"
	cd "$PRE" || true
}

# Fallback: Make new temporary /tmp, move everything there, mount new /tmp
# then move everything back.
mount_temp_fallback () {
	mkdir -p /tmptmp
	mv /tmp/* /tmp/.* /tmptmp/ 2> /dev/null
	mount "$@" /tmp || return 1
	chmod a+rwxt /tmp
	mv /tmptmp/* /tmptmp/.* /tmp/
	rmdir /tmptmp
	return 0
}

# call with --hdd <secs>: Wait for max. <secs> seconds until an HDD appears
# call with <secs>: Wait for max. <secs> seconds until udevadm settile finishes
wait_for_udev () {
	local upid ctr hdd
	hdd=
	if [ "$1" = "--hdd" ]; then
		hdd=true
		shift
	fi
	ctr=$(( "$1" * 10 ))
	if ! [ "$ctr" -gt 0 ]; then # Negation to catch NaN
		ctr=1
	fi
	udevadm trigger &
	usleep 20000 # 20ms
	udevadm settle &> /dev/null & # --timeout doesn't work reliably, sometimes the process just hangs
	upid=$!
	while [ "$ctr" -gt 0 ]; do
		[ -n "$hdd" ] && has_hdd && break
		[ -z "$hdd" ] && ! [ -d "/proc/$upid" ] && break
		usleep 100000 # 100ms
		ctr=$(( ctr - 1 ))
	done
	if [ -d "/proc/$upid" ]; then
		kill -9 "$upid" &> /dev/null &
	fi
}

has_hdd () {
	[ -n "$( ls -U -1 /dev/disk/by-path/ )" ]
}

if ! [ -s "/run/openslx/dmsetup.state" ]; then
	# If above file exists we should've waited for HDDs to appear in stage 3 already,
	# so don't just wait for udev for two+ seconds for no reason.
	wait_for_udev 2
	if ! has_hdd; then
		wait_for_udev --hdd 4
	fi
fi

declare -A known=
shopt -s extglob
for disk in /dev/disk/by-path/!(*-part*|*-usb-*); do
	disk="$( readlink -f "$disk" )"
	[ -b "$disk" ] || continue
	[ -z "${known["$disk"]}" ] || continue
	known["$disk"]=1
	fdisk -l "$disk"
done > "$PARTITION_FILE"
shopt -u extglob
if ! [ -s "$PARTITION_FILE" ]; then
	echo "none" > "$PARTITION_FILE"
fi
echo "Partitions:"
cat "$PARTITION_FILE"

linux_pid=
if [ -n "$DO_LINUX" ]; then
	# Put detected linux partitions (83) into /etc/fstab with "noauto"
	echo "Adding existing linux partitions to fstab"
	for PART_DEV in $(dev_find_partitions "83"); do
		mkdir -p "/media/${PART_DEV#/dev/}"
		echo -e "${PART_DEV}\t/media/${PART_DEV#/dev/}\tauto\t\tnoauto,noexec\t 0 0" >> "/etc/fstab"
	done &
	linux_pid=$!
fi

if [ -n "$DO_SWAP" ]; then
	# Check for standard swap partitions and make them available to the system
	echo "Enabling existing swap partitions"
	HAVE_SWAP=no
	for PART_DEV in $(dev_find_partitions --rw "82" "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f"); do
		if swapon -p 10 "$PART_DEV"; then
			HAVE_SWAP=yes # low priority, in case we have zram swap, prefer that
			echo -e "$PART_DEV\tswap\t\tswap\t\tdefaults\t 0 0" >> "/etc/fstab"
		fi
	done
	if [ -b "/dev/mapper/slx-swap" ] && mkswap "/dev/mapper/slx-swap" \
			&& swapon -p 10 "/dev/mapper/slx-swap"; then
		echo "Added slx-swap in ID44"
		HAVE_SWAP=yes # finally, success
	fi
fi

if [ -n "$DO_TMP" ]; then
	# We use special non assigned partition type (id44) for harddisk scratch
	# space, thus no normal filesystem will be incidentally deleted or
	# corrupted
	echo "Mounting ID44 temp partitions"
	HAVE_TEMP=no
	for PART_DEV in $(dev_find_partitions --rw "44" "87f86132-ff94-4987-b250-444444444444"); do
		# check for supported filesystem and formatter
		if format_disk "$PART_DEV"; then
			mount_temp "${MOUNT_OPTIONS_SET_BY_FORMAT_DISK[@]}" "$PART_DEV" || continue
			echo -e "${PART_DEV}\t/tmp\t\tauto\t\tnoexec\t 0 0" >> "/etc/fstab"
			HAVE_TEMP=yes
			break
		else
			echo "formatting failed for some reason"
		fi # Made this non-forking, systemd should handle it - 2013-05-28
	done
fi

if [ -n "$DO_PERSISTENT" ]; then
	# special partition 45 (persistent scratch) to $MOUNT_POINT_45
	echo "Mounting ID45 persistent partitions"
	HAVE_PARTITION_45=no
	get_mount_options "ext4" mopts
	# try all the ID45 partitions until one succeeds, from large to small
	for PART_DEV in $(dev_find_partitions --rw "45" "87f86132-ff94-4987-b250-454545454545"); do
		mkdir -p "$MOUNT_POINT_45"
		# Let's see if this is an ext4 partition and if so, whether it has the proper size
		# Any fixing should happen first
		gettmp "logfile"
		COUNT=0
		while (( COUNT++ < 4 )); do
			fsck.ext4 -y "$PART_DEV" &> "$logfile"
			RET=$?
			if (( (RET & 7) == 4 )); then
				slxlog "partition-45-fsck" "Error fixing file system errors on ID45 partition" "$logfile"
				break
			fi
			(( (RET & 3) != 1 )) && break
		done
		# awk script to take block count and block size from dumpe2fs output and multiply them to get byte size (bc because awk suxx)
		fs_size=$(dumpe2fs -h "$PART_DEV" | awk -F: \
			'BEGIN{a=0;b=0}{if ($1 == "Block count") a=$2; if($1 == "Block size") b=$2;}END{ if (a>0 && b>0) print a "*" b}' | bc)
		echo "$PART_DEV has ext4 fs of size $fs_size"
		if [ -n "$fs_size" ] && (( fs_size > 1000000 )); then
			# It's ext4, see if partition size was changed offline
			dev_size=$(blockdev --getsize64 "$PART_DEV")
			echo "$PART_DEV has actual size of $dev_size"
			if [ -n "$dev_size" ] && (( dev_size > 1000000 )); then
				# somewhat sane, see what to do
				dev_mb=$(( dev_size / 1024 / 1024 ))
				fs_mb=$(( fs_size / 1024 / 1024 ))
				echo "Dev: $dev_mb, fs: $fs_mb"
				if (( (fs_mb + 100) < dev_mb )); then
					# dev size plus 100MB is still smaller than reported fs size -- resize fs
					gettmp "logfile"
					fsck.ext4 -f -y "$PART_DEV" &> "$logfile"
					if resize2fs "$PART_DEV" &>> "$logfile"; then
						slxlog "partition-45-resize-ok" "Resized partition $PART_DEV from $fs_mb MiB to $dev_mb MiB" "$logfile"
					else
						slxlog "partition-45-resize-fail" "Could not enlarge file system size of $PART_DEV from $fs_mb MiB to $dev_mb MiB" "$logfile"
						dd if=/dev/zero of="$PART_DEV" bs=1M count=1 &>/dev/null
					fi
				elif (( dev_size < fs_size )); then
					# partition is smaller than expected by fs -- killall
					slxlog "partition-45-shrink" "$PART_DEV has ext4 file system which is $fs_mb MiB, but partition size is only $dev_mb MiB. Will wipe partition to be safe..."
					dd if=/dev/zero of="$PART_DEV" bs=1M count=1 &>/dev/null
				fi
			fi
		fi
		# try to mount
		if ! mount -v -t ext4 "${mopts[@]}" "${PART_DEV}" "$MOUNT_POINT_45"; then
			# failed, try to format
			gettmp "logfile"
			if ! format_disk "$PART_DEV" "ext4" &> "$logfile"; then
				slxlog "partition-45-format" "Cannot format $PART_DEV with ext4" "$logfile"
				continue
			fi
			gettmp "logfile"
			if ! mount -v -t ext4 "${mopts[@]}" "${PART_DEV}" "$MOUNT_POINT_45" &> "$logfile"; then
				slxlog "partition-45-newmount" "Cannot mount $PART_DEV with ext4 right after formatting" "$logfile"
				continue
			fi
		fi
		# Mount success -- clean up lost+found
		find "${MOUNT_POINT_45}/slx_lost+found" -mindepth 1 -maxdepth 1 -mtime +90 -type d -exec rm -rf -- {} \;
		if [ -d "${MOUNT_POINT_45}/lost+found" ]; then
			touch "${MOUNT_POINT_45}/lost+found"
			mkdir -p "${MOUNT_POINT_45}/slx_lost+found"
			mv -f -- "${MOUNT_POINT_45}/lost+found" "${MOUNT_POINT_45}/slx_lost+found/$(date +%s)_$$-$RANDOM"
		fi
		# fstab entry
		echo -e "${PART_DEV}\t${MOUNT_POINT_45}\tauto\t\tnoauto\t\t 0 0" >> "/etc/fstab"
		HAVE_PARTITION_45=yes
		break # success, done
	done
fi # /persistent

# finally, prepare the data subdir on persistent part
if [ "$HAVE_PARTITION_45" = "yes" ]; then
	echo "Fixing permissions on ID45 partition"
	mkdir -p "$MOUNT_POINT_45/data"
	chown 0:0  "${MOUNT_POINT_45}" "${MOUNT_POINT_45}/slx_lost+found" "${MOUNT_POINT_45}/data"
	chmod 0700 "${MOUNT_POINT_45}/slx_lost+found"
	chmod 0755 "${MOUNT_POINT_45}"
	chmod a+rwxt "$MOUNT_POINT_45/data"
elif [ -d "$MOUNT_POINT_45" ]; then
	rm -f -- "$MOUNT_POINT_45"
fi

[ -n "$linux_pid" ] && wait "$linux_pid"

mount -a

# HAVE_* will be empty if the according cmdline option was not passed.

# Make tmpfs if nothing could be mounted for /tmp
# 2016-10-12: Use a sane size of 66% which should be generous enough and prevent the machine from
# just crashing if RAM is too full. We previously hugely oversized since vmware wants at least as
# much free space as the VMs RAM; however, this requirement can be disabled with a vmx setting,
# which we're now doing.
if [ "$HAVE_TEMP" = "no" ]; then
	# If --tmp wasn't requested HAVE_TEMP will be empty, so we don't end up here...
	mount_temp -t tmpfs -o size=66% none
	slxlog "partition-temp" "Running /tmp on tmpfs only!" "$PARTITION_FILE"
fi

if [ "$HAVE_SWAP" = "no" ]; then
	TOTAL_RAM=$(grep ^MemTotal /proc/meminfo | awk '{print $2}')
	if [ -n "$TOTAL_RAM" ] && [ "$TOTAL_RAM" -lt "3000000" ]; then
		slxlog "partition-swap" "Have no (formatted) swap partition, using zram swap only!" "$PARTITION_FILE"
	fi
fi

[ -n "$SLX_SPLASH" ] && splashtool --icon "/opt/openslx/icons/active/??-hdd.ppm"
exit 0