Terug naar hoofdinhoud
Linux concept: filesystems
Op deze pagina
# Topics

Linux concept: filesystems

07 september 2026

Een schijf bevat geen bestanden. Hij bevat een hele lange rij genummerde blokken, allemaal even groot, zonder namen, zonder mappen, en zonder enig idee welk blok bij welk document hoort. Alles wat jij als een bestand ziet, de naam, de map waarin het staat, de eigenaar, de rechten, de wijzigingsdatum, bestaat omdat één stuk software boekhouding in sommige van die blokken schrijft en weer terugleest. Die software is het bestandssysteem, en bijna alles wat een Linux-server verwarrend doet met opslag wordt duidelijk zodra je weet wat het opschrijft.

Dit artikel legt uit wat een bestandssysteem echt is, waar het woord vandaan komt, hoe ext4, XFS, Btrfs, ZFS, FAT en de virtuele bestandssystemen van elkaar verschillen, en hoe je je eigen bestandssysteem leest met df, stat, findmnt, filefrag en dumpe2fs. Daarna gaat het eronder kijken: inodes, blokken, extents, journals, uitgestelde toewijzing, sparse bestanden en copy-on-write.

Van "wat is hier gekoppeld" naar "waarom is dit bestand 4 KB als er één byte in staat".

Het doel: na het lezen kun je bij elk Linux-opslagprobleem zeggen bij welke laag het hoort.

1. De basis

Een bestandssysteem is de structuur die een systeem op een opslagapparaat schrijft, zodat kale blokken bruikbaar worden als bestanden met namen in mappen. Het is tegelijk een indeling op schijf en de kernelcode die die indeling begrijpt.

Begin bij wat de hardware aanbiedt. Een schijf, een SSD, een partitie, een LVM-volume: ze geven de kernel allemaal hetzelfde eenvoudige ding, een genummerde reeks blokken van vaste grootte die je kunt lezen en schrijven. Blok 0, blok 1, blok 2, tot een paar honderd miljoen aan toe. Meer structuur is er niet. Als jij een bestand factuur.pdf wil dat van jou is, 240 KB groot is en in /home/peter/admin/ staat, dan moet elk onderdeel van die zin door iemand in die blokken worden gezet.

Het bestandssysteem is die iemand. Het bepaalt welke blokken je inhoud bevatten, schrijft een record met "dit bestand is 240 KB, eigendom van gebruiker 1000, en de inhoud staat in blok 900.112 tot 900.171", en schrijft een tweede record in de map met "de naam factuur.pdf verwijst naar dat record". Al het andere volgt uit die twee ideeën.

1.1 Wat een bestandssysteem eigenlijk is

Het helpt om te zien dat het woord op drie manieren wordt gebruikt, want Linux gebruikt ze alle drie en ze betekenen niet hetzelfde:

Het woord betekentVoorbeeld
Een indeling: de opzet op schijf en de regels ervoor"ext4 is een journalend bestandssysteem"
Een exemplaar: één geformatteerd apparaat, ergens gekoppeld"het bestandssysteem op /dev/sda2 zit voor 96% vol"
De boom: alles wat vanaf / bereikbaar is"het bestand staat ergens in het bestandssysteem"

Als iemand zegt "het bestandssysteem is vol", bedoelt hij de tweede. Zegt iemand "controleer het bestandssysteem", dan bedoelt hij meestal ook fsck op die tweede. Zegt een programmeur "lees het van het bestandssysteem", dan bedoelt hij de derde. Die drie uit elkaar houden haalt verrassend veel verwarring uit een gesprek over opslag.

1.2 Drie dingen, geen één: data, metadata en namen

Het nuttigste idee uit dit hele artikel is dat een bestand niet één object is. Het zijn drie losse dingen die het bestandssysteem op drie losse plaatsen bewaart:

DIRECTORY ENTRY     "invoice.pdf"  →  inode 61743294       (the name)
        |
        v
INODE 61743294      size, owner, group, permissions, three
                    timestamps, link count, and a map of blocks   (the facts)
        |
        v
DATA BLOCKS         900112, 900113, 900114, ...                  (the content)

De mapvermelding is alleen een naam plus een nummer. De inode bevat elk feit over het bestand behalve de naam. De datablokken bevatten de bytes. Een map is geen doos waar bestanden in zitten; het is een kleine tabel met namen en inodenummers, en die map is zelf ook een bestand.

Het juiste mentale model: een bestandsnaam is een verwijzing, geen bestand. Het bestand is de inode plus zijn blokken. Deze ene scheiding verklaart harde links, waarom verwijderen direct klaar is, waarom een volle schijf vol kan blijven na rm, en waarom een bestand verplaatsen binnen één bestandssysteem helemaal geen tijd kost.

1.3 Eén boom, veel bestandssystemen

Windows geeft elk geformatteerd volume een eigen letter. Linux doet het omgekeerd: er is precies één boom, die begint bij /, en elk ander bestandssysteem wordt daarin geënt op een koppelpunt, wat gewoon een bestaande map is. Na het koppelen zie je nergens in een pad waar het ene bestandssysteem ophoudt en het volgende begint.

$ findmnt -o TARGET,SOURCE,FSTYPE
TARGET            SOURCE                             FSTYPE
/                 /dev/mapper/ubuntu--vg-ubuntu--lv  ext4
├─/boot           /dev/nvme0n1p2                     ext4
│ └─/boot/efi     /dev/nvme0n1p1                     vfat
├─/dev/shm        tmpfs                              tmpfs
└─/run            tmpfs                              tmpfs

Vier verschillende bestandssystemen op drie soorten opslag, en één ervan (tmpfs) staat helemaal niet op een schijf. Een gebruiker die cd /boot/efi typt, steekt twee grenzen over zonder het te merken, en dat is precies de bedoeling.

Het deel van de kernel dat dit mogelijk maakt is de VFS, het Virtual File System. Het definieert één set bewerkingen, open, read, write, rename, en elk type bestandssysteem implementeert die. Jouw programma roept open() aan en komt nooit te weten of het antwoord van ext4 kwam, van een USB-stick met FAT, van een netwerkshare of uit het geheugen. Het artikel over de kernel behandelt de VFS als een van de kerntaken van de kernel; hier is het van belang omdat het de reden is dat alle commando's hieronder op elk type bestandssysteem hetzelfde werken.

1.4 Wat er onder zit

Het bestandssysteem is één laag in een stapel, en opslagproblemen zijn veel makkelijker op te lossen als je weet naar welke laag je kijkt. Op de machine waarop dit artikel is gemaakt, is de stapel vijf lagen diep:

$ lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT
NAME                         SIZE TYPE  FSTYPE       MOUNTPOINT
nvme0n1                      1.9T disk
├─nvme0n1p1                    1G part  vfat         /boot/efi
├─nvme0n1p2                    2G part  ext4         /boot
└─nvme0n1p3                  1.9T part  crypto_LUKS
  └─dm_crypt-0               1.9T crypt LVM2_member
    └─ubuntu--vg-ubuntu--lv  1.9T lvm   ext4         /

Lees het van onder naar boven: een ext4-bestandssysteem staat op een logisch volume, dat in een LVM-volumegroep zit, die in een versleutelde LUKS-container zit, die een partitie op een NVMe-schijf is. Elke laag ziet alleen genummerde blokken van de laag eronder en geeft genummerde blokken door aan de laag erboven.

Door die gelaagdheid zijn "de schijf is vol" en "het bestandssysteem is vol" twee verschillende zinnen, kun je een bestandssysteem vergroten zonder de schijf aan te raken, en is versleuteling onzichtbaar voor ext4. Het is ook de volgorde waarin je moet denken als er iets stukgaat: een schijffout, een LUKS-probleem, een LVM-probleem en een probleem in het bestandssysteem zien er allemaal uit als "ik kan niet bij mijn bestanden".

Naar boven

2. Waar komt de naam vandaan?

De Engelse naam is een samenstelling van twee gewone woorden, en beide helften zijn met zorg gekozen.

File komt uit het kantoorwerk. Een papieren dossier was een set documenten in volgorde, van het Franse fil, een draad, omdat papieren ooit letterlijk aan een draad of ijzerdraad werden geregen om ze bij elkaar te houden. De vroege informatica nam de kantoorwoorden compleet over: files, folders, records, filing. Toen Unix een stroom bytes op schijf een "file" noemde, deed het daarmee een belofte: dit ding gedraagt zich als dat papieren ding, het heeft een naam, je kunt het ergens neerleggen, en je kunt het terugvinden.

System is de eerlijke helft. Het gaat niet om het losse bestand maar om het georganiseerde geheel: de naamgeving, de hiërarchie, de boekhouding en de regels die alles kloppend houden. Een bestandssysteem is het archiefsysteem.

De schrijfwijze verschilt en beide vormen zijn goed. Klassieke Unix-documentatie schrijft file system als twee woorden; de Linux-kernelgemeenschap, en dit artikel, schrijven meestal filesystem aan elkaar. Je komt ze allebei tegen in dezelfde handleiding.

Een paar andere woorden in dit artikel hebben hun eigen kleine geschiedenis, en die kennen maakt de gereedschappen makkelijker te onthouden:

WoordWaar het vandaan komt
inodeKort voor index node, indexknoop. De inodes vormen een genummerde index; dat nummer is de echte identiteit van het bestand.
superblokHet ene blok "boven" alle andere, met de beschrijving van het hele bestandssysteem: grootte, blokgrootte, functievlaggen, toestand.
mountUit het tijdperk van tapes en schijfpakketten, toen een operator een spoel of plaat fysiek in een station moest hangen voordat het systeem hem kon lezen.
blokDe kleinste eenheid ruimte die het bestandssysteem uitdeelt, tegenwoordig vrijwel altijd 4096 bytes. Niet hetzelfde als de sector van 512 bytes waarin de hardware praat.
journalUit de boekhouding: een doorlopend logboek van wat je gaat doen, geschreven voordat je het doet.
extExtended file system, uitgebreid bestandssysteem. Het breidde het zeer beperkte Minix-bestandssysteem uit waar het vroege Linux aan vastzat.

Let op wat "inode" impliceert. Omdat de identiteit van een bestand een nummer in een index is, en de naam een losse vermelding die naar dat nummer wijst, kunnen die twee zich veel-op-een verhouden. Dat is geen slimme functie die later is toegevoegd; het volgt rechtstreeks uit de naamgeving.

Naar boven

3. Een korte geschiedenis

Het ontwerp van bestandssystemen is gedreven door drie soorten druk, in deze volgorde: schijven überhaupt bruikbaar maken, een crash overleven, en meegroeien met schijven die veel harder groeiden dan iemand had gepland.

Unix had in 1969 het inode-idee al, en dat is in vijftig jaar nauwelijks veranderd. Wat wel veranderde waren snelheid en veiligheid. Het oorspronkelijke Unix-bestandssysteem verspreidde inodes en data over de schijf en verloor veel tijd met zoeken. In 1983 introduceerde het Berkeley Fast File System cilindergroepen, grotere blokken en het idee om de metadata van een bestand dicht bij zijn data te houden, en maakte Unix-schijven meerdere keren sneller. Vrijwel elk bestandssysteem sindsdien heeft dat idee in een of andere vorm overgenomen.

Linux begon met het Minix-bestandssysteem, dat bestandsnamen van maar 14 tekens toestond en partities van maar 64 MB. Die grens is de reden dat ext überhaupt bestaat: Remy Card schreef in 1992 het Extended File System om eraan te ontsnappen, en verving het een jaar later door ext2, dat tien jaar lang de Linux-standaard bleef.

De volgende druk was herstel na een crash. Op een groot ext2-bestandssysteem betekende een onverwachte stroomuitval een volledige fsck, en een volledige fsck op een grote schijf kon uren duren terwijl de server plat lag. Journalen loste dat op, en ext3 (2001) voegde het toe aan ext2 zonder de indeling op schijf te veranderen, zodat je ter plekke kon bijwerken. ext4 (2006, stabiel in 2008) voegde daarna extents, uitgestelde toewijzing, veel ruimere grenzen en snellere toewijzing toe, en is nog steeds de standaard in de meeste distributies.

De derde druk was schaal, en die leverde een ander soort bestandssysteem op. XFS kwam in 1994 van Silicon Graphics voor machines die video verwerkten, werd in 2000 als open source vrijgegeven, en was vanaf het begin gebouwd voor zeer grote bestanden en veel gelijktijdige schrijvers. ZFS (Sun, uitgebracht in 2005) en Btrfs (in 2007 begonnen bij Oracle, vanaf 2009 in de hoofdkernel) gingen verder en trokken de volumebeheerder in het bestandssysteem, met checksums op alles, snapshots en copy-on-write.

JaarMijlpaal
1969Unix introduceert inodes, mappen als bestanden, en één gekoppelde boom
1977FAT verschijnt bij Microsoft; nog steeds de indeling van bijna elke USB-stick en SD-kaart
1983Het Berkeley Fast File System maakt Unix-schijven snel, met cilindergroepen en grotere blokken
1985Het vnode- en VFS-ontwerp van Sun laat één kernel meerdere typen bestandssystemen tegelijk koppelen, inclusief NFS
1992ext verschijnt en bevrijdt Linux van de 14 tekens en 64 MB van Minix
1993ext2 wordt de Linux-standaard en blijft dat tien jaar
1994SGI levert XFS op IRIX, ontworpen voor zeer grote bestanden en parallelle I/O
1995VFAT voegt lange bestandsnamen toe aan FAT, met naast elke naam een korte 8.3-naam
2001ext3 voegt een journal toe aan ext2 en maakt een eind aan de urenlange fsck na een stroomstoring
2005ZFS komt uit en voegt volumebeheer, checksums en snapshots samen in één laag
2008ext4 wordt stabiel verklaard en brengt extents en uitgestelde toewijzing naar het standaardbestandssysteem
2009Btrfs komt in de hoofdkernel als Linux' eigen copy-on-write-bestandssysteem
2013F2FS komt van Samsung, ontworpen voor de flashchips in telefoons in plaats van voor draaiende schijven
Nuext4 blijft de veilige standaard; XFS is standaard bij Red Hat, Btrfs bij SUSE en Fedora

Eén licentiedetail verklaart een praktisch ongemak. ZFS is uitstekend en mag niet in de Linux-kernel worden meegeleverd, omdat de CDDL-licentie ervan en de GPL van de kernel als onverenigbaar worden gezien. Daarom komt ZFS op Linux als een losse module die je installeert en bij elke kernel opnieuw bouwt, terwijl Btrfs, dat op dezelfde doelen mikt, er gewoon is.

Naar boven

4. Eenvoudige toepassingen: je eigen systeem lezen

Vier commando's beantwoorden bijna elke dagelijkse vraag over bestandssystemen. Leer die eerst, want elk dieper hoofdstuk hieronder is een uitleg van iets wat een van deze vier afdrukt.

4.1 Wat is gekoppeld, en van welk type

Het klassieke gereedschap is df (kort voor "disk free"). De vlag -T (kort voor "print type") is degene die mensen vergeten, en het is de nuttige:

$ df -hT
Filesystem                        Type   Size  Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv ext4   1.9T  1.7T   77G  96% /
/dev/nvme0n1p2                    ext4   2.0G  186M  1.7G  11% /boot
/dev/nvme0n1p1                    vfat   1.1G   42M  1.1G   4% /boot/efi
tmpfs                             tmpfs   32G  492M   31G   2% /dev/shm

-h is kort voor "human readable" en maakt van blokken G en M. Voordat je iets afstelt, defragmenteert of online advies gaat zoeken: draai dit. Het antwoord in de kolom Type bepaalt welk deel van dit artikel op jou van toepassing is.

findmnt toont dezelfde koppelingen als een boom, met de opties waarmee ze gekoppeld zijn, en die laat df nooit zien:

$ findmnt /boot/efi
TARGET    SOURCE         FSTYPE OPTIONS
/boot/efi /dev/nvme0n1p1 vfat   rw,relatime,fmask=0022,dmask=0022,codepage=437,...

En stat -f (de -f is kort voor "file system") vraagt het aan het bestandssysteem zelf in plaats van de koppeltabel te lezen:

$ stat -f /
  File: "/"
    ID: b147cbc2ef6dfddb Namelen: 255     Type: ext2/ext3
Block size: 4096       Fundamental block size: 4096
Blocks: Total: 491176740  Free: 44949837   Available: 19980980
Inodes: Total: 124829696  Free: 110260882

Drie dingen in die uitvoer zijn het waard om twee keer te lezen. Namelen: 255 is de maximale lengte van een bestandsnaam, en hoofdstuk 7.2 laat zien dat de eenheid bytes is en geen tekens. Free en Available schelen 25 miljoen blokken, ongeveer 95 GiB die bestaat, ongebruikt is en niet van jou is; hoofdstuk 7.3 legt uit waar die is gebleven. En Type: ext2/ext3 is geen fout en geen degradatie: ext2, ext3 en ext4 delen het magische getal 0xEF53 in het superblok, dus deze algemene weergave kan ze niet uit elkaar houden. Vertrouw op df -T voor de echte naam.

4.2 Hoe vol: twee verschillende vragen

Een bestandssysteem kan door twee losse voorraden heen raken, en maar één daarvan is ruimte. Blokken bevatten je inhoud; inodes bevatten de identiteit van je bestanden, en op ext4 ligt het aantal inodes vast bij het aanmaken en kan het nooit meer groeien:

$ df -h /                 # blocks: how much content fits
Filesystem                         Size  Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv  1.9T  1.7T   77G  96% /

$ df -i /                 # inodes: how many files fit
Filesystem                            Inodes    IUsed     IFree IUse% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv  124829696 14568814 110260882   12% /

Dit bestandssysteem heeft plaats voor 124,8 miljoen bestanden en bevat er 14,5 miljoen. De twee percentages bewegen los van elkaar: 96% van de ruimte is gebruikt maar slechts 12% van de inodes. Een mailspool of een sessiemap kan dat volledig omdraaien en op 100% inodes komen terwijl de schijf halfleeg is, waarna elke schrijfactie faalt met "No space left on device" terwijl df -h volhoudt dat er ruimte zat is. Hoofdstuk 5.5 behandelt dat probleem in detail.

4.3 De dierentuin aan bestandssystemen

In normaal Linux-werk kom je een stuk of twaalf typen bestandssystemen tegen. Dit is waar elk ervan voor bedoeld is:

TypeWaarvoor het bedoeld isGoed om te weten
ext4De algemene standaard in de meeste distributiesJournalend, op extents gebaseerd, saai in de beste zin. Vast aantal inodes. Kan online groeien, alleen ontkoppeld krimpen.
XFSGrote bestanden, veel gelijktijdige schrijvers; standaard bij Red Hat en afgeleidenVerdeelt het apparaat in allocation groups die onafhankelijk toewijzen, en daar komt de parallelliteit vandaan. Inodes worden op aanvraag gemaakt, dus ze kunnen niet op raken. Het kan groeien maar nooit krimpen.
BtrfsCopy-on-write met snapshots, checksums en ingebouwde RAIDSubvolumes en snapshots zijn goedkoop. Vrije ruimte is echt lastig te rapporteren; zie hoofdstuk 6.7.
ZFSDezelfde ideeën, ouder en zeer goed beproefd, populair voor opslagserversZit om licentieredenen niet in de kernel, dus het is een losse module. Wil veel RAM.
F2FSFlashopslag: telefoons, SD-kaarten, sommige embedded systemenLog-gestructureerd: het voegt schrijfacties achter elkaar toe in grote segmenten in plaats van ze te verspreiden, en daar is flashhardware het snelst in. Zie hoofdstuk 7.9.
vfat / exFATUSB-sticks, SD-kaarten, de EFI-opstartpartitieGeen eigenaar, geen rechten, geen symbolische links. FAT32 kan geen bestand van 4 GiB of meer bevatten; exFAT haalt die grens weg.
NTFSWindows-schijven lezenHet moderne kernelstuurprogramma heet ntfs3, toegevoegd in Linux 5.15; oudere systemen gebruiken het op FUSE gebaseerde ntfs-3g.
tmpfsBestanden die in RAM leven en bij het opnieuw opstarten verdwijnenDraagt /run, /dev/shm en vaak /tmp. Wordt in geheugen afgemeten, en kan swappen.
squashfsAlleen-lezen gecomprimeerde images: snaps, live-cd's, appliancesAltijd 100% vol, want het is precies zo groot als zijn inhoud.
overlayfsEen schrijfbare laag op een alleen-lezen laag stapelen; de basis van container-imagesBehandeld in het Docker-artikel, waar de kosten van copy-up het meest tellen.
APFS / HFS+Mac-schijvenHFS+ is leesbaar met de module hfsplus; APFS heeft helemaal geen stuurprogramma in de hoofdkernel. Zie hoofdstuk 7.8.
NFS / SMBOpslag op een andere machine, gekoppeld als een gewone mapDezelfde systeemaanroepen, heel andere manieren om stuk te gaan: een server die niet meer antwoordt kan elk proces dat de koppeling aanraakt laten hangen.
FUSEBestandssystemen geschreven als gewone programma's, in user spaceGeen indeling maar een brug: sshfs, ntfs-3g, cloudopslag en archiefbladeraars komen allemaal zo binnen. Zie hoofdstuk 4.4.
proc / sysfsKerneltoestand gepresenteerd als bestandenHelemaal geen opslag. Elke leesactie draait kernelcode en maakt het antwoord ter plekke.

Een netwerkbestandssysteem verdient een eigen waarschuwing, want het is de ene rij waar de VFS-abstractie je een kleine ondienst bewijst. open() en read() zien er op een NFS-koppeling identiek uit, maar eronder zijn het netwerkverzoeken, en de vragen die daarop volgen zijn vragen over gedistribueerde systemen in plaats van over opslag: wat gebeurt er terwijl de server onbereikbaar is, hoe horen gebruikers-ID's op twee machines overeen te komen, werkt vergrendelen, en wanneer is een schrijfactie echt duurzaam. Een harde NFS-koppeling waarvan de server verdwijnt blokkeert processen in toestand D tot hij terugkomt, en dat is precies de puzzel rond de load average die het artikel over top beschrijft.

Heb je geen sterke reden om te kiezen, kies dan ext4. Het is degene die je distributie het meest test, degene die elk herstelgereedschap begrijpt, en degene met de minste verrassingen in de boekhouding van vrije ruimte. Kies XFS als je zeer grote bestanden met veel processen tegelijk schrijft, en kies Btrfs of ZFS als je echt snapshots en checksums wil en bereid bent hun gereedschap te leren.

4.4 De meeste van je bestandssystemen staan niet op een schijf

Tel de koppelingen op een gewone desktop en de uitkomst is verrassend:

$ awk '{print $3}' /proc/mounts | sort | uniq -c | sort -rn | head -6
     48 squashfs        # one per installed snap package, all read-only
      5 tmpfs           # /run, /dev/shm, /run/lock, per-user runtime dirs
      5 nsfs            # namespace references, not storage at all
      3 ext4            # the only real disks: /, /boot, and one bind mount
      1 vfat            # /boot/efi
      1 tracefs         # kernel tracing, presented as files

Drie van die ruim zestig bestandssystemen bewaren bytes op een schijf. De rest zijn gecomprimeerde alleen-lezen images, geheugen, of kerneltoestand verkleed als bestanden. De kernel houdt de lijst met typen die hij kan koppelen bij in een eigen bestand:

$ grep -v nodev /proc/filesystems      # the ones that need a real device
        ext3
        ext2
        ext4
        squashfs
        vfat
        fuseblk

Twee van die regels zijn het waard om bij stil te staan, want het zijn eigenlijk helemaal geen bestandssystemen. fuseblk en fuse zijn FUSE, Filesystem in Userspace: een kernelmodule die elke bewerking doorstuurt naar een gewoon programma in plaats van hem zelf af te handelen. Dat programma mag antwoorden wat het wil, en zo krijg je een bestandssysteem dat gedragen wordt door een SSH-verbinding, een Windows-partitie, een cloudbucket, een ZIP-archief of een AppImage. Er draaien er op dit moment twee op deze desktop:

