Post

Working with Disk and File System in Linux

After knowing how the Linux Kernel presents devices to userspace. Now its time to look at how we can partition disks, create and maintain filesystems that goes inside disk partitions, and work with the swap space.

Partitioning Disk Devices

  • Partitons are the subdivisons of the whole disk
  • Partitions are defined on a small area of the disk called partition table
  • Two types of partition tables: Master Boot Record (MBR) and GUID Parttion Table (GPT)
  • Linux Partitioning tools: parted, gparted, fdisk, gdisk

Viewing Partition Table

1
2
3
4
5
$ sudo parted -l
# OR
$ fdisk -l
# OR
$ lsblk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Output of 'parted -l'
Model: ATA Samsung SSD 870 EVO 1TB (scsi)
Disk /dev/sda: 1000GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags: 

Number  Start   End     Size    File system     Name     Flags
 1      1049kB  538MB   537MB   fat32           EFI      boot, esp
 2      538MB   4295MB  3757MB  ext4
 3      4295MB  1000GB  996GB   ext4

Model: WD My Passport 0820 (scsi)
Disk /dev/sdb: 2000GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags: 

Number  Start   End     Size    Type      File system  Flags
 1      1049kB  2000GB  2000GB  primary   ntfs         lba
1
2
3
4
5
6
7
8
9
10
11
12
# Output of fdisk -l 
sudo fdisk -l
Disk /dev/ram0: 4 MiB, 4194304 bytes, 8192 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes


Disk /dev/ram1: 4 MiB, 4194304 bytes, 8192 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes

Which one to choose: fdisk or parted ?

With fdisk, you design your new partition table before making the actual changes to the disk, and it makes changes only when you exit the program. But with the parted, partitions are created, modified, and removed as you issue the command.


Creating a Partition Table

Check and select the device

In my case it is a flash drive and it is mounted as /dev/sdb, once the drive is selected, type p to print the current partition on the flash drive.

1
2
$ lsscsi
$ sudo fdisk /dev/sdb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Output
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
...
...
...

# print the current table
Command (m for help): p

Disk /dev/sdb: 115.41 GiB, 123924905984 bytes, 242040832 sectors
Disk model: TESLADRIVE      
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: B9083D28-1E94-45BC-A8A3-DA68BE09A9A9

Device       Start     End Sectors  Size Type
/dev/sdb1       64     635     572  286K Microsoft basic data
/dev/sdb2      636   17019   16384    8M EFI System
/dev/sdb3    17020 3577255 3560236  1.7G Apple HFS/HFS+
/dev/sdb4  3577256 3577855     600  300K Microsoft basic data

Deleting a partition

Here in my example, I am deleting sdb3 using the command d and printing the output with p.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# deleting a partition
Command (m for help): d
Partition number (1-4, default 4): 3

Partition 3 has been deleted.

# print the partition again
Command (m for help): p
Disk /dev/sdb: 115.41 GiB, 123924905984 bytes, 242040832 sectors
Disk model: TESLADRIVE      
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: B9083D28-1E94-45BC-A8A3-DA68BE09A9A9

Device       Start     End Sectors  Size Type
/dev/sdb1       64     635     572  286K Microsoft basic data
/dev/sdb2      636   17019   16384    8M EFI System
/dev/sdb4  3577256 3577855     600  300K Microsoft basic data

Creating a new partition

We can use n command to create a new partition. As seen in the below example, it will prompt you for the sector to choose, lets say you want to create multiple partition, one of them is 200MB then you will enter the first sector (default) and in the next line add +200M.

But in my case, I am creating a partition on the remaining entire sectors. SO I will just hit enter twice.

1
2
3
4
5
6
Command (m for help): n
Partition number (3,5-176, default 3): 3
First sector (17020-242040786, default 3577856): 
Last sector, +/-sectors or +/-size{K,M,G,T,P} (3577856-242040786, default 242038783): 

Created a new partition 3 of type 'Linux filesystem' and of size 113.7 GiB.

Now if I check all the partitions available, I get the following result

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Command (m for help): p
Disk /dev/sdb: 115.41 GiB, 123924905984 bytes, 242040832 sectors
Disk model: TESLADRIVE      
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: B9083D28-1E94-45BC-A8A3-DA68BE09A9A9

Device       Start       End   Sectors   Size Type
/dev/sdb1       64       635       572   286K Microsoft basic data
/dev/sdb2      636     17019     16384     8M EFI System
/dev/sdb3  3577856 242038783 238460928 113.7G Linux filesystem
/dev/sdb4  3577256   3577855       600   300K Microsoft basic data

Partition table entries are not in disk order.

