linux-boot-probe: mounted/40grub2 add tests for handling escaped $ " \
[os-prober.git] / common.sh
1 newns () {
2   [ "$OS_PROBER_NEWNS" ] || exec /usr/lib/os-prober/newns "$0" "$@"
3 }
4
5 cleanup_tmpdir=false
6 cleanup () {
7   if $cleanup_tmpdir; then
8     rm -rf "$OS_PROBER_TMP"
9   fi
10 }
11
12 require_tmpdir() {
13   if [ -z "$OS_PROBER_TMP" ]; then
14     if type mktemp >/dev/null 2>&1; then
15       export OS_PROBER_TMP="$(mktemp -d /tmp/os-prober.XXXXXX)"
16       cleanup_tmpdir=:
17       trap cleanup EXIT HUP INT QUIT TERM
18     else
19       export OS_PROBER_TMP=/tmp
20     fi
21   fi
22 }
23
24 count_for() {
25   _labelprefix="$1"
26   _result=$(grep "^${_labelprefix} " /var/lib/os-prober/labels 2>/dev/null || true)
27
28   if [ -z "$_result" ]; then
29     return
30   else
31     echo "$_result" | cut -d' ' -f2
32   fi
33 }
34
35 count_next_label() {
36   require_tmpdir
37
38   _labelprefix="$1"
39   _cfor="$(count_for "${_labelprefix}")"
40
41   if [ -z "$_cfor" ]; then
42     echo "${_labelprefix} 1" >> /var/lib/os-prober/labels
43   else
44     sed "s/^${_labelprefix} ${_cfor}/${_labelprefix} $(($_cfor + 1))/" /var/lib/os-prober/labels > "$OS_PROBER_TMP/os-prober.tmp"
45     mv "$OS_PROBER_TMP/os-prober.tmp" /var/lib/os-prober/labels
46   fi
47   
48   echo "${_labelprefix}${_cfor}"
49 }
50
51 progname=
52 cache_progname() {
53   case $progname in
54     '')
55       progname="${0##*/}"
56       ;;
57   esac
58 }
59
60 log() {
61   cache_progname
62   logger -t "$progname" "$@"
63 }
64
65 error() {
66   log "error: $@"
67 }
68
69 warn() {
70   log "warning: $@"
71 }
72
73 debug() {
74   if [ -z "$OS_PROBER_DISABLE_DEBUG" ]; then
75     log "debug: $@"
76   fi
77 }
78
79 result () {
80   log "result:" "$@"
81   echo "$@"
82 }
83
84 # shim to make it easier to use os-prober outside d-i
85 if ! type mapdevfs >/dev/null 2>&1; then
86   mapdevfs () {
87     readlink -f "$1"
88   }
89 fi
90
91 item_in_dir () {
92         if [ "$1" = "-q" ]; then
93                 q="-q"
94                 shift 1
95         else
96                 q=""
97         fi
98         [ -d "$2" ] || return 1
99         # find files with any case
100         ls -1 "$2" | grep $q -i "^$1$"
101 }
102
103 # We can't always tell the filesystem type up front, but if we have the
104 # information then we should use it. Note that we can't use block-attr here
105 # as it's only available in udebs.
106 # If not detected after different attempts then "NOT-DETECTED" will be printed
107 # because function is not supposed to exit error codes.
108 fs_type () {
109         local fstype=""
110         if (export PATH="/lib/udev:$PATH"; type vol_id) >/dev/null 2>&1; then
111                 PATH="/lib/udev:$PATH" \
112                         fstype=$(vol_id --type "$1" 2>/dev/null || true)
113                 [ -z "$fstype" ] || { echo "$fstype"; return; }
114         fi
115         if type lsblk >/dev/null 2>&1 ; then
116                 fstype=$(lsblk --nodeps --noheading --output FSTYPE -- "$1" || true)
117                 [ -z "$fstype" ] || { echo "$fstype"; return; }
118         fi
119         if type blkid >/dev/null 2>&1; then
120                 fstype=$(blkid -o value -s TYPE "$1" 2>/dev/null || true)
121                 [ -z "$fstype" ] || { echo "$fstype"; return; }
122         fi
123         echo "NOT-DETECTED"
124 }
125
126 is_dos_extended_partition() {
127         if type blkid >/dev/null 2>&1; then
128                 local output
129
130                 output="$(blkid -o export $1)"
131
132                 # old blkid (util-linux << 2.24) errors out on extended p.
133                 if [ "$?" = "2" ]; then
134                         return 0
135                 fi
136
137                 # dos partition type and no filesystem type?...
138                 if echo $output | grep -q ' PTTYPE=dos ' &&
139                                 ! echo $output | grep -q ' TYPE='; then
140                         return 0
141                 else
142                         return 1
143                 fi
144         fi
145
146         return 1
147 }
148
149 parse_proc_mounts () {
150         while read -r line; do
151                 set -f
152                 set -- $line
153                 set +f
154                 printf '%s %s %s\n' "$(mapdevfs "$1")" "$2" "$3"
155         done
156 }
157
158 parsefstab () {
159         while read -r line; do
160                 case "$line" in
161                         "#"*)
162                                 :       
163                         ;;
164                         *)
165                                 set -f
166                                 set -- $line
167                                 set +f
168                                 printf '%s %s %s\n' "$1" "$2" "$3"
169                         ;;
170                 esac
171         done
172 }
173
174 unescape_mount () {
175         printf %s "$1" | \
176                 sed 's/\\011/   /g; s/\\012/\n/g; s/\\040/ /g; s/\\134/\\/g'
177 }
178
179 find_label () {
180         local output
181         if type blkid >/dev/null 2>&1; then
182                 # Hopefully everyone has blkid by now
183                 output="$(blkid -o device -t LABEL="$1")" || return 1
184                 echo "$output" | head -n1
185         elif [ -h "/dev/disk/by-label/$1" ]; then
186                 # Last-ditch fallback
187                 readlink -f "/dev/disk/by-label/$1"
188         else
189                 return 1
190         fi
191 }
192
193 find_uuid () {
194         local output
195         if type blkid >/dev/null 2>&1; then
196                 # Hopefully everyone has blkid by now
197                 output="$(blkid -o device -t UUID="$1")" || return 1
198                 echo "$output" | head -n1
199         elif [ -h "/dev/disk/by-uuid/$1" ]; then
200                 # Last-ditch fallback
201                 readlink -f "/dev/disk/by-uuid/$1"
202         else
203                 return 1
204         fi
205 }
206
207 # Sets $mountboot as output variables.  This is very messy, but POSIX shell
208 # isn't really up to the task of doing it more cleanly.
209 linux_mount_boot () {
210         partition="$1"
211         tmpmnt="$2"
212
213         bootpart=""
214         mounted=""
215         if [ -e "$tmpmnt/etc/fstab" ]; then
216                 # Try to mount any /boot partition.
217                 bootmnt=$(parsefstab < "$tmpmnt/etc/fstab" | grep " /boot ") || true
218                 if [ -n "$bootmnt" ]; then
219                         set -f
220                         set -- $bootmnt
221                         set +f
222                         boottomnt=""
223
224                         # Try to map labels and UUIDs ourselves if possible,
225                         # so that we can check whether they're already
226                         # mounted somewhere else.
227                         tmppart="$1"
228                         if echo "$1" | grep -q "LABEL="; then
229                                 label="$(echo "$1" | cut -d = -f 2)"
230                                 if tmppart="$(find_label "$label")"; then
231                                         debug "mapped LABEL=$label to $tmppart"
232                                 else
233                                         debug "found boot partition LABEL=$label for Linux system on $partition, but cannot map to existing device"
234                                         mountboot="$partition 0"
235                                         return
236                                 fi
237                         elif echo "$1" | grep -q "UUID="; then
238                                 uuid="$(echo "$1" | cut -d = -f 2)"
239                                 if tmppart="$(find_uuid "$uuid")"; then
240                                         debug "mapped UUID=$uuid to $tmppart"
241                                 else
242                                         debug "found boot partition UUID=$uuid for Linux system on $partition, but cannot map to existing device"
243                                         mountboot="$partition 0"
244                                         return
245                                 fi
246                         fi
247                         shift
248                         set -- "$(mapdevfs "$tmppart")" "$@"
249
250                         if grep -q "^$1 " "$OS_PROBER_TMP/mounted-map"; then
251                                 bindfrom="$(grep "^$1 " "$OS_PROBER_TMP/mounted-map" | head -n1 | cut -d " " -f 2)"
252                                 bindfrom="$(unescape_mount "$bindfrom")"
253                                 if [ "$bindfrom" != "$tmpmnt/boot" ]; then
254                                         if mount --bind "$bindfrom" "$tmpmnt/boot"; then
255                                                 mounted=1
256                                                 bootpart="$1"
257                                         else
258                                                 debug "failed to bind-mount $bindfrom onto $tmpmnt/boot"
259                                         fi
260                                 fi
261                         fi
262                         if [ "$mounted" ]; then
263                                 :
264                         elif [ -e "$1" ]; then
265                                 bootpart="$1"
266                                 boottomnt="$1"
267                         elif [ -e "$tmpmnt/$1" ]; then
268                                 bootpart="$1"
269                                 boottomnt="$tmpmnt/$1"
270                         elif [ -e "/target/$1" ]; then
271                                 bootpart="$1"
272                                 boottomnt="/target/$1"
273                         else
274                                 bootpart=""
275                         fi
276
277                         if [ ! "$mounted" ]; then
278                                 if [ -z "$bootpart" ]; then
279                                         debug "found boot partition $1 for linux system on $partition, but cannot map to existing device"
280                                 else
281                                         debug "found boot partition $bootpart for linux system on $partition"
282                                         if type grub-mount >/dev/null 2>&1 && \
283                                            grub-mount "$boottomnt" "$tmpmnt/boot" 2>/dev/null; then
284                                                 mounted=1
285                                         fi
286                                 fi
287                         fi
288                 fi
289         fi
290         if [ -z "$bootpart" ]; then
291                 bootpart="$partition"
292         fi
293         if [ -z "$mounted" ]; then
294                 mounted=0
295         fi
296
297         mountboot="$bootpart $mounted"
298 }