Skip to content

Commit

Permalink
feat(isolation): add directory support for UhyveFileMap
Browse files Browse the repository at this point in the history
If the guest path is part of a directory that is not mapped, we'll
recursively look for the parent directories and check if the parent
directories are mapped. If that's the case, we'll use the file in the
mapped host directory instead.

This feature also comes with a unit test. We partially rely on PathBuf
to prevent any funny behavior from taking place.

A current flaw of this approach is that the filename that can be found
in the OpenParams struct is "relative" instead of absolute, and
providing paths that are otherwise whitelisted (e.g. "/root/file.txt"
instead of "file.txt") will result in the map not return a result.
  • Loading branch information
n0toose committed Nov 26, 2024
1 parent c80cbe2 commit 494039e
Showing 1 changed file with 109 additions and 1 deletion.
110 changes: 109 additions & 1 deletion src/isolation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,44 @@ impl UhyveFileMap {
///
/// * `guest_path` - The guest path that is to be looked up in the map.
pub fn get_host_path(&mut self, guest_path: &str) -> Option<OsString> {
self.files.get(guest_path).map(OsString::from)
let host_path = self.files.get(guest_path).map(OsString::from);
if host_path.is_some() {
host_path
} else {
info!("Guest requested to open a path that was not mapped.");
if self.files.is_empty() {
info!("UhyveFileMap is empty, returning None...");
return None;
}

let requested_guest_pathbuf = PathBuf::from(guest_path);
if let Some(parent_of_guest_path) = requested_guest_pathbuf.parent() {
info!("The file is in a child directory, searching for a parent directory...");
let ancestors = parent_of_guest_path.ancestors();
for searched_parent_guest in ancestors {
// If one of the guest paths' parent directories (parent_host) is mapped,
// use the mapped host path and push the "remainder" (the path's components
// that come after the mapped guest path) onto the host path.
let parent_host: Option<&OsString> =
self.files.get(searched_parent_guest.to_str().unwrap());
if let Some(parent_host) = parent_host {
let mut host_path = PathBuf::from(parent_host);
let guest_path_remainder = requested_guest_pathbuf
.strip_prefix(searched_parent_guest)
.unwrap();

host_path.push(guest_path_remainder);

// Handles symbolic links.
return fs::canonicalize(&host_path)
.map_or(host_path.into_os_string(), PathBuf::into_os_string)
.into();
}
}
}
info!("The file is not in a child directory, returning None...");
None
}
}

/// Inserts an opened temporary file into the file map. Returns a CString so that
Expand Down Expand Up @@ -197,4 +234,75 @@ mod tests {

assert!(map.get_host_path("this_file_is_not_mapped").is_none());
}

#[test]
fn test_uhyvefilemap_directory() {
let mut fixture_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
fixture_path.push("tests/data/fixtures/fs");
assert!(fixture_path.is_dir());

// Tests successful directory traversal starting from file in child
// directory of a mapped directory.
let mut guest_path_map = PathBuf::from("this_folder_exists");
let mut host_path_map = fixture_path.clone();
host_path_map.push("this_folder_exists");

let mut target_guest_path =
PathBuf::from("this_folder_exists/folder_in_folder/file_in_second_folder.txt");
let mut target_host_path = fixture_path.clone();
target_host_path.push(target_guest_path.clone());

let mut uhyvefilemap_params = [format!(
"{}:{}",
host_path_map.to_str().unwrap(),
guest_path_map.to_str().unwrap()
)];
let mut map = UhyveFileMap::new(&uhyvefilemap_params);

let mut found_host_path = map.get_host_path(target_guest_path.clone().to_str().unwrap());

assert_eq!(
found_host_path.unwrap(),
target_host_path.as_os_str().to_str().unwrap()
);

// Tests successful directory traversal of the child directory.
// The pop() just removes the text file.
// guest_path.pop();
target_host_path.pop();
target_guest_path.pop();

found_host_path = map.get_host_path(target_guest_path.to_str().unwrap());
assert_eq!(
found_host_path.unwrap(),
target_host_path.as_os_str().to_str().unwrap()
);

// Tests directory traversal leading to valid symbolic link with an
// empty guest_path_map.
host_path_map = fixture_path.clone();
guest_path_map = PathBuf::from("");
uhyvefilemap_params = [format!(
"{}:{}",
host_path_map.to_str().unwrap(),
guest_path_map.to_str().unwrap()
)];

map = UhyveFileMap::new(&uhyvefilemap_params);

target_guest_path = PathBuf::from("this_symlink_leads_to_a_file");
target_host_path = fixture_path.clone();
target_host_path.push("this_folder_exists/file_in_folder.txt");
found_host_path = map.get_host_path(target_guest_path.to_str().unwrap());
assert_eq!(
found_host_path.unwrap(),
target_host_path.as_os_str().to_str().unwrap()
);

// Tests directory traversal with no maps
let empty_array: [String; 0] = [];
map = UhyveFileMap::new(&empty_array);
found_host_path = map.get_host_path(target_guest_path.to_str().unwrap());
assert!(found_host_path.is_none());
}
}

0 comments on commit 494039e

Please sign in to comment.