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
┌─────────────────────────────────────────────┐
│ 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ör | Gereksinim | Çözüm |
|---|---|---|
| IoT sensör | Güç kesintisi toleransı | SquashFS + tmpfs upper |
| Sanayi HMI | Deterministik boot | SquashFS + /data persist |
| Medikal cihaz | Yazılım bütünlüğü | SquashFS + dm-verity |
| Otomotiv ECU | OTA + rollback | A/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
# 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ı
| Algoritma | Oran | Decomp hızı | Kullanım |
|---|---|---|---|
| gzip | Orta | Hızlı | Genel amaç, eski sistemler |
| lzo | Düşük | Çok hızlı | Yavaş flash, latency kritik |
| xz | En iyi | Yavaş | Küçük flash, güçlü CPU |
| zstd | İyi | Çok hızlı | Modern sistemler, önerilen |
| lz4 | Düşük | En hızlı | RAM kısıtlı, çok düşük latency |
Kernel konfigürasyonu
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/-sile image inceleme- Kernel config:
CONFIG_SQUASHFS=yve 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
lowerdir=a:b:c.Mount komutu
# 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ı
# 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
#!/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
# /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
#!/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
/etciç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
# 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
# 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
# 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
[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
# /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/statekernel parametresi ile volatile mode- /etc/fstab: squashfs ro + ext4 /var + tmpfs /tmp ayrımı
systemd-tmpfiles: boot'ta dizin ve dosya oluşturmaProtectSystem=strict+ReadWritePathsile 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
Writable olmalı
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
| Teknoloji | Avantaj | Dezavantaj | Kullanım |
|---|---|---|---|
| SquashFS + ext4 | Basit, iyi araç desteği | NAND için wear leveling gerekir | eMMC, SD, NOR flash |
| UBI + UBIFS | NAND native, wear leveling, bad block | Daha karmaşık setup | Raw NAND |
| SquashFS + UBI | RO squashfs üstünde UBI volume | Kompleks | NAND + RO rootfs |
Factory reset mekanizması
#!/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ı
# 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
# 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
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
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şturmaveritysetup 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
# 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
#!/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
#!/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"
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
# 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
sqfsloadkomutu 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
# 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
# /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ı
# 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
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ü
# 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 aktivasyonuvolatile-binds: tmpfs bind mount tanımları- /etc/volatile.conf: geçici dizinlerin formatı
- Image recipe:
IMAGE_FSTYPES = "squashfs-xz ext4" - QA:
unsquashfs -lile 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 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
[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
#!/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
#!/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
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_availableveboot_slotdeğişkenleri - Health check servisi: kritik servis kontrolü ve otomatik rollback
- Atomik squashfs swap:
dd+fw_setenvzinciri - Factory reset: data partition yeniden formatlama