Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX ROOTFS 2026

Read-Only Rootfs
Güvenli Gömülü Sistem

güç kesilmelerine dayanıklı, malware'e kapalı, her boot'ta deterministik — squashfs, overlayfs, dm-verity ve A/B partition stratejileri.

00 Read-only rootfs neden

Salt okunur kök dosya sistemi, güç kesilmelerine karşı bozulmayan, malware'in kalıcı olamadığı ve her boot'ta deterministik davranan gömülü sistemlerin temel yapı taşıdır.

Güç kesintisi ve dosya sistemi bozulması

Gömülü sistemler çoğunlukla güvenilmez güç ortamlarında çalışır: araç elektroniği, endüstriyel sensörler, saha ekipmanları. ext4 ile journaling kullanılsa bile, yazma işlemi ortasında yaşanan güç kesintisi journal'ın kendisini bozabilir. Metadata tutarsızlıkları fsck ile çözülmeye çalışılsa da bu boot süresini ciddi ölçüde uzatır ve her zaman başarılı olmaz.

Salt okunur bir root dosya sisteminde yazma işlemi yoktur. Rootfs'e hiçbir şey yazılmadığı için güç kesilmesinden etkilenecek tutarsız durum da oluşmaz. Cihaz her boot'ta temiz bir sayfa ile başlar.

Flash hafıza ömrü

NAND ve eMMC flash, her hücre için sınırlı sayıda program/erase döngüsü destekler (SLC: ~100.000, MLC: ~10.000, TLC: ~3.000). Yazılabilir rootfs'te systemd journal, syslog ve geçici dosyalar sürekli yazma üretir. Read-only rootfs bu problemi ortadan kaldırır; yalnızca /var ve /tmp gibi özel partition'lar yazılabilir kalır.

Güvenlik: kalıcı malware imkânsızlaşır

Yazılabilir bir rootfs'e erişim kazanan saldırgan persistence mekanizmaları kurabilir: /etc/rc.local değiştirme, shared library injection, cron job ekleme. Salt okunur sistemde bu değişiklikler tmpfs katmanına düşer ve bir sonraki reboot'ta silinir. Malware çalışabilir ama yerleşemez.

Sistem bileşenleri

SquashFS (lower)Sıkıştırılmış, salt okunur rootfs. Tüm sistem dosyaları: /bin, /lib, /usr, /etc.
OverlayFS (merge)Lower (ro) + upper (rw) = merged görünüm. Tüm değişiklikler upper'a yazılır; lower dokunulmaz kalır.
tmpfs (upper)RAM'de geçici upper katmanı. Reboot sonrası sıfırlanır. /tmp, /run, /var/run için.
Data partitionReboot'lar arasında kalıcı olması gereken kullanıcı verileri ve config dosyaları. /data.
  ┌─────────────────────────────────────────────┐
  │  Merged (OverlayFS) — uygulamalar burayı görür        │
  │  /bin  /lib  /usr  /etc  /tmp  /var  ...             │
  └──────────────────┬────────────────┬──────────┘
                     │                │
            lower (SquashFS ro)   upper (tmpfs rw)
            /dev/mmcblk0p2        RAM
            Değiştirilemez        Reboot'ta sıfırlanır
SektörGereksinimÇözüm
IoT sensörGüç kesintisi toleransıSquashFS + tmpfs upper
Sanayi HMIDeterministik bootSquashFS + /data persist
Medikal cihazYazılım bütünlüğüSquashFS + dm-verity
Otomotiv ECUOTA + rollbackA/B SquashFS + SWUpdate

Bu bölümde

  • Güç kesintisi: journaling yetersizliği ve fsck gecikmeleri
  • Flash ömrü: TLC/MLC write endurance ve read-only'nin katkısı
  • Güvenlik: tmpfs upper ile malware persistence'ı engelleme
  • Sistem bileşenleri: SquashFS + OverlayFS + tmpfs + data partition

01 SquashFS

SquashFS, gömülü sistemlerde read-only rootfs için standart sıkıştırılmış dosya sistemidir; xz sıkıştırması ile tipik bir rootfs yüzde 40-60 oranında küçülür.

Image oluşturma

bash
# Temel kullanım
mksquashfs rootfs/ rootfs.squashfs

