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

add support for dirent related function #558

Closed
wants to merge 1 commit into from

Conversation

Mic92
Copy link
Contributor

@Mic92 Mic92 commented Mar 17, 2017

This pr is currently work in progress, as it depends on an outstanding pull request for fdopendir in libc. I wanted to know if this function and DIR wrapper I added fit into the scope of the nix crate.

src/dirent.rs Outdated
}

impl Dir {
pub fn read(&mut self) -> Result<Option<&libc::dirent>> {
Copy link
Contributor Author

@Mic92 Mic92 Mar 17, 2017

Choose a reason for hiding this comment

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

borrowing mutually Dir is required here as the dirent reference might get overwritten by subsequent calls.

src/dirent.rs Outdated
use errno;

pub struct Dir {
pub handle: *mut DIR,
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this field should be public. Instead I'd propose implementing AsRawFd and IntoRawFd instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe Into<*mut DIR> because AsRawFd is for file descriptor.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, you're right. Into<*mut DIR> is what you'd want to do.

There is also dirfd which you could use to implement the *RawFd traits if you wanted to.

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 currently leave it private as I have implemented all functions, where the dir pointer is useful. If it turns out, that I missed a use case, it can be still added.

Copy link
Member

@kamalmarhubi kamalmarhubi left a comment

Choose a reason for hiding this comment

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

Thanks for this! While here, it seems like you should also add opendir(3). We'd also need something in the changelog.

However, I'm trying to decide on what the best way to expose this is. At one end, we could have fdopendir(3) and opendir(3) as here, and instead of Dir::read, we have readdir as a free function. At the other end, we expose readdir(3) as an iterator. And somewhere in between is the approach here.

Thoughts @fiveop @posborne?

Cargo.toml Outdated
@@ -21,7 +21,7 @@ preadv_pwritev = []
signalfd = []

[dependencies]
libc = { git = "https://github.com/rust-lang/libc" }
libc = { git = "https://github.com/Mic92/libc", branch = "master" }
Copy link
Member

Choose a reason for hiding this comment

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

Will wait till this hits libc upstream.

src/dirent.rs Outdated
_ => Err(Error::last().into()),
}
} else {
Ok(Some(unsafe{&*dirent}))
Copy link
Member

Choose a reason for hiding this comment

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

Could you annotate the lifetime in the signature so we're sure that this reference has the correct lifetime:

pub fn read<'a>(&'a mut self) -> Result<Option<&'a libc::dirent>>

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 switched back to readdir

@Mic92 Mic92 force-pushed the fdopendir branch 3 times, most recently from 1eb7cb7 to e498677 Compare March 20, 2017 12:42
@Mic92
Copy link
Contributor Author

Mic92 commented Mar 20, 2017

added seekdir/telldir

@Mic92 Mic92 force-pushed the fdopendir branch 2 times, most recently from 81ffa22 to 3ca111b Compare March 20, 2017 13:30
src/dirent.rs Outdated
};
if dirent.is_null() {
match Errno::last() {
errno::UnknownErrno => Ok(None),
Copy link
Member

@kamalmarhubi kamalmarhubi Mar 20, 2017

Choose a reason for hiding this comment

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

Is this right? It seems that on error, -1 is returned, not NULL. NULL indicates end of stream.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

from readdir(3):

 If the end of the directory stream is reached,  NULL  is  returned  and
 errno  is  not changed.  If an error occurs, NULL is returned and errno
 is set appropriately.  To distinguish end of stream and from an  error,
 set  errno to zero before calling readdir() and then check the value of
 errno if NULL is returned.

Copy link
Member

Choose a reason for hiding this comment

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

Right you are! I must have had one of the other functions' man pages open.

Copy link
Member

@kamalmarhubi kamalmarhubi left a comment

Choose a reason for hiding this comment

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

Thanks ofr the update! If you're adding the whole family, could you also include closedir(3), rewinddir(3), scandir(3), and dirfd(3)? Either that or update the commit message.

It would also make sense to implement AsRawFd and IntoRawFd for Dir using dirfd(3), but I'm happy to have that in a later PR.

@kamalmarhubi
Copy link
Member

Ah just noted that @Susurrus had mentioned {As,Into}RawFd already. Oh and there's also the changelog entry! :-)

@Mic92 Mic92 changed the title add support for fdopendir add support for dirent related function Mar 20, 2017
@Mic92
Copy link
Contributor Author

Mic92 commented Mar 20, 2017

I would leave adding the other functions open to someone else, as I currently only add functions, I need to proceed my project.

@kamalmarhubi
Copy link
Member

@Mic92 that's fair. Could you look at the CI failures? They seem legitimate.

@Mic92
Copy link
Contributor Author

Mic92 commented Mar 20, 2017

Order of .. and . seems to differ depending on platform

@Mic92 Mic92 force-pushed the fdopendir branch 6 times, most recently from d22cfb1 to 0cad72e Compare March 21, 2017 00:35
src/dirent.rs Outdated
}
}

