1#!/bin/bash 2 3# Copyright (C) 2018 Oracle. All Rights Reserved. 4# 5# Author: Darrick J. Wong <darrick.wong@oracle.com> 6# 7# This program is free software; you can redistribute it and/or 8# modify it under the terms of the GNU General Public License 9# as published by the Free Software Foundation; either version 2 10# of the License, or (at your option) any later version. 11# 12# This program is distributed in the hope that it would be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program; if not, write the Free Software Foundation, 19# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 20 21scrub_all=0 22conffile="@root_sysconfdir@/e2scrub.conf" 23 24test -f "${conffile}" && . "${conffile}" 25 26scrub_args="" 27 28print_help() { 29 echo "Usage: $0 [OPTIONS]" 30 echo " -A: Scrub all ext[234] filesystems even if not mounted." 31 echo " -r: Remove e2scrub snapshots." 32 echo " -V: Print version information and exit." 33} 34 35print_version() { 36 echo "e2scrub_all @E2FSPROGS_VERSION@ (@E2FSPROGS_DATE@)" 37} 38 39exitcode() { 40 ret="$1" 41 42 # If we're being run as a service, the return code must fit the LSB 43 # init script action error guidelines, which is to say that we 44 # compress all errors to 1 ("generic or unspecified error", LSB 5.0 45 # section 22.2) and hope the admin will scan the log for what 46 # actually happened. 47 48 # We have to sleep 2 seconds here because journald uses the pid to 49 # connect our log messages to the systemd service. This is critical 50 # for capturing all the log messages if the scrub fails, because the 51 # fail service uses the service name to gather log messages for the 52 # error report. 53 if [ -n "${SERVICE_MODE}" ]; then 54 test "${ret}" -ne 0 && ret=1 55 sleep 2 56 fi 57 58 exit "${ret}" 59} 60 61while getopts "ArV" opt; do 62 case "${opt}" in 63 "A") scrub_all=1;; 64 "r") scrub_args="${scrub_args} -r";; 65 "V") print_version; exitcode 0;; 66 *) print_help; exitcode 2;; 67 esac 68done 69shift "$((OPTIND - 1))" 70 71# Find scrub targets, make sure we only do this once. 72ls_scrub_targets() { 73 lsblk -o NAME,FSTYPE,MOUNTPOINT -p -P -n | while read vars; do 74 eval "${vars}" 75 76 # Skip non-ext[234] 77 case "${FSTYPE}" in 78 ext[234]) ;; 79 *) continue;; 80 esac 81 82 # Skip unmounted filesystems unless -A 83 if [ "${scrub_all}" -eq 0 ] && [ -z "${MOUNTPOINT}" ]; then 84 continue; 85 fi 86 87 # Skip non-lvm devices and lvm snapshots 88 lvm_vars="$(lvs --nameprefixes -o vg_name,lv_name,lv_role --noheadings "${NAME}" 2> /dev/null)" 89 test $? -ne 0 && continue 90 eval "${lvm_vars}" 91 echo "${LVM2_LV_ROLE}" | grep -q "snapshot" && continue 92 93 if [ -n "${MOUNTPOINT}" ]; then 94 echo "${MOUNTPOINT}" 95 else 96 echo "${NAME}" 97 fi 98 done | sort | uniq 99} 100 101# systemd doesn't know to do path escaping on the instance variable we pass 102# to the e2scrub service, which breaks things if there is a dash in the path 103# name. Therefore, do the path escaping ourselves if needed. 104escape_path_for_systemd() { 105 local path="$1" 106 107 if echo "${path}" | grep -q -- "-"; then 108 echo "-$(systemd-escape --path "${path}")" 109 else 110 echo "${path}" 111 fi 112} 113 114# Scrub any mounted fs on lvm by creating a snapshot and fscking that. 115stdin="$(realpath /dev/stdin)" 116ls_scrub_targets | while read tgt; do 117 # If we're not reaping and systemd is present, try invoking the 118 # systemd service. 119 if [ -z "${scrub_args}" ] && type systemctl > /dev/null 2>&1; then 120 tgt_esc="$(escape_path_for_systemd "${tgt}")" 121 ${DBG} systemctl start "e2scrub@${tgt_esc}" 2> /dev/null < "${stdin}" 122 res=$? 123 if [ "${res}" -eq 0 ] || [ "${res}" -eq 1 ]; then 124 continue; 125 fi 126 fi 127 128 # Otherwise use direct invocation 129 ${DBG} "@root_sbindir@/e2scrub" ${scrub_args} "${tgt}" < "${stdin}" 130done 131 132exitcode 0 133