# xz sıkıştırması, 1 MB block boyutu (en iyi oran)
mksquashfs rootfs/ rootfs.squashfs \
    -comp xz \
    -b 1M \
    -Xdict-size 100% \
    -noappend

# zstd sıkıştırması (daha hızlı decompress, modern tercih)
mksquashfs rootfs/ rootfs.squashfs \
    -comp zstd \
    -Xcompression-level 15

# Geçici dosyaları hariç tut
mksquashfs rootfs/ rootfs.squashfs \
    -comp xz \
    -e rootfs/tmp rootfs/var/log rootfs/var/cache

# Image içeriğini mount etmeden listele
unsquashfs -l rootfs.squashfs | head -30

# Superblock bilgisi (boyut, sıkıştırma, block size)
unsquashfs -s rootfs.squashfs

# Loop device ile mount
sudo mount -t squashfs -o loop rootfs.squashfs /mnt

# Block device'ten direkt mount
sudo mount -t squashfs -o ro /dev/mmcblk0p2 /mnt

Sıkıştırma algoritması karşılaştırması

AlgoritmaOranDecomp hızıKullanım
gzipOrtaHızlıGenel amaç, eski sistemler
lzoDüşükÇok hızlıYavaş flash, latency kritik
xzEn iyiYavaşKüçük flash, güçlü CPU
zstdİyiÇok hızlıModern sistemler, önerilen
lz4DüşükEn hızlıRAM kısıtlı, çok düşük latency

Kernel konfigürasyonu

linux/.config
CONFIG_SQUASHFS=y
CONFIG_SQUASHFS_FILE_CACHE=y
CONFIG_SQUASHFS_ZLIB=y
CONFIG_SQUASHFS_LZO=y
CONFIG_SQUASHFS_XZ=y
CONFIG_SQUASHFS_LZ4=y
CONFIG_SQUASHFS_ZSTD=y
CONFIG_OVERLAY_FS=y

Bu bölümde

  • mksquashfs: xz ve zstd sıkıştırma seçenekleri ve parametreleri
  • Sıkıştırma algoritmaları: oran vs decompress hız tradeoff
  • unsquashfs -l/-s ile image inceleme
  • Kernel config: CONFIG_SQUASHFS=y ve sıkıştırma seçenekleri

02 OverlayFS temelleri

OverlayFS, salt okunur alt katmanı yazılabilir üst katmanla birleştirerek unified görünüm sunar; SquashFS'in salt okunur kısıtlamasını aşmanın Linux'ta standart yoludur.

Katman modeli

lowerdirSalt okunur alt katman. SquashFS veya herhangi bir ro filesystem. Birden fazla lower katman: lowerdir=a:b:c.
upperdirYazılabilir üst katman. tmpfs veya ext4 üzerinde olabilir. Tüm değişiklikler buraya yazılır.
workdirOverlayFS iç kullanımı için gerekli. upperdir ile aynı filesystem üzerinde olmalıdır.
mergedUygulamaların gördüğü birleşik görünüm. Lower ve upper'ın union'u.

Mount komutu

bash
# Dizinleri hazırla
mkdir -p /lower /upper/upper /upper/work /merged

# SquashFS'i lower olarak mount et
mount -t squashfs -o loop,ro rootfs.squashfs /lower

# tmpfs'i upper/work için mount et
mount -t tmpfs tmpfs /upper

# OverlayFS mount
mount -t overlay overlay \
    -o lowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work \
    /merged

Copy-on-Write mekanizması

OverlayFS'te bir dosya değiştirildiğinde şu adımlar gerçekleşir: dosya lower katmandan upper katmana kopyalanır (copy-up), ardından değişiklik upper kopyası üzerinde yapılır. Lower katman hiçbir zaman değiştirilmez.

  echo "myhostname" > /merged/etc/hostname

  1. /lower/etc/hostname  ──copy-up──▶  /upper/upper/etc/hostname
  2. /upper/upper/etc/hostname güncellenir
  3. /lower/etc/hostname değişmeden kalır
  4. merged'de /etc/hostname → upper versiyonunu gösterir

Silme: whiteout dosyaları

bash
# Lower'da var olan dosyayı sil
rm /merged/etc/motd

# Upper'da whiteout oluştu (karakter cihazı 0:0)
ls -la /upper/upper/etc/motd
# c--------- 1 root root 0, 0 ... /upper/upper/etc/motd

