| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | xfsprogs 6.16.0-1.el10e2fsprogs 1.47.1-5.el10dosfstools 4.2-12.el10util-linux 2.40.2-18.el10parted 3.6-7.el10lvm2 2.03.36-2.el10file 5.45-9.el10psmisc 23.6-8.el10 |
| Applies to | RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora, Ubuntu, Debian |
| Privilege | sudo or root to create and mount filesystems; listing devices works as a normal user |
| Scope | Turning a block device into a usable filesystem: picking between ext4, XFS and VFAT, creating them with mkfs, adding labels, mounting and unmounting, reading capacity correctly, and checking or repairing each type. Does not cover partitioning depth, LVM, or persistent fstab entries. |
| Related guides | Create a filesystem on an LVM partition Mount by UUID and label in fstab parted command mke2fs command Format an SD card |
Every mkfs command on this page destroys access to whatever is already on the device you name, and it is irreversible once it proceeds. These tools do carry safeguards, but they are uneven. All three refuse a device that is currently mounted.
An existing filesystem on an idle device is treated quite differently: mkfs.xfs and mkfs.ext4 stop and make you override them explicitly with -f and -F, while mkfs.vfat performs no such check and simply overwrites what is there. Those flag letters are not portable either, since -f and -F mean something else entirely to mkfs.vfat, so never assume a force option carries the same meaning from one mkfs tool to the next.
None of them asks you to confirm the device is the one you meant. Verify the target with lsblk, findmnt, and a signature check as shown below, and never practise on a disk that holds data you would miss.
A new disk is not usable storage. The kernel gives you a block device the moment you attach the disk, but until something writes filesystem metadata onto it there is nowhere to put a file and nothing to mount.
This guide walks the whole path on a live host: find the right device, create XFS, ext4 and VFAT on three partitions of one spare disk, mount them, read their capacity honestly, and check or repair each one. I ran every command below on a 10 GB scratch disk and kept the real output, including the errors I ran into on purpose so you can recognise them.
What Is a Linux Filesystem?
A block device is raw addressable storage. /dev/sdb is the whole disk, /dev/sdb1 is a byte range inside it, and the kernel is happy to read and write either one without knowing anything about files. What it cannot do is answer a question like "give me the third file in this directory", because on a raw device there is no such thing as a directory.
A filesystem is the set of on-disk structures that answers those questions. Creating one writes:
- A superblock recording the type, size, and identity of the filesystem
- Inode tables or FAT allocation tables that map names and metadata to data blocks
- Free space maps so the kernel knows where it may write next
- A journal, on ext4 and XFS, so an interrupted write can be replayed rather than guessed at
A mount point is the directory where you attach that tree to the running system. The filesystem exists on the device whether or not it is mounted; mounting simply makes it reachable at a path.
Two consequences matter before you type anything. First, creating a filesystem destroys access to the previous one, because the new metadata replaces the map that told the kernel where the old files were. Second, a partition can exist perfectly well with no filesystem at all, which is the state my three scratch partitions are in right now.
The -f flag of lsblk prints filesystem information rather than sizes, so it shows that gap clearly:
lsblk -f /dev/sdbNAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
sdb
├─sdb1
├─sdb2
└─sdb3Three partitions exist and the kernel can address all of them, but every filesystem column is empty. Nothing here can be mounted yet.
blkid reads the device directly and reports what it recognises, which separates partition identity from filesystem identity:
blkid /dev/sdb1/dev/sdb1: PARTLABEL="xfsdata" PARTUUID="b55738e7-94de-4142-9bbe-51d42bd38300"The PARTLABEL and PARTUUID come from the GPT partition table, so they describe the slot on the disk. There is no TYPE and no UUID, because those belong to a filesystem that does not exist yet. Keeping those two families of identifier apart saves a lot of confusion later, since mount LABEL= matches the filesystem label and never the partition name.
Filesystem Creation Quick Reference
The whole workflow is six steps, and the commands change very little between filesystem types:
| Step | Command |
|---|---|
| Confirm the device is free | lsblk -f /dev/sdb, findmnt -S /dev/sdb1, then wipefs /dev/sdb1 |
| Create the filesystem | mkfs.xfs /dev/sdb1, mkfs.ext4 /dev/sdb2, or mkfs.vfat /dev/sdb3 |
| Confirm what was written | blkid /dev/sdb1 |
| Mount it | mkdir -p /mnt/data then mount /dev/sdb1 /mnt/data |
| Verify the mount | findmnt /mnt/data and df -hT /mnt/data |
| Unmount when finished | umount /mnt/data |
Creation and labelling differ per type, and the label flags are not interchangeable:
| Filesystem | Create | Create with a label | Repair tool |
|---|---|---|---|
| XFS | mkfs.xfs /dev/sdb1 |
mkfs.xfs -L labxfs /dev/sdb1 |
xfs_repair |
| ext4 | mkfs.ext4 /dev/sdb2 |
mkfs.ext4 -L labext4 /dev/sdb2 |
e2fsck |
| VFAT | mkfs.vfat /dev/sdb3 |
mkfs.vfat -n LABVFAT /dev/sdb3 |
fsck.vfat |
Identify the Correct Block Device
This is the step that prevents a bad afternoon, and it deserves more than a glance. mkfs will format the root disk as readily as a scratch disk if you name it, so the goal is positive proof that the device you are about to write to holds nothing you need.
Start with the whole picture. Run lsblk with no arguments to see every disk, its partitions, and where each one is currently attached:
lsblkNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 30G 0 disk
├─sda1 8:1 0 1M 0 part
├─sda2 8:2 0 2G 0 part /boot
└─sda3 8:3 0 28G 0 part
├─rhel-root 253:0 0 25G 0 lvm /
└─rhel-swap 253:1 0 3G 0 lvm [SWAP]
sdb 8:16 0 10G 0 disk
├─sdb1 8:17 0 3G 0 part
├─sdb2 8:18 0 3G 0 part
└─sdb3 8:19 0 2G 0 part
sr0 11:0 1 1024M 0 romThe MOUNTPOINTS column does most of the work. On sda there is a /boot partition and an LVM layer carrying / and swap, so that disk is the running system. Every partition on sdb has an empty mount point, which is the first sign it is idle. Reading these trees is worth practising on your own hosts, and the lsblk command guide covers the other output columns.
An empty mount point column is suggestive rather than conclusive, so ask about the specific device. findmnt -S searches the mount table by source device:
findmnt -S /dev/sdb1Nothing is printed and the exit status is 1, which is findmnt saying it found no mount using that device. Silence here is the answer you want; if the device were mounted anywhere, even somewhere you had forgotten, this command would print the row.
Absence from the mount table still does not rule out the device being claimed by a storage layer. A disk can be an LVM physical volume with no mounted filesystem of its own, and formatting it would destroy a volume group. Ask LVM directly:
pvsPV VG Fmt Attr PSize PFree
/dev/sda3 rhel lvm2 a-- 28.00g 0Only /dev/sda3 is a physical volume, so sdb is not part of any volume group. If your target disk did appear in this list, stop and work through creating a filesystem on an LVM partition instead, because the device you format is the logical volume rather than the disk.
One last look at the raw bytes tells you whether anything recognisable is already there. The -s flag lets file read a block device rather than describing the device node itself:
file -s /dev/sdb1/dev/sdb1: dataRead that answer narrowly. Plain data means file did not recognise a known filesystem or other format, and it does not prove that every block is unused. The device could still hold application data, a database written straight to raw storage, or metadata this tool has no rule for. Had it said SGI XFS filesystem data or Linux rev 1.0 ext4 filesystem data, something was already there and I would want to know what before overwriting it.
wipefs is the better tool for the same question, because it looks specifically for the signatures that matter here. Given no erase options it only reports:
wipefs /dev/sdb1Nothing is printed, which means wipefs found no filesystem, RAID member, or partition-table signature on the device. Run the same command against the partition after it has been formatted and the difference is obvious:
DEVICE OFFSET TYPE UUID LABEL
sdb1 0x0 xfs a2d95b7d-0394-40d5-a667-6c69da0dc048 labxfsThe row is a signature wipefs would erase if you asked it to with -a, and listing it changes nothing on disk. That is what makes it safe to run first.
It also catches cases file -s misses, such as a stale RAID superblock or a partition table on a disk you thought was blank, so it belongs in the same sequence as lsblk, findmnt, and pvs rather than as a replacement for any of them.
If you want the root device named explicitly rather than inferred from a tree, ask about / on its own:
findmnt -n -o SOURCE //dev/mapper/rhel-rootThat is the one device that must never appear in an mkfs command on a running system.
Create the partitions this walkthrough uses
My scratch disk arrived with an empty GPT and no partitions, so I carved out three before formatting anything. Partitioning is its own topic and the parted command guide goes through the options properly, but for completeness here is the label I wrote first. On a disk that already has a layout worth keeping, back up the partition table before mklabel replaces it:
parted -s /dev/sdb mklabel gptWriting a fresh GPT prints nothing on success and discards any previous partition table, so it belongs only on a disk you have already confirmed is spare. The first partition takes 3 GB starting at the conventional 1 MiB offset, which keeps it aligned:
parted -s /dev/sdb mkpart xfsdata 1MiB 3GiBEach mkpart is silent in script mode, so there is nothing to read until the table is complete. The second partition starts where the first ended:
parted -s /dev/sdb mkpart ext4data 3GiB 6GiBThe third is smaller at 2 GB, which is plenty for the VFAT filesystem and leaves the last 2 GB of the disk unallocated:
parted -s /dev/sdb mkpart vfatdata 6GiB 8GiBThe names xfsdata, ext4data, and vfatdata are GPT partition names, not filesystem labels, which is exactly the PARTLABEL field you saw in the earlier blkid output. Confirm the kernel picked up all three before you format them:
lsblk /dev/sdbNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sdb 8:16 0 10G 0 disk
├─sdb1 8:17 0 3G 0 part
├─sdb2 8:18 0 3G 0 part
└─sdb3 8:19 0 2G 0 partTwo 3 GB partitions and one 2 GB partition, with 2 GB of the disk left unallocated. If a partition you just created is missing here, the kernel has not re-read the table yet and udevadm settle or partprobe will usually fix it.
Choose Between ext4, XFS and VFAT
The three filesystems on this page are not competing for the same job, so the choice is usually obvious once you know what the storage is for.
| Property | XFS | ext4 | VFAT |
|---|---|---|---|
| Typical use | Default on RHEL and derivatives, large files, high throughput | General purpose Linux storage, wide tooling support | Removable media, EFI system partitions, sharing with Windows |
| Journaling | Yes, metadata journal | Yes, metadata and optionally data | No |
| Unix ownership and permissions | Full | Full | None, synthesised at mount time |
| Readable on Windows and macOS | No, without extra software | No, without extra software | Yes, natively |
| Grow | Online with xfs_growfs while mounted |
Online with resize2fs |
Not practical in place |
| Shrink | Not supported | Supported offline | Not practical in place |
| Largest single file | Very large, effectively not a limit here | Very large, effectively not a limit here | 4 GiB |
| Repair tool | xfs_repair |
e2fsck |
fsck.vfat |
| Label limit | 12 characters | 16 characters | 11 characters, uppercase |
Two rows drive most real decisions. The 4 GiB file size limit rules VFAT out for anything but interchange and boot partitions, because a single large archive or disk image will simply fail to copy. The shrink row matters when you are not sure how big a volume should be: XFS grows online but never shrinks, so if you expect to reclaim space later, ext4 leaves you an option that XFS does not.
For a data volume on an enterprise Linux host, XFS is the sensible default and is what the installer already chose for /. Pick ext4 when you want the option to shrink, or when a tool you depend on expects it. Pick VFAT only when something outside Linux has to read the media.
Create an XFS Filesystem
With the device confirmed idle, creating the filesystem is one command. mkfs.xfs prints the geometry it chose, which is worth reading rather than skipping past:
mkfs.xfs /dev/sdb1meta-data=/dev/sdb1 isize=512 agcount=4, agsize=196544 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=1
= reflink=1 bigtime=1 inobtcount=1 nrext64=1
= exchange=0 metadir=0
data = bsize=4096 blocks=786176, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1, parent=0
log =internal log bsize=4096 blocks=16384, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
= rgcount=0 rgsize=0 extents
= zoned=0 start=0 reserved=0Three numbers explain the layout. The data line shows 786176 blocks of bsize=4096, which is the 3 GB of usable space. agcount=4 means XFS split that into four allocation groups it can work on in parallel. The log line shows a 16384 block internal journal living inside the same device, and that journal is part of why the filesystem reports slightly less free space than the partition size.
Creation is silent about identity, so ask blkid what now exists on the device:
blkid /dev/sdb1/dev/sdb1: UUID="82c40344-5048-40d5-adc3-cef76b9f7473" BLOCK_SIZE="512" TYPE="xfs" PARTLABEL="xfsdata" PARTUUID="b55738e7-94de-4142-9bbe-51d42bd38300"Compare this with the same command earlier in the article. TYPE="xfs" and a UUID have appeared, generated during creation, while PARTLABEL and PARTUUID are unchanged because the partition table was never touched. There is no LABEL field, because I did not ask for one.
The signature is now visible to tools that read raw bytes too, which is the difference file -s reported as plain data before:
file -s /dev/sdb1/dev/sdb1: SGI XFS filesystem data (blksz 4096, inosz 512, v2 dirs)XFS is often reported under its SGI heritage, which is the same filesystem despite the older name.
Now the safety behaviour worth knowing before you need it. Running the same mkfs.xfs command a second time does not quietly reformat:
mkfs.xfs /dev/sdb1mkfs.xfs: /dev/sdb1 appears to contain an existing filesystem (xfs).
mkfs.xfs: Use the -f option to force overwrite.The tool detected the signature it wrote a moment ago and refused. That refusal is the last automatic guard between a mistyped device name and lost data, so treat it as information rather than an obstacle.
mkfs.xfs -f skips exactly that check. Use it only when you have already confirmed which device you are naming and that its contents are expendable. On the wrong device, -f turns a harmless error message into an unrecoverable format.
Because I do want a label on this filesystem and the device is already formatted, -f is genuinely required here. The output is the same geometry report, trimmed to the first lines:
mkfs.xfs -f -L labxfs /dev/sdb1meta-data=/dev/sdb1 isize=512 agcount=4, agsize=196544 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=1
= reflink=1 bigtime=1 inobtcount=1 nrext64=1One detail in the result catches people out later, so check the identity again:
blkid /dev/sdb1/dev/sdb1: LABEL="labxfs" UUID="a2d95b7d-0394-40d5-a667-6c69da0dc048" BLOCK_SIZE="512" TYPE="xfs" PARTLABEL="xfsdata" PARTUUID="b55738e7-94de-4142-9bbe-51d42bd38300"The LABEL arrived as requested, but the UUID also changed from 82c40344 to a2d95b7d. Reformatting mints a brand new filesystem, so any /etc/fstab line or script referring to the old UUID now points at something that no longer exists. That is a routine cause of a host that boots into emergency mode after a "quick reformat".
Create an ext4 Filesystem
ext4 lives on the second partition. mke2fs accepts the label at creation time with -L, so there is no reason to add it afterwards:
mkfs.ext4 -L labext4 /dev/sdb2mke2fs 1.47.1 (20-May-2024)
Creating filesystem with 786432 4k blocks and 196608 inodes
Filesystem UUID: 830f3cbf-d586-4d43-aa50-fde3786a3569
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912
Allocating group tables: 0/24 done
Writing inode tables: 0/24 done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: 0/24 doneThis output is more talkative than XFS and each line is a commitment made on your behalf:
- 196608 inodes, which caps how many files this filesystem can ever hold regardless of free space
- Superblock backups at five known block numbers, which is what lets
e2fsckrecover from a damaged primary superblock - A 16384 block journal
- A UUID generated for you at creation time
The mke2fs command reference covers the tuning options if the defaults do not suit a particular workload.
Now a small surprise from my own run. Checking the result immediately with lsblk looked as though nothing had happened:
lsblk -f /dev/sdb2NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
sdb2Empty columns, right after a successful format. The filesystem was fine; lsblk reports properties that udev caches, and udev had not yet re-read the device. blkid goes to the disk itself and told the truth straight away:
blkid /dev/sdb2/dev/sdb2: LABEL="labext4" UUID="830f3cbf-d586-4d43-aa50-fde3786a3569" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="ext4data" PARTUUID="d8dc1e1e-0e48-455d-9432-935cc3e10270"Label, UUID, type, and a 4096 byte block size, all present. A second later the cache caught up on its own and lsblk -f showed the same values. When the two disagree, believe blkid and give udev a moment, or prompt it with udevadm settle.
Create a VFAT Filesystem
VFAT needs the dosfstools package, which is not always installed on a minimal server. Confirm it before reaching for the command:
rpm -q dosfstoolsdosfstools-4.2-12.el10.x86_64The package is present, so mkfs.vfat is available. On Debian and Ubuntu the package carries the same name and installs with apt install dosfstools.
VFAT labels use -n rather than -L, and they are conventionally uppercase:
mkfs.vfat -n LABVFAT /dev/sdb3mkfs.fat 4.2 (2021-01-31)One version line and nothing else. FAT has far less metadata to build than ext4 or XFS, so there is no geometry report to print and no journal to create. Silence here means success.
The identity of a FAT filesystem looks noticeably different from the other two:
blkid /dev/sdb3/dev/sdb3: LABEL_FATBOOT="LABVFAT" LABEL="LABVFAT" UUID="7A93-48B7" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="vfatdata" PARTUUID="c154f0aa-70bc-446b-8a5d-98343bbbd428"The UUID is 7A93-48B7, which is eight hexadecimal digits rather than the long random identifier ext4 and XFS use. FAT has no field for a real UUID, so this is a volume serial number that tools present in the UUID position. It is unique enough for /etc/fstab in practice, but it carries far less entropy, and reformatting will produce a different one.
The LABEL_FATBOOT entry is the copy of the label stored in the boot sector alongside the one in the root directory.
Reading the raw bytes shows how much of FAT's design is inherited from DOS:
file -s /dev/sdb3/dev/sdb3: DOS/MBR boot sector, code offset 0x58+2, OEM-ID "mkfs.fat", sectors/cluster 8, Media descriptor 0xf8, sectors/track 63, heads 255, hidden sectors 12582912, sectors 4194288 (volumes > 32 MB), FAT (32 bit), sectors/FAT 4088, serial number 0x7a9348b7, label: "LABVFAT "Two details are worth pulling out. FAT (32 bit) confirms mkfs.vfat chose FAT32 for a 2 GB device, and the label is stored padded to eleven characters as "LABVFAT ", which is the fixed-width field FAT allows. The sectors per track and heads values are geometry from an era of physical cylinders and are ignored on modern hardware.
What this filesystem cannot do is store Unix ownership, which changes how files appear once it is mounted. That behaviour has its own section below, because it surprises people who reach for chmod and find it silently ineffective. For the removable media case specifically, formatting an SD card walks through the same tools on a device you can carry.
Assign Filesystem Labels During Creation
A label is a human readable name stored inside the filesystem. It matters because mounting by label survives device renaming: today's /dev/sdb1 can become /dev/sdc1 after you add a disk, but LABEL=labxfs still finds the right filesystem.
Each tool spells the option differently and enforces different limits:
| Filesystem | Flag at creation | Maximum length | Case handling |
|---|---|---|---|
| XFS | -L labxfs |
12 characters | Preserved |
| ext4 | -L labext4 |
16 characters | Preserved |
| VFAT | -n LABVFAT |
11 characters | Uppercase expected |
Those limits are enforced, not advisory, and each tool reacts in its own way. I tried all three against a 400 MB scratch image file rather than a real partition, so nothing important was at risk. XFS rejects an over-long label outright and refuses to run:
mkfs.xfs -f -L thirteenchars /root/label-test.imgInvalid value thirteenchars for -L option
Usage: mkfs.xfsThirteen characters is one too many and the command stops without creating anything. You get an error you cannot miss.
mke2fs is more forgiving and that forgiveness is the trap:
mkfs.ext4 -q -L abcdefghijklmnopqrst /root/label-test.imgWarning: label too long; will be truncated to 'abcdefghijklmnop'It created the filesystem successfully with a label you did not ask for. If a script later mounts LABEL=abcdefghijklmnopqrst, it will fail while blkid shows the truncated sixteen character version, and the warning that explained it scrolled past during the install.
VFAT enforces its own shorter limit and refuses:
mkfs.vfat -n TWELVECHARSX /root/label-test.imgmkfs.vfat: Label can be no longer than 11 characters
mkfs.fat 4.2 (2021-01-31)Eleven characters is the hard ceiling. Lowercase is accepted but warned about, because other operating systems reading the media may not handle it consistently:
mkfs.vfat -n lowercase /root/label-test.imgmkfs.fat: Warning: lowercase labels might not work properly on some systems
mkfs.fat 4.2 (2021-01-31)The filesystem is created either way, so this one is a judgement call. For media that will only ever be read on Linux the case does not matter; for a USB stick heading to a Windows machine, use uppercase and avoid the question.
Labels are only half of the persistence story. Making a mount survive a reboot means writing an entry that refers to the label or UUID, and mounting by UUID and label in fstab covers the file format and the failure modes that come with it.
Mount a Filesystem
A filesystem you cannot reach is not much use. Mounting attaches it to a directory, and that directory needs to exist first:
mkdir -p /mnt/xfsdatamkdir prints nothing when it succeeds, and -p means it will not complain if the directory is already there. Any empty directory works as a mount point; /mnt is the conventional place for storage you attach by hand.
With somewhere to put it, attach the XFS filesystem:
mount /dev/sdb1 /mnt/xfsdataSuccess is silent, which is why the next command matters. findmnt queries the kernel's mount table and shows what is actually attached rather than what you asked for:
findmnt /mnt/xfsdataTARGET SOURCE FSTYPE OPTIONS
/mnt/xfsdata /dev/sdb1 xfs rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquotaFour things are confirmed at once: the target path, the device behind it, the type the kernel detected without being told, and the options in force. rw means writable, and the rest are XFS defaults the kernel filled in. Notice I never passed -t xfs; mount reads the superblock and works out the type itself.
A mount that reports correctly can still be read-only or otherwise unusable, so write something before you trust it:
echo "created on xfs" > /mnt/xfsdata/notes.txtRedirection into a new file prints nothing, so confirm the file landed with the size and ownership you expect:
ls -l /mnt/xfsdata/notes.txt-rw-r--r--. 1 root root 15 Aug 8 16:11 /mnt/xfsdata/notes.txtFifteen bytes owned by root with mode 644. XFS stored the ownership and permissions exactly as the process created them, which is the behaviour you should keep in mind when the VFAT filesystem later refuses to do the same.
Finally, ask how much space the mounted filesystem actually offers. The -T flag adds the type column, which is useful when several filesystems are mounted:
df -hT /mnt/xfsdataFilesystem Type Size Used Avail Use% Mounted on
/dev/sdb1 xfs 3.0G 90M 2.9G 3% /mnt/xfsdataThe partition is 3 GB and df agrees on the size, but 90 MB is already used on a filesystem holding a single fifteen byte file. That is the XFS metadata and journal from the creation output, not your data, and the next section on capacity comes back to it.
Mount by label or UUID instead of device name
Device names are assigned in the order the kernel finds disks, so they can change when hardware does. Mounting by label removes that dependency. Unmount first, since the device is currently attached:
umount /mnt/xfsdataWith the mount point free, ask for the filesystem by the label set at creation:
mount LABEL=labxfs /mnt/xfsdataSilent again, so confirm which device the label resolved to:
findmnt -n -o TARGET,SOURCE,FSTYPE /mnt/xfsdata/mnt/xfsdata /dev/sdb1 xfsThe label matched /dev/sdb1 without me naming it. Behind the scenes udev maintains symlinks under /dev/disk/by-label/ and mount follows them, which is why a label you have just changed may need udevadm settle before it can be used this way.
UUIDs work identically and are stronger, because a UUID is generated per filesystem while two disks can easily carry the same label:
umount /mnt/xfsdataThe unmount is silent, so go straight to mounting by the UUID that blkid reported earlier:
mount UUID=a2d95b7d-0394-40d5-a667-6c69da0dc048 /mnt/xfsdataConfirm it resolved to the same device as the label did:
findmnt -n -o TARGET,SOURCE,FSTYPE /mnt/xfsdata/mnt/xfsdata /dev/sdb1 xfsSame device, reached three different ways. For anything permanent, UUID is the safest of the three, and it is what an installer writes into /etc/fstab for exactly that reason.
Mount the ext4 filesystem too
The ext4 filesystem created earlier is still sitting on /dev/sdb2 with nothing attached to it, and the capacity and repair sections further down both expect it to be mounted. The order is the same for every filesystem type: make the mount point, mount the device, verify. Start with the directory:
mkdir -p /mnt/ext4dataThen attach the device, again without naming a type:
mount /dev/sdb2 /mnt/ext4dataSilence, so confirm it rather than assuming:
findmnt /mnt/ext4dataTARGET SOURCE FSTYPE OPTIONS
/mnt/ext4data /dev/sdb2 ext4 rw,relatime,seclabelThe kernel detected ext4 from the superblock and mounted it read-write. Compare that OPTIONS column with the XFS one above: ext4 needed three options where XFS listed nine, because most XFS behaviour is recorded in the geometry at creation time while ext4 keeps its defaults in the superblock. Both filesystems stay mounted from here through the capacity section, so leave them attached.
Fix VFAT Ownership and Permissions with Mount Options
This section exists because the behaviour below looks like a bug the first time you meet it. The third filesystem needs the same treatment as the other two, so give it a mount point first:
mkdir -p /mnt/vfatdataThen mount the VFAT filesystem the same way you mounted XFS and ext4:
mount /dev/sdb3 /mnt/vfatdataVerify it before drawing any conclusions from what follows, because the options are the whole story in this section:
findmnt /mnt/vfatdataTARGET SOURCE FSTYPE OPTIONS
/mnt/vfatdata /dev/sdb3 vfat rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-roNothing was passed on the command line, yet the kernel recorded fmask=0022, dmask=0022, and a character set. Those defaults are where the surprising behaviour below comes from. Now create a file and look at what the kernel reports for it:
echo "created on vfat" > /mnt/vfatdata/notes.txtThe write succeeds silently. The interesting part is how that file is described:
ls -l /mnt/vfatdata/notes.txt-rwxr-xr-x. 1 root root 16 Aug 8 16:11 /mnt/vfatdata/notes.txtMode 755 on a text file nobody asked to be executable. On the ext4 filesystem the identical operation produced 644. Nothing went wrong: FAT has no field on disk for a Unix mode, so the kernel synthesises one for every file from the fmask mount option, and the default produces 755.
Because the mode is invented rather than stored, changing it does not work in the way the exit status suggests:
chmod 600 /mnt/vfatdata/notes.txtchmod exits 0 with no error, which normally means it did the job. Check whether anything actually changed:
ls -l /mnt/vfatdata/notes.txt-rwxr-xr-x. 1 root root 16 Aug 8 16:11 /mnt/vfatdata/notes.txtStill 755. The call was accepted and discarded, because there is nowhere to record mode 600. This silent no-op is worth remembering when a script that hardens permissions appears to run cleanly on removable media and changes nothing.
Ownership behaves the same way, except it does report an error:
chown student /mnt/vfatdata/notes.txtchown: changing ownership of '/mnt/vfatdata/notes.txt': Operation not permittedRunning as root and still not permitted, because the filesystem cannot represent per-file ownership at all. The practical consequence is that an ordinary user cannot write here either:
runuser -u student -- touch /mnt/vfatdata/fromstudent.txttouch: cannot touch '/mnt/vfatdata/fromstudent.txt': Permission deniedThe whole filesystem is presented as owned by root, so student has read and execute access and nothing more. Since the ownership is a property of the mount rather than the files, the fix is to mount it differently. Unmount first:
umount /mnt/vfatdataThen mount again, naming who the files should appear to belong to:
mount -o uid=student,gid=student /dev/sdb3 /mnt/vfatdataThe mount is silent, so read back the options the kernel recorded:
findmnt /mnt/vfatdataTARGET SOURCE FSTYPE OPTIONS
/mnt/vfatdata /dev/sdb3 vfat rw,relatime,uid=1001,gid=1001,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,errors=remount-roThe names resolved to uid=1001,gid=1001. Now look at the file that already existed before this mount:
ls -l /mnt/vfatdatatotal 4
-rwxr-xr-x. 1 student student 16 Aug 8 16:11 notes.txtThe same notes.txt from earlier now belongs to student, without anything being rewritten on disk. That is the clearest possible demonstration that ownership here is a display decision made at mount time. Confirm the practical effect:
runuser -u student -- touch /mnt/vfatdata/fromstudent.txtThe command produces no output, which means it worked this time. Use fmask and dmask alongside uid and gid when you need the synthesised modes to be narrower than the default 755.
Understand Filesystem Capacity
Three different numbers get called "size" and mixing them up leads to bad decisions about disks. This section works on the ext4 filesystem mounted earlier, so confirm it is still attached before writing to it:
findmnt /mnt/ext4dataTARGET SOURCE FSTYPE OPTIONS
/mnt/ext4data /dev/sdb2 ext4 rw,relatime,seclabelIf that prints nothing, the filesystem is not mounted and mount /dev/sdb2 /mnt/ext4data will put it back. With a confirmed mount, write a file large enough to be visible before comparing the numbers:
dd if=/dev/zero of=/mnt/ext4data/big.bin bs=1M count=200200+0 records in
200+0 records out
209715200 bytes (210 MB, 200 MiB) copied, 0.111265 s, 1.9 GB/sExactly 200 MiB of zeroes now occupy real blocks on the ext4 filesystem. Ask df what that did to the filesystem:
df -hT /mnt/ext4dataFilesystem Type Size Used Avail Use% Mounted on
/dev/sdb2 ext4 2.9G 201M 2.6G 8% /mnt/ext4dataSize is 2.9G even though the partition is 3G, and Used is 201M rather than the 200M I wrote.
Both differences are metadata: the filesystem spent part of the device on inode tables and a journal when it was created, and the directory entry plus indirect blocks for a 200 MiB file cost slightly more than the file contents.
du measures something different, and the distinction is the useful one. It walks the directory tree and adds up what the files occupy:
du -sh /mnt/ext4data201M /mnt/ext4dataHere du and the Used column agree, because I put one file on an otherwise empty filesystem.
They diverge in real life: du cannot see space consumed by filesystem metadata, nor space held by a deleted file that a running process still has open, which is the classic reason a disk reports itself full while du insists there is nothing there. The df and du command guide goes deeper into that gap.
The third number is the device itself, and lsblk can show both at once:
lsblk -o NAME,SIZE,FSTYPE,FSSIZE,FSAVAIL /dev/sdb2NAME SIZE FSTYPE FSSIZE FSAVAIL
sdb2 3G ext4 2.9G 2.5GSIZE is the partition at 3G and FSSIZE is the filesystem at 2.9G, side by side in one row. Roughly 100 MB of the partition is filesystem overhead you will never store files in.
There is a further deduction that df hides from ordinary users. ext4 keeps a percentage of blocks for root so that a full filesystem does not immediately break privileged processes:
tune2fs -l /dev/sdb2 | grep -E "Block count|Reserved block count"Block count: 786432
Reserved block count: 3932139321 of 786432 blocks is 5 percent, which at 4 KB per block is about 161 MB set aside. On a root filesystem that reserve is a safety net worth having.
On a large data volume it is space you paid for and cannot use, which is why mkfs.ext4 -m 1 or a later tune2fs -m 1 is common on dedicated data disks. XFS has no equivalent reserve, which is part of why its Avail figure looked closer to its Size earlier.
Unmount a Filesystem
Unmounting flushes pending writes and detaches the filesystem. You can name either end of the relationship, and the mount point is the usual choice:
umount /mnt/xfsdataWhen it works there is no output at all. Run it a second time and you get the one error that is not a problem:
umount: /mnt/xfsdata: not mounted.Exit status 32 with nothing detached, because there was nothing there to detach. That message means you have already reached the state you wanted.
The failure worth understanding is the other one, and it is almost always something using the filesystem. Reproducing it needs the filesystem back:
mount /dev/sdb1 /mnt/xfsdataWith it mounted again, standing inside it is enough to block the unmount:
cd /mnt/xfsdataChanging directory is silent, and now my shell's working directory is inside the filesystem I am about to detach:
umount /mnt/xfsdataumount: /mnt/xfsdata: target is busy.The exit status is 32 and nothing was unmounted. A working directory counts as usage just as much as an open file does, which is why this happens so often to the person doing the maintenance rather than to a service.
Rather than guessing, ask which processes are involved. fuser -vm lists everything using the filesystem containing the given path:
fuser -vm /mnt/xfsdataUSER PID ACCESS COMMAND
/mnt/xfsdata: root kernel mount /mnt/xfsdata
root 22595 ..c.. bashThe ACCESS column is the answer. The c flag means the process is using the directory as its current working directory, and the culprit is my own bash. The kernel row is the mount itself rather than a process, so it is never something you act on. If you saw f instead of c, a process has a file open on the filesystem, and you would want to know which service that is before killing anything.
The fix here needs no force and no -l lazy unmount, just moving out of the way:
cd /rootWith nothing holding the filesystem, the same command that failed now succeeds:
umount /mnt/xfsdataSilence, which means the filesystem is detached. Confirm rather than assume:
findmnt /mnt/xfsdataNo output and exit status 1, which is findmnt reporting that nothing is mounted there. The directory /mnt/xfsdata still exists and is empty again, because the files live on the device rather than in the mount point.
You can also unmount by device, which is handy when you cannot remember where something was attached:
umount /dev/sdb3That detaches the VFAT filesystem from whatever directory it was on, with the same silence on success. Naming the device and naming the mount point are equivalent as long as the device is mounted in exactly one place.
Check and Repair ext4 Filesystems
Two different operations get called the same thing here, and keeping them apart is the whole skill. Checking reads metadata and reports what it finds. Repairing rewrites that metadata to make it consistent again, which means throwing away whatever cannot be reconciled.
Both belong on an unmounted filesystem, and the reason is the same in each case: a live filesystem changes underneath the tool, so what it reads may never have existed as a single consistent state.
The ext4 filesystem is still mounted from the capacity section, so start by doing the tempting thing and checking it in place. The -n flag answers no to every question e2fsck would ask, which is what makes it safe to try:
e2fsck -n /dev/sdb2e2fsck 1.47.1 (20-May-2024)
Warning! /dev/sdb2 is mounted.
Warning: skipping journal recovery because doing a read-only filesystem check.
labext4: clean, 14/196608 files, 82238/786432 blocksRead the two warnings rather than the verdict. e2fsck documents that the results it reports are not valid when the filesystem is mounted, so clean on that last line is not a finding you can act on.
The second warning shows one reason why: journal recovery was skipped, so a filesystem with an unreplayed journal can be reported as damaged purely because of what was not applied. Treat this output as a message telling you to unmount, not as a diagnosis.
The documented order on RHEL is to mount the filesystem and then unmount it, which lets the kernel replay the journal, and only then check the unmounted device. The filesystem has been mounted throughout this walkthrough, so the unmount is the step that matters:
umount /mnt/ext4dataA clean unmount records that fact in the superblock, and tune2fs -l reads it back without checking anything:
tune2fs -l /dev/sdb2 | grep -E "Filesystem state|Last checked"Filesystem state: clean
Last checked: Sat Aug 8 16:50:22 2026clean means the journal has no outstanding work, so a check now sees the same filesystem the kernel last agreed on. A value of not clean means the journal still holds updates, which the kernel replays automatically at the next mount, and that is exactly why the mount-then-unmount step comes first.
Now run the same -n check that was unreliable a moment ago:
e2fsck -n /dev/sdb2e2fsck 1.47.1 (20-May-2024)
labext4: clean, 14/196608 files, 82238/786432 blocksSame verdict, but this time with no warnings above it, which is the difference between a result and a guess. Fourteen inodes and 82238 blocks in use, exit status 0.
-f forces a full check even when the superblock says clean, which is what you want when you are actually investigating rather than confirming:
e2fsck -f /dev/sdb2e2fsck 1.47.1 (20-May-2024)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
labext4: 14/196608 files (0.0% non-contiguous), 82238/786432 blocksAll five passes completed with no questions asked, and the exit status was 0.
This is where checking turns into repair on a filesystem that is not healthy: each pass reports the problems it finds and asks whether to correct them, so the same command that only reported here would start rewriting metadata as soon as you answered yes. That is why -y, which answers yes to everything, deserves real thought before it goes into a script, and why -n is the right first command on a filesystem you do not yet understand.
When the filesystem you need to check is the root filesystem, you cannot unmount it from a running system, and repairing ext4 from rescue mode covers that path.
One side effect is worth knowing about. Read the superblock again:
tune2fs -l /dev/sdb2 | grep -E "Filesystem state|Last checked"Filesystem state: clean
Last checked: Sat Aug 8 16:52:01 2026e2fsck -f wrote the new Last checked time even though it changed nothing else, so a full check is never quite read-only in the way -n is. That distinction matters when you are gathering evidence from a filesystem you may need to hand to someone else.
Put the filesystem back where the rest of the walkthrough expects it:
mount /dev/sdb2 /mnt/ext4dataThe mount is silent, and the filesystem is available again with the 200 MiB file intact.
Check and Repair XFS Filesystems
XFS is the one type on this page where the obvious command is the wrong one. fsck.xfs exists, so fsck /dev/sdb1 and any generic tooling that calls the per-type helper will find it, but it does not check anything:
fsck.xfs /dev/sdb1If you wish to check the consistency of an XFS filesystem or
repair a damaged filesystem, see xfs_repair(8).It printed advice and exited 0, which is a success status on a filesystem it never read. The file is not even a binary:
file -L "$(command -v fsck.xfs)"/usr/sbin/fsck.xfs: a /usr/bin/sh -f script, ASCII text executableA shell script shipped in xfsprogs purely so the fsck framework has something to call at boot, where XFS needs no check because it replays its log at mount. Treat a clean exit from fsck.xfs as no information at all. xfs_repair is the real tool, and like e2fsck it wants the device unmounted and the log already replayed by a clean unmount. The -n flag means no modify, so it reports what it would do without touching anything:
xfs_repair -n /dev/sdb1Phase 1 - find and verify superblock...
Phase 2 - using internal log
- zero log...
- scan filesystem freespace and inode maps...
- found root inode chunk
Phase 3 - for each AG...
- scan (but don't clear) agi unlinked lists...
- process known inodes and perform inode discovery...
- agno = 0
- agno = 1
- agno = 2
- agno = 3
- process newly discovered inodes...
Phase 4 - check for duplicate blocks...
- setting up duplicate extent list...
- check for inodes claiming duplicate blocks...
- agno = 0
- agno = 1
- agno = 2
- agno = 3
No modify flag set, skipping phase 5
Phase 6 - check inode connectivity...
- traversing filesystem ...
- traversal finished ...
- moving disconnected inodes to lost+found ...
Phase 7 - verify link counts...
No modify flag set, skipping filesystem flush and exiting.The phases walk the four allocation groups that mkfs.xfs reported when it created the filesystem. The two No modify flag set lines are -n doing its job: phase 5 rebuilds metadata and is skipped entirely, and nothing is flushed at the end.
Phase 6 mentions moving disconnected inodes to lost+found, but with -n in effect that is a description of what a real repair would do rather than something that happened. No problems were reported, so this filesystem is healthy.
Unlike e2fsck, xfs_repair will not even start on a mounted filesystem. Mount it and try:
mount /dev/sdb1 /mnt/xfsdataThe mount succeeds silently, and now the repair tool has an opinion about it:
xfs_repair -n /dev/sdb1xfs_repair: /dev/sdb1 contains a mounted and writable filesystem
fatal error -- couldn't initialize XFS libraryIt refuses before reading anything. This is deliberate and more helpful than a partial answer, because a repair against a live filesystem can destroy it. If you need to check the root XFS filesystem, boot from rescue media so it is not mounted.
xfs_repair -L zeroes the log rather than replaying it, and it discards whatever metadata updates the log was holding. It is a last resort for a filesystem that cannot mount at all, not a way to get past the mounted-device error. Reach for it only after an unmounted xfs_repair has failed, and expect data loss.
For the routine question of how a mounted XFS filesystem is laid out, xfs_info reads geometry from the live mount and needs no unmount:
xfs_info /mnt/xfsdatameta-data=/dev/sdb1 isize=512 agcount=4, agsize=196544 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=1
= reflink=1 bigtime=1 inobtcount=1 nrext64=1
= exchange=0 metadir=0
data = bsize=4096 blocks=786176, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1, parent=0
log =internal log bsize=4096 blocks=16384, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
= rgcount=0 rgsize=0 extents
= zoned=0 start=0 reserved=0This is the same report mkfs.xfs printed at creation, which makes it a useful way to confirm the geometry of a filesystem somebody else built. The blocks=786176 figure is also what you would compare against after growing the device with xfs_growfs.
Check VFAT Filesystems
FAT has no journal, so a check is the only mechanism for finding inconsistencies after an unclean removal. That is exactly the situation removable media ends up in, and it is why this check matters more on a USB stick than the equivalent does on a server disk.
This filesystem was already detached by the umount /dev/sdb3 above, so there is nothing to unmount here. Confirm that rather than assuming it, because a check against a mounted FAT filesystem has the same validity problem as it does on ext4:
findmnt /dev/sdb3No output and exit status 1, so the device is free. If it did print a row, unmount it with umount /mnt/vfatdata before going any further. With the device free, start with -n, which makes no changes whatever it finds:
fsck.vfat -n /dev/sdb3fsck.fat 4.2 (2021-01-31)
/dev/sdb3: 3 files, 2/523260 clustersThat is the summary and nothing else, because there was nothing to report. The reason to reach for -n first on FAT is that the bare command is interactive: given a problem it stops and asks whether to fix it, so -n is how you find out what is wrong before deciding anything. The three-file count includes the volume label, which occupies a directory entry in the root directory like a file does.
-v gives the same verdict with a verbose report of the structures it walked, which is the version to read when you want to know how the filesystem is laid out:
fsck.vfat -v /dev/sdb3fsck.fat 4.2 (2021-01-31)
Checking we can access the last sector of the filesystem
Boot sector contents:
System ID "mkfs.fat"
Media byte 0xf8 (hard disk)
512 bytes per logical sector
4096 bytes per cluster
32 reserved sectors
First FAT starts at byte 16384 (sector 32)
2 FATs, 32 bit entries
2093056 bytes per FAT (= 4088 sectors)
Root directory start at cluster 2 (arbitrary size)
Data area starts at byte 4202496 (sector 8208)
523260 data clusters (2143272960 bytes)
63 sectors/track, 255 heads
12582912 hidden sectors
4194288 sectors total
Checking for unused clusters.
Checking free cluster summary.
/dev/sdb3: 3 files, 2/523260 clustersThe last line is the verdict: three files using two of 523260 clusters, and no problems reported.
The line above it matters more than it looks. FAT keeps a summary of free clusters that can drift out of step with the actual allocation tables after an unclean unmount, and that mismatch is the single most common thing this tool fixes.
I did manage to create a real inconsistency during this walkthrough by relabelling the filesystem while it was mounted, which the tool reported as a difference between the boot sector and its backup copy. The -a flag repairs what it finds without asking:
fsck.vfat -a /dev/sdb3fsck.fat 4.2 (2021-01-31)
/dev/sdb3: 3 files, 2/523260 clustersThe warning is gone and the summary is clean. fsck.vfat -a repairs automatically without prompting, and where more than one repair is possible it chooses the least destructive approach. Run the read-only -n pass first when you want to inspect the problems before allowing any changes.
Automatic repair is not unique to FAT either: ext4 has e2fsck -p, which fixes what can be fixed safely without human intervention and exits with a distinct status when a problem needs an administrator to decide.
Change Filesystem Labels After Creation
Labels are not fixed at creation. Each filesystem has its own tool, and their rules about mounted devices are the part worth getting right, because two of the three want the filesystem detached first.
XFS uses xfs_admin -L, and xfs_admin(8) is explicit that "devices that are mounted cannot be modified", so the supported order is to unmount, relabel, then mount again. Red Hat documents the same prerequisite. Start by detaching it:
umount /mnt/xfsdataWith the device free, set the new label:
xfs_admin -L xfsvol /dev/sdb1writing all SBs
new label = "xfsvol"writing all SBs is the useful half of that output. XFS keeps a superblock copy in every allocation group, and xfs_admin updates all of them directly on the device, which is precisely why it wants the kernel out of the way. Next, give udev a chance to notice:
udevadm settleThat returns silently once the event queue is empty. Skipping it is what leaves /dev/disk/by-label/ pointing at the old name, and mounting by label then fails for a filesystem that is perfectly healthy. Now mount it back:
mount /dev/sdb1 /mnt/xfsdataThe mount is silent, so confirm the change reached the disk rather than just the tool's output:
blkid /dev/sdb1/dev/sdb1: LABEL="xfsvol" UUID="a2d95b7d-0394-40d5-a667-6c69da0dc048" BLOCK_SIZE="512" TYPE="xfs" PARTLABEL="xfsdata" PARTUUID="b55738e7-94de-4142-9bbe-51d42bd38300"LABEL is now xfsvol while the UUID is unchanged, which is the difference between relabelling and reformatting. The PARTLABEL is also untouched, since that lives in the partition table rather than in the filesystem.
ext4 is the exception in this group. tune2fs -L changes the label online, and /dev/sdb2 is mounted at /mnt/ext4data as this runs:
tune2fs -L ext4vol /dev/sdb2tune2fs 1.47.1 (20-May-2024)Only the version banner, which is tune2fs succeeding quietly. Verify the same way:
blkid /dev/sdb2/dev/sdb2: LABEL="ext4vol" UUID="830f3cbf-d586-4d43-aa50-fde3786a3569" BLOCK_SIZE="4096" TYPE="ext4" PARTLABEL="ext4data" PARTUUID="d8dc1e1e-0e48-455d-9432-935cc3e10270"New label, same UUID. Note that tune2fs enforces the sixteen character ceiling here just as mkfs.ext4 did.
FAT uses fatlabel, and this is the one to be careful with. The VFAT filesystem is still detached from the check above, which is the state it should be in for this. Called with no label, fatlabel reads the current one:
fatlabel /dev/sdb3LABVFATThat is the label set at creation. Writing a new one is equally terse:
fatlabel /dev/sdb3 USBDATANo output, and the change is immediate:
blkid /dev/sdb3/dev/sdb3: LABEL_FATBOOT="USBDATA" LABEL="USBDATA" UUID="7A93-48B7" BLOCK_SIZE="512" TYPE="vfat" PARTLABEL="vfatdata" PARTUUID="c154f0aa-70bc-446b-8a5d-98343bbbd428"Both label fields updated together. The catch is that fatlabel does not refuse to run on a mounted filesystem: it writes to the block device directly, behind the kernel's cached copy of the boot sector. That is how I produced the boot sector mismatch that fsck.vfat -a cleaned up in the previous section. Unmount FAT media before relabelling it.
| Filesystem | Change label with | Mounted? |
|---|---|---|
| XFS | xfs_admin -L xfsvol /dev/sdb1 |
Unmount first |
| ext4 | tune2fs -L ext4vol /dev/sdb2 |
Can be changed while mounted |
| VFAT | fatlabel /dev/sdb3 USBDATA |
Unmount first |
Renaming a label breaks anything that mounts by the old name, so check /etc/fstab and any scripts before you change a label on a system you did not build yourself.
Compare the Three Filesystems Side by Side
This comparison needs all three filesystems attached at once, and VFAT has been unmounted since the check and relabel above, so put it back:
mount /dev/sdb3 /mnt/vfatdataConfirm all three are attached before comparing anything, because an unmounted filesystem silently drops out of the output of every command below rather than reporting an error:
lsblk -o NAME,FSTYPE,LABEL,MOUNTPOINTS /dev/sdbNAME FSTYPE LABEL MOUNTPOINTS
sdb
├─sdb1 xfs xfsvol /mnt/xfsdata
├─sdb2 ext4 ext4vol /mnt/ext4data
└─sdb3 vfat USBDATA /mnt/vfatdataThree partitions, three types, three mount points, and the labels are the ones set in the previous section rather than the originals. Now one command shows how differently the three behave with identical input. I wrote the same twenty two byte string to each:
for m in xfsdata ext4data vfatdata; do printf 'filesystem comparison\n' > /mnt/$m/compare.txt; doneThe loop prints nothing, so compare how the three filesystems describe files that are byte for byte identical:
ls -l /mnt/xfsdata/compare.txt /mnt/ext4data/compare.txt /mnt/vfatdata/compare.txt-rw-r--r--. 1 root root 22 Aug 8 16:15 /mnt/ext4data/compare.txt
-rwxr-xr-x. 1 root root 22 Aug 8 16:15 /mnt/vfatdata/compare.txt
-rw-r--r--. 1 root root 22 Aug 8 16:15 /mnt/xfsdata/compare.txtSame size, same owner, same content, and the VFAT copy is mode 755 while the other two are 644. Nothing about the file caused that; the mode came from the mount options because FAT had nowhere to store the real one.
Capacity tells a similar story about metadata cost:
df -hT /mnt/xfsdata /mnt/ext4data /mnt/vfatdataFilesystem Type Size Used Avail Use% Mounted on
/dev/sdb1 xfs 3.0G 90M 2.9G 3% /mnt/xfsdata
/dev/sdb2 ext4 2.9G 201M 2.6G 8% /mnt/ext4data
/dev/sdb3 vfat 2.0G 12K 2.0G 1% /mnt/vfatdataThree rows worth reading against each other:
- XFS reports the full 3.0G but has 90M in use for a single small file, all of it metadata
- ext4 reports 2.9G rather than 3.0G because its metadata is deducted from the size instead, and its 201M is mostly the test file from earlier
- VFAT has almost no fixed overhead at all, showing 12K used, because it lacks the journal and inode tables the other two spend space on
The consolidated view ties device, filesystem, label, and mount point together:
lsblk -f /dev/sdbNAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
sdb
├─sdb1 xfs xfsvol a2d95b7d-0394-40d5-a667-6c69da0dc048 2.8G 3% /mnt/xfsdata
├─sdb2 ext4 1.0 ext4vol 830f3cbf-d586-4d43-aa50-fde3786a3569 2.5G 7% /mnt/ext4data
└─sdb3 vfat FAT32 USBDATA 7A93-48B7 2G 0% /mnt/vfatdataOne disk, three filesystems, three labels, three mount points. The FSVER column shows FAT32 for the VFAT partition, which is the variant mkfs.vfat selected for a 2 GB device, and the short 7A93-48B7 identifier stands out against the two full UUIDs.
findmnt gives the same information from the mount table's perspective, which is the view that matters when you are working out what is attached where:
findmnt -t xfs,ext4,vfat -o TARGET,SOURCE,FSTYPETARGET SOURCE FSTYPE
/ /dev/mapper/rhel-root xfs
├─/mnt/xfsdata /dev/sdb1 xfs
├─/boot /dev/sda2 xfs
├─/mnt/ext4data /dev/sdb2 ext4
└─/mnt/vfatdata /dev/sdb3 vfatFiltering by type puts the three new filesystems in the same list as the root and boot filesystems, which is a good final sanity check that you built what you intended and nothing landed on the wrong device.
Troubleshoot Filesystem Creation and Mounting
| Symptom | Likely cause | Fix |
|---|---|---|
appears to contain an existing filesystem |
A filesystem is already there and mkfs is protecting it |
Confirm the device is the right one, then add -f for XFS or -F for ext4 |
is mounted; will not make a filesystem here! |
The target device is currently mounted | Unmount it first; the force flags do not override this particular check |
target is busy on umount |
A process has an open file or its working directory inside the mount | Run fuser -vm /mnt/data, move out with cd, or stop the service holding it |
mount point does not exist |
The directory was never created | mkdir -p /mnt/data then mount again |
wrong fs type, bad option, bad superblock |
Wrong -t value, or no filesystem on the device at all |
Drop -t and let mount autodetect; check blkid output and dmesg for the driver's complaint |
Can't open blockdev on mount |
The device is already mounted, so the new mount cannot open it exclusively | Check findmnt -S /dev/sdb3 and unmount it first; this is a busy device, not a damaged one |
Filesystem must be larger than 300MB |
The device is below the XFS minimum size | Use a larger device or choose ext4, which works on much smaller volumes |
Read-only file system when writing |
Mounted read-only, or remounted that way after an error | Check findmnt options; remount with mount -o remount,rw /mnt/data and investigate dmesg if the kernel forced it |
Operation not permitted from chown on VFAT |
FAT cannot store per-file ownership | Remount with uid=, gid=, fmask= and dmask= instead |
chmod succeeds but nothing changes |
Same FAT limitation, applied to mode bits | Set the permissions you want through mount options |
lsblk -f shows no filesystem after mkfs |
udev has not re-read the device yet | Trust blkid, then run udevadm settle or wait a moment |
contains a mounted and writable filesystem |
xfs_repair was pointed at a live filesystem |
Unmount it first, or boot rescue media when it is the root filesystem |
| Mounting by label fails after renaming | The /dev/disk/by-label/ symlink is stale |
Run udevadm settle, or mount by UUID instead |
Two of these deserve a closer look because their error text is misleading. The wrong fs type message appears both when you name the wrong type and when there is no filesystem at all, so it never tells you which. To see it, the device has to be free, because a mounted device produces a different error entirely:
umount /mnt/vfatdataWith /dev/sdb3 detached, naming ext4 on the VFAT partition produced this:
mount -t ext4 /dev/sdb3 /mnt/vfatdatamount: /mnt/vfatdata: wrong fs type, bad option, bad superblock on /dev/sdb3, missing codepage or helper program, or other error.
dmesg(1) may have more information after failed mount system call.Four possible causes in one sentence, which is not much help on its own. The kernel is more specific, and the last line of the message is telling you where to look:
dmesg | tail -3[ 1707.026357] XFS (sdb1): Mounting V5 Filesystem a2d95b7d-0394-40d5-a667-6c69da0dc048
[ 1707.048865] XFS (sdb1): Ending clean mount
[ 1823.759539] EXT4-fs (sdb3): VFS: Can't find ext4 filesystemCan't find ext4 filesystem on sdb3 is unambiguous: the ext4 driver was asked to mount something that is not ext4. Omitting -t entirely avoids the whole class of problem, since mount reads the superblock and picks the right driver.
That is also why the unmount above mattered. Put the filesystem back:
mount /dev/sdb3 /mnt/vfatdataThen run the identical wrong-type command against the device now that it is busy:
mount -t ext4 /dev/sdb3 /mnt/vfatdatamount: /mnt/vfatdata: fsconfig system call failed: /dev/sdb3: Can't open blockdev.
dmesg(1) may have more information after failed mount system call.Nothing here mentions the filesystem type, because the ext4 driver never got far enough to look: the device was already open exclusively by the VFAT mount. Can't open blockdev means busy, not wrong type, and confusing the two sends you hunting for corruption that is not there.
The XFS size floor is the other one that reads oddly, because the number is not documented anywhere near the command you are running. Pointing mkfs.xfs at a 100 MB device gives:
mkfs.xfs /root/small.imgFilesystem must be larger than 300MB.Below roughly 300 MB, XFS declines to create a filesystem at all, because its allocation groups and log need more room than that leaves. ext4 has no comparable floor, so small volumes are one of the few places where ext4 is the only reasonable choice of the two.
There is one refusal you should be glad to see, and it is the reason a mistyped device name is usually survivable. Pointing mkfs.ext4 at a device that is currently mounted gets you nowhere:
mkfs.ext4 /dev/sdb2mke2fs 1.47.1 (20-May-2024)
/dev/sdb2 is mounted; will not make a filesystem here!The command stops before writing anything. What makes this guard more valuable than the signature check is that the force flag does not switch it off:
mkfs.ext4 -F -n /dev/sdb2mke2fs 1.47.1 (20-May-2024)
/dev/sdb2 is mounted; will not make a filesystem here!Same refusal with -F present, and -n here means dry run so nothing was at risk either way. -F overrides the existing-filesystem warning but not the mounted-device check, which means an active production filesystem is protected even from a forced command.
An idle device gets far less protection. On XFS and ext4 only the signature check stands between it and -f or -F, and on VFAT not even that, because mkfs.vfat does not look for an existing filesystem before overwriting it.
Those two letters are also specific to these tools rather than a shared convention. On mkfs.vfat, -f sets the number of file allocation tables and -F selects FAT12, FAT16 or FAT32, and its own safety-check override is -I.
References
- Red Hat Enterprise Linux 10: Managing file systems
- mkfs.xfs(8) manual page
- mke2fs(8) manual page
- mount(8) manual page
- umount(8) manual page
- xfs_repair(8) manual page
- xfs_admin(8) manual page
- mkfs.fat(8) manual page
- e2fsck(8) manual page
- tune2fs(8) manual page
- fsck.fat(8) manual page
- blkid(8) manual page
- wipefs(8) manual page
- findmnt(8) manual page
- Linux kernel documentation: XFS
- Linux kernel documentation: ext4
- dosfstools project repository
Summary
Creating a filesystem is a short command surrounded by decisions that matter more than the command does. The path through this guide was always the same six steps: confirm the block device is genuinely free, run the right mkfs tool, check the result with blkid, mount it somewhere, verify with findmnt and df, and unmount cleanly when you are finished.
On one spare 10 GB disk I built XFS, ext4 and VFAT alongside each other, and the differences showed up immediately in how much space each one spent on metadata and in how each one treated an identical file.
The step worth slowing down on is identification. mkfs has two safety nets and they are not equally strong. The refusal to touch a mounted device is the reliable one and holds against every flag I tried.
Protection for an idle device is much weaker and varies by tool: mkfs.xfs and mkfs.ext4 refuse an existing signature until you override them with -f or -F, while mkfs.vfat never checks and overwrites silently. Those option letters are filesystem specific rather than a general force convention, so read the right manual page instead of carrying a flag across tools.
The practical upshot is that a running production filesystem is well protected and a decommissioned one is barely protected at all. Everything else that protects you comes from lsblk, findmnt, pvs and wipefs before you press Enter, and it is worth being precise about what those tools prove: file -s reporting data only means it recognised no format, not that the device is empty.
Two habits that saved me time here are worth keeping:
- Believe
blkidoverlsblkwhen they disagree, becauselsblkreports a udev cache that lags a fresh format - Remember that reformatting mints a new UUID even when the label is unchanged, which is what silently breaks an
/etc/fstabentry after a supposedly harmless redo
Checking and repairing follow one rule that covers all three types: unmount first. Mounting and then unmounting is what lets the kernel replay the journal, so the check that follows sees a state the filesystem actually agreed on.
e2fsck -n will read a mounted ext4 filesystem, but it warns that the results are not valid and it skips journal recovery. xfs_repair refuses a live device outright rather than giving you half an answer.
Start with the read-only form in every case: xfs_repair -n, e2fsck -n, or fsck.vfat -n. On XFS, ignore fsck.xfs altogether, since it is a shell script that prints advice and exits successfully without reading the filesystem.
The other lasting lesson is that VFAT is a different kind of filesystem rather than a weaker version of the other two. Its files looked executable, chmod accepted a change and discarded it, and chown failed outright as root, all because FAT has no field on disk for Unix ownership and the kernel invents one from uid, gid, fmask and dmask at mount time.
That makes it the right answer for a USB stick a Windows machine has to read and the wrong answer for anything relying on permissions. It is also why its 4 GiB per-file ceiling tends to be discovered at the worst moment.
From here the natural next step is making these mounts permanent, which is a separate file and a separate set of failure modes rather than another mount command. Write the entry by UUID or label rather than by device name, and test it with mount -a before you trust a reboot to it.

