Shouldn't File struct expose a method to show the underlying path ?
In systems like Linux, a path is not something that a file has. It's something that can be used to obtain access to a file, but the path may not be the canonical one, and may not even exist afterwards. For example, it's possible to open a File
, then delete the path (unlink
), and still read and write from the File
successfully.
Currently File
(at least on unix-like systems) is just a thin wrapper over fd
and takes only 4 bytes, with no allocations on the Rust's side.
If you mean for diagnostic purposes when displaying a std::io::Error
, there is an open issue and a draft PR showing it should be possible in theory. At this point I think it just needs a dedicated individual to spearhead it.
Note that this isn't quite what the original post asked for; the draft PR changes functions like File::open
and File::create
to include the path in the error message if they fail, but does not change things like File::metadata
to include the path.
Basically, the draft PR says "if you passed in a path to this function call, and it fails, give the path back to you as part of the error"; for functions that didn't get a path, there's no path to return to you.
That's fair, and even on an OS where it can be retrieved after being discarded, I don't think users would be happy with the extra syscall during error generation.
If OP really wants it unconditionally available on File
, the method either needs to return Option<PathBuf>
(and have the docs clarify it is not a zero-cost getter) or it needs to be a method on something more like std::os::windows::fs::FileExt
.
One can also create File
s from non-files (on Unices at least).
fn main() -> Result<(), Box<dyn Error>> {
let file = File::from(io::stdout().as_fd().try_clone_to_owned()?);
writeln!(&file, "unbuffered")?;
Ok(())
}
On Linux you can technically retrieve this information, via reading the symlink target path of /proc/self/fd/N
where N is the fd number.
Of course, it might not be an actual file, or a file that still exists. And /proc may not be mounted. And even if everything works OK, that is still an overhead of a syscall, plus parsing logic to determine what sort of path it is.
So I don't think std should do this (especially since I don't know if you can even do this on other Unices).
procfs
is available on some Unices, but it is a Linux-ism and so usually not a part of the base system. You can query a file descriptors path on macOS (through a fcntl
flag) and FreeBSD (through a sysctl
) but I'm less sure about NetBSD or OpenBSD. A quick google search leads me to believe that OpenBSD has intentionally not provided such an API.
It might be a useful fallible API, abstracting over platform differences. But then you need the list of reasons why it’s probably not a good idea for most clients, and at that point it probably doesn’t need to be in std at all. A crate can collect all the approaches just as well.
nit: actually it originated on plan9, and linux copied it (with some modification) from there (it did a similar thing with FUSE)
Other than the issues listed already, I've played with this before. /proc
is…a strange place[1]. If you delete a file, the symlink's target is updated to have a " (deleted)
" suffix. If you then make this file, the readlink
target exits, but it cannot be read via the symlink (because the kernel knows it is not readable). You can therefore detect whether tools read the symlink or do their own readlink
following internally based on whether such paths "work" for them.
Cue the Monty Python's Knights of the Roundtable scene. ↩︎
it also doesn't work for files in deeply nested directories when it exceeds PATH_MAX
Ah, that reminds me that mount namespaces can differ and the realpath
go somewhere completely different even if it resolves. All one can really do is open()
the path itself and not treat them like symlinks at all.
I think it's been delegated to Debug
only, because the function is unreliable, and has many platform-specific limitations.
fn get_path(_fd: c_int) -> Option<PathBuf> {
// FIXME(#24570): implement this for other Unix platforms
None
}
If this kinda mostly worked on Linux, I expect Rust programs would suffer Hyrum's Law. If it's public, people will use it. If it mostly works, people will rely on it. And then it will annoyingly break in some situations on some platforms where someone uses it in an argument to Command
or saves as a path in config file.
I think io::Error
could carry a path in cases where it's available via arguments to the function that failed, and that would fix a lot of complaints. For other cases, just carry (File, PathBuf)
explicitly.