Search notes:

Shrink and grow filesystems and partitions in Linux

These example scripts try to show how filesystems and partitions can be shrunk and grown if a partition has become too little to accomodate for larger files.
We're going to create a few partitions, most of the 10 M in size. The goal will be to grow the third partition to 15 M. In order to be able to that, the fourth partition needs to be shrank to 5 MB and then moved »to the right«.

Variables

First, we need to define a few variables.
device is the harddisk on which these examples are to be run. If you run the following code, please make sure that you choose a hard disk with no important data on it!
exec_parted is how parted is to be executed. It contains a reference to $device to make sure that we're not accidentally going to destroy the data on /dev/sda.
mountdir specifies the directory where we're going to mount the filesystems on.
Finally, part_n_start define, in bytes, where a partition starts on $device. The last partition also needs part_7_end.
#
#    Specify the device on which we're going to run the examples.
#
device=sdb

#
#    Where is parted located:
#
parted_executable=/sbin/parted

if [[ ! -x $parted_executable ]]; then
   echo "$parted_executable not executable, exiting"
   exit
fi

#
#    How parted is to be executed.
#    The -s flag is for non-interactive (scripted) mode. So
#    no questions will be asked from parted.
#
exec_parted="sudo $parted_executable -s /dev/$device"


#
#    The directory into which the partitions/devices will be
#    mounted.
#
mount_dir=./mountpoints/

part_1_start=$((        17 * 1024                     ))
part_2_start=$((       512 * 1024                     )) 
part_3_start=$(( 10 * 1024 * 1024                     ))
part_4_start=$(( 10 * 1024 * 1024 + $part_3_start     ))
part_5_start=$(( 10 * 1024 * 1024 + $part_4_start     ))
part_6_start=$(( 10 * 1024 * 1024 + $part_5_start     ))
part_7_start=$(( 10 * 1024 * 1024 + $part_6_start     ))
part_7_end=$((   10 * 1024 * 1024 + $part_7_start - 1 ))
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/variables

Removing each partition

In order to start with the example, we remove each partition on the hard disk.
I believe this is technically not really necessary because in the next step we're going to create a partition table anyway. But it shows how to use the rm command of parted.
#
#    Remove each partition
#
for part_nr in $($exec_parted  print | awk '/^ / {print $1}'); do
   echo "removing $part_nr on $device" 
   $exec_parted  rm  ${part_nr}
done
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/remove-partitions

Creating a partition table

Before we can create the partitions we need to initialise the hard disk with a partition table.
I probably could choose the partition type msdos, but I go with the more modern gpt.
However, before I create the partition table, I really (and possibly paranoidly) want to erase evertything that reminds me of the past of the hard disk.
So, I use dd to erase the start (and thus the old partition table) of the hard disk.
#
#   Create a GPT partition table.
#     ( Other possible values would be:
#       aix, amiga, bsd, dvh, loop, mac, msdos, pc98, sun )
#


sudo dd if=/dev/zero of=/dev/$device bs=1024 count=$(( $part_7_end / 1024 ))

$exec_parted  mklabel gpt
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/create-partition-table

Creating the partition

With gparted's mkpart command I can create on partition after another.
I use the previously declared variables to specify the start and the end for each partition.
Note also the b after each start and end number. It signifies that the numbers are to be interpreted in bytes.
#
#   Create a few partitions
#
#      use --align cylinder to prevent
#         »Warning: The resulting partition is not properly aligned for best performance.«
#
#      Note:
#        - apparently, multiple primary partitions are possible.
#

$exec_parted  mkpart  --align cylinder  nm_one    ext4        ${part_1_start}b $(( $part_2_start - 1 ))b
$exec_parted  mkpart  --align cylinder  nm_two    ext4        ${part_2_start}b $(( $part_3_start - 1 ))b
$exec_parted  mkpart  --align cylinder  nm_three  ext4        ${part_3_start}b $(( $part_4_start - 1 ))b
$exec_parted  mkpart  --align cylinder  nm_four   ext4        ${part_4_start}b $(( $part_5_start - 1 ))b
$exec_parted  mkpart  --align cylinder  nm_five   ext4        ${part_5_start}b $(( $part_6_start - 1 ))b
$exec_parted  mkpart  --align cylinder  nm_six    ext4        ${part_6_start}b $(( $part_7_start - 1 ))b
$exec_parted  mkpart  --align cylinder  nm_seven  linux-swap  ${part_7_start}b $(( $part_7_end       ))b


