From d570a9c740fec2c89f006c83f0cd46cec5e547a8 Mon Sep 17 00:00:00 2001 From: chenjiayi Date: Mon, 3 Jul 2023 01:03:39 +0800 Subject: [PATCH] feature(devmaster): support update device database and tags Besides, move the loop device interfaces to libdevice as some test cases in libdevice need to use loop device. --- libs/basic/src/fs_util.rs | 88 +++++- 9 files changed, 542 insertions(+), 128 deletions(-) delete mode 100644 exts/devmaster/src/lib/utils/loop_device.rs diff --git a/libs/basic/src/fs_util.rs b/libs/basic/src/fs_util.rs index 8a8f276..413008b 100644 --- a/libs/basic/src/fs_util.rs +++ b/libs/basic/src/fs_util.rs @@ -15,7 +15,7 @@ use crate::{error::*, format_proc_fd_path}; use libc::{fchownat, mode_t, timespec, AT_EMPTY_PATH, S_IFLNK, S_IFMT}; use nix::{ - fcntl::{readlink, renameat, OFlag}, + fcntl::{open, readlink, renameat, OFlag}, sys::stat::{fstat, Mode}, unistd::{unlinkat, Gid, Uid, UnlinkatFlags}, }; @@ -23,7 +23,7 @@ use pathdiff::diff_paths; use rand::Rng; use std::{ ffi::CString, - fs::{remove_dir, File}, + fs::{create_dir_all, remove_dir, File}, io::ErrorKind, os::unix::prelude::{AsRawFd, FromRawFd, PermissionsExt, RawFd}, path::Path, @@ -245,13 +245,70 @@ pub fn remove_dir_until(path: &str, stop: &str) -> Result<()> { Ok(()) } +/// create and open a temporary file +pub fn open_temporary(path: &str) -> Result<(File, String)> { + let tmp_file = format!("{}.{}", path, rand::thread_rng().gen::()); + + let f = open( + tmp_file.as_str(), + OFlag::O_CLOEXEC | OFlag::O_NOCTTY | OFlag::O_RDWR | OFlag::O_CREAT | OFlag::O_EXCL, + Mode::from_bits(0o644).unwrap(), + ) + .context(NixSnafu)?; + + let file = unsafe { File::from_raw_fd(f) }; + + Ok((file, tmp_file)) +} + +/// Create a new file based on absolute path. +pub fn touch_file( + path: &str, + parents: bool, + mode: Option, + uid: Option, + gid: Option, +) -> Result { + let p = Path::new(path); + + if parents && p.parent().is_some() { + let _ = create_dir_all(p.parent().unwrap()); + } + + let fd = match open( + p, + OFlag::O_PATH | OFlag::O_CLOEXEC | OFlag::O_NOFOLLOW, + Mode::empty(), + ) { + Ok(n) => n, + Err(e) => { + if e != nix::Error::ENOENT { + return Err(Error::Nix { source: e }); + } + + open( + p, + OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_CLOEXEC, + mode.map(|v| Mode::from_bits(v).unwrap_or(Mode::empty())) + .unwrap_or(Mode::empty()), + ) + .context(NixSnafu)? + } + }; + + /* The returned value should be explicitly declared, otherwise the file will be closed at once */ + let _f = unsafe { File::from_raw_fd(fd) }; + + fchmod_and_chown(fd, path, mode, uid, gid) +} + #[cfg(test)] mod tests { use super::*; use crate::fs_util::symlink; - use nix::unistd::{self}; + use nix::unistd::{self, unlink}; use std::{ - fs::{create_dir_all, File}, + fs::{create_dir_all, remove_file, File}, os::unix::prelude::MetadataExt, time::SystemTime, }; @@ -369,4 +426,27 @@ mod tests { assert!(!Path::new("/tmp/test_remove_dir_until_2/test2").exists()); assert!(Path::new("/tmp/test_remove_dir_until_2/test_file").exists()); } + + #[test] + fn test_open_temporary() { + let (file, name) = open_temporary("test_open_temporary").unwrap(); + let p = Path::new(&name); + println!("{:?}", p.canonicalize().unwrap()); + drop(file); + remove_file(name).unwrap(); + } + + #[test] + fn test_touch_file() { + let _ = unlink("/tmp/test_touch_file/f1"); + touch_file("/tmp/test_touch_file/f1", true, Some(0o444), None, None).unwrap(); + let p = Path::new("/tmp/test_touch_file/f1"); + assert!(p.exists()); + let md = p.metadata().unwrap(); + assert_eq!(md.mode() & 0o777, 0o444); + touch_file("/tmp/test_touch_file/f1", true, Some(0o666), None, None).unwrap(); + let md = p.metadata().unwrap(); + assert_eq!(md.mode() & 0o777, 0o666); + let _ = unlink("/tmp/test_touch_file/f1"); + } } -- 2.33.0