fix(partition): lsblk'in "[SWAP]" biçimi mountpoint'e sızıyordu

Canlı ISO'ya SSH ile bağlanıp GERÇEK `lsblk` çıktısı okunarak
bulundu — tahminle değil:

    $ lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT -b --noheadings -l /dev/sdb
    sdb1    524288000 vfat /boot/efi
    sdb2   1572864000 swap [SWAP]        ← köşeli parantezli
    sdb3  25168969728 ext4 /

`read_existing_partitions` mountpoint'i doğrudan alıyordu, yani
`CustomPartition::mountpoint = "[SWAP]"` oluyordu.

## İki yere sızıyordu

1. **UI** — tablonun bağlama noktası sütununda `[SWAP]` görünüyordu
2. **MÜKERRER KONTROLÜ** — iki swap'ı olan makinede (Windows diski +
   Linux diski = çift önyüklemeli kurulumun tipik hali) ikisi de aynı
   `"[SWAP]"` değerini taşıyor ve kullanıcı VAR OLMAYAN bir "mükerrer
   bağlama noktası" hatası görüyordu

Bu makinede tek swap olduğu için sorun görünmüyordu. Tek swap'lı
kurulumda hata hiç ortaya çıkmaz — kolayca kaçar.

## Düzeltme

- `normalize_lsblk_mountpoint(raw, fstype)`: `[SWAP]` → `"swap"`.
  Yalnızca `FsType::Swap` için; diğer dosya sistemlerinde köşeli
  parantezli tuhaf bir çıktı gelirse DOKUNULMAZ (sessizce yutmak,
  özgün hatayı gizlemekten daha kötüdür).
- Mükerrer kontrolü artık yalnızca **gerçekten bağlanan** dosya
  sistemlerini karşılaştırır: `Swap`/`Lvm`/`LvmLv` hariç.
  `mountpoint_required` ile aynı gerekçe — bağlama noktası
  semantiği olmayan hiçbir şey karşılaştırılmaz.

## fstab etkilenmedi

Satır `part.mountpoint`'e değil `part.fstype`'e göre yazılıyor
(`FsType::Swap => ("none", "sw", "0", "0")`). Doğrulandı: kurucunun
yazdığı /etc/fstab'ta swap satırı `none`. Yani hata fstab'da değil,
mükerrer kontrolünde görünüyordu.

## Testler (145 → 150)

- `[SWAP]` → `swap` normalleşir
- mount edilmemiş swap (sütun boş) de `swap` olur
- diğer dosya sistemleri değiştirilmez, `[weird]` de dokunulmaz
- ⭐ iki swap mükerrer SAYILMAZ (gerçek lsblk biçimiyle)
- iki gerçek `/` hâlâ mükerrer sayılır (filtre körleştirmiyor)
- iki LVM PV mükerrer sayılmaz

Bir test ilk yazımda kök bölümü içermiyordu ("No root partition"
hattı) — beklenti düzeltildi.

