Current File : //usr/local/jetapps/usr/share/rear/lib/filesystems-functions.sh
# This file is part of Relax-and-Recover, licensed under the GNU General
# Public License. Refer to the included COPYING for full text of license.
#
# File system support functions

function btrfs_snapshot_subvolume_exists() {
    # returns true if the btrfs snapshot subvolume ($2) exists in the Btrfs
    # file system at the mount point ($1).

    # Use -s so that btrfs subvolume list considers snapshots only
    btrfs_subvolume_exists "$1" "$2" "-s"
}

function btrfs_subvolume_exists() {
    # returns true if the btrfs subvolume ($2) exists in the Btrfs file system at the mount point ($1).
    local subvolume_mountpoint="$1" btrfs_subvolume_path="$2"

    # extra options for the btrfs subvolume list command ($3)
    local btrfs_extra_opts="$3"

    # A root subvolume can be assumed to always exist
    [ "$btrfs_subvolume_path" == "/" ] && return 0

    # A non-root subvolume exists if the btrfs subvolume list contains its complete path at the end of one line.
    # This code deliberately uses a plain string comparison rather than a regexp.
    btrfs subvolume list -a $btrfs_extra_opts "$subvolume_mountpoint" | sed -e 's; path <FS_TREE>/; path ;' |
    awk -v path="$btrfs_subvolume_path" '
        BEGIN {
            match_string = " path " path;
            match_string_length = length(match_string);
            matching_line_count = 0;
        }

        (substr($0, length($0) - match_string_length + 1) == match_string) {
            matching_line_count++;
        }

        END {
            exit(matching_line_count == 1 ? 0 : 1);
        }'

    # Return awk's exit status
}


#Parse output from xfs_info for later use by mkfs.xfs