$ grep fuse /proc/mounts
portal     /run/user/1000/doc  fuse.portal      rw,nosuid,nodev,relatime,user_id=1000,...
gvfsd-fuse /run/user/1000/gvfs fuse.gvfsd-fuse  rw,nosuid,nodev,relatime,user_id=1000,...

De prijs is een heenreis naar user space en terug voor elke bewerking, dus een FUSE-bestandssysteem is langzamer dan een kernelbestandssysteem. De opbrengst is dat iedereen er een kan schrijven, in elke taal, zonder kernelcode en zonder root, en dat een crash een proces neerhaalt in plaats van de machine. sshfs, ntfs-3g, rclone mount en de prullenbak van je bestandsbeheerder zijn allemaal dit.

Elke andere regel in dat bestand draagt de aanduiding nodev, wat betekent "heeft geen blokapparaat nodig": proc, sysfs, tmpfs, cgroup2, overlay en nog een stuk of twintig. Die aanduiding is het formele verschil tussen een bestandssysteem dat je gegevens bewaart en een dat alleen iets als bestanden presenteert, en de verhouding tussen de twee lijsten is de duidelijkste manier om te zien hoe ver Unix het idee heeft doorgevoerd dat alles op een bestand moet lijken.

Naar boven

5. Gematigde toepassingen: inodes, namen en blokken

Dit hoofdstuk neemt het model met drie delen uit hoofdstuk 1.2 en laat elk deel op een echt systeem zien. Alles hier werkt op ext4 en heeft geen root nodig.

5.1 Een inode lezen met stat

ls -l geeft een gepolijste samenvatting. stat laat de inode bijna rauw zien, en dat is het gereedschap waar je naar grijpt zodra een bestand zich vreemd gedraagt:

$ printf 'x' > tiny.txt        # a file containing exactly one byte

$ stat tiny.txt
  File: tiny.txt
  Size: 1          Blocks: 8          IO Block: 4096   regular file
Device: 252,1      Inode: 61743294    Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   pe7er)   Gid: ( 1000/   pe7er)
Access: 2026-09-06 22:14:03.840817742 +0200
Modify: 2026-09-06 22:14:03.840817742 +0200
Change: 2026-09-06 22:14:03.840817742 +0200
 Birth: 2026-09-06 22:14:03.840817742 +0200

Lees dat veld voor veld, want elke regel is een feit dat het bestandssysteem ergens heeft moeten opschrijven:

VeldBetekenis
Size: 1De inhoud is één byte lang.
Blocks: 8Acht eenheden van 512 bytes, dus 4096 bytes, zijn toegewezen om die ene byte te bevatten. Zie 5.4.
Inode: 61743294De echte identiteit van het bestand op dit bestandssysteem. De naam staat niet in de inode.
Device: 252,1Op welk bestandssysteem het staat. Een inodenummer is alleen uniek binnen één apparaat.
Links: 1Hoeveel namen hierheen wijzen. Het bestand verdwijnt als dit nul wordt.
Access (atime)Laatst gelezen. Goedkoop om over te slaan, en wordt meestal overgeslagen; zie 5.6.
Modify (mtime)Laatste keer dat de inhoud veranderde. Dit is degene die ls -l toont.
Change (ctime)Laatste keer dat de inode veranderde, en dat is ook een hernoeming of een chmod. Je kunt hem niet zelf zetten.
BirthAanmaaktijd. ext4 heeft die altijd bewaard; Linux kreeg pas in 2017 met statx een manier om hem te lezen.

Let op wat er niet staat: de bestandsnaam komt alleen op de eerste regel voor, en alleen omdat jij hem intypte. De inode zelf weet niet hoe hij heet.

5.2 Een naam is geen bestand

Geef dezelfde inode een tweede naam en beide namen zijn even echt. Geen van beide is het origineel:

$ ln tiny.txt hard.txt          # a hard link: a second name, same inode

$ stat -c '%n inode=%i links=%h' tiny.txt hard.txt
tiny.txt inode=61743294 links=2
hard.txt inode=61743294 links=2

Verwijder er één en de data blijft bestaan, want rm verwijdert geen bestanden. Het haalt een naam weg en verlaagt de linkteller; het bestandssysteem geeft de blokken pas vrij als die teller nul wordt en niemand het bestand meer open heeft. Dat is het hele mechanisme achter de schijf die vol blijft nadat je een logbestand hebt verwijderd, en het artikel over du volgt dat in detail.

Een symbolische link is iets heel anders: een eigen inode, met een pad als tekst erin.

$ ln -s tiny.txt soft.txt

$ stat -c '%n inode=%i size=%s type=%F' soft.txt
soft.txt inode=61743295 size=8 type=symbolic link

De grootte is 8 bytes, precies de lengte van de tekst tiny.txt. Meer is een symbolische link niet: een klein bestand waarvan de inhoud een pad is, dat bij elk gebruik opnieuw wordt opgezocht. Hij mag naar een ander bestandssysteem wijzen en naar dingen die niet bestaan; een harde link kan geen van beide, omdat een inodenummer buiten zijn eigen bestandssysteem niets betekent.

5.3 De metadata die niet in de inode staat

De inode heeft een vast aantal velden, en veertig jaar lang was dat alle metadata die een bestand kon hebben. Moderne bestandssystemen voegen een tweede, open opslag toe: uitgebreide attributen, willekeurige paren van naam en waarde die aan een bestand hangen. Dat zijn de functie ext_attr en de koppeloptie user_xattr die je terugziet in de uitvoer van dumpe2fs in hoofdstuk 6.1.

De gebruikelijke gereedschappen zijn setfattr en getfattr uit het pakket attr, dat veel distributies niet standaard installeren. De systeemaanroepen zijn er altijd, dus alles kan ze gebruiken:

$ python3 -c "
import os
os.setxattr('tiny.txt', 'user.author', b'Peter Martin')
print(os.listxattr('tiny.txt'), os.getxattr('tiny.txt', 'user.author'))
"
['user.author'] b'Peter Martin'

Attribuutnamen zitten in vier naamruimtes, en het voorvoegsel bepaalt wie ze mag schrijven:

NaamruimteGebruikt voor
user.Wat je maar wil. Programma's bewaren hier labels, checksums, herkomst-URL's en opmerkingen.
security.SELinux-labels, en security.capability, waarmee een programma één rootbevoegdheid krijgt zonder setuid root te zijn.
system.Kernelgebruik, inclusief toegangscontrolelijsten.
trusted.Alleen zichtbaar voor processen met CAP_SYS_ADMIN.

