summaryrefslogtreecommitdiffstats
path: root/slx-tools
blob: 1a95fa1958221e635894d960f45a5309bf3bef29 (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
#!/bin/ash

# Collection of small bash functions utilized in various scripts.
################################################################################
case "$PATH" in
	*opt/openslx*) ;;
	*) export PATH="$PATH:/opt/openslx/bin:/opt/openslx/sbin" ;;
esac

DM_STATE_FILE="/run/openslx/dmsetup.state"
readonly DM_STATE_FILE

# Return the device corresponding to the given path, or
# empty if it's not mounted from a device (tmpfs, cifs, ...)
fs_path_getdev() {
	local _dev
	_dev="$( df -P "$1" | awk '$1 ~ "^/dev/" {print $1}' )"
	[ -n "$_dev" ] || return 1
	[ -b "$_dev" ] || return 1
	printf "%s" "$_dev"
}

# Return base mount point of given path
fs_path_getmountpoint() {
	local _mp
	_mp="$( df -P "$1" | awk '$6 ~ "^/" {print $6}' )"
	[ -n "$_mp" ] || return 1
	[ -d "$_mp" ] || return 1
	printf "%s" "$_mp"
}

# Get fstype of given directory
fs_path_gettype() {
	local _mp
	_mp="$( df -P "$1" | awk '$6 ~ "^/" {print $6}' )"
	[ -n "$_mp" ] || return 1
	[ -d "$_mp" ] || return 1
	awk '$2 == "'"$_mp"'" {print $3}' '/proc/mounts'
}

# Helper to check whether given directory resides in RAM, either
# by being mounted as tmpfs or not mounted at all in which case
# we assume the same. Returns 0 if so, 1 if otherwise backed,
# 2 when errors occur.
fs_path_isvolatile() {
	[ -z "$1" ] && return 2
	local _dev _mp
	_dev="$(fs_path_getdev "$1")"
	[ -z "$_dev" ] && return 0 # No result, assume tmpfs
	[ "$_dev" = "${_dev#/dev/zram}" ] || return 0 # zram is in RAM, like tmpfs
	[ "$_dev" != "${_dev#/dev/mapper/}" ] || return 1 # Doesn't start with /dev/mapper, assume some kind of storage
	[ -s "$DM_STATE_FILE" ] || return 2 # No point in continuing
	_mp="$(fs_path_getmountpoint "$1")"
	[ -z "$_mp" ] && return 2
	# it is a device mapper device, check if it was setup in dracut's initramfs.
	grep -qE "^$( regex_escape "${_dev}" )\s+$( regex_escape "${_mp}" )\s+type=0" "$DM_STATE_FILE"
}

# Return free/total space at given path, in KiB.
# If the given path is backed by a thin pool, snapshot, etc.
# it returns a conservative estimate, considering the remaining
# space of the backing device.
# Returns "<free> <total>"
fs_path_space() {
	[ -z "$1" ] && return 1
	local _free _total _dev _type _id _name _bs _tmp _tfree _ttotal
	_type="$( fs_path_gettype "$1" )"
	[ -z "$_type" ] && return 1
	_free="$( df -P "$1" | awk '$6 ~ "^/" {print $4 " " $2}' )"
	case "$_type" in # EASY!
		tmpfs|nfs*|cifs)
		printf "%s" "$_free"
		return
	esac
	_dev="$( fs_path_getdev "$1" )"
	if [ "$_dev" = "${_dev#/dev/mapper/}" ]; then # does *NOT* start with /dev/mapper, assume not device mapped
		# In case we start using aufs again, check if it needs special handling...
		printf "%s" "$_free"
		return
	fi
	# Split, since we might need to do Min()
	_total="${_free#* }"
	_free="${_free% *}"
	_type="$( dmsetup status "$_dev" | awk '{print $3}' )"
	_tfree=0
	_ttotal=0
	case "$_type" in
		snapshot)
			# Field 4 is used/total in sectors of 512 byte, so divide by two for KiB
			_tmp="$( dmsetup status "$_dev" | awk '{split($4, a, "/"); print (a[2] - a[1]) / 2 " " a[2] / 2}' )"
			_tfree="${_tmp% *}"
			_ttotal="${_tmp#* }"
			;;
		thin)
			# Determine matching pool
			_id="$( dmsetup table "$_dev" | awk '{print $4}' )"
			if [ -n "$_id" ]; then
				_name="$( cat "/sys/dev/block/${_id}/dm/name" )"
				if [ -n "$_name" ]; then
					# Get data block size (in sectors of 512 byte)
					_bs="$( dmsetup table "$_name" | awk '{print $6}' )"
					if [ "$_bs" -gt 0 ]; then
						# Field 6 is used/total (in data blocks), so transform into KiB
						_tmp="$( dmsetup status "$_name" | awk -v "bs=$_bs" '{split($6, a, "/"); print ((a[2] - a[1]) / 2) * bs " " (a[2] / 2) * bs}' )"
						_tfree="${_tmp% *}"
						_ttotal="${_tmp#* }"
					fi
				fi
			fi
			;;
	esac
	if [ "$_tfree" -ge 0 ] && [ "$_ttotal" -gt 0 ]; then
		[ "$_tfree" -lt "$_free" ] && _free="$_tfree"
		[ "$_ttotal" -lt "$_total" ] && _total="$_ttotal"
	fi
	printf "%s" "$_free $_total"
}

# Match $1 against perl regex $2, case insensitive
regex_imatch() {
	printf "%s" "$1" | grep -qPi "$2"
}

# Escapes $1 for use in grep -E/-P, sed -r, etc.
# Pass --slash to escape '/' too. (e.g. for sed)
regex_escape() {
	if [ "x$1" = "x--slash" ]; then
		printf "%s" "$2" | sed 's,[][()\.^$?*+/],\\&,g'
	else
		printf "%s" "$1" | sed 's/[][()\.^$?*+]/\\&/g'
	fi
}

# 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 with the given UUID,
# or with the given name.
# The output will be a list of matching devices,
# sorted from largest to smallest.
dev_find_partitions() {
	local ID dev exp target
	exp=
	# target for the scan, defaults to /dev to check everything
	if [ -b "$1" ]; then
		target="$1"
		shift
	elif [ -d "$1" ]; then
		target="$1/"
	else
		target="/dev/"
	fi
	while [ "$#" -gt 0 ]; do
		ID="$1"
		shift
		[ -z "$ID" ] && continue
		# if single digit, e.g. 7, look for 0x7 and 0x07
		if regex_imatch "$ID" "^[0-9a-f]$"; then
			ID="0?$ID"
		fi

		if regex_imatch "$ID" "^[0-9a-f?]{2,3}$"; then # Match two digit and the expanded three digit version from above
			# if double digit look for MBR types and OpenSLX-ID$ID GPT labels
			exp="$exp|ID_PART_ENTRY_(NAME=OpenSLX-ID|TYPE=0x)$ID"
		elif regex_imatch "$ID" "^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$"; then
			# if UUID, look for TYPE
			exp="$exp|ID_PART_ENTRY_TYPE=$ID"
		else
			# something else, look for names of partitions / filesystems
			ID="$( regex_escape "$ID" )"
			exp="$exp|ID_(PART_ENTRY_NAME|FS_LABEL)=$ID"
		fi
	done
	exp="${exp:1}"
	for dev in $(find $target* -type b); do
		udevadm info --name="$dev" | grep -iqE "($exp)\$" \
			&& printf "%s\n" "$(blockdev --getsize64 "$dev") $dev"
	done | sort -n -k1 -r | cut -d' ' -f2
}