#
#     hexdumping it
#
#  sudo dd if=/dev/${device} bs=512 count=63 | hexdump -C

Github repository about-filesystems-and-partitions, path: /shrink-and-grow/create-partitions

Printing the partition table

Now that the partition table has been created, I want to print it. This is also possible with gparted.
#
#    Print actual partition table
#

print_partition_table() {
    local unit=$1

  #
  # Print the partition table.
  # The sed construct skips until the first
  # empty line (the header of parted).
  #
    $exec_parted  unit $unit  print | sed -n '/^$/,$p'
}

# print_partition_table s    # s    = sectors
  print_partition_table b    # b    = bytes
# print_partition_table kb   # kb   = kilobytes = 1000 bytes
# print_partition_table kib  # kib  = kibibytes = 1024 bytes
# print_partition_table mb   # mb   = megabytes = 1000 kilobytes
  print_partition_table mib  # mib  = mebibytes = 1020 kibibytes
# print_partition_table gb
# print_partition_table gib
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/print-partition-table

Formatting and mounting the partitions

We're now ready to mount and format (command mkfs.ext4) the new partitions.
rm -rf $mount_dir
 
# for device_to_mount in $(ls -1 /dev/${dev}?*); do
#   device_short=${device_to_mount:5}
#   mount_point=${mount_dir}$device_short
#   echo "mounting $device_to_mount to $mount_point"
#   mkdir -p $mount_point
#   sudo mount $device_to_mount $mount_point
# done
# ----------------------------------


mount_partition() {
  local dev_nr=$1

  sudo mount /dev/${device}$dev_nr ${mount_dir}${device}${dev_nr} # -o uid=$(id -u),gid=$(id -g)
  sudo chown -R $(id -u):$(id -g)  ${mount_dir}${device}${dev_nr} 
}

format_and_mount() {
    local dev_nr=$1
    mkdir -p ${mount_dir}${device}${dev_nr}


  # Wipe partition 

    local logical_block_size=$(cat /sys/block/$device/queue/logical_block_size)
    local size=$(cat /sys/block/$device/${device}${dev_nr}/size)                   # size in block size?

  # use dd to overwrite partition.
  # status=none only writes error messages.
    sudo dd if=/dev/zero of=/dev/${device}${dev_nr} bs=$logical_block_size count=$size status=none

    echo "formatting /dev/${device}$dev_nr"

    sudo mkfs.ext4 /dev/${device}$dev_nr  -q  # -q: quiet mode
    mount_partition $dev_nr
}

# don't create 1
format_and_mount 2
format_and_mount 3
format_and_mount 4
format_and_mount 5
format_and_mount 6
# don't create 7
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/format-and-mount-partitions

Creating some files

We're going to create some files.
Note especially partition 4 (/dev/${device}4) where I create two 4M files.
After creating those huge files, I create a rather smallisch readme.txt file. This file is placed at the »end« of the file system (because the two 4M files occupy most of the file system.
Then I drop one of the 4M files to make room when we're going to shrink this filesystem
echo "This is the second  partition" > ${mount_dir}${device}2/readme.txt

echo "This is the third   partition" > ${mount_dir}${device}3/readme.txt

# Create two 4 M file on the fourth partition
dd if=/dev/zero of=${mount_dir}{$device}4/4M.1 bs=1024 count=1024
dd if=/dev/zero of=${mount_dir}{$device}4/4M.2 bs=1024 count=1024

# Then write a file to the fourth partition
echo "This is the fourth  partition" > ${mount_dir}${device}4/readme.txt

# Remove the 1st of the 4M files again:
rm ${mount_dir}${device}4/4M1


echo "This is the fifth   partition" > ${mount_dir}${device}5/readme.txt

echo "This is the sixth   partition" > ${mount_dir}${device}6/readme.txt
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/create-some-files

Unmount partitions

Going into »administration mode«. We unmount the partitions so that we're able to shrink and/or grow them.
unmount() { # Note unmount, not umount
  local dev_nr=$1
  sudo umount /dev/${device}${dev_nr}
}

unmount 2
unmount 3
unmount 4
unmount 5
unmount 6
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/unmount-partitions

Shrinking filesystem on partition 4

Before we can grow partition 3, we need to make room for it. So we shrink partition 4 to 5 MB.
Yet, before we can shrink the partition we need to shrink the filesystem.
#    Use -p to show progress
sudo resize2fs  /dev/${device}4  5M

#    Check if filesystem is still ok
sudo e2fsck -f -y -C 0 /dev/${device}4 
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/shrink-filesystem

Creating a file system image

The fileystem in partition 4 is now 5 M in size. We store it away so that we can restore it again when the partition is shrunk.
sudo dd if=/dev/${device}4 bs=1024 count=$((5*1024)) of=img
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/create-filesystem-image

Moving partition 4

The fourth partition is not actually shrunk in one step: it is first deleted entierly (rm) and then recreated with a different start and the same end as it previously had.
#
#    First, we define a new variable to store
#    the start of the shrunk filesystem's new
#    start:
#
part_4_start_new=$(( $part_4_start + 5*1024*1024 ))

#
#    Then, we use parted again to remove and
#    recreate the partition with a different size:
#
$exec_parted rm 4
$exec_parted mkpart --align cylinder nm_four_resized ext4  $(( $part_4_start_new ))b   $(( $part_5_start - 1 ))b
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/shrink-partition

Restoring the filesystem image

Again with dd, the file system image we created is restored into the moved partition;
sudo dd if=img bs=1024 of=/dev/${device}4

#
#    It cannot hurt to check the fileystem again
#
sudo e2fsck -f -y -C 0 /dev/${device}4
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/restore-filesystem-image

Grow partition 3

After moving the fourth partition up, we have enough room for the third partition go grow.
Again, we first delete it entirely and resize it.
Note: removing the partition does no harm to the filesystem that is stored in it. Since the filesystem will physically reside in the same place on the harddisk after recreating the partition, there is no need to take an image file.
$exec_parted rm 3
$exec_parted mkpart --align cylinder nm_three_resized $(( $part_3_start ))b  $(( $part_4_start_new -1 ))b

#
#  Check file system three.
#  Apparently, we have to give the system some time to recognize the new
#  partition:
#
sleep 0.1

sudo e2fsck -f -y -C 0 /dev/${device}3 
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/grow-partition

Grow filesystem

The partition is now 15 M, but the filesystem is still 10 M. So, we have to grow it as well.
sudo resize2fs  /dev/${device}3  15M
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/grow-filesystem

Mounting again

After we have finished the shrinking and growing, we can mount the filesystems again:
for dev_nr in {2..6}; do
    mount_partition $dev_nr
done
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/mount-again

Writing a large file

Since the third partition is now 15 M, we can write a file up to ca 12 M (some space is needed by the filesystem itself which we cannot access).
dd if=/dev/zero of=${mount_dir}${device}3/12.MiB bs=1024 count=$((12*1024))
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/write-large-file

Showing the files and contents

Let's check if the files are still valid and intact and if the 12 M file is indeed that size.
cat    ${mount_dir}${device}3/readme.txt
cat    ${mount_dir}${device}4/readme.txt

#
#      Verify that the size is  of 12.MiB
#      is indeed 12 MB:
#
ls -l  ${mount_dir}${device}3/12.MiB
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/cat-files

Unmounting again

In order to release the hard disk, we're unmounting the harddisk again.
unmount() { # Note unmount, not umount
  local dev_nr=$1
  sudo umount /dev/${device}${dev_nr}
}

unmount 2
unmount 3
unmount 4
unmount 5
unmount 6
Github repository about-filesystems-and-partitions, path: /shrink-and-grow/unmount-partitions

Index