OSDN Git Service

Fixed the wrong implementation of show errormessage.
[portsreinstall/current.git] / lib / libfs.sh
1 #!/bin/sh -e
2 # ==============================================================================
3 # portsreinstall library script
4 # - File system operations -
5 # Copyright (C) 2018 Mamoru Sakaue, MwGhennndo, All Rights Reserved.
6 # This software is distributed under the 2-Clause BSD License.
7 # ==============================================================================
8
9 # ============= Get the global path of a possibly not yet created file/directory =============
10 fs_global_path ()
11 {
12         local $path_src
13         path_src=$1
14         if [ -e "$path_src" ]
15         then
16                 realpath "$path_src"
17         else
18                 expr "$path_src" : '^/' > /dev/null || echo -n `realpath .`
19                 echo "$path_src"
20         fi
21 }
22
23 # ============= Inspect the privilege of the current environment on file system operation =============
24 fs_inspect_fs_privilege ()
25 {
26         local tgv mp mp_regexp mount_privilege basedir
27         [ -d "${TMPDIR}"/fs_privilege ] && return
28         tgv=${TMPDIR}/fs_privilege/test_tg
29         mkdir -p "$tgv" "${TMPDIR}"/fs_privilege/test_mp
30         mp=`realpath "${TMPDIR}"/fs_privilege/test_mp`
31         mp_regexp=`str_escape_regexp "$mp"`
32         echo yes > ${TMPDIR}/fs_privilege/fs_privilege
33         cat > ${TMPDIR}/fs_privilege/fslist << eof
34 devfs   devfs
35 fdescfs null
36 procfs  proc
37 tmpfs   tmpfs
38 nullfs  "$tgv"
39 unionfs "$tgv"
40 eof
41         while read fs tg
42         do
43                 if mount -t $fs "$tgv" "$mp" 2> /dev/null && umount "$mp"
44                 then
45                         echo yes
46                 else
47                         echo no
48                         echo no > ${TMPDIR}/fs_privilege/fs_privilege
49                 fi > ${TMPDIR}/fs_privilege/fs_privilege:$fs 2> /dev/null
50                 umount -f "$mp" 2> /dev/null || :
51                 [ -e "${TMPDIR}"/fs_privilege/basedir ] && continue
52                 mount -t $fs "$tgv" "$mp" 2> /dev/null && \
53                         df "$mp" > ${TMPDIR}/fs_privilege/df:$fs && \
54                         umount "$mp"
55                 real_mp=`sed 1d "${TMPDIR}"/fs_privilege/df:$fs | tail -n 1 | \
56                         sed -E 's/^.*[[:space:]][0-9]+%[[:space:]]+//'`
57                 echo "$real_mp" | sed -E "s/$mp_regexp$//" > ${TMPDIR}/fs_privilege/basedir
58         done < ${TMPDIR}/fs_privilege/fslist
59         mount_privilege=`cat "${TMPDIR}"/fs_privilege/fs_privilege`
60         if [ "x$mount_privilege" = xyes ]
61         then
62                 mount -t nullfs /bin "$mp" 2> /dev/null
63                 if [ `ls "$mp" 2> /dev/null | wc -l` -gt 0 ]
64                 then
65                         echo yes
66                 else
67                         echo no
68                 fi > ${TMPDIR}/fs_privilege/nullfs_target_recognition
69                 umount -f "$mp" 2> /dev/null || :
70                 nullfs_target_recognition=`cat "${TMPDIR}"/fs_privilege/nullfs_target_recognition`
71                 if [ "x$nullfs_target_recognition" = xyes ]
72                 then
73                         message_echo "INFO: The current environment has the full required privilege of mounting/unmounting file systems."
74                 else
75                         message_echo "INFO: The current environment formally has the full required privilege of mounting/unmounting file systems but the recognition of nullfs/unionfs targets is incorrect."
76                 fi
77         else
78                 echo no > ${TMPDIR}/fs_privilege/nullfs_target_recognition
79                 message_echo "INFO: The current environment does not have the privilege of mounting/unmounting for the following file system(s)."
80                 while read fs tg
81                 do
82                          mount_privilege=`cat "${TMPDIR}"/fs_privilege/fs_privilege:$fs`
83                          [ "x$mount_privilege" = xyes ] || echo '  '$fs
84                 done < ${TMPDIR}/fs_privilege/fslist | message_cat
85         fi
86         basedir=`cat "${TMPDIR}"/fs_privilege/basedir 2> /dev/null || :`
87         if [ -n "$basedir" ]
88         then
89                 message_echo "INFO: The current environment will be a chroot/jail guest whose base directory is \"$basedir\"."
90         fi
91         if [ "x$opt_invalidate_mount_privilege" = xyes ]
92         then
93                 message_echo "INFO: The privilege of mounting/unmounting in this environment is forcibly invalidated."
94         fi
95 }
96
97 # ============= Check whether mounting file systems are available at the current environment =============
98 fs_chk_mount_privilege ()
99 {
100         local mount_privilege
101         fs_inspect_fs_privilege
102         [ "x$opt_invalidate_mount_privilege" = xno ] || return
103         mount_privilege=`cat "${TMPDIR}"/fs_privilege/fs_privilege`
104         nullfs_target_recognition=`cat "${TMPDIR}"/fs_privilege/nullfs_target_recognition`
105         [ "x$nullfs_target_recognition" = xyes -a "x$mount_privilege" = xyes ]
106 }
107
108 # ============= Check whether mounting file systems are available at the current environment =============
109 fs_chk_unmount_privilege ()
110 {
111         local mount_privilege
112         fs_inspect_fs_privilege
113         [ "x$opt_invalidate_mount_privilege" = xno ] || return
114         mount_privilege=`cat "${TMPDIR}"/fs_privilege/fs_privilege`
115         [ "x$mount_privilege" = xyes ]
116 }
117
118 # ============= Get the base directory the current environment (applicable in case of a chroot guest) =============
119 fs_get_system_basedir ()
120 {
121         fs_inspect_fs_privilege
122         cat "${TMPDIR}"/fs_privilege/basedir 2> /dev/null || :
123 }
124
125 # ============= Get the regular expression pattern of the actual mount point =============
126 fs_get_actual_mount_point_pattern ()
127 {
128         local mountpoint basedir mountpoint_real
129         mountpoint=$1
130         fs_inspect_fs_privilege
131         basedir=`cat "${TMPDIR}"/fs_privilege/basedir 2> /dev/null || :`
132         if [ -e  "$mountpoint" ]
133         then
134                 mountpoint_real=`realpath "$mountpoint"`
135         else
136                 curdir=$mountpoint
137                 curnode=$mountpoint
138                 relative=
139                 while { curbase=`basename "$curdir"`; curdir=`dirname "$curdir"`; [ -n "$curdir" ]; }
140                 do
141                         relative=$curbase/$relative
142                         if [ -e  "$curdir" ]
143                         then
144                                 mountpoint_real=`realpath "$curdir"`/$relative
145                                 break
146                         fi
147                 done
148         fi
149         mountpoint_real_full=`str_regularize_df_path "$basedir/$mountpoint_real"`
150         str_escape_regexp "$mountpoint_real_full"
151 }
152
153 # ============= Get mount info at the descendant directory levels required for builder chroot environment =============
154 fs_get_descendant_mount_info ()
155 {
156         local mountpoint mountpoint_real_regexp basedir
157         mountpoint=$1
158         mountpoint_real_regexp=`fs_get_actual_mount_point_pattern "$mountpoint"` || return
159         basedir=`cat "${TMPDIR}"/fs_privilege/basedir 2> /dev/null || :`
160         basedir_ptn=`str_escape_regexp "$basedir"`
161         df | sed 1d | grep -E "%[[:space:]]+$mountpoint_real_regexp\/" | while read fs data
162         do
163                 echo "$fs" | grep -q -e '^/' -e '^<above>:' && fs=normal
164                 mp_abs=`echo "$data" | sed -E  's|.*%[[:space:]]+(/.+)$|\1|'`
165                 mp=`echo "$mp_abs" | sed -E "s|^$basedir_ptn||"`
166                 relative=`echo "$mp_abs" | sed -E "s|^$mountpoint_real_regexp||"`
167                 printf '%s\t%s\t%s\n' "$fs" "$mp" "$relative"
168         done
169 }
170
171 # ============= Repair the unionfs image if applicable and hidden =============
172 # Use the side effect of find(1) as a medicine for disappearance of lower layer images of unionfs.
173 # This treatment, however, can lose its effect when the symptom gets serious.
174 # When the effect is lost, "umount -A" after "shutdown now" may be the only practical solution.
175 fs_fix_unionfs_image_if_hidden ()
176 {
177         local needlepath needlepath_cur needlepath_next needlepath_lowest_exist
178         needlepath=$1
179         needlepath_cur=$needlepath
180         needlepath_lowest_exist=
181         while :
182         do
183                 [ -z "$needlepath_lowest_exist" -a -e "$needlepath_cur" ] && needlepath_lowest_exist=$needlepath_cur
184                 find -dx "$needlepath_cur" -maxdepth 0 > /dev/null 2>&1 || :
185                 needlepath_next=`dirname "$needlepath_cur"`
186                 [ "x$needlepath_cur" = "x$needlepath_next" ] && break
187                 needlepath_cur=$needlepath_next
188         done
189         [ -e "$needlepath" ] && return
190         if [ -n "$needlepath_lowest_exist" ] && df "$needlepath_lowest_exist" | sed 1d | grep -q '^<above>:'
191         then
192                 message_echo "ERROR: Failed to recover a lost mandatory file, probably due to the bug of unionfs: $needlepath" >&2
193                 mkdir -p "${DBDIR}/execflag"
194                 touch "${DBDIR}/execflag/unionfs_error"
195         else
196                 message_echo "ERROR: Failed to recover a lost mandatory file due to unknown reasons: $needlepath" >&2
197         fi
198         exit 1
199 }
200
201 # ============= Check whether a directory is mounted properly =============
202 fs_chk_mounted ()
203 {
204         local type target mountpoint target_ptn mountpoint_real_regexp tmpsrc
205         type=$1
206         target=`str_regularize_df_path "$2"`
207         mountpoint=`str_regularize_df_path "$3"`
208         target_ptn=`str_escape_regexp "$target"`
209         mountpoint_real_regexp=`fs_get_actual_mount_point_pattern "$mountpoint"` || return
210         basedir=`cat "${TMPDIR}"/fs_privilege/basedir 2> /dev/null || :`
211         basedir_target_ptn=`str_regularize_df_path "$basedir/$target" | str_escape_regexp_filter`
212         tmpsrc=`df | sed 1d | grep -E "%[[:space:]]+$mountpoint_real_regexp$"`
213         case $type in
214                 nullfs )
215                         echo "$tmpsrc" | grep -qE "^${target_ptn}[[:space:]]" && return
216                         echo "$tmpsrc" | grep -qE "^${basedir_target_ptn}[[:space:]]" && return
217                         ;;
218                 unionfs )
219                         echo "$tmpsrc" | grep -qE "^<above>:${target_ptn}[[:space:]]" && return
220                         echo "$tmpsrc" | grep -qE "^<above>:${basedir_target_ptn}[[:space:]]" && return
221                         ;;
222                 devfs | fdescfs | procfs | linprocfs | tmpfs )
223                         echo "$tmpsrc" | grep -q "^$type" && return
224                         ;;
225                 *)
226                         message_echo "ERROR: Unsupported fyle system [$type]" >&2
227                         exit 1
228                         ;;
229         esac
230         return 1
231 }
232