function xfs_parse
{
    local xfs_opt_file=$1
    local xfs_opts=""

    # Check if we can read configuration file produced by xfs_info.
    # Fall back to mkfs.xfs defaults if trouble with configuration file occur.
    if [ ! -r $xfs_opt_file ]; then
        Log "Can't read $xfs_opt_file, falling back to mkfs.xfs defaults."
        return
    fi

    infile=$(cat $xfs_opt_file)

    # Remove some unused characters like commas (,) "empty" equal signs " = "
    infile_format=$(echo $infile | sed -e 's/ = / /g' -e 's/,//g' -e 's/ =/=/g')

    # xfs_info is divided into sections.
    # Sections will be later searched for right option.
    metadata_section=$(echo $infile_format | sed -e 's/.*\(meta-data=.*\) data.*/\1/')
    data_section=$(echo $infile_format     | sed -e 's/.*\(data.*\) naming.*/\1/')
    naming_section=$(echo $infile_format   | sed -e 's/.*\(naming.*\) log=.*/\1/')
    log_section=$(echo $infile_format      | sed -e 's/.*\(log=.*\) realtime.*/\1/')
    realtime_section=$(echo $infile_format | sed -e 's/.*\(realtime.*\).*/\1/')

    # Definitions of options to search for
    # meta-data section of xfs_info output
    xfs_param_iname[0]="isize"
    xfs_param_search[0]="metadata_section"
    xfs_param_opt[0]="-i"
    xfs_param_name[0]="size"

    xfs_param_iname[1]="agcount"
    xfs_param_search[1]="metadata_section"
    xfs_param_opt[1]="-d"
    xfs_param_name[1]="agcount"

    xfs_param_iname[2]="sectsz"
    xfs_param_search[2]="metadata_section"
    xfs_param_opt[2]="-s"
    xfs_param_name[2]="size"

    xfs_param_iname[3]="attr"
    xfs_param_search[3]="metadata_section"
    xfs_param_opt[3]="-i"
    xfs_param_name[3]="attr"

    xfs_param_iname[4]="projid32bit"
    xfs_param_search[4]="metadata_section"
    xfs_param_opt[4]="-i"
    xfs_param_name[4]="projid32bit"

    xfs_param_iname[5]="crc"
    xfs_param_search[5]="metadata_section"
    xfs_param_opt[5]="-m"
    xfs_param_name[5]="crc"

    xfs_param_iname[6]="finobt"
    xfs_param_search[6]="metadata_section"
    xfs_param_opt[6]="-m"
    xfs_param_name[6]="finobt"

    # data section of xfs_info output
    xfs_param_iname[7]="bsize"
    xfs_param_search[7]="data_section"
    xfs_param_opt[7]="-b"
    xfs_param_name[7]="size"

    xfs_param_iname[8]="imaxpct"
    xfs_param_search[8]="data_section"
    xfs_param_opt[8]="-i"
    xfs_param_name[8]="maxpct"

    xfs_param_iname[9]="sunit"
    xfs_param_search[9]="data_section"
    xfs_param_opt[9]="-d"
    xfs_param_name[9]="sunit"

    xfs_param_iname[10]="swidth"
    xfs_param_search[10]="data_section"
    xfs_param_opt[10]="-d"
    xfs_param_name[10]="swidth"

    # log section of xfs_info output
    xfs_param_iname[11]="version"
    xfs_param_search[11]="log_section"
    xfs_param_opt[11]="-l"
    xfs_param_name[11]="version"

    xfs_param_iname[12]="sunit"
    xfs_param_search[12]="log_section"
    xfs_param_opt[12]="-l"
    xfs_param_name[12]="sunit"

    xfs_param_iname[13]="lazy-count"
    xfs_param_search[13]="log_section"
    xfs_param_opt[13]="-l"
    xfs_param_name[13]="lazy-count"

    # naming section of xfs_info output
    xfs_param_iname[14]="bsize"
    xfs_param_search[14]="naming_section"
    xfs_param_opt[14]="-n"
    xfs_param_name[14]="size"

    xfs_param_iname[15]="ascii-ci"
    xfs_param_search[15]="naming_section"
    xfs_param_opt[15]="-n"
    xfs_param_name[15]="ascii-ci"

    xfs_param_iname[16]="ftype"
    xfs_param_search[16]="naming_section"
    xfs_param_opt[16]="-n"
    xfs_param_name[16]="ftype"

    # realtime section of xfs_info output
    xfs_param_iname[17]="extsz"
    xfs_param_search[17]="realtime_section"
    xfs_param_opt[17]="-r"
    xfs_param_name[17]="extsize"

    # Here we will save some variables, that will be later used for
    # calculations (block_size) or due dependencies with other options (crc).

    block_size=$(echo $data_section \
    | grep -oE "bsize=[0-9]*" | cut -d "=" -f2)

    crc=$(echo $metadata_section \
    | grep -oE "crc=[0-9]*" | cut -d "=" -f2)

    # Count how many parameter we have
    for i in "${xfs_param_iname[@]}" ; do
      xfs_param_count=$((xfs_param_count+1))
    done

    i=0
    while [ $i -lt $xfs_param_count ]; do

        # Find variable and its value in `xfs_output' list
        var_val=$(eval "echo \$${xfs_param_search[$i]}" \
        | grep -oE "${xfs_param_iname[$i]}=[0-9]*")

        if [ -n "$var_val" ]; then

            # Substitute variable name from `xfs_info' output
            # so it can serve as input for mkfs.xfs
            var_val_mapped=$(echo $var_val \
            | sed -e 's/'${xfs_param_iname[$i]}'/'${xfs_param_name[$i]}'/')

            var=$(echo $var_val_mapped | cut -d "=" -f1)
            val=$(echo $var_val_mapped | cut -d "=" -f2)

            # Handle mkfs.xfs special cases
            # sunit & swidth are set in blocks
            if [ $var = "sunit" ] || [ $var = "swidth" ]; then
                val=$((val*$block_size/512))
            fi

            # A bit obscure checking naming_options version
            if [ $var = "ascii-ci" ]; then
                var="version"
                if [ $val -eq 1 ]; then
                    val="ci"
                elif [ $val -eq 0 ]; then
                    val="2"
                fi
            fi

            # xfsprogs > 4.7.0 evaluates -l sunit=0 "illegal"
            #
            # mkfs.xfs -l sunit=0 ...
            # "Illegal value 0 for -l sunit option. value is too small"
            #
            # Skipping -l sunit=0 satisfies mkfs.xfs and does not change
            # original XFS file system properties.
            # c.f. ReaR: https://github.com/rear/rear/issues/1579
            # and https://www.spinics.net/lists/linux-xfs/msg13135.html
            if [ ${xfs_param_search[$i]} = "log_section" ] &&
               [ $var = "sunit" ] && [ $val = 0 ]; then
                i=$((i+1))
                continue
            fi

            # crc and ftype are mutually exclusive.
            # crc option might be even completely missing in older versions of
            # xfsprogs, which would cause behaviour like described in
            # https://github.com/rear/rear/issues/1915.
            # To avoid messages like "[: -eq: unary operator expected",
            # we will set default value for $crc variable to 0.
            if [ ${crc:-0} -eq 1 ] && [ $var = "ftype" ]; then
                i=$((i+1))
                continue
            fi

            # Add option to mkfs.xfs option list
            xfs_opts+="${xfs_param_opt[$i]} $var=$val "
        fi

        i=$((i+1))

    done

  # Output xfs options for further use
  echo "$xfs_opts"
}

