forked from pisilinux-rs/yali-rs
fix(partition): P0 veri kaybı — bölüm numaralandırması yanlış modellemiş
## 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<u32> = 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<Vec<PlannedCreate>, 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) <noreply@anthropic.com>
This commit is contained in:
@@ -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<u32> = 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<Vec<PlannedCreate>, 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.
|
||||
|
||||
+97
-116
@@ -77,7 +77,7 @@ pub fn plan_disk_layout(
|
||||
physical: &[PhysicalExtent],
|
||||
destroyed: &[u32],
|
||||
creates: &[(usize, u64)],
|
||||
) -> Result<(Vec<PlannedCreate>, Vec<(u32, String)>), String> {
|
||||
) -> Result<Vec<PlannedCreate>, String> {
|
||||
// 1. Silinmeyen fiziksel bölümler
|
||||
let mut survivors: Vec<(u64, u64, Option<u32>)> = 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<usize, u32> = 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<u32> = 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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user