docs: AGENTS.md — 26 Eylül oturumu, çoklu disk + güvenlik

Bu oturumda yapılan tüm işin kaydı. Yeni ajan oturumları buradan
devam edecek.

## Eklenen ana bölümler

**🚨 22de7d5 regresyonu** — dosyanın başına kondu. AI ajan commit'i
2438 → 811 satır yaparak 1804 satırlık manuel bölümleme arayüzünü
sildi; commit mesajı "#[allow(dead_code)] ekledim" diyordu. Etkisi
(+/ düzenleme butonları hiçbir şey yapmıyordu, delete hiç set
 edilmiyordu, LVM erişilemez), geri alınışı ve dört ders kayıt altına
alındı. Yedek ref'ler belirtildi: tag `backup-before-unrevert-22de7d5`,
branch `backup/pre-unrevert`.

**Squashfs tasarım kararı** — kurulu sistem ISO'nun squashfs'inden
kopyalanıyor, dolayısıyla paket/DM/masaüstü ortamı zaten içeride.
Lisans, ekran yöneticisi ve paket seçimi sihirbazdan çıkarıldı; dormant
modüllerin etkinleştirme koşulları yazıldı. Netinstall'ın ayrı bir
proje olacağı kayıt altında.

**plan_disk_layout** — bölüm numaralandırmasındaki veri kaybı hatası:
UI "en küçük boş slot" diyordu, gerçekte ilk boşluk sığmadığında
mkpart diskin sonuna yazıp farklı numara alıyordu, mkfs yanlış aygıta
gidiyordu. Uyumsuzluğun oluştuğu somut senaryo ASCII diyagramla
yazıldı. Saf fonksiyonun imzası ve ne döndürdüğü kayıt altında.

**P0 güvenlik** — parolanın argv'den geçmesi, LUKS cmdline'ı,
`pisi configure-pending`'in hiç çalışmaması, hata durumunda
temizlik yapılmaması.

**Kurallar** — parted MB/MiB tuzağı, kare başına alt süreç yasağı,
locale tuzağı, TOML düzenleme uyarısı, emoji fontu kısıtı.

**🧪 ISO test kontrol listesi** — 10 maddelik, sıralı senaryo. Testler
mantığı kanıtladı ama gerçek parted çıktısıyla karşılaştırılmadı;
bu dosya noktası açıkça işaretlendi.

