-
Notifications
You must be signed in to change notification settings - Fork 675
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
new dir module #916
new dir module #916
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can understand the need for such a module. I'll have to give some thought to the best name for it, though.
@Susurrus do you have any thoughts?
src/dir.rs
Outdated
|
||
impl Entry { | ||
pub fn inode(&self) -> u64 { | ||
self.0.d_ino |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This field is called d_fileno
on FreeBSD. I don't know about the other BSDs; you should check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed: now matches the std implementation's cfg stuff.
src/dir.rs
Outdated
} | ||
} | ||
|
||
// `Dir` is not `Sync`, but it's not reference-counted either, so it can be safely passed from one |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What makes it non-Sync
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I was under the impression it was only thread-safe when used with different DIR
objects. It turns out I was wrong with readdir_r
, but according to this glibc documentation, that call might be obsoleted, and readdir
will be specified to work as I'd imagined.
I'm inclined to update the comment and leave it non-Sync
. Opinions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense.
src/dir.rs
Outdated
///! * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing | ||
///! if the path represents a file or directory). | ||
///! * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc. | ||
///! * can be iterated through multiple times without closing and reopening the file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can it be iterated through multiple times if it only implemented IntoIterator
and not Iterator
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&mut Directory
implements IntoIterator
, so you can get an iterator more than once. The test does this.
I could have Dir
implement Iterator
instead but you'd have to remember to rewind it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see. You implemented IntoIterator
for &Dir
, not Dir
. I think it would be more idiomatic instead for Dir
to have a iter()
method, that returns an object which implemented Iterator
.
src/dir.rs
Outdated
match ptr::NonNull::new(unsafe { libc::fdopendir(fd) }) { | ||
None => { | ||
let e = Error::last(); | ||
unsafe { libc::close(fd) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dir
takes ownership of the file descriptor, and closes it on Drop
. Unfortunately, RawFd
is defined as an alias, so there's no way to enforce that the caller doesn't hold onto a copy. But could you at least warn the caller in the documentation? Bad stuff will happen if he accesses fd outside of the Dir
interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AsRawFd::as_raw_fd
has such a warning:
This method does not pass ownership of the raw file descriptor to the caller. The descriptor is only guaranteed to be valid while the original object has not yet been destroyed.
but I'll add one to Dir
's docs as well where I mention the AsRawFd
implementation.
src/dir.rs
Outdated
} | ||
} | ||
|
||
#[allow(missing_debug_implementations)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally, an ugly derived Debug
implementation is better than none at all, because none at all prevents derive
from working on larger types composed with this one. Is there any reason not to derive
it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose not. Done.
src/dir.rs
Outdated
|
||
/// A directory entry, similar to `std::fs::DirEntry`. | ||
/// Note that unlike the std version, this may represent the `.` or `..` entries. | ||
#[derive(Copy, Clone)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about Debug
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I manually implemented Debug
, so that name
would be shown as a CStr
rather than a [::c_char; 256]
or whatever.
src/dir.rs
Outdated
pub fn inode(&self) -> u64 { | ||
self.0.d_ino | ||
} | ||
pub fn name(&self) -> &ffi::CStr { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spaces between methods, please. Also, please document all public methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be named file_name
to match std::fs::DirEntry
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All done.
src/dir.rs
Outdated
/// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known; | ||
/// notably, some Linux filesystems don't implement this. The caller should use `stat` or | ||
/// `fstat` if this returns `None`. | ||
pub fn type_(&self) -> Option<Type> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since Entry
is basically a replacement for std::fs::DirEntry
, why not make the API as close as possible? This method should be named file_type
, and should return an object supporting methods like those of std::fs::FileType
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. I'd like to preserve the ability to see exactly what the OS returned, for a couple reasons:
- general educational value
- you might want to use this to filter out uninteresting types without calling stat. If you decide to look more closely, you might want to do the stat in a different way (openat + fstat), and you might want to see the other fields without a second stat call.
My general impression was that nix is supposed to pretty closely match what the Unix systems offer, where the std library is more high-level. Does that sound right?
I made a similar decision to preserve the potentially-useful .
and ..
entries (you might want to call ino()
on them, for example) where std filters it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's reasonable. But I would still rather call this method file_type
or filetype
. The trailing underscore is weird and unprecedented.
src/dir.rs
Outdated
} | ||
|
||
impl Entry { | ||
pub fn inode(&self) -> u64 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be named ino
to match std::fs::DirEntry
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/dir.rs
Outdated
|
||
impl PartialEq<Entry> for Entry { | ||
fn eq(&self, other: &Entry) -> bool { | ||
self.inode() == other.inode() && self.name() == other.name() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method could give incorrect results for files opened from different file systems. Probably best just to compare name
and the Dir
object's file descriptor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, honestly, comparing two Entry
s is probably not that useful at all. I just have it here for my test. What would you think of removing it and do something else there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing it would be reasonable. Is there a reason that Entry
needs to implement PartialEq
?
src/dir.rs
Outdated
///! * returns entries for `.` (current directory) and `..` (parent directory). | ||
///! * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc | ||
///! does). | ||
pub struct Dir(ptr::NonNull<libc::DIR>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This didn't go over well with the continuous build:
error[E0412]: cannot find type `NonNull` in module `ptr`
--> src/dir.rs:23:21
|
23 | pub struct Dir(ptr::NonNull<libc::DIR>);
| ^^^^^^^ not found in `ptr`
so I switched to *mut libc::DIR
instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's because NonNull
only got added in Rust 1.25.0. Nix's minimum requirement is 1.20.0.
Pushed a new commit that should address most of these comments. Thanks for the quick review! I appreciate it even though I wasn't able to respond for a while (got sick, yuck). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more thing: it's not safe to have two Iter
objects alive at the same time, because they'll affect each other's directory pointers. It may be better to implement Iterator
directly on Dir
, and add an explicit rewind
method.
src/dir.rs
Outdated
use libc::{dirent, readdir_r}; | ||
|
||
/// An open directory. | ||
///! This is a lower-level interface than `std::fs::ReadDir`. Notable differences: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this render correctly in Rust Doc? Normally ///!
annotates the enclosing object, which in this case would be the file. To describe Dir
instead, you should have all ///
s, with a blank line after the first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It did, but fixed anyway. Also adding some extra whitespace to the docstrings so the one-line extract would be less overwhelming.
src/dir.rs
Outdated
|
||
impl Entry { | ||
/// Returns the inode number (`d_ino`) of the underlying `dirent`. | ||
#[cfg(any(target_os = "macos", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alphabetical ordering, please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/dir.rs
Outdated
/// Returns the inode number (`d_fileno`) of the underlying `dirent`. | ||
#[cfg(any(target_os = "freebsd", | ||
target_os = "openbsd", | ||
target_os = "bitrig", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bitrig is dead. You can remove it. Also, the others should be alphabetically ordered. Does Nix support any OSes that can't implement ino
? If not, you can simplify this with cfg_if
, like this:
pub fn ino(&self) -> u64 {
cfg_if! {
if #[cfg(any(target_os = ...))] {
self.0.d_ino as u64
} else {
self.0.d_filenno as u64
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, that's neat, but I'm having trouble getting it to work. Tried a couple variations. Any ideas?
pub fn ino(&self) -> u64 {
cfg_if! {
if #[cfg(any(target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "ios",
target_os = "l4re",
target_os = "linux",
target_os = "macos",
target_os = "solaris"))] {
self.0.d_ino as u64
} else {
self.0.d_fileno as u64
}
}
}
produces
error: expected one of `!` or `::`, found `.`
--> src/dir.rs:158:21
|
158 | self.0.d_ino as u64
| ^ expected one of `!` or `::` here
and
cfg_if! {
if #[cfg(any(target_os = "android",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "ios",
target_os = "l4re",
target_os = "linux",
target_os = "macos",
target_os = "solaris"))] {
pub fn ino(&self) -> u64 { self.0.d_ino as u64 }
} else {
pub fn ino(&self) -> u64 { self.0.d_fileno as u64 }
}
}
produces a bunch of errors, starting with:
error: expected one of `::` or `:`, found `)`
--> src/dir.rs:175:29
|
175 | pub fn ino(&self) -> u64 { self.0.d_ino as u64 }
| ^ expected one of `::` or `:` here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like cfg_if
doesn't work with expressions, only items. An item is something like a module or a function.
https://docs.rs/cfg-if/0.1.4/cfg_if/macro.cfg_if.html
https://doc.rust-lang.org/reference/items.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, function didn't work either (second try above). I got it to work by having a separate impl
for this vs the other stuff, but then the two impls are separate in the documentation, which is annoying.
I think I'm giving up on cfg_if!
here, but I can do #[cfg(any(target_os = "bar", target_os="foo"))]
vs #[cfg(not(any(target_os = "bar", target_os="foo")))]
so it's obviously comprehensive.
src/dir.rs
Outdated
/// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known; | ||
/// notably, some Linux filesystems don't implement this. The caller should use `stat` or | ||
/// `fstat` if this returns `None`. | ||
pub fn type_(&self) -> Option<Type> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's reasonable. But I would still rather call this method file_type
or filetype
. The trailing underscore is weird and unprecedented.
test/test_dir.rs
Outdated
use std::fs::File; | ||
use self::tempdir::TempDir; | ||
|
||
fn open(path: &::std::path::Path) -> nix::Result<Dir> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be worthwhile to make this a method of Dir
, so that you can open by either RawFd
or a path?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done - both fcntl::open
and fcntl::openat
variants. Unlike my quick test version, I'm using a NixPath
, and I'm expecting the caller to provide OFlags
, so they can do O_NOFOLLOW
or O_EXLOCK
or whatever as desired.
test/test_dir.rs
Outdated
.map(|e| e.file_name().to_str().unwrap().to_owned()) | ||
.collect(); | ||
assert_eq!(&entry_names[..], &[".", "..", "bar", "foo"]); | ||
assert!(&[Some(Type::Directory), None].contains(&entries[0].type_())); // .: dir |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be worthwhile adding a comment explaining why the check for None is necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
The lifetime should prevent that problem. Each |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, I didn't notice that iter
takes a mutable reference. That should work.
src/dir.rs
Outdated
/// * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing | ||
/// if the path represents a file or directory). | ||
/// * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc. | ||
/// The descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/descriptor/file/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
file descriptor? it's really not the file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I see your point. Do you think it would make sense to implement AsRef<RawFd>
instead of AsRawFd
? That way the user would be unable to keep the RawFd
if the drop the Dir
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I think this argument applies equally well to anywhere else that uses AsRawFd
, and Google doesn't find any results for AsRef<RawFd>
. I think I'd prefer to match the rest of the ecosystem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and RawFd
is Copy
, so AsRef<RawFd>
is probably just as easy to mess up anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot about Copy
. In that case you're right that AsRef<RawRd>
wouldn't have much advantage.
src/dir.rs
Outdated
} | ||
|
||
/// Opens the given path as with `fcntl::openat`. | ||
pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag) -> Result<Self> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hardcoding Mode::empty
prevents this API from being used to create new directories. Any reason not to expose the mode
argument of fcntl::openat
and fcntl::open
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I just never imagined using this to create directories would actually work, but it does on Linux. (On macOS, it returns EINVAL.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any idea why? Can openat
not create directories on OSX?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks pretty good I think. A few minor style changes here. But I think you and asomers have hashed out most of it.
libc::DT_REG => Some(Type::File), | ||
libc::DT_LNK => Some(Type::Symlink), | ||
libc::DT_SOCK => Some(Type::Socket), | ||
/* libc::DT_UNKNOWN | */ _ => None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't this be better to actually use DT_UNKNOWN
and then have another catchall that maps to unreachable!()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the list of DT_
types is incomplete (which seems possible—I haven't read all the platforms' manpages, and a platform could add one anyway), what should this code do? With unreachable!
, it'd panic. I'd prefer it return None
. Calling code is supposed to handle DT_UNKNOWN
gracefully, and it makes sense to me to handle an unexpected enum type in the same way.
test/test_dir.rs
Outdated
|
||
#[test] | ||
fn rewind() { | ||
let tmp = TempDir::new("nix-dir-rewind").unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we have anonymous folders? I'd prefer we use that style rather than naming everything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tempdir
crate doesn't seem to support that. tempfile
version 3 does (and that obsoletes tempdir
), but nix still seems to be on version 2. This call to TempDir::new
matches the other ones in the tests
dir.
test/test_dir.rs
Outdated
|
||
#[test] | ||
fn read() { | ||
let tmp = TempDir::new("nix-dir-read").unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we have anonymous folders? I'd prefer we use that style rather than naming everything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
likewise
.collect(); | ||
assert_eq!(&entry_names[..], &[".", "..", "bar", "foo"]); | ||
|
||
// Check file types. The system is allowed to return DT_UNKNOWN (aka None here) but if it does |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why would it return DT_UNKNOWN
? Does it do this in practice on any systems?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it does. From the Linux readdir(3)
manpage:
Currently, only some filesystems (among them: Btrfs, ext2, ext3, and ext4) have
full support for returning the file type in d_type. All applications must properly
handle a return of DT_UNKNOWN.
|
||
impl Entry { | ||
/// Returns the inode number (`d_ino`) of the underlying `dirent`. | ||
#[cfg(any(target_os = "android", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use cfg-if
here instead of these huge if/else blocks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried this on July 5th, and the only way I could get it to work was by having two impl
blocks, which makes the rustdoc
output look weird. cfg_if
can only be attached at the "item" level; apparently neither functions nor expressions are items.
use std::{ffi, fmt, ptr}; | ||
use sys; | ||
|
||
#[cfg(target_os = "linux")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use the cfg_if!
macro here instead? It's a little more verbose but I find it easier to read an if-else rather than trying to determine that through consecutive cfg
statements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
likewise
Anything else I need to do to finish this? |
LGTM. @asomers any last changes you think are necessary? |
No changes except a squash. |
Squashed. Thank you! |
bors r+ |
916: new dir module r=asomers a=scottlamb Fixes #915 This is a lower-level interface than `std::fs::ReadDir`. Notable differences: * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing if the path represents a file or directory). Uses `fdopendir` for this, available on all Unix platforms as of rust-lang/libc#1018. * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc. * can be iterated through multiple times without closing and reopening the file descriptor. Each iteration rewinds when finished. * returns entries for `.` (current directory) and `..` (parent directory). * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc does). Co-authored-by: Scott Lamb <slamb@slamb.org>
Build failed |
Looks like we need to merge PR #900 first. |
#900 was just merged bors r+ |
916: new dir module r=Susurrus a=scottlamb Fixes #915 This is a lower-level interface than `std::fs::ReadDir`. Notable differences: * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing if the path represents a file or directory). Uses `fdopendir` for this, available on all Unix platforms as of rust-lang/libc#1018. * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc. * can be iterated through multiple times without closing and reopening the file descriptor. Each iteration rewinds when finished. * returns entries for `.` (current directory) and `..` (parent directory). * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc does). Co-authored-by: Scott Lamb <slamb@slamb.org>
Build failed |
Looks like you need to rebase and replace the tempdir crate with tempfile. |
Rebased, replaced tempfile with tempdir. |
Can you add an Added entry into the CHANGELOG? Then I think this LGTM. |
This is a lower-level interface than `std::fs::ReadDir`. Notable differences: * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing if the path represents a file or directory). Uses `fdopendir` for this, available on all Unix platforms as of rust-lang/libc#1018. * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc. * can be iterated through multiple times without closing and reopening the file descriptor. Each iteration rewinds when finished. * returns entries for `.` (current directory) and `..` (parent directory). * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc does).
Done |
Awesome! Thanks @scottlamb! bors r+ |
916: new dir module r=Susurrus a=scottlamb Fixes #915 This is a lower-level interface than `std::fs::ReadDir`. Notable differences: * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing if the path represents a file or directory). Uses `fdopendir` for this, available on all Unix platforms as of rust-lang/libc#1018. * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc. * can be iterated through multiple times without closing and reopening the file descriptor. Each iteration rewinds when finished. * returns entries for `.` (current directory) and `..` (parent directory). * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc does). Co-authored-by: Scott Lamb <slamb@slamb.org>
Fixes #915
This is a lower-level interface than
std::fs::ReadDir
. Notable differences:can be opened from a file descriptor (as returned by
openat
, perhapsbefore knowing if the path represents a file or directory). Uses
fdopendir
for this, available on all Unix platforms as ofadd fdopendir on macOS rust-lang/libc#1018.
implements
AsRawFd
, so it can be passed tofstat
,openat
, etc.can be iterated through multiple times without closing and reopening the
file descriptor. Each iteration rewinds when finished.
returns entries for
.
(current directory) and..
(parent directory).returns entries' names as a
CStr
(no allocation or conversion beyondwhatever libc does).