cargo check --all-targets : 0 hata, 0 uyarı
cargo test              : 150/150

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erkan IŞIK
2026-09-28 00:19:15 +03:00
parent 4f12334dd6
commit 03cc46098c
2 changed files with 206 additions and 9 deletions
+36 -3
View File
@@ -531,6 +531,39 @@ Kullanıcı tanımı (sda2) → "Biçimlendir" ☑ SEÇİLİ → biçi
`mp_skip_format` çeviri anahtarı kullanılmaz hale geldiği için iki
locale'den de **silindi** (ölü anahtar bırakılmadı).
### ⭐ `lsblk` AKTİF swap için `[SWAP]` yazar
**Bu, canlı ISO'ya SSH ile bağlanıp GERÇEK çıktı okunarak bulundu.**
Tahminle değil:
```
$ lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT -b --noheadings -l /dev/sdb
sdb1 524288000 vfat /boot/efi
sdb2 1572864000 swap [SWAP] ← köşeli parantezli!
sdb3 25168969728 ext4 /
```
`read_existing_partitions` mountpoint'i doğrudan alıyordu, yani
`CustomPartition::mountpoint = "[SWAP]"` oluyordu. İki yere sızıyordu:
1. **UI** — tablonun bağlama noktası sütununda `[SWAP]` görünüyordu
2. **mükerrer kontrolü** — iki swap'ı olan makinede (Windows diski +
Linux diski = çift önyüklemeli kurulumun tipik hali) ikisi de aynı
`"[SWAP]"` değerini taşıyor ve kullanıcı **var olmayan** bir
"mükerrer bağlama noktası" hatası görüyordu
Bu makinede tek swap olduğu için sorun görünmüyordu — **kolayca kaçar.**
| Düzeltme | Yer |
|---|---|
| `normalize_lsblk_mountpoint(raw, fstype)` | `[SWAP]` → `"swap"`. Yalnızca `FsType::Swap` için; diğerleri **dokunulmaz** (sessizce yutmak yok) |
| mükerrer kontrolü | yalnızca **gerçekten bağlanan** dosya sistemleri karşılaştırılır: `Swap`/`Lvm`/`LvmLv` hariç. `mountpoint_required` ile aynı gerekçe |
> `fstab` işi zaten doğruydu: satırı `part.mountpoint`'e değil
> `part.fstype`'e göre yazıyor (`FsType::Swap => ("none", "sw", …)`).
> Yani mountpoint `"swap"` olması fstab'ı bozmaz — hatta bozmadığı için
> hata fstab'da değil, **mükerrer kontrolünde** görünmüyordu.
### `selected_disk` hâlâ 3 yerde kullanılıyor
1. Tablo hangi diski gösteriyor + `manual_dual_bars`
2. Diyaloğun varsayılan hedefi
@@ -691,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 : 145/145 (test modülü sayısı arttıkça güncel tut)
cargo test : 150/150 (test modülü sayısı arttıkça güncel tut)
cargo clippy : ~77 uyarı, tamamı stil (collapsed_if, replace zinciri)
```
Bu oturumda 1 → 145 test. Test modülü olmayan dosyalar (`users.rs`,
Bu oturumda 1 → 150 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()`).
@@ -796,7 +829,7 @@ senaryoları dene:
|---|---|
| `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` | **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`, `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/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) |
| `src/jobs/bootloader.rs` | `CRYPTROOT_NAME`, `luks_backing_device`, `mounted_root_device` |
| `src/jobs/finish.rs` | `RunComarJob` geri geldi; `ldconfig`/`update-environment`/depo fatal |
+170 -6
View File
@@ -63,8 +63,22 @@ fn read_existing_partitions(disk_path: &str) -> Vec<crate::installer::CustomPart
_ => FsType::Ext4, // bilinmeyen → varsayılan
};
// lsblk mount point alanı boşsa sonraki token gelmez; unwrap_or ile güvenli al
let mountpoint = tokens.next().unwrap_or("").to_string();
// lsblk mount point alanı boşsa sonraki token gelmez;
// unwrap_or ile güvenli al.
//
// ⚠️ AKTİF swap için lsblk mountpoint sütununa `[SWAP]`
// YAZAR (canlı ISO'dan doğrulandı):
//
// sdb2 1572864000 swap [SWAP]
//
// Bu ham biçim UI'a sızıyordu: tablonun "bağlama noktası"
// sütununda `[SWAP]` görünüyordu ve mükerrer kontrolünde
// iki swap'ın çakışmasına yol açıyordu. Arayüzün kullandığı
// kanonik karşılığa çevriliyor.
let mountpoint = normalize_lsblk_mountpoint(
tokens.next().unwrap_or(""),
fstype.clone(),
);
parts.push(CustomPartition {
disk: disk_path.to_string(),
@@ -86,6 +100,34 @@ fn read_existing_partitions(disk_path: &str) -> Vec<crate::installer::CustomPart
parts
}
/// `lsblk` MOUNTPOINT sütununu arayüzün kullandığı biçime çevirir.
///
/// ⚠️ AKTİF swap için lsblk mountpoint sütununa **`[SWAP]`** yazar
/// (canlı ISO'dan doğrulandı):
///
/// sdb2 1572864000 swap [SWAP]
///
/// Ham biçim iki yere sızıyordu:
/// 1. tablonun "bağlama noktası" sütununda `[SWAP]` görünüyordu
/// 2. mükerrer kontrolünde iki swap çakışma üretiyordu
///
/// Swap kanonik karşılığı `"swap"`'tir (`COMMON_MOUNTPOINTS` ve fstab
/// işi `FsType::Swap`'e bakar, mountpoint'e değil).
fn normalize_lsblk_mountpoint(raw: &str, fstype: FsType) -> String {
// Başka türlerde köşeli parantezli değerler görürsek dokunma:
// nadir/normalleştirilmemiş bir çıktı olabilir, sessizce yutmayız.
match fstype {
FsType::Swap => {
if raw.eq_ignore_ascii_case("[SWAP]") || raw.is_empty() {
"swap".to_string()
} else {
raw.to_string()
}
}
_ => raw.to_string(),
}
}
/// `/sys/block` altındaki gerçek blok aygıtlarını (partition değil) listeler.
/// Aygıt adına (`sda`, `nvme0n1`) bakarak türünü belirler.
fn detect_drive_type(name: &str) -> String {
@@ -534,13 +576,33 @@ fn validate_manual_on(
// sda1'i neredeyse her zaman boş gelir. Boş değerler karşılaştırılırsa
// "N tane atanmamış bölüm" kendi kendine mükerrer sayılır ve kullanıcı
// var olmayan bir çakışma hatası görürdü.
let mountpoints: std::collections::HashSet<&str> = live_parts
// ⚠️ Yalnızca GERÇEKTEN bağlanan dosya sistemleri karşılaştırılır.
//
// REGRESYON: swap ve LVM satırları da sayılıyordu. `lsblk` AKTİF
// swap için mountpoint sütununa **`[SWAP]`** yazar (canlı ISO'dan
// doğrulandı):
//
// sdb2 1572864000 swap [SWAP]
//
// Bu bir bağlama noktası DEĞİLDİR. İki swap bölümü olan bir
// makinede (Windows'un diski + Linux diski — çift önyüklemeli
// kurulumun tipik hali) ikisi de aynı `"[SWAP]"` değerini alır ve
// kullanıcı var olmayan bir "mükerrer bağlama noktası" hatası
// görür. Tek swap olan makinelerde sorun görünmez, bu yüzden
// kolayca kaçar.
//
// LVM PV'nin de bağlama noktası yoktur (mount edilmez, fstab'a
// girmez) — `mountpoint_required` ile aynı gerekçe.
let is_mountable = |p: &&CustomPartition| {
!p.mountpoint.is_empty()
&& !matches!(p.fstype, FsType::Swap | FsType::Lvm | FsType::LvmLv)
};
let mountable: std::collections::HashSet<&str> = live_parts
.iter()
.filter(|p| is_mountable(p))
.map(|p| p.mountpoint.as_str())
.filter(|m| !m.is_empty())
.collect();
let assigned = live_parts.iter().filter(|p| !p.mountpoint.is_empty()).count();
if mountpoints.len() != assigned {
if mountable.len() != live_parts.iter().filter(|p| is_mountable(p)).count() {
return Some(t!("mp_duplicate_mountpoint").to_string());
}
@@ -3583,6 +3645,108 @@ mod multidisk_tests {
// ── REGRESYON: mount EDİLMEMİŞ fiziksel bölüm ──────────────
/// ⭐ `lsblk`in `[SWAP]` biçimi kanonik karşılığa çevrilmeli.
///
/// Gerçek canlı ISO çıktısı:
/// sdb2 1572864000 swap [SWAP]
#[test]
fn lsblk_swap_marker_is_normalized() {
use super::normalize_lsblk_mountpoint;
assert_eq!(
normalize_lsblk_mountpoint("[SWAP]", FsType::Swap),
"swap",
"lsblk'in [SWAP] biçimi 'swap' olmalı"
);
// Swap mount edilmemişse sütun boş gelir; yine de swap'tır
assert_eq!(
normalize_lsblk_mountpoint("", FsType::Swap),
"swap",
"mount edilmemiş swap de 'swap' olmalı"
);
}
/// Diğer dosya sistemleri DEĞİŞTİRİLMEZ — özellikle köşeli
/// parantezli tuhaf bir çıktı varsa sessizce yutmak yerine
/// olduğu gibi bırakılır.
#[test]
fn non_swap_mountpoints_are_untouched() {
use super::normalize_lsblk_mountpoint;
assert_eq!(normalize_lsblk_mountpoint("", FsType::Ext4), "");
assert_eq!(normalize_lsblk_mountpoint("/", FsType::Ext4), "/");
assert_eq!(
normalize_lsblk_mountpoint("/boot/efi", FsType::Fat32),
"/boot/efi"
);
assert_eq!(
normalize_lsblk_mountpoint("[weird]", FsType::Ext4),
"[weird]",
"swap dışında köşeli parantez normalleştirilmemeli"
);
}
/// ⭐ lsblk'in `[SWAP]` biçimi mountpoint'e SIZMASIN.
///
/// Canlı ISO'dan alınan GERÇEK çıktı:
///
/// sdb2 1572864000 swap [SWAP]
///
/// Ham biçim UI'da görünüyor ve mükerrer kontrolüne giriyordu.
/// İki swap'ı olan bir makinede (Windows diski + Linux diski —
/// çift önyüklemeli kurulumun tipik hali) ikisi de `"[SWAP]"`
/// olduğu için "mükerrer bağlama noktası" hatası veriyordu.
#[test]
fn two_swap_partitions_are_not_duplicate_mountpoints() {
let disks = vec![disk("/dev/sda", 250), disk("/dev/sdb", 26)];
let parts = vec![
p("/dev/sda", "/dev/sda1", 500, FsType::Ntfs, ""),
p("/dev/sdb", "/dev/sdb1", 512, FsType::Fat32, "/boot/efi"),
// Windows'un swap'ı — lsblk bunlara "[SWAP]" yazar
p("/dev/sda", "/dev/sda2", 2_000, FsType::Swap, "[SWAP]"),
// Linux'un swap'ı
p("/dev/sdb", "/dev/sdb2", 1_500, FsType::Swap, "[SWAP]"),
p("/dev/sdb", "/dev/sdb3", 24_000, FsType::Ext4, "/"),
];
assert_eq!(
validate_manual_on(&parts, &[], true, &disks),
None,
"iki swap mükerrer sayılmamalı: {:?}",
validate_manual_on(&parts, &[], true, &disks)
);
}
/// İki gerçek mountpoint hâlâ mükerrer sayılmalı — filtre körleştirmemeli.
#[test]
fn two_real_root_mountpoints_are_still_duplicates() {
let disks = vec![disk("/dev/sda", 250), disk("/dev/sdb", 26)];
let parts = vec![
p("/dev/sda", "/dev/sda3", 10_000, FsType::Ext4, "/"),
p("/dev/sdb", "/dev/sdb3", 24_000, FsType::Ext4, "/"),
];
assert!(
validate_manual_on(&parts, &[], false, &disks).is_some(),
"iki '/' hâlâ geçersiz olmalı"
);
}
/// LVM PV'ler de mountpoint'siz sayılmalı — iki PV aynı boş
/// değeri taşısa bile çakışma üretmemeli.
#[test]
fn two_lvm_pvs_are_not_duplicate_mountpoints() {
let disks = vec![disk("/dev/sda", 250), disk("/dev/sdb", 26)];
let mut pv1 = p("/dev/sda", "/dev/sda2", 10_000, FsType::Lvm, "");
pv1.orig_device = String::new();
let mut pv2 = p("/dev/sdb", "/dev/sdb2", 10_000, FsType::Lvm, "");
pv2.orig_device = String::new();
// doğrulama bir kök ister
let parts = vec![p("/dev/sdb", "/dev/sdb3", 20_000, FsType::Ext4, "/"), pv1, pv2];
assert_eq!(
validate_manual_on(&parts, &[], false, &disks),
None,
"iki PV mükerrer sayılmamalı: {:?}",
validate_manual_on(&parts, &[], false, &disks)
);
}
/// Canlı ISO'da mount edilmemiş fiziksel bölüm (mountpoint boş)
/// düzeni GEÇERSİZ kılmamalı.
///