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