Command (m for help): 

In the above output, at the end it says, the partition table entries are not in the disk order. sdb4’s last sector is 3577855 and sdb3’s first sector is 3577856 which makes sense. It is a poor way to create partition and difficult to maintain.

Saving the changes

1
2
3
4
5
6
7
Command (m for help): w
The partition table has been altered.
Failed to remove partition 3 from system: No such device or address

The kernel still uses the old partitions. The new table will be used at the next reboot. 
Syncing disks.

Cleanest modern approach:

Use gdisk instead of fdisk for GPT disks — it has better tools for reordering/optimizing.

1
sudo gdisk /dev/sdb

Then inside gdisk:

1
2
3
* s → sort partitions (reorders entries to match disk order)
* p → verify
* w → write

gdisk’s sort function often fixes this automatically without deleting anything.


File System

I have gone in depth about the file system in my post Head First Dive Into File System. So in this section, I will touch on the subject of creating, mounting and remounting of the file system.

Creating a Filesystem

1
2
3
sudo mkfs.<filesystem-type> [options] /dev/sdXn
# or the generic form (less common now):
sudo mkfs -t <filesystem-type> [options] /dev/sdXn

Example

1
2
3
4
sudo mkfs.ext4 /dev/sdb3           # Create ext4 filesystem
sudo mkfs.xfs -f /dev/nvme0n1p2    # Create XFS (force with -f if needed)
sudo mkfs.vfat -F 32 /dev/sdc1     # Create FAT32 for USB drive
sudo mkfs.btrfs -L mypool /dev/sdd # Create Btrfs with label "mypool"

Behind the Hood

mkfs is the front end, it is a symlink to a series of file creation utilities in ‘sbin.

1
$ ls -l /sbin/mkfs.*
1
2
3
4
5
6
7
8
9
10
11
# Output
-rwxr-xr-x 1 root root 22912 Sep 15 20:08 /sbin/mkfs.bfs
-rwxr-xr-x 1 root root 35144 Sep 15 20:08 /sbin/mkfs.cramfs
lrwxrwxrwx 1 root root     6 Apr 28  2024 /sbin/mkfs.ext2 -> mke2fs
lrwxrwxrwx 1 root root     6 Apr 28  2024 /sbin/mkfs.ext3 -> mke2fs
lrwxrwxrwx 1 root root     6 Apr 28  2024 /sbin/mkfs.ext4 -> mke2fs
-rwxr-xr-x 1 root root 52048 Mar 31  2024 /sbin/mkfs.fat
-rwxr-xr-x 1 root root 43408 Sep 15 20:08 /sbin/mkfs.minix
lrwxrwxrwx 1 root root     8 Mar 31  2024 /sbin/mkfs.msdos -> mkfs.fat
lrwxrwxrwx 1 root root     6 Apr  8  2024 /sbin/mkfs.ntfs -> mkntfs
lrwxrwxrwx 1 root root     8 Mar 31  2024 /sbin/mkfs.vfat -> mkfs.fat

Mounting a FileSystem

In Unix, the process of attaching a filesystem to a running system is called mounting.

1
2
3
$ mount
# OR
$ findmnt
1
2
3
4
5
6
7
8
# Output
/dev/sdd on / type ext4 (rw,relatime,discard,errors=remount-ro,data=ordered)
none on /mnt/wslg type tmpfs (rw,relatime)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,noatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,noatime)
devpts on /dev/pts type devpts (rw,nosuid,noexec,noatime,gid=5,mode=620,ptmxmode=000)
binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,relatime)
...

If we look at the above output, we see a general format:

1
2
device_or_source on mount_point type filesystem_type (options)
# options are the comma seperated mount flags

Also, other than sda,sdb, we see sysfs, none, proc etc, what does it mean?

  • none : no real device block
  • sysfs on /sys : virtual filesystem exposing kernel device/driver info
  • proc on /proc : Virtual FS for process info, kernal parameters, stats
  • tmpfs : RAM-based filesystems

How to mount

1
2
3
4
$ mount -t type device mountpoint

# Example :
$ mount -t ext4 /dev/sdf2/ /home/extra

How to unmount

1
$ unmount mountpoint

Filesystem UUID

UUID : Universally Unique Identifier

1
2
 $ blkid # block ID
 $ mount UUID=XXXX-XXXX-XXXX /home/extra

Disk Buffer

In Linux system, the kernel doesn’t ususally immediately write to a device. It caches it to RAM. sync tells the kernel to flush all dirty buffer (modified data in memory) to their respective block device immediately.

1
2
3
4
5
6
# before removing USB
$ sync

$ sync && sudo reboot