# /merged/etc/motd artık görünmez
# Reboot → tmpfs temizlendi → /etc/motd yeniden görünür

Bu bölümde

  • OverlayFS katmanları: lowerdir, upperdir, workdir, merged
  • Copy-on-Write: ilk yazma lower → upper kopyalama tetikler
  • Whiteout dosyaları: karakter cihazı 0:0 ile silme simülasyonu
  • tmpfs upper: reboot'ta sıfırlanan geçici overlay

03 Init script ile setup

Gerçek gömülü sistemlerde read-only rootfs ve overlay kurulumu, initramfs içinde pivot_root öncesinde yapılır; bu yaklaşım her dağıtımdan bağımsız çalışır.

initramfs init script

/init (initramfs)
#!/bin/sh
# initramfs /init — read-only rootfs + overlayfs kurulumu

mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev

ROOTDEV=/dev/mmcblk0p2
DATADEV=/dev/mmcblk0p3

# SquashFS'i /mnt/lower'a mount et
mkdir -p /mnt/lower /mnt/overlay
mount -t squashfs -o ro "$ROOTDEV" /mnt/lower

# Data partition'ı mount et (overlay upper için)
mount -t ext4 -o rw,noatime "$DATADEV" /mnt/data 2>/dev/null

# upper ve work dizinlerini hazırla
if mountpoint -q /mnt/data; then
    # Persistent upper (data partition)
    mkdir -p /mnt/data/overlay/upper /mnt/data/overlay/work
    UPPER=/mnt/data/overlay/upper
    WORK=/mnt/data/overlay/work
else
    # Fallback: tmpfs upper (reboot'ta kaybolur)
    mount -t tmpfs tmpfs /mnt/overlay
    mkdir /mnt/overlay/upper /mnt/overlay/work
    UPPER=/mnt/overlay/upper
    WORK=/mnt/overlay/work
fi

# OverlayFS mount
mount -t overlay overlay \
    -o lowerdir=/mnt/lower,upperdir="$UPPER",workdir="$WORK" \
    /mnt/newroot

# pivot_root: yeni rootfs'e geç
mkdir -p /mnt/newroot/oldroot
pivot_root /mnt/newroot /mnt/newroot/oldroot

# Eski proc/sys/dev'i temizle
mount --move /oldroot/proc /proc
mount --move /oldroot/sys /sys
mount --move /oldroot/dev /dev

umount -l /oldroot

exec /sbin/init

/etc için ayrı overlay

bash — /etc overlay
# /etc'yi ayrı overlay ile yönet
# lower: squashfs içindeki /etc
# upper: /data/etc-upper
mkdir -p /data/etc-upper /data/etc-work
mount -t overlay overlay \
    -o lowerdir=/etc,upperdir=/data/etc-upper,workdir=/data/etc-work \
    /etc

# /etc/network/interfaces gibi dosyalar artık /data/etc-upper'da
# Squashfs içindeki orijinal dosyalar dokunulmaz kalır

Buildroot post-build script örneği

board/myboard/post-build.sh
#!/bin/bash
# Buildroot post-build: tmpfs için sembolik linkler hazırla

TARGET_DIR="$1"

# /var/log → tmpfs symlink
rm -rf "$TARGET_DIR/var/log"
ln -sf /run/log "$TARGET_DIR/var/log"

# /var/tmp → tmpfs
rm -rf "$TARGET_DIR/var/tmp"
ln -sf /tmp "$TARGET_DIR/var/tmp"

# /var/run → /run (zaten var ama kontrol edelim)
rm -rf "$TARGET_DIR/var/run"
ln -sf /run "$TARGET_DIR/var/run"

# initramfs /init script'ini kopyala
install -m 755 board/myboard/initramfs-init \
    "$TARGET_DIR/../images/initramfs-init"

Bu bölümde

  • initramfs /init script: SquashFS + OverlayFS + pivot_root zinciri
  • Persistent vs tmpfs upper: data partition varlığına göre fallback
  • /etc için ayrı overlay: config dosyalarını kalıcı yapma
  • Buildroot post-build.sh: /var/log → tmpfs symlink

04 systemd ile read-only rootfs

systemd, read-only rootfs için yerleşik destek sunar; volatile mode, BindPaths ve systemd-tmpfiles ile kalıcı ve geçici dizinler bildirimsel biçimde yönetilir.

