This file is indexed.

/lib/bilibop/lockfs_mount_helper is in bilibop-lockfs 0.5.4.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
#!/bin/sh
set -e

# /lib/bilibop/lockfs_mount_helper {{{
# Mount helper script for 'lockfs' filesystem type entries in /etc/fstab.
# This script cannot be run manually. The expected way to run it is the
# following:
# 1. Enable bilibop-lockfs:
#    * set BILIBOP_LOCKFS to "true" in /etc/bilibop/bilibop.conf and reboot, or
#    * reboot and append 'lockfs' parameter in the boot commandline
# 2. Once '/' is an union filesystem (aufs or overlay mountpoint), the
#    temporary /etc/fstab is modified to replace filesystem types (third field)
#    of some entries by 'lockfs' (options are modified too to remember the
#    original fstype).
# 3. /sbin/mount.lockfs is created if it doesn't already exist. This can be a
#    symlink to /lib/bilibop/lockfs_mount_helper if this helper is executable,
#    or a copy of the helper (followed by chmod +x) if the helper is not
#    executable. If the helper is missing, /sbin/mount.lockfs will be a
#    poor fallback to call mount normally.
# 4. /etc/fstab is parsed by 'mount -a', and then mount calls mount.lockfs
#    with the proper arguments when a 'lockfs' fstype is encountered.
# }}}

PATH="/sbin:/bin"

usage() {
    cat <<EOF
${0##*/}: mount helper script for bilibop-lockfs.
This script can not be run manually, but only by a mount process,
and only if bilibop-lockfs is enabled.
EOF
}

# mount_fallback() =========================================================={{{
# What we want is: mount a device on its original mountpoint and rewrite the
# fstab entry to keep it consistent. This function should be called in case of
# error or if the device is whitelisted. This function parses the arguments of
# the script itself (i.e. mount_fallback "$@").
mount_fallback() {
    ${DEBUG} && echo "> mount_fallback $@" >&2
    local opt options= fstype=
    for opt in $(IFS=','; echo ${4}); do
        case "${opt}" in
            fstype=*)
                eval "${opt}"
                ;;
            *)
                options="${options:+${options},}${opt}"
                ;;
        esac
    done
    sed -i "s;^\s*\([^#][^ ]\+\s\+${2}\s\+\)lockfs\s.*;\1${fstype:-auto} ${options:-defaults} 0 0;" /etc/fstab
    mount ${flags} ${1} ${2} ${fstype:+-t ${fstype}} ${options:+-o ${options}}
}
# ===========================================================================}}}

# Works only if the parent process is /bin/mount:
if [ "$(readlink -f /proc/${PPID}/exe)" != "/bin/mount" ]; then
    usage >&2
    exit 3
fi

. /lib/bilibop/common.sh
get_bilibop_variables
get_udev_root

# Works only if the root filesystem is already managed by bilibop-lockfs:
if is_aufs_mountpoint -q / && [ -f "${BILIBOP_RUNDIR}/lock" ]; then
    LOCKFS="true"
    METHOD="aufs"
    ROBR="$(aufs_readonly_branch /)"
    RWBR="$(aufs_writable_branch /)"
elif is_overlay_mountpoint -q / && [ -f "${BILIBOP_RUNDIR}/lock" ]; then
    LOCKFS="true"
    METHOD="overlay"
    ROBR="$(overlay_lowerdir /)"
    RWBR="$(overlay_upperdir /)"
else
    echo "${0##*/}: bilibop-lockfs is disabled." >&2
    exit 1
fi

