From ae19e57d309303b4f8e16ea26dbe6c6c7fd04f7d Mon Sep 17 00:00:00 2001
From: chenjiayi <chenjiayi22@huawei.com>
Date: Tue, 27 Jun 2023 13:57:20 +0800
Subject: [PATCH] fix(devmaster): deal with fd leak

RawFd will not be automatically closed when it is dropped. Use std::fs::File
or nix::dir::Dir to take the RawFd when opening the file or directory, which
can automatically close the RawFd when the variable is dropped.

Also rename the fd type from i32 to RawFd to better declare the file descriptor
type.
---
 core/bin/mount/setup.rs                 |   9 +-
 libs/basic/src/fd_util.rs               |  18 ++--
 libs/basic/src/fs_util.rs               |  28 +++---
 libs/basic/src/mount_util.rs            |   4 +-
 8 files changed, 146 insertions(+), 107 deletions(-)

diff --git a/core/bin/mount/setup.rs b/core/bin/mount/setup.rs
index 6a13626..e7ca9df 100644
--- a/core/bin/mount/setup.rs
+++ b/core/bin/mount/setup.rs
@@ -23,6 +23,7 @@ use nix::{
     sys::stat::Mode,
     unistd::AccessFlags,
 };
+use std::os::unix::prelude::AsRawFd;
 use std::{
     collections::HashMap,
     fs,
@@ -361,7 +362,7 @@ impl MountPoint {
         // symlink
 
         let path = Path::new(&self.target);
-        let ret = fs_util::open_parent(
+        let file = fs_util::open_parent(
             path,
             OFlag::O_PATH | OFlag::O_CLOEXEC,
             Mode::from_bits(0).unwrap(),
@@ -369,7 +370,11 @@ impl MountPoint {
 
         let last_file_name = path.file_name().unwrap_or_default();
 
-        let ret = mount_util::mount_point_fd_valid(ret, last_file_name.to_str().unwrap(), flags)?;
+        let ret = mount_util::mount_point_fd_valid(
+            file.as_raw_fd(),
+            last_file_name.to_str().unwrap(),
+            flags,
+        )?;
 
         Ok(ret)
     }
diff --git a/libs/basic/src/fd_util.rs b/libs/basic/src/fd_util.rs
index 0d41966..210c830 100644
--- a/libs/basic/src/fd_util.rs
+++ b/libs/basic/src/fd_util.rs
@@ -18,6 +18,8 @@ use nix::{
     ioctl_read,
     sys::stat::{Mode, SFlag},
 };
+use std::os::unix::prelude::FromRawFd;
+use std::{fs::File, os::unix::prelude::RawFd};
 
 /// check if the given stat.st_mode is regular file
 pub fn stat_is_reg(st_mode: u32) -> bool {
@@ -30,7 +32,7 @@ pub fn stat_is_char(st_mode: u32) -> bool {
 }
 
 ///
-pub fn fd_nonblock(fd: i32, nonblock: bool) -> Result<()> {
+pub fn fd_nonblock(fd: RawFd, nonblock: bool) -> Result<()> {
     assert!(fd >= 0);
 
     let flags = nix::fcntl::fcntl(fd, FcntlArg::F_GETFL).context(NixSnafu)?;
@@ -51,7 +53,7 @@ pub fn fd_nonblock(fd: i32, nonblock: bool) -> Result<()> {
 }
 
 ///
-pub fn fd_cloexec(fd: i32, cloexec: bool) -> Result<()> {
+pub fn fd_cloexec(fd: RawFd, cloexec: bool) -> Result<()> {
     assert!(fd >= 0);
 
     let flags = nix::fcntl::fcntl(fd, FcntlArg::F_GETFD).context(NixSnafu)?;
@@ -69,7 +71,7 @@ pub fn fd_cloexec(fd: i32, cloexec: bool) -> Result<()> {
 }
 
 ///
-pub fn fd_is_cloexec(fd: i32) -> bool {
+pub fn fd_is_cloexec(fd: RawFd) -> bool {
     assert!(fd >= 0);
 
     let flags = nix::fcntl::fcntl(fd, FcntlArg::F_GETFD).unwrap_or(0);
@@ -78,7 +80,7 @@ pub fn fd_is_cloexec(fd: i32) -> bool {
 }
 
 ///
-pub fn close(fd: i32) {
+pub fn close(fd: RawFd) {
     if let Err(e) = nix::unistd::close(fd) {
         log::warn!("close fd {} failed, errno: {}", fd, e);
     }
@@ -90,12 +92,12 @@ pub fn close(fd: i32) {
 /// this function can not work on sockets, as they can not be opened
 ///
 /// note that this function implicitly reset the read index to zero
-pub fn fd_reopen(fd: i32, oflags: OFlag) -> Result<i32> {
+pub fn fd_reopen(fd: RawFd, oflags: OFlag) -> Result<File> {
     if oflags.intersects(OFlag::O_DIRECTORY) {
         let new_fd = nix::fcntl::openat(fd, ".", oflags, nix::sys::stat::Mode::empty())
             .map_err(|e| Error::Nix { source: e })?;
 
-        return Ok(new_fd);
+        return Ok(unsafe { File::from_raw_fd(new_fd) });
     }
 
     match nix::fcntl::open(
@@ -103,7 +105,7 @@ pub fn fd_reopen(fd: i32, oflags: OFlag) -> Result<i32> {
         oflags,
         nix::sys::stat::Mode::empty(),
     ) {
-        Ok(n) => Ok(n),
+        Ok(n) => Ok(unsafe { File::from_raw_fd(n) }),
         Err(e) => {
             if e != Errno::ENOENT {
                 return Err(Error::Nix { source: e });
@@ -137,7 +139,7 @@ ioctl_read!(
 );
 
 /// get the diskseq according to fd
-pub fn fd_get_diskseq(fd: i32) -> Result<u64> {
+pub fn fd_get_diskseq(fd: RawFd) -> Result<u64> {
     let mut diskseq: u64 = 0;
     let ptr: *mut u64 = &mut diskseq;
     unsafe {
diff --git a/libs/basic/src/fs_util.rs b/libs/basic/src/fs_util.rs
index 3f72658..8a8f276 100644
--- a/libs/basic/src/fs_util.rs
+++ b/libs/basic/src/fs_util.rs
@@ -22,16 +22,20 @@ use nix::{
 use pathdiff::diff_paths;
 use rand::Rng;
 use std::{
-    ffi::CString, fs::remove_dir, io::ErrorKind, os::unix::prelude::PermissionsExt, path::Path,
+    ffi::CString,
+    fs::{remove_dir, File},
+    io::ErrorKind,
+    os::unix::prelude::{AsRawFd, FromRawFd, PermissionsExt, RawFd},
+    path::Path,
 };
 
 /// open the parent directory of path
-pub fn open_parent(path: &Path, flags: OFlag, mode: Mode) -> Result<i32> {
+pub fn open_parent(path: &Path, flags: OFlag, mode: Mode) -> Result<File> {
     let parent = path.parent().ok_or(Error::Nix {
         source: nix::errno::Errno::EINVAL,
     })?;
 
-    nix::fcntl::open(parent, flags, mode).context(NixSnafu)
+    Ok(unsafe { File::from_raw_fd(nix::fcntl::open(parent, flags, mode).context(NixSnafu)?) })
 }
 
 /// create symlink link_name -> target
@@ -39,7 +43,7 @@ pub fn symlink(target: &str, link: &str, relative: bool) -> Result<()> {
     let link_path = Path::new(&link);
     let target_path = Path::new(&target);
 
-    let (target_path, dirfd) = if relative {
+    let (target_path, dir) = if relative {
         let link_path_parent = link_path.parent().ok_or(Error::NotExisted {
             what: format!("{}'s parent", link_path.to_string_lossy()),
         })?;
@@ -51,19 +55,19 @@ pub fn symlink(target: &str, link: &str, relative: bool) -> Result<()> {
             Mode::from_bits(0).unwrap(),
         )
         .context(NixSnafu)?;
-        (rel_path, Some(fd))
+        (rel_path, Some(unsafe { File::from_raw_fd(fd) }))
     } else {
         (target_path.to_path_buf(), None)
     };
 
     let mut rng = rand::thread_rng();
-
     let tmp_to = format!("{}.{}", link, rng.gen::<u32>());
+    let raw_fd = dir.map(|f| f.as_raw_fd());
 
-    nix::unistd::symlinkat(target_path.as_path(), dirfd, tmp_to.as_str()).context(NixSnafu)?;
+    nix::unistd::symlinkat(target_path.as_path(), raw_fd, tmp_to.as_str()).context(NixSnafu)?;
 
-    if let Err(e) = renameat(dirfd, tmp_to.as_str(), dirfd, link_path) {
-        let _ = unlinkat(dirfd, tmp_to.as_str(), UnlinkatFlags::NoRemoveDir);
+    if let Err(e) = renameat(raw_fd, tmp_to.as_str(), raw_fd, link_path) {
+        let _ = unlinkat(raw_fd, tmp_to.as_str(), UnlinkatFlags::NoRemoveDir);
         return Err(Error::Nix { source: e });
     }
 
@@ -71,7 +75,7 @@ pub fn symlink(target: &str, link: &str, relative: bool) -> Result<()> {
 }
 
 /// chmod based on fd opened with O_PATH
-pub fn fchmod_opath(fd: i32, mode: mode_t) -> Result<()> {
+pub fn fchmod_opath(fd: RawFd, mode: mode_t) -> Result<()> {
     let fd_path = format_proc_fd_path!(fd);
 
     let mut perms = std::fs::metadata(&fd_path).context(IoSnafu)?.permissions();
@@ -91,7 +95,7 @@ pub fn chmod(path: &str, mode: mode_t) -> Result<()> {
 /// the access mode is above the old mode under old owner or the new
 /// mode under new owner.
 pub fn fchmod_and_chown(
-    fd: i32,
+    fd: RawFd,
     path: &str,
     mode: Option<mode_t>,
     uid: Option<Uid>,
@@ -151,7 +155,7 @@ pub fn fchmod_and_chown(
 }
 
 /// Update the timestamp of a file with fd. If 'ts' is not provided, use the current timestamp by default.
-pub fn futimens_opath(fd: i32, ts: Option<[timespec; 2]>) -> Result<()> {
+pub fn futimens_opath(fd: RawFd, ts: Option<[timespec; 2]>) -> Result<()> {
     let fd_path = format_proc_fd_path!(fd);
     let c_string =
         CString::new(fd_path.clone()).map_err(|e| crate::Error::NulError { source: e })?;
diff --git a/libs/basic/src/mount_util.rs b/libs/basic/src/mount_util.rs
index 3b041d3..72a11d3 100644
--- a/libs/basic/src/mount_util.rs
+++ b/libs/basic/src/mount_util.rs
@@ -11,6 +11,8 @@
 // See the Mulan PSL v2 for more details.
 
 //!
+use std::os::unix::prelude::RawFd;
+
 use crate::error::*;
 use nix::{
     fcntl::AtFlags,
@@ -18,7 +20,7 @@ use nix::{
 };
 
 ///
-pub fn mount_point_fd_valid(fd: i32, file_name: &str, flags: AtFlags) -> Result<bool> {
+pub fn mount_point_fd_valid(fd: RawFd, file_name: &str, flags: AtFlags) -> Result<bool> {
     assert!(fd >= 0);
 
     let flags = if flags.contains(AtFlags::AT_SYMLINK_FOLLOW) {
-- 
2.33.0

