diff --git a/CHANGELOG.md b/CHANGELOG.md
index 36b528ca91..3e6b57e176 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#739](https://github.com/nix-rust/nix/pull/739))
- Added nix::sys::ptrace::detach.
([#749](https://github.com/nix-rust/nix/pull/749))
+- Added `nix::dirent::{opendir, fdopendir, readdir, telldir, seekdir}`
+ ([#558](https://github.com/nix-rust/nix/pull/558))
### Changed
- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692))
diff --git a/src/dirent.rs b/src/dirent.rs
new file mode 100644
index 0000000000..a29a33696b
--- /dev/null
+++ b/src/dirent.rs
@@ -0,0 +1,135 @@
+//! Directory Stream functions
+//!
+//! [Further reading and details on the C API](http://man7.org/linux/man-pages/man3/opendir.3.html)
+
+use {Result, Error, Errno, NixPath};
+use errno;
+use libc::{self, DIR, c_long};
+use std::convert::{AsRef, Into};
+use std::ffi::CStr;
+use std::mem;
+
+#[cfg(any(target_os = "linux"))]
+use libc::ino64_t;
+
+#[cfg(any(target_os = "android"))]
+use libc::ino_t as ino64_t;
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+use libc::{dirent64, readdir64};
+
+#[cfg(not(any(target_os = "linux", target_os = "android")))]
+use libc::{dirent as dirent64, ino_t as ino64_t, readdir as readdir64};
+
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+use std::os::unix::io::RawFd;
+
+/// Directory Stream object
+pub struct DirectoryStream(*mut DIR);
+
+impl AsRef
for DirectoryStream {
+ fn as_ref(&self) -> &DIR {
+ unsafe { &*self.0 }
+ }
+}
+
+/// Consumes directory stream and return underlying directory pointer.
+///
+/// The pointer must be deallocated manually using `libc::closedir`
+impl Into<*mut DIR> for DirectoryStream {
+ fn into(self) -> *mut DIR {
+ let dirp = self.0;
+ mem::forget(self);
+ dirp
+ }
+}
+
+impl Drop for DirectoryStream {
+ fn drop(&mut self) {
+ unsafe { libc::closedir(self.0) };
+ }
+}
+
+/// A directory entry
+pub struct DirectoryEntry<'a>(&'a dirent64);
+
+impl<'a> DirectoryEntry<'a> {
+ /// File name
+ pub fn name(&self) -> &CStr {
+ unsafe{
+ CStr::from_ptr(self.0.d_name.as_ptr())
+ }
+ }
+
+ /// Inode number
+ pub fn inode(&self) -> ino64_t {
+ #[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os="dragonfly")))]
+ return self.0.d_ino;
+ #[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os="dragonfly"))]
+ return self.0.d_fileno;
+ }
+}
+
+impl<'a> AsRef for DirectoryEntry<'a> {
+ fn as_ref(&self) -> &dirent64 {
+ self.0
+ }
+}
+
+/// Opens a directory stream corresponding to the directory name.
+///
+/// The stream is positioned at the first entry in the directory.
+pub fn opendir(name: &P) -> Result {
+ let dirp = try!(name.with_nix_path(|cstr| unsafe { libc::opendir(cstr.as_ptr()) }));
+ if dirp.is_null() {
+ Err(Error::last().into())
+ } else {
+ Ok(DirectoryStream(dirp))
+ }
+}
+
+/// Returns directory stream corresponding to the open file descriptor `fd`
+///
+/// After a successful call to this function, `fd` is used internally by
+/// the implementation, and should not otherwise be used by the application
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+pub fn fdopendir(fd: RawFd) -> Result {
+ let dirp = unsafe { libc::fdopendir(fd) };
+ if dirp.is_null() {
+ Err(Error::last().into())
+ } else {
+ Ok(DirectoryStream(dirp))
+ }
+}
+
+/// Returns the next directory entry in the directory stream.
+///
+/// It returns `Some(None)` on reaching the end of the directory stream.
+pub fn readdir<'a>(dir: &'a mut DirectoryStream) -> Result