From f4ff3b985d8be680afdec2b455ac7b5b325ec612 Mon Sep 17 00:00:00 2001 From: Facundo Poblete Date: Fri, 29 Nov 2024 01:17:32 -0300 Subject: [PATCH] Add mkdir support --- mirrord/agent/src/file.rs | 14 ++++++++++++++ mirrord/intproxy/protocol/src/lib.rs | 7 +++++++ mirrord/layer/src/file/hooks.rs | 27 ++++++++++++++++++++++++--- mirrord/layer/src/file/ops.rs | 19 ++++++++++++++++++- mirrord/layer/tests/common/mod.rs | 25 +++++++++++++++++++++++++ mirrord/layer/tests/mkdir.rs | 28 ++++++++++++++++++++++++++++ mirrord/protocol/src/codec.rs | 2 ++ mirrord/protocol/src/file.rs | 12 ++++++++++++ tests/go-e2e-dir/main.go | 9 ++++++++- 9 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 mirrord/layer/tests/mkdir.rs diff --git a/mirrord/agent/src/file.rs b/mirrord/agent/src/file.rs index 947fd8d2f78..79d4aa201a0 100644 --- a/mirrord/agent/src/file.rs +++ b/mirrord/agent/src/file.rs @@ -250,6 +250,9 @@ impl FileManager { }) => Some(FileResponse::GetDEnts64( self.getdents64(remote_fd, buffer_size), )), + FileRequest::MakeDir(MakeDirRequest { path, mode }) => { + Some(FileResponse::MakeDir(self.mkdir(&path, mode))) + }, }) } @@ -472,6 +475,17 @@ impl FileManager { }) } + pub(crate) fn mkdir(&mut self, path: &Path, mode: u32) -> RemoteResult { + trace!("FileManager::mkdir -> path {:#?} | mode {:#?}", path, mode); + + let path = resolve_path(path, &self.root_path)?; + + match std::fs::create_dir(Path::new(&path)) { + Ok(_) => Ok(MakeDirResponse { result: 0, errno: 0 }), + Err(err) => Ok(MakeDirResponse { result: -1, errno: err.raw_os_error().unwrap_or(0) }) + } + } + pub(crate) fn seek(&mut self, fd: u64, seek_from: SeekFrom) -> RemoteResult { trace!( "FileManager::seek -> fd {:#?} | seek_from {:#?}", diff --git a/mirrord/intproxy/protocol/src/lib.rs b/mirrord/intproxy/protocol/src/lib.rs index 5c5a003260c..522cdf2f164 100644 --- a/mirrord/intproxy/protocol/src/lib.rs +++ b/mirrord/intproxy/protocol/src/lib.rs @@ -310,6 +310,13 @@ impl_request!( res_path = ProxyToLayerMessage::File => FileResponse::ReadLink, ); +impl_request!( + req = MakeDirRequest, + res = RemoteResult, + req_path = LayerToProxyMessage::File => FileRequest::MakeDir, + res_path = ProxyToLayerMessage::File => FileResponse::MakeDir, +); + impl_request!( req = SeekFileRequest, res = RemoteResult, diff --git a/mirrord/layer/src/file/hooks.rs b/mirrord/layer/src/file/hooks.rs index 30217589d08..ca517730ebf 100644 --- a/mirrord/layer/src/file/hooks.rs +++ b/mirrord/layer/src/file/hooks.rs @@ -10,18 +10,18 @@ use std::{ os::unix::{ffi::OsStrExt, io::RawFd}, ptr, slice, time::Duration, + os::unix::raw::mode_t }; use errno::{set_errno, Errno}; use libc::{ - self, c_char, c_int, c_void, dirent, iovec, off_t, size_t, ssize_t, stat, statfs, AT_EACCESS, - AT_FDCWD, DIR, EINVAL, O_DIRECTORY, O_RDONLY, + self, c_char, c_int, c_void, dirent, iovec, off_t, size_t, ssize_t, stat, statfs, AT_EACCESS, AT_FDCWD, DIR, EINVAL, O_DIRECTORY, O_RDONLY }; #[cfg(target_os = "linux")] use libc::{dirent64, stat64, statx, EBADF, ENOENT, ENOTDIR}; use mirrord_layer_macro::{hook_fn, hook_guard_fn}; use mirrord_protocol::file::{ - FsMetadataInternal, MetadataInternal, ReadFileResponse, ReadLinkFileResponse, WriteFileResponse, + FsMetadataInternal, MetadataInternal, ReadFileResponse, ReadLinkFileResponse, WriteFileResponse, MakeDirResponse }; #[cfg(target_os = "linux")] use mirrord_protocol::ResponseError::{NotDirectory, NotFound}; @@ -1062,6 +1062,25 @@ pub(crate) unsafe extern "C" fn readlink_detour( }) } +/// Hook for `libc::mkdir`. +#[hook_guard_fn] +pub(crate) unsafe extern "C" fn mkdir_detour( + raw_path: *const c_char, + mode: mode_t, +) -> c_int { + mkdir(raw_path.checked_into(), mode).map(|MakeDirResponse {result, errno } | { + if result == -1 { + set_errno(Errno(errno)); + -1 + } else { + 0 + } + }).unwrap_or_bypass_with(|bypass| { + let raw_path = update_ptr_from_bypass(raw_path, &bypass); + FN_MKDIR(raw_path, mode) + }) +} + /// Convenience function to setup file hooks (`x_detour`) with `frida_gum`. pub(crate) unsafe fn enable_file_hooks(hook_manager: &mut HookManager) { replace!(hook_manager, "open", open_detour, FnOpen, FN_OPEN); @@ -1136,6 +1155,8 @@ pub(crate) unsafe fn enable_file_hooks(hook_manager: &mut HookManager) { FN_READLINK ); + replace!(hook_manager, "mkdir", mkdir_detour, FnMkdir, FN_MKDIR); + replace!(hook_manager, "lseek", lseek_detour, FnLseek, FN_LSEEK); replace!(hook_manager, "write", write_detour, FnWrite, FN_WRITE); diff --git a/mirrord/layer/src/file/ops.rs b/mirrord/layer/src/file/ops.rs index 352c570257c..6db46410e66 100644 --- a/mirrord/layer/src/file/ops.rs +++ b/mirrord/layer/src/file/ops.rs @@ -9,7 +9,7 @@ use mirrord_protocol::{ file::{ OpenFileRequest, OpenFileResponse, OpenOptionsInternal, ReadFileResponse, ReadLinkFileRequest, ReadLinkFileResponse, SeekFileResponse, WriteFileResponse, - XstatFsResponse, XstatResponse, + XstatFsResponse, XstatResponse, MakeDirRequest, MakeDirResponse }, ResponseError, }; @@ -337,6 +337,23 @@ pub(crate) fn read_link(path: Detour) -> Detour { } } +#[mirrord_layer_macro::instrument(level = Level::TRACE, ret)] +pub(crate) fn mkdir(path: Detour, mode: u32) -> Detour { + let path = remap_path!(path?); + + check_relative_paths!(path); + + ensure_not_ignored!(path, false); + + let mkdir = MakeDirRequest { path, mode }; + + match common::make_proxy_request_with_response(mkdir)? { + Ok(response) => Detour::Success(response), + Err(ResponseError::NotImplemented) => Detour::Bypass(Bypass::NotImplemented), + Err(fail) => Detour::Error(fail.into()), + } +} + pub(crate) fn pwrite(local_fd: RawFd, buffer: &[u8], offset: u64) -> Detour { let remote_fd = get_remote_fd(local_fd)?; trace!("pwrite: local_fd {local_fd}"); diff --git a/mirrord/layer/tests/common/mod.rs b/mirrord/layer/tests/common/mod.rs index 0469a6695bb..c84a56906d6 100644 --- a/mirrord/layer/tests/common/mod.rs +++ b/mirrord/layer/tests/common/mod.rs @@ -468,6 +468,27 @@ impl TestIntProxy { .unwrap(); } + /// Makes a [`FileRequest::MakeDir`] and answers it. + pub async fn expect_make_dir(&mut self, dir_name: &str) { + // Expecting `mkdir` call with path. + assert_matches!( + self.recv().await, + ClientMessage::FileRequest(FileRequest::MakeDir( + mirrord_protocol::file::MakeDirRequest {path, mode: _ } + )) if path.to_str().unwrap() == dir_name + ); + + // Answer `mkdir`. + self.codec + .send(DaemonMessage::File( + mirrord_protocol::FileResponse::MakeDir(Ok( + mirrord_protocol::file::MakeDirResponse { result: 0, errno: 0 }, + )), + )) + .await + .unwrap(); + } + /// Verify that the passed message (not the next message from self.codec!) is a file read. /// Return buffer size. pub async fn expect_message_file_read(message: ClientMessage, expected_fd: u64) -> u64 { @@ -743,6 +764,7 @@ pub enum Application { RustListenPorts, Fork, ReadLink, + MakeDir, OpenFile, CIssue2055, CIssue2178, @@ -796,6 +818,7 @@ impl Application { Application::PythonFastApiHTTP | Application::PythonIssue864 => String::from("uvicorn"), Application::Fork => String::from("tests/apps/fork/out.c_test_app"), Application::ReadLink => String::from("tests/apps/readlink/out.c_test_app"), + Application::MakeDir => String::from("tests/apps/mkdir/out.c_test_app"), Application::Realpath => String::from("tests/apps/realpath/out.c_test_app"), Application::NodeHTTP | Application::NodeIssue2283 | Application::NodeIssue2807 => { String::from("node") @@ -1032,6 +1055,7 @@ impl Application { | Application::Go23FAccessAt | Application::Fork | Application::ReadLink + | Application::MakeDir | Application::Realpath | Application::RustFileOps | Application::RustIssue1123 @@ -1108,6 +1132,7 @@ impl Application { | Application::BashShebang | Application::Fork | Application::ReadLink + | Application::MakeDir | Application::Realpath | Application::Go21Issue834 | Application::Go22Issue834 diff --git a/mirrord/layer/tests/mkdir.rs b/mirrord/layer/tests/mkdir.rs new file mode 100644 index 00000000000..9f2c490aaca --- /dev/null +++ b/mirrord/layer/tests/mkdir.rs @@ -0,0 +1,28 @@ +#![feature(assert_matches)] +use std::{path::Path, time::Duration}; + +use rstest::rstest; + +mod common; +pub use common::*; + +/// Test for the [`libc::mkdir`] function. +#[rstest] +#[tokio::test] +#[timeout(Duration::from_secs(60))] +async fn mkdir(dylib_path: &Path) { + let application = Application::ReadLink; + + let (mut test_process, mut intproxy) = application + .start_process_with_layer(dylib_path, Default::default(), None) + .await; + + println!("waiting for file request."); + intproxy.expect_make_dir("/folder1").await; + + assert_eq!(intproxy.try_recv().await, None); + + test_process.wait_assert_success().await; + test_process.assert_no_error_in_stderr().await; + test_process.assert_no_error_in_stdout().await; +} diff --git a/mirrord/protocol/src/codec.rs b/mirrord/protocol/src/codec.rs index 06957e562ef..6a839adacf8 100644 --- a/mirrord/protocol/src/codec.rs +++ b/mirrord/protocol/src/codec.rs @@ -76,6 +76,7 @@ pub enum FileRequest { CloseDir(CloseDirRequest), GetDEnts64(GetDEnts64Request), ReadLink(ReadLinkFileRequest), + MakeDir(MakeDirRequest), /// `readdir` request. /// @@ -126,6 +127,7 @@ pub enum FileResponse { OpenDir(RemoteResult), GetDEnts64(RemoteResult), ReadLink(RemoteResult), + MakeDir(RemoteResult), ReadDirBatch(RemoteResult), } diff --git a/mirrord/protocol/src/file.rs b/mirrord/protocol/src/file.rs index e8ff8db8cd3..126017f77f3 100644 --- a/mirrord/protocol/src/file.rs +++ b/mirrord/protocol/src/file.rs @@ -262,6 +262,12 @@ pub struct ReadLinkFileResponse { pub path: PathBuf, } +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] +pub struct MakeDirResponse { + pub result: i32, + pub errno: i32, +} + #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct ReadLimitedFileRequest { pub remote_fd: u64, @@ -275,6 +281,12 @@ pub struct ReadLinkFileRequest { pub path: PathBuf, } +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] +pub struct MakeDirRequest { + pub path: PathBuf, + pub mode: u32, +} + #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)] pub struct SeekFileRequest { pub fd: u64, diff --git a/tests/go-e2e-dir/main.go b/tests/go-e2e-dir/main.go index 03ce58d8b9b..b608f01b53b 100644 --- a/tests/go-e2e-dir/main.go +++ b/tests/go-e2e-dir/main.go @@ -20,9 +20,16 @@ func main() { os.Exit(-1) } // `os.ReadDir` sorts the result by file name. - if dir[0].Name() != "app.py" || dir[1].Name() != "test.txt"{ + if dir[0].Name() != "app.py" || dir[1].Name() != "test.txt" { os.Exit(-1) } + + err = os.Mkdir("/app/test_mkdir", 0755) + if err != nil { + fmt.Printf("Mkdir error: %s\n", err) + os.Exit(-1) + } + // let close requests be sent for test time.Sleep(1 * time.Second) os.Exit(0)