openat/
dir.rs

1use std::io;
2use std::mem;
3use std::ffi::{OsString, CStr};
4use std::fs::{File, read_link};
5use std::os::unix::io::{AsRawFd, RawFd, FromRawFd, IntoRawFd};
6use std::os::unix::ffi::{OsStringExt};
7use std::path::{PathBuf};
8
9use libc;
10use crate::metadata::{self, Metadata};
11use crate::list::{DirIter, open_dir, open_dirfd};
12
13use crate::{Dir, AsPath};
14
15#[cfg(target_os="linux")]
16const BASE_OPEN_FLAGS: libc::c_int = libc::O_PATH|libc::O_CLOEXEC;
17#[cfg(target_os="freebsd")]
18const BASE_OPEN_FLAGS: libc::c_int = libc::O_DIRECTORY|libc::O_CLOEXEC;
19#[cfg(not(any(target_os="linux", target_os="freebsd")))]
20const BASE_OPEN_FLAGS: libc::c_int = libc::O_CLOEXEC;
21
22impl Dir {
23    /// Creates a directory descriptor that resolves paths relative to current
24    /// working directory (AT_FDCWD)
25    #[deprecated(since="0.1.15", note="\
26        Use `Dir::open(\".\")` instead. \
27        Dir::cwd() doesn't open actual file descriptor and uses magic value \
28        instead which resolves to current dir on any syscall invocation. \
29        This is usually counter-intuitive and yields a broken \
30        file descriptor when using `Dir::as_raw_fd`. \
31        Will be removed in version v0.2 of the library.")]
32    pub fn cwd() -> Dir {
33        Dir(libc::AT_FDCWD)
34    }
35
36    /// Open a directory descriptor at specified path
37    // TODO(tailhook) maybe accept only absolute paths?
38    pub fn open<P: AsPath>(path: P) -> io::Result<Dir> {
39        Dir::_open(to_cstr(path)?.as_ref())
40    }
41
42    fn _open(path: &CStr) -> io::Result<Dir> {
43        let fd = unsafe {
44            libc::open(path.as_ptr(), BASE_OPEN_FLAGS)
45        };
46        if fd < 0 {
47            Err(io::Error::last_os_error())
48        } else {
49            Ok(Dir(fd))
50        }
51    }
52
53    /// List subdirectory of this dir
54    ///
55    /// You can list directory itself with `list_self`.
56    pub fn list_dir<P: AsPath>(&self, path: P) -> io::Result<DirIter> {
57        open_dir(self, to_cstr(path)?.as_ref())
58    }
59
60    /// List this dir
61    pub fn list_self(&self) -> io::Result<DirIter> {
62        unsafe {
63            open_dirfd(libc::dup(self.0))
64        }
65    }
66
67    /// Open subdirectory
68    ///
69    /// Note that this method does not resolve symlinks by default, so you may have to call
70    /// [`read_link`] to resolve the real path first.
71    ///
72    /// [`read_link`]: #method.read_link
73    pub fn sub_dir<P: AsPath>(&self, path: P) -> io::Result<Dir> {
74        self._sub_dir(to_cstr(path)?.as_ref())
75    }
76
77    fn _sub_dir(&self, path: &CStr) -> io::Result<Dir> {
78        let fd = unsafe {
79            libc::openat(self.0,
80                        path.as_ptr(),
81                        BASE_OPEN_FLAGS|libc::O_NOFOLLOW)
82        };
83        if fd < 0 {
84            Err(io::Error::last_os_error())
85        } else {
86            Ok(Dir(fd))
87        }
88    }
89
90    /// Read link in this directory
91    pub fn read_link<P: AsPath>(&self, path: P) -> io::Result<PathBuf> {
92        self._read_link(to_cstr(path)?.as_ref())
93    }
94
95    fn _read_link(&self, path: &CStr) -> io::Result<PathBuf> {
96        let mut buf = vec![0u8; 4096];
97        let res = unsafe {
98            libc::readlinkat(self.0,
99                        path.as_ptr(),
100                        buf.as_mut_ptr() as *mut libc::c_char, buf.len())
101        };
102        if res < 0 {
103            Err(io::Error::last_os_error())
104        } else {
105            buf.truncate(res as usize);
106            Ok(OsString::from_vec(buf).into())
107        }
108    }
109
110    /// Open file for reading in this directory
111    ///
112    /// Note that this method does not resolve symlinks by default, so you may have to call
113    /// [`read_link`] to resolve the real path first.
114    ///
115    /// [`read_link`]: #method.read_link
116    pub fn open_file<P: AsPath>(&self, path: P) -> io::Result<File> {
117        self._open_file(to_cstr(path)?.as_ref(),
118            libc::O_RDONLY, 0)
119    }
120
121    /// Open file for writing, create if necessary, truncate on open
122    ///
123    /// If there exists a symlink at the destination path, this method will fail. In that case, you
124    /// will need to remove the symlink before calling this method. If you are on Linux, you can
125    /// alternatively create an unnamed file with [`new_unnamed_file`] and then rename it,
126    /// clobbering the symlink at the destination.
127    ///
128    /// [`new_unnamed_file`]: #method.new_unnamed_file
129    pub fn write_file<P: AsPath>(&self, path: P, mode: libc::mode_t)
130        -> io::Result<File>
131    {
132        self._open_file(to_cstr(path)?.as_ref(),
133            libc::O_CREAT|libc::O_WRONLY|libc::O_TRUNC,
134            mode)
135    }
136
137    /// Open file for append, create if necessary
138    ///
139    /// If there exists a symlink at the destination path, this method will fail. In that case, you
140    /// will need to call [`read_link`] to resolve the real path first.
141    ///
142    /// [`read_link`]: #method.read_link
143    pub fn append_file<P: AsPath>(&self, path: P, mode: libc::mode_t)
144        -> io::Result<File>
145    {
146        self._open_file(to_cstr(path)?.as_ref(),
147            libc::O_CREAT|libc::O_WRONLY|libc::O_APPEND,
148            mode)
149    }
150
151    /// Create file for writing (and truncate) in this directory
152    ///
153    /// Deprecated alias for `write_file`
154    ///
155    /// If there exists a symlink at the destination path, this method will fail. In that case, you
156    /// will need to remove the symlink before calling this method. If you are on Linux, you can
157    /// alternatively create an unnamed file with [`new_unnamed_file`] and then rename it,
158    /// clobbering the symlink at the destination.
159    ///
160    /// [`new_unnamed_file`]: #method.new_unnamed_file
161    #[deprecated(since="0.1.7", note="please use `write_file` instead")]
162    pub fn create_file<P: AsPath>(&self, path: P, mode: libc::mode_t)
163        -> io::Result<File>
164    {
165        self._open_file(to_cstr(path)?.as_ref(),
166            libc::O_CREAT|libc::O_WRONLY|libc::O_TRUNC,
167            mode)
168    }
169
170    /// Create a tmpfile in this directory which isn't linked to any filename
171    ///
172    /// This works by passing `O_TMPFILE` into the openat call. The flag is
173    /// supported only on linux. So this function always returns error on
174    /// such systems.
175    ///
176    /// **WARNING!** On glibc < 2.22 file permissions of the newly created file
177    /// may be arbitrary. Consider chowning after creating a file.
178    ///
179    /// Note: It may be unclear why creating unnamed file requires a dir. There
180    /// are two reasons:
181    ///
182    /// 1. It's created (and occupies space) on a real filesystem, so the
183    ///    directory is a way to find out which filesystem to attach file to
184    /// 2. This method is mostly needed to initialize the file then link it
185    ///    using ``link_file_at`` to the real directory entry. When linking
186    ///    it must be linked into the same filesystem. But because for most
187    ///    programs finding out filesystem layout is an overkill the rule of
188    ///    thumb is to create a file in the the target directory.
189    ///
190    /// Currently, we recommend to fallback on any error if this operation
191    /// can't be accomplished rather than relying on specific error codes,
192    /// because semantics of errors are very ugly.
193    #[cfg(target_os="linux")]
194    pub fn new_unnamed_file(&self, mode: libc::mode_t)
195        -> io::Result<File>
196    {
197        self._open_file(unsafe { CStr::from_bytes_with_nul_unchecked(b".\0") },
198            libc::O_TMPFILE|libc::O_WRONLY,
199            mode)
200    }
201
202    /// Create a tmpfile in this directory which isn't linked to any filename
203    ///
204    /// This works by passing `O_TMPFILE` into the openat call. The flag is
205    /// supported only on linux. So this function always returns error on
206    /// such systems.
207    ///
208    /// Note: It may be unclear why creating unnamed file requires a dir. There
209    /// are two reasons:
210    ///
211    /// 1. It's created (and occupies space) on a real filesystem, so the
212    ///    directory is a way to find out which filesystem to attach file to
213    /// 2. This method is mostly needed to initialize the file then link it
214    ///    using ``link_file_at`` to the real directory entry. When linking
215    ///    it must be linked into the same filesystem. But because for most
216    ///    programs finding out filesystem layout is an overkill the rule of
217    ///    thumb is to create a file in the the target directory.
218    ///
219    /// Currently, we recommend to fallback on any error if this operation
220    /// can't be accomplished rather than relying on specific error codes,
221    /// because semantics of errors are very ugly.
222    #[cfg(not(target_os="linux"))]
223    pub fn new_unnamed_file<P: AsPath>(&self, _mode: libc::mode_t)
224        -> io::Result<File>
225    {
226        Err(io::Error::new(io::ErrorKind::Other,
227            "creating unnamed tmpfiles is only supported on linux"))
228    }
229
230    /// Link open file to a specified path
231    ///
232    /// This is used with ``new_unnamed_file()`` to create and initialize the
233    /// file before linking it into a filesystem. This requires `/proc` to be
234    /// mounted and works **only on linux**.
235    ///
236    /// On systems other than linux this always returns error. It's expected
237    /// that in most cases this methos is not called if ``new_unnamed_file``
238    /// fails. But in obscure scenarios where `/proc` is not mounted this
239    /// method may fail even on linux. So your code should be able to fallback
240    /// to a named file if this method fails too.
241    #[cfg(target_os="linux")]
242    pub fn link_file_at<F: AsRawFd, P: AsPath>(&self, file: &F, path: P)
243        -> io::Result<()>
244    {
245        let fd_path = format!("/proc/self/fd/{}", file.as_raw_fd());
246        _hardlink(&Dir(libc::AT_FDCWD), to_cstr(fd_path)?.as_ref(),
247            &self, to_cstr(path)?.as_ref(),
248            libc::AT_SYMLINK_FOLLOW)
249    }
250
251    /// Link open file to a specified path
252    ///
253    /// This is used with ``new_unnamed_file()`` to create and initialize the
254    /// file before linking it into a filesystem. This requires `/proc` to be
255    /// mounted and works **only on linux**.
256    ///
257    /// On systems other than linux this always returns error. It's expected
258    /// that in most cases this methos is not called if ``new_unnamed_file``
259    /// fails. But in obscure scenarios where `/proc` is not mounted this
260    /// method may fail even on linux. So your code should be able to fallback
261    /// to a named file if this method fails too.
262    #[cfg(not(target_os="linux"))]
263    pub fn link_file_at<F: AsRawFd, P: AsPath>(&self, _file: F, _path: P)
264        -> io::Result<()>
265    {
266        Err(io::Error::new(io::ErrorKind::Other,
267            "linking unnamed fd to directories is only supported on linux"))
268    }
269
270    /// Create file if not exists, fail if exists
271    ///
272    /// This function checks existence and creates file atomically with
273    /// respect to other threads and processes.
274    ///
275    /// Technically it means passing `O_EXCL` flag to open.
276    pub fn new_file<P: AsPath>(&self, path: P, mode: libc::mode_t)
277        -> io::Result<File>
278    {
279        self._open_file(to_cstr(path)?.as_ref(),
280            libc::O_CREAT|libc::O_EXCL|libc::O_WRONLY,
281            mode)
282    }
283
284    /// Open file for reading and writing without truncation, create if needed
285    ///
286    /// If there exists a symlink at the destination path, this method will fail. In that case, you
287    /// will need to call [`read_link`] to resolve the real path first.
288    ///
289    /// [`read_link`]: #method.read_link
290    pub fn update_file<P: AsPath>(&self, path: P, mode: libc::mode_t)
291        -> io::Result<File>
292    {
293        self._open_file(to_cstr(path)?.as_ref(),
294            libc::O_CREAT|libc::O_RDWR,
295            mode)
296    }
297
298    fn _open_file(&self, path: &CStr, flags: libc::c_int, mode: libc::mode_t)
299        -> io::Result<File>
300    {
301        unsafe {
302            // Note: In below call to `openat`, *mode* must be cast to
303            // `unsigned` because the optional `mode` argument to `openat` is
304            // variadic in the signature. Since integers are not implicitly
305            // promoted as they are in C this would break on Freebsd where
306            // *mode_t* is an alias for `uint16_t`.
307            let res = libc::openat(self.0, path.as_ptr(),
308                            flags|libc::O_CLOEXEC|libc::O_NOFOLLOW,
309                            mode as libc::c_uint);
310            if res < 0 {
311                Err(io::Error::last_os_error())
312            } else {
313                Ok(File::from_raw_fd(res))
314            }
315        }
316    }
317
318    /// Make a symlink in this directory
319    ///
320    /// Note: the order of arguments differ from `symlinkat`
321    pub fn symlink<P: AsPath, R: AsPath>(&self, path: P, value: R)
322        -> io::Result<()>
323    {
324        self._symlink(to_cstr(path)?.as_ref(), to_cstr(value)?.as_ref())
325    }
326    fn _symlink(&self, path: &CStr, link: &CStr) -> io::Result<()> {
327        unsafe {
328            let res = libc::symlinkat(link.as_ptr(),
329                self.0, path.as_ptr());
330            if res < 0 {
331                Err(io::Error::last_os_error())
332            } else {
333                Ok(())
334            }
335        }
336    }
337
338    /// Create a subdirectory in this directory
339    pub fn create_dir<P: AsPath>(&self, path: P, mode: libc::mode_t)
340        -> io::Result<()>
341    {
342        self._create_dir(to_cstr(path)?.as_ref(), mode)
343    }
344    fn _create_dir(&self, path: &CStr, mode: libc::mode_t) -> io::Result<()> {
345        unsafe {
346            let res = libc::mkdirat(self.0, path.as_ptr(), mode);
347            if res < 0 {
348                Err(io::Error::last_os_error())
349            } else {
350                Ok(())
351            }
352        }
353    }
354
355    /// Rename a file in this directory to another name (keeping same dir)
356    pub fn local_rename<P: AsPath, R: AsPath>(&self, old: P, new: R)
357        -> io::Result<()>
358    {
359        rename(self, to_cstr(old)?.as_ref(), self, to_cstr(new)?.as_ref())
360    }
361
362    /// Similar to `local_rename` but atomically swaps both paths
363    ///
364    /// Only supported on Linux.
365    #[cfg(target_os="linux")]
366    pub fn local_exchange<P: AsPath, R: AsPath>(&self, old: P, new: R)
367        -> io::Result<()>
368    {
369        // Workaround https://github.com/tailhook/openat/issues/35
370        // AKA https://github.com/rust-lang/libc/pull/2116
371        // Unfortunately since we made this libc::c_int in our
372        // public API, we can't easily change it right now.
373        let flags = libc::RENAME_EXCHANGE as libc::c_int;
374        rename_flags(self, to_cstr(old)?.as_ref(),
375            self, to_cstr(new)?.as_ref(),
376            flags)
377    }
378
379    /// Remove a subdirectory in this directory
380    ///
381    /// Note only empty directory may be removed
382    pub fn remove_dir<P: AsPath>(&self, path: P)
383        -> io::Result<()>
384    {
385        self._unlink(to_cstr(path)?.as_ref(), libc::AT_REMOVEDIR)
386    }
387    /// Remove a file in this directory
388    pub fn remove_file<P: AsPath>(&self, path: P)
389        -> io::Result<()>
390    {
391        self._unlink(to_cstr(path)?.as_ref(), 0)
392    }
393    fn _unlink(&self, path: &CStr, flags: libc::c_int) -> io::Result<()> {
394        unsafe {
395            let res = libc::unlinkat(self.0, path.as_ptr(), flags);
396            if res < 0 {
397                Err(io::Error::last_os_error())
398            } else {
399                Ok(())
400            }
401        }
402    }
403
404    /// Get the path of this directory (if possible)
405    ///
406    /// This uses symlinks in `/proc/self`, they sometimes may not be
407    /// available so use with care.
408    pub fn recover_path(&self) -> io::Result<PathBuf> {
409        let fd = self.0;
410        if fd != libc::AT_FDCWD {
411            read_link(format!("/proc/self/fd/{}", fd))
412        } else {
413            read_link("/proc/self/cwd")
414        }
415    }
416
417    /// Returns metadata of an entry in this directory
418    ///
419    /// If the destination path is a symlink, this will return the metadata of the symlink itself.
420    /// If you would like to follow the symlink and return the metadata of the target, you will
421    /// have to call [`read_link`] to resolve the real path first.
422    ///
423    /// [`read_link`]: #method.read_link
424    pub fn metadata<P: AsPath>(&self, path: P) -> io::Result<Metadata> {
425        self._stat(to_cstr(path)?.as_ref(), libc::AT_SYMLINK_NOFOLLOW)
426    }
427    fn _stat(&self, path: &CStr, flags: libc::c_int) -> io::Result<Metadata> {
428        unsafe {
429            let mut stat = mem::zeroed();
430            let res = libc::fstatat(self.0, path.as_ptr(),
431                &mut stat, flags);
432            if res < 0 {
433                Err(io::Error::last_os_error())
434            } else {
435                Ok(metadata::new(stat))
436            }
437        }
438    }
439
440    /// Returns the metadata of the directory itself.
441    pub fn self_metadata(&self) -> io::Result<Metadata> {
442        unsafe {
443            let mut stat = mem::zeroed();
444            let res = libc::fstat(self.0, &mut stat);
445            if res < 0 {
446                Err(io::Error::last_os_error())
447            } else {
448                Ok(metadata::new(stat))
449            }
450        }
451    }
452
453    /// Constructs a new `Dir` from a given raw file descriptor,
454    /// ensuring it is a directory file descriptor first.
455    ///
456    /// This function **consumes ownership** of the specified file
457    /// descriptor. The returned `Dir` will take responsibility for
458    /// closing it when it goes out of scope.
459    pub unsafe fn from_raw_fd_checked(fd: RawFd) -> io::Result<Self> {
460        let mut stat = mem::zeroed();
461        let res = libc::fstat(fd, &mut stat);
462        if res < 0 {
463            Err(io::Error::last_os_error())
464        } else {
465            match stat.st_mode & libc::S_IFMT {
466                libc::S_IFDIR => Ok(Dir(fd)),
467                _ => Err(io::Error::from_raw_os_error(libc::ENOTDIR))
468            }
469        }
470    }
471
472    /// Creates a new independently owned handle to the underlying directory.
473    pub fn try_clone(&self) -> io::Result<Self> {
474        let fd = unsafe { libc::dup(self.0) };
475        if fd == -1 {
476            Err(io::Error::last_os_error())
477        } else {
478            unsafe { Self::from_raw_fd_checked(fd) }
479        }
480    }
481}
482
483/// Rename (move) a file between directories
484///
485/// Files must be on a single filesystem anyway. This funtion does **not**
486/// fallback to copying if needed.
487pub fn rename<P, R>(old_dir: &Dir, old: P, new_dir: &Dir, new: R)
488    -> io::Result<()>
489    where P: AsPath, R: AsPath,
490{
491    _rename(old_dir, to_cstr(old)?.as_ref(), new_dir, to_cstr(new)?.as_ref())
492}
493
494fn _rename(old_dir: &Dir, old: &CStr, new_dir: &Dir, new: &CStr)
495    -> io::Result<()>
496{
497    unsafe {
498        let res = libc::renameat(old_dir.0, old.as_ptr(),
499            new_dir.0, new.as_ptr());
500        if res < 0 {
501            Err(io::Error::last_os_error())
502        } else {
503            Ok(())
504        }
505    }
506}
507
508/// Create a hardlink to a file
509///
510/// Files must be on a single filesystem even if they are in different
511/// directories.
512///
513/// Note: by default ``linkat`` syscall doesn't resolve symbolic links, and
514/// it's also behavior of this function. It's recommended to resolve symlinks
515/// manually if needed.
516pub fn hardlink<P, R>(old_dir: &Dir, old: P, new_dir: &Dir, new: R)
517    -> io::Result<()>
518    where P: AsPath, R: AsPath,
519{
520    _hardlink(old_dir, to_cstr(old)?.as_ref(),
521              new_dir, to_cstr(new)?.as_ref(),
522              0)
523}
524
525fn _hardlink(old_dir: &Dir, old: &CStr, new_dir: &Dir, new: &CStr,
526             flags: libc::c_int)
527    -> io::Result<()>
528{
529    unsafe {
530        let res = libc::linkat(old_dir.0, old.as_ptr(),
531            new_dir.0, new.as_ptr(), flags);
532        if res < 0 {
533            Err(io::Error::last_os_error())
534        } else {
535            Ok(())
536        }
537    }
538}
539
540/// Rename (move) a file between directories with flags
541///
542/// Files must be on a single filesystem anyway. This funtion does **not**
543/// fallback to copying if needed.
544///
545/// Only supported on Linux.
546#[cfg(target_os="linux")]
547pub fn rename_flags<P, R>(old_dir: &Dir, old: P, new_dir: &Dir, new: R,
548    flags: libc::c_int)
549    -> io::Result<()>
550    where P: AsPath, R: AsPath,
551{
552    _rename_flags(old_dir, to_cstr(old)?.as_ref(),
553        new_dir, to_cstr(new)?.as_ref(),
554        flags)
555}
556
557#[cfg(target_os="linux")]
558fn _rename_flags(old_dir: &Dir, old: &CStr, new_dir: &Dir, new: &CStr,
559    flags: libc::c_int)
560    -> io::Result<()>
561{
562    unsafe {
563        let res = libc::syscall(
564            libc::SYS_renameat2,
565            old_dir.0, old.as_ptr(),
566            new_dir.0, new.as_ptr(), flags);
567        if res < 0 {
568            Err(io::Error::last_os_error())
569        } else {
570            Ok(())
571        }
572    }
573}
574
575impl AsRawFd for Dir {
576    #[inline]
577    fn as_raw_fd(&self) -> RawFd {
578        self.0
579    }
580}
581
582impl FromRawFd for Dir {
583    /// The user must guarantee that the passed in `RawFd` is in fact
584    /// a directory file descriptor.
585    #[inline]
586    unsafe fn from_raw_fd(fd: RawFd) -> Dir {
587        Dir(fd)
588    }
589}
590
591impl IntoRawFd for Dir {
592    #[inline]
593    fn into_raw_fd(self) -> RawFd {
594        let result = self.0;
595        mem::forget(self);
596        return result;
597    }
598}
599
600impl Drop for Dir {
601    fn drop(&mut self) {
602        let fd = self.0;
603        if fd != libc::AT_FDCWD {
604            unsafe {
605                libc::close(fd);
606            }
607        }
608    }
609}
610
611fn to_cstr<P: AsPath>(path: P) -> io::Result<P::Buffer> {
612    path.to_path()
613    .ok_or_else(|| {
614        io::Error::new(io::ErrorKind::InvalidInput,
615                       "nul byte in file name")
616    })
617}
618
619#[cfg(test)]
620mod test {
621    use std::io::{Read};
622    use std::path::Path;
623    use std::os::unix::io::{FromRawFd, IntoRawFd};
624    use crate::{Dir};
625
626    #[test]
627    fn test_open_ok() {
628        assert!(Dir::open("src").is_ok());
629    }
630
631    #[test]
632    #[cfg_attr(target_os="freebsd", should_panic(expected="Not a directory"))]
633    fn test_open_file() {
634        Dir::open("src/lib.rs").unwrap();
635    }
636
637    #[test]
638    fn test_read_file() {
639        let dir = Dir::open("src").unwrap();
640        let mut buf = String::new();
641        dir.open_file("lib.rs").unwrap()
642            .read_to_string(&mut buf).unwrap();
643        assert!(buf.find("extern crate libc;").is_some());
644    }
645
646    #[test]
647    fn test_from_into() {
648        let dir = Dir::open("src").unwrap();
649        let dir = unsafe { Dir::from_raw_fd(dir.into_raw_fd()) };
650        let mut buf = String::new();
651        dir.open_file("lib.rs").unwrap()
652            .read_to_string(&mut buf).unwrap();
653        assert!(buf.find("extern crate libc;").is_some());
654    }
655
656    #[test]
657    #[should_panic(expected="No such file or directory")]
658    fn test_open_no_dir() {
659        Dir::open("src/some-non-existent-file").unwrap();
660    }
661
662    #[test]
663    fn test_list() {
664        let dir = Dir::open("src").unwrap();
665        let me = dir.list_dir(".").unwrap();
666        assert!(me.collect::<Result<Vec<_>, _>>().unwrap()
667                .iter().find(|x| {
668                    x.file_name() == Path::new("lib.rs").as_os_str()
669                })
670                .is_some());
671    }
672
673    #[test]
674    fn test_from_raw_fd_checked() {
675        let fd = Dir::open(".").unwrap().into_raw_fd();
676        let dir = unsafe { Dir::from_raw_fd_checked(fd) }.unwrap();
677        let filefd = dir.open_file("src/lib.rs").unwrap().into_raw_fd();
678        match unsafe { Dir::from_raw_fd_checked(filefd) } {
679            Ok(_) => assert!(false, "from_raw_fd_checked succeeded on a non-directory fd!"),
680            Err(e) => assert_eq!(e.raw_os_error().unwrap(), libc::ENOTDIR)
681        }
682    }
683
684    #[test]
685    fn test_try_clone() {
686        let d = Dir::open(".").unwrap();
687        let d2 = d.try_clone().unwrap();
688        drop(d);
689        let _file = d2.open_file("src/lib.rs").unwrap();
690    }
691}