forked from pisilinux-rs/yali-rs
parted mkpart söz dizimi + canlı autologin çakışması düzeltmeleri
- Manuel bölümleme parted söz dizimi disk etiketine göre seçiliyor (GPT: ad+fs, msdos: primary/logical+fs); etiket parted print'ten okunuyor, boş diske mktable kuruluyor - Yerelleşmiş parted çıktısı P0 veri kaybı yapıyordu: LC_ALL=C ile okunuyor, ayrıştırıcı Türkçe başlığı da tanıyor, mktable öncesi fiziksel bölüm kilidi var - Otomatik mod mkpart 4 argümana geçti (ad + fs türü) - GPT '/' adı sfdisk ile boşaltılıyor (warn-only, kurulumu durdurmaz) - ConfigureAutologinJob: /etc/sddm.conf.d/10-yali-autologin.conf kanonik dosya, hedeften oturum tespiti, kapalıyken canlı config'in ezilmesi (boş User= / lightdm satır temizliği) - Checkbox etiketi: 'Otomatik oturum aç' / 'Log in automatically' - 158 test, 0 uyarı
This commit is contained in:
@@ -95,9 +95,35 @@ dizini. Bu iş `CopyFilesJob`'dan sonra çalıştığı için `mount` doludur.
|
|||||||
> `lightdm.conf`'a dokunulmuyor, `sddm.conf` yazılıyor ve otomatik giriş
|
> `lightdm.conf`'a dokunulmuyor, `sddm.conf` yazılıyor ve otomatik giriş
|
||||||
> sessizce çalışmıyordu.
|
> sessizce çalışmıyordu.
|
||||||
|
|
||||||
|
### ⭐ Canlı ISO autologin'i + ters görünen checkbox (29 Eyl 2026)
|
||||||
|
|
||||||
|
ISO **canlı kullanıcı için çalışan bir autologin config'i** taşır
|
||||||
|
(kullanıcı doğruladı: canlı açılış otomatik giriyor). Bu iki arızayı
|
||||||
|
birlikte üretiyordu:
|
||||||
|
|
||||||
|
| Kutu | Olan | Neden |
|
||||||
|
|---|---|---|
|
||||||
|
| tikli | login ekranı | bizim config `/usr/lib/...` altına yazılıyordu → `/etc` altındaki canlı config'e **öncelikle yeniliyordu**; bir de `Session=plasmax11.desktop` **sabit** yazılıyordu, hedefte yoksa SDDM oturumu bulamıyor |
|
||||||
|
| boş | "oturum açıyor" | kutu boşken hiçbir şeye dokunulmuyordu → canlı config kopyalanıyor, kurulu sistem **canlı kullanıcıyla** (parolasız!) açılıyordu |
|
||||||
|
|
||||||
|
Kurallar:
|
||||||
|
- sddm config'i **`/etc/sddm.conf.d/10-yali-autologin.conf`** kanonik
|
||||||
|
dosyaya yazılır (en yüksek öncelik, deterministik kazanır).
|
||||||
|
ISO dosyaları **silinmez**, öncelikle ezilir.
|
||||||
|
- Oturum adı hedeften tespit edilir: `resolve_session()` →
|
||||||
|
`/usr/share/{wayland-sessions,xsessions}/*.desktop` taranır,
|
||||||
|
`pick_session_file()` seçer (saf + testli). Sabit eşleme yalnızca
|
||||||
|
**son çaredir**.
|
||||||
|
- Kutu boşken de kanonik dosya yazılır ama **`User=` boş** bırakılır —
|
||||||
|
SDDM bunu "kapalı" sayar, canlı config ezilir. lightdm'de
|
||||||
|
`autologin-user=`/`autologin-session=` satırları ayıklanır.
|
||||||
|
- DM bulunamazsa **yalnızca kutu tikliyken hata**; kapalıyken sessiz
|
||||||
|
geçilir (DM'siz sunucu kurulumunu öldürmemek için).
|
||||||
|
|
||||||
`ConfigureAutologinJob` ayrıca:
|
`ConfigureAutologinJob` ayrıca:
|
||||||
- sddm config dizini: `/usr/lib/sddm/sddm.conf.d` yoksa `/etc/sddm.conf.d`
|
- sddm config'i daima `/etc/sddm.conf.d/10-yali-autologin.conf` dosyasına
|
||||||
denir; ikisi de yoksa **hata** (sessizce yanlış yere yazmak yerine)
|
yazılır (dizin yoksa oluşturulur); eski `/usr/lib/...` + fallback
|
||||||
|
mantığı **kaldırıldı** (öncelik kaybı + yanlış dizin riski vardı)
|
||||||
- `session_desktop_file()` eşlemesini **tüm DM'lerde** kullanır
|
- `session_desktop_file()` eşlemesini **tüm DM'lerde** kullanır
|
||||||
(lightdm `autologin-session=plasma` yazıyordu; `.desktop` dosyası
|
(lightdm `autologin-session=plasma` yazıyordu; `.desktop` dosyası
|
||||||
adı bekler)
|
adı bekler)
|
||||||
@@ -495,6 +521,27 @@ mountpoint'i olan bölümü düzenlerken) liste **tamamen kilitlenmişti**.
|
|||||||
> (removable gibi) sorgulanmadığından emin ol. Sorgulanıyorsa probu
|
> (removable gibi) sorgulanmadığından emin ol. Sorgulanıyorsa probu
|
||||||
> dışarıdan al.
|
> dışarıdan al.
|
||||||
|
|
||||||
|
### ⭐ Yerelleşmiş CLI çıktısı ayrıştırılmaz (29 Eyl 2026, P0 veri kaybı)
|
||||||
|
|
||||||
|
`disk_label_is_gpt` parted çıktısındaki İngilizce `Partition Table`
|
||||||
|
satırını arıyordu. Ama parted yerelleşmiştir — canlı ISO'da satır
|
||||||
|
`Disk bölümü Tablosu: msdos` gelir. Ayrıştırıcı hiçbir diski
|
||||||
|
tanıyamadı → her disk "boş" sanıldı → `mktable` dolu diski **komple
|
||||||
|
sildi**. Boş disklerde zararsız olduğu için iki kurulum geçti, üçüncüde
|
||||||
|
(dolu diske ekleme) patladı.
|
||||||
|
|
||||||
|
Kurallar:
|
||||||
|
- İnsan-dili CLI çıktısı **daima `LC_ALL=C` ile** okunur
|
||||||
|
(`env LC_ALL=C parted …`). Makine-dili alternatif varsa (blkid
|
||||||
|
`PTTYPE` gibi) o tercih edilir.
|
||||||
|
- Türkçe başlık çekimlidir (`bölümü`, `bölümü`nün…): tam kalıp
|
||||||
|
eşleştirme ıskalar. Anahtar kelimeyle (`tablosu`) eşleştir + test
|
||||||
|
verisi olarak GERÇEK yerelleşmiş çıktıyı kullan.
|
||||||
|
- **Yıkıcı komutun kilidi olur:** `mktable` öncesi sysfs'ten fiziksel
|
||||||
|
bölüm sayılır; bölüm varken etiket okunamazsa **hata verilir**,
|
||||||
|
tabloya dokunulmaz. Ayrıştırıcı + locale + kilit üçü birlikte
|
||||||
|
kapatır; hiçbiri tek başına yeterli değildir.
|
||||||
|
|
||||||
### ⭐ "Biçimlendir" kutusu — olumlu çerçeve
|
### ⭐ "Biçimlendir" kutusu — olumlu çerçeve
|
||||||
|
|
||||||
Kullanıcı isteği: *"düzenle penceresindeki biçimlendirmeyi atla
|
Kullanıcı isteği: *"düzenle penceresindeki biçimlendirmeyi atla
|
||||||
|
|||||||
+1
-1
@@ -141,7 +141,7 @@ display_manager_title = "Display Manager Configuration"
|
|||||||
display_manager_description = "Select your display manager and desktop environment."
|
display_manager_description = "Select your display manager and desktop environment."
|
||||||
display_manager_select = "Display manager:"
|
display_manager_select = "Display manager:"
|
||||||
display_manager_de = "Desktop environment:"
|
display_manager_de = "Desktop environment:"
|
||||||
display_manager_autologin = "Automatic login"
|
display_manager_autologin = "Log in automatically"
|
||||||
display_manager_autologin_user = "Autologin user:"
|
display_manager_autologin_user = "Autologin user:"
|
||||||
display_manager_autologin_hint = "(first user)"
|
display_manager_autologin_hint = "(first user)"
|
||||||
display_manager_de_not_found = "(not detected)"
|
display_manager_de_not_found = "(not detected)"
|
||||||
|
|||||||
+1
-1
@@ -303,7 +303,7 @@ display_manager_title = "Görüntü Yöneticisi Yapılandırması"
|
|||||||
display_manager_description = "Görüntü yöneticinizi ve masaüstü ortamınızı seçin."
|
display_manager_description = "Görüntü yöneticinizi ve masaüstü ortamınızı seçin."
|
||||||
display_manager_select = "Görüntü yöneticisi:"
|
display_manager_select = "Görüntü yöneticisi:"
|
||||||
display_manager_de = "Masaüstü ortamı:"
|
display_manager_de = "Masaüstü ortamı:"
|
||||||
display_manager_autologin = "Otomatik oturum açma"
|
display_manager_autologin = "Otomatik oturum aç"
|
||||||
display_manager_autologin_user = "Otomatik oturum açılacak kullanıcı:"
|
display_manager_autologin_user = "Otomatik oturum açılacak kullanıcı:"
|
||||||
display_manager_autologin_hint = "(ilk kullanıcı)"
|
display_manager_autologin_hint = "(ilk kullanıcı)"
|
||||||
display_manager_de_not_found = "(algılanamadı)"
|
display_manager_de_not_found = "(algılanamadı)"
|
||||||
|
|||||||
+175
-46
@@ -4,8 +4,22 @@ use super::{Job, UiSender};
|
|||||||
///
|
///
|
||||||
/// Ekran yöneticisi (sddm/lightdm/gdm) yapılandırması ISO'da hazır olduğu
|
/// Ekran yöneticisi (sddm/lightdm/gdm) yapılandırması ISO'da hazır olduğu
|
||||||
/// için burada değiştirilmez; yalnızca kullanıcının seçtiği "otomatik oturum
|
/// için burada değiştirilmez; yalnızca kullanıcının seçtiği "otomatik oturum
|
||||||
/// açma" bilgisi mevcut konfigürasyona eklenir. Autologin kapalıysa hiçbir
|
/// açma" bilgisi mevcut konfigürasyona eklenir.
|
||||||
/// şeye dokunulmaz (ISO varsayılanında otomatik giriş yoktur).
|
///
|
||||||
|
/// ## Neden `/etc/sddm.conf.d/10-yali-autologin.conf`
|
||||||
|
///
|
||||||
|
/// ISO canlı kullanıcı için çalışan bir autologin config'i taşıyabilir.
|
||||||
|
/// `/usr/lib/...` altına yazılan drop-in, `/etc/...` altındaki canlı
|
||||||
|
/// config'e öncelik olarak YENİLİR — kutu tikliyken bile canlı config
|
||||||
|
/// kazanırdı. `/etc` altındaki kanonik dosya deterministik olarak kazanır.
|
||||||
|
///
|
||||||
|
/// ## Kapalıyken neden boş `User=` yazılır
|
||||||
|
///
|
||||||
|
/// Kutu boşken hiçbir şeye dokunulmamak, canlı ISO'nun autologin
|
||||||
|
/// config'ini olduğu gibi kopyalamak demekti: kurulu sistem canlı
|
||||||
|
/// kullanıcıyla (parolasız!) otomatik açılıyordu. Bu yüzden kapalı
|
||||||
|
/// durumda da kanonik dosya yazılır ama `User=` boş bırakılır — SDDM
|
||||||
|
/// bunu "autologin yok" sayar ve canlı config'i ezer.
|
||||||
pub struct ConfigureAutologinJob {
|
pub struct ConfigureAutologinJob {
|
||||||
pub mount: String,
|
pub mount: String,
|
||||||
pub display_manager: String,
|
pub display_manager: String,
|
||||||
@@ -19,19 +33,12 @@ impl Job for ConfigureAutologinJob {
|
|||||||
fn name(&self) -> &str { "job_autologin" }
|
fn name(&self) -> &str { "job_autologin" }
|
||||||
|
|
||||||
async fn run(&self, ui: &UiSender) -> Result<(), String> {
|
async fn run(&self, ui: &UiSender) -> Result<(), String> {
|
||||||
if !self.autologin {
|
|
||||||
ui.log("Otomatik oturum açma kapalı, değişiklik yapılmıyor.");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ekran yöneticisini SEÇMEK yerine TESPİT ediyoruz.
|
// Ekran yöneticisini SEÇMEK yerine TESPİT ediyoruz.
|
||||||
//
|
//
|
||||||
// YALI kurulu sistemi squashfs'ten kopyalar; ekran yöneticisi ISO'da
|
// YALI kurulu sistemi squashfs'ten kopyalar; ekran yöneticisi ISO'nun
|
||||||
// hazırdır. `state.display_manager` ise GlobalState'in "sddm"
|
// kendisidir; kullanıcı seçmez. Tespit çalışma zamanında yapılır çünkü
|
||||||
// varsayılanıdır ve kullanıcı tarafından değiştirilemez. Önceden
|
// `mount` dizini rsync'ten sonra tamdır (bu iş `CopyFilesJob`'dan
|
||||||
// bu sabite göre dallanılıyordu: ISO lightdm kullanıyorsa
|
// sonra çalışır).
|
||||||
// lightdm.conf'a hiç dokunulmaz, sddm.conf yazılır ve otomatik
|
|
||||||
// giriş sessizce çalışmazdı.
|
|
||||||
let dm = detect_display_manager(&self.mount).await;
|
let dm = detect_display_manager(&self.mount).await;
|
||||||
match &dm {
|
match &dm {
|
||||||
Some(found) if found != &self.display_manager => {
|
Some(found) if found != &self.display_manager => {
|
||||||
@@ -44,6 +51,11 @@ impl Job for ConfigureAutologinJob {
|
|||||||
ui.log(format!("Ekran yöneticisi: {}", found));
|
ui.log(format!("Ekran yöneticisi: {}", found));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
// Kapalıyken DM yoksa etkisizleştirilecek bir şey de yoktur.
|
||||||
|
if !self.autologin {
|
||||||
|
ui.log("Otomatik oturum açma kapalı, değişiklik yapılmıyor.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
return Err(
|
return Err(
|
||||||
"Hedef sistemde sddm/lightdm/gdm bulunamadı. \
|
"Hedef sistemde sddm/lightdm/gdm bulunamadı. \
|
||||||
Otomatik oturum açma uygulanamadı; kurulumda seçilen \
|
Otomatik oturum açma uygulanamadı; kurulumda seçilen \
|
||||||
@@ -53,42 +65,46 @@ impl Job for ConfigureAutologinJob {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.log(format!(
|
// Oturum adı HEDEFTEN tespit edilir (aşağıya bak). Eskiden sabit
|
||||||
"Otomatik oturum açma uygulanıyor: {} ({})",
|
// `plasmax11.desktop` yazılıyordu; hedefte bu dosya yoksa SDDM
|
||||||
self.username, self.username
|
// oturumu bulamaz ve kutu tikli olmasına rağmen login ekranına
|
||||||
));
|
// düşerdi — üstelik kutu boşken ISO'nun (doğru oturumlu) canlı
|
||||||
|
// config'i çalıştığı için davranış tam TERS görünüyordu.
|
||||||
let session = session_desktop_file(&self.desktop_environment);
|
let session = resolve_session(&self.mount, &self.desktop_environment).await;
|
||||||
|
ui.log(format!("Oturum dosyası: {}", session));
|
||||||
|
|
||||||
match dm.as_deref() {
|
match dm.as_deref() {
|
||||||
Some("sddm") => {
|
Some("sddm") => {
|
||||||
// ISO'nun /usr/lib/sddm/sddm.conf.d içeriğine dokunmadan
|
let conf = format!("{}/etc/sddm.conf.d/10-yali-autologin.conf", self.mount);
|
||||||
// ayrı bir drop-in dosyası ile autologin eklenir.
|
if !self.autologin {
|
||||||
let dir = format!("{}/usr/lib/sddm/sddm.conf.d", self.mount);
|
// Canlı config'i ezen, autologin'i kapatan kanonik dosya.
|
||||||
// Bu dizin yoksa ISO sddm kullanmıyordur demektir; sddm
|
// ISO dosyaları SİLİNMEZ, yalnızca öncelikle ezilir.
|
||||||
// genelde /etc/sddm.conf.d kullanır. İkisini de dene.
|
let content = "# Yali: otomatik oturum açma kapalı (canlı config'i ezer)\n\
|
||||||
let dir = if std::path::Path::new(&dir).is_dir() {
|
[Autologin]\nUser=\nSession=\n";
|
||||||
dir
|
if let Some(parent) = std::path::Path::new(&conf).parent() {
|
||||||
} else {
|
let _ = tokio::fs::create_dir_all(parent).await;
|
||||||
let alt = format!("{}/etc/sddm.conf.d", self.mount);
|
|
||||||
if std::path::Path::new(&alt).is_dir() {
|
|
||||||
alt
|
|
||||||
} else {
|
|
||||||
return Err(format!(
|
|
||||||
"sddm yapılandırma dizini bulunamadı ({}/usr/lib/sddm/sddm.conf.d, {}/etc/sddm.conf.d)",
|
|
||||||
self.mount, self.mount
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
};
|
tokio::fs::write(&conf, content)
|
||||||
tokio::fs::create_dir_all(&dir).await
|
.await
|
||||||
.map_err(|e| format!("{} oluşturulamadı: {}", dir, e))?;
|
.map_err(|e| format!("{} yazılamadı: {}", conf, e))?;
|
||||||
|
ui.log("Otomatik oturum açma kapalı, canlı config etkisizleştirildi.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
ui.log(format!(
|
||||||
|
"Otomatik oturum açma uygulanıyor: {} ({})",
|
||||||
|
self.username, self.username
|
||||||
|
));
|
||||||
|
if let Some(parent) = std::path::Path::new(&conf).parent() {
|
||||||
|
tokio::fs::create_dir_all(parent).await
|
||||||
|
.map_err(|e| format!("{} oluşturulamadı: {}", parent.display(), e))?;
|
||||||
|
}
|
||||||
let content = format!(
|
let content = format!(
|
||||||
"[Autologin]\nUser={}\nSession={}\n",
|
"# Yali tarafından oluşturuldu\n[Autologin]\nUser={}\nSession={}\n",
|
||||||
self.username, session
|
self.username, session
|
||||||
);
|
);
|
||||||
tokio::fs::write(format!("{}/autologin.conf", dir), content)
|
tokio::fs::write(&conf, content)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| format!("autologin.conf yazılamadı: {}", e))?;
|
.map_err(|e| format!("{} yazılamadı: {}", conf, e))?;
|
||||||
}
|
}
|
||||||
Some("lightdm") => {
|
Some("lightdm") => {
|
||||||
let dir = format!("{}/etc/lightdm", self.mount);
|
let dir = format!("{}/etc/lightdm", self.mount);
|
||||||
@@ -97,21 +113,40 @@ impl Job for ConfigureAutologinJob {
|
|||||||
let mut content = tokio::fs::read_to_string(&conf)
|
let mut content = tokio::fs::read_to_string(&conf)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
if !content.contains("[Seat:*]") {
|
// Önce bayat autologin satırlarını temizle (canlı config
|
||||||
content.push_str("[Seat:*]\n");
|
// kalıntısı da, kendi önceki yazımımız da).
|
||||||
|
content = content
|
||||||
|
.lines()
|
||||||
|
.filter(|l| {
|
||||||
|
let t = l.trim_start();
|
||||||
|
!(t.starts_with("autologin-user=")
|
||||||
|
|| t.starts_with("autologin-session="))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
if !content.ends_with('\n') {
|
||||||
|
content.push('\n');
|
||||||
}
|
}
|
||||||
if !content.contains("autologin-user=") {
|
if self.autologin {
|
||||||
|
if !content.contains("[Seat:*]") {
|
||||||
|
content.push_str("[Seat:*]\n");
|
||||||
|
}
|
||||||
// lightdm oturumu .desktop dosyası adı olarak bekler.
|
// lightdm oturumu .desktop dosyası adı olarak bekler.
|
||||||
// Ham DE adını ("plasma") yazmak, sddm/gdm'nin kullandığı
|
|
||||||
// eşlemenin aynısı değildi ve oturumu bulamazdı.
|
|
||||||
content.push_str(&format!("autologin-user={}\n", self.username));
|
content.push_str(&format!("autologin-user={}\n", self.username));
|
||||||
content.push_str(&format!("autologin-session={}\n", session));
|
content.push_str(&format!("autologin-session={}\n", session));
|
||||||
|
ui.log(format!("Otomatik oturum açma uygulanıyor: {}", self.username));
|
||||||
|
} else {
|
||||||
|
ui.log("Otomatik oturum açma kapalı, canlı config etkisizleştirildi.");
|
||||||
}
|
}
|
||||||
tokio::fs::write(&conf, content)
|
tokio::fs::write(&conf, content)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| format!("lightdm.conf yazılamadı: {}", e))?;
|
.map_err(|e| format!("lightdm.conf yazılamadı: {}", e))?;
|
||||||
}
|
}
|
||||||
Some("gdm") => {
|
Some("gdm") => {
|
||||||
|
if !self.autologin {
|
||||||
|
ui.log("Otomatik oturum açma kapalı, değişiklik yapılmıyor.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
let dir = format!("{}/etc/gdm", self.mount);
|
let dir = format!("{}/etc/gdm", self.mount);
|
||||||
let _ = tokio::fs::create_dir_all(&dir).await;
|
let _ = tokio::fs::create_dir_all(&dir).await;
|
||||||
let conf = format!("{}/custom.conf", dir);
|
let conf = format!("{}/custom.conf", dir);
|
||||||
@@ -159,6 +194,67 @@ fn session_desktop_file(de: &str) -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Hedef sistemde GERÇEKTEN var olan oturum dosyasını seçer (saf).
|
||||||
|
///
|
||||||
|
/// `wayland` ve `x11`, hedefteki `/usr/share/wayland-sessions` ve
|
||||||
|
/// `/usr/share/xsessions` içerik listesidir. Önce DE anahtarını içeren
|
||||||
|
/// dosya aranır (wayland öncelikli — ISO varsayılanı genelde odur),
|
||||||
|
/// bulunamazsa alfabetik ilk dosya, liste boşsa sabit eşleme döner.
|
||||||
|
///
|
||||||
|
/// Sabit eşleme tek başına YETERSİZDİR: hedefte `plasmax11.desktop`
|
||||||
|
/// yoksa (yalnızca wayland kurulu ISO) SDDM oturumu bulamaz ve kutu
|
||||||
|
/// tikli olmasına rağmen login ekranına düşer.
|
||||||
|
fn pick_session_file(de: &str, wayland: &[String], x11: &[String]) -> String {
|
||||||
|
let key = match de {
|
||||||
|
"plasma" => "plasma",
|
||||||
|
"xfce" => "xfce",
|
||||||
|
"gnome" => "gnome",
|
||||||
|
"cinnamon" => "cinnamon",
|
||||||
|
"budgie" => "budgie",
|
||||||
|
"lxde" => "lxde",
|
||||||
|
"lxqt" => "lxqt",
|
||||||
|
"mate" => "mate",
|
||||||
|
"lumina" => "lumina",
|
||||||
|
_ => "plasma",
|
||||||
|
};
|
||||||
|
for list in [wayland, x11] {
|
||||||
|
let mut sorted = list.to_vec();
|
||||||
|
sorted.sort();
|
||||||
|
for f in &sorted {
|
||||||
|
if f.to_lowercase().contains(key) {
|
||||||
|
return f.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut all: Vec<&String> = wayland.iter().chain(x11.iter()).collect();
|
||||||
|
all.sort();
|
||||||
|
if let Some(f) = all.first() {
|
||||||
|
return (*f).clone();
|
||||||
|
}
|
||||||
|
session_desktop_file(de).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hedef `mount` altındaki oturum dizinlerini okuyup dosya adı seçer.
|
||||||
|
async fn resolve_session(mount: &str, de: &str) -> String {
|
||||||
|
async fn list(mount: &str, sub: &str) -> Vec<String> {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
let mut rd = match tokio::fs::read_dir(format!("{}/usr/share/{}", mount, sub)).await {
|
||||||
|
Ok(rd) => rd,
|
||||||
|
Err(_) => return out,
|
||||||
|
};
|
||||||
|
while let Ok(Some(e)) = rd.next_entry().await {
|
||||||
|
let name = e.file_name().to_string_lossy().to_string();
|
||||||
|
if name.ends_with(".desktop") {
|
||||||
|
out.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
let wayland = list(mount, "wayland-sessions").await;
|
||||||
|
let x11 = list(mount, "xsessions").await;
|
||||||
|
pick_session_file(de, &wayland, &x11)
|
||||||
|
}
|
||||||
|
|
||||||
/// Hedef sistemde hangi ekran yöneticisinin KURULU olduğunu tespit eder.
|
/// Hedef sistemde hangi ekran yöneticisinin KURULU olduğunu tespit eder.
|
||||||
///
|
///
|
||||||
/// YALI squashfs'ten kopyaladığı için ekran yöneticisi ISO'nun kendisidir;
|
/// YALI squashfs'ten kopyaladığı için ekran yöneticisi ISO'nun kendisidir;
|
||||||
@@ -219,4 +315,37 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn s(v: &[&str]) -> Vec<String> {
|
||||||
|
v.iter().map(|x| x.to_string()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn picker_prefers_real_wayland_session() {
|
||||||
|
// Yalnızca wayland kurulu ISO: sabit `plasmax11.desktop` yok.
|
||||||
|
// Eskiden bu dosya adı körü körüne yazılıyor, SDDM oturumu
|
||||||
|
// bulamayıp login ekranına düşüyordu.
|
||||||
|
let got = pick_session_file("plasma", &s(&["plasma.desktop"]), &s(&[]));
|
||||||
|
assert_eq!(got, "plasma.desktop");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn picker_matches_de_key_case_insensitively() {
|
||||||
|
let got = pick_session_file(
|
||||||
|
"xfce",
|
||||||
|
&s(&[]),
|
||||||
|
&s(&["xfce.desktop", "plasma.desktop"]),
|
||||||
|
);
|
||||||
|
assert_eq!(got, "xfce.desktop");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn picker_falls_back_to_first_file_then_static_map() {
|
||||||
|
// DE'ye uyan yoksa alfabetik ilk dosya.
|
||||||
|
let got = pick_session_file("mate", &s(&[]), &s(&["zebra.desktop", "aaa.desktop"]));
|
||||||
|
assert_eq!(got, "aaa.desktop");
|
||||||
|
// Hiç dosya yoksa sabit eşleme (son çare).
|
||||||
|
let got = pick_session_file("plasma", &[], &[]);
|
||||||
|
assert_eq!(got, "plasmax11.desktop");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+294
-29
@@ -298,7 +298,145 @@ fn read_physical_extents(disk: &str) -> Result<Vec<PhysicalExtent>, String> {
|
|||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `parted` komutlarını çalıştırmak için özel sarmalayıcı.
|
/// `parted print` çıktısından disk etiketini çözer (saf, test edilebilir).
|
||||||
|
///
|
||||||
|
/// `parted -s /dev/sdx print` çıktısı `Partition Table: gpt` veya
|
||||||
|
/// `Partition Table: msdos` satırı içerir. Etiketsiz/boş diskte bu satır
|
||||||
|
/// yerine `unrecognised disk label` yazar → `None` döner, çağıran yeni
|
||||||
|
/// etiket oluşturur.
|
||||||
|
///
|
||||||
|
/// ⚠️ parted çıktısı YERELLEŞTİRİLMİŞTİR (tr: `Disk bölümü Tablosu`)!
|
||||||
|
/// Üretim kodu parted'i `LC_ALL=C` ile çalıştırır; bu ayrıştırıcı yine
|
||||||
|
/// de Türkçe anahtar kelimeleri de tanır (kemer + askı).
|
||||||
|
pub fn parse_parted_label(output: &str) -> Option<bool> {
|
||||||
|
for line in output.lines() {
|
||||||
|
let l = line.to_lowercase();
|
||||||
|
// Türkçe başlık çekimlidir (`Disk bölümü Tablosu`) — tam kalıp
|
||||||
|
// aramak ek yüzünden ıskalar, `tablosu` yeterlidir.
|
||||||
|
let is_label_line = l.contains("partition table") || l.contains("tablosu");
|
||||||
|
if is_label_line {
|
||||||
|
// Değer anahtarları (gpt/msdos) yerelleşmez; yalnızca
|
||||||
|
// satır başlığı (`Partition Table` / `Disk bölümü Tablosu`)
|
||||||
|
// dile göre değişir.
|
||||||
|
if l.contains("gpt") {
|
||||||
|
return Some(true);
|
||||||
|
}
|
||||||
|
if l.contains("msdos") || l.contains("dos") || l.contains("mbr") {
|
||||||
|
return Some(false);
|
||||||
|
}
|
||||||
|
// Etiket satırı var ama tür tanınmıyor (örn. "loop"):
|
||||||
|
// temkinli None.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if l.contains("unrecognised disk label") || l.contains("unrecognized disk label") {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Diskin bölüm tablosu etiketini okur: `Some(true)` = GPT, `Some(false)` =
|
||||||
|
/// msdos/MBR, `None` = etiketsiz/boş/okunamadı.
|
||||||
|
///
|
||||||
|
/// parted **daima `LC_ALL=C` ile** çalıştırılır: çıktısı yerelleşmiştir
|
||||||
|
/// (tr: `Disk bölümü Tablosu`) ve İngilizce ayrıştırıcı Türkçe satırı
|
||||||
|
/// tanıyamaz. 29 Eyl 2026'da bu, dolu diskin "boş" sanılıp `mktable`
|
||||||
|
/// ile SİLİNMESİNE yol açtı (P0 veri kaybı).
|
||||||
|
async fn disk_label_is_gpt(disk: &str) -> Option<bool> {
|
||||||
|
if DEMO_MODE.load(Ordering::Relaxed) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let out = Command::new("env")
|
||||||
|
.args(["LC_ALL=C", "parted", "-s", disk, "print"])
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.ok()?;
|
||||||
|
let combined = format!(
|
||||||
|
"{}{}",
|
||||||
|
String::from_utf8_lossy(&out.stdout),
|
||||||
|
String::from_utf8_lossy(&out.stderr)
|
||||||
|
);
|
||||||
|
parse_parted_label(&combined)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `mkpart`'ın parted söz dizimi tablo etiketine göre değişir:
|
||||||
|
///
|
||||||
|
/// - GPT: `mkpart <NAME> <FS> START END` (NAME serbest metin, boş OLAMAZ)
|
||||||
|
/// - msdos: `mkpart <TYPE> <FS> START END` (TYPE = primary|logical)
|
||||||
|
///
|
||||||
|
/// Eskiden firmware (`/sys/firmware/efi`) tablo türü sanılıyordu: UEFI
|
||||||
|
/// makinede msdos diskli kurulum `mkpart "" ext4 …` çalıştırıp
|
||||||
|
/// "Bir disk bölümü türü bekleniyor" hatasıyla düşüyordu.
|
||||||
|
pub fn mkpart_first_arg(is_gpt: bool, slot_num: u32) -> &'static str {
|
||||||
|
if is_gpt {
|
||||||
|
// GPT'de bu alan bölüm ADI'dır; çağıran `gpt_part_name` kullanır.
|
||||||
|
// Buraya düşülmemeli — imza simetrisi için korunuyor.
|
||||||
|
"yali"
|
||||||
|
} else if slot_num >= 5 {
|
||||||
|
"logical"
|
||||||
|
} else {
|
||||||
|
"primary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GPT bölüm adı: mountpoint'ten türetilir.
|
||||||
|
///
|
||||||
|
/// `/` için ad BOŞ bırakılır (kullanıcı isteği — `root` adı `/root`
|
||||||
|
/// ile karışıyordu). parted CLI boş ada izin vermez (boş argümanı
|
||||||
|
/// sessizce atlayıp fs ipucunu ad yapar!), bu yüzden `/` önce `/`
|
||||||
|
/// adıyla oluşturulup `sfdisk --part-label <disk> <N> ""` ile
|
||||||
|
/// sıfırlanır (`should_blank_gpt_name`).
|
||||||
|
pub fn gpt_part_name(mountpoint: &str, fstype: &FsType, slot_num: u32) -> String {
|
||||||
|
if mountpoint == "/boot/efi" {
|
||||||
|
return "EFI".to_string();
|
||||||
|
}
|
||||||
|
match mountpoint {
|
||||||
|
"/" => "/".to_string(),
|
||||||
|
"/home" => "home".to_string(),
|
||||||
|
"[SWAP]" | "swap" => "swap".to_string(),
|
||||||
|
m if !m.is_empty() => {
|
||||||
|
let s: String = m
|
||||||
|
.trim_start_matches('/')
|
||||||
|
.chars()
|
||||||
|
.map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
|
||||||
|
.collect();
|
||||||
|
if s.is_empty() {
|
||||||
|
format!("yali{}", slot_num)
|
||||||
|
} else {
|
||||||
|
s.chars().take(32).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => match fstype {
|
||||||
|
FsType::Swap => "swap".to_string(),
|
||||||
|
FsType::Fat32 => "EFI".to_string(),
|
||||||
|
_ => format!("yali{}", slot_num),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adı boş bırakılacak GPT bölümleri: yalnızca `/`.
|
||||||
|
///
|
||||||
|
/// Diğerleri (`/home` → `home`, takas → `swap`…) adını korur; boş ad
|
||||||
|
/// yalnızca `/root` karışıklığını önlemek için `/`'ye özeldir.
|
||||||
|
pub fn should_blank_gpt_name(mountpoint: &str) -> bool {
|
||||||
|
mountpoint == "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// parted'ın anladığı dosya sistemi ipucu.
|
||||||
|
pub fn parted_fs_label(fstype: &FsType) -> &'static str {
|
||||||
|
match 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 ipucu + ardından `set N lvm on`.
|
||||||
|
FsType::Lvm => "ext4",
|
||||||
|
FsType::LvmLv => "ext4",
|
||||||
|
}
|
||||||
|
}
|
||||||
///
|
///
|
||||||
/// `parted -s` canlı sistemde kullanımda olan bir disk üzerinde bölüm
|
/// `parted -s` canlı sistemde kullanımda olan bir disk üzerinde bölüm
|
||||||
/// tablosunu kernel'e bildiremeyince exit 1 döndürür; ancak değişiklikler
|
/// tablosunu kernel'e bildiremeyince exit 1 döndürür; ancak değişiklikler
|
||||||
@@ -521,10 +659,15 @@ impl Job for PartitionJob {
|
|||||||
if self.plan.efi_mb > 0 {
|
if self.plan.efi_mb > 0 {
|
||||||
let end_mb = next_mb + self.plan.efi_mb;
|
let end_mb = next_mb + self.plan.efi_mb;
|
||||||
ui.log(format!("EFI bölümü oluşturuluyor: {}–{} MiB", next_mb, end_mb));
|
ui.log(format!("EFI bölümü oluşturuluyor: {}–{} MiB", next_mb, end_mb));
|
||||||
run_parted(&[
|
let start_s = format!("{}MiB", next_mb);
|
||||||
"-s", disk, "mkpart", "fat32",
|
let end_s = format!("{}MiB", end_mb);
|
||||||
&format!("{}MiB", next_mb), &format!("{}MiB", end_mb),
|
if is_gpt {
|
||||||
]).await?;
|
// GPT: mkpart <AD> <FS> START END
|
||||||
|
run_parted(&["-s", disk, "mkpart", "EFI", "fat32", &start_s, &end_s]).await?;
|
||||||
|
} else {
|
||||||
|
// msdos: mkpart <TÜR> <FS> START END
|
||||||
|
run_parted(&["-s", disk, "mkpart", "primary", "fat32", &start_s, &end_s]).await?;
|
||||||
|
}
|
||||||
run_parted(&["-s", disk, "set", "1", "esp", "on"]).await?;
|
run_parted(&["-s", disk, "set", "1", "esp", "on"]).await?;
|
||||||
next_mb = end_mb;
|
next_mb = end_mb;
|
||||||
}
|
}
|
||||||
@@ -533,10 +676,13 @@ impl Job for PartitionJob {
|
|||||||
let swap_end = next_mb + self.plan.swap_mb;
|
let swap_end = next_mb + self.plan.swap_mb;
|
||||||
let swap_num: u32 = if self.plan.efi_mb > 0 { 2 } else { 1 };
|
let swap_num: u32 = if self.plan.efi_mb > 0 { 2 } else { 1 };
|
||||||
ui.log(format!("Swap bölümü oluşturuluyor: {}–{} MiB", next_mb, swap_end));
|
ui.log(format!("Swap bölümü oluşturuluyor: {}–{} MiB", next_mb, swap_end));
|
||||||
run_parted(&[
|
{
|
||||||
"-s", disk, "mkpart", "linux-swap",
|
let start_s = format!("{}MiB", next_mb);
|
||||||
&format!("{}MiB", next_mb), &format!("{}MiB", swap_end),
|
let end_s = format!("{}MiB", swap_end);
|
||||||
]).await?;
|
// Otomatik planda en fazla 3 bölüm var → msdos'ta daima primary.
|
||||||
|
let (first, fs) = if is_gpt { ("swap", "linux-swap") } else { ("primary", "linux-swap") };
|
||||||
|
run_parted(&["-s", disk, "mkpart", first, fs, &start_s, &end_s]).await?;
|
||||||
|
}
|
||||||
next_mb = swap_end;
|
next_mb = swap_end;
|
||||||
|
|
||||||
// 4. Kök bölümü (kalan alan)
|
// 4. Kök bölümü (kalan alan)
|
||||||
@@ -547,10 +693,19 @@ impl Job for PartitionJob {
|
|||||||
// 2048 sektörün katıdır, hizalama sorunu olmaz.
|
// 2048 sektörün katıdır, hizalama sorunu olmaz.
|
||||||
let root_num: u32 = if self.plan.efi_mb > 0 { 3 } else { 2 };
|
let root_num: u32 = if self.plan.efi_mb > 0 { 3 } else { 2 };
|
||||||
ui.log(format!("Kök bölümü oluşturuluyor: {}MiB – disk sonu", next_mb));
|
ui.log(format!("Kök bölümü oluşturuluyor: {}MiB – disk sonu", next_mb));
|
||||||
run_parted(&[
|
{
|
||||||
"-s", disk, "mkpart", "ext4",
|
let start_s = format!("{}MiB", next_mb);
|
||||||
&format!("{}MiB", next_mb), "100%",
|
let (first, fs) = if is_gpt { ("/", "ext4") } else { ("primary", "ext4") };
|
||||||
]).await?;
|
run_parted(&["-s", disk, "mkpart", first, fs, &start_s, "100%"]).await?;
|
||||||
|
// GPT'de `/` adı boş kalır (sfdisk ile; parted boş ada izin
|
||||||
|
// vermez). Kozmetiktir — başarısız olursa ad `/` kalır.
|
||||||
|
if is_gpt {
|
||||||
|
let slot = root_num.to_string();
|
||||||
|
if run_cmd("sfdisk", &["--part-label", disk, &slot, ""]).await.is_err() {
|
||||||
|
ui.log("Bölüm adı temizlenemedi (ad '/' kalıyor).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 4.5. Tüm mkpart bitti; kernel'e bildir ve aygıt düğümlerini bekle
|
// 4.5. Tüm mkpart bitti; kernel'e bildir ve aygıt düğümlerini bekle
|
||||||
sync_partition_table(disk).await;
|
sync_partition_table(disk).await;
|
||||||
@@ -786,9 +941,7 @@ impl Job for CustomPartitionJob {
|
|||||||
fn name(&self) -> &str { "job_custom_partition" }
|
fn name(&self) -> &str { "job_custom_partition" }
|
||||||
|
|
||||||
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 firmware_uefi = std::path::Path::new("/sys/firmware/efi").exists();
|
||||||
// MBR'de "primary" zorunludur, GPT'de ise bu alan isim (label) alanıdır.
|
|
||||||
let part_type_or_name = if is_uefi { "" } else { "primary" };
|
|
||||||
|
|
||||||
// UI'ın hesapladığı aygıt yolu → kurulum sonrası GERÇEK aygıt yolu.
|
// UI'ın hesapladığı aygıt yolu → kurulum sonrası GERÇEK aygıt yolu.
|
||||||
//
|
//
|
||||||
@@ -836,6 +989,45 @@ impl Job for CustomPartitionJob {
|
|||||||
// Bağlamaları ayır, swap kapat
|
// Bağlamaları ayır, swap kapat
|
||||||
prepare_disk(disk, ui).await;
|
prepare_disk(disk, ui).await;
|
||||||
|
|
||||||
|
// ── TABLO ETİKETİ: her disk kendi etiketine göre yazılır.
|
||||||
|
//
|
||||||
|
// REGRESYON (29 Eyl 2026): etiket firmware'den tahmin ediliyordu
|
||||||
|
// (UEFI → GPT varsayımı). UEFI makinede msdos etiketli veya
|
||||||
|
// TAMAMEN BOŞ (etiketsiz) disk `mkpart "" ext4 …` çalıştırıp
|
||||||
|
// "Bir disk bölümü türü bekleniyor" ile düşüyordu.
|
||||||
|
// Boş diskte parted "unrecognised disk label" verir → burada
|
||||||
|
// firmware'e göre taze etiket oluşturulur.
|
||||||
|
//
|
||||||
|
// ⭐ GÜVENLİK KİLİDİ (29 Eyl 2026, P0): `mktable` YALNIZCA
|
||||||
|
// fiziksel bölümü OLMAYAN diske uygulanır. Etiket
|
||||||
|
// okunamadı (`None`) ama diskte bölüm VARSA tabloyu
|
||||||
|
// sıfırlamak yerine HATA verilir — veri kaybı engellenir.
|
||||||
|
// (Türkçe parted çıktısı İngilizce ayrıştırıcıyı atlatıp
|
||||||
|
// dolu diski "boş" göstermişti; `LC_ALL=C` + bu kilit
|
||||||
|
// birlikte kapatır.)
|
||||||
|
let is_gpt = match disk_label_is_gpt(disk).await {
|
||||||
|
Some(v) => v,
|
||||||
|
None => {
|
||||||
|
let physical_check = read_physical_extents(disk).unwrap_or_default();
|
||||||
|
if !physical_check.is_empty() {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: bölüm tablosu okunamadı ama diskte {} bölüm var; \
|
||||||
|
veri kaybını önlemek için diske dokunulmuyor",
|
||||||
|
disk,
|
||||||
|
physical_check.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let label = if firmware_uefi { "gpt" } else { "msdos" };
|
||||||
|
ui.log(format!(
|
||||||
|
"{} üzerinde bölüm tablosu yok, yeni {} tablosu oluşturuluyor",
|
||||||
|
disk, label
|
||||||
|
));
|
||||||
|
run_parted(&["-s", disk, "mktable", label]).await?;
|
||||||
|
sync_partition_table(disk).await;
|
||||||
|
firmware_uefi
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// ── FAZ A: SİLMEK İŞARETLİ BÖLÜMLER (büyükten küçüğe numara) ──
|
// ── FAZ A: SİLMEK İŞARETLİ BÖLÜMLER (büyükten küçüğe numara) ──
|
||||||
let mut del_sorted = destroys.clone();
|
let mut del_sorted = destroys.clone();
|
||||||
del_sorted.sort_unstable_by(|a, b| b.cmp(a));
|
del_sorted.sort_unstable_by(|a, b| b.cmp(a));
|
||||||
@@ -885,39 +1077,54 @@ impl Job for CustomPartitionJob {
|
|||||||
let part = creates[plan.part_index];
|
let part = creates[plan.part_index];
|
||||||
// Yeni bölüm: UI'ın tahmin ettiği yol → gerçek yol.
|
// Yeni bölüm: UI'ın tahmin ettiği yol → gerçek yol.
|
||||||
device_map.insert(part.device.clone(), plan.device.clone());
|
device_map.insert(part.device.clone(), plan.device.clone());
|
||||||
let fs_label = match part.fstype {
|
let fs_label = parted_fs_label(&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,
|
|
||||||
};
|
|
||||||
|
|
||||||
// DİKKAT: parted "MB" sonekini onalık (10^6) okur;
|
// DİKKAT: parted "MB" sonekini onalık (10^6) okur;
|
||||||
// hesaplarımız binary (2^20) olduğu için "MiB" zorunlu.
|
// hesaplarımız binary (2^20) olduğu için "MiB" zorunlu.
|
||||||
// Tamsayı MiB = 2048 sektörün katı → parted hizalama
|
// Tamsayı MiB = 2048 sektörün katı → parted hizalama
|
||||||
// hatası vermez.
|
// hatası vermez.
|
||||||
|
//
|
||||||
|
// Söz dizimi etikete göre değişir:
|
||||||
|
// GPT: mkpart <AD> <FS> START END (AD boş olamaz)
|
||||||
|
// msdos: mkpart <TÜR> <FS> START END (primary|logical)
|
||||||
let start_str = format!("{}MiB", plan.start_mb);
|
let start_str = format!("{}MiB", plan.start_mb);
|
||||||
let end_str = format!("{}MiB", plan.end_mb);
|
let end_str = format!("{}MiB", plan.end_mb);
|
||||||
let slot_str = plan.num.to_string();
|
let slot_str = plan.num.to_string();
|
||||||
|
let first_arg: String = if is_gpt {
|
||||||
|
gpt_part_name(&part.mountpoint, &part.fstype, plan.num)
|
||||||
|
} else {
|
||||||
|
mkpart_first_arg(false, plan.num).to_string()
|
||||||
|
};
|
||||||
|
|
||||||
ui.log(format!(
|
ui.log(format!(
|
||||||
"{} üzerinde yeni bölüm oluşturuluyor: {} - {} ({}, slot {})",
|
"{} üzerinde yeni bölüm oluşturuluyor: {} - {} ({}, slot {})",
|
||||||
disk, start_str, end_str, fs_label, plan.num
|
disk, start_str, end_str, fs_label, plan.num
|
||||||
));
|
));
|
||||||
run_parted(&[
|
run_parted(&[
|
||||||
"-s", disk, "mkpart", part_type_or_name, fs_label,
|
"-s", disk, "mkpart", &first_arg, fs_label,
|
||||||
&start_str, &end_str
|
&start_str, &end_str
|
||||||
]).await?;
|
]).await?;
|
||||||
|
|
||||||
if part.mountpoint == "/boot/efi" || part.fstype == FsType::Fat32 {
|
if part.mountpoint == "/boot/efi" || part.fstype == FsType::Fat32 {
|
||||||
run_parted(&["-s", disk, "set", &slot_str, "esp", "on"]).await?;
|
run_parted(&["-s", disk, "set", &slot_str, "esp", "on"]).await?;
|
||||||
}
|
}
|
||||||
|
if part.fstype == FsType::Lvm {
|
||||||
|
run_parted(&["-s", disk, "set", &slot_str, "lvm", "on"]).await?;
|
||||||
|
}
|
||||||
|
// `/` adı boş kalır: parted boş ada izin vermez, sfdisk
|
||||||
|
// ile sıfırlanır. Kozmetik adımdır — başarısız olursa
|
||||||
|
// kurulum DURMAZ, ad `/` olarak kalır.
|
||||||
|
if is_gpt && should_blank_gpt_name(&part.mountpoint) {
|
||||||
|
match run_cmd("sfdisk", &["--part-label", disk, &slot_str, ""]).await {
|
||||||
|
Ok(()) => ui.log(format!(
|
||||||
|
"{} bölüm adı temizlendi (slot {})", plan.device, plan.num
|
||||||
|
)),
|
||||||
|
Err(e) => ui.log(format!(
|
||||||
|
"Bölüm adı temizlenemedi ({}, ad '/' kalıyor): {}",
|
||||||
|
plan.device, e
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sync_partition_table(disk).await;
|
sync_partition_table(disk).await;
|
||||||
}
|
}
|
||||||
@@ -1489,4 +1696,62 @@ mod layout_tests {
|
|||||||
.expect("planlanmalı");
|
.expect("planlanmalı");
|
||||||
assert_eq!(planned[0].device, "/dev/nvme0n1p2");
|
assert_eq!(planned[0].device, "/dev/nvme0n1p2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── mkpart söz dizimi (29 Eyl 2026 regresyonu) ──
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn msdos_first_arg_is_primary_then_logical() {
|
||||||
|
assert_eq!(mkpart_first_arg(false, 1), "primary");
|
||||||
|
assert_eq!(mkpart_first_arg(false, 4), "primary");
|
||||||
|
assert_eq!(mkpart_first_arg(false, 5), "logical");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gpt_names_are_never_empty() {
|
||||||
|
use crate::installer::FsType;
|
||||||
|
assert_eq!(gpt_part_name("/boot/efi", &FsType::Fat32, 1), "EFI");
|
||||||
|
assert_eq!(gpt_part_name("/", &FsType::Ext4, 2), "/");
|
||||||
|
assert_eq!(gpt_part_name("/home", &FsType::Ext4, 1), "home");
|
||||||
|
// Kullanıcının senaryosu: boş diske /home + ext4.
|
||||||
|
assert_eq!(gpt_part_name("/home", &FsType::Ext4, 1), "home");
|
||||||
|
assert!(!gpt_part_name("", &FsType::Ext4, 3).is_empty());
|
||||||
|
assert!(!gpt_part_name("", &FsType::Swap, 3).is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn only_root_gets_blanked_name() {
|
||||||
|
assert!(should_blank_gpt_name("/"));
|
||||||
|
assert!(!should_blank_gpt_name("/home"));
|
||||||
|
assert!(!should_blank_gpt_name("/boot/efi"));
|
||||||
|
assert!(!should_blank_gpt_name("swap"));
|
||||||
|
assert!(!should_blank_gpt_name(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parted_label_parsing() {
|
||||||
|
assert_eq!(
|
||||||
|
parse_parted_label("Model: ATA\nPartition Table: gpt\n"),
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_parted_label("Partition Table: msdos\n"),
|
||||||
|
Some(false)
|
||||||
|
);
|
||||||
|
// GERÇEK Türkçe parted çıktısı (canlı ISO'dan): satır başlığı
|
||||||
|
// yerelleşir, değerler (gpt/msdos) yerelleşmez.
|
||||||
|
assert_eq!(
|
||||||
|
parse_parted_label("Disk /tmp/opencode/parttest/t.img: 67,1MB\nDisk bölümü Tablosu: gpt\n"),
|
||||||
|
Some(true)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_parted_label("Disk bölümü Tablosu: msdos\n"),
|
||||||
|
Some(false)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_parted_label("Error: /dev/sdc: unrecognised disk label\n"),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
// Boş disk: etiket satırı yok → None (çağıran mktable yapar).
|
||||||
|
assert_eq!(parse_parted_label(""), None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user