pub fn readdir<'a>(dir: &'a mut Dir) -> Result<Option<&'a libc::dirent>> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

To obtain d_name I use:

let name = OsStr::from_bytes(unsafe {
    &mem::transmute::<[i8; 256], [u8; 256]>(entry.d_name)
});

any idea how I could how add a convenience wrapper to this library?

@Mic92
Copy link
Contributor Author

Mic92 commented Apr 8, 2017

This is ready for review.

@homu
Copy link
Contributor

homu commented Apr 15, 2017

☔ The latest upstream changes (presumably #536) made this pull request unmergeable. Please resolve the merge conflicts.

@homu
Copy link
Contributor

homu commented May 17, 2017

☔ The latest upstream changes (presumably ba5c837) made this pull request unmergeable. Please resolve the merge conflicts.

@Susurrus
Copy link
Contributor

@Mic92 Can you add comments for every function based on their man pages? We're trying to have complete documentation coverage for all PRs moving forward. You can see where things are lacking when you run cargo doc --no-deps --open

src/dirent.rs Outdated
#[cfg(not(any(target_os = "ios", target_os = "macos")))]
use std::os::unix::io::RawFd;

pub struct Dir(*mut DIR);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe name this DirStream so it's a little more clear?

Copy link
Contributor Author

@Mic92 Mic92 Jul 6, 2017

Choose a reason for hiding this comment

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

I prefer Dir as it it closer to the underlying API and matches the function names better, which all end in dir.

@Mic92 Mic92 force-pushed the fdopendir branch 2 times, most recently from 39cc8fd to 338d169 Compare July 6, 2017 23:58
@Mic92
Copy link
Contributor Author

Mic92 commented Jul 7, 2017

depends on rust-lang/libc#647

use std::path::Path;
use tempdir::TempDir;

fn test_readdir<OPEN>(open_fn: OPEN)
Copy link
Contributor

Choose a reason for hiding this comment

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

Please don't use all-caps here for a type name. Maybe just use O.

assert_eq!(letter1, '.' as u8);

let pos = telldir(&mut dir);
seekdir(&mut dir, pos); // no-op
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we have a test where we seek back a directory and check that the same directory is read twice (so that we no seekdir worked? I'd like to have a non-nop test of it.

}
}

/// Opens a directory stream corresponding to the directory name.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you run cargo doc --no-deps --open locally with this code and make sure the first sentence shows up nicely on the page listing all the functions here? I'm certain that some of these sentences are too long and that you need an empty /// on the next line to make them show up nicely.

src/dirent.rs Outdated
}

