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

new dir module #916

Merged
merged 1 commit into from
Sep 4, 2018
Merged

new dir module #916

merged 1 commit into from
Sep 4, 2018

Conversation

scottlamb
Copy link
Contributor

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
    add fdopendir on macOS 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).

Copy link
Member

@asomers asomers left a 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
Copy link
Member

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.

Copy link
Contributor Author

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
Copy link
Member

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 ?

Copy link
Contributor Author

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?

Copy link
Member

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
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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) };
Copy link
Member

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.

Copy link
Contributor Author

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)]
Copy link
Member

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?

Copy link
Contributor Author

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)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about Debug?

Copy link
Contributor Author

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 {
Copy link
Member

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.

Copy link
Member

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

Copy link
Contributor Author

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> {
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

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 {
Copy link
Member

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

Copy link
Contributor Author

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()
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, honestly, comparing two Entrys 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?

Copy link
Member

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>);
Copy link
Contributor Author

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.

Copy link
Member

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.

@scottlamb
Copy link
Contributor Author

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).

Copy link
Member

@asomers asomers left a 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:
Copy link
Member

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.

Copy link
Contributor Author

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",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alphabetical ordering, please.

Copy link
Contributor Author

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",
Copy link
Member

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
    }
  }
}

Copy link
Contributor Author

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

Copy link
Member

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

Copy link
Contributor Author

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> {
Copy link
Member

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> {
Copy link
Member

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?

Copy link
Contributor Author

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
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

@scottlamb
Copy link
Contributor Author

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.

The lifetime should prevent that problem. Each Iter has a &'d mut Dir, and there can only be one of those at once.

Copy link
Member

@asomers asomers left a 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`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/descriptor/file/

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Member

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> {
Copy link
Member

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?

Copy link
Contributor Author

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.)

Copy link
Member

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?

Copy link
Contributor

@Susurrus Susurrus left a 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,
Copy link
Contributor

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!()?

Copy link
Contributor Author

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();
Copy link
Contributor

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.

Copy link
Contributor Author

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();
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Contributor Author

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",
Copy link
Contributor

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?

Copy link
Contributor Author

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")]
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likewise

@scottlamb
Copy link
Contributor Author

Anything else I need to do to finish this?

@Susurrus
Copy link
Contributor

LGTM. @asomers any last changes you think are necessary?

@asomers
Copy link
Member

asomers commented Aug 31, 2018

No changes except a squash.

@scottlamb
Copy link
Contributor Author

Squashed. Thank you!

@asomers
Copy link
Member

asomers commented Aug 31, 2018

bors r+

bors bot added a commit that referenced this pull request Aug 31, 2018
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>
@bors
Copy link
Contributor

bors bot commented Aug 31, 2018

Build failed

@asomers
Copy link
Member

asomers commented Aug 31, 2018

Looks like we need to merge PR #900 first.

@Susurrus
Copy link
Contributor

Susurrus commented Sep 2, 2018

#900 was just merged

bors r+

bors bot added a commit that referenced this pull request Sep 2, 2018
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>
@bors
Copy link
Contributor

bors bot commented Sep 2, 2018

Build failed

@asomers
Copy link
Member

asomers commented Sep 2, 2018

Looks like you need to rebase and replace the tempdir crate with tempfile.

@scottlamb
Copy link
Contributor Author

Rebased, replaced tempfile with tempdir.

@Susurrus
Copy link
Contributor

Susurrus commented Sep 4, 2018

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).
@scottlamb
Copy link
Contributor Author

Done

@Susurrus
Copy link
Contributor

Susurrus commented Sep 4, 2018

Awesome! Thanks @scottlamb!

bors r+

bors bot added a commit that referenced this pull request Sep 4, 2018
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>
@bors
Copy link
Contributor

bors bot commented Sep 4, 2018

@bors bors bot merged commit f9ebcb7 into nix-rust:master Sep 4, 2018
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

Successfully merging this pull request may close these issues.

3 participants