From 948e1d548d0bb9761f11b3b58706034829969d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkan=20I=C5=9EIK?= Date: Sun, 27 Sep 2026 16:10:29 +0300 Subject: [PATCH] =?UTF-8?q?fix(partition):=20"yaln=C4=B1zca=20GRUB=20i?= =?UTF-8?q?=C3=A7in"=20b=C3=B6l=C3=BCm=20mountpoint'siz=20kaydedilemiyordu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kullanıcı raporu: "sda1 grub eklenecek o yüzden herhangi bir mount olayı yok ve yine bağlama noktası boş olamaz uyarısı alıyorum." Önceki commit yalnızca `validate_manual_on`'i düzeltmişti. Kaydetme DİYALOĞUNDA ikinci bir kural vardı ve koşulu `!is_pv` idi — yani fiziksel bölüm düzenlenirken de mountpoint zorunluydu. Kullanıcı sda1'i "kuruluma ait değil, yalnızca GRUB için" diye işaretlemek istiyordu ve diyalog kabul etmiyordu. Üstelik iki kural birbirinden farklıydı: validate serbest bırakırken diyalog reddediyor ve kullanıcı hangisinin geçerli olduğunu anlayamıyordu. ## mountpoint_required() — tek kural, iki yerde Kural artık saf bir yardımcıda tanımlı ve HER İKİ yerde de aynen kullanılıyor: - LVM PV → serbest - düzenlenen FİZİKSEL bölüm (orig_device dolu) → serbest - yeni tanım → zorunlu ⚠️ İki yerde ayrı yazılırsa birinde geçen diğerinde reddedilir ve kullanıcı "İleri"ye geçemez. Not yorumla sabitlendi. ## Hata mesajı artık bölümü adıyla söylüyor `mp_error_empty_mountpoint_named` eklendi. Genel mesaj ("Bağlama noktası boş olamaz") ekranda beş bölüm varken hangisinin kastedildiğini belli etmiyordu; bu bir hatanın iki tur turmasına yol açmıştı. ## Ölü i18n anahtarları silindi `rescue_button_tooltip` ve `job_display_manager` kodda KULLANILMIYORDU ve `en.toml`'da karşılıkları yoktu. Proje kuralı gereği çeviri yazmak yerine silindi: sessizleştirmek yerine kökten kaldır. tr/en artık tam eşleşiyor (463/463). ## Testler (98 → 102) - GRUB için ayrılmış fiziksel bölüm boş bırakılabilir - yeni tanımda mountpoint hâlâ zorunlu - LVM PV hiçbir koşulda mountpoint istemez - kullanıcının tam senaryosu (sda GRUB-only + sdb Linux + sdc /home) geçerli kabul ediliyor cargo check --all-targets : 0 hata, 0 uyarı cargo test : 102/102 Co-Authored-By: Claude Opus 4.8 (1M context) --- locales/en.toml | 1 + locales/tr.toml | 3 +- src/steps/partition.rs | 142 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 136 insertions(+), 10 deletions(-) diff --git a/locales/en.toml b/locales/en.toml index 5a04aa1..e13c286 100644 --- a/locales/en.toml +++ b/locales/en.toml @@ -296,6 +296,7 @@ job_mount_custom = "Mounting manual partitions" # ── Extra Localization / i18n ───────────────────────────── mp_delete_confirm_title = "Delete Partition" mp_error_empty_mountpoint = "Mount point cannot be empty." +mp_error_empty_mountpoint_named = "Mount point cannot be empty: {device}. Pick a mount point from the list, or mark the partition for deletion and continue." mp_plan_valid = "✓ Partition plan is valid. You can proceed." mp_select_disk_first = "← Select a disk first." mp_add_dialog_hint = "/ or /home or swap" diff --git a/locales/tr.toml b/locales/tr.toml index a5282f3..8031959 100644 --- a/locales/tr.toml +++ b/locales/tr.toml @@ -151,7 +151,6 @@ check_warn_notice = "Bazı önerilen gereksinimler karşılanmıyor. Kurulum d check_all_pass = "Tüm sistem gereksinimleri karşılandı." recheck = "🔄 Yeniden Kontrol Et" rescue_button = "🆘 Kurtarma Kipi" -rescue_button_tooltip = "PiSi Linux'u Kurtarma Modunda Yeniden Kur" # ── Users (ek) ──────────────────────────────────────────── pwd_very_weak = "Çok zayıf" @@ -261,6 +260,7 @@ job_mount_custom = "Manuel bölümler bağlanıyor" # ── Ek Yerelleştirme / i18n ────────────────────────────────── mp_delete_confirm_title = "Bölümü Sil" mp_error_empty_mountpoint = "Bağlama noktası boş olamaz." +mp_error_empty_mountpoint_named = "Bağlama noktası boş olamaz: {device}. Listeden bir bağlama noktası seçin ya da bölümü \"Sil\" işaretleyip geçin." mp_plan_valid = "Bölüm planı geçerli. İleri tuşuna basabilirsiniz." mp_select_disk_first = "← Önce bir disk seçin." mp_add_dialog_hint = "/ veya /home veya swap" @@ -317,7 +317,6 @@ job_ldconfig = "Paylaşımlı kütüphaneler yapılandırılıyor (ldc job_update_env = "Sistem ortamı güncelleniyor" job_cleanup_live = "Canlı ortam izleri temizleniyor" job_datetime = "Tarih & saat yapılandırılıyor" -job_display_manager = "Görüntü yöneticisi yapılandırılıyor" job_autologin = "Otomatik oturum açma uygulanıyor" job_netinstall = "Ek paketler kuruluyor" job_setup_lvm = "LVM yapılandırılıyor" diff --git a/src/steps/partition.rs b/src/steps/partition.rs index 979ebf8..0c991af 100644 --- a/src/steps/partition.rs +++ b/src/steps/partition.rs @@ -456,8 +456,12 @@ fn validate_manual_on( // Zorunluluk yeni tanımlarda (orig_device boş) ve LVM PV dışında // geçerlidir: kullanıcı "ne işe yarayacak" diye sormadan kaydedemez. for p in &live_parts { - if p.orig_device.is_empty() && p.mountpoint.is_empty() && p.fstype != FsType::Lvm { - return Some(t!("mp_error_empty_mountpoint").to_string()); + if p.mountpoint.is_empty() && mountpoint_required(Some(p), &p.fstype) { + // Bölümü ADIYLA söyle. Genel mesaj, ekranda birden fazla + // bölüm varken hangisinin kastedildiğini belli etmiyor ve + // kullanıcıyı (varsa) hiç ilgisi olmayan bir bölümde + // aramaya itiyordu. + return Some(t!("mp_error_empty_mountpoint_named", device = p.device).to_string()); } } @@ -468,6 +472,33 @@ fn validate_manual_on( // Çoklu disk yardımcıları // ───────────────────────────────────────────── +/// Bir tanımın bağlama noktası BOŞ olmak zorunda mı? +/// +/// Boş bırakmak "bu bölüm kuruluma ait değil" demektir. İki durumda +/// serbesttir: +/// +/// 1. **LVM PV** — üzerinde mantıksal hacimler yaşar, kendisi +/// bağlanmaz. +/// 2. **Düzenlenen FİZİKSEL bölüm** (`orig_device` dolu) — kullanıcı +/// çift önyüklemeli kurulumda Windows'un boot bölümünü "yalnızca +/// GRUB için, mount edilmeyecek" diye işaretlemek isteyebilir. +/// +/// Yeni bir tanımda (`None` ya da `orig_device` boş) ZORUNLUDUR: kullanıcı +/// "ne işe yarayacak" diye sormadan kaydedemez. +/// +/// ⚠️ Bu kural hem `validate_manual_on`'da hem de kaydetme diyaloğunda +/// aynen uygulanmalı — birinde serbest olan diğerinde reddediliyorsa +/// kullanıcı "İleri"ye geçemez. +fn mountpoint_required(editing: Option<&CustomPartition>, fstype: &FsType) -> bool { + if *fstype == FsType::Lvm { + return false; + } + match editing { + None => true, + Some(p) => p.orig_device.is_empty(), + } +} + /// "Yeni Bölüm" diyaloğu için varsayılan bağlama noktası önerisi. /// /// `/`, `/boot/efi` ve `swap` bir kurulumda **TEKİLDİR** — birdir. Bu @@ -1919,12 +1950,25 @@ impl PartitionStep { let encrypt = self.manual.add_encrypt; let luks_password = if encrypt { self.manual.add_luks_password.clone() } else { String::new() }; - // LVM PV'nin bağlama noktası YOKTUR; üzerinde - // mantıksal hacimler yaşar. Diğer her dosya - // sistemi için mountpoint zorunludur. - let is_pv = fstype == FsType::Lvm; - if mp.is_empty() && !is_pv { - self.manual.error = Some(t!("mp_error_empty_mountpoint").to_string()); + // Aynı kural `validate_manual_on` ile paylaşılır + // (`mountpoint_required`): ikisi de "fizikselse + // serbest" demeli, yoksa birinde geçen diğerinde + // reddedilir. + let editing = if is_edit { + self.manual.edit_idx.and_then(|i| state.custom_partitions.get(i)) + } else { + None + }; + if mp.is_empty() && mountpoint_required(editing, &fstype) { + // Hangi bölüm olduğunu SÖYLE: ekranda beş + // bölüm varken genel mesaj kullanıcıyı + // yanlış bölümü aramaya yolluyordu. + let label = editing + .map(|p| p.device.clone()) + .unwrap_or_else(|| t!("mp_new_partition").to_string()); + self.manual.error = Some( + t!("mp_error_empty_mountpoint_named", device = label).to_string() + ); } else if target_disk.is_empty() { self.manual.error = Some(t!("mp_select_disk_first").to_string()); } else if is_edit { @@ -3174,6 +3218,88 @@ mod multidisk_tests { ); } + // ── REGRESYON: "yalnızca GRUB için" bölüm ────────────────── + + /// Düzenlenen FİZİKSEL bölümün mountpoint'i boş bırakılabilir. + /// + /// Kullanıcı raporu: "sda1 grub eklenecek o yüzden herhangi bir + /// mount olayı yok ve yine bağlama noktası boş olamaz uyarısı + /// alıyorum." + /// + /// REGRESYON: kaydetme diyaloğunun koşulu `!is_pv` idi; fiziksel + /// bölüm düzenlenirken de mountpoint zorunluydu. Çift önyüklemeli + /// kurulumda Windows'un boot bölümünü "kuruluma ait değil, yalnızca + /// GRUB için" diye işaretlemek isteyen kullanıcı DİYALOĞU geçemiyordu. + #[test] + fn grub_only_physical_partition_may_have_no_mountpoint() { + let sda1 = p("/dev/sda", "/dev/sda1", 512, FsType::Fat32, ""); + assert!( + !mountpoint_required(Some(&sda1), &FsType::Fat32), + "yalnızca GRUB için kullanılacak fiziksel bölüm boş bırakılabilmeli" + ); + } + + /// Yeni tanımda mountpoint yine de ZORUNLU. + /// + /// Yukarıdaki gevşetme "her şey serbest" olmamalı — kullanıcı + /// eklediği yeni bir bölümün ne işe yarayacağını söylemeden + /// kaydedemez, yoksa sessizce mount edilmeyen bir tanım kalır. + #[test] + fn new_definition_still_requires_a_mountpoint() { + let mut new_def = p("/dev/sdc", "/dev/sdc1", 100_000, FsType::Ext4, ""); + new_def.orig_device = String::new(); + assert!( + mountpoint_required(Some(&new_def), &FsType::Ext4), + "yeni tanımda mountpoint zorunlu olmalı" + ); + // Ekleme modunda düzenlenen bölüm yoktur → zorunlu + assert!( + mountpoint_required(None, &FsType::Ext4), + "ekleme modunda mountpoint zorunlu olmalı" + ); + } + + /// LVM PV serbest — mount edilmez, üzerinde LV'ler yaşar. + #[test] + fn lvm_pv_never_requires_a_mountpoint() { + let physical = p("/dev/sda", "/dev/sda1", 20_000, FsType::Ext4, ""); + assert!( + !mountpoint_required(Some(&physical), &FsType::Lvm), + "fiziksel bölüm LVM'ye çevrilse bile PV mountpoint istemez" + ); + assert!( + !mountpoint_required(None, &FsType::Lvm), + "yeni PV de mountpoint istemez" + ); + } + + /// Kullanıcının tam senaryosu: sda'da Windows (sda1 yalnızca GRUB + /// için, mount edilmeyecek), sdb'de Linux, sdc boş. Düzen GEÇERLİ + /// olmalı — GRUB için ayrılmış bölüm yüzünden reddedilmemeli. + #[test] + fn full_user_scenario_with_grub_only_partition_is_valid() { + let disks = vec![ + disk("/dev/sda", 250), + disk("/dev/sdb", 500), + disk("/dev/sdc", 1_000), + ]; + let parts = vec![ + // Windows boot — yalnızca GRUB, mount yok + p("/dev/sda", "/dev/sda1", 512, FsType::Fat32, ""), + p("/dev/sdb", "/dev/sdb1", 1_024, FsType::Fat32, "/boot/efi"), + p("/dev/sdb", "/dev/sdb2", 8_192, FsType::Swap, "swap"), + p("/dev/sdb", "/dev/sdb3", 400_000, FsType::Ext4, "/"), + // sdc'de kullanıcının eklediği /home + p("/dev/sdc", "/dev/sdc1", 900_000, FsType::Ext4, "/home"), + ]; + assert_eq!( + validate_manual_on(&parts, &[], true, &disks), + None, + "GRUB için ayrılmış bölüm düzeni geçersiz kılmamalı: {:?}", + validate_manual_on(&parts, &[], true, &disks) + ); + } + /// numaralandırma: iki diskte bağımsız slot dağıtımı. /// ESKİ test (renumber_keeps_disks_independent) yalnızca fiziksel /// bölümleri kullandığı için TUUTOLOJİK'ydi: global (disk-suz)