forked from pisilinux-rs/yali-rs
feat: çoklu disk desteği ve eski Yali tarzı granular bölümleme işlemleri
- CustomPartition artık disk alanı taşıyor; bölümler diske göre gruplanıyor - Manuel modda TÜM disklerin mevcut bölümleri otomatik listeleniyor - Tablo her disk için kendi sütun başlıklarını gösteriyor - Silinmek işaretli bölümler tek 'Boş Alan' satırında birleşiyor (GParted tarzı); Boş Alan = disk − (korunanlar + yeni tanımlar), 0'a düşerse satır gizlenir - Sil/Geri Al: fiziksel bölümler silinmek İŞARETLENİR (OperationDestroyDevice karşılığı), kullanıcı tanımı olanlar listeden tamamen kalkar - Kurulum job'ları granular modele geçti: mktable ile tüm disk sıfırlanmıyor; rm → boş alana mkpart (sysfs geometrisi) → yalnızca istenenler mkfs. İşlemi olmayan disklere hiç dokunulmaz - Slot ataması parted davranışıyla birebir: silinen numara yeniden kullanılır - 'Kalan alan' sembolik kaldı; boyut kurulum anında boşluktan belirlenir (taşma imkânsız) + kapasite aşımı uyarısı/kilidi eklendi - Özet ekranı ve bölüm planı: disk başına Mevcut/Kurulum Sonrası çift bar, kapasite gösterimleri düzeltildi - Remixicon simge düzeltmesi (hard-drive), mp_* yeni çeviri anahtarları - Testler: slot yeniden kullanımı, kapasite aşımı, disk-bazlı hesaplar (22/22)
This commit is contained in:
@@ -141,6 +141,13 @@ display_manager_autologin_hint = "(first user)"
|
|||||||
display_manager_de_not_found = "(not detected)"
|
display_manager_de_not_found = "(not detected)"
|
||||||
display_manager_note = "Note: The display manager will be enabled by default on the installed system."
|
display_manager_note = "Note: The display manager will be enabled by default on the installed system."
|
||||||
mp_size_remaining = "Remaining Space"
|
mp_size_remaining = "Remaining Space"
|
||||||
|
mp_size_available = "Available"
|
||||||
|
mp_create_here = "Create Here"
|
||||||
|
mp_will_delete = "will be deleted"
|
||||||
|
mp_restore = "Restore"
|
||||||
|
mp_free_space = "Free Space"
|
||||||
|
mp_bar_now = "Current Layout"
|
||||||
|
mp_bar_after = "After Installation"
|
||||||
|
|
||||||
# ── UI / General ──────────────────────────────────────────
|
# ── UI / General ──────────────────────────────────────────
|
||||||
installer = "YALI Installation Tool"
|
installer = "YALI Installation Tool"
|
||||||
|
|||||||
@@ -117,6 +117,13 @@ disk_too_small = "Disk çok küçük"
|
|||||||
summary_partition_plan = "Bölümleme"
|
summary_partition_plan = "Bölümleme"
|
||||||
summary_custom_partitions = "Manuel Bölümler"
|
summary_custom_partitions = "Manuel Bölümler"
|
||||||
mp_size_remaining = "Kalan Alan"
|
mp_size_remaining = "Kalan Alan"
|
||||||
|
mp_size_available = "Kullanılabilir"
|
||||||
|
mp_create_here = "Buraya Oluştur"
|
||||||
|
mp_will_delete = "silinecek"
|
||||||
|
mp_restore = "Geri Al"
|
||||||
|
mp_free_space = "Boş Alan"
|
||||||
|
mp_bar_now = "Mevcut Durum"
|
||||||
|
mp_bar_after = "Kurulum Sonrası"
|
||||||
|
|
||||||
# ── UI / Genel ────────────────────────────────────────────
|
# ── UI / Genel ────────────────────────────────────────────
|
||||||
installer = "PisiLinux Kurulum Aracı"
|
installer = "PisiLinux Kurulum Aracı"
|
||||||
|
|||||||
@@ -167,11 +167,86 @@ pub struct LogicalVolume {
|
|||||||
pub mountpoint: String,
|
pub mountpoint: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cihaz yolundan ait olduğu diskin yolunu çıkarır.
|
||||||
|
///
|
||||||
|
/// - `/dev/sda1` → `/dev/sda`
|
||||||
|
/// - `/dev/nvme0n1p2` → `/dev/nvme0n1`
|
||||||
|
/// - `/dev/mmcblk0p1` → `/dev/mmcblk0`
|
||||||
|
#[allow(dead_code)] // genel yardımcı; testlerde ve gelecekteki işlerde kullanılır
|
||||||
|
pub fn device_to_disk(device: &str) -> String {
|
||||||
|
let base = device.trim_start_matches("/dev/");
|
||||||
|
// NVMe/MMC/loop aygıtlarında bölüm ayracı "p" + rakamdır
|
||||||
|
let needs_p = base.contains("nvme") || base.contains("mmcblk") || base.contains("loop");
|
||||||
|
if let Some(pos) = base.rfind('p') {
|
||||||
|
if needs_p && base[pos + 1..].chars().all(|c| c.is_ascii_digit()) && !base[pos + 1..].is_empty() {
|
||||||
|
return format!("/dev/{}", &base[..pos]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// SATA/virtio: sondaki tüm rakamları at
|
||||||
|
let trimmed = base.trim_end_matches(|c: char| c.is_ascii_digit());
|
||||||
|
format!("/dev/{}", trimmed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cihaz adından bölüm numarasını çıkarır ("/dev/sda3" → 3,
|
||||||
|
/// "/dev/nvme0n1p2" → 2). Sonda rakam yoksa None döner.
|
||||||
|
pub fn part_number(device: &str) -> Option<u32> {
|
||||||
|
let base = device.trim_start_matches("/dev/");
|
||||||
|
let digits: String = base.chars().rev().take_while(|c| c.is_ascii_digit()).collect();
|
||||||
|
digits.chars().rev().collect::<String>().parse().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bölüm tanımlarına parted'ın gerçek slot davranışını izleyerek
|
||||||
|
/// aygıt numarası atar.
|
||||||
|
///
|
||||||
|
/// Eski Yali modeliyle aynı mantık: fiziksel (silinmemiş) bölümler
|
||||||
|
/// kendi numaralarını korur; silinmek işaretlenenlerin slotları boşalır
|
||||||
|
/// ve yeni tanımlar en küçük boş slotu sırayla doldurur. Örn. sda2
|
||||||
|
/// silinip yeni bölüm eklenirse yeni bölüm sda2 olur; sda3 yerinde kalır.
|
||||||
|
pub fn assign_partition_numbers(parts: &mut [CustomPartition]) {
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
// Disk bazlı kullanılan slot kümeleri
|
||||||
|
let mut used: std::collections::HashMap<String, BTreeSet<u32>> = std::collections::HashMap::new();
|
||||||
|
for p in parts.iter() {
|
||||||
|
if p.delete { continue; } // silinecekler slot açar
|
||||||
|
if !p.orig_device.is_empty() { // fiziksel: kendi numarası
|
||||||
|
if let Some(n) = part_number(&p.orig_device) {
|
||||||
|
used.entry(p.disk.clone()).or_default().insert(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for p in parts.iter_mut() {
|
||||||
|
if p.delete {
|
||||||
|
// Silinecek bölüm: kurulumda yok olacak; adı olduğu gibi kalır,
|
||||||
|
// yeni slot tüketmez.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match part_number(&p.orig_device) {
|
||||||
|
Some(n) => { p.device = part_path(&p.disk, n); }
|
||||||
|
None => {
|
||||||
|
let set = used.entry(p.disk.clone()).or_default();
|
||||||
|
// en küçük boş slot
|
||||||
|
let mut n = 1u32;
|
||||||
|
while set.contains(&n) { n += 1; }
|
||||||
|
set.insert(n);
|
||||||
|
p.device = part_path(&p.disk, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Kullanıcının manuel olarak tanımladığı tek bir bölüm.
|
/// Kullanıcının manuel olarak tanımladığı tek bir bölüm.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CustomPartition {
|
pub struct CustomPartition {
|
||||||
|
/// Bölümün ait olduğu disk (ör. "/dev/sda").
|
||||||
|
/// Çoklu disk kurulumunda bölümler diske göre gruplanır.
|
||||||
|
pub disk: String,
|
||||||
/// Bölüm aygit adı; oluşturulacak (ör. "/dev/sda1")
|
/// Bölüm aygit adı; oluşturulacak (ör. "/dev/sda1")
|
||||||
pub device: String,
|
pub device: String,
|
||||||
|
/// Taramada bulunan fiziksel cihaz adı (örn. "/dev/sda3").
|
||||||
|
/// Kullanıcı tarafından yeni eklenen tanımlarda boştur.
|
||||||
|
/// Silme işleminde satırın listede kalıp yeniden
|
||||||
|
/// tanımlanabilmesini sağlar.
|
||||||
|
pub orig_device: String,
|
||||||
/// Boyut MB cinsinden; 0 = "kalan alan"
|
/// Boyut MB cinsinden; 0 = "kalan alan"
|
||||||
pub size_mb: u64,
|
pub size_mb: u64,
|
||||||
/// Dosya sistemi
|
/// Dosya sistemi
|
||||||
@@ -186,6 +261,10 @@ pub struct CustomPartition {
|
|||||||
pub luks_name: String,
|
pub luks_name: String,
|
||||||
/// false = biçimlendir, true = olduğu gibi bırak (mevcut bölümleri koru)
|
/// false = biçimlendir, true = olduğu gibi bırak (mevcut bölümleri koru)
|
||||||
pub skip_format: bool,
|
pub skip_format: bool,
|
||||||
|
/// true = kurulum sırasında bu bölüm diskten SİLİNECEK
|
||||||
|
/// (eski Yali'deki OperationDestroyDevice karşılığı).
|
||||||
|
/// Satır listede görünür kalır; istenirse işaret geri alınır.
|
||||||
|
pub delete: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Kurulum boyunca toplanan tüm veriler.
|
/// Kurulum boyunca toplanan tüm veriler.
|
||||||
|
|||||||
+1
-1
@@ -633,7 +633,7 @@ pub fn build_custom_job_queue(
|
|||||||
let src = if cfg.source.is_empty() { detect_source_dir() } else { cfg.source };
|
let src = if cfg.source.is_empty() { detect_source_dir() } else { cfg.source };
|
||||||
|
|
||||||
let mut jobs: Vec<Box<dyn Job>> = vec![
|
let mut jobs: Vec<Box<dyn Job>> = vec![
|
||||||
Box::new(CustomPartitionJob { disk: disk.clone(), custom_partitions: custom_partitions.clone() }),
|
Box::new(CustomPartitionJob { custom_partitions: custom_partitions.clone() }),
|
||||||
Box::new(SetupLvmJob { volume_groups: volume_groups.clone(), logical_volumes: logical_volumes.clone() }),
|
Box::new(SetupLvmJob { volume_groups: volume_groups.clone(), logical_volumes: logical_volumes.clone() }),
|
||||||
Box::new(MountCustomPartitionsJob { custom_partitions: custom_partitions.clone(), logical_volumes: logical_volumes.clone(), mount: mt.clone() }),
|
Box::new(MountCustomPartitionsJob { custom_partitions: custom_partitions.clone(), logical_volumes: logical_volumes.clone(), mount: mt.clone() }),
|
||||||
Box::new(MountBindJob { mount: mt.clone() }),
|
Box::new(MountBindJob { mount: mt.clone() }),
|
||||||
|
|||||||
+200
-60
@@ -448,9 +448,75 @@ async fn read_partition_uuids(
|
|||||||
// MANUEL BÖLÜMLEME İÇİN DESTEKLEYİCİ JOB'LAR
|
// MANUEL BÖLÜMLEME İÇİN DESTEKLEYİCİ JOB'LAR
|
||||||
// ─────────────────────────────────────────────
|
// ─────────────────────────────────────────────
|
||||||
|
|
||||||
/// Manuel bölümleme ayarlarını disk üzerinde uygulayan ve biçimlendiren Job.
|
/// Diskteki boş bölgeleri MB cinsinden hesaplar (sysfs tabanlı).
|
||||||
|
///
|
||||||
|
/// `exclude_nums`: silinecek işaretli bölümler — boşluk olarak
|
||||||
|
/// davranılır (kurulum sırasında önce silindikleri için).
|
||||||
|
/// Dönen değer: (başlangıç_mb, bitiş_mb) aralıkları; disk sonunda
|
||||||
|
/// GPT yedek başlığı için 1MB pay bırakılır.
|
||||||
|
fn disk_free_regions_mb(disk: &str, exclude_nums: &[u32]) -> Result<Vec<(u64, u64)>, String> {
|
||||||
|
let base = disk.trim_start_matches("/dev/");
|
||||||
|
const SECTOR: u64 = 512;
|
||||||
|
const MB: u64 = 1024 * 1024;
|
||||||
|
|
||||||
|
let disk_sectors: u64 = std::fs::read_to_string(format!("/sys/block/{}/size", base))
|
||||||
|
.map_err(|e| format!("{} boyutu okunamadı: {}", disk, e))?
|
||||||
|
.trim().parse().unwrap_or(0);
|
||||||
|
if disk_sectors == 0 {
|
||||||
|
return Err(format!("{} disk boyutu okunamadı", disk));
|
||||||
|
}
|
||||||
|
let total_mb = disk_sectors * SECTOR / MB;
|
||||||
|
let usable_total = total_mb.saturating_sub(1); // disk sonu güvenlik payı
|
||||||
|
|
||||||
|
// Dolu aralıklar
|
||||||
|
let mut occupied: Vec<(u64, u64)> = Vec::new();
|
||||||
|
if let Ok(entries) = std::fs::read_dir(format!("/sys/block/{}", base)) {
|
||||||
|
for entry in entries.flatten() {
|
||||||
|
let name = entry.file_name().to_string_lossy().to_string();
|
||||||
|
if !name.starts_with(base) || name == base { continue; }
|
||||||
|
let num = match crate::installer::part_number(&format!("/dev/{}", name)) {
|
||||||
|
Some(n) => n,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
if exclude_nums.contains(&num) { continue; }
|
||||||
|
let start_sectors: u64 = std::fs::read_to_string(entry.path().join("start"))
|
||||||
|
.ok().and_then(|s| s.trim().parse().ok()).unwrap_or(0);
|
||||||
|
let len_sectors: u64 = std::fs::read_to_string(entry.path().join("size"))
|
||||||
|
.ok().and_then(|s| s.trim().parse().ok()).unwrap_or(0);
|
||||||
|
if len_sectors == 0 { continue; }
|
||||||
|
let start_mb = (start_sectors * SECTOR + MB - 1) / MB;
|
||||||
|
let end_mb = ((start_sectors + len_sectors) * SECTOR) / MB;
|
||||||
|
if end_mb > start_mb {
|
||||||
|
occupied.push((start_mb, end_mb.min(usable_total)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
occupied.sort();
|
||||||
|
|
||||||
|
// Boşlukları çıkar (ilk 1MB boot alanı hariç)
|
||||||
|
const HEADROOM_MB: u64 = 1;
|
||||||
|
let mut regions: Vec<(u64, u64)> = Vec::new();
|
||||||
|
let mut cursor = HEADROOM_MB.min(usable_total);
|
||||||
|
for (s, e) in &occupied {
|
||||||
|
if *s > cursor { regions.push((cursor, *s)); }
|
||||||
|
cursor = cursor.max(*e);
|
||||||
|
}
|
||||||
|
if cursor < usable_total { regions.push((cursor, usable_total)); }
|
||||||
|
regions.retain(|(s, e)| e > s);
|
||||||
|
Ok(regions)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manuel bölümleme ayarlarını granular (eski Yali Operation modeli)
|
||||||
|
/// yaklaşımla uygulayan Job.
|
||||||
|
///
|
||||||
|
/// Eski Yali'nin DeviceTree/Operation mimarisiyle aynı felsefe:
|
||||||
|
/// - Dokunulmayan disklere ve bölümlere HİÇ dokunulmaz (veriler korunur)
|
||||||
|
/// - Silinmek işaretli bölümler `parted rm` ile kaldırılır
|
||||||
|
/// (OperationDestroyDevice karşılığı)
|
||||||
|
/// - Yeni tanımlar diskin BOŞ alanlarına yerleştirilir; tüm disk
|
||||||
|
/// sıfırlanmaz (OperationCreateDevice karşılığı)
|
||||||
|
/// - skip_format=false olanlar biçimlendirilir (OperationCreateFormat)
|
||||||
pub struct CustomPartitionJob {
|
pub struct CustomPartitionJob {
|
||||||
pub disk: String,
|
|
||||||
pub custom_partitions: Vec<CustomPartition>,
|
pub custom_partitions: Vec<CustomPartition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,68 +526,141 @@ impl Job for CustomPartitionJob {
|
|||||||
|
|
||||||
async fn run(&self, ui: &UiSender) -> Result<(), String> {
|
async fn run(&self, ui: &UiSender) -> Result<(), String> {
|
||||||
let is_uefi = std::path::Path::new("/sys/firmware/efi").exists();
|
let is_uefi = std::path::Path::new("/sys/firmware/efi").exists();
|
||||||
|
|
||||||
// 0. Diski hazırla: bağlamaları ayır, kernel tablosunu temizle
|
|
||||||
prepare_disk(&self.disk, ui).await;
|
|
||||||
|
|
||||||
let is_gpt = is_uefi; // Genellikle UEFI ise GPT kullanılır
|
|
||||||
// MBR'de "primary" zorunludur, GPT'de ise bu alan isim (label) alanıdır.
|
// MBR'de "primary" zorunludur, GPT'de ise bu alan isim (label) alanıdır.
|
||||||
let part_type_or_name = if is_gpt { "" } else { "primary" };
|
let part_type_or_name = if is_uefi { "" } else { "primary" };
|
||||||
|
|
||||||
// 1. Bölüm tablosu oluştur
|
// 0. İşlem yapılan diskleri tanım sırasına göre topla
|
||||||
let table_label = if is_uefi { "gpt" } else { "msdos" };
|
let mut disks: Vec<String> = Vec::new();
|
||||||
ui.log(format!("parted {} mktable {}", self.disk, table_label));
|
for part in &self.custom_partitions {
|
||||||
run_parted(&["-s", &self.disk, "mktable", table_label]).await?;
|
if !disks.contains(&part.disk) {
|
||||||
|
disks.push(part.disk.clone());
|
||||||
let mut next_mb: u64 = 1; // İlk MB boot sektörü
|
|
||||||
|
|
||||||
// 2. Bölümleri parted ile oluştur
|
|
||||||
for (i, part) in self.custom_partitions.iter().enumerate() {
|
|
||||||
let part_num = (i + 1) as u32;
|
|
||||||
let end_str = if part.size_mb == 0 {
|
|
||||||
"100%".to_string()
|
|
||||||
} else {
|
|
||||||
format!("{}MB", next_mb + part.size_mb)
|
|
||||||
};
|
|
||||||
let start_str = format!("{}MB", next_mb);
|
|
||||||
|
|
||||||
let fs_label = match part.fstype {
|
|
||||||
FsType::Fat32 => "fat32",
|
|
||||||
FsType::Swap => "linux-swap",
|
|
||||||
FsType::Ext4 => "ext4",
|
|
||||||
FsType::Ext3 => "ext3",
|
|
||||||
FsType::Btrfs => "btrfs",
|
|
||||||
FsType::Xfs => "xfs",
|
|
||||||
FsType::Ntfs => "ntfs",
|
|
||||||
// LVM PV için parted "ext4" ile işaretle, pvcreate sonra
|
|
||||||
FsType::Lvm => "ext4",
|
|
||||||
// LVM LV doğrudan parted ile oluşturulmaz
|
|
||||||
FsType::LvmLv => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
ui.log(format!(
|
|
||||||
"Bölüm {} oluşturuluyor: {} - {} ({})",
|
|
||||||
part_num, start_str, end_str, fs_label
|
|
||||||
));
|
|
||||||
run_parted(&[
|
|
||||||
"-s", &self.disk, "mkpart", part_type_or_name, fs_label,
|
|
||||||
&start_str, &end_str
|
|
||||||
]).await?;
|
|
||||||
|
|
||||||
if part.mountpoint == "/boot/efi" || part.fstype == FsType::Fat32 {
|
|
||||||
run_parted(&["-s", &self.disk, "set", &part_num.to_string(), "esp", "on"]).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if part.size_mb > 0 {
|
|
||||||
next_mb += part.size_mb;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.5. Tüm mkpart bitti; kernel'e bildir ve aygıt düğümlerini bekle
|
for disk in &disks {
|
||||||
sync_partition_table(&self.disk).await;
|
let parts: Vec<&CustomPartition> = self.custom_partitions.iter()
|
||||||
|
.filter(|p| p.disk == *disk).collect();
|
||||||
|
|
||||||
// 3. Biçimlendir (LUKS ile şifreleme desteklenir)
|
// Bu disk için planlanan işlemler
|
||||||
|
let destroys: Vec<u32> = parts.iter()
|
||||||
|
.filter(|p| p.delete && !p.orig_device.is_empty())
|
||||||
|
.filter_map(|p| crate::installer::part_number(&p.orig_device))
|
||||||
|
.collect();
|
||||||
|
let mut creates: Vec<&CustomPartition> = parts.iter()
|
||||||
|
.filter(|p| !p.delete && p.orig_device.is_empty() && p.fstype != FsType::LvmLv)
|
||||||
|
.map(|p| *p)
|
||||||
|
.collect();
|
||||||
|
// Sabit boyutlular ÖNCE yerleşir; "kalan alan" (size 0)
|
||||||
|
// tanımları EN SON, geriye kalan boşluğa yerleştirilir.
|
||||||
|
// (Stable sort → aynı grupta tanım sırası korunur.)
|
||||||
|
creates.sort_by_key(|p| p.size_mb == 0);
|
||||||
|
let has_format = parts.iter().any(|p| !p.delete && !p.skip_format);
|
||||||
|
|
||||||
|
if destroys.is_empty() && creates.is_empty() && !has_format {
|
||||||
|
ui.log(format!("⏭️ {}: işlem yok, korunuyor", disk));
|
||||||
|
continue; // eski Yali gibi dokunulmayan diske girilmez
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.log(format!("── Disk işleniyor: {} ──", disk));
|
||||||
|
// Bağlamaları ayır, swap kapat
|
||||||
|
prepare_disk(disk, ui).await;
|
||||||
|
|
||||||
|
// ── FAZ A: SİLMEK İŞARETLİ BÖLÜMLER (büyükten küçüğe numara) ──
|
||||||
|
let mut del_sorted = destroys.clone();
|
||||||
|
del_sorted.sort_unstable_by(|a, b| b.cmp(a));
|
||||||
|
for n in &del_sorted {
|
||||||
|
ui.log(format!("parted {} rm {} (bölüm siliniyor)", disk, n));
|
||||||
|
run_parted(&["-s", disk, "rm", &n.to_string()]).await?;
|
||||||
|
}
|
||||||
|
if !del_sorted.is_empty() {
|
||||||
|
sync_partition_table(disk).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── FAZ B: YENİ BÖLÜMLER (boş alana yerleştir) ──────────────
|
||||||
|
if !creates.is_empty() {
|
||||||
|
let mut regions = disk_free_regions_mb(disk, &destroys)?;
|
||||||
|
for part in &creates {
|
||||||
|
let fs_label = match part.fstype {
|
||||||
|
FsType::Fat32 => "fat32",
|
||||||
|
FsType::Swap => "linux-swap",
|
||||||
|
FsType::Ext4 => "ext4",
|
||||||
|
FsType::Ext3 => "ext3",
|
||||||
|
FsType::Btrfs => "btrfs",
|
||||||
|
FsType::Xfs => "xfs",
|
||||||
|
FsType::Ntfs => "ntfs",
|
||||||
|
// LVM PV için parted "ext4" ile işaretle, pvcreate sonra
|
||||||
|
FsType::Lvm => "ext4",
|
||||||
|
FsType::LvmLv => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Yerleşim: boyut belirtilmişse sığan İLK boş bölge,
|
||||||
|
// "kalan alan" ise en büyük bölgenin tamamı.
|
||||||
|
let need = part.size_mb;
|
||||||
|
let placement = if need == 0 {
|
||||||
|
let idx = regions.iter().enumerate()
|
||||||
|
.max_by_key(|(_, (s, e))| e.saturating_sub(*s))
|
||||||
|
.map(|(i, _)| i);
|
||||||
|
match idx {
|
||||||
|
Some(i) => {
|
||||||
|
let r = regions.remove(i);
|
||||||
|
Some(r)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut found = None;
|
||||||
|
for (i, (s, e)) in regions.iter().enumerate() {
|
||||||
|
if e.saturating_sub(*s) >= need {
|
||||||
|
found = Some((i, (*s, s + need)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match found {
|
||||||
|
Some((i, seg)) => {
|
||||||
|
let (_, e) = regions[i];
|
||||||
|
let start = seg.0;
|
||||||
|
let new_start = start + need;
|
||||||
|
if e > new_start {
|
||||||
|
regions[i] = (new_start, e);
|
||||||
|
} else {
|
||||||
|
regions.remove(i);
|
||||||
|
}
|
||||||
|
Some(seg)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (start_mb, end_mb) = placement.ok_or_else(|| format!(
|
||||||
|
"{} üzerinde {} MB için yeterli boş alan yok", disk, need))?;
|
||||||
|
|
||||||
|
let start_str = format!("{}MB", start_mb);
|
||||||
|
let end_str = format!("{}MB", end_mb);
|
||||||
|
let slot = crate::installer::part_number(&part.device).unwrap_or(0);
|
||||||
|
|
||||||
|
ui.log(format!(
|
||||||
|
"{} üzerinde yeni bölüm oluşturuluyor: {} - {} ({})",
|
||||||
|
disk, start_str, end_str, fs_label
|
||||||
|
));
|
||||||
|
run_parted(&[
|
||||||
|
"-s", disk, "mkpart", part_type_or_name, fs_label,
|
||||||
|
&start_str, &end_str
|
||||||
|
]).await?;
|
||||||
|
|
||||||
|
if part.mountpoint == "/boot/efi" || part.fstype == FsType::Fat32 {
|
||||||
|
run_parted(&["-s", disk, "set", &slot.to_string(), "esp", "on"]).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sync_partition_table(disk).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── FAZ C: BİÇİMLENDİRME aşağıdaki ortak döngüde ────────────
|
||||||
|
}
|
||||||
|
|
||||||
|
// Biçimlendir (LUKS ile şifreleme desteklenir) — tüm diskler.
|
||||||
|
// Silinmek işaretli bölümlere hiç dokunulmaz.
|
||||||
for part in &self.custom_partitions {
|
for part in &self.custom_partitions {
|
||||||
|
if part.delete { continue; }
|
||||||
let path = &part.device;
|
let path = &part.device;
|
||||||
|
|
||||||
// LUKS şifreleme (kök veya isteğe bağlı bölümler)
|
// LUKS şifreleme (kök veya isteğe bağlı bölümler)
|
||||||
@@ -667,7 +806,8 @@ impl Job for MountCustomPartitionsJob {
|
|||||||
|
|
||||||
async fn run(&self, ui: &UiSender) -> Result<(), String> {
|
async fn run(&self, ui: &UiSender) -> Result<(), String> {
|
||||||
// 1. Kök (/) bölümünü (fiziksel partition veya LVM LV) bul ve bağla
|
// 1. Kök (/) bölümünü (fiziksel partition veya LVM LV) bul ve bağla
|
||||||
let root_device = if let Some(root_part) = self.custom_partitions.iter().find(|p| p.mountpoint == "/") {
|
let root_device = if let Some(root_part) = self.custom_partitions.iter()
|
||||||
|
.find(|p| p.mountpoint == "/" && !p.delete) {
|
||||||
self.device_path(root_part)
|
self.device_path(root_part)
|
||||||
} else if let Some(root_lv) = self.logical_volumes.iter().find(|lv| lv.mountpoint == "/") {
|
} else if let Some(root_lv) = self.logical_volumes.iter().find(|lv| lv.mountpoint == "/") {
|
||||||
format!("/dev/{}/{}", root_lv.vg_name, root_lv.name)
|
format!("/dev/{}/{}", root_lv.vg_name, root_lv.name)
|
||||||
@@ -687,7 +827,7 @@ impl Job for MountCustomPartitionsJob {
|
|||||||
let mut mount_items = Vec::new();
|
let mut mount_items = Vec::new();
|
||||||
|
|
||||||
for part in &self.custom_partitions {
|
for part in &self.custom_partitions {
|
||||||
if part.mountpoint != "/" && part.fstype != FsType::Swap && !part.mountpoint.is_empty() {
|
if part.mountpoint != "/" && part.fstype != FsType::Swap && !part.mountpoint.is_empty() && !part.delete {
|
||||||
mount_items.push(MountItem {
|
mount_items.push(MountItem {
|
||||||
device: self.device_path(part),
|
device: self.device_path(part),
|
||||||
mountpoint: part.mountpoint.clone(),
|
mountpoint: part.mountpoint.clone(),
|
||||||
@@ -747,7 +887,7 @@ impl Job for FstabFromCustomPartitionsJob {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for part in &self.custom_partitions {
|
for part in &self.custom_partitions {
|
||||||
if part.mountpoint.is_empty() { continue; }
|
if part.mountpoint.is_empty() || part.delete { continue; }
|
||||||
|
|
||||||
let is_encrypted = part.encrypt && !part.luks_password.is_empty();
|
let is_encrypted = part.encrypt && !part.luks_password.is_empty();
|
||||||
|
|
||||||
|
|||||||
+706
-180
File diff suppressed because it is too large
Load Diff
+55
-15
@@ -43,15 +43,25 @@ impl InstallerStep for SummaryStep {
|
|||||||
};
|
};
|
||||||
summary_row(ui, t!("summary_keyboard"), &kb);
|
summary_row(ui, t!("summary_keyboard"), &kb);
|
||||||
|
|
||||||
let disk_str = match &state.selected_disk {
|
let disk_str = if state.erase_disk {
|
||||||
Some(d) => {
|
match &state.selected_disk {
|
||||||
if state.erase_disk {
|
Some(d) => format!("{} \u{EA6B} {}", d, t!("summary_erase_mode")),
|
||||||
format!("{} \u{EA6B} {}", d, t!("summary_erase_mode"))
|
None => t!("summary_not_selected").to_string(),
|
||||||
} else {
|
}
|
||||||
format!("{} \u{EA6B} {}", d, t!("summary_manual_mode"))
|
} else if !state.custom_partitions.is_empty() {
|
||||||
|
// Manuel modda tanım yapılan TÜM diskleri listele
|
||||||
|
let mut disks: Vec<&str> = Vec::new();
|
||||||
|
for part in &state.custom_partitions {
|
||||||
|
if !disks.contains(&part.disk.as_str()) {
|
||||||
|
disks.push(&part.disk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => t!("summary_not_selected").to_string(),
|
format!("{} \u{EA6B} {}", disks.join(", "), t!("summary_manual_mode"))
|
||||||
|
} else {
|
||||||
|
match &state.selected_disk {
|
||||||
|
Some(d) => format!("{} \u{EA6B} {}", d, t!("summary_manual_mode")),
|
||||||
|
None => t!("summary_not_selected").to_string(),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
summary_row(ui, t!("summary_disk"), &disk_str);
|
summary_row(ui, t!("summary_disk"), &disk_str);
|
||||||
|
|
||||||
@@ -73,15 +83,45 @@ impl InstallerStep for SummaryStep {
|
|||||||
}
|
}
|
||||||
} else if !state.custom_partitions.is_empty() {
|
} else if !state.custom_partitions.is_empty() {
|
||||||
let mut plan_lines = Vec::new();
|
let mut plan_lines = Vec::new();
|
||||||
|
// Bölümleri ait oldukları diske göre gruplu listele
|
||||||
|
let mut seen_disks: Vec<&str> = Vec::new();
|
||||||
for part in &state.custom_partitions {
|
for part in &state.custom_partitions {
|
||||||
let size_str = if part.size_mb == 0 {
|
if !seen_disks.contains(&part.disk.as_str()) {
|
||||||
t!("mp_size_remaining").to_string()
|
seen_disks.push(&part.disk);
|
||||||
} else if part.size_mb >= 1024 {
|
}
|
||||||
format!("{:.1} GB", part.size_mb as f64 / 1024.0)
|
}
|
||||||
} else {
|
for d in seen_disks {
|
||||||
format!("{} MB", part.size_mb)
|
plan_lines.push(format!("\u{EDFB} {}", d));
|
||||||
};
|
for part in state.custom_partitions.iter().filter(|p| p.disk == d) {
|
||||||
plan_lines.push(format!("{} \u{EA6B} {} ({}, {})", part.device, part.mountpoint, part.fstype, size_str));
|
let size_str = if part.size_mb == 0 {
|
||||||
|
// Sembolik boyut: kalan kapasiteyi göster
|
||||||
|
let others: u64 = state.custom_partitions.iter()
|
||||||
|
.filter(|q| q.disk == part.disk && !q.delete && q.size_mb > 0)
|
||||||
|
.map(|q| q.size_mb)
|
||||||
|
.sum();
|
||||||
|
let cap = state.available_disks.iter()
|
||||||
|
.find(|dk| dk.name == part.disk)
|
||||||
|
.map(|dk| dk.size_bytes / 1_048_576)
|
||||||
|
.unwrap_or(0);
|
||||||
|
let rem = cap.saturating_sub(others);
|
||||||
|
if rem >= 1024 {
|
||||||
|
format!("{:.1} GB", rem as f64 / 1024.0)
|
||||||
|
} else {
|
||||||
|
format!("{} MB", rem)
|
||||||
|
}
|
||||||
|
} else if part.size_mb >= 1024 {
|
||||||
|
format!("{:.1} GB", part.size_mb as f64 / 1024.0)
|
||||||
|
} else {
|
||||||
|
format!("{} MB", part.size_mb)
|
||||||
|
};
|
||||||
|
if part.delete {
|
||||||
|
plan_lines.push(format!("{} \u{EA6B} {} ({}, {}) \u{ECA1} {}",
|
||||||
|
part.device, "—", "—", size_str, t!("mp_will_delete")));
|
||||||
|
} else {
|
||||||
|
plan_lines.push(format!("{} \u{EA6B} {} ({}, {})",
|
||||||
|
part.device, part.mountpoint, part.fstype, size_str));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
summary_row(ui, t!("summary_custom_partitions"), &plan_lines.join("\n"));
|
summary_row(ui, t!("summary_custom_partitions"), &plan_lines.join("\n"));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user