From 0b9863bfcb3148247986ead64683bd9add2502fa Mon Sep 17 00:00:00 2001
From: chenjiayi <chenjiayi22@huawei.com>
Date: Wed, 21 Jun 2023 21:29:27 +0800
Subject: [PATCH] feature(libbasic): add opendirat

opendirat is used to open a directory with the directory fd.
---
 libs/basic/src/fd_util.rs | 46 +++++++++++++++++++++++++++++++++++----
 1 file changed, 42 insertions(+), 4 deletions(-)

diff --git a/libs/basic/src/fd_util.rs b/libs/basic/src/fd_util.rs
index b397472..0d41966 100644
--- a/libs/basic/src/fd_util.rs
+++ b/libs/basic/src/fd_util.rs
@@ -14,9 +14,9 @@
 use crate::error::*;
 use nix::{
     errno::Errno,
-    fcntl::{FcntlArg, FdFlag, OFlag},
+    fcntl::{openat, FcntlArg, FdFlag, OFlag},
     ioctl_read,
-    sys::stat::SFlag,
+    sys::stat::{Mode, SFlag},
 };
 
 /// check if the given stat.st_mode is regular file
@@ -157,11 +157,33 @@ pub fn fd_get_diskseq(fd: i32) -> Result<u64> {
     Ok(diskseq)
 }
 
+/// open the directory at fd
+pub fn opendirat(dirfd: i32, flags: OFlag) -> Result<nix::dir::Dir> {
+    let nfd = openat(
+        dirfd,
+        ".",
+        OFlag::O_RDONLY | OFlag::O_NONBLOCK | OFlag::O_DIRECTORY | OFlag::O_CLOEXEC | flags,
+        Mode::empty(),
+    )
+    .context(NixSnafu)?;
+
+    nix::dir::Dir::from_fd(nfd).context(NixSnafu)
+}
+
 #[cfg(test)]
 mod tests {
     use crate::fd_util::{stat_is_char, stat_is_reg};
-    use nix::sys::stat::fstat;
-    use std::{fs::File, os::unix::prelude::AsRawFd, path::Path};
+    use nix::{
+        fcntl::{open, OFlag},
+        sys::stat::{fstat, Mode},
+    };
+    use std::{
+        fs::{remove_dir_all, File},
+        os::unix::prelude::AsRawFd,
+        path::Path,
+    };
+
+    use super::opendirat;
 
     #[test]
     fn test_stats() {
@@ -185,4 +207,20 @@ mod tests {
         let st = fstat(fd_non_char_file.as_raw_fd()).unwrap();
         assert!(!stat_is_char(st.st_mode));
     }
+
+    #[test]
+    fn test_opendirat() {
+        std::fs::create_dir_all("/tmp/test_opendirat").unwrap();
+        File::create("/tmp/test_opendirat/entry0").unwrap();
+        File::create("/tmp/test_opendirat/entry1").unwrap();
+
+        let dirfd = open("/tmp/test_opendirat", OFlag::O_DIRECTORY, Mode::empty()).unwrap();
+        let mut dir = opendirat(dirfd, OFlag::O_NOFOLLOW).unwrap();
+
+        for e in dir.iter() {
+            let _ = e.unwrap();
+        }
+
+        remove_dir_all("/tmp/test_opendirat").unwrap();
+    }
 }
-- 
2.33.0

