[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#675467: ITP: bilibop -- run Debian from external media



Hi,

Le 2012-06-03 00:59, intrigeri a écrit :
Nice to hear. However, we're generally happy to replace custom code
with some generic code maintained by others than us, who seem to be
experts in the specific area this code is about -- especially when our
own quick hack quickly shows its limits in unusual installations of
Tails. We try to cap our instance of the NIH syndrom to the
bare minimum.

The "wipe memory on shutdown" is a good feature, but seems to be very
hard to implement. About bilibop-common functions, you have to remember
that they apply only on (physical or virtual) block devices. Specific
functions have been written for aufs, but only for the case the readonly branch is the mountpoint of a block device. This means such functions are
unusable if you run Tails entirely into RAM (I believe with the 'toram'
boot parameter, or something like that).

Ah, you mean *owned* by the "disk" vs. "floppy" group.
Now (and now that I've read the referenced bug) it's perfectly clear :)
I don't think it's correct and clear to tell a drive is a "member" of
a group.

Yes. Annoying for me... I have to rewrite a part of the documentation.
I should begin tonight.

(fixes bug #645466).
I think s/fixes/is a way to workaround/ would be more correct.
Unfortunately, #645466 is likely to remain unfixed even once bilibop
is in Debian :(

*workaround*, why not. I say 'fix' to not say 'close'.

For example, you told me about Tails. So, boot on it (the LiveUSB,
of course) find the drive which your system is running from (here,
we say /dev/sdb), and, as the normal user, just type 'shred -vzn0
/dev/sdb'. Now your 'secured' system is lost.

Right. I'm glad we've learnt of this security issue. Thank you.
I've added it to our bug tracker:

https://tails.boum.org/bugs/writable_system_disk:_belongs_to_floppy_group/

Do you want to be credited for this discovery? (even if, formally
speaking, you did not report it to us: had I not read debian-devel,
I would probably not have learnt about it that soon, would I?)

mmmh... maybe this could give additional audience to my package :)
More seriously, I don't know. Why not? But I'm not alone. The author
of the bugreport #645466 could be credited too.

So, find the drive hosting the running system and protect it from
user mistakes is what I call 'fix a security issue' or 'make the
system more robust'.

Sure. I can't wait using it in Tails. Are there any difficulties you
think we may encounter in the process?

I have tried today with the last release of Tails. It works fine but
needs to modify two lines in the bilibop-common functions. This will
be available in my next 'dput' on mentors.debian.net. If you really
want to try it now, do as follows:
1. Boot Tails and open a session with password enabled
2. Get bilibop_0.1.tar.gz, unpack it and copy the relevant files:
   /lib/bilibop/rules.sh
   /lib/udev/bilibop_disk
   /lib/udev/rules.d/60-loopback.rules
   /lib/udev/rules.d/66-bilibop.rules
3. Copy the attachment of this message in
   /lib/bilibop/common.sh
4. If necessary, don't forget to change permissions of the files
5. Restart udev (this is mandatory) and trigger uevents
   $ sudo invoke-rc.d udev restart
   $ sudo udevadm trigger --action=add
   $ sudo udevadm trigger --action=change
6. Verify the result:
   $ ls -l /dev/sd*
   $ ls -l /dev/bilibop

But commands provided by bilibop-common (drivemap) and bilibop-rules
(lsbilibop) work partially. This is due to important changes in
base-files and udev packages. drivemap and lsbilibop could be easily
backported to Squeeze but in the other hand I think they are not so
important in the Tails context. Say me what you think about.

Please note that I did not mean to suggest bilibop does not fix
security issues or does not makes a system more robust: I was merely
pointing out that 1. the description was not explaining why and how
clearly enough to my taste; 2. I was interested and I wanted to know
more. Your reply looks like a good source of inspiration to make the
package description more thorough and precise.

I agree.

Other optional features for the desktop environment (based on
Udisks).
[...]
Looks like this could be useful for Tails persistence feature :)

Yes, this could be useful, but this could be useless too, if not
correctly configured: the user could be unable to access such or
such partition from Nautilus. Just remember that a feature as
'hide partitions' is either disabled or whitelist based (show all,
or hide all unless what is listed by: UUID, LABEL, TYPE or USAGE).

For Tails context/design, you should see in
/usr/share/doc/bilibop-rules/examples/90-internal-drives.rules
the rules titled A or C (forbid access to internal drives, even
by root).

I have send a RFS: #675532

I'll consider sponsoring this package. I expect my decision to be
mostly a function of whether there is some bilibop feature we can use
as is in Tails.

How much do you care about seeing bilibop shipped in Wheezy?
(I presume not much, else you would have posted this ITP quite sooner,
but who knows, I myself have not uploaded yet all new packages I want
to see in Wheezy -- the difference being I won't have to argue with
myself about packaging style and tools ;)

Yes, who knows. I wanted to post ITP and RFS sooner, but I have spent
last weeks to learn how to build debian packages...
Now I think bilibop (the source and the binaries) is in a good
shape and can interest people, at least for a review. If bilibop is
shipped in Wheezy, I will be happy; and I will include it in a
preseed file for further installations of Debian on removable media.
But what seems so good for me, maybe is not so good for the community?

Cheers,
quidame
# /lib/bilibop/common.sh
# vim: set et sw=4 sts=4 ts=4 fdm=marker fcl=all:

# For tests and debug purposes, set it to 'true':
DEBUG="false"

# README {{{
#
#> We assume that the commands in /usr/bin are not available (awk, cut, tail,
#  and others), and then are replaced by grep and sed euristics.
#> We assume, even if it is not often, that /etc/udev/udev.conf can have been
#  modified and that 'udev_root' can be something else than '/dev'.
#> dm-crypt/LUKS, LVM, loopback and aufs root filesystems (and combinations
#  of them) are now fully supported.
#> Functions that just output informations about devices/filesystems can be
#  called by any unprivileged user.

# Variable subtitutions and builtin commands: ==============================={{{
# The bilibop shell functions use a lot of variable subtitutions. Some of them
# (bashisms?) can not work with some shells. For:
# - 'id=fe:00', we can use:
#   echo "$((0x${id%:*})):$((${id#*:}))"
#   (this is equivalent to: printf "%d:%d\n" "0x${id%:*}" "0x${id#*:}")
# - 'dm=/dev/mapper/system', we can use:
#   echo "${dm##*/}"
# and other things like that.
#
# They have been tested (and work) with:
# - /bin/bash
# - /bin/dash
# - /bin/busybox sh
# - /usr/lib/klibc/bin/sh.shared
# - /bin/ksh93
# - /bin/mksh
# - /bin/zsh4
#
# They have been tested and don't work with:
# /bin/pdksh    (this shell has even no 'printf' builtin!)
#
# Because the 'echo' builtin command is not samely implemented in all shells
# (especially the '-e' option is implicit in dash's echo), we never use this
# command with options, and never use escaped characters (\t, \n, \c...) in
# the string to echo.
# }}}
# Needed external commands: ================================================={{{
# Here is a table of external GNU commands used in the following functions.
# Some of them having different options and behaviors when they are provided
# by 'busybox' or 'klibc', we note here which are working or not in the context
# of these functions (this is a base to write an initramfs-tools hook). 'YES'
# means it works, 'NO' means it don't, and '-' means it is not provided.
#
# GNU tools                 busybox builtins                klibc commands
# ------------------------------------------------------------------------------
# /bin/cat                  YES                             YES
# /bin/df                   YES                             -
# /bin/grep                 YES                             -
# /bin/readlink             YES                             NO
# /bin/sed                  YES                             -
# /sbin/udevadm             -                               -
#
# Now we can say:
# - there is no need to add 'cat'.
# - 'udevadm' must be added (we assume udev provides a hook to do that).
# - if 'busybox' is not added into the initramdisk, we have to use the 'df',
#   'grep', 'readlink' and 'sed' GNU commands.
#   So, if busybox is not available into the initramdisk:
#   * add 'df', 'grep' and 'sed'
#   * replace the klibc's 'readlink' by the GNU one
#
# }}}
# Needed files: ============================================================={{{
# To run correctly, the bilibop functions need to read informations into some
# virtual files or directory, especially:
#
# /dev/* (or ${udev_root}/*)
# /dev/block/*
# /proc/cmdline
# /proc/filesystems
# /proc/mounts
# /proc/partitions
# /sys/block/sd?/removable
# /sys/block/dm-?/slaves
# /sys/block/loop?/loop/backing_file
# /sys/class/block/*/dev
# /sys/fs/aufs/si_*/br?
#
# So we assume that /dev, /proc and /sys are mounted. If you have to use this
# file from into a chrooted environment, you have to do something like that:
#
# # mount DEVICE /mnt
# # mount -t sysfs -o nodev,noexec,nosuid sysfs /mnt/sys
# # mount -t proc -o nodev,noexec,nosuid proc /mnt/proc
# # mount -t devtmpfs -o mode=0755 udev /mnt/dev
# and optionally:
# # mount -t tmpfs -o nosuid,nodev tmpfs /mnt/tmp
# # mount -t tmpfs -o nosuid,size=10%,mode=0755 tmpfs /mnt/run
# # mkdir -p /mnt/dev/pts /mnt/run/lock /mnt/run/shm
# # mount -t devpts -o noexec,nosuid,gid=5,mode=0620 devpts /mnt/dev/pts
# # mount -t tmpfs -o noexec,nodev,nosuid,size=10% tmpfs /mnt/run/lock
# # mount -t tmpfs -o nosuid,nodev tmpfs /mnt/run/shm
# and finally:
# # chroot /mnt
# }}}

# }}}

# bilibop_common_functions() ================================================{{{
# What we want is: output a list of useful bilibop functions, to use them
# manually.
bilibop_common_functions() {
    get_udev_root
    cat >&2 <<EOF
aufs_dirs <MOUNTPOINT>
aufs_mountpoints
aufs_readonly_branch <MOUNTPOINT>
aufs_writable_branch <MOUNTPOINT>
backing_file_from_loop <DEVICE>
device_node_from_major_minor <MAJ:MIN>
device_id_of_file <FILE|DIR>
find_mountpoint <FILE|DIR>
get_biliop_variables
is_aufs_mountpoint [-q] <MOUNTPOINT>
is_removable <DEVICE>
mapper_name_from_dm_node <DEVICE>
major_minor_from_device_node <DEVICE>
physical_hard_disk [FILE|DIR|DEVICE]
query_sysfs_attrs <DEVICE>
query_udev_envvar <DEVICE>
underlying_device <FILE|DIR|DEVICE>
underlying_device_from_aufs <MOUNTPOINT>
underlying_device_from_device <DEVICE>
underlying_device_from_dm <DEVICE>
underlying_device_from_file <FILE|DIR>
underlying_device_from_loop <DEVICE>
underlying_partition <FILE|DIR|DEVICE>
EOF
}
# ===========================================================================}}}

### INTERNAL FUNCTIONS ###
### The following functions can be called by others in this file, except the
### last one, 'physical_hard_disk'.
### Here is the main function's dependency tree {{{
# physical_hard_disk
# |
# |__underlying_partition
#    |
#    |__underlying_device
#    |  |
#    |  |__underlying_device_from_device
#    |  |  |
#    |  |  |__underlying_device_from_dm
#    |  |  |__underlying_device_from_loop
#    |  |     |
#    |  |     |__backing_file_from_loop
#    |  |     |__device_id_of_file
#    |  |     |__device_node_from_major_minor
#    |  |
#    |  |__underlying_device_from_file
#    |     |
#    |     |__device_node_from_major_minor
#    |     |__device_id_of_file
#    |     |__find_mountpoint
#    |     |  |
#    |     |  |__is_aufs_mountpoint
#    |     |     |
#    |     |     |__canonical
#    |     |
#    |     |__underlying_device_from_aufs
#    |        |
#    |        |__aufs_dirs
#    |           |
#    |           |__aufs_si_directory
#    |              |
#    |              |__is_aufs_mountpoint
#    |                 |
#    |                 |__canonical
#    |
#    |__underlying_device_from_device
#       |
#       |__underlying_device_from_dm
#       |__underlying_device_from_loop
#          |
#          |__backing_file_from_loop
#          |__device_id_of_file
#          |__device_node_from_major_minor
#
# }}}

# canonical() ==============================================================={{{
# What we want is: remove trailing slash of a directory name (to match with the
# /proc/mounts mountpoints, between others). Another solution? Maybe:
# local arg="${1%/}"; echo "${arg:-/}"
# But this is a little bit different: if $1 is empty, the output is "/". Try
# again.
canonical() {
    ${DEBUG} && echo "> canonical $@" >&2
    case    "${1}" in
        /)  echo "/" ;;
        *)  echo "${1%/}" ;;
    esac
}
# ===========================================================================}}}
# find_mountpoint() ========================================================={{{
# What we want is: output the mountpoint of the filesystem the file or directory
# given as argument depends is onto. Because it outputs the last field of the
# last line of the 'df' output, df don't need the '-P' (POSIX format) option,
# and so we are sure it works with all df commands or builtins (busybox).
find_mountpoint() {
    ${DEBUG} && echo "> find_mountpoint $@" >&2
    df "${1}" | sed -n '$p' | sed -e 's,.* \([^[:blank:]]\+\)$,\1,'
}
# ===========================================================================}}}
# device_node_from_major_minor() ============================================{{{
# What we want is: translate the 'major:minor' given as argument to the
# corresponding device node. What is the best way?
device_node_from_major_minor() {
    ${DEBUG} && echo "> device_node_from_major_minor $@" >&2
    #grep "^\s*${1%:*}\s\+${1#*:}\s" /proc/partitions |
    #    sed -e "s,^[[:blank:][:digit:]]*\(.\+\)$,${udev_root}/\1,"
    # maybe best:
    local   path="$(readlink -f /sys/dev/block/${1})"
    echo "${udev_root}/${path##*/}"
}
# ===========================================================================}}}
# device_id_of_file() ======================================================={{{
# What we want is: output the major:minor of the filesystem containing the
# file or directory given as argument. Here we use the full path of the command
# for the case this function is called by a normal user without /sbin in its
# PATH. Since the bilibop functions do not depend on blkid, blockdev, losetup
# or dmsetup to query informations about devices/filesystems, anyone can run
# them without special privileges. Formely, this uses udev and sysfs databases
# instead of direct access to the devices.
device_id_of_file() {
    ${DEBUG} && echo "> device_id_of_file $@" >&2
    /sbin/udevadm info --device-id-of-file "${1}"
}
# ===========================================================================}}}
# is_aufs_mountpoint() ======================================================{{{
# What we want is: check if a directory given as argument is an aufs mountpoint
# and print the corresponding line from /proc/mounts. Accepts the '-q' (quiet)
# option: print nothing, but return a 0/1 exit value.
is_aufs_mountpoint() {
    ${DEBUG} && echo "> is_aufs_mountpoint $@" >&2
    local   opt
    case    "${1}" in
        -*)
            opt="${1}"
            shift ;;
    esac
    grep ${opt} "^[^ ]\+ $(canonical ${1}) aufs " /proc/mounts
}
# ===========================================================================}}}
# aufs_si_directory() ======================================================={{{
# What we want is: output the sysfs directory where informations can be found
# about an aufs mount point given as argument.
aufs_si_directory() {
    ${DEBUG} && echo "> aufs_si_directory $@" >&2
    is_aufs_mountpoint "${1}" | sed -e 's|.*si=\([^ ,]\+\).*|/sys/fs/aufs/si_\1|'
}
# ===========================================================================}}}
# aufs_dirs() ==============================================================={{{
# What we want is: output all the underlying mountpoints (called branches) an
# aufs filesystem given as argument is made of.
aufs_dirs() {
    ${DEBUG} && echo "> aufs_dirs $@" >&2
    local   br
    for br in $(aufs_si_directory "${1}")/br*
    do
        cat ${br}
    done
}
# ===========================================================================}}}
# backing_file_from_loop() =================================================={{{
# What we want is: output the backing file of a loopback device given as
# argument. This requires kernel >= 2.6.37. Great thing! Before that, it was
# necessary to call losetup (as root) and parse its output, with possible
# failures: the filename is truncated (currently 64 characters) and even,
# depending on the version of the command or its implementation, it can be
# relative to the directory from where the command was called to setup the
# device... an other solution is to parse the output of losetup and use
# the major:minor of the device and the inode number of the file to find it.
# Pfff! See now:
backing_file_from_loop() {
    ${DEBUG} && echo "> backing_file_from_loop $@" >&2
    [ -f /sys/block/${1##*/}/loop/backing_file ] &&
    cat /sys/block/${1##*/}/loop/backing_file
}
# ===========================================================================}}}
# underlying_device_from_loop() ============================================={{{
# What we want is: output the underlying device of a loop device given as
# argument. If the loop device is associated to a block device, then output it;
# otherwise, find the device from its major:minor numbers. This function has
# been entirely rewritten to not depend on losetup, but requires kernel version
# >= 2.6.37.
underlying_device_from_loop() {
    ${DEBUG} && echo "> underlying_device_from_loop $@" >&2
    local   lofile="$(backing_file_from_loop ${1})" || return 1
    if      [ -b "${lofile}" ]
    then    readlink -f "${lofile}"
    elif    [ -e "${lofile}" ]
    then    device_node_from_major_minor $(device_id_of_file "${lofile}")
    elif    [ -r "${1}" ]
    then
            # For some cases, when the loop device is set from inside the
            # initramfs (Live Systems)
            local dev="$(/sbin/losetup ${1} | sed "s;^${1}: \[\([0-9a-f]\{4\}\)\].*;\1;")"
            device_node_from_major_minor $(echo "$((0x${dev}/256)):$((0x${dev}%256))")
    else
            return 1
    fi
}
# ===========================================================================}}}
# underlying_device_from_aufs() ============================================={{{
# What we want is: output the underlying device of the (generally) readonly
# branch of an aufs mountpoint given as argument. We assume that there is only
# and at least one real device used to build the aufs, other branch(s) being
# virtual fs.
underlying_device_from_aufs() {
    ${DEBUG} && echo "> underlying_device_from_aufs $@" >&2
    local   dir dev
    for dir in $(aufs_dirs "${1}")
    do
        dev="$(grep "^/[^ ]\+ ${dir%=r?*} " /proc/mounts | sed -e 's,^\(/[^ ]\+\) .*,\1,')"
        if      [ -b "${dev}" ]
        then    readlink -f "${dev}"
                return 0
        fi
    done
    return 1
}
# ===========================================================================}}}
# underlying_device_from_dm() ==============================================={{{
# What we want is: output the underlying device of a dm device given as
# argument. This function has been rewritten to not depend on dmsetup, grep and
# sed. A loop is now used to output the final underlying device, not just the
# parent device (for example for LUKS/LVM combinations). The output will be of
# the form /dev/sdXN or /dev/loopN. As for the aufs, we assume that the scheme
# is very simple, and there is only and at least one slave device per dm device
# (this is the case for bilibop: because it runs from an external media, RAID
# is not used as the root filesystem, and if LVM is used, this is to create
# several LV in a VG, not several PV in a VG).
underlying_device_from_dm() {
    ${DEBUG} && echo "> underlying_device_from_dm $@" >&2
    local   slave dev="$(readlink -f "${1}")"
    dev="${dev##*/}"
    while   true
    do
        case    "${dev}" in
            dm-*)
                #slave="$(parent_device_from_dm ${udev_root}/${dev})"
                slave="$(echo /sys/block/${dev}/slaves/*)"
                [ "${slave}" = "/sys/block/${dev}/slaves/*" ] && return 3
                dev="${slave##*/}"
                ;;
            *)
                echo "${udev_root}/${dev}"
                return 0
                ;;
        esac
    done
}
# ===========================================================================}}}
# underlying_device_from_device() ==========================================={{{
# What we want is: find the underlying device of a device (dm-crypt, loop or
# LVM) given as argument. This function can be included into a loop to finally
# output a device name that has no parent/underlying device, i.e a partition.
underlying_device_from_device() {
    ${DEBUG} && echo "> underlying_device_from_device $@" >&2
    local   dev="${1}"
    case    "${dev}" in
        ${udev_root}/dm-[0-9]*)
            underlying_device_from_dm "${dev}"
            ;;
        ${udev_root}/loop[0-7])
            underlying_device_from_loop "${dev}"
            ;;
        ${udev_root}/*)
            readlink -f "${dev}"
            ;;
        *)
            return 1
    esac
}
# ===========================================================================}}}
# underlying_device_from_file() ============================================={{{
# What we want is: output the filesystem containing a file given as argument,
# even if the filesystem is mounted as the lower branch of an aufs mountpoint.
underlying_device_from_file() {
    ${DEBUG} && echo "> underlying_device_from_file $@" >&2
    local   id="$(device_id_of_file "${1}")" dev mntpnt
    if      [ "${id%:*}" = "0" ]
    then
            # 0 is the major number of all ramfs (tmpfs, devtmpfs, sysfs, proc
            # and others). If the file is hosted on a such virtual filesystem,
            # we encounter an alternative: the file is on aufs and we continue
            # after a jump on the real block device under the aufs, or we stop
            # here.
            mntpnt="$(find_mountpoint "${1}")"
            if      is_aufs_mountpoint -q "${mntpnt}"
            then    dev="$(underlying_device_from_aufs "${mntpnt}")"
            else    return 1
            fi
    else
            dev="$(device_node_from_major_minor "${id}")"
    fi

    [ -b "${dev}" ] && readlink -f "${dev}"
}
# ===========================================================================}}}
# underlying_device() ======================================================={{{
# What we want is: output the underlying device node of a file/directory or
# block device given as argument. All is explained in the previous functions.
# The question here is to know what to do with the symlinks: follow them, or
# not? For a lot of them, there is no importance, but for something as /etc/mtab
# linked to /proc/mounts, this makes a big difference. For the moment, we have
# choosen to not follow them... Maybe we have to clean some other function in
# the same sense.
# NEWS: the 'find_mountpoint' function uses df which operates on the pointed
# file, not on the symlink.
underlying_device() {
    ${DEBUG} && echo "> underlying_device $@" >&2
    local   dev target="${1}"

    if      [ -b "${target}" ]
    then
            dev="$(underlying_device_from_device "${target}")"

    elif    [ -f "${target}" -o -d "${target}" ]
    then
            dev="$(underlying_device_from_file "${target}")"
    fi

    if      [ -b "${dev}" ]
    then    echo "${dev}"
    else    return 3
    fi
}
# ===========================================================================}}}
# underlying_partition() ===================================================={{{
# What we want is: output the partition which the device, or directory, or
# file given as argument is on. With this simple loop, supports combinations
# of mapped devices (LVM, dm-crypt), loopback devices and aufs filesystems.
underlying_partition() {
    ${DEBUG} && echo "> underlying_partition $@" >&2
	local	dev="$(underlying_device "${1}")"
	local	old new="${dev}"

	while	true
	do
		case	"${new}" in
			"")
				return 1
				;;
			"${udev_root}"/sd[a-z]*)
				echo "${new}"
				return 0
				;;
			"${old}")
				echo "${new}"
				return 0
				;;
		esac
		old="${new}"
		new="$(underlying_device_from_device "${old}")"
	done
}
# ===========================================================================}}}
# physical_hard_disk() ======================================================{{{
# What we want is: output the physical hard disk node of a device, file or
# directory given as argument. The main usage is: 'physical_hard_disk /', or
# just 'physical_hard_disk'. The thing to do here is to find the whole disk
# name after 'underlying_partition' has given the partition name. This is not
# also simple to just remove digits at the end of the name: the partitions
# of MMC/SD/SDHC memsticks are of the form /dev/mmcblk0p1, and the whole
# device name is /dev/mmcblk0. What to do whith the 'p'? A circle? We prefer
# a loop. This seems to be best than a lot of exceptions (how to track them
# to build a poor function?) For that, of course, we assume that in
# /proc/partitions, nodes are sorted in alphanumeric order, and the whole disk
# node always comes before its partitions.
physical_hard_disk() {
    ${DEBUG} && echo "> physical_hard_disk $@" >&2
    [ -z "${1}" ] && eval set -- /
    local   part disk dev

    if      [ -e "/sys/class/block/${1##*/}" -a ! -e "/sys/devices/virtual/block/${1##*/}" ]
    then    dev="${1}"
    else    dev="$(underlying_partition "${1}")"
    fi

    for part in $(sed -n '/[[:digit:]]/p' /proc/partitions | sed 's,.*\s\([^ ]\+\)$,\1,')
    do
        case    "${dev}" in
		    ${udev_root}/${part}*)
                disk="${udev_root}/${part}"
                break
                ;;
        esac
    done

    if      [ -b "${disk}" ]
    then    echo "${disk}"
    elif    [ "${1}" = "/" ]
    then    # Maybe you have forgotten to get/set the udev_root variable before
            # to run this function ? Run 'get_udev_root' and retry
            return 127
    else    # If the argument is a file/directory in a virtual fs, there is no
            # way to find its hosting block device name. It don't exist. This
            # seems simple, but how to manage other cases?
            return 3
    fi
}
# ===========================================================================}}}