Toegangscontrolelijsten (ACL's) blijken dan een gebruiker van datzelfde mechanisme te zijn en geen losse functie. De klassieke bits voor eigenaar, groep en anderen kunnen "en deze ene extra gebruiker mag schrijven" niet uitdrukken, dus ACL's voegen vermeldingen per gebruiker en per groep toe. Ze worden aangezet met de koppeloptie acl, die op ext4 standaard aanstaat:

$ setfacl -m u:root:rw tiny.txt      # give one named user write access

$ ls -l tiny.txt
-rw-rw-r--+ 2 pe7er pe7er 1 Sep  6 22:14 tiny.txt      # note the trailing +

$ getfacl -c tiny.txt
user::rw-
user:root:rw-                       # the extra entry
group::rw-
mask::rw-
other::r--

Die + achter de rechten is de enige hint die ls je geeft, en je kijkt er makkelijk overheen terwijl je je afvraagt waarom een gebruiker kan schrijven in een bestand dat dat lijkt te verbieden. Waar staat de ACL? In een uitgebreid attribuut:

$ python3 -c "import os; print(os.listxattr('tiny.txt'))"
['user.author', 'system.posix_acl_access']

Twee praktische gevolgen. De vermelding mask is een bovengrens voor elke benoemde vermelding, en chmod op een bestand met een ACL verandert het masker in plaats van de vermeldingen, waardoor rechten uit zichzelf lijken terug te springen. En uitgebreide attributen worden standaard niet meegekopieerd: gebruik cp -a (of --preserve=xattr), rsync -X en tar --xattrs, of je laat stilletjes ACL's, SELinux-labels en bestandscapabilities vallen terwijl je "alles" kopieert.

5.4 Waarom een bestand van één byte vier kilobyte kost

Het bestandssysteem deelt ruimte uit in hele blokken, normaal 4096 bytes, dus een bestand van één byte neemt een heel blok in en verspilt er 4095 van:

$ du -h --apparent-size tiny.txt
1       tiny.txt              # the content

$ du -h tiny.txt
4.0K    tiny.txt              # the allocation

Bij één bestand is dat een curiositeit. Op schaal is het een begrotingspost: 100.000 bestanden van 100 bytes bevatten 10 MB aan inhoud en nemen 410 MB schijf in. Mappen vol PHP-sessies, mailberichten, miniaturen of Git-objecten kosten routineus tien keer wat ze wegen.

De blokgrootte wordt gekozen als het bestandssysteem wordt aangemaakt en kan daarna niet worden veranderd zonder opnieuw te formatteren. Op Linux is hij feitelijk altijd 4096, want dat is de paginagrootte van het geheugen, en een blok groter dan een pagina is nooit eenvoudig te ondersteunen geweest.

5.5 Inodes op terwijl df zegt dat er ruimte is

Omdat ext4 het aantal inodes vastlegt bij het aanmaken, kun je door je bestanden heen raken terwijl er ruimte over is. Het aantal volgt uit één instelling in /etc/mke2fs.conf:

$ grep inode_ratio /etc/mke2fs.conf
        inode_ratio = 16384

Eén inode per 16 KB bestandssysteem. Op een volume van 2 TB zijn dat de 124,8 miljoen inodes uit hoofdstuk 4.2, ruim voldoende voor gewoon gebruik. Niet ruim voldoende voor een mailserver, en mkfs laat je dat vooraf zeggen:

$ mkfs.ext4 -T news  /dev/sdb1     # one inode per 4 KB: many small files
$ mkfs.ext4 -i 4096  /dev/sdb1     # the same thing, said directly
$ mkfs.ext4 -T largefile /dev/sdb1 # one inode per 1 MB: video, backups, images

Het verschil is niet subtiel. Hetzelfde apparaat van 512 MB krijgt, op drie manieren geformatteerd, 512, 32.768 of 131.072 inodes. Kies verkeerd richting "largefile" op een mailspool en het bestandssysteem zit vol na een paar honderd berichten terwijl df -h laat zien dat het leeg is. Kies verkeerd de andere kant op en je besteedt schijfruimte aan een inodetabel die niemand gebruikt.

Meldt een schijf "No space left on device" terwijl df -h dat tegenspreekt, controleer dan eerst de inodes en zoek daarna de map die het veroorzaakt:

$ df -i /var
$ du --inodes -d 1 /var | sort -rn | head    # count files, not bytes

XFS ontloopt dit hele probleem door inodes te maken terwijl het bezig is, en dat is een echte reden om het te kiezen voor een bestandssysteem dat een onvoorspelbaar aantal kleine bestanden gaat bevatten.

5.6 Koppelopties die ertoe doen

Opties staan in /etc/fstab en worden getoond door findmnt. Een handvol verandert het gedrag genoeg om ze bij naam te kennen:

OptieEffect
relatimeDe standaard. Werkt de toegangstijd alleen bij als die ouder is dan de wijzigingstijd of ouder dan een dag, dus een bestand lezen schrijft meestal niets.
noatimeWerk toegangstijden helemaal nooit bij. Een kleine, veilige winst op drukke servers, zolang niets van atime afhangt.
roAlleen-lezen. De kernel zet een bestandssysteem hier ook zelf op zodra hij corruptie ziet.
nosuid, nodev, noexecWeiger setuid-bits, apparaatnodes en het uitvoeren van programma's. Standaardverharding voor /tmp en verwisselbare media.
errors=remount-roWat te doen bij een fout in het bestandssysteem. De ext4-standaard, en de reden dat een falende schijf vaak opduikt als "read-only file system".
discardVertel de SSD meteen over vrijgekomen blokken. De meeste systemen gebruiken liever een wekelijkse fstrim.timer.

Je kunt relatime lui zien zijn. Lees een net geschreven bestand en de toegangstijd beweegt niet, omdat die nog niet ouder is dan de wijzigingstijd:

$ findmnt -n -o OPTIONS /
rw,relatime

$ stat -c '%x' tiny.txt
2026-09-06 22:16:06.891262059 +0200
$ cat tiny.txt > /dev/null       # read it
$ stat -c '%x' tiny.txt
2026-09-06 22:16:06.891262059 +0200      # unchanged: no write happened

Dat is een bewuste tussenweg: relatime kwam in 2007 en werd twee jaar later de standaard in de kernel. Strikte atime maakte van elke leesactie een schrijfactie, wat duur en voor bijna iedereen zinloos was, terwijl relatime de toegangstijd bruikbaar genoeg houdt voor de paar gereedschappen die hem lezen.

5.7 fstab, UUID's en waarom apparaatnamen niet te vertrouwen zijn

Apparaatnamen als /dev/sda2 worden toegekend in de volgorde waarin de kernel hardware vindt. Voeg een schijf toe, verplaats een kabel, of start een virtuele machine met een andere controller, en de /dev/sdb van gisteren is de /dev/sdc van vandaag. Daarom worden bestandssystemen aangeduid met een UUID die in hun eigen superblok staat:

$ grep -v '^#' /etc/fstab
/dev/disk/by-uuid/e6894fe9-f40d-42ef-8183-6a31e0996127 /boot     ext4 defaults 0 1
/dev/disk/by-uuid/DAB0-BDC6                            /boot/efi vfat defaults 0 1

$ lsblk -f          # the same UUIDs, read from the filesystems themselves

De laatste twee cijfers op elke regel verdienen een zin. Het eerste is voor het oeroude backupgereedschap dump en is altijd 0. Het tweede is de fsck-volgorde bij het opstarten: 1 voor het rootbestandssysteem, 2 voor de rest, 0 voor "nooit controleren". Zet het op 0 voor een bestandssysteem dat er bij het opstarten misschien niet is, of voor een bestandssysteem, zoals een netwerkkoppeling, waar een controle bij het opstarten geen zin heeft.

Eén waarschuwing die mensen veel tijd kost: een kapotte regel in fstab laat het opstarten vastlopen. Test een nieuwe regel met mount -a terwijl het systeem draait, en start pas daarna opnieuw op.

Naar boven

6. Gevorderde toepassingen: hoe een bestandssysteem echt werkt

Alles tot nu toe was van buitenaf te zien. Dit hoofdstuk maakt het bestandssysteem open: waar de blokken van een bestand worden vastgelegd, wanneer ze worden gekozen, wat het journal beschermt, en wat copy-on-write aan dat alles verandert.

6.1 Een bestandssysteem in een bestand

Je hebt geen reserveschijf nodig om te experimenteren, en je hebt geen root nodig. mkfs formatteert met alle plezier een gewoon bestand, en het ext-gereedschap bekijkt het met alle plezier:

$ truncate -s 512M test.img       # a 512 MB file full of nothing
$ mkfs.ext4 -q -F test.img        # -F: yes, I know it is not a block device

$ dumpe2fs -h test.img
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype
                          extent 64bit flex_bg sparse_super large_file huge_file
                          dir_nlink extra_isize metadata_csum
Inode count:              32768
Block count:              131072
Reserved block count:     6553
Free blocks:              124719
Block size:               4096
Inodes per group:         8192
Inode size:               256
Journal inode:            8
Total journal size:       16M

Dat is een compleet, echt ext4-bestandssysteem dat je kunt slopen, volgooien en weggooien. Het koppelen heeft wel root nodig (sudo mount -o loop test.img /mnt), maar de interessante delen niet: dumpe2fs leest het superblok, en debugfs leest en schrijft de structuren rechtstreeks. Elk ext4-voorbeeld hieronder is zo gemaakt.

Lees de functielijst bovenaan als een specificatie. has_journal is hoofdstuk 6.6, extent is hoofdstuk 6.2, metadata_csum betekent dat het bestandssysteem zijn eigen boekhouding van checksums voorziet zodat corruptie wordt opgemerkt in plaats van gevolgd, en resize_inode is wat online groeien later mogelijk maakt.

6.2 Extents: hoe een bestand vastlegt waar het staat

De oude indeling van ext2 en ext3 bewaarde een lijst met bloknummers, één vermelding per blok, met indirecte blokken zodra een bestand groot werd. Een bestand van 1 GB had een kwart miljoen vermeldingen nodig. Extents vervingen dat door reeksen: "1280 blokken, beginnend bij blok 2127". Zet een bestand in de testimage en vraag debugfs wat de inode zegt:

$ debugfs -w -R "write big.bin big.bin" test.img     # 5 MB of random data
Allocated inode: 12

$ debugfs -R "stat big.bin" test.img
Inode: 12   Type: regular    Mode:  0664   Flags: 0x80000
Links: 1   Blockcount: 10240
Size: 5242880
EXTENTS:
(0-1279):2127-3406

Eén regel beschrijft het hele bestand: logische blokken 0 tot 1279 staan in fysieke blokken 2127 tot 3406. Dat is wat Flags: 0x80000 betekent, de extents-vlag, en daarom toont lsattr een e bij elk bestand op een modern ext4-bestandssysteem.

Op een gekoppeld bestandssysteem komt dezelfde informatie uit filefrag, en het is een goede gewoonte om dat te bekijken als een bestand traag aanvoelt:

$ filefrag /usr/bin/bash /var/log/syslog
/usr/bin/bash: 1 extent found
/var/log/syslog: 9 extents found

Een programma dat één keer in één stuk is geïnstalleerd heeft één extent nodig. Een logbestand dat wekenlang met een paar regels tegelijk is gegroeid, terwijl er andere bestanden omheen werden toegewezen, komt op negen uit. Dit is fragmentatie, en op ext4 is die mild genoeg dat het antwoord bijna altijd "laat maar zitten" is. Is een specifiek bestand echt erg gefragmenteerd, dan kan e4defrag het herschrijven, maar een bestandssysteem dat onder ongeveer 80% vol blijft en niet chronisch krap zit, houdt zichzelf netjes.

6.3 Uitgestelde toewijzing: het bestand staat er nog niet

Hier is een uitkomst die op een fout lijkt en het niet is. Schrijf een bestand en vraag meteen daarna waar zijn blokken staan:

$ head -c 5M /dev/urandom > big.bin
$ filefrag -v big.bin
 ext:     logical_offset:   physical_offset: length:   expected: flags:
   0:        0..    1279:         0..      0:      0:            last,unknown_loc,delalloc,eof
big.bin: 1 extent found

Fysieke positie nul, lengte nul, en de vlag delalloc. Het bestand bestaat, de data staat veilig in het geheugen, en het bestandssysteem heeft nog niet besloten waar op schijf het komt. Forceer het wegschrijven en vraag het opnieuw:

$ sync
$ filefrag -v big.bin
 ext:     logical_offset:      physical_offset: length:   expected: flags:
   0:        0..    1023:  434518016..434519039:   1024:
   1:     1024..    1279:  434517760..434518015:    256:  434519040: last,eof

Dit is uitgestelde toewijzing, en het is een van de grootste redenen dat ext4 sneller is dan ext3. Door te wachten tot de data echt wordt weggeschreven, kent het bestandssysteem de uiteindelijke grootte en kan het één goede reeks kiezen in plaats van blok voor blok te gokken terwijl de schrijfacties binnenkomen. Bestanden die snel worden geschreven en weer verwijderd, wat de meeste tijdelijke bestanden zijn, komen misschien nooit op schijf terecht.

De prijs merken mensen na een stroomstoring: een bestand kan bestaan met de juiste grootte en vol nullen staan, omdat de metadata de schijf bereikte en de data niet. Daarom roept een programma dat geen data mag verliezen fsync() aan, en daarom doen databases en pakketbeheerders dat allemaal. De kernel spoelt vuile pagina's sowieso binnen een paar seconden weg, en je kunt de wachtrij bekijken:

$ cat /sys/fs/ext4/dm-1/delayed_allocation_blocks    # blocks waiting for a home
1

6.4 De page cache: je schijf is zelden de schijf

Uitgestelde toewijzing werkt alleen dankzij een groter idee eronder. Bijna niets van wat je leest of schrijft gaat rechtstreeks naar het apparaat. De kernel houdt bestandsinhoud in vrij RAM, de page cache, en bedient daaruit wat hij kan. Lees een groot bestand dat je sinds het opstarten niet hebt aangeraakt, en kijk hoe de cache precies met die grootte groeit:

$ ls -lh /usr/lib/balena-etcher/balena-etcher
-rwxr-xr-x 1 root root 172M Oct 10  2024 ...

$ grep ^Cached: /proc/meminfo
Cached:         10849064 kB

$ time cat /usr/lib/balena-etcher/balena-etcher > /dev/null
real    0m0.138s

$ grep ^Cached: /proc/meminfo
Cached:         11024544 kB          # 175,480 kB more: the whole file

$ time cat /usr/lib/balena-etcher/balena-etcher > /dev/null
real    0m0.020s                     # seven times faster, from memory

Daarom ziet vrij geheugen op een gezonde Linux-server er verontrustend laag uit. De kernel besteedt elke vrije pagina aan gecachte bestandsdata en geeft die terug zodra een programma hem nodig heeft, dus cache is geen geheugen dat in gebruik is, het is geheugen dat ondertussen iets nuttigs doet. Het is ook de reden dat elke tijdmeting van een bewerking op een bestandssysteem de tweede keer te optimistisch is.

Schrijfacties worden op dezelfde manier gecacht, en dat is het deel met gevolgen. Een write() keert terug zodra de data in een vuile pagina staat, niet zodra die op het apparaat staat:

$ grep -E '^(Dirty|Writeback):' /proc/meminfo
Dirty:                64 kB          # written by programs, not yet on disk
Writeback:            32 kB          # on its way to the device right now

De kernel spoelt vuile pagina's binnen een paar seconden weg, en sync duwt de wachtrij er nu uit. Maar een programma dat moet weten dat zijn data veilig is, moet fsync() op het bestand aanroepen, en daarna fsync() op de map als het bestand nieuw is, want de naam is een aparte wijziging. Databases, pakketbeheerders en mailservers doen dit allemaal; dat is het verschil tussen een programma dat een stroomstoring overleeft en een dat zijn laatste minuut werk kwijt is.

De kernel cacht ook de naamgevingslaag. De dentry cache onthoudt het resultaat van het opzoeken van paden, dus een tweede wandeling door /var/www/html/index.php leest niet opnieuw vier mappen van schijf. Die cache maakt een herhaalde du zoveel sneller dan de eerste, en vm.vfs_cache_pressure (standaard 100) is de knop die bepaalt hoe gretig de kernel hem weggooit als het geheugen krap wordt.

6.5 Sparse bestanden en vooraf toewijzen

De grootte van een bestand en wat het kost zijn twee verschillende getallen, en ze kunnen beide kanten op verschillen. Een sparse bestand claimt een grootte die het nooit heeft geschreven:

$ dd if=/dev/zero of=sparse.bin bs=1 count=0 seek=100M   # seek, write nothing

$ ls -lh sparse.bin
-rw-rw-r-- 1 pe7er pe7er 100M ... sparse.bin

$ du -h sparse.bin
0       sparse.bin

$ filefrag sparse.bin
sparse.bin: 0 extents found

Nul extents: het bestand heeft helemaal geen blokken. Het bestandssysteem noteert simpelweg dat het bereik leeg is en geeft nullen terug als je leest. Schijven van virtuele machines, container-images, databasebestanden en vooraf aangemaakte logbestanden zijn routineus sparse, en daarom kan een onvoorzichtige kopie van 100 MB ineens 100 GB worden. Gebruik cp --sparse=always, rsync -S of tar -S om de gaten te behouden.

fallocate doet het omgekeerde. Het reserveert de blokken nu, zonder iets te schrijven, zodat niemand anders de ruimte kan pakken:

$ fallocate -l 50M prealloc.bin
$ du -h prealloc.bin                 50M    # allocated
$ du -h --apparent-size prealloc.bin 50M    # and claimed

Beide getallen komen overeen, en dat is het verschil met het sparse bestand hierboven. Zo garandeert een database ruimte voor zijn volgende segment voordat hij die nodig heeft, en het gaat meteen omdat er geen data wordt geschreven.

6.6 Het journal: wat het beschermt en wat niet

Een bestand schrijven is nooit één handeling. factuur.pdf aanmaken betekent een inode toewijzen, blokken als bezet markeren, de data schrijven en een mapvermelding toevoegen, en een stroomstoring tussen twee van die stappen laat het bestandssysteem inconsistent achter: blokken die als bezet gelden maar bij niets horen, of een mapvermelding die wijst naar een inode die nooit is geschreven.

Het journal lost dit op door de bedoeling op te schrijven voor de daad. Het bestandssysteem schrijft "ik ga zo deze metadatawijzigingen doen" in een gereserveerd gebied, doet ze daarna, en markeert de vermelding als klaar. Na een crash leest het herstel het journal, doet alles wat was vastgelegd opnieuw en gooit alles wat onaf is weg. Het is een klein apart bestand, en je kunt ernaar kijken:

$ dumpe2fs -h test.img | grep -i journal
Journal inode:            8
Total journal size:       16M

$ debugfs -R "stat <8>" test.img | grep Size
Size: 16777216

Inode 8 is op een ext-bestandssysteem altijd het journal, en 16 MB van een bestandssysteem van 512 MB gaat eraan op voordat je iets opslaat. Dat is de afspraak: een paar procent van de schijf in ruil voor nooit meer uren wachten op een volledige fsck na een stroomstoring.

Het journal beschermt de consistentie van het bestandssysteem, niet je data. Na herstel garandeert ext4 dat zijn eigen structuren kloppen. Het garandeert niet dat het laatste wat jouw programma schreef er nog is. Dat doet alleen fsync(), en alleen voor een programma dat het aanroept.

ext4 kent drie journalmodi, in te stellen met de koppeloptie data=:

ModusWat er in het journal komtAfweging
data=orderedAlleen metadata, maar data wordt altijd geschreven voor de metadata die ernaar wijstDe standaard. Veilig en snel genoeg voor bijna iedereen.
data=writebackAlleen metadata, in willekeurige volgordeIets sneller, en een crash kan een bestand achterlaten dat naar oude data van iemand anders wijst.
data=journalMetadata en bestandsinhoud allebeiHet veiligst, en alles wordt twee keer geschreven. Zelden de moeite waard.

XFS en de copy-on-write-bestandssystemen lossen hetzelfde probleem anders op: XFS journalt ook metadata, terwijl Btrfs en ZFS de vraag vermijden door levende data nooit te overschrijven.

ext4 en XFS overschrijven ter plekke: verander een byte midden in een bestand en hetzelfde blok wordt herschreven. Btrfs en ZFS doen dat nooit. Zij schrijven de wijziging naar een vrij blok en werken daarna de verwijzingen bij, en dat is copy-on-write. Daar rollen bijna gratis drie nuttige functies uit.

Reflinks zijn kopieën die blokken delen tot een van beide verandert. De kopie gaat meteen en kost geen ruimte:

$ cp --reflink=always big.bin clone.bin        # on Btrfs, XFS or ZFS

$ cp --reflink=always big.bin clone.bin        # on this machine, ext4
cp: failed to clone 'clone.bin' from 'big.bin': Operation not supported

Snapshots zijn hetzelfde idee toegepast op een heel subvolume of dataset. Een snapshot maken gaat meteen, en hij kost niets tot de oorspronkelijke data begint te veranderen. Compressie is transparant, dus een bestand kan minder blokken innemen dan het bevat.

Alle drie breken ze een aanname die elk gereedschap dat paden afloopt maakt: dat een blok bij precies één bestand hoort. Een snapshot bevat blokken waar geen pad naartoe leidt. Een reflink-paar meldt twee keer de volle grootte terwijl het de ruimte één keer inneemt. Op deze bestandssystemen komt het eerlijke antwoord op "waar is mijn ruimte gebleven" dus helemaal niet van du, en het artikel over du houdt precies bij deze grens op. Gebruik de eigen boekhouding van het bestandssysteem:

$ btrfs filesystem usage /mnt      # real free space, including metadata and RAID profile
$ btrfs subvolume list /mnt        # what snapshots exist
$ btrfs qgroup show -p /mnt        # per-subvolume usage, once quotas are enabled

$ zfs list -o space                # USED split into snapshots, children and data
$ zfs get compressratio tank       # what compression actually saved

Bij copy-on-write horen twee praktische waarschuwingen. Laat zo'n bestandssysteem niet te vol worden: ze hebben vrije blokken nodig om überhaupt iets te schrijven, ook om te verwijderen, en boven ruwweg 80 tot 90 procent gedragen ze zich slecht. En een snapshot is geen backup. Hij staat op dezelfde apparaten als het origineel, dus hij beschermt je tegen een vergissing, niet tegen een kapotte schijf of een brand.

6.8 Checksums en stille corruptie

Kijk nog eens naar de functielijst in hoofdstuk 6.1 en lees metadata_csum nauwkeurig, want het woord dat het werk doet is metadata. ext4 voorziet zijn superblok, groepsbeschrijvingen, inodes, extent-bomen en mapblokken van checksums, zodat een beschadigde structuur wordt opgemerkt in plaats van gevolgd tot een crash. Het zet geen checksum op de inhoud van je bestanden.

De praktische betekenis is ongemakkelijk. Klapt er een bit om in een datablok, door een falende schijf, een slechte kabel, een firmwarefout of gewoon pech, dan geven ext4 en XFS de beschadigde bytes zonder een woord aan je programma. Niets meldt een fout, want wat het bestandssysteem betreft is er niets misgegaan. Dit is wat mensen stille corruptie of bitrot noemen, en het is stil in de precieze zin dat geen enkele laag kijkt.

Btrfs en ZFS kiezen het andere standpunt: elk datablok krijgt een checksum, en die wordt bij elke leesactie gecontroleerd. Een verschil is een fout, geen verrassing, en heeft het bestandssysteem een tweede kopie (RAID1, een mirror of ZFS copies=2), dan geeft het de goede terug en repareert het de slechte. Allebei kunnen ze ook het hele bestandssysteem op de achtergrond controleren terwijl het gekoppeld blijft:

$ sudo btrfs scrub start -B /mnt      # read and verify everything, and report
$ sudo btrfs device stats /mnt        # per-device error counters that persist

$ sudo zpool scrub tank
$ zpool status tank                   # CKSUM column: checksum errors found

Let op wat dit zonder redundantie niet doet. Op een enkele schijf maken checksums van stille corruptie een luide, specifieke fout die het beschadigde bestand noemt, en dat is een echte verbetering, maar nog steeds geen reparatie. Opmerken vraagt checksums; herstellen vraagt een tweede kopie. En klassieke RAID geeft je de tweede kopie zonder de checksums, dus die kan zien dat twee mirrors het oneens zijn maar niet welke gelijk heeft. Integriteit en redundantie zijn twee verschillende dingen, en je wil ze allebei.

6.9 Groeien, krimpen en controleren

Een bestandssysteem ligt niet vast op de grootte waarmee het is gemaakt, maar de regels verschillen per type en die verschillen tellen als je een volume plant:

Handelingext4XFS
Groeienresize2fs, terwijl het gekoppeld isxfs_growfs, terwijl het gekoppeld is
Krimpenresize2fs, alleen ontkoppeld, na e2fsck -fOnmogelijk. Backup maken, opnieuw aanmaken, terugzetten.
Controlerene2fsck -f, alleen ontkoppeldxfs_repair, alleen ontkoppeld

Groeien gaat in twee stappen, en de eerste vergeten is de gebruikelijke fout: vergroot eerst de laag eronder (de partitie of het logische volume) en zeg pas daarna tegen het bestandssysteem dat het de nieuwe ruimte mag gebruiken.

De regel over controleren kent geen uitzonderingen die het proberen waard zijn. Draai nooit een herstelgereedschap op een gekoppeld bestandssysteem. De kernel houdt structuren in het geheugen die het herstelgereedschap niet ziet, en als beide tegelijk schrijven wordt een klein probleem een verloren bestandssysteem. Start op vanaf herstelmedia, of ontkoppel eerst.

Naar boven

7. Iets wat de meeste gebruikers niet weten

7.1 Bestandsnamen zijn bytes, geen tekst

Linux bewaart bestandsnamen niet als tekst. Het bewaart een reeks bytes, en het handhaaft precies twee regels: de reeks mag geen schuine streep bevatten, want die scheidt padonderdelen, en geen nulbyte, want die beëindigt de tekst. Al het andere mag:

$ touch $'bad\xffname'          # 0xff is not valid UTF-8 at all
$ touch $'two\nlines'           # a newline inside the name

$ ls -b
bad\377name
two\nlines

Beide bestanden bestaan en gedragen zich normaal. De kernel heeft nooit gecontroleerd of die bytes iets spellen; dat is het werk van je terminal, en met ls -b (kort voor "escape") zie je wat er echt staat.

Dit is geen curiositeit maar de reden achter een hele familie scriptgewoontes. Een bestandsnaam kan een regeleinde bevatten, dus for f in $(ls) is per constructie kapot; een bestandsnaam kan met een streepje beginnen, dus rm * kan van een bestand een vlag maken; een bestandsnaam kan ongeldige UTF-8 bevatten, dus een script dat leesbare tekst verwacht klapt erop. Daarom bestaan find -print0 en xargs -0, en daarom zetten zorgvuldige scripts elke variabele tussen aanhalingstekens.

7.2 De grens van 255 is in bytes, dus accenten kosten dubbel

De Namelen: 255 uit hoofdstuk 4.1 telt bytes, geen tekens. In pure ASCII is dat hetzelfde, en passen er precies 255 tekens in:

$ touch $(printf 'a%.0s' {1..255})     # 255 bytes: fine
$ touch $(printf 'a%.0s' {1..256})
touch: cannot touch 'aaaa...aaa': File name too long

Gebruik nu een letter die UTF-8 in twee bytes bewaart, zoals een e met een accent aigu, en de grens komt op de helft:

$ touch $(printf '\xc3\xa9%.0s' {1..127})   # 127 letters, 254 bytes: fine
$ touch $(printf '\xc3\xa9%.0s' {1..128})   # 128 letters, 256 bytes
touch: cannot touch '...': File name too long

Een Nederlandse, Franse of Duitse bestandsnaam kan dus bij 128 tekens worden geweigerd terwijl een Engelse van 255 wordt geaccepteerd, en van een emoji, vier bytes per stuk, passen er 63. Dezelfde rekensom geldt voor de padgrens van 4096 bytes, en daar lopen diep geneste mappen met lange vertaalde namen af en toe tegen een muur aan die op een fout in de toepassing lijkt.

7.3 Een nieuw bestandssysteem mist al ruimte

Formatteer een apparaat en een merkbaar deel is weg voordat je één bestand schrijft. De testimage van 512 MB uit hoofdstuk 6.1 zegt het onomwonden:

Block count:              131072       # 131072 x 4096 = 512 MiB
Free blocks:              124719       # 6353 blocks gone already = 24.8 MiB
Reserved block count:       6553       # another 25.6 MiB you cannot use
Inode count:              32768        # x 256 bytes each = 8 MiB of inode table
Total journal size:         16M        # the journal, before any data

De rekensom klopt bijna precies: 8 MiB inodetabel plus een journal van 16 MiB plus groepsboekhouding is de 24,8 MiB die verdween. Daarbovenop is 5% gereserveerd voor root, dus ongeveer 10% van een vers bestandssysteem is al vergeven.

Schaal dat naar het bestandssysteem van 2 TB uit hoofdstuk 4.2 en de getallen zijn niet schattig meer. De 124.829.696 inodes van 256 bytes per stuk zijn 29,8 GiB inodetabel, geschreven tijdens mkfs en permanent. De rootreserve van 5% is nog eens 95 GiB, en dat is het gat tussen Free en Available in de uitvoer van stat -f. Die reserve is geen verspilling: hij houdt root in staat om in te loggen en syslog in staat om te schrijven als de schijf volloopt, precies wanneer je allebei nodig hebt. Op een puur datavolume, waar niets van die veiligheidsmarge afhangt, kun je het meeste ervan terugnemen:

$ sudo tune2fs -m 1 /dev/sdb1     # reduce the root reserve from 5% to 1%

Doe dat op een dataschijf. Doe het niet op het rootbestandssysteem.

7.4 FAT rondt elke tijdstempel af op twee seconden

De FAT-indeling bewaart een wijzigingstijd in 16 bits, en besteedt daarvan maar vijf aan seconden, dus telt hij in stappen van twee. Je kunt het bewijzen zonder USB-stick, met een image en het pakket mtools, dat FAT leest zonder te koppelen:

$ truncate -s 64M fat.img && mkfs.vfat -F 32 -n TESTFAT fat.img
$ for s in 01 02 03; do
>   touch -d "2026-01-01 10:00:$s" t$s.txt
>   mcopy -m -o -i fat.img t$s.txt ::/      # -m preserves the timestamp
> done

Lees nu het rauwe tijdveld van 16 bits uit elke mapvermelding, op positie 22, en decodeer het:

$ python3 -c "
d = open('fat.img','rb').read()
for n in (b'T01     TXT', b'T02     TXT', b'T03     TXT'):
    i = d.find(n); t = int.from_bytes(d[i+22:i+24], 'little')
    print(n.decode(), '%02d:%02d:%02d' % (t>>11, (t>>5)&63, (t&31)*2))
"
T01     TXT 10:00:00      # 10:00:01 rounded down
T02     TXT 10:00:02
T03     TXT 10:00:02      # 10:00:03 rounded down too

Drie bestanden met een seconde ertussen, twee verschillende tijdstempels. Daarom meldt een backup- of synchronisatieprogramma dat wijzigingstijden vergelijkt op een FAT-stick dat bestanden veranderd zijn terwijl er niets veranderde, en daarom heeft rsync een optie --modify-window=1 die bijna alleen hiervoor bestaat.

Dezelfde mapvermelding toont het andere FAT-compromis. Elke lange naam wordt twee keer bewaard, als klassieke 8.3-naam plus verborgen extra vermeldingen met de echte naam:

$ mdir -i fat.img ::/
MYLONG~1 TXT         6 2026-09-06  22:16  MyLongFileName.txt
tiny     txt         1 2026-09-06  22:16

En omdat FAT namen hoofdletterongevoelig vergelijkt, zijn MyLongFileName.txt en mylongfilename.TXT daar hetzelfde bestand en op ext4 twee verschillende. Kopieer een map van Linux naar een USB-stick en bestanden kunnen elkaar alleen daardoor stilletjes overschrijven.

7.5 Je bestandssysteem telt elke byte die er ooit in geschreven is

ext4 houdt in zijn superblok een teller bij van alles wat er ooit in is geschreven en toont die via /sys, zonder root:

$ cat /sys/fs/ext4/dm-1/lifetime_write_kbytes
19500766413                        # about 18.2 TiB since this disk was formatted

$ cat /sys/fs/ext4/dm-1/session_write_kbytes
6183360                            # about 5.9 GiB since the last mount

Dat eerste getal is echt nuttig op een SSD, waar de levensduur in totaal geschreven bytes wordt opgegeven. Het is ook een stille herinnering aan hoeveel een gewone desktop schrijft: achttien terabyte door een bestandssysteem waarvan de eigenaar nooit bewust meer dan een paar honderd gigabyte heeft weggeschreven.

Dezelfde map bevat de rest van de levende knoppen en tellers van ext4, en /sys/fs/ext4/features/ laat zien wat het ext4-stuurprogramma van jouw kernel kan, en dat is niet hetzelfde als waarmee jouw bestandssysteem is aangemaakt.

7.6 ext4 kan hoofdletterongevoelig zijn, en ext3 raakt in 2038 door de tijd heen

Twee functies die tegenspreken wat de meeste mensen over ext4 aannemen.

De eerste is casefolding. Sinds Linux 5.2 kan ext4 namen per map hoofdletterongevoelig vergelijken, wat vooral bestaat zodat Wine en Android software kunnen draaien die Windows-gedrag verwacht. Het staat uit tenzij het bestandssysteem ermee is aangemaakt:

$ mkfs.ext4 -q -F -O casefold cf.img
$ dumpe2fs -h cf.img | grep features
Filesystem features: has_journal ext_attr resize_inode dir_index filetype extent
                     64bit flex_bg casefold sparse_super large_file huge_file ...

De tweede is het jaar 2038-probleem. Een tijdstempel van 32 bits met teken die seconden vanaf 1970 telt, loopt op 19 januari 2038 over. ext4 ontsnapte eraan met inodes van 256 bytes met extra bits voor de tijd, die de grens naar het jaar 2446 duwen:

$ touch -d '2100-06-01 12:00:00' future.txt
$ stat -c '%y' future.txt
2100-06-01 12:00:00.000000000 +0200        # accepted without complaint

Een oud ext3-bestandssysteem, of een ext4 dat lang geleden met inodes van 128 bytes is aangemaakt, heeft geen ruimte voor die bits en stopt in 2038. De inodegrootte ligt vast bij het aanmaken en kan niet worden veranderd, dus de enige oplossing is het bestandssysteem opnieuw maken. Het is de moeite waard om de regel Inode size in dumpe2fs te bekijken op elke server waarvan de schijven meer dan tien jaar geleden zijn geformatteerd.

7.7 Je SSD hoort misschien nooit wat je hebt verwijderd

Een SSD kan een blok niet overschrijven; hij kan alleen een groot gebied wissen en opnieuw schrijven. De controller moet dus weten welke blokken niets nuttigs meer bevatten, anders besteedt hij zijn leven aan het zorgvuldig bewaren van data die maanden geleden is verwijderd. TRIM, ook discard genoemd, is het bestandssysteem dat tegen het apparaat zegt: "deze blokken zijn nu vrij".

Of dat bericht aankomt is een andere vraag, en het antwoord verschilt per laag. lsblk -D toont de discard-grenzen van elke laag in de stapel, en op deze machine breekt de ketting:

$ lsblk -D -o NAME,DISC-GRAN,DISC-MAX,MOUNTPOINTS -e7
NAME                        DISC-GRAN DISC-MAX MOUNTPOINTS
nvme0n1                          512B       2T
├─nvme0n1p1                      512B       2T /boot/efi
├─nvme0n1p2                      512B       2T /boot
└─nvme0n1p3                      512B       2T
  └─dm_crypt-0                   512B       0B
    └─ubuntu--vg-ubuntu--lv      512B       0B /

De schijf en zijn partities accepteren discards tot 2 TB. De LUKS-laag erboven meldt 0B, en het logische volume daarboven ook. Het rootbestandssysteem op deze laptop heeft nog nooit iets getrimd, en zal dat ook niet doen zolang de stapel er zo uitziet, want LUKS gooit discard-verzoeken weg tenzij het wordt geopend met --allow-discards.

Die standaard is bewust gekozen en geen vergissing. Discards door versleuteling heen laten vertelt iedereen die het rauwe apparaat kan lezen welke blokken ongebruikt zijn, en dat verraadt de vorm van het bestandssysteem en hoe vol het zit, precies wat een versleuteld volume geheim hoort te houden. De afweging is echt: laat discards door en de SSD blijft snel, of houd ze tegen en geef een stukje op van waar versleuteling voor was.

Waar discards wel doorkomen, kies de wekelijkse batch boven de koppeloptie: fstrim loopt de vrije ruimte één keer af, terwijl discard bij elke verwijdering een opdracht stuurt en op sommige hardware kan haperen.

$ systemctl is-enabled fstrim.timer
enabled

$ sudo fstrim -av                 # trim every mounted filesystem that supports it

Nog iets wat dit duidelijk maakt: het bestandssysteem bepaalt niet waar je data fysiek terechtkomt. De vertaallaag van de SSD zelf doet de plaatsing, de slijtageverdeling en het opruimen, en vertelt het bestandssysteem daar niets over. Elk bloknummer uit hoofdstuk 6.2 is een verzinsel dat het apparaat voor jouw gemak in stand houdt.

7.8 Oversteken naar Windows en macOS

De meeste Linux-servers worden beheerd vanaf een Mac of een Windows-laptop, en bestanden gaan de hele dag heen en weer tussen die drie. Je hoeft niet te weten hoe NTFS of APFS vanbinnen werken om dat veilig te doen, maar je moet wel weten waar ze het oneens zijn met Linux, want elk meningsverschil levert een fout op die op iets heel anders lijkt.

Hoofdletters zijn de eerste. Linux vergelijkt bestandsnamen byte voor byte, dus Logo.png en logo.png zijn twee verschillende bestanden. NTFS en APFS zijn standaard hoofdletterongevoelig, dus op een laptop zijn het er één. Een webpagina die om logo.png vraagt terwijl de repository Logo.png bevat, werkt daarom perfect op de machine van de ontwikkelaar en geeft een 404 op de server, en niets op die laptop zal er ooit voor waarschuwen. Hoofdstuk 7.4 liet dezelfde botsing zien op een FAT-stick; dit is de versie die de productieomgeving haalt.

Unicode-normalisatie is de tweede, en die is gemener omdat je hem niet kunt zien. De letter e met een accent aigu kan worden opgeslagen als één teken, of als een gewone e gevolgd door een combinerend accent. macOS normaliseert bestandsnamen zodat beide schrijfwijzen bij hetzelfde bestand uitkomen. Linux bewaart precies de bytes die het kreeg:

$ printf 'nfc' > "$(printf 'caf\xc3\xa9')"     # e-acute as one character
$ printf 'nfd' > "$(printf 'cafe\xcc\x81')"    # plain e plus a combining accent

$ ls -1 | grep caf | while IFS= read -r f; do
>   printf '%s  %s  %s\n' "$f" "$(printf '%s' "$f" | xxd -p)" "$(cat -- "$f")"
> done
café  63616665cc81  nfd
café  636166c3a9    nfc

Twee bestanden, één map, op het scherm identiek, andere bytes. Kopieer een boom van een Mac naar een Linux-server en een bestandsnaam die je op de server intypt kan niet overeenkomen met de naam die is aangekomen, met de melding "no such file" voor een bestand waar je recht naar kijkt. Het is ook de reden dat git status in zo'n boom hetzelfde bestand soms tegelijk als verwijderd en als onbekend meldt.

Toegestane namen zijn de derde. Linux verbiedt precies twee dingen in een bestandsnaam: de schuine streep en de nulbyte. Windows verbiedt een stuk meer, en Linux maakt ze allemaal met alle plezier aan:

$ touch 'CON' 'aux.txt' 'a:b.txt' 'what?.txt' 'trailing.' 'space '
$ ls -b
a:b.txt
aux.txt
CON
space\ 
trailing.
what?.txt

Alle zes zijn hier gewone bestanden en geen van zes kan op Windows bestaan. De tekens < > : " / \ | ? * zijn daar verboden, net als stuurtekens, een punt aan het eind en een spatie aan het eind, en ook de oude apparaatnamen CON, PRN, AUX, NUL, COM1 tot COM9 en LPT1 tot LPT9, met of zonder extensie. Een backup-tar van een Linux-server kan daarom weigeren uit te pakken op een Windows-machine, en het bestand dat het tegenhoudt is meestal een log of een export met een dubbele punt in een tijdstempel.

En metadata overleeft de oversteek meestal helemaal niet. Eigenaar, rechtenbits, symbolische links en de uitgebreide attributen uit hoofdstuk 5.3 hebben op FAT of exFAT geen plek om te wonen, en wat ls -l op zo'n volume toont is verzonnen door de koppelopties. macOS werkt daaromheen door zijn eigen metadata in begeleidende bestanden te schrijven, en daarom staat een USB-stick die bij een Mac is geweest vol met ._bestandsnaam-vermeldingen en .DS_Store-bestanden die voor Linux niets betekenen.

Tot slot: wat Linux echt kan lezen. Drie van de vier vreemde bestandssystemen hebben een stuurprogramma in de hoofdkernel, en het ontbrekende is degene waar mensen het meest naar vragen:

$ for m in ntfs3 exfat hfsplus apfs; do printf '%-8s ' $m; modinfo -F filename $m 2>/dev/null || echo "(no module)"; done
ntfs3    /lib/modules/6.11.0-29-generic/kernel/fs/ntfs3/ntfs3.ko.zst
exfat    /lib/modules/6.11.0-29-generic/kernel/fs/exfat/exfat.ko.zst
hfsplus  /lib/modules/6.11.0-29-generic/kernel/fs/hfsplus/hfsplus.ko.zst
apfs     (no module)
BestandssysteemKomt vanOp Linux
exFATWindows, en elke grote SD-kaartLezen en schrijven, in de kernel. De veiligste keuze voor een stick die overal moet werken.
NTFSWindows-systeemschijvenLezen en schrijven met ntfs3. Windows-ACL's zijn niet te vertalen naar POSIX-rechten, dus de eigenaar komt uit de koppelopties.
HFS+Macs van voor 2017Leesbaar met hfsplus; schrijven wordt geweigerd zolang het journal van het volume actief is.
APFSElke Mac sinds 2017Geen ondersteuning in de hoofdkernel. Externe FUSE-stuurprogramma's zijn alleen-lezen en onofficieel. Kopieer liever over het netwerk.

Dat alles levert één saaie regel op die elk probleem uit dit hoofdstuk in één keer wegneemt: gebruik voor alles wat tussen de drie heen en weer gaat namen in ASCII-kleine letters, zonder spaties, zonder dubbele punten en zonder punt aan het eind. Het lijkt bijgeloof tot de dag dat een uitrol struikelt over een hoofdletter.

7.9 Het bestandssysteem in je broekzak

Dit is het feit dat het hele onderwerp opnieuw ordent: Android is Linux. Niet Linux-achtig, niet op Linux geïnspireerd. Een telefoon draait dezelfde kernel, dezelfde VFS, dezelfde inodes, dezelfde extents en dezelfde page cache als in elk hoofdstuk hierboven, meestal op ext4 of F2FS. De userspace erboven is die van Android in plaats van GNU, maar de bestandssysteemlaag is degene die dit artikel uit elkaar haalt. Begrijp je de opslag van een Linux-server, dan begrijp je al ruwweg negentig procent van die van een telefoon.

De verschillen zijn die andere tien procent waard, en ze beginnen bij de flashchip. Een telefoon bewaart data in eMMC- of UFS-geheugen, dat een blok niet kan overschrijven en in grote gebieden wist, precies zoals de SSD uit hoofdstuk 7.7, en dat verstopt achter een vertaallaag op dezelfde manier. F2FS is het bestandssysteem dat daarvoor is geschreven: het is log-gestructureerd, wat betekent dat het wijzigingen niet over het apparaat verspreidt maar achter elkaar toevoegt in grote segmenten en oude segmenten op de achtergrond opruimt. Dat patroon is waar flashhardware het snelst in is, en daarom is een bestandssysteem dat Samsung in 2012 voor telefoons ontwierp nu gebruikelijk op de datapartitie van Android-toestellen, terwijl ext4 elders op diezelfde telefoon wijdverbreid blijft.

Je hebt geen telefoon nodig om het tegen te komen. Het stuurprogramma staat op je laptop:

$ modinfo -F filename f2fs
/lib/modules/6.11.0-29-generic/kernel/fs/f2fs/f2fs.ko.zst

En het verkeer gaat ook de andere kant op. Hoofdstuk 7.5 wees naar /sys/fs/ext4/features/ zonder te zeggen wat daarin staat. Kijk naar drie van de vermeldingen:

$ ls /sys/fs/ext4/features/ | grep -E 'casefold|encryption|verity'
casefold
encrypted_casefold
encryption
test_dummy_encryption_v2
verity

Alle drie bestaan grotendeels dankzij Android, en ze zitten in het ext4-stuurprogramma van een doodgewone Ubuntu-laptop:

ext4-functieWat Android ermee doet
encryptionfscrypt, versleuteling per map in het bestandssysteem zelf in plaats van over het hele apparaat. Het is wat een telefoon in staat stelt de bestanden van elke gebruiker met een andere sleutel te versleutelen, en de reden dat wekkers en inkomende oproepen werken na een herstart maar vóór de eerste ontgrendeling.
verityfs-verity, transparante integriteitscontrole tijdens het lezen tegen een hashboom. Android gebruikt het zodat een systeembestand of een geïnstalleerde app niet op schijf kan worden gewijzigd zonder dat dat bij de volgende leesactie opvalt.
casefoldHoofdletterongevoelig zoeken per map, uit hoofdstuk 7.6. De geëmuleerde opslag van Android gedraagt zich hoofdletterongevoelig sinds die een SD-kaart met FAT nabootste, en zo levert de kernel dat zelf.

Drie andere verschillen bepalen hoe een telefoon aanvoelt vergeleken met een server, en elk ervan is een laag boven op de bestandssystemen uit dit artikel in plaats van een nieuw soort bestandssysteem:

  • /sdcard is geen SD-kaart. Op een moderne telefoon is het geëmuleerde opslag die in de datapartitie leeft en via FUSE aan apps wordt gepresenteerd, het mechanisme uit hoofdstuk 4.4. In die omweg zit het filteren van rechten per app, en daardoor is bestandstoegang op Android trager dan het onderliggende bestandssysteem zelf.
  • De systeempartitie is alleen-lezen en geverifieerd. Android koppelt die via dm-verity, een device-mapper-doel dat elk blok tijdens het lezen controleert tegen een ondertekende hashboom. Het is dezelfde device-mapper-laag die LUKS en LVM draagt in de stapel uit hoofdstuk 1.4, hier gebruikt voor integriteit in plaats van versleuteling.
  • Elke app is een Unix-gebruiker. Android geeft elke geïnstalleerde toepassing een eigen UID en vertrouwt op gewone POSIX-bestandsrechten om appgegevens privé te houden. De rechtenbits uit hoofdstuk 5.1 doen het werk; de telefoon laat ze je alleen nooit zien.

Apple ging de andere kant op. iPhones en iPads draaien sinds iOS 10.3 in 2017 op APFS, hetzelfde bestandssysteem als de Macs uit hoofdstuk 7.8, met dezelfde copy-on-write-klonen en snapshots uit hoofdstuk 6.7. Het verschil is dat iOS de gebruiker helemaal geen bestandssysteem toont: er is geen pad om in te typen, en de Bestanden-app is een samengestelde weergave in plaats van een venster op een boom.

De eerlijke samenvatting van mobiele opslag is dus dat het verschil veel kleiner is dan de marketing doet vermoeden. Van de twee grote telefoonplatforms draait het ene de bestandssystemen uit dit artikel op hardware die zich gedraagt als een kleine SSD, en heeft het drie functies teruggeduwd in het ext4-stuurprogramma op je bureaublad. Het andere draait het bestandssysteem uit het Mac-hoofdstuk en verstopt het gewoon.

7.10 Weten waar het bestandssysteem ophoudt

Als je nodig hebtGrijp naar
Wat is gekoppeld, en met welke optiesfindmnt, lsblk -f, /proc/mounts
Welke bestanden de ruimte gebruikendu, ncdu, en het artikel over du
Feiten over één bestandstat, filefrag, lsattr
Feiten over het hele bestandssysteemdumpe2fs -h, tune2fs -l, xfs_info, btrfs filesystem usage
Ruimte van verwijderde maar nog open bestandenlsof -nP +L1: geen enkel bestandssysteemgereedschap ziet ze, want ze hebben geen naam
Structuren die geen gekoppeld gereedschap laat ziendebugfs op ext, xfs_db op XFS
Grenzen per gebruiker in plaats van rapportenQuota: quota, repquota, edquota
De lagen onder het bestandssysteemlsblk, cryptsetup status, lvs, smartctl

Die laatste regel slaan mensen over. Meldt een bestandssysteem fouten, dan zit de oorzaak vaak in een laag eronder, en smartctl -a /dev/nvme0n1 beantwoordt een vraag die geen bestandssysteemgereedschap kan beantwoorden.

Naar boven

8. Beste werkwijzen

  • Weet op welk bestandssysteem je zit voordat je iets verandert. Draai eerst df -T. De helft van het advies op internet klopt voor ext4 en niet voor Btrfs, of andersom.
  • Kies ext4 tenzij je de reden kunt noemen om dat niet te doen. Het is het best beproefd, het best begrepen door herstelgereedschap en het minst verrassend. XFS voor zeer grote bestanden en zwaar parallel schrijven, Btrfs of ZFS als je echt snapshots en checksums wil.
  • Koppel op UUID, nooit op /dev/sdX. Apparaatnamen veranderen als de hardware verandert. Een UUID staat in het superblok van het bestandssysteem zelf en reist mee.
  • Test een nieuwe regel in fstab met mount -a voordat je opnieuw opstart. Een typefout daar laat het opstarten vastlopen, en dan heb je herstelmedia nodig.
  • Houd inodes net zo goed in de gaten als blokken. Zet df -i naast df -h in je monitoring. Op ext4 ligt het aantal inodes voor altijd vast op het moment van mkfs.
  • Kies de inodeverhouding bewust als je een bestandssysteem maakt voor veel kleine bestanden. mkfs.ext4 -i 4096 voor mailspools en caches; de standaardverhouding van 16 KB gaat uit van gewoon gemengd gebruik.
  • Laat de rootreserve van 5% op / met rust. Neem hem met tune2fs -m 1 alleen terug op datavolumes. Hij is wat een volle server beheersbaar houdt.
  • Laat een copy-on-write-bestandssysteem niet bijna vollopen. Btrfs en ZFS hebben vrije blokken nodig om iets te schrijven, ook om te verwijderen. Houd ze onder ruwweg 80%.
  • Draai fsck, e2fsck of xfs_repair nooit op een gekoppeld bestandssysteem. Ontkoppel, of start op vanaf herstelmedia. Er is geen variant hiervan die veilig te proberen is.
  • Onthoud dat een snapshot geen backup is. Hij deelt de apparaten met het origineel. Hij beschermt je tegen je eigen vergissing, niet tegen een dode schijf of een brand.
  • Kopieer metadata, niet alleen inhoud. cp -a, rsync -X en tar --xattrs behouden uitgebreide attributen, ACL's, SELinux-labels en bestandscapabilities. Een gewone cp laat ze allemaal stilletjes vallen.
  • Scrub een bestandssysteem met checksums volgens een schema. Btrfs en ZFS vinden stille corruptie alleen als iets de data leest, dus draai maandelijks btrfs scrub of zpool scrub en lees daarna de fouttellers.
  • Controleer of discards het apparaat echt bereiken. lsblk -D toont elke laag. Een 0B in DISC-MAX op een LUKS- of LVM-laag betekent dat de SSD eronder nooit hoort wat je hebt verwijderd.
  • Behoud sparse bestanden bij het kopiëren. cp --sparse=always, rsync -S, tar -S, of kijk hoe een virtuele schijf van 40 GB een volle 40 GB wordt.
  • Laat fstrim.timer het trimmen van SSD's doen. Een wekelijkse batch is op de meeste hardware vriendelijker voor de prestaties dan de koppeloptie discard.
  • Experimenteer op een imagebestand, niet op een server. truncate plus mkfs geeft je een echt bestandssysteem dat je mag slopen, en dumpe2fs en debugfs lezen het zonder root.
  • Lees de documentatie. De handleidingen over bestandssystemen zijn ongewoon goed, en ze beantwoorden versiespecifieke vragen die geen artikel kan beantwoorden.
$ man 5 fstab         # the mount table, field by field
$ man 8 mount         # the options shared by every filesystem
$ man 5 ext4          # every ext4-specific mount option, in one place
$ man 8 mkfs.ext4     # creation options: -b, -i, -N, -T, -O
$ man 8 tune2fs       # what you can still change afterwards
$ man 8 fstrim        # SSD trimming, and why the timer is preferred
$ man 7 xattr         # the four extended-attribute namespaces
$ man 5 acl           # access control lists, and how the mask works
$ man 2 fsync         # the only promise that your data reached the device
Naar boven

9. Veelgemaakte fouten

9.1 Mythe versus werkelijkheid

MytheWerkelijkheid
"Een bestand is één object, en de naam hoort erbij." Een bestand is een inode plus zijn blokken. De naam is een aparte vermelding in een map, en het kunnen er meerdere zijn of even geen.
"rm verwijdert een bestand." Het haalt een naam weg en verlaagt een linkteller. De ruimte komt terug als die teller nul is en geen enkel proces het bestand nog open heeft.
"Verwijderen geeft de ruimte meteen vrij." Niet zolang een proces het bestand open houdt. Dit is de klassieke volle schijf die du niet kan verklaren en lsof -nP +L1 wel.
"Een map bevat zijn bestanden." Een map is een bestand met een tabel van namen en inodenummers. De data zit er niet in, en daarom kopieert een verplaatsing binnen één bestandssysteem niets.
"No space left on device betekent dat de schijf vol is." Het kan net zo goed betekenen dat de inodes op zijn. df -h en df -i beantwoorden twee verschillende vragen.
"Linux-bestandssystemen fragmenteren nooit." Ze fragmenteren wel, ze gaan er alleen goed mee om. filefrag toont de waarheid, en een logbestand met negen extents is normaal en onschadelijk.
"Door het journal kan ik geen data verliezen." Het journal beschermt de structuren van het bestandssysteem. De laatste schrijfactie van je programma wordt alleen beschermd door fsync().
"Bestandsnamen zijn tekst." Het zijn bytes. Alles behalve een schuine streep en een nulbyte mag, inclusief regeleindes en ongeldige UTF-8.
"De bestandsgrootte is hoeveel schijf het gebruikt." Een bestand van één byte gebruikt 4 KB, een sparse bestand van 100 MB gebruikt niets, en een gecomprimeerd of via reflink gedeeld bestand gebruikt minder dan het claimt.
"Je kunt elk bestandssysteem krimpen." ext4 kan krimpen, ontkoppeld. XFS kan helemaal nooit krimpen. Dat is een keuze die je maakt op het moment van mkfs.
"ext4 beschermt mijn bestanden met checksums." Het zet checksums op zijn eigen metadata. Een omgeklapt bit in je data wordt in stilte aan je programma gegeven. Alleen Btrfs en ZFS zetten checksums op de inhoud.
"cp kopieert alles van een bestand." Een gewone cp laat uitgebreide attributen, ACL's en bestandscapabilities vallen. Daarvoor is cp -a nodig, en sparse bestanden vragen --sparse=always.
"Bijna geen vrij geheugen betekent dat de server meer RAM nodig heeft." Het meeste is page cache met bestandsdata, en de kernel geeft die meteen terug als een programma erom vraagt. Lees available, niet free.
"write() is klaar, dus de data staat op schijf." Het staat in een vuile pagina in het geheugen. Alleen fsync() op het bestand, plus fsync() op de map bij een nieuw bestand, belooft meer.
"Mijn SSD wordt getrimd want fstrim.timer staat aan." Alleen als elke laag discards doorgeeft. LUKS gooit ze standaard weg, en lsblk -D laat dat zien als DISC-MAX 0B.
"Een bestandsnaam die op mijn Mac werkt, werkt op de server." Linux is hoofdlettergevoelig en byte-exact waar macOS en Windows dat geen van beide zijn. Dezelfde naam kan één bestand op de laptop zijn en twee, of nul, op de server.
"Telefoons gebruiken een speciaal mobiel bestandssysteem." Android is Linux, op ext4 of F2FS, met dezelfde inodes en rechten. De functies voor versleuteling, integriteit en hoofdletterongevoeligheid zijn ext4-functies die jouw laptop ook heeft.
"Snapshots zijn backups." Ze staan op dezelfde apparaten. Ze overleven jouw vergissingen, niet een hardwarestoring of een brand.
"Als stat -f ext2/ext3 zegt, is de schijf oud." ext2, ext3 en ext4 delen het magische getal 0xEF53 in het superblok. Gebruik df -T voor het echte type.

9.2 Andere valkuilen

  • Bestanden kopiëren naar een koppelpunt dat niet gekoppeld is. Het schrijven lukt, in de onderliggende map, waarna het echte bestandssysteem eroverheen koppelt en de data uit beeld verdwijnt terwijl hij nog steeds ruimte inneemt. Controleer met findmnt voordat je schrijft.
  • Formatteren met de standaard inodeverhouding voor een mail- of cachevolume. De inodes zijn op na een paar miljoen bestanden en kunnen er achteraf niet bij.
  • Een sparse schijfimage kopiëren zonder --sparse=always. De gaten worden echte nullen en een kleine image wordt een image op volle grootte.
  • Aannemen dat een FAT-stick eigenaar en rechten bewaart. Hij bewaart geen van beide. Wat ls -l daar toont komt uit de koppelopties uid, gid, fmask en dmask, niet van de schijf.
  • Het bestandssysteem vergroten zonder het volume eronder te vergroten. De volgorde is altijd: eerst de partitie of het logische volume uitbreiden, dan pas resize2fs of xfs_growfs.
  • e2fsck "even ter controle" draaien op een gekoppeld rootbestandssysteem. Het kan een bestandssysteem vernietigen waar niets mis mee was.
  • du vertrouwen op een volume met snapshots of deduplicatie. Het loopt namen af, en snapshots hebben er geen. Gebruik btrfs filesystem usage of zfs list -o space.
  • Een database of VM-image zonder nadenken op een copy-on-write-bestandssysteem zetten. Willekeurige herschrijvingen in een CoW-bestand fragmenteren zwaar; gebruik chattr +C op de map, of een subvolume met CoW uit.
  • Een repository uitrollen die op een hoofdletterongevoelige laptop is gebouwd. Logo.png en logo.png zijn één bestand op macOS en Windows en twee op Linux, dus de 404 verschijnt pas in productie. Herstel de hoofdletters in de repository, niet op de server.
  • Aannemen dat je een Mac-schijf even kunt aansluiten en uitlezen. APFS heeft geen stuurprogramma in de Linux-kernel. Plan een kopie over het netwerk in plaats van daar met de schijf in je hand achter te komen.
  • De + in ls -l missen. Die betekent dat een ACL toegang geeft die de rechtenbits niet tonen. Draai getfacl voordat je concludeert dat de rechten kapot zijn.
  • Een bestandssysteem twee keer benchmarken en het tweede getal geloven. De page cache en de dentry cache maken elke herhaling sneller. Vergelijk koud met koud, of vergelijk niet.
  • Besluiten dat een bestand weg is omdat ls niets toont. Een streepje vooraan, een spatie achteraan of een regeleinde in de naam komt vaak genoeg voor om eerst ls -b te draaien voordat je in paniek raakt.
Naar boven

10. Samenvatting

Een bestandssysteem is de boekhouding die een genummerde rij blokken verandert in bestanden met namen, eigenaren, datums en rechten. Zodra je de drie delen ziet die het bijhoudt, de naam in een map, de inode met de feiten en de blokken met de bytes, houdt de rest van Linux-opslag op mysterieus te zijn: harde links, sparse bestanden, volle schijven die niet leeglopen, en het gat tussen twee commando's die allebei beweren grootte te meten.

  • Een mapvermelding is een naam en een inodenummer, een inode bevat elk feit over een bestand behalve de naam, en de datablokken bevatten de inhoud.
  • Linux koppelt elk bestandssysteem in één boom, en de VFS-laag van de kernel laat ze allemaal dezelfde aanroepen beantwoorden, of ze nu op schijf staan, in RAM, of ter plekke worden gemaakt zoals /proc.
  • Het bestandssysteem is één laag in een stapel die ook partities, LUKS-versleuteling en LVM kan bevatten. lsblk toont de hele stapel, en weten welke laag stukging is de helft van elke reparatie.
  • Een bestandssysteem kan door zijn blokken of door zijn inodes heen raken, en op ext4 ligt het aantal inodes vast bij het aanmaken. Bewaak df -h en df -i samen.
  • Ruimte wordt uitgedeeld in blokken van 4 KB, dus een bestand van één byte kost 4 KB en een map vol piepkleine bestanden kost een veelvoud van wat hij bevat.
  • ext4 legt de plaats van bestanden vast als extents, beslist zo laat mogelijk waar ze komen (uitgestelde toewijzing), en beschermt met een journal zijn eigen structuren, niet jouw laatste schrijfactie.
  • Sparse bestanden claimen ruimte die ze nooit gebruikten, fallocate reserveert ruimte die het nooit schreef, en copy-on-write-bestandssystemen laten twee bestanden dezelfde blokken delen. Alle drie breken ze het idee dat grootte gelijk is aan kosten.
  • Op Btrfs en ZFS bevatten snapshots en reflinks blokken waar geen pad naartoe leidt, dus alleen het eigen gereedschap van het bestandssysteem kan vrije ruimte verantwoorden.
  • Naast de vaste velden van de inode dragen bestanden uitgebreide attributen, en POSIX-ACL's zitten in een van die attributen. Geen van beide overleeft een gewone cp.
  • De page cache zit tussen jou en het apparaat, dus leesacties komen uit RAM en een write() die klaar is staat nog niet op schijf.
  • ext4 en XFS zetten checksums op alleen hun metadata; Btrfs en ZFS ook op data, en dat is het verschil tussen stille corruptie opmerken en er nooit van horen.
  • Bestandsnamen zijn bytes: alles behalve een schuine streep en een nulbyte, met een grens van 255 bytes die letters met accenten twee keer zo snel bereiken.
  • Linux is hoofdlettergevoelig en byte-exact waar Windows en macOS dat geen van beide zijn, en daarom kan een bestandsnaam die op een laptop werkt op de server falen, en zijn APFS-schijven op Linux helemaal niet te lezen.
  • Een telefoon is geen andere wereld: Android is Linux op ext4 of F2FS, en fscrypt, fs-verity en casefolding zijn ext4-functies die je eigen kernel ook draagt.
  • ext4 groeit online en krimpt ontkoppeld; XFS krimpt nooit; en geen enkel bestandssysteem hoort te worden gecontroleerd of gerepareerd terwijl het gekoppeld is.
df -hT                       # what is mounted, how full, and which type
df -i                        # the other way a filesystem fills up
findmnt /path                # the mount, its source and its options
lsblk -f                     # the whole stack: disk, crypt, LVM, filesystem
stat file                    # inode number, links, blocks, four timestamps
stat -f /path                # block size, name length, free vs available
ls -b                        # filenames as they really are
filefrag -v file             # the extents a file is stored in
du -h --apparent-size file   # content size, next to plain du for real cost
dumpe2fs -h /dev/sdX1        # superblock: features, inodes, journal, reserve
tune2fs -m 1 /dev/sdX1       # reclaim the root reserve on a data volume
resize2fs /dev/sdX1          # grow ext4 online, after growing the volume
getfacl file                 # the ACL behind the + in ls -l
lsblk -D                     # does discard reach the device, layer by layer
lsof -nP +L1                 # deleted files still holding space open
btrfs filesystem usage /mnt  # free space on a copy-on-write filesystem
zfs list -o space            # the same question, ZFS edition

En als een server "No space left on device" meldt terwijl df -h een halfvolle schijf laat zien, is het bestandssysteem niet in de war en jij ook niet: de inodes zijn op, of er wordt nog een verwijderd logbestand vastgehouden door een proces dat het nooit heeft gesloten.

Naar boven
Linux concept: filesystems
Peter Martin
Peter Martin
Joomla Specialist

Peter is Joomla specialist en Linux admin voor snelle, veilige en schaalbare websites.

Gerelateerde artikelen