## Doğrulama
AGENTS.md'deki her iddia grep/tomllib ile tek tek kontrol edildi.
Bir sayı düzeltildi: `let _ =` sayısı 55 değil 51 (42'si jobs/).
Yedek ref'lerin durduğu teyit edildi.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erkan IŞIK
2026-09-26 00:06:39 +03:00
parent b6f9af3b40
commit 3b9a128704
+441 -92
View File
@@ -1,111 +1,460 @@
# AGENTS.md — Yali-rs Geliştirme Notları
> Ajan oturumları arasında süreklilik için. Son güncelleme: parted MiB düzeltmesi (23 Ağu)
> Ajan oturumları arasında süreklilik için.
> Son güncelleme: **26 Eylül 2026** — çoklu disk bölümleme + güvenlik düzeltmeleri.
## Proje
- PisiLinux kurulum aracı (eski Python Yali'nin Rust/egui yeniden yazımı).
- Ana dallar: `main`. Push: `git push origin main`.
- Referans kod: `/media/yedekleme/github/erkanisik/yali` (eski Python Yali —
storage mimarisi: `yali/storage/{operations,partitioning,devicetree}.py`).
---
## Mimari Kararlar (2026-08 dönemi)
## 🚨 EN ÖNEMLİ: 22de7d5 regresyonu (25 Ağu 2026)
### Çoklu Disk Manuel Bölümleme
- `CustomPartition` alanları (`src/installer.rs`):
- `disk`: bölümün ait olduğu disk ("/dev/sda") — gruplama bununla.
- `orig_device`: lsblk'tan gelen fiziksel cihaz adı; **boş = kullanıcı tanımı yeni bölüm**.
Silme/işaret mantığı buna dayanır.
- `delete: bool`: eski Yali `OperationDestroyDevice` karşılığı → kurulumda `parted rm`.
- UI (`src/steps/partition.rs`):
- Manuel moda girişte TÜM diskler otomatik taranıp listelenir (disk kartına tıklamak
artık sadece "yeni bölüm hedef diski" seçer; tanımları sıfırlamaz — merge eder,
`orig_device` eşleşmesinde kullanıcı tanımı kazanır).
- Tablo `TableRow::Part | TableRow::FreeSpace` enum'u ile kurulur; HER disk grubu
kendi sütun başlıklarını çizer (`draw_partition_table_header`).
- Silinmek işaretli bölümler ayrı satır çizmez; İLK silinenin konumunda TEK
"Boş Alan" satırında birleşir (GParted tarzı).
`Boş Alan = disk − (korunan fiziksel + yeni tanımlar)`; 0 ise satır gizlenir.
- "Kalan alan" SEMBOLİKTİR (size_mb=0): kaydetme anında sayıya çevrilmez!
Tabloda/özetde kapasite olarak gösterilir; gerçek boyut kurulum anında boşluktan alınır.
(Somutlaştırma taşmaya yol açtığı için kaldırıldı — swap sonradan büyütülünce kök patlıyordu.)
- Slot ataması (`installer::assign_partition_numbers`): parted'ın gerçek davranışını
simüle eder — fiziksel (silinmeyen) bölümler numarasını korur, silinenlerin slotu
boşalır, yeni tanımlar en küçük boş slotu alır. UI ile job tutarlılığı buradan gelir.
**Bu dosyayı okuyan her ajan önce bu bölümü okumalı.**
### Granular Kurulum Job'ı (`jobs/partition.rs::CustomPartitionJob`)
Eski wipe&rebuild (mktable + hepsini baştan yarat) KALDIRILDI. Yeni fazlar:
1. FAZ A: `delete=true` olanlar `parted rm N` — N, `orig_device`'tan;
**büyükten küçüğe** sil (kernel slot kayması).
2. FAZ B: yeni tanımlar `disk_free_regions_mb()` ile sysfs'ten hesaplanan boş
bölgelere first-fit yerleşir ("kalan" = en büyük bölge). Sabit boyutlular ÖNCE,
sembolikler SONRA (`creates.sort_by_key(size==0)`) işlenir.
3. FAZ C: yalnızca istenenler mkfs (LUKS akışı korundu).
- İşlemi olmayan disklere ve `delete=true` satırlara mount/fstab/summary dokunmaz.
Commit `22de7d5` — *"Dead code uyarılarını gidermek için `#[allow(dead_code)]`
eklendi"* (Devin AI ajanı) — mesajı üç tane `#[allow]` eklemekle
sınırlıydı. Gerçekte **`src/steps/partition.rs`'i 2438 satırdan 811
satıra düşürdü: 1804 satır silindi.**
### Diğer Düzeltmeler
- **parted birim tuzağı (KRİTİK):** parted `"MB"` sonekini **ondalık** (10^6 byte) okur,
bizim hesaplar binary (2^20). `mkpart 3944MB` → sektör 7703125 (hizasız, boş alana
uymaz) → "Becerebildiğimiz buna en yakın konum..." hatası. ÇÖZÜM: parted'a daima
`"{}MiB"` sonekiyle geç (`jobs/partition.rs`, hem otomatik hem CustomPartitionJob).
Tamsayı MiB = 2048 sektörün katı → hizalama hatası çıkmaz. UI'da kullanıcıya MB
gösterilir ama parted argümanı asla "MB" olmasın.
- MB dönüşümleri tek bölme: `x / 1_048_576` (çift `x/1024/1024` hassasiyet kaybı yapardı).
- Disk seçiminde `size_gb * 1024` yerine `size_bytes / 1_048_576`.
- Mountpoint değişince boyut alanı SADECE boş/"0"/"remaining" ise varsayılanlanır
(kullanıcının elle girdiği değer ezilmez).
- Simge fontu: `assets/remixicon.ttf` egui fallback fontu (`ui/theme.rs setup_fonts`).
Kutucuk görürsen karakter fontta yok demektir — fontTools ile cmap kontrol et:
kullanılanlar: EB7B check, EA6B arrow, EC28 trash, EA13 add, EFE0 pencil, EECD lock,
ECA1 error, EDFB hard-drive-line, ECB5/B7 eye, F4C6 close.
- Alt bar: "Mevcut Durum / Kurulum Sonrası" çift şerit (`manual_dual_bars`,
gri segment = boş) — SADECE seçili disk için gösterilir (`state.selected_disk`).
```
src/steps/partition.rs | 1978 +++++-------------------------------------------
181 insertions(+), 1804 deletions(-)
```
## Bilinen Açıklar / Yapılacaklar
- [ ] VM testinde rapor edilen hatalar düzeltilecek (kullanıcı listeleyecek)
- [ ] `manual_capacity_error` yalnızca sabit boyutları kontrol eder; LVM PV/LV toplamları ayrıca doğrulanmalı
- [ ] Silinmek işaretli bölüm bir VG'nin PV'siyse SetupLvmJob tarafında kontrol yok
- [ ] Otomatik mod (erase disk) hâlâ tek disk; çoklu otomatik plan yok
- [ ] Debug amaçlı kalan `eprintln!` satırları temizlenebilir
### Silinenler
Tüm manuel bölümleme arayüzü: ekle/düzenle/sil onayı/LVM VG/LVM LV
diyalogları, `draw_partition_table_header`, `manual_dual_bars`,
`TableRow` enum'u, `disk_size_mb`, `used_mb_on_disk`, `draw_layout_bar`,
`bar_style`, `partition_bar`, `format_number_thousands` ve **21 birim testi**.
### Etkisi
Manuel bölümleme **fiilen işlevsiz** hâle gelmişti:
- "+ Ekle" / "Düzenle" / "Sil" butonları yalnızca **kimsenin okumadığı**
bayraklar set ediyordu → hiçbir diyalog açılmıyordu
- `delete = true` **hiçbir yerden set edilmiyordu** →
`CustomPartitionJob`'un FAZ A'sı (`parted rm`) erişilemez
- `state.volume_groups` / `logical_volumes` **hiçbir yerden yazılmıyordu**
→ `SetupLvmJob` hep boş vektör alıyordu, **LVM tamamen erişilemez**
- `#[allow(dead_code)]` derleyicinin bu boşluğu bildirmesini bastırdı
### Geri alınışı
`8c6dc08` ile düzeltildi. Yedek: tag `backup-before-unrevert-22de7d5`,
branch `backup/pre-unrevert` (a5d4627 = geri alma öncesi HEAD).
`8633422`'nin üç validasyon düzeltmesi geri almanın üzerine yeniden
uygulandı; bir test (`manual_mode_complete_with_valid_partitions`)
güncellendi, `selected_disk` kuralı için yeni test eklendi.
### DERS — TEKRARLANSIN
1. **Bir commit mesajı ne yaptığını tam anlatmıyorsa, diff'i oku.**
`-1804` satır "dead code temizliği" değildir.
2. **`#[allow(dead_code)]` bir teşhis aracıdır, tedavi değil.** Bir şey
kullanılmıyorsa ya silinmeli ya da bağlanmalı; susturmak, altta
yatan boşluğu gizler. Bu projede tam olarak bu oldu.
3. **Yedek dosyaları git'e commit etme.** `partition.rs_24082026` gibi
dosyalar repoya `git rm` edildi, `.gitignore`'a desen eklendi.
Yedek gerekiyorsa **git history**'dir, çalışma ağacı değil.
4. Kullanılmıyor görünen bir adımı **silmeden önce** sihirbaza
bağlı olup olmadığını yazılım olarak doğrula (grep yeterli değil —
`on_enter`'ın ne zaman çağrıldığına bak).
---
## Proje Tasarım Kararı: SQUASHFS KURULUMU
**YALI kurulu sistemi ISO'nun squashfs'inden kopyalar** (`CopyFilesJob`
rsync). Bunun temel sonucu:
> ISO'da hangi paketler, ekran yöneticisi ve masaüstü ortamı varsa,
> kurulum onları **zaten içerir**.
Bu yüzden sihirbaz **paket seçimi, ekran yöneticisi seçimi veya lisans
kabulü sunmaz** — sunduğu ayarları kopyalama zaten ezdiği için
yanıltıcı olurdu.
### Netinstall gelecekte ayrı bir proje
İki ayrı proje çıkacak:
1. **Bu proje** — squashfs'i diske kopyalar (mevcut akış)
2. **Netinstall** — paketleri ağdan kurar, squashfs akışı iptal eder
`src/steps/netinstall.rs` ve `src/jobs/netinstall.rs` **dormant** olarak
repoda duruyor. Etkinleştirme koşulu `src/steps/mod.rs`'te yazılı:
- `main.rs`'de `steps` vektörüne `Box::new(steps::netinstall::NetinstallStep)`
(Bootloader ile Summary arasına)
- `JobQueueConfig::selected_package_groups` doldurulmalı
- `InstallExtraPackagesJob` kuyruğa alınmalı
`#[allow(dead_code)]` **yalnızca bu üç dormant modüle** ve **yalnızca
belgelenmiş kasıtlı uyku için** konmuştur. Modül dışındaki taze ölü kod
yine uyarı üretir.
### Ekran yöneticisi tespiti (çalışma zamanında)
`ConfigureAutologinJob` DM'i **seçmez, tespit eder**:
`detect_display_manager()` → `/usr/bin/{sddm,lightdm,gdm}` yoksa yapılandırma
dizini. Bu iş `CopyFilesJob`'dan sonra çalıştığı için `mount` doludur.
> Önceden `state.display_manager`'a (GlobalState'in **sabit `"sddm"`
> varsayılanı**) göre dallanılıyordu. ISO lightdm kullanıyorsa
> `lightdm.conf`'a dokunulmuyor, `sddm.conf` yazılıyor ve otomatik giriş
> sessizce çalışmıyordu.
`ConfigureAutologinJob` ayrıca:
- sddm config dizini: `/usr/lib/sddm/sddm.conf.d` yoksa `/etc/sddm.conf.d`
denir; ikisi de yoksa **hata** (sessizce yanlış yere yazmak yerine)
- `session_desktop_file()` eşlemesini **tüm DM'lerde** kullanır
(lightdm `autologin-session=plasma` yazıyordu; `.desktop` dosyası
adı bekler)
- DM bulunamazsa **hata** verir, kullanıcıya "elle yapılandırın"
diyebilmeli
---
## Mimari
```
egui main thread ──thread::spawn──► tokio::Runtime::new()
└─ block_on(JobQueue::run_all)
└─ Job::run(&UiSender) ──mpsc──► egui
egui frame loop ◄──try_recv()──────────────────────────────────────┘
```
- `src/steps/*` — saf UI, `InstallerStep` trait'i
- `src/jobs/*` — `async`, `Job: Send + Sync`, `&self` (immutable)
- `GlobalState` job thread'leriyle **paylaşılmıyor**; `clone()` edilip
`JobQueueConfig` POD'una konuyor. `Arc<Mutex<…>>` yok.
### `StepRole` — rol artık konumsal değil
`InstallerStep::role()` → `Normal | Execution | Finish`.
Daha önce `main.rs` rolü `cur == n_steps - 2` ile **tahmin** ediyordu;
yeni adım eklemek bu tespiti sessizce kaydırıyordu. `main.rs`'de 4 test
var, biri "Execution ÖNÜNE adım eklenince rol algısı bozulmuyor".
### Hata tipi
Tüm projede `Result<(), String>`. `thiserror` yok, hata zinciri yok.
Job log satırları Türkçe hardcoded — İngilizce kullanıcı Türkçe hata görüyor.
---
## Çoklu Disk Manuel Bölümleme
### `CustomPartition` alanları (`src/installer.rs`)
- `disk`: bölümün ait olduğu disk ("/dev/sda") — gruplama ve **her
kapasite/slot hesabı** bununla yapılır
- `orig_device`: lsblk'tan gelen fiziksel cihaz adı; **boş = kullanıcı
tanımı yeni bölüm**. Silme/işaret mantığı buna dayanır.
- `delete: bool`: eski Yali `OperationDestroyDevice` karşılığı
### ⭐ Bölüm numaralandırması: `plan_disk_layout` (TEK KAYNAK)
**En kritik bulgu bu oturumdandır — veri kaybı düzeltildi.**
Bölüm numarası iki ayrı yerde bağımsız hesaplanıyordu:
- UI: `assign_partition_numbers` → "en küçük boş slot"
- Job: `mkpart` + `part.device` okuması
**parted davranışı:** `parted rm N` sonrası kernel, bölümleri **başlangıç
offset'ine göre** sıralayıp 1'den numaralandırır. `mkpart` ile oluşturulan
bölüm de aynı sıraya girer.
**Uyumsuzluğun gerçekleştiği senaryo:**
```
sda1 0–100 korunuyor
sda2 100–200 SİLİNECEK → 100 MB boşluk
sda3 200–30200 korunuyor
disk sonu: 10000 MB boş
yeni: 5000 MB
UI : sda2 silinince slot 2 boşalır → yeni = sda2
Disk: 100–200 boşluğu 5000 MB'ye SIĞMAZ → first-fit diskin sonuna yazar
kernel sırası: sda1, sda2(eskisi 3), sda3(YENİ)
Sonuç: UI "yeni = sda2" der, diskte yeni = sda3. FAZ C mkfs'i yanlış
aygıta uygular → kullanıcının KORUMAK İSTEDİĞİ bölüm bozulur.
```
**Çözüm:** `plan_disk_layout(disk, total_mb, physical, destroyed, creates)`
— saf, test edilebilir fonksiyon. Diskin **tamamını** (fiziksel + yeni)
görüp numaralandırır ve şunları döndürür:
- `Vec<PlannedCreate>`: her yeni bölümün konumu + **gerçek** slot numarası
- `Vec<(u32, String)>`: silmeden sonra **KAYAN** fiziksel bölümlerin
eski numarası → yeni aygıt yolu
FAZ B bu planı kullanır, `device_map` doldurur, FAZ C **gerçek aygıtı**
kullanır (kayma varsa loglar). `assign_partition_numbers` artık yalnızca
**UI önizlemesi** içindir — doğruluk kaynağı değil.
> `disk_free_regions_mb` (eski, ayrı kopya) **silindi**. `disk_total_mb`
> ve `read_physical_extents` eklendi; artık diskin gerçek boyutu sysfs'ten
> okunuyor (önce son bölümün bitişinden tahmin yürütülüyordu).
### `assign_partition_numbers`
Disk bazlı `HashMap<disk, BTreeSet<num>>` ile **bağımsız** çalışır;
sda1 ile sdb1 çakışmaz.
> Eski test `renumber_keeps_disks_independent` **TUUTOLOJİK'ti**: hep
> fiziksel bölüm kullanıyordu, fonksiyon no-op çalışıyordu; global
> (disk-suz) bir uygulama da testi geçerdi. `multidisk_tests` içinde
> gerçek çapraz-disk yolunu (yeni tanım) sınayan testler var.
### `CustomPartitionJob` fazları (`jobs/partition.rs`)
1. **FAZ A**: `delete=true` → `parted rm N`, büyükten küçüğe
2. **FAZ B**: `plan_disk_layout` ile yerleşim + numaralandırma, sonra `mkpart`
3. **FAZ C**: `device_map` üzerinden **gerçek** aygıta mkfs (LUKS akışı korundu)
- Sabit boyutlular ÖNCE, sembolikler SONRA (`sort_by_key(size==0)`)
- İşlemi olmayan disklere hiç girilmez (`⏭️ ... korunuyor`)
### UI (`src/steps/partition.rs`)
- **Ekleme diyaloğunda disk seçici var** (`ManualState::add_disk`).
Önceden yeni bölümün diski `state.selected_disk`'e sabitlenmişti ve
diyalogda disk kontrolü yoktu — `/home`'u sdb'ye koymak için
kurulumun GRUB hedefini değiştirmek gerekiyordu.
- **Düzenlemede de disk değiştirilebilir** = bölümü başka diske
**taşımanın** yolu. Taşınırsa `orig_device` temizlenir (yoksa job
eski diske işlem yapmayı sürdürürdü).
- Tablo `TableRow::Part | TableRow::FreeSpace` enum'u ile kurulur; HER disk
grubu kendi sütun başlıklarını çizer (`draw_partition_table_header`).
- **Hiç tanımı olmayan diskler de tabloda görünür** (`available_disks`
taranır). Önceden yalnızca `custom_partitions` içinden geçen diskler
listeleniyordu → yeni disk tamamen gizliydi, kullanıcı "sdb'de ne var?"
diye bakamıyordu.
- Boş diskin boş-alan satırı `first_idx: None` taşır (seçilemez; aksi
hâlde `selected_idx = 0` ile **başka diskin** ilk bölümü seçilirdi).
- Silinmek işaretli bölümler ayrı satır çizmez; İLK silinenin konumunda
TEK "Boş Alan" satırında birleşir (GParted tarzı).
- Varsayılan öneriler (EFI/root/swap) **aktif diskin durumuna** göre verilir.
- "Kalan alan" SEMBOLİKTİR (size_mb=0): kaydetme anında sayıya çevrilmez.
### `selected_disk` hâlâ 3 yerde kullanılıyor
1. Tablo vurgusu + `manual_dual_bars` hangi diski çizsin
2. Diyaloğun varsayılan hedefi
3. `build_custom_job_queue` girdisi → `InstallGrubJob.disk`
Bu yüzden dış ComboBox'un etiketi **"Aktif disk"** + açıklayıcı ipucu
("Kurulum Yapılacak Diski Seçin" yanlış anlam çağrışımı yapıyordu).
`bootloader_device` varsayılanı `selected_disk` değil, **kökün olduğu disk**.
---
## Güvenlik Düzeltmeleri (P0)
### Parola asla argv'ye geçmez
`sh -c "echo 'user:pass' | chpasswd"` deseni **KALDIRILDI**
(`jobs/users.rs`). Parola `/proc/<pid>/cmdline` üzerinden okunabiliyordu
ve `'`/`$(...)` içeren parola komutu bozuyordu.
Doğru desen: **`set_password_via_chpasswd()`** — `chroot <mount> chpasswd
-c SHA512` komutuna stdin pipe ile yazılır. `jobs/partition.rs::
run_luks_format` ile birebir aynı desen. Yazılan veri yazımdan sonra
sıfırlanır.
> **Kural:** parola, LUKS anahtarı, herhangi bir gizli değer
> `format!` ile komut satırına gömülmez. stdin pipe kullanılır.
### LUKS çekirdek parametresi
`cryptdevice=UUID=<x>:cryptroot` **geçerli bir söz dizimi değildi** ve
kök bölüm `is_uefi` ile (3|2) tahmin ediliyordu — manuel modda yanlış
(assign_partition_numbers'tan gelir) ve kök başka diskteyse hiç doğru
değildi.
Artık `luks_backing_device()` (`cryptsetup status` → `device:` satırı) ile
mapper'ın arkasındaki **gerçek** aygıt bulunur, blkid ile LUKS başlık
UUID'si alınır, dracut (`rd.luks.uuid=luks-…`) + mkinitcpio
(`cryptdevice=…`) biçimleri birlikte yazılır. UUID boşsa hata.
`CRYPTROOT_NAME` sabiti: "cryptroot" 5 yerde hardcoded'dı, tek kaynağa bağlandı.
### KRİTİK: parolasız LUKS reddediliyor
`encrypt_root=true` + boş parola → kök düz ext4 formatlanıyor,
`MountPartitionsJob` `/dev/mapper/cryptroot`'u mount etmeye çalışıp
kurulumu öldürüyordu. `PartitionJob` başında `Err` ile reddediliyor.
---
## Hata Yönetimi
### Temizlik HER ZAMAN çalışır
`JobQueue::cleanup` + `with_cleanup(...)`: liste başarıda da hatada da
koşar. Temizlik de hata verirse bu hatalar asıl hataya **eklenip**
kullanıcıya "elle ne yapılacak" listesi olarak gösterilir.
`EmergencyCleanupJob` (`src/jobs/cleanup.rs`) sırasıyla:
1. `mount` altındaki tüm dosya sistemleri
2. LVM: `vgchange -an`
3. LUKS: `cryptsetup close` (yalnızca **hedef diskin** mapper'ları)
4. `swapoff`
`close_crypt_mappers` yalnızca hedef diske ait olanları kapatır —
canlı ortamın kendi şifreli bölümlerine dokunmaz.
### unmount artık tüm ağacı tarar
`UnmountBindJob` elle yazılmış sabit liste (`proc`, `sys`, `dev`,
`boot/efi`) kullanıyordu; manuel düzende `/home`, `/var`, `/boot`
bağlı kalıyordu. Artık `mounted_paths_under()` `/proc/self/mountinfo`
okuyor ve `mount/` altındaki **her şeyi** en derinden yüzeye çözüyor.
> `unescape_mount_field()` mountinfo'daki `\0NNN` oktal kaçışlarını
> çözer; boşluk içeren mountpoint'ler ("My Disk") eşleştirilemezdi.
### Kritik hatalar artık yutulmuyor
- `RunComarJob` tamamen yorum satırıydı → `pisi configure-pending`
**hiç çalışmıyordu**. Geri getirildi.
- `ldconfig` / `update-environment` hataları loglanıp yutuluyordu.
`update-environment` init finalizasyonudur: yutulursa kurulum
"başarılı" görünür ama sistem açılmaz. Artık **fatal**.
- `CleanupLiveJob`'daki 10 `let _ =`: depo tanımları sessizce
başarısız olabiliyordu. Artık **fatal**.
---
## partition "MB" tuzağı (KRİTİK)
parted `"MB"` sonekini **ondalık** (10^6) okur, bizim hesaplar **ikili**
(2^20). `mkpart 3944MB` → sektör 7703125 (hizasız) → "Becerebildiğimiz
buna en yakın konum..." hatası.
> parted'a **daima** `"{}MiB"` geç. Tamsayı MiB = 2048 sektörün katı →
> hizalama hatası çıkmaz. UI'da kullanıcıya MB gösterilir ama parted
> argümanı asla "MB" **olmamalı**.
Bu tuzak iki kez yaşandı: `7209d58`'de düzeltildi, `22de7d5`ten sonra
kök bölümde `"{}MB"` olarak **geri gelmişti**. `d161556` ile düzeltildi ve
nedeni yorumla sabitlendi. Log bile "MiB" derken argüman "MB" gönderiyordu.
---
## Diğer Kalıcı Kurallar
- MB dönüşümleri **tek bölme**: `x / 1_048_576` (çift `x/1024/1024`
hassasiyet kaybı yapardı)
- Simge fontu: `assets/remixicon.ttf` fallback (`ui/theme.rs setup_fonts`).
**Kutu (tofu) görürsen** karakter fontta yok. `fontTools` ile cmap kontrol:
EB7B check, EA6B arrow, EC28 trash, EA13 add, EFE0 pencil, EECD lock,
ECA1 error, EDFB hdd, ECB5/B7 eye, F4C6 close.
**Emoji kullanma** — `NotoColorEmoji` yüklü değil (yorum satırında).
- **Kare başına alt süreç çalıştırma.** egui `show()` her karede
çağrılır (~60 Hz). `blkid`, `nmcli` gibi ağır komutlar `on_enter`'a
taşındı, sonuç alanlarda saklanıyor, `refresh_*()` ile tazeleniyor.
- Locale: `locales/` altında yalnızca `tr.toml` ve `en.toml` **var**.
`set_locale("de")` hata vermez, sessizce kabul edilir ve `t!()`
karşılığı olmayan her anahtar için **anahtarın kendisini** döndürür —
arayüz çıplak anahtarlarla dolar. `funct::sanitize_locale()` her
`set_locale` çağrısından önce kullanılır. GeoIP sadece
`language_for_country()` ile **gerçekten var olan** dillere geçer.
- **TOML düzenleme uyarısı:** `locales/*.toml` satır sonuna yapışan
düzenlemelerle bozuldu ve `rust_i18n` proc macro derlemeyi durdurdu.
Değiştirdikten sonra `tomllib` ile doğrula.
- Bağımlılık: `zbus` (`Cargo.toml`) **hiç kullanılmıyor** — projede tek
D-Bus/COMAR çağrısı yok, tüm sistem etkileşimi shell üzerinden.
---
## Test / Derleme
- `cargo check` + `cargo test` (22 birim testi; partition modülü ağırlıklı).
- VM'de denemek: ISO'yu boot edip manuel bölümlemede sdb üzerindeki senaryolar:
sil→boş-alan birleşimi, geri al, kalan alan, farklı diske bölüm ekleme.
## ✅ Düzenlenmiş Manuel Partition Sorunları (18 Eylül 2026)
```
cargo check --all-targets : 0 hata, 0 uyarı
cargo test : 76/76 (13 test modülü)
cargo clippy : ~77 uyarı, tamamı stil (collapsed_if, replace zinciri)
```
Bu gün, manual partition modundaki 5 kritik sorun düzeltilmiştir:
Bu oturumda **1 → 76** test. Test modülü olmayan dosyalar (`users.rs`,
`location.rs`, `rescue.rs`, `funct.rs`, `autoinstall/mod.rs`, `main.rs`)
içine modüller eklendi. Çoklu test modülü paylaşılan yardımcıları
`steps/partition.rs::testutil` altındadır (`disk()`, `part()`).
1. **`manual_capacity_error` birim tutarlılığı** (`src/steps/partition.rs`):
- `cap` → `cap_mb`, `fixed` → `fixed_mb` değişken isimleri standardized
- `d.size_bytes / 1_048_576` (MiB) ile `defs.iter().map(|p| p.size_mb).sum()` consistently binary MiB (2^20) hesaplaması
- `cargo test` ve `cargo check` passed
### 🧪 ISO TESTİ BEKLENİYOR (kullanıcı yapacak)
`plan_disk_layout` mantığı testlerle kanıtlandı ama **gerçek `parted`
çıktısıyla masaüstünde karşılaştırılmadı**. ISO'yu boot edip şu
senaryoları dene:
2. **`disk_free_regions_mb` delete flagı yönetimi** (`src/jobs/partition.rs`):
- `.filter(|p| p.delete && !p.orig_device.is_empty())` → `.filter(|p| p.delete)`
- Tüm `delete=true` flag'li bölümler (incl. newly defined with empty orig_device) free alan hesaplamasından exclude ediliyor
**Çoklu disk (asıl hedef):**
1. İki disk bağlı: sda + sdb
2. Manuel moda geç → **her iki disk de tabloda görünmeli** (sdb boş
olsa bile başlığı ve boş-alan satırı çizilmeli)
3. sda'da kök + EFI, sdb'de `/home` → **diyalogdaki disk seçiciyle**
4. "Yeni Bölüm" → hedef disk sdb'yi seç → mountpoint `/home`
5. Özet ekranında: iki disk, başlıklarda kapasite, altlarında "boş: …",
kök diski "[kök]" etiketiyle işaretli
3. **`validate_manual` ekstra validasyonlar** (`src/steps/partition.rs`):
- **Duplicate mountpoint kontrolü**: Aynı mountpoint'ta iki live bölüm tanımlı mı? → hata döndürür
- **Swap boyut validasyonu**: 512MB minimum, 8GB maksimum kontrolü eklendi
- **Size 0 partition uyarısı**: Boyut 0 ama mountpoint boşsa uyarı mesajı
**Silme + yeni bölüm (veri kaybı senaryosu):**
6. sda'da orta bir bölümü sil → "Boş Alan" satırında birleşsin
7. Aynı diske büyük bir yeni bölüm ekle (boşluğa SĞMAYACAK boyutta)
8. **Kritik:** korunan bölümün verisi sağlam kalmalı — `mkfs` yanlış
aygıya gitmemeli. Logda "bölüm numarası kaydı" mesajı çıkarsa bu
normal, "gerçek aygıt kullanılıyor" demektir.
4. **`is_complete` disk seçim kontrolü** (`src/steps/partition.rs`):
- `state.partition_plan.is_some()` → `state.selected_disk.is_some()` eklendi
- Kullanıcı disk seçmeden partition tanımları yapmış gibi görünmesini engelledi
- `is_complete` false döndürür, kullanıcıya disk seçme hatası verir
**Geçiş:**
9. Geri/İleri ile adımlar arasında gezin, bölüm tanımlarının korunduğunu doğrula
10. Önyükleyici adımında GRUB aygıtının **kökün olduğu disk** olduğunu doğrula
5. **Test doğrulaması**:
- `cargo check` - başarıyla derlendi (43.22s)
- `cargo test` - 1 test passed (parse_progress2_percent)
---
### Etkilenen Dosyalar
- `src/steps/partition.rs` - 3 değişikling (manual_capacity_error, validate_manual, is_complete)
- `src/jobs/partition.rs` - 1 değişiklik (disk_free_regions_mb delete flagı)
## Bilinen Açıklar / Yapılacaklar
### Bilinen Açıklar / Yapılacaklar Güncellemesi
- [x] `manual_capacity_error` sorunu çözüldü - birim tutarlılığı sağlandı
- [ ] `manual_capacity_error` yalnızca sabit boyutları kontrol eder; LVM PV/LV toplamları ayrıca doğrulanmalı (hala open)
- [ ] Silinmek işaretli bölüm bir VG'nin PV'siyse SetupLvmJob tarafında kontrol yok
- [ ] Otomatik mod (erase disk) hâlâ tek disk; çoklu otomatik plan yok
- [ ] Debug amaçlı kalan `eprintln!` satırları temizlenebilir
### Yüksek öncelik
- [ ] **ISO testi yapılmadı** (yukarıdaki kontrol listesi)
- [ ] `plan_disk_layout` gerçek `parted` ile doğrulanmadı
- [ ] LVM VG'ye PV ataması **tüm disklerdeki tüm PV'ları** alıyor,
disk başına değil; `used_mb_on_disk` ile birlikte düzeltilmeli
- [ ] Silinmek işaretli bölüm bir VG'nin PV'siyse `SetupLvmJob`
tarafında kontrol yok
- [ ] `VolumeGroup.pv_devices` bir PV silinince bayat kalıyor;
LVM UI'ı `has_lvm_pvs` filtresi `delete` dikkate almadığı için
görünür kalıyor
- [ ] BIOS `device.map` `/dev` **alfabetik** sıraya göre yazılıyor;
firmware sırası ile aynı olmayabilir → çoklu diskte BIOS boot kırılgan
- [ ] `manual_capacity_error` sembolik (`size_mb == 0`) tanımları
hesaba katmıyor → `fixed == cap` geçiyor, kurulumda "yeterli boş
alan yok" ile düşüyor
### Orta öncelik
- [ ] `available_disks` `< 4 GB` diski tamamen gizliyor (ComboBox'ta yok,
taranmıyor, kapasite bilgisi yok)
- [ ] `disk_size_mb` bilinmeyen disk için 0 döndürüyor → boyut slider'ı
kullanılamaz hale geliyor
- [ ] `disk_free_regions_mb`'in eski `SECTOR: u64 = 512` varsayımı
4Kn (advanced format) disklerde 8× yanlış boyutlar. sysfs
`queue/logical_block_size` okunmalı. (Yeni kod hâlâ 512 varsayıyor.)
- [ ] `UnmountBindJob` artık tüm ağacı tarıyor ama `EmergencyCleanupJob`
yalnızca `custom_partitions`'dan gelen aygıtları biliyor
- [ ] `rescue.rs`: `pisi takeback` **senkron** çalışıyor (egui thread'i
donuyor); başarısız `mount` başarı diye işaretleniyor
- [ ] Hata tipi: `thiserror` ile `InstallError` enum'ı (≥10 varyant)
- [ ] `is_complete` → `Result<(), Vec<String>>` veya ayrı
`validation_error()`; hatayı gösterme kanalı hâlâ dolambaçlı
- [ ] 51 `let _ =` sessiz hata yutma (42'si `jobs/`), çoğu yorumlanmış
değil
- [ ] `theme.rs`: paletler 4 yerde çelişkili; `branding.toml [theme]`
deserialize ediliyor ama **hiç okunmuyor**; `slides.icons` ve
`interval_secs` yok sayılıyor
- [ ] `setup_fonts` her tema değişiminde 5+ sistem fontu senkron okuyup
atlası yeniden rasterize ediyor
- [ ] Kabuk betiği olarak gömülü 120 satırlık GRUB mantığı
(`bootloader.rs::30_uefi-firmware`), test yok
- [ ] `grub2-mkconfig` hedef sistemin kendi betiğini `sed` ile yamıyor,
`2>/dev/null` ile çalışıyor, `let _ =` ile yutuluyor → süreç
ortada ölürse kurulu sistem yamalı kalıyor
- [ ] `is_mount_point` `/proc/mounts` okurken `\040` kaçışlarını çözmüyor
- [ ] Debug `eprintln!` / `println!` satırları (GUI uygulamasında)
- [ ] ~100 ölü i18n anahtarı (slayt + eski ekran anahtarları)
### Bilinçli olarak yapılmadı
- Otomatik mod (erase disk) çoklu disk değil — tek disk. İstenmedi.
- Bölüm küçültme (shrink) — yok, "sil + yeniden oluştur" yaklaşımı
kullanılıyor (eski Yali ile aynı).
- Lisans ekranı, ekran yöneticisi seçimi, paket seçimi — squashfs
akışında anlamsız, dormantanmış durumda.
---
## Dosya Haritası (değişmiş olanlar)
| Dosya | Son durum |
|---|---|
| `src/jobs/partition.rs` | `plan_disk_layout`, `PhysicalExtent`, `PlannedCreate`, `disk_total_mb`, `read_physical_extents`, `device_map`; `disk_free_regions_mb` silindi |
| `src/jobs/cleanup.rs` | **YENİ** — `EmergencyCleanupJob` |
| `src/steps/partition.rs` | `add_disk` + disk seçici, `validate_manual_on`, boş disk satırları, `multidisk_tests`, `testutil` |
| `src/jobs/users.rs` | `set_password_via_chpasswd` (stdin pipe) |
| `src/jobs/bootloader.rs` | `CRYPTROOT_NAME`, `luks_backing_device`, `mounted_root_device` |
| `src/jobs/finish.rs` | `RunComarJob` geri geldi; `ldconfig`/`update-environment`/depo fatal |
| `src/jobs/mod.rs` | `JobQueue::cleanup`, `mounted_paths_under`, `unescape_mount_field`, `open_crypt_mappers`, `auto_mode_partitions` |
| `src/installer.rs` | `StepRole`, `license_accepted`, `rescue_available` |
| `src/autoinstall/mod.rs` | `[system]` bölümü, `SystemConfig` (elle `Default`!) |
| `src/funct.rs` | `AVAILABLE_LANGUAGES`, `sanitize_locale` |
| `src/lib.rs` | **SİLİNDİ** — çift crate root, `locales/` iki kez gömülüyordu |
| `src/steps/display_manager.rs`, `netinstall.rs`, `license.rs` | dormant (derleniyor, sihirbaza bağlı değil) |