### OTHER FUNCTIONS ###
### only called from other scripts (from bilibop-lockfs and bilibop-rules, and
### maybe others).

# unknown_argument() ========================================================{{{
# A general error message for helper scripts.
unknown_argument() {
    ${DEBUG} && echo "> unknown_argument $@" >&2
    cat <<EOF
${0##*/}: unrecognized argument (${1}).
EOF
}
# ===========================================================================}}}
# required_argument() ======================================================={{{
# An other error message for helper scripts.
required_argument() {
    ${DEBUG} && echo "> required_argument $@" >&2
    cat <<EOF
${0##*/}: argument is required.
EOF
}
# ===========================================================================}}}

# get_udev_root() ==========================================================={{{
# What we want is: get the 'udev_root' variable from the udev configuration
# file, or set it to its default value. This function must not be called from
# into the initramdisk, where '/dev' is always used.
get_udev_root() {
    ${DEBUG} && echo "> get_udev_root $@" >&2
    if      [ -f /etc/udev/udev.conf ]
    then    . /etc/udev/udev.conf
    fi
    udev_root="${udev_root%/}"
    udev_root="${udev_root:-/dev}"
}
# ===========================================================================}}}
# get_bilibop_variables() ==================================================={{{
# What we want is: get bilibop variables from its configuration file if it
# exists, and set/overwrite the most important of them (BILIBOP_RUNDIR). If not
# existing, BILIBOP_COMMON_BASENAME is set to 'bilibop'.
# This function can be called from into the running system, or from into the
# initramdisk. In this case, the mountpoint of the future root filesystem
# (${rootmnt}) must be given as argument.
get_bilibop_variables() {
    ${DEBUG} && echo "> get_bilibop_variables $@" >&2
    if      [ -f ${1}/etc/bilibop/bilibop.conf ]
    then    . ${1}/etc/bilibop/bilibop.conf
    fi
    BILIBOP_RUNDIR="/run/${BILIBOP_COMMON_BASENAME:=bilibop}"
}
# ===========================================================================}}}
# get_aufs_variables() ======================================================{{{
# What we want is: set the default aufs variables (used by the aufs tools) and
# override them by the admin settings, just as do the aufs tools.
get_aufs_variables() {
    ${DEBUG} && echo "> get_aufs_variables $@" >&2
    AUFS_SUPER_MAGIC="1635083891"
    AUFS_SUPER_MAGIC_HEX="0x61756673"
    AUFS_WH_PFX=".wh."
    AUFS_WH_PFX2=".wh..wh."
    AUFS_WH_BASE=".wh..wh.aufs"
    AUFS_WH_DIROPQ=".wh..wh..opq"
    AUFS_WH_PLINKDIR=".wh..wh.plnk"
    AUFS_WH_ORPHDIR=".wh..wh.orph"
    if      [ -f /etc/default/aufs ]
    then    . /etc/default/aufs
    fi
}
# ===========================================================================}}}

# aufs_mountpoints() ========================================================{{{
# What we want is: output the mountpoints of all aufs filesystems.
aufs_mountpoints() {
    ${DEBUG} && echo "> aufs_mountpoints $@" >&2
    grep '^[^ ]\+ /[^ ]* aufs .*[, ]si=[0-9a-f]\+[, ].*' /proc/mounts |
    sed -e 's,^[^ ]\+ \(/[^ ]*\) aufs .*,\1,'
}
# ===========================================================================}}}
# aufs_readonly_branch() ===================================================={{{
# What we want is: output the lower (readonly) branch of an aufs mount point
# given as argument.
aufs_readonly_branch() {
    ${DEBUG} && echo "> aufs_readonly_branch $@" >&2
    local   br
    for br in $(aufs_si_directory "${1}")/br*
    do
        grep '=r[or]\(+.*\)\?' ${br} | sed -e 's,=r[or].*,,'
    done
}
# ===========================================================================}}}
# aufs_writable_branch() ===================================================={{{
# What we want is: output the upper (read-write) branch of an aufs mount point
# given as argument.
aufs_writable_branch() {
    ${DEBUG} && echo "> aufs_writable_branch $@" >&2
    local   br
    for br in $(aufs_si_directory "${1}")/br*
    do
        grep '=rw$' ${br} | sed -e 's,=rw,,'
    done
}
# ===========================================================================}}}

# is_removable() ============================================================{{{
# What we want is: check if a whole disk node given as argument is seen as
# removable from its sysfs attribute. If yes, this means the disk given as
# argument is an USB stick (or a CD/DVD).
is_removable() {
    ${DEBUG} && echo "> is_removable $@" >&2
    [ "$(cat /sys/block/${1##*/}/removable)" = "1" ]
}
# ===========================================================================}}}
# is_readonly() ============================================================={{{
# What we want is: check if a block device given as argument is writable.
is_readonly() {
    ${DEBUG} && echo "> is_readonly $@" >&2
    [ "$(cat /sys/class/block/${1##*/}/ro)" = "1" ]
}
# ===========================================================================}}}

# parent_device_from_dm() ==================================================={{{
# What we want is: output the direct parent device (slave) of a dm device given
# as argument.
parent_device_from_dm() {
    ${DEBUG} && echo "> parent_device_from_dm $@" >&2
    local   dev="$(readlink -f "${1}")"
    local   slave="$(echo /sys/block/${dev##*/}/slaves/*)"
    [ "${slave}" = "/sys/block/${dev}/slaves/*" ] && return 3
    echo "${udev_root}/${slave##*/}"
}
# ===========================================================================}}}
# mapper_name_from_dm_node() ================================================{{{
# What we want is: output the basename (in /dev/mapper) of a dm device node
# (dm-*) given as argument.
mapper_name_from_dm_node() {
    ${DEBUG} && echo "> mapper_name_from_dm_node $@" >&2
    cat /sys/block/${1##*/}/dm/name
}
# ===========================================================================}}}
# major_minor_from_device_node() ============================================{{{
# What we want is: translate the device node given as argument to the
# corresponding major:minor.
major_minor_from_device_node() {
    ${DEBUG} && echo "> major_minor_from_device_node $@" >&2
    cat /sys/class/block/${1##*/}/dev
}
# ===========================================================================}}}
# query_sysfs_attrs() ======================================================={{{
# What we want is: query the sysfs attributes database for a device node given
# as argument. See the 'device_id_of_file()' comments about the full path of
# the command.
query_sysfs_attrs() {
    ${DEBUG} && echo "> query_sysfs_attrs $@" >&2
	/sbin/udevadm info --attribute-walk --name "${1}"
}
# ===========================================================================}}}
# query_udev_envvar() ======================================================={{{
# What we want is: query the udev properties database for a device node given
# as argument. See the 'device_id_of_file()' comments about the full path of
# the command. The --export option is mandatory to eval the output of this
# command.
query_udev_envvar() {
    ${DEBUG} && echo "> query_udev_envvar $@" >&2
	/sbin/udevadm info --query property --name "${1}" --export
}
# ===========================================================================}}}


Reply to: