xref: /aosp_15_r20/external/e2fsprogs/scrub/e2scrub.in (revision 6a54128f25917bfc36a8a6e9d722c04a0b4641b6)
1*6a54128fSAndroid Build Coastguard Worker#!/bin/bash
2*6a54128fSAndroid Build Coastguard Worker
3*6a54128fSAndroid Build Coastguard Worker#  Copyright (C) 2018 Oracle.  All Rights Reserved.
4*6a54128fSAndroid Build Coastguard Worker#
5*6a54128fSAndroid Build Coastguard Worker#  Author: Darrick J. Wong <[email protected]>
6*6a54128fSAndroid Build Coastguard Worker#
7*6a54128fSAndroid Build Coastguard Worker#  This program is free software; you can redistribute it and/or
8*6a54128fSAndroid Build Coastguard Worker#  modify it under the terms of the GNU General Public License
9*6a54128fSAndroid Build Coastguard Worker#  as published by the Free Software Foundation; either version 2
10*6a54128fSAndroid Build Coastguard Worker#  of the License, or (at your option) any later version.
11*6a54128fSAndroid Build Coastguard Worker#
12*6a54128fSAndroid Build Coastguard Worker#  This program is distributed in the hope that it would be useful,
13*6a54128fSAndroid Build Coastguard Worker#  but WITHOUT ANY WARRANTY; without even the implied warranty of
14*6a54128fSAndroid Build Coastguard Worker#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*6a54128fSAndroid Build Coastguard Worker#  GNU General Public License for more details.
16*6a54128fSAndroid Build Coastguard Worker#
17*6a54128fSAndroid Build Coastguard Worker#  You should have received a copy of the GNU General Public License
18*6a54128fSAndroid Build Coastguard Worker#  along with this program; if not, write the Free Software Foundation,
19*6a54128fSAndroid Build Coastguard Worker#  Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
20*6a54128fSAndroid Build Coastguard Worker
21*6a54128fSAndroid Build Coastguard Worker# Automatically check an LVM-managed filesystem online.
22*6a54128fSAndroid Build Coastguard Worker# We use lvm snapshots to do this, which means that we can only
23*6a54128fSAndroid Build Coastguard Worker# check filesystems in VGs that have at least 256MB (or so) of
24*6a54128fSAndroid Build Coastguard Worker# free space.
25*6a54128fSAndroid Build Coastguard Worker
26*6a54128fSAndroid Build Coastguard WorkerPATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
27*6a54128fSAndroid Build Coastguard Worker
28*6a54128fSAndroid Build Coastguard Workerif (( $EUID != 0 )); then
29*6a54128fSAndroid Build Coastguard Worker    echo "e2scrub must be run as root"
30*6a54128fSAndroid Build Coastguard Worker    exit 1
31*6a54128fSAndroid Build Coastguard Workerfi
32*6a54128fSAndroid Build Coastguard Worker
33*6a54128fSAndroid Build Coastguard Workersnap_size_mb=256
34*6a54128fSAndroid Build Coastguard Workerfstrim=0
35*6a54128fSAndroid Build Coastguard Workerreap=0
36*6a54128fSAndroid Build Coastguard Workere2fsck_opts=""
37*6a54128fSAndroid Build Coastguard Workerconffile="@root_sysconfdir@/e2scrub.conf"
38*6a54128fSAndroid Build Coastguard Worker
39*6a54128fSAndroid Build Coastguard Workertest -f "${conffile}" && . "${conffile}"
40*6a54128fSAndroid Build Coastguard Worker
41*6a54128fSAndroid Build Coastguard Workerprint_help() {
42*6a54128fSAndroid Build Coastguard Worker	echo "Usage: $0 [OPTIONS] mountpoint | device"
43*6a54128fSAndroid Build Coastguard Worker	echo
44*6a54128fSAndroid Build Coastguard Worker	echo "mountpoint must be on an LVM-managed block device"
45*6a54128fSAndroid Build Coastguard Worker	echo "-n: Show what commands e2scrub would execute."
46*6a54128fSAndroid Build Coastguard Worker	echo "-r: Remove e2scrub snapshot and exit, do not check anything."
47*6a54128fSAndroid Build Coastguard Worker	echo "-t: Run fstrim if successful."
48*6a54128fSAndroid Build Coastguard Worker	echo "-V: Print version information and exit."
49*6a54128fSAndroid Build Coastguard Worker}
50*6a54128fSAndroid Build Coastguard Worker
51*6a54128fSAndroid Build Coastguard Workerprint_version() {
52*6a54128fSAndroid Build Coastguard Worker	echo "e2scrub @E2FSPROGS_VERSION@ (@E2FSPROGS_DATE@)"
53*6a54128fSAndroid Build Coastguard Worker}
54*6a54128fSAndroid Build Coastguard Worker
55*6a54128fSAndroid Build Coastguard Workerexitcode() {
56*6a54128fSAndroid Build Coastguard Worker	ret="$1"
57*6a54128fSAndroid Build Coastguard Worker
58*6a54128fSAndroid Build Coastguard Worker	# If we're being run as a service, the return code must fit the LSB
59*6a54128fSAndroid Build Coastguard Worker	# init script action error guidelines, which is to say that we
60*6a54128fSAndroid Build Coastguard Worker	# compress all errors to 1 ("generic or unspecified error", LSB 5.0
61*6a54128fSAndroid Build Coastguard Worker	# section 22.2) and hope the admin will scan the log for what
62*6a54128fSAndroid Build Coastguard Worker	# actually happened.
63*6a54128fSAndroid Build Coastguard Worker
64*6a54128fSAndroid Build Coastguard Worker	# We have to sleep 2 seconds here because journald uses the pid to
65*6a54128fSAndroid Build Coastguard Worker	# connect our log messages to the systemd service.  This is critical
66*6a54128fSAndroid Build Coastguard Worker	# for capturing all the log messages if the scrub fails, because the
67*6a54128fSAndroid Build Coastguard Worker	# fail service uses the service name to gather log messages for the
68*6a54128fSAndroid Build Coastguard Worker	# error report.
69*6a54128fSAndroid Build Coastguard Worker	if [ -n "${SERVICE_MODE}" -a "${ret}" -ne 0 ]; then
70*6a54128fSAndroid Build Coastguard Worker		test "${ret}" -ne 0 && ret=1
71*6a54128fSAndroid Build Coastguard Worker		sleep 2
72*6a54128fSAndroid Build Coastguard Worker	fi
73*6a54128fSAndroid Build Coastguard Worker
74*6a54128fSAndroid Build Coastguard Worker	exit "${ret}"
75*6a54128fSAndroid Build Coastguard Worker}
76*6a54128fSAndroid Build Coastguard Worker
77*6a54128fSAndroid Build Coastguard Workerwhile getopts "nrtV" opt; do
78*6a54128fSAndroid Build Coastguard Worker    case "${opt}" in
79*6a54128fSAndroid Build Coastguard Worker	"n") DBG="echo Would execute: " ;;
80*6a54128fSAndroid Build Coastguard Worker	"r") reap=1;;
81*6a54128fSAndroid Build Coastguard Worker	"t") fstrim=1;;
82*6a54128fSAndroid Build Coastguard Worker	"V") print_version; exitcode 0;;
83*6a54128fSAndroid Build Coastguard Worker	*) print_help; exitcode 2;;
84*6a54128fSAndroid Build Coastguard Worker	esac
85*6a54128fSAndroid Build Coastguard Workerdone
86*6a54128fSAndroid Build Coastguard Workershift "$((OPTIND - 1))"
87*6a54128fSAndroid Build Coastguard Worker
88*6a54128fSAndroid Build Coastguard Workerarg="$1"
89*6a54128fSAndroid Build Coastguard Workerif [ -z "${arg}" ]; then
90*6a54128fSAndroid Build Coastguard Worker	print_help
91*6a54128fSAndroid Build Coastguard Worker	exitcode 1
92*6a54128fSAndroid Build Coastguard Workerfi
93*6a54128fSAndroid Build Coastguard Worker
94*6a54128fSAndroid Build Coastguard Workerif ! type lsblk >& /dev/null ; then
95*6a54128fSAndroid Build Coastguard Worker    echo "e2scrub: can't find lsblk --- is util-linux installed?"
96*6a54128fSAndroid Build Coastguard Worker    exitcode 1
97*6a54128fSAndroid Build Coastguard Workerfi
98*6a54128fSAndroid Build Coastguard Worker
99*6a54128fSAndroid Build Coastguard Workerif ! type lvcreate >& /dev/null ; then
100*6a54128fSAndroid Build Coastguard Worker    echo "e2scrub: can't find lvcreate --- is lvm2 installed?"
101*6a54128fSAndroid Build Coastguard Worker    exitcode 1
102*6a54128fSAndroid Build Coastguard Workerfi
103*6a54128fSAndroid Build Coastguard Worker
104*6a54128fSAndroid Build Coastguard Worker# close file descriptor 3 (from cron) since it causes lvm to kvetch
105*6a54128fSAndroid Build Coastguard Workerexec 3<&-
106*6a54128fSAndroid Build Coastguard Worker
107*6a54128fSAndroid Build Coastguard Worker# Find the device for a given mountpoint
108*6a54128fSAndroid Build Coastguard Workerdev_from_mount() {
109*6a54128fSAndroid Build Coastguard Worker	local mountpt="$(realpath "$1")"
110*6a54128fSAndroid Build Coastguard Worker
111*6a54128fSAndroid Build Coastguard Worker	lsblk -o NAME,FSTYPE,MOUNTPOINT -p -P -n 2> /dev/null | while read vars; do
112*6a54128fSAndroid Build Coastguard Worker		eval "${vars}"
113*6a54128fSAndroid Build Coastguard Worker		if [ "${mountpt}" != "${MOUNTPOINT}" ]; then
114*6a54128fSAndroid Build Coastguard Worker			continue
115*6a54128fSAndroid Build Coastguard Worker		fi
116*6a54128fSAndroid Build Coastguard Worker		case "${FSTYPE}" in
117*6a54128fSAndroid Build Coastguard Worker		ext[234])
118*6a54128fSAndroid Build Coastguard Worker			echo "${NAME}"
119*6a54128fSAndroid Build Coastguard Worker			return 0
120*6a54128fSAndroid Build Coastguard Worker			;;
121*6a54128fSAndroid Build Coastguard Worker		esac
122*6a54128fSAndroid Build Coastguard Worker	done
123*6a54128fSAndroid Build Coastguard Worker	return 1
124*6a54128fSAndroid Build Coastguard Worker}
125*6a54128fSAndroid Build Coastguard Worker
126*6a54128fSAndroid Build Coastguard Worker# Check a device argument
127*6a54128fSAndroid Build Coastguard Workerdev_from_arg() {
128*6a54128fSAndroid Build Coastguard Worker	local dev="$1"
129*6a54128fSAndroid Build Coastguard Worker	local fstype="$(lsblk -o FSTYPE -n "${dev}" 2> /dev/null)"
130*6a54128fSAndroid Build Coastguard Worker
131*6a54128fSAndroid Build Coastguard Worker	case "${fstype}" in
132*6a54128fSAndroid Build Coastguard Worker	ext[234])
133*6a54128fSAndroid Build Coastguard Worker		echo "${dev}"
134*6a54128fSAndroid Build Coastguard Worker		return 0
135*6a54128fSAndroid Build Coastguard Worker		;;
136*6a54128fSAndroid Build Coastguard Worker	esac
137*6a54128fSAndroid Build Coastguard Worker	return 1
138*6a54128fSAndroid Build Coastguard Worker}
139*6a54128fSAndroid Build Coastguard Worker
140*6a54128fSAndroid Build Coastguard Workermnt_from_dev() {
141*6a54128fSAndroid Build Coastguard Worker	local dev="$1"
142*6a54128fSAndroid Build Coastguard Worker
143*6a54128fSAndroid Build Coastguard Worker	if [ -n "${dev}" ]; then
144*6a54128fSAndroid Build Coastguard Worker		lsblk -o MOUNTPOINT -n "${dev}"
145*6a54128fSAndroid Build Coastguard Worker	fi
146*6a54128fSAndroid Build Coastguard Worker}
147*6a54128fSAndroid Build Coastguard Worker
148*6a54128fSAndroid Build Coastguard Worker# Construct block device path and mountpoint from argument
149*6a54128fSAndroid Build Coastguard Workerif [ -b "${arg}" ]; then
150*6a54128fSAndroid Build Coastguard Worker	dev="$(dev_from_arg "${arg}")"
151*6a54128fSAndroid Build Coastguard Worker	mnt="$(mnt_from_dev "${dev}")"
152*6a54128fSAndroid Build Coastguard Workerelse
153*6a54128fSAndroid Build Coastguard Worker	dev="$(dev_from_mount "${arg}")"
154*6a54128fSAndroid Build Coastguard Worker	mnt="${arg}"
155*6a54128fSAndroid Build Coastguard Workerfi
156*6a54128fSAndroid Build Coastguard Workerif [ ! -e "${dev}" ]; then
157*6a54128fSAndroid Build Coastguard Worker	echo "${arg}: Not an ext[234] filesystem."
158*6a54128fSAndroid Build Coastguard Worker	print_help
159*6a54128fSAndroid Build Coastguard Worker	exitcode 16
160*6a54128fSAndroid Build Coastguard Workerfi
161*6a54128fSAndroid Build Coastguard Worker
162*6a54128fSAndroid Build Coastguard Worker# Make sure this is an LVM device we can snapshot
163*6a54128fSAndroid Build Coastguard Workerlvm_vars="$(lvs --nameprefixes -o name,vgname,lv_role --noheadings "${dev}" 2> /dev/null)"
164*6a54128fSAndroid Build Coastguard Workereval "${lvm_vars}"
165*6a54128fSAndroid Build Coastguard Workerif [ -z "${LVM2_VG_NAME}" ] || [ -z "${LVM2_LV_NAME}" ] ||
166*6a54128fSAndroid Build Coastguard Worker   echo "${LVM2_LV_ROLE}" | grep -q "snapshot"; then
167*6a54128fSAndroid Build Coastguard Worker	echo "${arg}: Not connected to an LVM logical volume."
168*6a54128fSAndroid Build Coastguard Worker	print_help
169*6a54128fSAndroid Build Coastguard Worker	exitcode 16
170*6a54128fSAndroid Build Coastguard Workerfi
171*6a54128fSAndroid Build Coastguard Workerstart_time="$(date +'%Y%m%d%H%M%S')"
172*6a54128fSAndroid Build Coastguard Workersnap="${LVM2_LV_NAME}.e2scrub"
173*6a54128fSAndroid Build Coastguard Workersnap_dev="/dev/${LVM2_VG_NAME}/${snap}"
174*6a54128fSAndroid Build Coastguard Worker
175*6a54128fSAndroid Build Coastguard Workerteardown() {
176*6a54128fSAndroid Build Coastguard Worker	# Remove and wait for removal to succeed.
177*6a54128fSAndroid Build Coastguard Worker	${DBG} lvremove -f "${LVM2_VG_NAME}/${snap}"
178*6a54128fSAndroid Build Coastguard Worker	while [ -e "${snap_dev}" ] && [ "$?" -eq "5" ]; do
179*6a54128fSAndroid Build Coastguard Worker		sleep 0.5
180*6a54128fSAndroid Build Coastguard Worker		${DBG} lvremove -f "${LVM2_VG_NAME}/${snap}"
181*6a54128fSAndroid Build Coastguard Worker	done
182*6a54128fSAndroid Build Coastguard Worker}
183*6a54128fSAndroid Build Coastguard Worker
184*6a54128fSAndroid Build Coastguard Workercheck() {
185*6a54128fSAndroid Build Coastguard Worker	# First we recover the journal, then we see if e2fsck tries any
186*6a54128fSAndroid Build Coastguard Worker	# non-optimization repairs.  If either of these two returns a
187*6a54128fSAndroid Build Coastguard Worker	# non-zero status (errors fixed or remaining) then this fs is bad.
188*6a54128fSAndroid Build Coastguard Worker	E2FSCK_FIXES_ONLY=1
189*6a54128fSAndroid Build Coastguard Worker	export E2FSCK_FIXES_ONLY
190*6a54128fSAndroid Build Coastguard Worker	${DBG} "@root_sbindir@/e2fsck" -E journal_only -p ${e2fsck_opts} "${snap_dev}" || return $?
191*6a54128fSAndroid Build Coastguard Worker	${DBG} "@root_sbindir@/e2fsck" -f -y ${e2fsck_opts} "${snap_dev}"
192*6a54128fSAndroid Build Coastguard Worker}
193*6a54128fSAndroid Build Coastguard Worker
194*6a54128fSAndroid Build Coastguard Workermark_clean() {
195*6a54128fSAndroid Build Coastguard Worker	${DBG} "@root_sbindir@/tune2fs" -C 0 -T "${start_time}" "${dev}"
196*6a54128fSAndroid Build Coastguard Worker}
197*6a54128fSAndroid Build Coastguard Worker
198*6a54128fSAndroid Build Coastguard Workermark_corrupt() {
199*6a54128fSAndroid Build Coastguard Worker	${DBG} "@root_sbindir@/tune2fs" -E force_fsck "${dev}"
200*6a54128fSAndroid Build Coastguard Worker}
201*6a54128fSAndroid Build Coastguard Worker
202*6a54128fSAndroid Build Coastguard Workersetup() {
203*6a54128fSAndroid Build Coastguard Worker	# Try to remove snapshot for 30s, bail out if we can't remove it.
204*6a54128fSAndroid Build Coastguard Worker	lvremove_deadline="$(( $(date "+%s") + 30))"
205*6a54128fSAndroid Build Coastguard Worker	${DBG} lvremove -f "${LVM2_VG_NAME}/${snap}" 2>/dev/null
206*6a54128fSAndroid Build Coastguard Worker	while [ -e "${snap_dev}" ] && [ "$?" -eq "5" ] &&
207*6a54128fSAndroid Build Coastguard Worker	      [ "$(date "+%s")" -lt "${lvremove_deadline}" ]; do
208*6a54128fSAndroid Build Coastguard Worker		sleep 0.5
209*6a54128fSAndroid Build Coastguard Worker		${DBG} lvremove -f "${LVM2_VG_NAME}/${snap}"
210*6a54128fSAndroid Build Coastguard Worker	done
211*6a54128fSAndroid Build Coastguard Worker	if [ -e "${snap_dev}" ]; then
212*6a54128fSAndroid Build Coastguard Worker		echo "${arg}: e2scrub snapshot is in use, cannot check!"
213*6a54128fSAndroid Build Coastguard Worker		return 1
214*6a54128fSAndroid Build Coastguard Worker	fi
215*6a54128fSAndroid Build Coastguard Worker	# Create the snapshot, wait for device to appear.
216*6a54128fSAndroid Build Coastguard Worker	${DBG} lvcreate -s -L "${snap_size_mb}m" -n "${snap}" "${LVM2_VG_NAME}/${LVM2_LV_NAME}"
217*6a54128fSAndroid Build Coastguard Worker	if [ $? -ne 0 ]; then
218*6a54128fSAndroid Build Coastguard Worker		echo "${arg}: e2scrub snapshot FAILED, will not check!"
219*6a54128fSAndroid Build Coastguard Worker		return 1
220*6a54128fSAndroid Build Coastguard Worker	fi
221*6a54128fSAndroid Build Coastguard Worker	${DBG} udevadm settle 2> /dev/null
222*6a54128fSAndroid Build Coastguard Worker	return 0
223*6a54128fSAndroid Build Coastguard Worker}
224*6a54128fSAndroid Build Coastguard Worker
225*6a54128fSAndroid Build Coastguard Workerif [ "${reap}" -gt 0 ]; then
226*6a54128fSAndroid Build Coastguard Worker	if [ -e "${snap_dev}" ]; then
227*6a54128fSAndroid Build Coastguard Worker		teardown 2> /dev/null
228*6a54128fSAndroid Build Coastguard Worker	fi
229*6a54128fSAndroid Build Coastguard Worker	exit 0
230*6a54128fSAndroid Build Coastguard Workerfi
231*6a54128fSAndroid Build Coastguard Workerif ! setup; then
232*6a54128fSAndroid Build Coastguard Worker	exitcode 8
233*6a54128fSAndroid Build Coastguard Workerfi
234*6a54128fSAndroid Build Coastguard Workertrap "teardown; exit 1" EXIT INT QUIT TERM
235*6a54128fSAndroid Build Coastguard Worker
236*6a54128fSAndroid Build Coastguard Worker# Check and react
237*6a54128fSAndroid Build Coastguard Workercheck
238*6a54128fSAndroid Build Coastguard Workercase "$?" in
239*6a54128fSAndroid Build Coastguard Worker"0")
240*6a54128fSAndroid Build Coastguard Worker	# Clean check!
241*6a54128fSAndroid Build Coastguard Worker	echo "${arg}: Scrub succeeded."
242*6a54128fSAndroid Build Coastguard Worker	mark_clean
243*6a54128fSAndroid Build Coastguard Worker	teardown
244*6a54128fSAndroid Build Coastguard Worker	trap '' EXIT
245*6a54128fSAndroid Build Coastguard Worker
246*6a54128fSAndroid Build Coastguard Worker	# Trim the free space, which requires the snapshot be deleted.
247*6a54128fSAndroid Build Coastguard Worker	if [ "${fstrim}" -eq 1 ] && [ -d "${mnt}" ] && type fstrim > /dev/null 2>&1; then
248*6a54128fSAndroid Build Coastguard Worker		echo "${arg}: Trimming free space."
249*6a54128fSAndroid Build Coastguard Worker		fstrim -v "${mnt}"
250*6a54128fSAndroid Build Coastguard Worker	fi
251*6a54128fSAndroid Build Coastguard Worker
252*6a54128fSAndroid Build Coastguard Worker	ret=0
253*6a54128fSAndroid Build Coastguard Worker	;;
254*6a54128fSAndroid Build Coastguard Worker"8")
255*6a54128fSAndroid Build Coastguard Worker	# Operational error, what now?
256*6a54128fSAndroid Build Coastguard Worker	echo "${arg}: e2fsck operational error."
257*6a54128fSAndroid Build Coastguard Worker	teardown
258*6a54128fSAndroid Build Coastguard Worker	trap '' EXIT
259*6a54128fSAndroid Build Coastguard Worker	ret=8
260*6a54128fSAndroid Build Coastguard Worker	;;
261*6a54128fSAndroid Build Coastguard Worker*)
262*6a54128fSAndroid Build Coastguard Worker	# fsck failed.  Check if the snapshot is invalid; if so, make a
263*6a54128fSAndroid Build Coastguard Worker	# note of that at the end of the log.  This isn't necessarily a
264*6a54128fSAndroid Build Coastguard Worker	# failure because the mounted fs could have overflowed the
265*6a54128fSAndroid Build Coastguard Worker	# snapshot with regular disk writes /or/ our repair process
266*6a54128fSAndroid Build Coastguard Worker	# could have done it by repairing too much.
267*6a54128fSAndroid Build Coastguard Worker	#
268*6a54128fSAndroid Build Coastguard Worker	# If it's really corrupt we ought to fsck at next boot.
269*6a54128fSAndroid Build Coastguard Worker	is_invalid="$(lvs -o lv_snapshot_invalid --noheadings "${snap_dev}" | awk '{print $1}')"
270*6a54128fSAndroid Build Coastguard Worker	if [ -n "${is_invalid}" ]; then
271*6a54128fSAndroid Build Coastguard Worker		echo "${arg}: Scrub FAILED due to invalid snapshot."
272*6a54128fSAndroid Build Coastguard Worker		ret=8
273*6a54128fSAndroid Build Coastguard Worker	else
274*6a54128fSAndroid Build Coastguard Worker		echo "${arg}: Scrub FAILED due to corruption!  Unmount and run e2fsck -y."
275*6a54128fSAndroid Build Coastguard Worker		mark_corrupt
276*6a54128fSAndroid Build Coastguard Worker		ret=6
277*6a54128fSAndroid Build Coastguard Worker	fi
278*6a54128fSAndroid Build Coastguard Worker	teardown
279*6a54128fSAndroid Build Coastguard Worker	trap '' EXIT
280*6a54128fSAndroid Build Coastguard Worker	;;
281*6a54128fSAndroid Build Coastguard Workeresac
282*6a54128fSAndroid Build Coastguard Worker
283*6a54128fSAndroid Build Coastguard Workerexitcode "${ret}"
284