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 # ==============================================================================
9 # ============= Get the global path of a possibly not yet created file/directory =============
18 expr "$path_src" : '^/' > /dev/null || echo -n `realpath .`
23 # ============= Inspect the privilege of the current environment on file system operation =============
24 fs_inspect_fs_privilege ()
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
43 if mount -t $fs "$tgv" "$mp" 2> /dev/null && umount "$mp"
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 && \
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 ]
62 mount -t nullfs /bin "$mp" 2> /dev/null
63 if [ `ls "$mp" 2> /dev/null | wc -l` -gt 0 ]
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 ]
73 message_echo "INFO: The current environment has the full required privilege of mounting/unmounting file systems."
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."
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)."
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
86 basedir=`cat "${TMPDIR}"/fs_privilege/basedir 2> /dev/null || :`
89 message_echo "INFO: The current environment will be a chroot/jail guest whose base directory is \"$basedir\"."
91 if [ "x$opt_invalidate_mount_privilege" = xyes ]
93 message_echo "INFO: The privilege of mounting/unmounting in this environment is forcibly invalidated."
97 # ============= Check whether mounting file systems are available at the current environment =============
98 fs_chk_mount_privilege ()
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 ]
108 # ============= Check whether mounting file systems are available at the current environment =============
109 fs_chk_unmount_privilege ()
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 ]
118 # ============= Get the base directory the current environment (applicable in case of a chroot guest) =============
119 fs_get_system_basedir ()
121 fs_inspect_fs_privilege
122 cat "${TMPDIR}"/fs_privilege/basedir 2> /dev/null || :
125 # ============= Get the regular expression pattern of the actual mount point =============
126 fs_get_actual_mount_point_pattern ()
128 local mountpoint basedir mountpoint_real
130 fs_inspect_fs_privilege
131 basedir=`cat "${TMPDIR}"/fs_privilege/basedir 2> /dev/null || :`
132 if [ -e "$mountpoint" ]
134 mountpoint_real=`realpath "$mountpoint"`
139 while { curbase=`basename "$curdir"`; curdir=`dirname "$curdir"`; [ -n "$curdir" ]; }
141 relative=$curbase/$relative
144 mountpoint_real=`realpath "$curdir"`/$relative
149 mountpoint_real_full=`str_regularize_df_path "$basedir/$mountpoint_real"`
150 str_escape_regexp "$mountpoint_real_full"
153 # ============= Get mount info at the descendant directory levels required for builder chroot environment =============
154 fs_get_descendant_mount_info ()
156 local mountpoint mountpoint_real_regexp basedir
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
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"
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 ()
177 local needlepath needlepath_cur needlepath_next needlepath_lowest_exist
179 needlepath_cur=$needlepath
180 needlepath_lowest_exist=
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
189 [ -e "$needlepath" ] && return
190 if [ -n "$needlepath_lowest_exist" ] && df "$needlepath_lowest_exist" | sed 1d | grep -q '^<above>:'
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"
196 message_echo "ERROR: Failed to recover a lost mandatory file due to unknown reasons: $needlepath" >&2
201 # ============= Check whether a directory is mounted properly =============
204 local type target mountpoint target_ptn mountpoint_real_regexp tmpsrc
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$"`
215 echo "$tmpsrc" | grep -qE "^${target_ptn}[[:space:]]" && return
216 echo "$tmpsrc" | grep -qE "^${basedir_target_ptn}[[:space:]]" && return
219 echo "$tmpsrc" | grep -qE "^<above>:${target_ptn}[[:space:]]" && return
220 echo "$tmpsrc" | grep -qE "^<above>:${basedir_target_ptn}[[:space:]]" && return
222 devfs | fdescfs | procfs | linprocfs | tmpfs )
223 echo "$tmpsrc" | grep -q "^$type" && return
226 message_echo "ERROR: Unsupported fyle system [$type]" >&2