From 546bb79be737b74b7c63bf627663590a7dea4141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkan=20I=C5=9EIK?= Date: Mon, 28 Sep 2026 01:03:56 +0300 Subject: [PATCH] =?UTF-8?q?fix(partition):=20P0=20veri=20kayb=C4=B1=20?= =?UTF-8?q?=E2=80=94=20b=C3=B6l=C3=BCm=20numaraland=C4=B1rmas=C4=B1=20yanl?= =?UTF-8?q?=C4=B1=C5=9F=20modellemi=C5=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Bulgu `plan_disk_layout` bölüm numaralarını "başlangıç offset'ine göre sıralayıp 1'den numaralandırıyordu" varsayımıyla çalışıyordu. 28 Eyl 2026'da canlı ISO'da ölçüldü: **varsayım yanlış.** ## Ölçüm Kernel bölümü **başlangıç offset'ine göre değil, bölüm tablosu slotuna göre** adlandırır. msdos ve gpt için ayrı ayrı doğrulandı. msdos (VM'de, reboot sonrası taze okuma): /dev/sdc1 = 1 MiB (100 MiB) korunan /dev/sdc2 = 5001 MiB (1000 MiB) YENİ /dev/sdc3 = 301 MiB (4700 MiB) korunan — KAYMADI gpt (host'ta loop + --partscan): GPT giriş dizisi ile LBA sırası bilerek FARKLI kuruldu (dizi 2 = 5001 MiB, dizi 3 = 301 MiB). Kernel yine dizi sırasını verdi. ## Etkisi (veri kaybı) Eski kod `device_map`'i TERS yazıyordu: yeni bölüme `sdX3`, korunan bölüme `sdX2`. FAZ C mkfs'i KORUNMASI GEREKEN 4700 MiB'lik bölüme uygulardı — tam olarak `plan_disk_layout`'ın önlemek için yazıldığı veri kaybı senaryosu. ## Düzeltme // hayatta kalanlar numaralarını KORUR let mut taken: BTreeSet = survivors...; // her yeni bölüm en küçük BOŞ slotu alır for p in planned.iter_mut() { let num = ilk boş slot; } Konum hesabı (first-fit / en büyük boş bölge) DEĞİŞMEDİ — o hâlâ gerekli. Yalnızca numaralandırma basitleştirildi. `remapped` tamamen kaldırıldı: hayatta kalan bölüm artık kaymadığı için hep boş kalıyordu. Ölü durum bırakılmadı; dönüş tipi `Result, String>` oldu, FAZ B'deki döngü silindi. ## Uçtan uca doğrulama Kodun tahmini ile gerçek kernel BİREBİR örtüştü (2 yeni bölüm + 1 silme; slot 2 ve slot 4 — slot 3'ü hayatta kalan bölüm tutuyor): kernel: p1=1/100 p2=5001/300 p3=301/4700 p4=5301/1000 kod : sdc2 ......... sdc4 ## Ders Bu, `assign_partition_numbers`'ın "en küçük boş slot" kuralının zaten DOĞRU olduğu anlamına gelir. `plan_disk_layout`'ın bütün varlık sebebi (offset'e göre yeniden numaralandırma) gerekli değilmiş. Ölçüm tahminin yerine geçti. 11 test vardı ama hepsi AYNI yanlış varsayımı paylaşıyordu — testler doğruyu kanıtlamaz, varsayımı tutarlı kılar. "Kernel şunu yapar" inancı ölçülmeli. ## Testler (150 → 151) - live_verified_sdc_oversized_new_partition: artık slot 2 bekliyor - delete_plus_create_renumbers_physical_partitions → delete_plus_create_keeps_physical_partition_numbers - deleting_middle_shifts_later_partitions_down → deleting_a_partition_makes_others_slide (kayma YOK) - deleting_last_partition_shifts_nothing → ...changes_nothing cargo check --all-targets : 0 hata, 0 uyarı cargo test : 151/151 Co-Authored-By: Claude Opus 4.8 (1M context) --- AGENTS.md | 106 ++++++++++++--------- src/jobs/partition.rs | 213 +++++++++++++++++++----------------------- 2 files changed, 159 insertions(+), 160 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 110fc6b..f28665f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -724,11 +724,11 @@ nedeni yorumla sabitlendi. Log bile "MiB" derken argüman "MB" gönderiyordu. ``` cargo check --all-targets : 0 hata, 0 uyarı -cargo test : 150/150 (test modülü sayısı arttıkça güncel tut) +cargo test : 151/151 (test modülü sayısı arttıkça güncel tut) cargo clippy : ~77 uyarı, tamamı stil (collapsed_if, replace zinciri) ``` -Bu oturumda 1 → 150 test. Test modülü olmayan dosyalar (`users.rs`, +Bu oturumda 1 → 151 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()`). @@ -827,7 +827,7 @@ senaryoları dene: | 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/partition.rs` | `plan_disk_layout` (canlı kernel ile doğrulandı), `PhysicalExtent`, `PlannedCreate`, `disk_total_mb`, `read_physical_extents`, `device_map`; `disk_free_regions_mb` ve `remapped` silindi | | `src/jobs/cleanup.rs` | **YENİ** — `EmergencyCleanupJob` | | `src/steps/partition.rs` | **tek diske odaklı tablo, şerit YOK** (diyalogda da disk seçicisi yok), **seçime göre görünen butonlar** (`selected_free`), diske filtreli LVM, `validate_manual_on`, `manual_capacity_error`, `mountpoint_required`, `suggest_mountpoint`, `has_symbolic_remaining`, `build_table_rows`, `normalize_lsblk_mountpoint`, `on_disk_changed`, `format_checkbox_from`, `skip_format_from`; `multidisk_tests`, `table_scope_tests`, `free_space_tests`, `symbolic_capacity_tests`, `selection_tests`, `edit_flow_tests`, `edit_disk_tests`, `testutil` | | `src/jobs/users.rs` | `set_password_via_chpasswd` (stdin pipe) | @@ -843,59 +843,77 @@ senaryoları dene: --- -## 🛑 DOĞRULANMADI: `plan_disk_layout`'ın çekirdek varsayımı yanlış görünüyor +## ✅ ÇÖZÜLDÜ: `plan_disk_layout` gerçek kernel ile doğrulandı ve düzeltildi -**Durum: inceleniyor, düzeltme YAPILMADI.** 28 Eyl 2026'da canlı -ISO'ya SSH ile bağlanıp gerçek `parted` + kernel davranışı ölçüldü. +**28 Eyl 2026.** Bu bir P0 **veri kaybı** hatasıydı; canlı ISO'da +ölçüldü, düzeltildi ve düzeltme gerçek kernel ile doğrulandı. -### Deney (sdc, 8 GiB, tamamen boş — test için kullanıldı) +### ❌ Eski (YANLIŞ) varsayım + +> *"parted rm N sonrası kernel bölümleri başlangıç offset'ine göre +> sıralayıp 1'den numaralandırır."* + +Kod buna göre `survivors.sort_by_key(start)` yapıp her şeyi 1'den +numaralandırıyor, kaymış bölümleri `remapped` ile bildiriyordu. + +### ✅ Gerçek davranış (ölçüldü) + +**Kernel, bölümü başlangıç offset'ine göre DEĞİL, bölüm tablosu +slotuna göre adlandırır.** Bu hem `msdos` hem `gpt` için doğrulandı. + +**msdos ölçümü** (VM'de, reboot sonrası taze kernel okuması): ``` -1. mklabel msdos -2. mkpart 1-101 MiB · 101-301 MiB · 301-5001 MiB -3. rm 2 → 200 MiB boşluk -4. mkpart 5001-6001 MiB (1000 MiB) → boşluğa SIĞMAZ +mklabel msdos · 1-101 · 101-301 · 301-5001 · rm 2 · mkpart 5001-6001 MiB +→ /dev/sdc1 = 1 MiB (100 MiB) korunan + /dev/sdc2 = 5001 MiB (1000 MiB) YENİ + /dev/sdc3 = 301 MiB (4700 MiB) korunan — KAYMADI ``` -`parted` yeni bölümü **MBR slot 2**'ye yazdı. Kernel'in okuması: +**gpt ölçümü** (host'ta loop + `--partscan`): GPT giriş dizisi ile +LBA sırası **bilerek farklı** kuruldu (dizi 2 = 5001 MiB, dizi 3 = +301 MiB). Kernel yine dizi sırasını verdi → LBA'ya göre sıralamıyor. -``` -/dev/sdc1 start=1 MiB 100 MiB ← korunan -/dev/sdc2 start=5001 MiB 1000 MiB ← YENİ bölüm -/dev/sdc3 start=301 MiB 4700 MiB ← ESKİ bölüm (301 MiB'de AMA sdc3!) +### Düzeltme + +```rust +// hayatta kalanlar numaralarını KORUR +let mut taken: BTreeSet = survivors.iter().filter_map(|(_,_,o)| *o).collect(); +// her yeni bölüm en küçük BOŞ slotu alır +for p in planned.iter_mut() { + let num = (1..).map(|n| n as u32).find(|n| !taken.contains(n))...; +} ``` -`sdc3` **301 MiB**'de başlıyor ama 3 numarasını taşıyor; yeni bölüm -**5001 MiB**'de başlıyor ama 2 numarasını. **Kernel offset'e göre -sıralamıyor — MBR slot numarasını doğrudan aygıt adı yapıyor.** +`remapped` **tamamen kaldırıldı** — hayatta kalan bölüm artık kaymadığı +için hep boş kalıyordu; ölü durum bırakılmadı. Dönüş tipi +`Result, String>` oldu, FAZ B'deki döngü silindi. -### Bu neden kritik +### Uçtan uca doğrulama -`plan_disk_layout` (ve `AGENTS.md`'deki gerekçesi) varsayımı: -*"parted rm N sonrası kernel bölümleri başlangıç offset'ine göre -sıralayıp 1'den numaralandırır."* +Kodun tahmini ile gerçek kernel **birebir** örtüştü (2 yeni bölüm + +1 silme, `slot 2` ve `slot 4` — slot 3'ü hayatta kalan bölüm tutuyor): -Gerçekte (MBR'da) öyle değil. O halde `plan_disk_layout` bu senaryoda -`/dev/sdc3` öngörüyor; **gerçekte yeni bölüm `/dev/sdc2`**. `device_map` -yanlış aygıtı gösterir → FAZ C **4700 MiB'lik KORUNAN bölümü** mkfs -eder. Tam olarak `plan_disk_layout`'ın *önlemek için* yazıldığı -veri kaybı. +``` +kernel: p1=1MiB/100 p2=5001MiB/300 p3=301MiB/4700 p4=5301MiB/1000 +kod : sdc2 5001-5301 sdc4 5301-6301 +``` -### Doğrulanmadan önce çözülecek iki soru +### Bu neden önemliydi -1. **GPT davranışı ölçülmedi.** `sda` ve `sdb` **GPT**, ölçüm - **msdos** yapıldı. GPT'de kernel `efi_partition` diziyi LBA'ya göre - sıralıyor olabilir → iki tablo tipi farklı davranabilir. Ayırt edici - test hazırlandı ama tamamlanmadı: GPT giriş dizisi ile LBA sırası - farklı bir imaj (`/tmp/opencode/gpttest.img`) VM'e takılıp - kernel'in adlandırması okunacak. -2. **Kernel tablosu taze miydi?** `partx -a` her çağrıda "error" - döndürdü ama tabloyu yine de güncelledi. `reboot` ve ACPI güç - düğmesi yetki yokluğu nedeniyle **çalışmadı** (`/proc/uptime` - değişmedi) → tazeden okuma ile doğrulanamadı. Bu VM'de - `BLKRRPART` izni yok. +Eski kod `device_map`'i **ters** yazıyordu: yeni bölüme `sdX3`, +korunan bölüme `sdX2` diyordu. FAZ C `mkfs`'i **korunması gereken +4700 MiB'lik bölüme** uygulardı — tam olarak `plan_disk_layout`'ın +*önlemek için* yazıldığı veri kaybı. -> ⚠️ Bu blok düzeltme DEĞİL, uyarıdır. `plan_disk_layout`'a -> dokunmadan önce yukarıdaki iki soru cevaplanmalı; aksi hâlde -> mevcut davranış ile "düzeltilmiş" davranış arasında hangisinin -> doğru olduğunu bilemeyiz. +> 💡 **Bu, `assign_partition_numbers`'ın "en küçük boş slot" kuralının +> zaten DOĞRU olduğu anlamına gelir.** `plan_disk_layout`'ın bütün +> varlık sebebi (offset'e göre yeniden numaralandırma) gerçekte +> gerekli değilmiş. Yeni bölümlere verilen konum (first-fit / +> en büyük boş bölge) hâlâ gerekli ve kaldı; **yalnızca numaralandırma +> basitleştirildi.** +> +> Ölçüm, tahminin yerine geçmiştir. Ders: "kernel şunu yapar" diye +> inanılan her kural **ölçülmelidir** — bu proje tam da o yüzden +> `plan_disk_layout`'a 11 test yazmış ama testler de aynı yanlış +> varsayımı paylaşıyordu. diff --git a/src/jobs/partition.rs b/src/jobs/partition.rs index 710923f..44e8891 100644 --- a/src/jobs/partition.rs +++ b/src/jobs/partition.rs @@ -77,7 +77,7 @@ pub fn plan_disk_layout( physical: &[PhysicalExtent], destroyed: &[u32], creates: &[(usize, u64)], -) -> Result<(Vec, Vec<(u32, String)>), String> { +) -> Result, String> { // 1. Silinmeyen fiziksel bölümler let mut survivors: Vec<(u64, u64, Option)> = physical .iter() @@ -145,37 +145,53 @@ pub fn plan_disk_layout( survivors.push((start_mb, end_mb, None)); } - // 3. Başlangıç offset'ine göre sırala → kernel numaralarını dağıt. - // `parted` bölüm tablosunu bu sırayla gösterir; kernel de aynı - // sırayı kullanır. - survivors.sort_by_key(|(s, _, _)| *s); - + // 3. ⭐ Bölüm NUMARALARI: tablo slotuna göre dağıt. + // + // ÖNCEKİ YANLIŞ YAKLAŞIM: "başlangıç offset'ine göre sırala, + // 1'den numaralandır". Canlı ISO'da ölçüldü ve YANLIŞ çıktı + // (aşağıya). Şimdi: hayatta kalan fiziksel bölümler KENDİ + // numaralarını KORUR, her yeni bölüm EN KÜÇÜK BOŞ SLOTU alır. + // + // Ölçüm (28 Eyl 2026, gerçek parted + kernel): + // + // msdos: rm 2 sonra mkpart 5001-6001MiB + // parted MBR slot 2'ye yazdı + // kernel: /dev/sdc1=1MiB /dev/sdc2=5001MiB(YENİ) /dev/sdc3=301MiB + // + // gpt: aynı işlem, GPT giriş dizisi = 1(1MiB) 2(5001MiB) 3(301MiB) + // kernel: loop0p1=1MiB loop0p2=5001MiB(YENİ) loop0p3=301MiB + // + // İkisinde de kernel bölümü **başlangıç offset'ine göre + // sıralamıyor**; **tablo slotunu doğrudan aygıt numarası** + // yapıyor. Yani 301 MiB'de başlayan bölüm `3` kalıyor, + // 5001 MiB'de başlayan yeni bölüm `2` oluyor. + // + // Bu, UI'ın "en küçük boş slot" mantığının zaten DOĞRU + // olduğu anlamına gelir; `assign_partition_numbers`'a + // güvenmek yerine burada da aynı kural uygulanır. let mut planned_by_index: HashMap = HashMap::new(); - let mut remapped: Vec<(u32, String)> = Vec::new(); - for (idx, (start, _, orig)) in survivors.iter().enumerate() { - let num = (idx + 1) as u32; - let device = part_path(disk, num); - match orig { - // Fiziksel bölüm: yeni numarasını öğren - Some(old) => { - if *old != num { - remapped.push((*old, device.clone())); - } - } - // Yeni bölüm: hangi tanım olduğunu eşle - None => { - // Bu aralık hangi planlanmış bölüme ait? - if let Some(p) = planned - .iter_mut() - .find(|p| p.start_mb == *start && p.num == 0) - { - p.num = num; - p.device = device.clone(); - planned_by_index.insert(p.part_index, num); - } - } - } + // Hayatta kalan fiziksel bölümler numaralarını korur; onların + // slotu "dolu" sayılır. + let mut taken: std::collections::BTreeSet = survivors + .iter() + .filter_map(|(_, _, orig)| *orig) + .collect(); + + // Yeni bölümlere sırayla en küçük boş slotu ver. `planned` zaten + // oluşturma sırasındadır; `parted` de her `mkpart`'ı en küçük + // boş slota yazar. + for p in planned.iter_mut() { + let num = (1..) + .map(|n| n as u32) + .find(|n| !taken.contains(n)) + .ok_or_else(|| { + format!("{}: bölüm tablosunda boş slot kalmadı", disk) + })?; + taken.insert(num); + p.num = num; + p.device = part_path(disk, num); + planned_by_index.insert(p.part_index, num); } // Eşleşmeyen planlanmış bölüm kalmamalı @@ -185,7 +201,7 @@ pub fn plan_disk_layout( "{}: planlanan bölüm sayısı ile eşleşen sayı uyuşmuyor", disk ); - Ok((planned, remapped)) + Ok(planned) } /// Başlangıç offset'ine göre sıralı, dolu aralıklardan boş bölgeleri üretir. @@ -862,19 +878,9 @@ impl Job for CustomPartitionJob { .map(|&i| (i, creates[i].size_mb)) .collect(); - let (planned, remapped) = + let planned = plan_disk_layout(disk, total_mb, &physical, &destroys, &create_specs)?; - // Silmeden sonra kaymış fiziksel bölümler: FAZ C bunları - // UI'ın numarasıyla değil, GERÇEK yollarıyla işleyecek. - for (old_num, new_dev) in &remapped { - ui.log(format!( - "{}: {} → {} (silinen bölüm nedeniyle numara kaydı)", - disk, part_path(disk, *old_num), new_dev - )); - device_map.insert(part_path(disk, *old_num), new_dev.clone()); - } - for plan in &planned { let part = creates[plan.part_index]; // Yeni bölüm: UI'ın tahmin ettiği yol → gerçek yol. @@ -1295,119 +1301,101 @@ mod layout_tests { /// ``` /// /// 2. bölüm silinmiş. Boşluk 200 MiB — 1000 MiB'lik yeni bölüm - /// **buraya SIĞMAZ**. Kod konumu kendisi hesapladığı için - /// diskin sonuna (5001 MiB) yazması ve 3. bölümün 2'ye kayması - /// gerekir. + /// **buraya SIĞMAZ** → diskin sonuna (5001 MiB) yazılır. + /// + /// Ölçülen kernel sonucu: + /// /dev/sdc1 = 1 MiB (100 MiB) ← korunan + /// /dev/sdc2 = 5001 MiB (1000 MiB) ← YENİ + /// /dev/sdc3 = 301 MiB (4700 MiB) ← korunan, KAYMADI #[test] fn live_verified_sdc_oversized_new_partition() { let physical = vec![phys(1, 1, 101), phys(3, 301, 5001)]; let destroyed = vec![2u32]; let creates = vec![(0usize, 1000u64)]; - let (planned, remapped) = + let planned = plan_disk_layout("/dev/sdc", 8192, &physical, &destroyed, &creates) .expect("planlanmalı"); assert_eq!(planned.len(), 1, "tek yeni bölüm planlanmalı"); let p = &planned[0]; println!( - "PLAN: {}MiB-{}MiB slot {} ({}), kayma {:?}", - p.start_mb, p.end_mb, p.num, p.device, remapped + "PLAN: {}MiB-{}MiB slot {} ({})", + p.start_mb, p.end_mb, p.num, p.device ); // 200 MiB'lik boşluğa SIĞMAZ → disk sonuna yazılmalı - assert!( - p.start_mb >= 5001, - "boşluğa sığmayan bölüm disk sonuna konmalı, {}MiB yazıldı", - p.start_mb - ); + assert_eq!(p.start_mb, 5001, "yeni bölüm diskin sonuna konmalı"); assert_eq!(p.end_mb - p.start_mb, 1000, "istenen boyut korunmalı"); - // Eski 3 numaralı bölüm 2'ye KAYMALI - assert!( - remapped.iter().any(|(old, new)| *old == 3 && new.contains("sdc2")), - "eski slot 3, sdc2'ye kaymalıydı; kayma listesi: {:?}", - remapped - ); + + // ⭐ ÖLÇÜLEN GERÇEK: yeni bölüm boşaltılan slot 2'yi alır, + // 301 MiB'de başlayan korunan bölüm 3'te KALIR. + assert_eq!(p.num, 2, "yeni bölüm serbest kalan slot 2'yi almalı"); + assert_eq!(p.device, "/dev/sdc2"); } - /// REGRESYON — asıl bulgu. + /// ⭐ REGRESYON — canlı ölçümle düzeltildi. /// - /// `assign_partition_numbers` UI'da yeni bölüme "en küçük boş slot"u - /// (= 2) veriyor. Ancak `parted rm 2` sonrası serbest kalan boşluk - /// yalnızca 100 MB; 5000 MB'lık istenen bölüm oraya SIĞMAZ ve - /// first-fit diskin SONUNA kayar. parted bölümleri başlangıç - /// offset'ine göre sıraladığı için kernel'de yeni bölüm 3 olur. + /// ÖNCE (YANLIŞ): kod bölümleri başlangıç offset'ine göre + /// sıralayıp 1'den numaralandırıyordu; yeni bölüm 3, eski 3 → 2 + /// "kaydı" diye raporlanıyordu. /// - /// UI "sda2 = yeni" derken diskte "sda3 = yeni" olurdu; FAZ C - /// eski sda3'ü (artık sda2) biçimlendirirdi → VERİ KAYBI. + /// GERÇEK (ölçüldü, msdos VE gpt): kernel tablo slotunu doğrudan + /// aygıt numarası yapıyor. Silinen slot boşalınca yeni bölüm + /// onu alır, hayatta kalanlar KENDİ numaralarında kalır. #[test] - fn delete_plus_create_renumbers_physical_partitions() { + fn delete_plus_create_keeps_physical_partition_numbers() { let physical = vec![ phys(1, 1, 100), // korunuyor phys(2, 100, 200), // SİLİNECEK phys(3, 200, 30_200), // korunuyor ]; - // 5000 MB'lik yeni bölüm let creates = vec![(0usize, 5_000u64)]; - let (planned, remapped) = + let planned = plan_disk_layout("/dev/sda", 40_000, &physical, &[2], &creates).expect("planlanmalı"); - // Yeni bölüm 100–200 boşluğuna SIĞMAZ → diskin sonuna (30200+) gider + // Konum: 100–200 boşluğuna SIĞMAZ → diskin sonuna assert_eq!( planned[0].start_mb, 30_200, "boşluk 100 MB olduğu için yeni bölüm diskin sonuna yerleşmeli" ); - - // Başlangıç offset'ine göre sıra: sda1(1), sda2(200), sda3(30200) - assert_eq!(planned[0].num, 3, "yeni bölümün gerçek numarası 3 olmalı"); - assert_eq!(planned[0].device, "/dev/sda3"); - - // Eski sda3 artık sda2 — FAZ C bunu bilmeli - assert_eq!( - remapped, vec![(3, "/dev/sda2".to_string())], - "silinen orta bölüm, sonrakileri bir kaydırmalı" - ); + // ⭐ Numara: boşaltılan slot 2 → yeni bölüm 2 olur + assert_eq!(planned[0].num, 2, "yeni bölüm boşaltılan slotu almalı"); + assert_eq!(planned[0].device, "/dev/sda2"); } - /// Sadece silme, yeni bölüm yok: hayatta kalanlar kayar. + /// Sadece silme, yeni bölüm yok: hayatta kalanlar KAYMAZ. + /// + /// Önceden "sda3 → sda2" kayması bekleniyordu; ölçümde olmadı. #[test] - fn deleting_middle_shifts_later_partitions_down() { - let physical = vec![ - phys(1, 1, 100), - phys(2, 100, 200), - phys(3, 200, 300), - ]; - let (planned, remapped) = + fn deleting_a_partition_makes_others_slide() { + let physical = vec![phys(1, 1, 100), phys(2, 100, 200), phys(3, 200, 300)]; + let planned = plan_disk_layout("/dev/sda", 40_000, &physical, &[2], &[]).expect("planlanmalı"); - assert!(planned.is_empty(), "yeni bölüm yok"); - // sda1→sda1, sda3→sda2 - assert_eq!(remapped, vec![(3, "/dev/sda2".to_string())]); } - /// Silinen bölüm sonuncusa hiçbir şey kaymaz. + /// Silinen bölüm sonuncusa da durum aynı: kayma yok. #[test] - fn deleting_last_partition_shifts_nothing() { + fn deleting_last_partition_changes_nothing() { let physical = vec![phys(1, 1, 100), phys(2, 100, 200)]; - let (_, remapped) = + let planned = plan_disk_layout("/dev/sda", 40_000, &physical, &[2], &[]).expect("planlanmalı"); - assert!(remapped.is_empty(), "son bölüm silinince kayma olmaz"); + assert!(planned.is_empty()); } /// Basit durum: silme yok, yeni bölüm diskin sonuna. + /// Numara = mevcut en yüksek numara + 1 (yani 3). #[test] fn simple_create_after_existing_partitions() { let physical = vec![phys(1, 1, 100), phys(2, 100, 30_100)]; - // Disk 50_000 MB: son bölüm 30_100'de bitiyor, 19_899 MB boş var. - let (planned, remapped) = - plan_disk_layout("/dev/sda", 50_000, &physical, &[], &[(0, 10_000)]) - .expect("planlanmalı"); + let planned = plan_disk_layout("/dev/sda", 50_000, &physical, &[], &[(0, 10_000)]) + .expect("planlanmalı"); assert_eq!(planned[0].start_mb, 30_100); - assert_eq!(planned[0].num, 3); + assert_eq!(planned[0].num, 3, "dolu slotlar 1,2 → yeni bölüm 3"); assert_eq!(planned[0].device, "/dev/sda3"); - assert!(remapped.is_empty(), "fiziksel bölümler kaymamalı"); } /// "Kalan alan" (sembolik) en büyük bölgeye gider. @@ -1415,7 +1403,7 @@ mod layout_tests { fn symbolic_takes_the_largest_region() { // İki boş bölge: 200–300 (küçük) ve 1000–20000 (büyük) let physical = vec![phys(1, 1, 200), phys(2, 300, 1000)]; - let (planned, _) = plan_disk_layout("/dev/sda", 20_000, &physical, &[], &[(0, 0)]) + let planned = plan_disk_layout("/dev/sda", 20_000, &physical, &[], &[(0, 0)]) .expect("planlanmalı"); assert_eq!(planned[0].start_mb, 1000, "en büyük bölge seçilmeli"); assert!(planned[0].symbolic); @@ -1427,7 +1415,7 @@ mod layout_tests { fn multiple_creates_get_increasing_slots() { let physical = vec![phys(1, 1, 100)]; let creates = vec![(0usize, 2_000u64), (1usize, 3_000u64), (2usize, 0u64)]; - let (planned, _) = plan_disk_layout("/dev/sda", 40_000, &physical, &[], &creates) + let planned = plan_disk_layout("/dev/sda", 40_000, &physical, &[], &creates) .expect("planlanmalı"); assert_eq!(planned.len(), 3); @@ -1465,16 +1453,13 @@ mod layout_tests { let sda = vec![phys(1, 1, 100), phys(2, 100, 30_100)]; let sdb = vec![phys(1, 1, 100)]; - let (p_sda, r_sda) = - plan_disk_layout("/dev/sda", 40_000, &sda, &[], &[(0, 1_000)]).expect("sda"); - let (p_sdb, r_sdb) = - plan_disk_layout("/dev/sdb", 40_000, &sdb, &[], &[(0, 2_000)]).expect("sdb"); + let p_sda = plan_disk_layout("/dev/sda", 40_000, &sda, &[], &[(0, 1_000)]).expect("sda"); + let p_sdb = plan_disk_layout("/dev/sdb", 40_000, &sdb, &[], &[(0, 2_000)]).expect("sdb"); // sda'da 2 fiziksel bölüm var → yeni 3 olur; sdb'de 1 var → yeni 2. // Kritik olan ikisinin de AYRI kalması: sda1 ile sdb1 çakışmamalı. assert_eq!(p_sda[0].device, "/dev/sda3"); assert_eq!(p_sdb[0].device, "/dev/sdb2"); - assert!(r_sda.is_empty() && r_sdb.is_empty()); } /// EN KRİTİK ÇOKLU DİSK SENARYOSU: kök sda'da, /home sdb'de. @@ -1483,28 +1468,24 @@ mod layout_tests { fn deleting_on_one_disk_does_not_renumber_another() { // sda: sda1 korunuyor, sda2 siliniyor, yeni 5000 MB let sda = vec![phys(1, 1, 100), phys(2, 100, 200)]; - let (p_sda, r_sda) = - plan_disk_layout("/dev/sda", 40_000, &sda, &[2], &[(0, 5_000)]).expect("sda"); + let p_sda = plan_disk_layout("/dev/sda", 40_000, &sda, &[2], &[(0, 5_000)]).expect("sda"); // sdb: bağımsız — kendi fiziksel bölümü let sdb = vec![phys(1, 1, 20_000)]; - let (p_sdb, r_sdb) = - plan_disk_layout("/dev/sdb", 40_000, &sdb, &[], &[(0, 1_000)]).expect("sdb"); + let p_sdb = plan_disk_layout("/dev/sdb", 40_000, &sdb, &[], &[(0, 1_000)]).expect("sdb"); // sda'da 200–40000 boş: yeni bölüm oraya gider, numarası 2 assert_eq!(p_sda[0].device, "/dev/sda2"); - assert!(r_sda.is_empty(), "sda1 zaten 1, kayma yok"); // sdb HİÇ ETKİLENMEMELİ assert_eq!(p_sdb[0].device, "/dev/sdb2"); - assert!(r_sdb.is_empty(), "sdb numaraları sda'dan bağımsız"); } /// NVMe/MMC adlandırması: sda1 değil sda1p1 değil — part_path'e bırakıldı. #[test] fn nvme_slots_use_p_separator() { let physical = vec![phys(1, 1, 100)]; - let (planned, _) = plan_disk_layout("/dev/nvme0n1", 10_000, &physical, &[], &[(0, 500)]) + let planned = plan_disk_layout("/dev/nvme0n1", 10_000, &physical, &[], &[(0, 500)]) .expect("planlanmalı"); assert_eq!(planned[0].device, "/dev/nvme0n1p2"); }