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 '$3 == "'"$_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
}
|