/// Inode number
pub fn ino(&self) -> libc::ino_t {
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason not to call this inode here for a little more clarity?

#[cfg(not(any(target_os = "ios", target_os = "macos")))]
use std::os::unix::io::RawFd;

/// Directory Stream object
Copy link
Contributor

Choose a reason for hiding this comment

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

Why don't we call this DirectoryStream or DirStream instead of 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.

also renamed Dirent to DirectoryEntry for consistency.

@Susurrus
Copy link
Contributor

Susurrus commented Jul 7, 2017

This is looking pretty good. Mostly some minor changes here. I would love to have more comprehensive module documentation, but I think linking to the man pages should be fine.

@Mic92 Mic92 force-pushed the fdopendir branch 2 times, most recently from 8aa9e48 to 6ed79b0 Compare July 7, 2017 07:16
/// Consumes directory stream and return underlying directory pointer.
///
/// The pointer must be deallocated manually using `libc::closedir`
impl Into<*mut DIR> for DirectoryStream {
Copy link
Contributor

Choose a reason for hiding this comment

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

A convention in std is to use a method named into_raw here when you're unwrapping a type. I'd suggest using that here instead of Into<*mut DIR>. I don't see this used anywhere, however. Can you either remove it if it's unnecessary, or if it's necessary add it to the test or use it in an example.

///
/// The `loc` argument should be a value returned by a previous call to `telldir`
#[cfg(not(any(target_os = "android")))]
pub fn seekdir<'a>(dir: &'a mut DirectoryStream, loc: c_long) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm thinking a newtype around c_long for use as the return value of telldir and the 2nd argument of seekdir would be good, since that's an invariant that should be embedded in the type system. I can't think of a good name for it, hopefully you can, but I think we should definitely do it.

src/dirent.rs Outdated
}
}

/// Like `opendir()`, but returns a directory stream for the directory
Copy link
Contributor

Choose a reason for hiding this comment

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

Please make the first line all one line. You can go out to 99 characters with your comments.

.unwrap()
.name()
.to_bytes()[0];
assert_eq!(letter3, '.' as u8);
Copy link
Contributor

Choose a reason for hiding this comment

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

This would be more clear if you compared letter2 and letter3 directly if that's what should be happening here.


fn test_readdir<Open>(open_fn: Open)
where Open: Fn(&Path) -> DirectoryStream
{
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you check the full name of the directory? It's confusing that you're checking just the first character of the filename. It'd also be helpful if you created a directory in the tempdir you made so you have 3 directories, then you iterate through all of them checking their names, and then seek back to check that seeking works.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is no guarantee on the order of directory entries.

Copy link
Contributor

Choose a reason for hiding this comment

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

I still think it's worth checking their names, we can treat it as a set, where the order doesn't matter but the contents should still be the same. We'd expect to encounter both "." and "..", but we aren't certain of the order, but that's fine, we can still do that check.

@Susurrus
Copy link
Contributor

Susurrus commented Jul 7, 2017

Just saw you comment about the order of . and .. varying depending on platform. I do know many platforms don't guarantee returning things in alphabetical order. Maybe you should iterate through all the directories collecting their names into a sorted list and then compare that?

@Mic92 Mic92 force-pushed the fdopendir branch 4 times, most recently from db5380b to 1c570ea Compare July 7, 2017 21:28
@Susurrus
Copy link
Contributor

@Mic92 I'd still like to get this merged, can you resolve the lingering issues and rebase this?

@Mic92
Copy link
Contributor Author

Mic92 commented Aug 18, 2017

Next week - this week I am on holiday. Maybe CHANGELOG.md should not be modified by pull requests at all. It creates to much merge conflicts and could be easily added from the pull request description after a merge.

@Mic92 Mic92 force-pushed the fdopendir branch 2 times, most recently from ce662b6 to 6035dd8 Compare September 1, 2017 10:28
@Mic92 Mic92 changed the title add support for dirent related function [WIP] add support for dirent related function Sep 1, 2017
@Mic92
Copy link
Contributor Author

Mic92 commented Sep 1, 2017

Can you restart the one failing test? Looks like a transient failure: https://travis-ci.org/nix-rust/nix/builds/270775924

@Mic92 Mic92 changed the title [WIP] add support for dirent related function add support for dirent related function Sep 1, 2017
@asomers
Copy link
Member

asomers commented Sep 1, 2017

offending test restarted

@Mic92
Copy link
Contributor Author

Mic92 commented Sep 6, 2017

if somebody is interested in dirfd and wants to make a pull request, feel free to cherry-pick from this branch: https://github.com/Mic92/nix/tree/dirfd

@Susurrus
Copy link
Contributor

Looks like there are legitimate test failures here. @Mic92 Are you interested in pushing this PR through to be merged? Based on your last comment, it sounds like you aren't interested in continuing to work on this PR. Is that correct?

@Mic92
Copy link
Contributor Author

Mic92 commented Nov 13, 2017

Yes.

@Mic92 Mic92 closed this Nov 13, 2017
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.

None yet

5 participants