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