$ sync /dev/sdb

The /etc/fstab Filesystem Table

/etc/fstab (often just called fstab) is a plain-text configuration file in Linux that tells the system which filesystems to mount automatically at boot, and how to mount them (options, mount point, etc.).

1
<device>  <mount point>  <filesystem type>  <mount options>  <dump>  <fsck pass>
1
2
# Example
UUID=abcd1234-5678-90ef-ghij-klmnopqrstuv  /home  ext4  defaults,noatime,discard  0  2

File System Capacity

df stands for disk free and shows disk space usage.

1
2
3
$ df
$ df -h # Human-Readable form
$ df -i # Show inode usage
1
2
3
4
5
6
# Example:
Filesystem     1K-blocks      Used Available Use% Mounted on
/dev/sda1      104857600   45236892  54520408  46% /
/dev/sda2      209715200  123456789  86258311  59% /home
tmpfs           4194304     123456   4070848   3% /run
/dev/sdb1       976762584  876543210 100219374  90% /mnt/backup

Checking and Repairing File systems

fsck - file system check

1
2
3
4
5
# Must be unmounted! (or read-only for root in recovery)
sudo fsck /dev/sda1
sudo fsck -f /dev/sda1          # force check even if clean
sudo fsck -y /dev/sda1          # auto-answer "yes" to all fixes (dangerous but convenient)
sudo fsck -C /dev/sda1          # show progress bar

Never run fsck on a mounted filesystem (except read-only with -n)

1
sudo fsck -n /dev/sda1   # check only, no changes (safe on mounted FS)

Swap Space

Swap is the space in storage device where the operating system creates virtual memory, if it runs out of the real memory.

1
2
3
4
$ free -h
               total        used        free      shared  buff/cache   available
Mem:            15Gi       4.5Gi       2.5Gi       829Mi       9.0Gi        11Gi
Swap:          4.0Gi          0B       4.0Gi

Creating and managing Swap

1
2
3
4
5
sudo fdisk /dev/sda   # create partition type 82 (A dedicated partition (type 82 in fdisk, 8200 in GPT))
sudo mkswap /dev/sdaX
sudo swapon /dev/sdaX
# Add to /etc/fstab:
UUID=abcd-1234  none  swap  sw  0  0

Logical Volume Manager

LVM

Logical Volume Manager (LVM) is a flexible storage management system in Linux that sits between physical disks/partitions and filesystems. It allows you to abstract, pool, resize, and manage storage much more dynamically than traditional fixed partitions.

Quick Viewing Commands

CommandDescription
pvsphysical volumes
vgsvolume groups
lvslogical volumes
lvdisplaydetailed view
vgdisplay -vshow free space, etc.

Example

Imagine I have two devices sdb1 and sdc1, 5GB and 15 GB respectively. How I will create two LVs with 10GB space each?

Step 1: Convert the partitions /dev/sdb1 and /dev/sdc1 into PVs.

1
2
3
4
5
$ sudo pvcreate /dev/sdb1
$ sudo pvcreate /dev/sdc1

# verify
$ sudo pvs 

Step 2: Combine the PVs into a single VG named myvg.

1
2
3
4
$ sudo vgcreate myvg /dev/sdb1 /dev/sdc1

# Verify
$ sudo vgs

Step 3: Create two 10GB LVs named lv1 and lv2 from myvg.

1
2
3
4
5
$ sudo lvcreate -L 10G -n lv1 myvg
$ sudo lvcreate -L 10G -n lv2 myvg

# Verify
$ sudo lvs

Step 4: Format each LV as ext4 (creates the “partitions”).

1
2
sudo mkfs.ext4 /dev/myvg/lv1
sudo mkfs.ext4 /dev/myvg/lv2

Step 5: Mount them (optional, for testing):

1
2
3
sudo mkdir /mnt/lv1 /mnt/lv2
sudo mount /dev/myvg/lv1 /mnt/lv1
sudo mount /dev/myvg/lv2 /mnt/lv2

Now lets say, I don’t want the second LV and want to remove it and add that space to first LV.

1
2
3
4
5
6
7
8
9
10
11
12
sudo umount /mnt/lv2 # unmount

sudo lvremove /dev/myvg/lv2 # remove the LV

sudo lvs # confirm removal

sudo lvextend -L +10G /dev/myvg/lv1 # Extend lv1 by 10GB (to 20GB total)

sudo resize2fs /dev/myvg/lv1 # Resize the ext4 filesystem to fill the new space

sudo lvs # verify
df -h /mnt/lv1  # if mounted

The LVM Implementation

More on this later

This post is licensed under CC BY 4.0 by the author.