systemd.volatile kernel parametresi

U-Boot bootargs
# Tüm /var'ı tmpfs'te tut (volatile mode)
root=/dev/mmcblk0p2 rootfstype=squashfs ro systemd.volatile=yes

# Sadece /var volatile, rootfs'in geri kalanı ro
root=/dev/mmcblk0p2 rootfstype=squashfs ro systemd.volatile=state

/var için ayrı partition

/etc/fstab
# rootfs: read-only squashfs
/dev/mmcblk0p2   /        squashfs  ro,noatime              0  0

# /var: yazılabilir ext4 (power-safe journaling)
/dev/mmcblk0p3   /var     ext4      rw,noatime,data=journal 0  2

# /data: kullanıcı verileri
/dev/mmcblk0p4   /data    ext4      rw,noatime              0  2

# /tmp: RAM
tmpfs            /tmp     tmpfs     rw,nosuid,nodev,size=64m 0 0

systemd-tmpfiles ile dizin oluşturma

/etc/tmpfiles.d/myapp.conf
# d = dizin oluştur (yoksa), Mode UID GID Age
d  /var/log/myapp   0755  root  root  -
d  /var/lib/myapp   0755  myapp myapp -
d  /run/myapp       0755  myapp myapp -
d  /tmp/myapp       0755  myapp myapp 7d

# L = symlink oluştur
L  /var/lock        -     -     -     -  /run/lock

# f = dosya oluştur (yoksa)
f  /var/lib/myapp/config.json  0640  myapp  myapp  -  {}

systemd unit: BindPaths ve TemporaryFileSystem

/etc/systemd/system/myapp.service
[Unit]
Description=My Embedded Application
After=network.target var.mount

[Service]
Type=simple
User=myapp
ExecStart=/usr/bin/myapp

# Service özel namespace: sadece bu servis yazabilir
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes

# Yazma iznine ihtiyaç duyulan dizinler
ReadWritePaths=/var/lib/myapp
ReadWritePaths=/data/myapp

[Install]
WantedBy=multi-user.target

Journal persistent yapma

bash
# /var/log/journal dizini oluştur (persistent journal için)
mkdir -p /var/log/journal
systemd-tmpfiles --create --prefix /var/log/journal
systemctl restart systemd-journald

# journald.conf
# [Journal]
# Storage=persistent
# SystemMaxUse=32M
# SystemKeepFree=10M

Bu bölümde

  • systemd.volatile=yes/state kernel parametresi ile volatile mode
  • /etc/fstab: squashfs ro + ext4 /var + tmpfs /tmp ayrımı
  • systemd-tmpfiles: boot'ta dizin ve dosya oluşturma
  • ProtectSystem=strict + ReadWritePaths ile servis izolasyonu
  • Persistent journal: /var/log/journal + Storage=persistent

05 Persist edilecek veriler

Read-only rootfs tasarımında en kritik kararlardan biri, hangi verinin kalıcı (persistent) hangisinin geçici (volatile) olduğunu belirlemektir.

Read-only kalmali

/bin, /sbin, /lib, /usrTüm sistem ikilileri ve kütüphaneler. Hiçbir zaman çalışma zamanında değiştirilmemeli.
/etc (çoğu)Sistem konfigürasyonunun büyük kısmı. Sadece değişmesi gereken dosyalar (ağ config, hostname) overlay upper'da tutulur.
/bootKernel, DTB, initramfs, bootloader. OTA güncellemeleri dışında dokunulmamalı.

Writable olmalı

