Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting the path of a Dir #373

Open
ned opened this issue Nov 10, 2024 · 3 comments
Open

Getting the path of a Dir #373

ned opened this issue Nov 10, 2024 · 3 comments

Comments

@ned
Copy link

ned commented Nov 10, 2024

Sometimes I'd like to be able to get the path for a Dir.

For example,

  • logs or debugging
  • passing the path to a separate process
  • passing the path to a library that doesn't yet support the capability model

For example, in a personal project, I'm using the git2 wrapper around libgit2. In order to call Repository::clone(url, path), I need the path of the cache directory. I could use gitoxide, a pure rust implementation of git, however that would have the same problem. Maybe in the future we can make gitoxide use cap-std, but for now that's not an option.

Yes, some of these examples break the capability model, however, from a pragmatic point of view, that may be acceptable.

I'm not sure what the best approach here is, but in my personal project I'm using a wrapper like

pub struct DirWithPath {
    dir: Dir,
    path: PathBuf,
}

with a constructor like

pub fn open_ambient_dir<P: AsRef<Path>>(path: P, ambient_authority: AmbientAuthority) -> io::Result<Self> {
    Dir::open_ambient_dir(path.as_ref(), ambient_authority).map(|dir| Self {
        dir,
        path: path.as_ref().to_path_buf(),
    })
}

and an open_dir method that concatenates the path:

pub fn open_dir<P: AsRef<Path>>(self: &Self, path: P) -> io::Result<Self> { 
    let full_path = self.path.join(path.as_ref());
    self.dir.open_dir(path).map(|dir| Self {                                                                                                                                     
        dir,
        path: full_path,
    })
}

One issue with maintaining my own wrapper is that I can't use e.g ProjectDirs from cap-directories because its methods return Dirs, so the paths are unknown. I would have to wrap directories_next directly.

I wonder if it would be useful for other people if something like this was added to cap-std (with appropriate warnings with respect to it breaking the capability model)?

@ned ned changed the title Absolute paths Getting the path of a Dir Nov 10, 2024
@cgwalters
Copy link
Contributor

passing the path to a separate process

See https://docs.rs/cap-std-ext/latest/cap_std_ext/cmdext/trait.CapStdExtCommandExt.html

passing the path to a library that doesn't yet support the capability model

This is OS specific but possible, see https://github.com/rust-lang/rust/blob/c24e166527cd46d3b4033b0c061d02657e3c3cbf/library/std/src/sys/pal/unix/fs.rs#L1508

logs or debugging

Same as above.

@sunfishcode
Copy link
Member

Yes. I think this makes sense to add to cap-fs-ext using an extension trait, similar to the other traits in that crate, since it doesn't directly correspond to a "std" API.

cap-primitives already has an internal implementation of this (file_path), and even has platform-independent fallback code so it works even on platforms that don't have special magic to do this, so we'd just need to make that public and then expose it as an extension trait in cap-fs-ext.

@ned
Copy link
Author

ned commented Nov 23, 2024

Thanks to you both for responding!

I tried sunfishcode's suggestion, however the file_path function takes a &File and I'm not sure if there is a way to get a &File from a Dir without consuming it?

Here are the changes I tried:

For cap-primitives

diff --git a/cap-primitives/src/rustix/darwin/fs/file_path.rs b/cap-primitives/src/rustix/darwin/fs/file_path.rs
index 377309b..8b0f182 100644
--- a/cap-primitives/src/rustix/darwin/fs/file_path.rs
+++ b/cap-primitives/src/rustix/darwin/fs/file_path.rs
@@ -12,7 +12,7 @@ use std::os::unix::ffi::OsStringExt;
 use std::os::wasi::ffi::OsStringExt;
 use std::path::PathBuf;
 
-pub(crate) fn file_path(file: &fs::File) -> Option<PathBuf> {
+pub fn file_path(file: &fs::File) -> Option<PathBuf> {
     if let Ok(path) = getpath(file) {
         return Some(OsString::from_vec(path.into_bytes()).into());
     }
diff --git a/cap-primitives/src/rustix/linux/fs/file_path.rs b/cap-primitives/src/rustix/linux/fs/file_path.rs
index 430f10a..4fe53e5 100644
--- a/cap-primitives/src/rustix/linux/fs/file_path.rs
+++ b/cap-primitives/src/rustix/linux/fs/file_path.rs
@@ -2,7 +2,7 @@ use super::procfs::get_path_from_proc_self_fd;
 use std::fs;
 use std::path::PathBuf;
 
-pub(crate) fn file_path(file: &fs::File) -> Option<PathBuf> {
+pub fn file_path(file: &fs::File) -> Option<PathBuf> {
     use std::os::unix::fs::MetadataExt;
 
     // Ignore paths that don't start with '/', which are things like
diff --git a/cap-primitives/src/windows/fs/mod.rs b/cap-primitives/src/windows/fs/mod.rs
index 0de735a..d804b4f 100644
--- a/cap-primitives/src/windows/fs/mod.rs
+++ b/cap-primitives/src/windows/fs/mod.rs
@@ -81,7 +81,7 @@ pub(crate) use symlink_unchecked::*;
 // <https://docs.microsoft.com/en-us/windows/win32/fileio/reparse-points>
 pub(crate) const MAX_SYMLINK_EXPANSIONS: u8 = 63;
 
-pub(crate) fn file_path(file: &std::fs::File) -> Option<std::path::PathBuf> {
+pub fn file_path(file: &std::fs::File) -> Option<std::path::PathBuf> {
     get_path::get_path(file).ok()
 }

I'm also not sure whether these architecture-specific functions should be made public and exposed individually, or wrapped in an architecture-independent function which is exposed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants