fsadm.sh revision 1.1.1.2 1 1.1 haad #!/bin/sh
2 1.1 haad #
3 1.1.1.2 haad # Copyright (C) 2007-2009 Red Hat, Inc. All rights reserved.
4 1.1 haad #
5 1.1 haad # This file is part of LVM2.
6 1.1 haad #
7 1.1 haad # This copyrighted material is made available to anyone wishing to use,
8 1.1 haad # modify, copy, or redistribute it subject to the terms and conditions
9 1.1 haad # of the GNU General Public License v.2.
10 1.1 haad #
11 1.1 haad # You should have received a copy of the GNU General Public License
12 1.1 haad # along with this program; if not, write to the Free Software Foundation,
13 1.1 haad # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 1.1 haad #
15 1.1 haad # Author: Zdenek Kabelac <zkabelac at redhat.com>
16 1.1 haad #
17 1.1 haad # Script for resizing devices (usable for LVM resize)
18 1.1 haad #
19 1.1 haad # Needed utilities:
20 1.1 haad # mount, umount, grep, readlink, blockdev, blkid, fsck, xfs_check
21 1.1 haad #
22 1.1.1.2 haad # ext2/ext3/ext4: resize2fs, tune2fs
23 1.1 haad # reiserfs: resize_reiserfs, reiserfstune
24 1.1 haad # xfs: xfs_growfs, xfs_info
25 1.1 haad #
26 1.1 haad
27 1.1 haad TOOL=fsadm
28 1.1 haad
29 1.1 haad PATH=/sbin:/usr/sbin:/bin:/usr/sbin:$PATH
30 1.1 haad
31 1.1 haad # utilities
32 1.1 haad TUNE_EXT=tune2fs
33 1.1 haad RESIZE_EXT=resize2fs
34 1.1 haad TUNE_REISER=reiserfstune
35 1.1 haad RESIZE_REISER=resize_reiserfs
36 1.1 haad TUNE_XFS=xfs_info
37 1.1 haad RESIZE_XFS=xfs_growfs
38 1.1 haad
39 1.1 haad MOUNT=mount
40 1.1 haad UMOUNT=umount
41 1.1 haad MKDIR=mkdir
42 1.1 haad RMDIR=rmdir
43 1.1 haad BLOCKDEV=blockdev
44 1.1 haad BLKID=blkid
45 1.1 haad GREP=grep
46 1.1 haad READLINK=readlink
47 1.1 haad READLINK_E="-e"
48 1.1 haad FSCK=fsck
49 1.1 haad XFS_CHECK=xfs_check
50 1.1 haad
51 1.1.1.2 haad # user may override lvm location by setting LVM_BINARY
52 1.1.1.2 haad LVM=${LVM_BINARY-lvm}
53 1.1 haad
54 1.1 haad YES=
55 1.1 haad DRY=0
56 1.1 haad VERB=
57 1.1 haad FORCE=
58 1.1 haad EXTOFF=0
59 1.1 haad DO_LVRESIZE=0
60 1.1 haad FSTYPE=unknown
61 1.1 haad VOLUME=unknown
62 1.1 haad TEMPDIR="${TMPDIR:-/tmp}/${TOOL}_${RANDOM}$$/m"
63 1.1 haad BLOCKSIZE=
64 1.1 haad BLOCKCOUNT=
65 1.1 haad MOUNTPOINT=
66 1.1 haad MOUNTED=
67 1.1 haad REMOUNT=
68 1.1 haad
69 1.1 haad IFS_OLD=$IFS
70 1.1.1.2 haad # without bash $'\n'
71 1.1.1.2 haad NL='
72 1.1.1.2 haad '
73 1.1 haad
74 1.1 haad tool_usage() {
75 1.1 haad echo "${TOOL}: Utility to resize or check the filesystem on a device"
76 1.1 haad echo
77 1.1 haad echo " ${TOOL} [options] check device"
78 1.1 haad echo " - Check the filesystem on device using fsck"
79 1.1 haad echo
80 1.1 haad echo " ${TOOL} [options] resize device [new_size[BKMGTPE]]"
81 1.1 haad echo " - Change the size of the filesystem on device to new_size"
82 1.1 haad echo
83 1.1 haad echo " Options:"
84 1.1 haad echo " -h | --help Show this help message"
85 1.1 haad echo " -v | --verbose Be verbose"
86 1.1.1.2 haad echo " -e | --ext-offline unmount filesystem before ext2/ext3/ext4 resize"
87 1.1 haad echo " -f | --force Bypass sanity checks"
88 1.1 haad echo " -n | --dry-run Print commands without running them"
89 1.1 haad echo " -l | --lvresize Resize given device (if it is LVM device)"
90 1.1 haad echo " -y | --yes Answer \"yes\" at any prompts"
91 1.1 haad echo
92 1.1 haad echo " new_size - Absolute number of filesystem blocks to be in the filesystem,"
93 1.1 haad echo " or an absolute size using a suffix (in powers of 1024)."
94 1.1 haad echo " If new_size is not supplied, the whole device is used."
95 1.1 haad
96 1.1 haad exit
97 1.1 haad }
98 1.1 haad
99 1.1 haad verbose() {
100 1.1 haad test -n "$VERB" && echo "$TOOL: $@" || true
101 1.1 haad }
102 1.1 haad
103 1.1 haad error() {
104 1.1 haad echo "$TOOL: $@" >&2
105 1.1 haad cleanup 1
106 1.1 haad }
107 1.1 haad
108 1.1 haad dry() {
109 1.1 haad if [ "$DRY" -ne 0 ]; then
110 1.1 haad verbose "Dry execution $@"
111 1.1 haad return 0
112 1.1 haad fi
113 1.1 haad verbose "Executing $@"
114 1.1 haad $@
115 1.1 haad }
116 1.1 haad
117 1.1 haad cleanup() {
118 1.1 haad trap '' 2
119 1.1 haad # reset MOUNTPOINT - avoid recursion
120 1.1 haad test "$MOUNTPOINT" = "$TEMPDIR" && MOUNTPOINT="" temp_umount
121 1.1 haad if [ -n "$REMOUNT" ]; then
122 1.1 haad verbose "Remounting unmounted filesystem back"
123 1.1 haad dry $MOUNT "$VOLUME" "$MOUNTED"
124 1.1 haad fi
125 1.1 haad IFS=$IFS_OLD
126 1.1 haad trap 2
127 1.1 haad
128 1.1 haad # start LVRESIZE with the filesystem modification flag
129 1.1 haad # and allow recursive call of fsadm
130 1.1 haad unset FSADM_RUNNING
131 1.1.1.2 haad test "$DO_LVRESIZE" -eq 2 && exec $LVM lvresize $VERB -r -L$(( $NEWSIZE / 1048576 )) $VOLUME
132 1.1 haad exit ${1:-0}
133 1.1 haad }
134 1.1 haad
135 1.1 haad # convert parameter from Exa/Peta/Tera/Giga/Mega/Kilo/Bytes and blocks
136 1.1 haad # (2^(60/50/40/30/20/10/0))
137 1.1 haad decode_size() {
138 1.1 haad case "$1" in
139 1.1 haad *[eE]) NEWSIZE=$(( ${1%[eE]} * 1152921504606846976 )) ;;
140 1.1 haad *[pP]) NEWSIZE=$(( ${1%[pP]} * 1125899906842624 )) ;;
141 1.1 haad *[tT]) NEWSIZE=$(( ${1%[tT]} * 1099511627776 )) ;;
142 1.1 haad *[gG]) NEWSIZE=$(( ${1%[gG]} * 1073741824 )) ;;
143 1.1 haad *[mM]) NEWSIZE=$(( ${1%[mM]} * 1048576 )) ;;
144 1.1 haad *[kK]) NEWSIZE=$(( ${1%[kK]} * 1024 )) ;;
145 1.1 haad *[bB]) NEWSIZE=${1%[bB]} ;;
146 1.1 haad *) NEWSIZE=$(( $1 * $2 )) ;;
147 1.1 haad esac
148 1.1 haad #NEWBLOCKCOUNT=$(round_block_size $NEWSIZE $2)
149 1.1 haad NEWBLOCKCOUNT=$(( $NEWSIZE / $2 ))
150 1.1 haad
151 1.1 haad if [ $DO_LVRESIZE -eq 1 ]; then
152 1.1 haad # start lvresize, but first cleanup mounted dirs
153 1.1 haad DO_LVRESIZE=2
154 1.1 haad cleanup 0
155 1.1 haad fi
156 1.1 haad }
157 1.1 haad
158 1.1 haad # detect filesystem on the given device
159 1.1 haad # dereference device name if it is symbolic link
160 1.1 haad detect_fs() {
161 1.1 haad VOLUME=${1#/dev/}
162 1.1.1.2 haad VOLUME=$($READLINK $READLINK_E "/dev/$VOLUME") || error "Cannot get readlink $1"
163 1.1.1.2 haad # strip newline from volume name
164 1.1.1.2 haad VOLUME=${VOLUME%%$NL}
165 1.1 haad # use /dev/null as cache file to be sure about the result
166 1.1.1.2 haad # not using option '-o value' to be compatible with older version of blkid
167 1.1.1.2 haad FSTYPE=$($BLKID -c /dev/null -s TYPE "$VOLUME") || error "Cannot get FSTYPE of \"$VOLUME\""
168 1.1.1.2 haad FSTYPE=${FSTYPE##*TYPE=\"} # cut quotation marks
169 1.1.1.2 haad FSTYPE=${FSTYPE%%\"*}
170 1.1 haad verbose "\"$FSTYPE\" filesystem found on \"$VOLUME\""
171 1.1 haad }
172 1.1 haad
173 1.1 haad # check if the given device is already mounted and where
174 1.1 haad detect_mounted() {
175 1.1 haad $MOUNT >/dev/null || error "Cannot detect mounted device $VOLUME"
176 1.1 haad MOUNTED=$($MOUNT | $GREP "$VOLUME")
177 1.1 haad MOUNTED=${MOUNTED##* on }
178 1.1 haad MOUNTED=${MOUNTED% type *} # allow type in the mount name
179 1.1 haad test -n "$MOUNTED"
180 1.1 haad }
181 1.1 haad
182 1.1 haad # get the full size of device in bytes
183 1.1 haad detect_device_size() {
184 1.1 haad # check if blockdev supports getsize64
185 1.1.1.2 haad $BLOCKDEV 2>&1 | $GREP getsize64 >/dev/null
186 1.1.1.2 haad if test $? -eq 0; then
187 1.1 haad DEVSIZE=$($BLOCKDEV --getsize64 "$VOLUME") || error "Cannot read size of device \"$VOLUME\""
188 1.1 haad else
189 1.1 haad DEVSIZE=$($BLOCKDEV --getsize "$VOLUME") || error "Cannot read size of device \"$VOLUME\""
190 1.1 haad SSSIZE=$($BLOCKDEV --getss "$VOLUME") || error "Cannot block size read device \"$VOLUME\""
191 1.1 haad DEVSIZE=$(($DEVSIZE * $SSSIZE))
192 1.1 haad fi
193 1.1 haad }
194 1.1 haad
195 1.1 haad # round up $1 / $2
196 1.1 haad # could be needed to gaurantee 'at least given size'
197 1.1 haad # but it makes many troubles
198 1.1 haad round_up_block_size() {
199 1.1 haad echo $(( ($1 + $2 - 1) / $2 ))
200 1.1 haad }
201 1.1 haad
202 1.1 haad temp_mount() {
203 1.1 haad dry $MKDIR -p -m 0000 "$TEMPDIR" || error "Failed to create $TEMPDIR"
204 1.1 haad dry $MOUNT "$VOLUME" "$TEMPDIR" || error "Failed to mount $TEMPDIR"
205 1.1 haad }
206 1.1 haad
207 1.1 haad temp_umount() {
208 1.1 haad dry $UMOUNT "$TEMPDIR" || error "Failed to umount $TEMPDIR"
209 1.1 haad dry $RMDIR "${TEMPDIR}" || error "Failed to remove $TEMPDIR"
210 1.1 haad dry $RMDIR "${TEMPDIR%%m}" || error "Failed to remove ${TEMPDIR%%m}"
211 1.1 haad }
212 1.1 haad
213 1.1 haad yes_no() {
214 1.1 haad echo -n "$@? [Y|n] "
215 1.1.1.2 haad
216 1.1 haad if [ -n "$YES" ]; then
217 1.1.1.2 haad echo y ; return 0
218 1.1 haad fi
219 1.1.1.2 haad
220 1.1.1.2 haad while read -r -s -n 1 ANS ; do
221 1.1.1.2 haad case "$ANS" in
222 1.1.1.2 haad "y" | "Y" | "") echo y ; return 0 ;;
223 1.1.1.2 haad "n" | "N") echo n ; return 1 ;;
224 1.1.1.2 haad esac
225 1.1.1.2 haad done
226 1.1 haad }
227 1.1 haad
228 1.1 haad try_umount() {
229 1.1 haad yes_no "Do you want to unmount \"$MOUNTED\"" && dry $UMOUNT "$MOUNTED" && return 0
230 1.1.1.2 haad error "Can not proceed with mounted filesystem \"$MOUNTED\""
231 1.1 haad }
232 1.1 haad
233 1.1 haad validate_parsing() {
234 1.1 haad test -n "$BLOCKSIZE" -a -n "$BLOCKCOUNT" || error "Cannot parse $1 output"
235 1.1 haad }
236 1.1 haad ####################################
237 1.1.1.2 haad # Resize ext2/ext3/ext4 filesystem
238 1.1 haad # - unmounted or mounted for upsize
239 1.1 haad # - unmounted for downsize
240 1.1 haad ####################################
241 1.1 haad resize_ext() {
242 1.1 haad verbose "Parsing $TUNE_EXT -l \"$VOLUME\""
243 1.1 haad for i in $($TUNE_EXT -l "$VOLUME"); do
244 1.1 haad case "$i" in
245 1.1 haad "Block size"*) BLOCKSIZE=${i##* } ;;
246 1.1 haad "Block count"*) BLOCKCOUNT=${i##* } ;;
247 1.1 haad esac
248 1.1 haad done
249 1.1 haad validate_parsing $TUNE_EXT
250 1.1 haad decode_size $1 $BLOCKSIZE
251 1.1 haad FSFORCE=$FORCE
252 1.1 haad
253 1.1 haad if [ "$NEWBLOCKCOUNT" -lt "$BLOCKCOUNT" -o "$EXTOFF" -eq 1 ]; then
254 1.1 haad detect_mounted && verbose "$RESIZE_EXT needs unmounted filesystem" && try_umount
255 1.1 haad REMOUNT=$MOUNTED
256 1.1 haad # CHECKME: after umount resize2fs requires fsck or -f flag.
257 1.1 haad FSFORCE="-f"
258 1.1 haad fi
259 1.1 haad
260 1.1.1.2 haad verbose "Resizing filesystem on device \"$VOLUME\" to $NEWSIZE bytes ($BLOCKCOUNT -> $NEWBLOCKCOUNT blocks of $BLOCKSIZE bytes)"
261 1.1 haad dry $RESIZE_EXT $FSFORCE "$VOLUME" $NEWBLOCKCOUNT
262 1.1 haad }
263 1.1 haad
264 1.1 haad #############################
265 1.1 haad # Resize reiserfs filesystem
266 1.1 haad # - unmounted for upsize
267 1.1 haad # - unmounted for downsize
268 1.1 haad #############################
269 1.1 haad resize_reiser() {
270 1.1.1.2 haad detect_mounted && verbose "ReiserFS resizes only unmounted filesystem" && try_umount
271 1.1.1.2 haad REMOUNT=$MOUNTED
272 1.1 haad verbose "Parsing $TUNE_REISER \"$VOLUME\""
273 1.1 haad for i in $($TUNE_REISER "$VOLUME"); do
274 1.1 haad case "$i" in
275 1.1 haad "Blocksize"*) BLOCKSIZE=${i##*: } ;;
276 1.1 haad "Count of blocks"*) BLOCKCOUNT=${i##*: } ;;
277 1.1 haad esac
278 1.1 haad done
279 1.1 haad validate_parsing $TUNE_REISER
280 1.1 haad decode_size $1 $BLOCKSIZE
281 1.1 haad verbose "Resizing \"$VOLUME\" $BLOCKCOUNT -> $NEWBLOCKCOUNT blocks ($NEWSIZE bytes, bs: $NEWBLOCKCOUNT)"
282 1.1 haad if [ -n "$YES" ]; then
283 1.1 haad dry echo y | $RESIZE_REISER -s $NEWSIZE "$VOLUME"
284 1.1 haad else
285 1.1 haad dry $RESIZE_REISER -s $NEWSIZE "$VOLUME"
286 1.1 haad fi
287 1.1 haad }
288 1.1 haad
289 1.1 haad ########################
290 1.1 haad # Resize XFS filesystem
291 1.1 haad # - mounted for upsize
292 1.1 haad # - can not downsize
293 1.1 haad ########################
294 1.1 haad resize_xfs() {
295 1.1 haad detect_mounted
296 1.1 haad MOUNTPOINT=$MOUNTED
297 1.1 haad if [ -z "$MOUNTED" ]; then
298 1.1 haad MOUNTPOINT=$TEMPDIR
299 1.1 haad temp_mount || error "Cannot mount Xfs filesystem"
300 1.1 haad fi
301 1.1 haad verbose "Parsing $TUNE_XFS \"$MOUNTPOINT\""
302 1.1 haad for i in $($TUNE_XFS "$MOUNTPOINT"); do
303 1.1 haad case "$i" in
304 1.1 haad "data"*) BLOCKSIZE=${i##*bsize=} ; BLOCKCOUNT=${i##*blocks=} ;;
305 1.1 haad esac
306 1.1 haad done
307 1.1 haad BLOCKSIZE=${BLOCKSIZE%%[^0-9]*}
308 1.1 haad BLOCKCOUNT=${BLOCKCOUNT%%[^0-9]*}
309 1.1 haad validate_parsing $TUNE_XFS
310 1.1 haad decode_size $1 $BLOCKSIZE
311 1.1 haad if [ $NEWBLOCKCOUNT -gt $BLOCKCOUNT ]; then
312 1.1 haad verbose "Resizing Xfs mounted on \"$MOUNTPOINT\" to fill device \"$VOLUME\""
313 1.1 haad dry $RESIZE_XFS $MOUNTPOINT
314 1.1 haad elif [ $NEWBLOCKCOUNT -eq $BLOCKCOUNT ]; then
315 1.1 haad verbose "Xfs filesystem already has the right size"
316 1.1 haad else
317 1.1 haad error "Xfs filesystem shrinking is unsupported"
318 1.1 haad fi
319 1.1 haad }
320 1.1 haad
321 1.1 haad ####################
322 1.1 haad # Resize filesystem
323 1.1 haad ####################
324 1.1 haad resize() {
325 1.1 haad NEWSIZE=$2
326 1.1 haad detect_fs "$1"
327 1.1 haad detect_device_size
328 1.1.1.2 haad verbose "Device \"$VOLUME\" size is $DEVSIZE bytes"
329 1.1 haad # if the size parameter is missing use device size
330 1.1 haad #if [ -n "$NEWSIZE" -a $NEWSIZE <
331 1.1 haad test -z "$NEWSIZE" && NEWSIZE=${DEVSIZE}b
332 1.1 haad trap cleanup 2
333 1.1.1.2 haad IFS=$NL
334 1.1 haad case "$FSTYPE" in
335 1.1.1.2 haad "ext3"|"ext2"|"ext4") resize_ext $NEWSIZE ;;
336 1.1 haad "reiserfs") resize_reiser $NEWSIZE ;;
337 1.1 haad "xfs") resize_xfs $NEWSIZE ;;
338 1.1 haad *) error "Filesystem \"$FSTYPE\" on device \"$VOLUME\" is not supported by this tool" ;;
339 1.1 haad esac || error "Resize $FSTYPE failed"
340 1.1 haad cleanup 0
341 1.1 haad }
342 1.1 haad
343 1.1 haad ###################
344 1.1 haad # Check filesystem
345 1.1 haad ###################
346 1.1 haad check() {
347 1.1 haad detect_fs "$1"
348 1.1.1.2 haad detect_mounted && error "Can not fsck device \"$VOLUME\", filesystem mounted on $MOUNTED"
349 1.1 haad case "$FSTYPE" in
350 1.1 haad "xfs") dry $XFS_CHECK "$VOLUME" ;;
351 1.1 haad *) dry $FSCK $YES "$VOLUME" ;;
352 1.1 haad esac
353 1.1 haad }
354 1.1 haad
355 1.1 haad #############################
356 1.1 haad # start point of this script
357 1.1 haad # - parsing parameters
358 1.1 haad #############################
359 1.1 haad
360 1.1 haad # test if we are not invoked recursively
361 1.1 haad test -n "$FSADM_RUNNING" && exit 0
362 1.1 haad
363 1.1 haad # test some prerequisities
364 1.1 haad test -n "$TUNE_EXT" -a -n "$RESIZE_EXT" -a -n "$TUNE_REISER" -a -n "$RESIZE_REISER" \
365 1.1 haad -a -n "$TUNE_XFS" -a -n "$RESIZE_XFS" -a -n "$MOUNT" -a -n "$UMOUNT" -a -n "$MKDIR" \
366 1.1 haad -a -n "$RMDIR" -a -n "$BLOCKDEV" -a -n "$BLKID" -a -n "$GREP" -a -n "$READLINK" \
367 1.1.1.2 haad -a -n "$FSCK" -a -n "$XFS_CHECK" -a -n "LVM" \
368 1.1 haad || error "Required command definitions in the script are missing!"
369 1.1 haad
370 1.1.1.2 haad $LVM version >/dev/null 2>&1 || error "Could not run lvm binary '$LVM'"
371 1.1.1.2 haad $($READLINK -e / >/dev/null 2>&1) || READLINK_E="-f"
372 1.1 haad TEST64BIT=$(( 1000 * 1000000000000 ))
373 1.1 haad test $TEST64BIT -eq 1000000000000000 || error "Shell does not handle 64bit arithmetic"
374 1.1 haad $(echo Y | $GREP Y >/dev/null) || error "Grep does not work properly"
375 1.1 haad
376 1.1 haad
377 1.1.1.2 haad if [ "$#" -eq 0 ] ; then
378 1.1 haad tool_usage
379 1.1 haad fi
380 1.1 haad
381 1.1.1.2 haad while [ "$#" -ne 0 ]
382 1.1 haad do
383 1.1.1.2 haad case "$1" in
384 1.1.1.2 haad "") ;;
385 1.1.1.2 haad "-h"|"--help") tool_usage ;;
386 1.1.1.2 haad "-v"|"--verbose") VERB="-v" ;;
387 1.1.1.2 haad "-n"|"--dry-run") DRY=1 ;;
388 1.1.1.2 haad "-f"|"--force") FORCE="-f" ;;
389 1.1.1.2 haad "-e"|"--ext-offline") EXTOFF=1 ;;
390 1.1.1.2 haad "-y"|"--yes") YES="-y" ;;
391 1.1.1.2 haad "-l"|"--lvresize") DO_LVRESIZE=1 ;;
392 1.1.1.2 haad "check") CHECK="$2" ; shift ;;
393 1.1.1.2 haad "resize") RESIZE="$2"; NEWSIZE="$3" ; shift 2 ;;
394 1.1.1.2 haad *) error "Wrong argument \"$1\". (see: $TOOL --help)"
395 1.1 haad esac
396 1.1 haad shift
397 1.1 haad done
398 1.1 haad
399 1.1 haad if [ -n "$CHECK" ]; then
400 1.1 haad check "$CHECK"
401 1.1 haad elif [ -n "$RESIZE" ]; then
402 1.1 haad export FSADM_RUNNING="fsadm"
403 1.1 haad resize "$RESIZE" "$NEWSIZE"
404 1.1 haad else
405 1.1 haad error "Missing command. (see: $TOOL --help)"
406 1.1 haad fi
407