summaryrefslogtreecommitdiffstats
path: root/core/modules/disk-partitions/data/opt/openslx/scripts/systemd-setup_partitions
blob: 9ca3ac789fc21a46920036f7ca1cb7e70a0145c8 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
#!/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