/var/logLog dosyaları. Tercih: tmpfs (reboot'ta kaybolur) veya /data altında persist.
/var/libServis durum verileri (systemd, NetworkManager, BT pairing). Persistent olmalı.
/tmp, /runGeçici dosyalar ve socket'ler. Her zaman tmpfs.
/dataKullanıcı verileri, device-specific config, uygulama durumu. Ayrı ext4 partition.

Partition şeması

  eMMC / SD Kart Partition Tablosu
  ┌─────────────────────────────────────────────────────┐
  │  p1: boot        64 MB   FAT32   kernel + dtb + initramfs  │
  │  p2: rootfsA    256 MB   squashfs  Slot A (aktif)          │
  │  p3: rootfsB    256 MB   squashfs  Slot B (güncelleme)     │
  │  p4: data       Kalan    ext4      Kalıcı kullanıcı verisi  │
  └─────────────────────────────────────────────────────┘

NAND flash için UBI+UBIFS vs ext4+overlayfs

TeknolojiAvantajDezavantajKullanım
SquashFS + ext4Basit, iyi araç desteğiNAND için wear leveling gerekireMMC, SD, NOR flash
UBI + UBIFSNAND native, wear leveling, bad blockDaha karmaşık setupRaw NAND
SquashFS + UBIRO squashfs üstünde UBI volumeKompleksNAND + RO rootfs

Factory reset mekanizması

factory_reset.sh
#!/bin/sh
# Factory reset: data partition'ı sıfırla

echo "UYARI: Tüm kullanıcı verileri silinecek!"
read -r answer
[ "$answer" != "EVET" ] && exit 1

DATADEV=/dev/mmcblk0p4

# /data'yı umount et
umount /data

# Sıfırdan ext4 formatla
mkfs.ext4 -L data -F "$DATADEV"

# Yeniden mount et
mount /data

# Temel dizin yapısını oluştur
mkdir -p /data/overlay/upper /data/overlay/work
mkdir -p /data/etc-upper /data/etc-work
mkdir -p /data/app /data/logs

echo "Factory reset tamamlandı. Yeniden başlatılıyor..."
reboot

Bu bölümde

  • Kalıcı vs geçici veri ayrımı: hangi dizin nerede tutulur
  • Partition şeması: boot + rootfsA + rootfsB + data
  • NAND flash: UBI+UBIFS vs eMMC: SquashFS+ext4 karşılaştırması
  • Factory reset: data partition yeniden formatlama script'i

06 dm-verity ile bütünlük doğrulama

dm-verity, read-only block device üzerinde her okumada hash doğrulaması yaparak storage tampering'ini tespit eder; medikal ve otomotiv sistemlerde zorunlu olan doğrulanmış boot zincirinin temel bileşenidir.

dm-verity nasıl çalışır

dm-verity, block device üzerindeki her 4 KB veri bloğu için bir hash değeri saklar. Hash'ler Merkle ağacı yapısında düzenlenir; ağacın kökü (root hash) bootloader'a gömülür veya imzalanmış bir dosyadan okunur. Her blok okumada hash hesaplanır ve ağaçtaki değerle karşılaştırılır — uyuşmazlık anında I/O hatası döner.

  Blok 0     Blok 1     Blok 2     Blok 3
     │          │          │          │
   H(0)       H(1)       H(2)       H(3)       Hash Level 0
     └─────────┘           └─────────┘
      H(H(0)+H(1))          H(H(2)+H(3))       Hash Level 1
           └────────────────────┘
               root hash                        Bootloader'da saklı

veritysetup kullanımı

bash
# Hash device oluştur (rootfs block device + ayrı hash device)
# p2 = rootfs (ro squashfs), p4 = hash partition
veritysetup format /dev/mmcblk0p2 /dev/mmcblk0p4
# VERITY header information for /dev/mmcblk0p2
# UUID:                  ...
# Hash type:             1
# Data blocks:           131072
# Data block size:       4096
# Hash block size:       4096
# Hash algorithm:        sha256
# Salt:                  abcdef1234...
# Root hash:             7f3a2b9c...  ← bunu kaydet!

# Root hash'i kaydet
ROOT_HASH=$(veritysetup format /dev/mmcblk0p2 /dev/mmcblk0p4 2>&1 \
    | grep "Root hash" | awk '{print $3}')

# Bütünlüğü doğrula
veritysetup verify /dev/mmcblk0p2 /dev/mmcblk0p4 "$ROOT_HASH"
# Başarı: exit code 0 (çıktı yok)

# dm-verity device map'le
veritysetup open /dev/mmcblk0p2 vroot /dev/mmcblk0p4 "$ROOT_HASH"
# /dev/mapper/vroot oluşur

# Doğrulanmış rootfs'i mount et
mount -t squashfs -o ro /dev/mapper/vroot /mnt

Kernel cmdline ile boot

U-Boot bootargs
# dm-verity ile boot — root hash bootargs içinde
root=/dev/dm-0
dm-mod.create="vroot,,0,ro,0 262144 verity 1 /dev/mmcblk0p2 /dev/mmcblk0p4 4096 4096 131072 131072 sha256 <root_hash> <salt>"
rootfstype=squashfs ro

Kernel konfigürasyonu

linux/.config
CONFIG_BLK_DEV_DM=y
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y   # root hash imzası doğrulama
CONFIG_CRYPTO_SHA256=y
UYARI

dm-verity her okumada hash hesapladığından, xz sıkıştırmalı SquashFS üzerinde ek CPU yükü oluşturur. Performans kritik sistemlerde lz4 veya zstd tercih edin. dm-verity, SquashFS'in sıkıştırılmış blok katmanının altında, raw block device düzeyinde çalışır.

Bu bölümde

  • dm-verity: Merkle ağacı ile her blok okumada hash doğrulaması
  • veritysetup format: rootfs + ayrı hash partition oluşturma
  • veritysetup open: /dev/mapper/vroot device'ı
  • Kernel cmdline ile dm-verity parametreleri
  • Kernel config: CONFIG_DM_VERITY=y

07 Buildroot ile read-only image

Buildroot, SquashFS rootfs üretimi için hazır destek sunar; post-build script ve fstab overlay mekanizmasıyla tam read-only sistem oluşturulabilir.

Buildroot konfigürasyonu

make menuconfig seçenekleri
# Filesystem Images → SquashFS root filesystem
BR2_TARGET_ROOTFS_SQUASHFS=y
BR2_TARGET_ROOTFS_SQUASHFS_LZMA=n
BR2_TARGET_ROOTFS_SQUASHFS_XZ=y

# ext4 image de üretelim (data partition için)
BR2_TARGET_ROOTFS_EXT2=y
BR2_TARGET_ROOTFS_EXT2_4=y
BR2_TARGET_ROOTFS_EXT2_SIZE="256M"

# Overlay directory: içeriği rootfs üzerine kopyalanır
BR2_ROOTFS_OVERLAY="board/myboard/rootfs-overlay"

# Post-build ve post-image script'ler
BR2_ROOTFS_POST_BUILD_SCRIPT="board/myboard/post-build.sh"
BR2_ROOTFS_POST_IMAGE_SCRIPT="board/myboard/post-image.sh"

post-build.sh

board/myboard/post-build.sh
#!/bin/bash
TARGET_DIR="$1"

# /var/log → tmpfs, log'lar RAM'de
rm -rf  "$TARGET_DIR/var/log"
ln -sf  /run/log  "$TARGET_DIR/var/log"

# /var/tmp → /tmp
rm -rf  "$TARGET_DIR/var/tmp"
ln -sf  /tmp  "$TARGET_DIR/var/tmp"

# /var/run → /run
rm -rf  "$TARGET_DIR/var/run"
ln -sf  /run  "$TARGET_DIR/var/run"

# fstab oluştur
cat > "$TARGET_DIR/etc/fstab" <<'EOF'
/dev/mmcblk0p2  /      squashfs  ro,noatime              0  0
/dev/mmcblk0p3  /data  ext4      rw,noatime,errors=remount-ro  0  2
tmpfs           /tmp   tmpfs     rw,nosuid,nodev,size=32m 0  0
tmpfs           /run   tmpfs     rw,nosuid,nodev,size=32m 0  0
EOF

post-image.sh — flash layout üretimi

board/myboard/post-image.sh
#!/bin/bash
BINARIES_DIR="${BINARIES_DIR}"
BOARD_DIR="$(dirname $0)"

# genimage ile tam eMMC image üret
genimage \
    --config  "$BOARD_DIR/genimage.cfg" \
    --rootpath "$TARGET_DIR" \
    --tmppath  "$BUILD_DIR/genimage.tmp" \
    --inputpath "$BINARIES_DIR" \
    --outputpath "$BINARIES_DIR"
board/myboard/genimage.cfg
image sdcard.img {
  hdimage {}

  partition boot {
    partition-type = 0xC
    bootable = true
    image = "boot.vfat"
    size = 64M
  }

  partition rootfsA {
    partition-type = 0x83
    image = "rootfs.squashfs"
    size = 256M
  }

  partition rootfsB {
    partition-type = 0x83
    image = "rootfs.squashfs"
    size = 256M
  }

  partition data {
    partition-type = 0x83
    image = "rootfs.ext2"
    size = 512M
  }
}

U-Boot'tan squashfs boot

U-Boot environment
# squashfs'i mmc'den kernel'a yükle
setenv bootcmd 'mmc dev 0; sqfsload mmc 0:2 ${loadaddr} /boot/Image; \
    sqfsload mmc 0:2 ${fdt_addr} /boot/myboard.dtb; \
    setenv bootargs root=/dev/mmcblk0p2 rootfstype=squashfs ro \
    systemd.volatile=state; \
    booti ${loadaddr} - ${fdt_addr}'

Bu bölümde

  • BR2_TARGET_ROOTFS_SQUASHFS=y: Buildroot SquashFS konfigürasyonu
  • post-build.sh: /var/log → tmpfs symlink, fstab oluşturma
  • genimage.cfg: boot + rootfsA + rootfsB + data partition şeması
  • U-Boot sqfsload komutu ile squashfs'ten kernel yükleme

08 Yocto ile read-only image

Yocto, read-only rootfs için meta-layer düzeyinde hazır destek sunar; IMAGE_FEATURES ve volatile-binds recipe'si ile bildirimsel konfigürasyon yapılır.

IMAGE_FEATURES

conf/local.conf veya image recipe
# Read-only rootfs aktif et
IMAGE_FEATURES += "read-only-rootfs"

# Debug sembolleri içeren paketler (geliştirme için)
IMAGE_FEATURES += "dbg-pkgs"

# SquashFS image üretimi
IMAGE_FSTYPES += "squashfs-xz"

# ext4 data partition
IMAGE_FSTYPES += "ext4"

volatile-binds recipe

meta-myboard/recipes-core/volatile-binds/volatile-binds.bbappend
# /etc/volatile.conf — tmpfs üzerine bind mount tanımları
VOLATILE_LOG_DIR ?= "volatile"

# Formatı: "type mode uid gid path [link]"
VOLATILE_BINDS += "/var/volatile/log 0755 root root /var/log"
VOLATILE_BINDS += "/var/volatile/tmp 1777 root root /var/tmp"
VOLATILE_BINDS += "/var/volatile/lib/systemd 0755 root root /var/lib/systemd"

/etc/volatile.conf formatı

/etc/volatile.conf
# d = dizin oluştur, f = dosya oluştur
# Format: type  mode  uid  gid  path
d  0755  root  root  /var/volatile/log
d  0755  root  root  /var/volatile/run
d  1777  root  root  /var/volatile/tmp
d  0755  root  root  /var/volatile/lib
d  0755  root  root  /var/volatile/lib/systemd
d  0755  root  root  /var/volatile/lib/NetworkManager

Yocto image recipe örneği

meta-myboard/recipes-core/images/myboard-image.bb
SUMMARY = "MyBoard Read-Only Production Image"
LICENSE = "MIT"

inherit core-image

IMAGE_FEATURES += " \
    read-only-rootfs \
    ssh-server-openssh \
"

IMAGE_INSTALL += " \
    packagegroup-core-boot \
    kernel-modules \
    myapp \
    volatile-binds \
"

IMAGE_FSTYPES = "squashfs-xz ext4"

# /var'ı tmpfs'e bağlamak için systemd-volatile-root
DISTRO_FEATURES:append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"

QA kontrolü

bash
# Image'ı build et
bitbake myboard-image

# Üretilen dosyaları kontrol et
ls -lh tmp/deploy/images/myboard/
# myboard-image-myboard.squashfs-xz
# myboard-image-myboard.ext4

# SquashFS içeriğini doğrula
unsquashfs -l myboard-image-myboard.squashfs-xz | grep var/log
# squashfs-root/var/log -> /run/log (symlink olmalı)

Bu bölümde

  • IMAGE_FEATURES += "read-only-rootfs": Yocto ro rootfs aktivasyonu
  • volatile-binds: tmpfs bind mount tanımları
  • /etc/volatile.conf: geçici dizinlerin formatı
  • Image recipe: IMAGE_FSTYPES = "squashfs-xz ext4"
  • QA: unsquashfs -l ile symlink doğrulama

09 Factory reset ve güncelleme stratejisi

A/B partition şeması ve read-only rootfs birlikte atomik OTA güncellemeleri ve güvenilir factory reset mekanizması sağlar.

A/B partition güncelleme akışı

  Mevcut durum: Slot A aktif, Slot B eski/boş

  1. OTA daemon yeni rootfs.squashfs indirir
  2. Slot B'ye (mmcblk0p3) yazar: dd if=rootfs.squashfs of=/dev/mmcblk0p3
  3. U-Boot env: fw_setenv upgrade_available 1
                 fw_setenv boot_slot B
  4. Sistem yeniden başlar, U-Boot Slot B'yi açar
  5. Health check: systemd service 30 saniye sonra durum raporlar
  6. Başarılı: fw_setenv upgrade_available 0 (Slot B kalıcı)
  7. Başarısız: bootcount aşımı → U-Boot Slot A'ya geri döner

U-Boot bootcount ve rollback

U-Boot bootscript
# U-Boot boot.cmd
if test "${upgrade_available}" = "1"; then
    setexpr bootcount ${bootcount} + 1
    if test ${bootcount} > 3; then
        echo "Rollback: boot_slot A"
        setenv boot_slot A
        setenv upgrade_available 0
        setenv bootcount 0
        saveenv
    fi
fi

if test "${boot_slot}" = "B"; then
    setenv rootpart 3   # mmcblk0p3 = rootfsB
else
    setenv rootpart 2   # mmcblk0p2 = rootfsA
fi

setenv bootargs root=/dev/mmcblk0p${rootpart} rootfstype=squashfs ro \
    systemd.volatile=state
sqfsload mmc 0:${rootpart} ${loadaddr} /boot/Image
booti ${loadaddr} - ${fdt_addr_r}

Health check servisi

/etc/systemd/system/health-check.service
[Unit]
Description=OTA Update Health Check
After=network-online.target myapp.service
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/health-check.sh
RemainAfterExit=no
/usr/bin/health-check.sh
#!/bin/sh
# Güncelleme sağlık kontrolü

UPGRADE_AVAILABLE=$(fw_printenv upgrade_available 2>/dev/null | cut -d= -f2)

[ "$UPGRADE_AVAILABLE" != "1" ] && exit 0

# Kritik servislerin çalışıp çalışmadığını kontrol et
check_service() {
    systemctl is-active --quiet "$1" && return 0
    echo "HATA: $1 çalışmıyor — rollback başlatılıyor"
    fw_setenv upgrade_available 0
    fw_setenv boot_slot A
    reboot
}

check_service myapp.service
check_service NetworkManager.service

# Tüm kontroller geçti: güncellemeyi onayla
fw_setenv upgrade_available 0
fw_setenv bootcount 0
echo "Güncelleme onaylandı."

OTA ile squashfs atomik swap

ota_update.sh
#!/bin/sh
# Aktif slot'u oku
ACTIVE_SLOT=$(fw_printenv boot_slot 2>/dev/null | cut -d= -f2)
ACTIVE_SLOT=${ACTIVE_SLOT:-A}

if [ "$ACTIVE_SLOT" = "A" ]; then
    TARGET_DEV=/dev/mmcblk0p3
    TARGET_SLOT=B
else
    TARGET_DEV=/dev/mmcblk0p2
    TARGET_SLOT=A
fi

echo "Güncelleniyor: Slot ${TARGET_SLOT} (${TARGET_DEV})"

# Yeni squashfs'i hedef partition'a yaz
dd if=/tmp/rootfs-new.squashfs of="$TARGET_DEV" bs=4M status=progress
sync

# Yeni slot'u aktif yap
fw_setenv boot_slot "$TARGET_SLOT"
fw_setenv upgrade_available 1
fw_setenv bootcount 0

echo "Güncelleme yazıldı. Yeniden başlatılıyor..."
reboot
NOT

Bu yaklaşım, SWUpdate veya Mender gibi araçların kendi A/B mekanizmalarıyla da birleştirilebilir (bkz. OTA rehberi). Elle yazılan script yerine SWUpdate kullanılması önerilir; imza doğrulaması, şifreleme ve network download dahil edilir.

Bu bölümde

  • A/B partition güncelleme akışı: indir → yaz → aktif → health check → onayla
  • U-Boot bootcount + rollback: upgrade_available ve boot_slot değişkenleri
  • Health check servisi: kritik servis kontrolü ve otomatik rollback
  • Atomik squashfs swap: dd + fw_setenv zinciri
  • Factory reset: data partition yeniden formatlama