# return the total used disk space of the target file systems
function total_target_fs_used_disk_space() {
    # get all mounted file systems for TARGET_FS_ROOT that are mounted on a local device (starting with /)
    # and exclude virtual filesystems like tmpfs, devtmpfs, sysfs, none
    # and return the 3rd column of the last line of the df output that looks like this:
    # Filesystem                         Size  Used Avail Use% Mounted on
    # /dev/mapper/ubuntu--vg-ubuntu--lv  5.6G  4.2G  1.2G  78% /mnt/local
    # /dev/sda2                          1.7G  277M  1.4G  17% /mnt/local/boot
    # /dev/sda1                          537M  6.1M  531M   2% /mnt/local/boot/efi
    # total                              7.8G  4.4G  3.1G  60% -
    #
    # shellcheck disable=SC2046
    df --total --local -h \
        --exclude-type=tmpfs --exclude-type=devtmpfs --exclude-type=sysfs --exclude-type=none \
        $(mount | sed -n -e "\#^/.*$TARGET_FS_ROOT#s/ .*//p") | sed -E -n -e '$s/[^ ]+ +[^ ]+ +([^ ]+).*/\1/p'
}


# $1 is a mount command argument (string containing comma-separated
# mount options). The remaining arguments to the function ($2 ... )
# specify the mount options to remove from $1, together with a trailing "="
# and any value that follows each option.
# For example, the call
# "remove_mount_options_values nodev,uid=1,rw,gid=1 uid gid"
# returns "nodev,rw".
# There is no support for removing a mount option without a value and "=",
# so "remove_mount_options_values nodev,uid=1,rw,gid=1 rw" will not work.
# The function will return the modified string on stdout.

function remove_mount_options_values () {
    local str="$1"

    shift
    # First add a comma at the end so that it is easier to remove a mount option at the end:
    str="${str/%/,}"
    for i in "$@" ; do
        # FIXME this also removes trailing strings at the end of longer words
        # For example if one wants to remove any id=... option,
        # the function will also replace "uid=1" by "u" by removing
        # the trailing "id=1", which is not intended.
        # Not easy to fix because $str can contain prefixes which are not
        # mount options but arguments to the mount command itself
        # (in particluar, "-o ").
        # FIXME this simple approach would fail in case of mount options
        # containing commas, for example the "context" option values,
        # see mount(8)

        # the extglob shell option is enabled in rear
        str="${str//$i=*([^,]),/}"
    done
    # Remove all commas at the end:
    echo "${str/%,/}"
}