summaryrefslogtreecommitdiff
path: root/backend/modules/hdmap
blob: d57d1058acadfcc51acb3c1edd7010342b43089a (plain)
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
#!/bin/bash

# Synopsis: mointpoint_demands
#
# This function dumps the demands of various mountpoints
function mointpoint_demands()
{
cat <<"EOT"
/: Type:Linux
/boot: LVM:no Type:Linux
/bin: Type:Linux
/etc: Type:Linux
/home: Type:Linux
/lib: Type:Linux
/opt: Type:Linux
/root: Type:Linux
/sbin: Type:Linux
/tmp: Type:Linux
/usr: Type:Linux
/var: Type:Linux
EOT
}

# FIXME TODO OLD OUTDATED 
function emit_error()
{
send debug "$@"
cat
return 1
}
# FIXME TODO OLD OUTDATED 
function emit_progress()
{
        percent=$1
        [ "$percent" -gt 100 ] && percent=100
        send progress "$percent"
}


# Synopsis: is_disk /dev/xyz
#
# This function returns 0 if the supplied argument is a disk.
function is_disk()
{
    stat --format "%t %G" "$1" | grep -qe "^3 " -e " disk$" && return 0
    return 1
}

# Synopsis: calculate_min_space
#
# This function estimates the minimum space needed for a hdinstall
function calculate_min_space()
{
    ESTIMATED_ROOT_MIN=$(df -m "/live/filesystem" | tail -1 | gawk '{print $3}')
    grep -q squashfs /proc/mounts && ESTIMATED_ROOT_MIN=$(($ESTIMATED_ROOT_MIN*270/100))
    export ESTIMATED_ROOT_MIN
}

# Synopsis: handle_mountpoint_demands <device>
#
# This function handles the demands of the mountpoints (data from function mountpoint_demands)
#   * check if filesystem matches (Filesystem:reiserfs / Filesystem:ext3 / see get_filesystem() )
#   * check if filesystem-type matches (Type:Linux / Type:Windows / see get_filesystem_type() )
#   * check if <device> is a LVM-device (LVM:yes / LVM:no)
# TODO: from old installer, not adapted yet.
function handle_mountpoint_demands()
{
	PART="$1";
	FORMAT_FS="$2";
	POINT=$(echo $3 | cut -d":" -f1); shift 3
	while [ "$1" ];
	do
		VAR=$(echo $1 | cut -d":" -f1)
		VAL=$(echo $1 | cut -d":" -f2)
		case "$VAR" in
		Filesystem)
			if [ -n "$FORMAT_FS" -a "$FORMAT_FS" != "$VAL" ]; then
				echo "ERROR: Filesystem on $PART ($POINT) has to be formatted with $VAL!" \
				| emit_error 1
				return 1
			elif [ -z "$FORMAT_FS" -a "$(get_filesystem "$PART")" != "$VAL" ]; then
				echo "ERROR: Filesystem on $PART ($POINT) is not $VAL!" \
				| emit_error 1
				return 1
			fi
			;;
		Type)
			if [ -n "$FORMAT_FS" -a "$(get_filesystem_type -fs "$FORMAT_FS")" != "$VAL" ]; then
				echo "ERROR: Filesystem on $PART ($POINT) has to be formatted with a $VAL-Filesystem!" \
				| emit_error 1
				return 1
			elif [ -z "$FORMAT_FS" -a "$(get_filesystem_type "$PART")" != "$VAL" ]; then
				echo "ERROR: Filesystem on $PART ($POINT) is not a $VAL-Filesystem!" \
				| emit_error 1
				return 1
			fi
			;;
		LVM)
			not=not
			lvdisplay $PART &>/dev/null; A=$?
			[ "$VAL" = "yes" ] && unset not; B=$?
			((A*B)) || ! ((A-B)) || ( echo ERROR: $PART "($POINT)" must $not be on a LVM-device! | emit_error 1; return 1 ) || return 1
			;;
		esac
		shift
	done
}

# Synopsis: check_partitions_for_install
#
# This function processes the hd_map-config:
#   * make sure that the device exists
#   * take care of the mountpoint demands
#   * check if partition has the "automount"-flag
#   * check if all partitions are big enough to install the system on it
# TODO: from old installer, not adapted yet.
function check_partitions_for_install()
{
	unset found_root
	local progress_steps=$(( $(wc -l <<<"$cfg_hdmap") * 2 + 1))
	local progress=0
        progress=$[progress+1]; emit_progress $[100*progress/progress_steps]
        # compute partition_min_table
        local partition_min_table_=$ESTIMATED_ROOT_MIN
        while IFS=: read device mountpoint filesystem automount
        do
            case "$mountpoint" in
            "") continue;;
            /) continue;;
            /usr) MP_MIN=$(( $ESTIMATED_ROOT_MIN - $(du -sm --exclude /live/filesystem/usr /live/filesystem | cut -f1) ));;
            *) MP_MIN=$(du -sm "/live/filesystem$mountpoint" 2>/dev/null | cut -f1);;
            esac
            [ -z "$MP_MIN" ] && MP_MIN=0
            mp="$mountpoint"
            while mp="$(dirname "$mp")"
            do
                var_mp="$(echo "$mp" | sed 's/[^a-zA-Z0-9]/_/g')"
                parent_min="$(eval echo \$partition_min_table$var_mp)"
                if [ -n "$parent_min" ]; then
                    eval local partition_min_table$var_mp=$(( $parent_min-$MP_MIN ))
                    break
                fi
                [ "${#mp}" -gt 1 ] || break;
            done
            var_mp="$(echo "$mountpoint" | sed 's/[^a-zA-Z0-9]/_/g')"
            eval local partition_min_table$var_mp=$MP_MIN
            progress=$[progress+1]; emit_progress $[100*progress/progress_steps]
        done <<<"$cfg_hdmap"

        isosrc_dev="$(awk '{if($2 == "/isosrc"){print $1}}' /proc/mounts)"
	while IFS=: read device mountpoint filesystem automount
	do
		[ -z "$mountpoint" ] && continue;
		
		# set flag if there is a root-partition in the hd_map
		[ "$mountpoint" = / ] && found_root=1
		
                if [ "$device" = "$isosrc_dev" ]; then
                    if [ "$filesystem" ]; then
                        emit_error 1 "Partition $device ($mountpoint) is mounted to /isosrc... This partition cannot be formatted!"
                        return 1
                    fi
                fi
		
		# take care of the mountpoint demands
		if part_demands="$(mointpoint_demands | egrep "^$mountpoint:" 2>/dev/null)"; then
			[ "$automount" = "auto" ] || emit_error 1 "Partition $device ($mountpoint) doesn't have the automount-flag set!" || return 1
			eval handle_mountpoint_demands \"$device\" \"$filesystem\" $part_demands
		fi
		
		# Check if the partition is big enough to contain the installation
		var_mp="$(echo "$mountpoint" | sed 's/[^a-zA-Z0-9]/_/g')"
		mp_min="$(eval echo \$partition_min_table$var_mp)"
		partition_size_min=$[mp_min*1024*1024*115/100] # + 15% Filesystem overhead
		partition_size="$(blockdev --getsize64 $device)" # actual size of device (in bytes)
		if [ "$partition_size" -lt "$partition_size_min" ]; then
			emit_error 1 "Partition $device ($mountpoint) is too small! it is $[partition_size/1024/1024] MB big, but it should be at least $[partition_size_min/1024/1024+10] MB big"
			return 1
		fi
		progress=$[progress+1]; emit_progress $[100*progress/progress_steps]
	done <<<"$cfg_hdmap"
	
	if [ -z "$found_root" ]; then
		echo "No root-partition selected!" | emit_error 1
	fi
}

