summaryrefslogtreecommitdiffstats
path: root/core/rootfs/rootfs-stage32/data/opt/openslx/scripts/systemd-setup_partitions
blob: 70f36444fa336790c74f286e018a946fe0d40220 (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
#!/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 bwlehrpool@hs-offenburg.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"

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
}

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 ls -alh | grep -v -E ' \.\.?$' | grep -v '^total')
	[ -n "$OLD" ] && echo -- "Leftovers:" && echo -- "$OLD"
	cd "$PRE" || true
}

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
}

wait_for_udev () {
	local upid ctr hdd
	hdd=
	if [ "x$1" = "x--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/ )" ]
}

wait_for_udev 2

if ! has_hdd; then
	wait_for_udev --hdd 4
fi

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

# Get all partitions with given id (list of /dev/sdXX)
# Works for MBR/DOS by looking at the type (1 byte)
# and for GPT by looking for the label 'OpenSLX-ID$1'
# in case an id was given, or just at the UUID if some
# longer string was given
# The output will be a list of matching devices,
# sorted from largest to smallest
get_all_with_id () {
	local ID dev exp
	exp=
	while [ $# -gt 0 ]; do
		ID=$1
		shift
		[ -z "$ID" ] && continue
		[ ${#ID} -eq 1 ] && ID="0?$ID" # passed '7' as argument -- look for 0x7 or 0x07
		if [ ${#ID} -le 3 ]; then
			exp="$exp|ID_PART_ENTRY_(NAME=OpenSLX-ID|TYPE=0x)$ID"
		else
			exp="$exp|ID_PART_ENTRY_TYPE=$ID"
		fi
	done
	exp=${exp:1}
	#echo "Partition find is '$exp'" >&2
	for dev in $(find /dev/ -type b); do
		udevadm info --name="$dev" | grep -iqE "($exp)\$" && echo "$(blockdev --getsize64 "$dev") $dev"
	done | sort -n -k1 -r | cut -d' ' -f2
}

# Check for standard swap partitions and make them available to the system
HAVE_SWAP=no
for PART_DEV in $(get_all_with_id "82" "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f"); do
	if swapon "$PART_DEV" -p 10; 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

# We use special non assigned partition type (id44) for harddisk scratch
# space, thus no normal filesystem will be incidentally deleted or
# corrupted
HAVE_TEMP=no
for PART_DEV in $(get_all_with_id "44"); 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

# Put detected linux partitions (83) into /etc/fstab with "noauto"
for PART_DEV in $(get_all_with_id 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

# special partition 45 (persistent scratch) to $MOUNT_POINT_45
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 $(get_all_with_id "45"); 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 true; do
		[ "$COUNT" -ge 4 ] && break
		let COUNT++
		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
	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}')
	echo "$PART_DEV has ext4 fs of size $fs_size"
	if [ -n "$fs_size" ] && [ "$fs_size" -gt 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" -gt 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 ))" -lt "$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" -lt "$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
	chmod 0700 "${MOUNT_POINT_45}/slx_lost+found"
	chown 0:0 "${MOUNT_POINT_45}/slx_lost+found"
	# 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

# and 46 to /media/devXX
for PART_DEV in $(get_all_with_id "46"); do
	mkdir -p "/media/${PART_DEV#/dev/*}"
	#mount -t auto ${PART_DEV} /mnt/media/${PART_DEV#/dev/*} \n\
	#test -d /mnt/media/${PART_DEV#/dev/*}/home && \
	#  ln -sf /media/${PART_DEV#/dev/*} /var/home
	echo -e "${PART_DEV}\t/media/${PART_DEV#/dev/*}\tauto\t\tnoauto\t\t 0 0" >> "/etc/fstab"
done

# finally, prepare the data subdir on persistent part
if [ "$HAVE_PARTITION_45" = "yes" ]; then
	mkdir -p "$MOUNT_POINT_45/data"
	chown root:root "$MOUNT_POINT_45" "$MOUNT_POINT_45/data"
	chmod a+rwxt "$MOUNT_POINT_45/data"
elif [ -d "$MOUNT_POINT_45" ]; then
	rm -f -- "$MOUNT_POINT_45"
fi

mount -a

# 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
	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

exit 0