Diskussions- und Newsboard der Linux Interessen Gruppe Suletuxe
allgemeine Kategorie => Tutorials => Thema von: Sebastian am 29. September 2025, 17:20:20

Titel: Dateisystem: Speicherplatz für eine bekannte Dateigröße vorreservieren
Beitrag von: Sebastian am 29. September 2025, 17:20:20

Einleitung:
Ich beschäftige mich schon länger mit Dateisystemen, insbesondere mit Btrfs. Je tiefer ich in die Interna eintauche, desto mehr Gedanken mache ich mir, wie ich das Dateisystem optimal nutzen kann. Vor kurzem hatte ich ein praktisches Beispiel: ich habe mir ein stark komprimiertes DietPi-Image (von dietpi.com (https://dietpi.com)) für meinen Raspberry Pi heruntergeladen.

Das Herunterladen habe ich mit aria2 und der Option --file-allocation=falloc erledigt, um die Fragmentierung bereits beim Download zu minimieren. Die eigentliche Herausforderung kam danach: wie dekomprimiere ich das rund 1 GiB große Image möglichst fragmentationsfrei aus dem .xz-Archiv? Der Standardweg über xz -kd image.xz erzeugt viele Dateifragmente, was für Btrfs suboptimal ist.

TLDR;
Mit einer Kombination aus xz, fallocate und dd lässt sich die Datei vorallozieren und fragmentationsfrei dekomprimieren.

Hauptteil:
Der Schlüssel liegt darin, das entpackte Image zuerst als zusammenhängenden Block im Dateisystem vorzubelegen, bevor die eigentlichen Daten hineingeschrieben werden. Mit dem normalen xz -kd passiert das nicht – xz schreibt die Daten in kleinen Blöcken und das Dateisystem verteilt diese Blöcke dann oft über verschiedene Stellen, was zu Fragmentierung führt.

Mein Ansatz bestand aus drei Schritten:

1. Mit xz -vl image.xz habe ich die unkomprimierte Größe ermittelt.

  • -v zeigt zusätzliche Infos (verbose).
  • -l listet Metadaten des Archivs, u.a. die Größe des dekomprimierten Inhalts.

  • 2. Mit der ermittelten Größe konnte ich die Zieldatei vorbereiten: fallocate -l <Größe in Bytes> image.img

    • -l legt die gewünschte Länge fest.
    • Der Befehl reserviert einen zusammenhängenden Speicherbereich auf der Platte, ohne Daten hineinzuschreiben.

    • 3. Anschließend habe ich die eigentlichen Daten hineingeschrieben: xz -cd image.xz | dd of=image.img conv=notrunc

      • -c schreibt die Ausgabe nach stdout (anstatt Datei).
      • -d dekomprimiert.
      • dd schreibt den Stream in die vorbereitete Datei.
      • conv=notrunc sorgt dafür, dass die Datei nicht erneut abgeschnitten wird, sondern genau den vorreservierten Bereich befüllt.

      • Warum nicht einfach mit > image.img umleiten?

        • Eine Shell-Umleitung mit > öffnet die Zieldatei neu und kürzt sie automatisch auf Länge null.
        • Damit geht die zuvor mit fallocate reservierte, zusammenhängende Blockstruktur verloren.
        • Genau deshalb muss dd of=image.img conv=notrunc verwendet werden: die Datei bleibt in ihrer vordefinierten Länge bestehen und wird blockgenau überschrieben.

        • Ohne diesen Zwischenschritt mit fallocate würde Btrfs beim fortlaufenden Schreiben des Streams immer wieder neue Blöcke anfordern und diese verteilt ablegen. Das führt zu unnötiger Fragmentierung.

          Beispiele:

          Code:

          # 1. Größe des entpackten Images bestimmen
          xz -vl dietpi.img.xz

          # 2. Datei mit exakter Zielgröße vorbelegen
          fallocate -l 1073741824 dietpi.img

          # 3. Daten dekomprimieren und in die vorbelegte Datei schreiben
          xz -cd dietpi.img.xz | dd of=dietpi.img conv=notrunc


          Fazit:
          Mit diesem Vorgehen lässt sich ein komprimiertes Image fragmentationsarm entpacken, was insbesondere auf Btrfs klare Vorteile bringt. Gerade bei häufigen Snapshot-Operationen oder beim späteren Zugriff auf große Image-Dateien lohnt sich die zusätzliche Mühe.

          Quellenangabe:

          • DietPi Projektseite (https://dietpi.com)
          • Btrfs Dokumentation (https://btrfs.readthedocs.io/en/latest/)


          • LG
            Sebastian

Titel: Re:Dateisystem: Speicherplatz für eine bekannte Dateigröße vorreservieren
Beitrag von: Sebastian am 03. Oktober 2025, 08:01:59

Im vorigen Beitrag habe ich beschrieben, wie man durch das Vorab-Allokieren einer Datei mit der späteren Zielgröße Fragmentierung vermeiden kann. Das Dateisystem kann dadurch den Platz besser einplanen.

Nachdem ich dieses Vorgehen beim Entpacken einer DietPi-Image-Datei getestet und mit einem normalen Entpacken verglichen habe, fiel mir Folgendes auf:

Die Datei, bei der ich die Größe vorher allokiert hatte, bestand nur aus vier Extents – also nahezu zusammenhängend ohne Fragmentierung. Eine Prüfung mit compsize zeigte jedoch, dass Btrfs die Datei nicht als gut komprimierbar einstufte. Sie belegte daher die vollen 100 % von rund 1 GiB.

Die normal entpackte Image-Datei hingegen sah anders aus:


Code:

❯ compsize DietPi_RPi234-ARMv8-Trixie.img
Processed 1 file, 4735 regular extents (4735 refs), 0 inline.
Type Perc Disk Usage Uncompressed Referenced
TOTAL 79% 500M 628M 628M
none 100% 441M 441M 441M
zstd 31% 59M 186M 186M


Zwar war diese Datei mit über 4,7k Extents stark fragmentiert, ließ sich aber deutlich besser komprimieren. Der Grund: Btrfs-Kompression arbeitet extentweise. Für jedes einzelne Extent wird entschieden, ob es komprimiert wird oder nicht. Mit tausenden Extents ergeben sich viel mehr Chancen für sinnvolle Kompression als mit nur vier.

Noch wichtiger: Die normal entpackte Datei war eine Sparse-Datei. Sie hatte also Lücken und war nur logisch etwa 1 GiB groß, während tatsächlich weniger Daten vorhanden waren. Diese Lücken konnte Btrfs problemlos „komprimieren“.

Das führte dazu:

  • Logische Größe: ca. 1 GiB
  • Tatsächlicher Speicherplatz ohne Löcher: 628 MiB
  • Mit Btrfs-Kompression reduziert: 500 MiB


  • Beim vorab allokierten Image war das anders. Durch das Anlegen einer 1 GiB großen Datei und späteren befüllen mit dd wurden die freien Bereiche mit Nullen gefüllt. Damit war es kein Sparse-File mehr, sondern verbrauchte tatsächlich 1 GiB Platz. Außerdem nahm ich Btrfs die Möglichkeit, sinnvoll zu komprimieren.

    Auf einem ext4-Dateisystem wäre das kein Problem gewesen, da dort ohnehin keine Kompression existiert. Aber gerade mit Btrfs sollte man die Mechanismen des Dateisystems nicht ausbremsen.

    Für die Zukunft habe ich daher folgende Best Practices für mich abgeleitet:

    • Wenn Btrfs mit Kompression im Einsatz ist: Das Dateisystem entscheiden lassen und nicht künstlich eingreifen.
    • Nach Möglichkeit mit Pipes arbeiten, damit unnötige Daten gar nicht erst ins Dateisystem geschrieben werden. Beispiel: den dekomprimierten Datenstrom direkt nach dd weiterleiten.
    • Alternativ Tools wie caligula (https://archlinux.org/packages/extra/x86_64/caligula/) nutzen, die Images direkt aus dem komprimierten Archiv flashen können.
    • Bei ext4 kann man hingegen unbesorgt vorallokieren, da dort keine Kompression vorhanden ist, die beeinträchtigt würde.


    • Fazit: Bei Btrfs mit Kompression lohnt es sich, das Dateisystem arbeiten zu lassen, anstatt manuell einzugreifen. So erreicht man die beste Platzersparnis – aber leider zu Lasten von Fragmentation. Das spielt bei SSDs jedoch keine Rolle.

      LG
      Sebastian

Titel: Re:Dateisystem: Speicherplatz für eine bekannte Dateigröße vorreservieren
Beitrag von: Andreas am 03. Oktober 2025, 08:49:17

Ja, BTRFS ist schon Klasse. Und es ist jetzt so schnell, dass ich mein Produktivsystem umstellen könnte. Wenn ich die Zeit dafür habe werde ich es machen!

LG und schönen Feiertag
Andreas

Titel: Re:Dateisystem: Speicherplatz für eine bekannte Dateigröße vorreservieren
Beitrag von: Sebastian am 03. Oktober 2025, 10:50:33

Das freut mich zu hören, dass du auf Btrfs umstellen möchtest. Verständlich ist auch, dass man das nicht sofort angeht, da dafür eine gewisse Planung nötig ist, wenn man doppelte Arbeit vermeiden will. Je nach Einsatzzweck kann es erforderlich sein, auch die Partitionstabelle neu zu strukturieren.

Hier ein paar Tipps aus meiner eigenen Konfiguration, vielleicht ist etwas Nützliches für dich dabei. Den Aspekt LUKS im Partitionsschema lasse ich dabei außen vor, da er für dich nicht relevant ist.

Auf meiner Festplatte verwende ich eine GPT mit drei Partitionen:

  • ESP mit 2 GiB Größe (genug Platz für Kernel, initramfs usw., Empfehlung von EndeavourOS)
  • Große Btrfs-Partition (logische Aufteilung von root, home usw. erfolgt über Subvolumes)
  • Swap-Partition mit 10 GiB (damit keine Swapdatei auf Btrfs liegt und Hibernate sowie später ein Btrfs-RAID genutzt werden könnte)


  • Dieses Schema kann man manuell einrichten, es wird aber auch direkt vom EndeavourOS-Installer unterstützt.

    Als Bootloader verwende ich mittlerweile systemd-boot (Teil der systemd-Suite, erfordert UEFI). Er ist schlanker und einfacher zu konfigurieren als GRUB. Da die ESP ohnehin unverschlüsselt bleiben muss, nutze ich den Vorteil, dass systemd-boot in Kombination mit dem initramfs auch LUKS2-Volumes öffnen kann. Damit bin ich nicht länger auf LUKS1 beschränkt und kann moderne Verschlüsselungsverfahren einsetzen, die auch gegen aktuelle GPUs robust sind.

    Besonders schätze ich an systemd-boot, dass die Konfiguration über einfache Textdateien erfolgt und Bootmenü-Einträge direkt angelegt werden können. Im Gegensatz zu GRUB, wo Skripte eine Konfiguration erst generieren müssen. Zudem ist systemd-boot nahtlos in systemd integriert, und über pacman gibt es bereits fertige Hooks, die den Bootloader bei systemd-Updates automatisch mitaktualisieren.

    Vorteil von Grub wäre – Das es Projekte gibt einen Grub Menü Eintrag automatisch zu erzeugen, der einen ermöglicht in ein älteren Readonly Snapshot zu booten um Repartur maßnahmen oder ein Rollback zu ermöglichen. Dies mache ich aber lieber klassisch per Live-System.

    Mein Btrfs habe ich so erstellt:


    Code:

    mkfs.btrfs -L "Mein Neues Btrfs" /dev/sda2


    Damit wird ein Btrfs-Dateisystem mit den Standardparametern angelegt. Für Checksummen wird crc32c genutzt, wofür fast alle CPUs spezielle Befehle bieten. Dadurch entsteht praktisch kein Leistungsverlust. Als Datenprofil wird single genutzt, für Metadaten DUP. Das bedeutet: Dateiinhalte werden nur einmal gespeichert, Metadaten dagegen redundant, um im Fehlerfall das Dateisystem reparieren zu können.
    Die offizielle Btrfs-Dokumentation weist darauf hin, dass DUP auf SSDs unter Umständen unnötigen Overhead erzeugt, da viele SSD-Controller intern Redundanzen entfernen. Ich habe es trotzdem beibehalten, um auf Nummer sicher zu gehen.

    Meine Subvolume-Struktur sieht so aus:

    • @ – Root-Subvolume (EndeavourOS ist dort installiert)
    • @home – für /home
    • @cache – für /var/cache (damit der Cache nicht in Snapshots landet)
    • @.snapshots – für /.snapshots, hier erstellt yabsnap automatisch Snapshots von / und /home
    • @vm-images – für /var/lib/libvirt/images, um VM-Images aus Snapshots auszuschließen; zusätzlich habe ich hier mit chattr +C Copy-on-Write deaktiviert, um die Performance zu verbessern
    • ${XDG_CACHE_HOME} – als verschachteltes Subvolume im Home-Verzeichnis, damit Cache-Daten ebenfalls aus den Snapshots ausgeschlossen werden


    • Gemountet habe ich die Subvolumes mit folgenden Optionen:


      Code:

      TARGET SOURCE FSTYPE OPTIONS
      / UUID=07912c18-f0f8-40fb-b4ad-29459cab4292 btrfs subvol=/@,noatime,compress=zstd
      /home UUID=07912c18-f0f8-40fb-b4ad-29459cab4292 btrfs subvol=/@home,noatime,compress=zstd
      /var/cache UUID=07912c18-f0f8-40fb-b4ad-29459cab4292 btrfs subvol=/@cache,noatime,compress=zstd
      /.snapshots UUID=07912c18-f0f8-40fb-b4ad-29459cab4292 btrfs subvol=/@.snapshots,noatime,compress=zstd


      Die Option compress=zstd entspricht Level 3. Sie bietet schnelle, transparente Kompression, die im Alltag kaum Leistung kostet, aber viel Speicherplatz spart.

      Für Backups kannst du – wo sinnvoll – anstelle von rsync auch btrfs send/receive verwenden. Damit lassen sich ganze Subvolumes inklusive Struktur, Dateirechten und Attributen effizient in eine Datei oder über ein Netzwerk übertragen.

      LG und auch ich wünsche dir einen schönen Feiertag
      Sebastian


Diskussions- und Newsboard der Linux Interessen Gruppe Suletuxe | Powered by YaBB SE
© 2001-2003, YaBB SE Dev Team. All Rights Reserved.