# Synopsis: umount_all_affected [-fdisk] <device>
#
# This function unlocks a device and all affected devices:
#   * it umounts mounted partitions with "umount" and disables swap-partitions with "swapoff"
#   * if "-fdisk" is set:
#     - it unlocks all partitions that are on the same device as the one given
#       (e.g. "umount_all_affected -fdisk /dev/hda3" is called, so /dev/hda* is unlocked)
#     - it takes care of LVM-Volumes and unlocks them if they are affected
#     - it takes care of dm_crypt-Disks and unlocks them if they are affected
# TODO: from old installer, not fully adapted yet.
function umount_all_affected()
{
	unset DEV NR EXHAUSITIVE
	if [ "$1" = "-fdisk" ]; then
		# partition itself and its "sister" partitions (just to be sure, for fdisk):
		shift
		#DEV="${1%%[0-9]*}"
		DEV="$(get_disk "$1")"
		NR="${1:${#disk}}"
		EXHAUSITIVE=1
	fi
	[ -z "$DEV" ] && DEV="$(echo "$1" | dereferce_links_in_list)"
	# Mounts
	while IFS=" " read mnt_dev mnt_point mnt_dummy
	do
		[ "$mnt_point" = "/live/filesystem" -o -z "$mnt_point" ] && continue
		case "$mnt_point" in
		/|/cdrom|/dev|/dev/*|/isosrc|/proc|/proc/*|/ramdisk|/sys|/tmp) ;;
		*)
			echo $mnt_dev | dereferce_links_in_list | grep -q ^$DEV$ || continue
			for umount_point in $(gawk '{print $2}' /proc/mounts | grep -e "^$mnt_point$" -e "^$mnt_point/" | sort -r)
			do
				fuser -km "$umount_point"
				umount "$umount_point" &>/dev/null || \
				umount -l "$umount_point"
			done
			;;
		esac
	done < /proc/mounts
	# Swap
	for i in $(grep -e "^$DEV" /proc/swaps  | cut -d\  -f1)
	do
		swapoff "$i";
	done
#	# LVM
#	for i in $(pvdisplay -c 2>/dev/null | grep "$DEV" | cut -d: -f2 | sort | uniq)
#	do
#		lv_name="$(lvdisplay $i | gawk '/LV Name/{print $3}')"
#		lv_dev="$(echo $lv_name | dereferce_links_in_list)"
#		for dev in $( ( echo $lv_name; echo $lv_dev ) | sort | uniq)
#		do
#			# recursive function call
#			umount_all_affected $dev
#		done
#		((EXHAUSITIVE)) && vgchange -a n $i &>/dev/null
#	done
#	# Crypttab
#	for cmapname in $(grep $DEV /etc/crypttab | cut -d\  -f1)
#	do
#		umount_all_affected "/dev/mapper/$cmapname"
#		((EXHAUSITIVE)) && cryptsetup remove $cmapname
#	done
}

# Synopsis: prepare_partitions_for_install [--nomount]
#
# This function processes the hd_map-config:
#   * create mountpoints
#   * format the partitions if a filesystem is given
#   * take care of LVM-Volumes and dm_crypt-Disks
#   * create the specified mountpoints in $TARGET
#   * mount the partitions to the target
#   * mount (bind) /dev, /proc and /sys to the target
#
# --nomount simply doesn't mount or umount partitions
# TODO: from old installer, not adapted yet.
function prepare_partitions_for_install()
{
	send install_step prepare_partitions_for_install
	emit_progress 0
        local progress_steps=$(( $(wc -l <<<"$cfg_hdmap") ))
	local progress=0
	while IFS=: read device mountpoint filesystem automount
	do	
		[ "$1" != "--nomount" ] && umount_all_affected $device
		
		# format device with filesystem (if specified)
		if [ "$filesystem" ]; then
			
			dd if=/dev/zero of=$device bs=1k count=16 >/dev/null 2>/dev/null # shutup! :-)
			
			TMP=/tmp/mkfs.$$
			case "$filesystem" in
			xfs)
				mkfs.$filesystem -f $device 2> $TMP 1>&2
				;;
			reiser*)
				echo y | mkfs.$filesystem $device 2> $TMP 1>&2
				;;
			jfs)
				echo y | mkfs.$filesystem $device 2> $TMP 1>&2
				;;
			*)
				mkfs.$filesystem $device 2> $TMP 1>&2
				;;
			esac

			RC="$?"
			if [ $RC -ne 0 ]; then
				ERROR_MESSAGES=$(tail -8 $TMP)
				echo "$ERROR_MESSAGES"
				emit_error 1 "mkfs failed" || return 1
			fi
	
			# Deactivate dir_index-feature of ext2/ext3/ext4-partitions
			case $filesystem in
				*ext*)
					tune2fs -O ^dir_index $device &>/dev/null
					;;
			esac
		fi
		
		mkdir -p ${TARGET}${mountpoint} # make sure mountpoint exists
		
		# only mount the partition if: 1. it has the automount flag
		#                              2. the mountpoint exists on the Live system
		#                          and 3. prepare_partitions_for_install was not called with "--nomount"
		if [ "$automount" = "auto" -a "$1" != "--nomount" -a -d "/live/filesystem$mountpoint" ]; then
			# mount device to mountpoint
			EC="$(LC_ALL=C mount $device ${TARGET}${mountpoint} 2>&1)"
			if (($?)); then
				case "$EC" in
				"*already mounted*") # then let's try "mount --bind"
					already_mp="$(grep ^$device /proc/mounts | cut -d\  -f2)"
					if [ -d "$already_mp" ]; then
						awk '/^\/dev/{if($4 ~ /^rw/){print $1}}' /proc/mounts  | grep -qw $device || mount -o remount,rw "$already_mp"
						mount --bind "$already_mp" ${TARGET}${mountpoint} 2>&1
					fi
					;;
				*)
					emit_error 1 "mount failed: $EC" || return 1
					;;
				esac
			fi
			if ! awk '/^\/dev/{if($4 ~ /^rw/){print $1}}' /proc/mounts | grep -qw $device; then
				emit_error 1 "mount failed: $EC" || return 1
			fi
		fi
		
		# update progress
		progress=$[progress+1]; emit_progress $[100*progress/progress_steps]
	done <<<"$cfg_hdmap"
}