Skip to content

Commit e859e56

Browse files
committed
resolve host home path
1 parent 45dedaf commit e859e56

File tree

3 files changed

+136
-84
lines changed

3 files changed

+136
-84
lines changed

src/create_distrobox_dialog.rs

Lines changed: 95 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use adw::prelude::*;
22
use adw::subclass::prelude::*;
33
use gtk::gio::File;
44
use gtk::{gio, glib};
5+
use tracing::error;
56

67
use crate::distrobox::{self, CreateArgName, CreateArgs, Error};
78
use crate::root_store::RootStore;
@@ -15,7 +16,7 @@ use gtk::glib::{derived_properties, Properties};
1516

1617
pub enum FileRowSelection {
1718
File,
18-
Folder
19+
Folder,
1920
}
2021
mod imp {
2122
use super::*;
@@ -41,7 +42,6 @@ mod imp {
4142
pub init_row: adw::SwitchRow,
4243
pub volume_rows: Rc<RefCell<Vec<adw::EntryRow>>>,
4344
pub scrolled_window: gtk::ScrolledWindow,
44-
pub current_create_args: RefCell<CreateArgs>,
4545
}
4646

4747
#[derived_properties]
@@ -50,7 +50,6 @@ mod imp {
5050
self.obj().set_title("Create a Distrobox");
5151
self.obj().set_content_width(480);
5252

53-
5453
let toolbar_view = adw::ToolbarView::new();
5554
let header = adw::HeaderBar::new();
5655

@@ -99,24 +98,29 @@ mod imp {
9998
self.image_row.set_use_subtitle(true);
10099

101100
let obj = self.obj().clone();
102-
let home_row = self.obj().build_file_row("Select Home Directory", FileRowSelection::Folder, move |path| {
103-
obj.set_home_folder(Some(path.display().to_string()));
104-
});
101+
let home_row = self.obj().build_file_row(
102+
"Select Home Directory",
103+
FileRowSelection::Folder,
104+
move |path| {
105+
obj.set_home_folder(Some(path.display().to_string()));
106+
},
107+
);
105108
self.home_row_expander.set_title("Custom Home Directory");
106109
self.home_row_expander.set_show_enable_switch(true);
107110
self.home_row_expander.set_enable_expansion(false);
108111
self.home_row_expander.add_row(&home_row);
109112
let obj = self.obj().clone();
110-
self.home_row_expander.connect_enable_expansion_notify(clone!(
111-
#[weak]
112-
home_row,
113-
move |expander| {
114-
if !expander.enables_expansion() {
115-
obj.set_home_folder(None::<&str>);
113+
self.home_row_expander
114+
.connect_enable_expansion_notify(clone!(
115+
#[weak]
116+
home_row,
117+
move |expander| {
118+
if !expander.enables_expansion() {
119+
obj.set_home_folder(None::<&str>);
120+
}
121+
home_row.set_subtitle(obj.home_folder().as_deref().unwrap_or(""));
116122
}
117-
home_row.set_subtitle(obj.home_folder().as_deref().unwrap_or(""));
118-
}
119-
));
123+
));
120124

121125
let nvidia_row = adw::SwitchRow::new();
122126
nvidia_row.set_title("NVIDIA Support");
@@ -142,13 +146,13 @@ mod imp {
142146
#[weak]
143147
obj,
144148
move |_| {
145-
let res = obj.update_create_args();
146-
obj.update_errors(&res);
147-
if let Ok(()) = res {
148-
obj.root_store().create_container(
149-
obj.imp().current_create_args.borrow().clone(),
150-
);
151-
}
149+
glib::MainContext::ref_thread_default().spawn_local(async move {
150+
let res = obj.extract_create_args().await;
151+
obj.update_errors(&res);
152+
if let Ok(create_args) = res {
153+
obj.root_store().create_container(create_args);
154+
}
155+
});
152156
}
153157
));
154158
create_btn.add_css_class("suggested-action");
@@ -169,9 +173,13 @@ mod imp {
169173
assemble_group.set_description(Some("Create a container from an assemble file"));
170174

171175
let obj = self.obj().clone();
172-
let file_row = self.obj().build_file_row("Select Assemble File", FileRowSelection::File, move |path| {
173-
obj.set_assemble_file(Some(path.display().to_string()));
174-
});
176+
let file_row = self.obj().build_file_row(
177+
"Select Assemble File",
178+
FileRowSelection::File,
179+
move |path| {
180+
obj.set_assemble_file(Some(path.display().to_string()));
181+
},
182+
);
175183
assemble_group.add(&file_row);
176184
assemble_page.append(&assemble_group);
177185

@@ -202,8 +210,6 @@ mod imp {
202210
create_btn.set_sensitive(obj.assemble_file().is_some());
203211
});
204212

205-
206-
207213
// Create page for URL creation
208214
let url_page = gtk::Box::new(gtk::Orientation::Vertical, 12);
209215
url_page.set_margin_start(12);
@@ -246,7 +252,8 @@ mod imp {
246252
#[weak]
247253
obj,
248254
move |_| {
249-
obj.root_store().assemble_container(&obj.assemble_url().as_ref().unwrap());
255+
obj.root_store()
256+
.assemble_container(&obj.assemble_url().as_ref().unwrap());
250257
obj.close();
251258
}
252259
));
@@ -255,7 +262,6 @@ mod imp {
255262
create_btn.set_sensitive(obj.assemble_url().is_some());
256263
});
257264

258-
259265
// Add pages to view stack
260266
view_stack.add_titled(&gui_page, Some("create"), "Guided");
261267
view_stack.add_titled(&assemble_page, Some("assemble-file"), "From File");
@@ -327,58 +333,53 @@ impl CreateDistroboxDialog {
327333
this
328334
}
329335

330-
pub fn build_file_row(&self, title: &str, selection: FileRowSelection, cb: impl Fn(PathBuf) + Clone + 'static) -> adw::ActionRow {
331-
let row = adw::ActionRow::new();
332-
row.set_title(title);
333-
row.set_subtitle("No file selected");
334-
row.set_activatable(true);
335-
336-
let file_icon = gtk::Image::from_icon_name("document-open-symbolic");
337-
row.add_suffix(&file_icon);
338-
339-
let title = title.to_owned();
340-
let dialog_cb = clone!(
341-
#[weak]
342-
row,
343-
move |res: Result<File, _>| {
344-
if let Ok(file) = res {
345-
if let Some(path) = file.path() {
346-
row.set_subtitle(&path.display().to_string());
347-
cb(path);
348-
}
349-
}
350-
}
351-
);
352-
row.connect_activated(
353-
move |_| {
354-
let file_dialog = gtk::FileDialog::builder()
355-
.title(&title)
356-
.modal(true)
357-
.build();
358-
let dialog_cb = dialog_cb.clone();
359-
match selection {
360-
FileRowSelection::File => {
361-
file_dialog.open(
362-
None::<&gtk::Window>,
363-
None::<&gio::Cancellable>,
364-
dialog_cb,
365-
);
366-
}
367-
FileRowSelection::Folder => {
368-
file_dialog.select_folder(
369-
None::<&gtk::Window>,
370-
None::<&gio::Cancellable>,
371-
dialog_cb,
372-
);
373-
}
336+
pub fn build_file_row(
337+
&self,
338+
title: &str,
339+
selection: FileRowSelection,
340+
cb: impl Fn(PathBuf) + Clone + 'static,
341+
) -> adw::ActionRow {
342+
let row = adw::ActionRow::new();
343+
row.set_title(title);
344+
row.set_subtitle("No file selected");
345+
row.set_activatable(true);
346+
347+
let file_icon = gtk::Image::from_icon_name("document-open-symbolic");
348+
row.add_suffix(&file_icon);
349+
350+
let title = title.to_owned();
351+
let dialog_cb = clone!(
352+
#[weak]
353+
row,
354+
move |res: Result<File, _>| {
355+
if let Ok(file) = res {
356+
if let Some(path) = file.path() {
357+
row.set_subtitle(&path.display().to_string());
358+
cb(path);
374359
}
375360
}
376-
);
377-
row
378-
361+
}
362+
);
363+
row.connect_activated(move |_| {
364+
let file_dialog = gtk::FileDialog::builder().title(&title).modal(true).build();
365+
let dialog_cb = dialog_cb.clone();
366+
match selection {
367+
FileRowSelection::File => {
368+
file_dialog.open(None::<&gtk::Window>, None::<&gio::Cancellable>, dialog_cb);
369+
}
370+
FileRowSelection::Folder => {
371+
file_dialog.select_folder(
372+
None::<&gtk::Window>,
373+
None::<&gio::Cancellable>,
374+
dialog_cb,
375+
);
376+
}
377+
}
378+
});
379+
row
379380
}
380381

381-
pub fn update_create_args(&self) -> Result<(), Error> {
382+
pub async fn extract_create_args(&self) -> Result<CreateArgs, Error> {
382383
let imp = self.imp();
383384
let image = imp
384385
.image_row
@@ -402,17 +403,27 @@ impl CreateDistroboxDialog {
402403

403404
let name = CreateArgName::new(&imp.name_row.text())?;
404405

406+
dbg!(&self.home_folder());
405407
let create_args = CreateArgs {
406408
name,
407409
image: image.to_string(),
408410
nvidia: imp.nvidia_row.is_active(),
409-
home_path: self.home_folder(),
411+
home_path: if let Some(home) = self.home_folder() {
412+
Some(
413+
self.root_store()
414+
.resolve_host_path(&home)
415+
.await
416+
.map_err(|e| Error::InvalidField("home".to_string(), e.to_string()))?,
417+
)
418+
} else {
419+
None
420+
},
410421
init: imp.init_row.is_active(),
411422
volumes,
412423
};
424+
dbg!(&create_args);
413425

414-
self.imp().current_create_args.replace(create_args);
415-
Ok(())
426+
Ok(create_args)
416427
}
417428

418429
pub fn build_volumes_group(&self) -> adw::PreferencesGroup {
@@ -462,10 +473,13 @@ impl CreateDistroboxDialog {
462473
volumes_group
463474
}
464475

465-
fn update_errors(&self, res: &Result<(), distrobox::Error>) {
476+
fn update_errors<T>(&self, res: &Result<T, distrobox::Error>) {
466477
let imp = self.imp();
467478
imp.name_row.remove_css_class("error");
468479
imp.name_row.set_tooltip_text(None);
480+
if let Err(ref e) = res {
481+
error!(error = %e, "CreateDistroboxDialog: update_errors");
482+
}
469483
match res {
470484
Err(distrobox::Error::InvalidField(field, msg)) if field == "name" => {
471485
imp.name_row.add_css_class("error");

src/distrobox/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,9 @@ pub enum Error {
206206
command: String,
207207
stderr: String,
208208
},
209+
210+
#[error("failed to resolve host path: {0}. getfattr may not be installed on the host")]
211+
ResolveHostPath(String),
209212
}
210213

211214
fn dbcmd() -> Command {

src/store/root_store.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ use gtk::prelude::*;
88
use gtk::{gio, glib};
99
use std::cell::RefCell;
1010
use std::default;
11+
use std::path::PathBuf;
1112
use std::rc::Rc;
1213
use std::time::Duration;
1314
use tracing::debug;
1415
use tracing::error;
1516
use tracing::info;
1617

1718
use crate::container::Container;
18-
use crate::distrobox::Command;
19+
use crate::distrobox::{self, wrap_capture_cmd, Command};
1920
use crate::distrobox::CreateArgs;
2021
use crate::distrobox::Distrobox;
2122
use crate::distrobox::Status;
@@ -70,7 +71,9 @@ mod imp {
7071
Self {
7172
containers: gio::ListStore::new::<crate::container::Container>(),
7273
command_runner: OnceCell::new(),
73-
terminal_repository: RefCell::new(TerminalRepository::new(Rc::new(NullCommandRunner::default()))),
74+
terminal_repository: RefCell::new(TerminalRepository::new(Rc::new(
75+
NullCommandRunner::default(),
76+
))),
7477
selected_container: Default::default(),
7578
current_view: Default::default(),
7679
current_dialog: Default::default(),
@@ -299,7 +302,7 @@ impl RootStore {
299302
.terminal_repository
300303
.borrow()
301304
.terminal_by_name(&name_or_program);
302-
305+
303306
if let Some(terminal) = by_name {
304307
Some(terminal)
305308
} else if let Some(terminal) = self
@@ -376,6 +379,38 @@ impl RootStore {
376379
}
377380
});
378381
}
382+
383+
pub async fn resolve_host_path(&self, path: &str) -> Result<String, distrobox::Error> {
384+
let mut cmd = Command::new_with_args(
385+
"getfattr",
386+
[
387+
"-n",
388+
"user.document-portal.host-path",
389+
"--only-values",
390+
path,
391+
],
392+
);
393+
wrap_capture_cmd(&mut cmd);
394+
let output = self.command_runner()
395+
.output(cmd)
396+
.await
397+
.map_err(|e| distrobox::Error::ResolveHostPath(e.to_string()));
398+
399+
400+
let is_from_sandbox = path.starts_with("/run/user");
401+
402+
// If the path is not from a flatpak sandbox, we assume it's a regular path, so we can skip the getfattr command error.
403+
// If the command was successful, but for some reason the output is empty, we also return the path as is.
404+
let stdout = if (output.is_err() && !is_from_sandbox) || output.as_ref().map_or(false, |o| o.stdout.is_empty()) {
405+
return Ok(path.to_string());
406+
} else {
407+
output?.stdout
408+
};
409+
410+
Ok(String::from_utf8(stdout)
411+
.map_err(|e| distrobox::Error::ParseOutput(e.to_string()))?
412+
.trim().to_string())
413+
}
379414
}
380415

381416
impl Default for RootStore {

0 commit comments

Comments
 (0)