153 lines
5.5 KiB
Diff
153 lines
5.5 KiB
Diff
From 11b505249041b6368eaac0b5699c455599a9565a Mon Sep 17 00:00:00 2001
|
|
From: chenjiayi <chenjiayi22@huawei.com>
|
|
Date: Tue, 27 Jun 2023 05:01:37 +0800
|
|
Subject: [PATCH] fix(libbasic): try to follow the fd path to make the
|
|
utimensat work
|
|
|
|
In devmaster threads, utimensat will fail with errno of ENOENT, which means
|
|
the file path does not exist, if the fd path is used. The fd path is a symlink to
|
|
the opened real file. It is weired because the fd path really exists but utimesat
|
|
can not find it. To avoid the failure, we have no choice but to follow the fd path
|
|
symlink to the real file, and update the timestamp directly on it.
|
|
---
|
|
libs/basic/src/error.rs | 7 +++-
|
|
libs/basic/src/fs_util.rs | 78 ++++++++++++++++++++++++++-------------
|
|
2 files changed, 57 insertions(+), 28 deletions(-)
|
|
|
|
diff --git a/libs/basic/src/error.rs b/libs/basic/src/error.rs
|
|
index 2730883..cfa89f8 100644
|
|
--- a/libs/basic/src/error.rs
|
|
+++ b/libs/basic/src/error.rs
|
|
@@ -44,6 +44,9 @@ pub enum Error {
|
|
#[snafu(display("procfs: {}", source))]
|
|
Proc { source: procfs::ProcError },
|
|
|
|
+ #[snafu(display("NulError: '{}'", source))]
|
|
+ NulError { source: std::ffi::NulError },
|
|
+
|
|
#[snafu(display("Error parsing from string: {}", source))]
|
|
Parse {
|
|
source: Box<dyn std::error::Error + Send + Sync>,
|
|
@@ -52,13 +55,13 @@ pub enum Error {
|
|
#[snafu(display("Invalid naming scheme string: {}", what))]
|
|
ParseNamingScheme { what: String },
|
|
|
|
- #[snafu(display("Not exist): '{}'.", what))]
|
|
+ #[snafu(display("Not exist: '{}'.", what))]
|
|
NotExisted { what: String },
|
|
|
|
#[snafu(display("Invalid: '{}'.", what))]
|
|
Invalid { what: String },
|
|
|
|
- #[snafu(display("OtherError): '{}'.", msg))]
|
|
+ #[snafu(display("OtherError: '{}'.", msg))]
|
|
Other { msg: String },
|
|
}
|
|
|
|
diff --git a/libs/basic/src/fs_util.rs b/libs/basic/src/fs_util.rs
|
|
index 6a6378d..3f72658 100644
|
|
--- a/libs/basic/src/fs_util.rs
|
|
+++ b/libs/basic/src/fs_util.rs
|
|
@@ -15,13 +15,15 @@
|
|
use crate::{error::*, format_proc_fd_path};
|
|
use libc::{fchownat, mode_t, timespec, AT_EMPTY_PATH, S_IFLNK, S_IFMT};
|
|
use nix::{
|
|
- fcntl::{renameat, OFlag},
|
|
+ fcntl::{readlink, renameat, OFlag},
|
|
sys::stat::{fstat, Mode},
|
|
unistd::{unlinkat, Gid, Uid, UnlinkatFlags},
|
|
};
|
|
use pathdiff::diff_paths;
|
|
use rand::Rng;
|
|
-use std::{fs::remove_dir, io::ErrorKind, os::unix::prelude::PermissionsExt, path::Path};
|
|
+use std::{
|
|
+ ffi::CString, fs::remove_dir, io::ErrorKind, os::unix::prelude::PermissionsExt, path::Path,
|
|
+};
|
|
|
|
/// open the parent directory of path
|
|
pub fn open_parent(path: &Path, flags: OFlag, mode: Mode) -> Result<i32> {
|
|
@@ -148,32 +150,56 @@ pub fn fchmod_and_chown(
|
|
Ok(do_chown || do_chmod)
|
|
}
|
|
|
|
-/// if ts are not provided, use the current timestamp by default.
|
|
+/// 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<()> {
|
|
- let r = unsafe {
|
|
- libc::utimensat(
|
|
- libc::AT_FDCWD,
|
|
- format_proc_fd_path!(fd).as_ptr() as *const libc::c_char,
|
|
- &ts.unwrap_or([
|
|
- timespec {
|
|
- tv_sec: 0,
|
|
- tv_nsec: libc::UTIME_NOW,
|
|
- },
|
|
- timespec {
|
|
- tv_sec: 0,
|
|
- tv_nsec: libc::UTIME_NOW,
|
|
- },
|
|
- ])[0],
|
|
- 0,
|
|
- )
|
|
- };
|
|
- if r < 0 {
|
|
- Err(Error::Nix {
|
|
- source: nix::Error::from_i32(std::io::Error::last_os_error().raw_os_error().unwrap()),
|
|
- })
|
|
- } else {
|
|
- Ok(())
|
|
+ let fd_path = format_proc_fd_path!(fd);
|
|
+ let c_string =
|
|
+ CString::new(fd_path.clone()).map_err(|e| crate::Error::NulError { source: e })?;
|
|
+ let times = ts.unwrap_or([
|
|
+ timespec {
|
|
+ tv_sec: 0,
|
|
+ tv_nsec: libc::UTIME_NOW,
|
|
+ },
|
|
+ timespec {
|
|
+ tv_sec: 0,
|
|
+ tv_nsec: libc::UTIME_NOW,
|
|
+ },
|
|
+ ])[0];
|
|
+
|
|
+ if unsafe { libc::utimensat(libc::AT_FDCWD, c_string.as_ptr(), ×, 0) } < 0 {
|
|
+ let errno = nix::Error::from_i32(
|
|
+ std::io::Error::last_os_error()
|
|
+ .raw_os_error()
|
|
+ .unwrap_or_default(),
|
|
+ );
|
|
+
|
|
+ if errno == nix::Error::ENOENT {
|
|
+ /*
|
|
+ * In devmaster threads, utimensat will fail with errno of ENOENT, which means
|
|
+ * the file path does not exist, if the fd path is used. The fd path is a symlink to
|
|
+ * the opened real file. It is weird because the fd path really exists but utimesat
|
|
+ * can not find it. To avoid the failure, we try to follow the fd path symlink to the
|
|
+ * real file and update the timestamp directly on it.
|
|
+ */
|
|
+ let target = readlink(fd_path.as_str()).unwrap();
|
|
+ let c_string = target
|
|
+ .to_str()
|
|
+ .ok_or(Error::Nix { source: errno })
|
|
+ .map(|s| unsafe { libc::strdup(s.as_ptr() as *const libc::c_char) })?;
|
|
+
|
|
+ if unsafe { libc::utimensat(libc::AT_FDCWD, c_string, ×, 0) } < 0 {
|
|
+ return Err(Error::Nix {
|
|
+ source: nix::Error::from_i32(
|
|
+ std::io::Error::last_os_error().raw_os_error().unwrap(),
|
|
+ ),
|
|
+ });
|
|
+ }
|
|
+ } else {
|
|
+ return Err(Error::Nix { source: errno });
|
|
+ }
|
|
}
|
|
+
|
|
+ Ok(())
|
|
}
|
|
|
|
/// recursively remove parent directories until specific directory
|
|
--
|
|
2.33.0
|
|
|