# Some configurations can have been overridden from the boot commandline:
for param in $(cat /proc/cmdline); do
    case "${param}" in
        lockfs=*)
            for policy in $(IFS=','; echo ${param#lockfs=}); do
                case "${policy}" in
                    default)
                        BILIBOP_LOCKFS_POLICY=""
                        BILIBOP_LOCKFS_WHITELIST=""
                        BILIBOP_LOCKFS_SIZE=""
                        ;;
                    hard|soft)
                        BILIBOP_LOCKFS_POLICY="${policy}"
                        ;;
                    all)
                        BILIBOP_LOCKFS_WHITELIST=""
                        ;;
                    -/*)
                        BILIBOP_LOCKFS_WHITELIST="${BILIBOP_LOCKFS_WHITELIST:+${BILIBOP_LOCKFS_WHITELIST} }${policy#-}"
                        ;;
                esac
            done
            ;;
    esac
done

# But if there is a physical lock, it takes precedence:
if [ -f "${BILIBOP_RUNDIR}/plocked" ]; then
    . ${BILIBOP_RUNDIR}/plocked
fi

# the mount(8) command, after parsing commandline arguments and/or /etc/fstab,
# always provides arguments to the helper scripts in this order:
# FILESYSTEM MOUNTPOINT [FLAGS] -o MOUNTOPTIONS
# where FLAGS are generic, not filesystem specific: -n, -s, -v for example; -r
# (or --read-only) and -w (or --rw or --read-write) are translated to -o ro and
# -o rw respectively by the mount command itself.

while [ "${1}" ]; do
    case "${1}" in
        -o)
            MNTARGS="${MNTARGS:+${MNTARGS} }${1} ${2}"
            shift 2
            ;;
        -*)
            # Do not skip other options (-n, -s, -v), but take them
            # apart: we will reuse them for each mount invocation.
            flags="${flags:+${flags} }${1}"
            shift
            ;;
        *)
            MNTARGS="${MNTARGS:+${MNTARGS} }${1}"
            shift
            ;;
    esac
done

# Reinitialize script arguments
eval set -- "${MNTARGS}"

if [ -b "${1}" ]; then
    device="${1}"
    # Check if this device is whitelisted:
    if [ -n "${BILIBOP_LOCKFS_WHITELIST}" ]; then
        # Query ID_FS_* udev environment variables of the device:
        eval $(query_udev_envvar $(readlink -f ${device}))
        if [ -z "${ID_FS_USAGE}" ]; then
            eval $(blkid -o udev -p ${device})
        fi
        [ "${ID_FS_USAGE}" = "filesystem" -o "${ID_FS_USAGE}" = "crypto" ] &&
        for skip in ${BILIBOP_LOCKFS_WHITELIST}; do
            case "${skip}" in
                UUID=${ID_FS_UUID}|LABEL=${ID_FS_LABEL}|TYPE=${ID_FS_TYPE})
                    LOCKFS="false"
                    break
                    ;;
            esac
        done
    fi

elif [ -f "${1}" ]; then
    lofile="${1}"
    LOFILE="${ROBR}${lofile}"

else
    # There is no block device to mount (here 'block device' includes
    # files associated to a loop device). Bind mounts and remote fs
    # should have been discarded by the bilibop-lockfs script in the
    # initramfs...
    LOCKFS="false"
fi

# If bilibop-lockfs is not enabled (the device is whitelisted, or we don't
# know how to manage it), rewrite the fstab entry and do a normal mount:
if [ "${LOCKFS}" != "true" ]; then
    mount_fallback "${@}"
    exit $?
fi

mntpnt="${2}"
options="${4}"

# Parse mount options. Two cases:
# 1. the block device will be mounted with the same options than in the
#    original fstab entry, plus 'ro'.
# 2. the tmpfs will be mounted with only some options of the previous:
#    ro, nodev, noexec, nosuid, if they exist.

for opt in $(IFS=','; echo ${options}); do
    # 1. Options for the readonly branch:
    case "${opt}" in
        fstype=*)
            eval "${opt}"
            ;;
        rw)
            ;;
        *)
            robr_opts="${robr_opts:+${robr_opts},}${opt}"
            ;;
    esac

    # 2. Options for the writable branch:
    case "${opt}" in
        ro|nodev|noexec|nosuid)
            rwbr_opts="${rwbr_opts:+${rwbr_opts},}${opt}"
            ;;
        *)
            ;;
    esac
done

# For aufs:
# Several schemes are available (there was only one until bilibop 0.4.23).
# 1. nested
# Each readonly branch is mounted under the subtree of the main readonly
# branch (/aufs/ro) and each writable branch is mounted under the subtree
# of the main writable branch (/aufs/rw). For examples:
# <readonly branch>  + <writable branch>  -> <aufs mount>
# /aufs/ro           + /aufs/rw           -> /
# /aufs/ro/boot      + /aufs/rw/boot      -> /boot
# /aufs/ro/usr/local + /aufs/rw/usr/local -> /usr/local
# 2. isolated
# Each readonly branch is mounted beside its writable branch in a dedicated
# subdirectory referring to the union fs mount point. For examples:
# <readonly branch>  + <writable branch>  -> <aufs mount>
# /aufs/ro           + /aufs/rw           -> /
# /aufs/boot/ro      + /aufs/boot/rw      -> /boot
# /aufs/usr/local/ro + /aufs/usr/local/rw -> /usr/local
# 3. hybrid
#
# For overlay:
# A dedicated temporary filesystem (tmpfs) is set for the first overlay mount
# point, and readonly and writable branches (and also workdir) are
# subdirectories of the tmpfs mountpoint. Next mounts (done by this script)
# may use the same *isolated* scheme or a hybrid one. Nested scheme is not
# possible. For examples (where /overlay and /overlay/boot are the tmpfs
# mountpoints):
# 1. isolated
# <lowerdir>       + <upperdir>       + <workdir>         -> <overlay>
# /overlay/ro      + /overlay/rw      + /overlay/.rw      -> /
# /overlay/boot/ro + /overlay/boot/rw + /overlay/boot/.rw -> /boot
# 2. hybrid
# <lowerdir>       + <upperdir>       + <workdir>         -> <overlay>
# /overlay/ro      + /overlay/rw      + /overlay/.rw      -> /
# /overlay/ro/boot + /overlay/boot/rw + /overlay/boot/.rw -> /boot
#
# In any non-isolated scheme, we will create symlinks to make that
# ${UNION}${MNTPNT}/r{o,w} always exist.
#
# /aufs/usr/local/ro -> ../../ro/usr/local
# /aufs/usr/local/rw -> ../../rw/usr/local
# or
# /overlay/boot/ro -> ../ro/boot
# /overlay/rw/boot -> ../boot/rw

if [ "${METHOD}" = "aufs" ]; then
    case "${BILIBOP_LOCKFS_PATH_SCHEME}" in
        isolated)
            # Use clearly separated mountpoints (this breaks symlinks over fs
            # boundaries).
            robr="${ROBR%/*}${mntpnt}/ro"   # /aufs/usr/local/ro        |ro not symlinked
            rwbr="${RWBR%/*}${mntpnt}/rw"   # /aufs/usr/local/rw        |rw not symlinked
            ;;
        hybrid)
            # ro branches are nested, rw are isolated.
            roln="${ROBR%/*}${mntpnt}/ro"   # /aufs/usr/local/ro        |ro symlink
            rwln="${RWBR}${mntpnt}"         # /aufs/rw/usr/local        |rw symlink
            robr="${ROBR}${mntpnt}"         # /aufs/ro/usr/local        |ro symlinked
            rwbr="${RWBR%/*}${mntpnt}/rw"   # /aufs/usr/local/rw        |rw symlinked
            ;;
        *)
            # This scheme is only available with aufs; this is the only one
            # available in bilibop =<0.4.23. Symlinks are now created for
            # compatibility with other schemes
            roln="${ROBR%/*}${mntpnt}/ro"   # /aufs/usr/local/ro        |ro symlink
            rwln="${RWBR%/*}${mntpnt}/rw"   # /aufs/usr/local/rw        |rw symlink
            robr="${ROBR}${mntpnt}"         # /aufs/ro/usr/local        |ro symlinked
            rwbr="${RWBR}${mntpnt}"         # /aufs/rw/usr/local        |rw symlinked
            ;;
    esac
    temp="${RWBR}"

elif [ "${METHOD}" = "overlay" ]; then
    # Upperdirs (writable branches) can't be nested, as they are a subdirectory
    # of a tmpfs mountpoint, not the mountpoint itself.
    TEMP="$(find_mountpoint ${RWBR})"   # /overlay
    temp="${TEMP}${mntpnt}"             # /overlay/usr/local
    work="${temp}/.rw"                  # /overlay/usr/local/.rw
    rwbr="${temp}/rw"                   # /overlay/usr/local/rw         |rw not nested
    case "${BILIBOP_LOCKFS_PATH_SCHEME}" in
        hybrid)
            # ro branches are nested, rw are isolated.
            robr="${ROBR}${mntpnt}"         # /overlay/ro/usr/local     |ro symlinked
            rwln="${RWBR}${mntpnt}"         # /overlay/rw/usr/local     |rw symlink
            roln="${temp}/ro"               # /overlay/usr/local/ro     |ro symlink
            ;;
        *)
            # isolated
            robr="${temp}/ro"           # /overlay/usr/local/ro         |ro not symlinked
            ;;
    esac
fi

# If the policy is not explicitly set to 'soft', set the block device as
# readonly, and use 'rr' aufs option to improve performances:
if [ "${BILIBOP_LOCKFS_POLICY}" = "soft" ]; then
    RO="ro"
else
    RO="rr"
    [ -b "${device}" ] && blockdev --setro ${device}
fi

# The amount of RAM to allow to this mountpoint:
SIZE=
for size in ${BILIBOP_LOCKFS_SIZE}; do
    case "${size}" in
        ${mntpnt}=[1-9]*)
            SIZE="$(printf "${size#${mntpnt}=}" | grep '^[1-9][0-9]*[KkMmGg%]\?$')"
            break
            ;;
    esac
done

# Prepare the tmpfs mountpoint (it should not exist before this step):
if [ ! -d "${temp}" ]; then
    mkdir -p ${temp}
    grep -q "^${mntpnt}$" ${BILIBOP_RUNDIR}/lock ||
    echo "${mntpnt}" >>${BILIBOP_RUNDIR}/lock
fi

# Try to mount the writable branch, and in case of failure, undo what
# has been done before, etc.
if ! mount ${flags} -t tmpfs -o ${rwbr_opts:+${rwbr_opts},}${SIZE:+size=${SIZE},}mode=0755 tmpfs ${temp}; then
    [ "${RO}" = "rr" ] && [ -b "${device}" ] && blockdev --setrw "${device}"
    mount_fallback "${@}"
    exit 3
fi

# Create the needed directories; some may not exist, depending on the union fs
# type and the mount scheme.
[ -d "${robr}" ] || mkdir -p ${robr}
[ -d "${rwbr}" ] || mkdir -p ${rwbr}
if [ "${METHOD}" = "overlay" ]; then
    mkdir -p ${work}
fi

# Try to mount the readonly branch. In case of failure, undo what has been
# done before, do a normal mount, rewrite the fstab entry to be consistent
# with that, and exit:
if ! mount ${flags} ${fstype:+-t ${fstype}} -o ${robr_opts:+${robr_opts},}ro ${device:-${LOFILE}} ${robr}; then
    umount ${temp}
    [ "${RO}" = "rr" ] && [ -b "${device}" ] && blockdev --setrw "${device}"
    mount_fallback "${@}"
    exit 3
fi

# Fix permissions and ownership of the writable branch (and catch the values;
# they will be reused later):

mod="$(LC_ALL=C chmod -v --reference="${robr}" "${rwbr}" | sed 's;.* \([0-7]\{4\}\) (.\+)$;\1;')"
own="$(LC_ALL=C chown -v --reference="${robr}" "${rwbr}" | sed 's;.* \([^:]\+:[^:]\+\)$;\1;')"

owner="${own%:*}"
if [ "${owner}" != "root" ]; then
    uid="$(grep "^${owner}:" /etc/passwd | sed 's;^\([^:]*:\)\{2\}\([^:]\+\):.*;\2;')"
fi

group="${own#*:}"
if [ "${group}" != "root" ]; then
    gid="$(grep "^${group}:" /etc/group | sed 's;^\([^:]*:\)\{2\}\([^:]\+\):.*;\2;')"
fi

# Now set the union filesystem mount options
if [ "${METHOD}" = "aufs" ]; then
    UNIONFS_OPTS="br:${rwbr}=rw:${robr}=${RO}"
elif [ "${METHOD}" = "overlay" ]; then
    UNIONFS_OPTS="lowerdir=${robr},upperdir=${rwbr},workdir=${work}"
fi

# Try to mount the union fs now. In case of failure, undo what has been done
# before, etc.
#
# If the virtual filesystem's arbitrary name exists in the current directory,
# then mount will record it (in /proc/mounts) by translating it as an absolute
# path (here, /aufs or /overlay), which may confuse users parsing df or mount
# output. So we move somewhere else before calling mount.
cd /tmp
if ! mount ${flags} -t ${METHOD} -o ${UNIONFS_OPTS} ${METHOD} ${mntpnt}; then
    umount ${robr}
    umount ${temp}
    [ "${RO}" = "rr" ] && [ -b "${device}" ] && blockdev --setrw "${device}"
    mount_fallback "${@}"
    exit 3
fi
cd ${OLDPWD}

# Create symlinks, for example:
#
# (/overlay/)ro/usr/local: the target
# (/overlay/)usr/local/ro: the symlink
# ln -s ../../ro/usr/local /overlay/usr/local/ro
#
# (/overlay/)usr/local/rw: the target
# (/overlay/)rw/usr/local: the symlink
# ln -s ../../usr/local/rw /overlay/rw/usr/local
prefix="$(echo ${mntpnt} | sed -e 's,[^/],,g; s,/,../,g')"

if [ "${METHOD}" = "aufs" ]; then
    case "${BILIBOP_LOCKFS_PATH_SCHEME}" in
        isolated)
            ;;
        hybrid)
            [ -d "${rwln%/*}" ] || mkdir -p ${rwln%/*}
            [ -d "${roln%/*}" ] || mkdir -p ${roln%/*}
            ln -s ${prefix%/}/ro${mntpnt} ${roln}
            ln -s ${prefix%/}${mntpnt}/rw ${rwln}
            ;;
        *)
            [ -d "${rwln%/*}" ] || mkdir -p ${rwln%/*}
            [ -d "${roln%/*}" ] || mkdir -p ${roln%/*}
            ln -s ${prefix%/}/ro${mntpnt} ${roln}
            ln -s ${prefix%/}/rw${mntpnt} ${rwln}
            ;;
    esac

elif [ "${METHOD}" = "overlay" ]; then
    case "${BILIBOP_LOCKFS_PATH_SCHEME}" in
        hybrid)
            [ -d "${rwln%/*}" ] || mkdir -p ${rwln%/*}
            [ -d "${roln%/*}" ] || mkdir -p ${roln%/*}
            ln -s ${prefix%/}/ro${mntpnt} ${roln}
            ln -s ${prefix%/}${mntpnt}/rw ${rwln}
            ;;
        *)
            ;;
    esac
fi

# All is OK. So we can rewrite fstab entry to reflect the real mounts. This
# can be important for clean unmounts at shutdown (for the case a filesystem
# is remounted rw during a session).
robr_line="${device:-${LOFILE}} ${robr} ${fstype:-auto} ${robr_opts:+${robr_opts},}ro 0 0"
rwbr_line="tmpfs ${temp} tmpfs ${rwbr_opts:+${rwbr_opts},}${SIZE:+size=${SIZE},}${uid:+uid=${uid},}${gid:+gid=${gid},}mode=${mod} 0 0"
union_line="${METHOD} ${mntpnt} ${METHOD} ${UNIONFS_OPTS} 0 0"

sed -i "s;^\s*[^#][^ ]\+\s\+${mntpnt}\s\+lockfs\s.*;${rwbr_line}\n${robr_line}\n${union_line};" /etc/fstab

# vim: et sts=4 sw=4 ts=4