Skip to content

Commit

Permalink
Add mkdir support
Browse files Browse the repository at this point in the history
  • Loading branch information
facundopoblete committed Nov 29, 2024
1 parent e24147c commit f4ff3b9
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 5 deletions.
14 changes: 14 additions & 0 deletions mirrord/agent/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
},
})
}

Expand Down Expand Up @@ -472,6 +475,17 @@ impl FileManager {
})
}

pub(crate) fn mkdir(&mut self, path: &Path, mode: u32) -> RemoteResult<MakeDirResponse> {
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<SeekFileResponse> {
trace!(
"FileManager::seek -> fd {:#?} | seek_from {:#?}",
Expand Down
7 changes: 7 additions & 0 deletions mirrord/intproxy/protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,13 @@ impl_request!(
res_path = ProxyToLayerMessage::File => FileResponse::ReadLink,
);

impl_request!(
req = MakeDirRequest,
res = RemoteResult<MakeDirResponse>,
req_path = LayerToProxyMessage::File => FileRequest::MakeDir,
res_path = ProxyToLayerMessage::File => FileResponse::MakeDir,
);

impl_request!(
req = SeekFileRequest,
res = RemoteResult<SeekFileResponse>,
Expand Down
27 changes: 24 additions & 3 deletions mirrord/layer/src/file/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
19 changes: 18 additions & 1 deletion mirrord/layer/src/file/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use mirrord_protocol::{
file::{
OpenFileRequest, OpenFileResponse, OpenOptionsInternal, ReadFileResponse,
ReadLinkFileRequest, ReadLinkFileResponse, SeekFileResponse, WriteFileResponse,
XstatFsResponse, XstatResponse,
XstatFsResponse, XstatResponse, MakeDirRequest, MakeDirResponse
},
ResponseError,
};
Expand Down Expand Up @@ -337,6 +337,23 @@ pub(crate) fn read_link(path: Detour<PathBuf>) -> Detour<ReadLinkFileResponse> {
}
}

#[mirrord_layer_macro::instrument(level = Level::TRACE, ret)]
pub(crate) fn mkdir(path: Detour<PathBuf>, mode: u32) -> Detour<MakeDirResponse> {
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<WriteFileResponse> {
let remote_fd = get_remote_fd(local_fd)?;
trace!("pwrite: local_fd {local_fd}");
Expand Down
25 changes: 25 additions & 0 deletions mirrord/layer/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -743,6 +764,7 @@ pub enum Application {
RustListenPorts,
Fork,
ReadLink,
MakeDir,
OpenFile,
CIssue2055,
CIssue2178,
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -1032,6 +1055,7 @@ impl Application {
| Application::Go23FAccessAt
| Application::Fork
| Application::ReadLink
| Application::MakeDir
| Application::Realpath
| Application::RustFileOps
| Application::RustIssue1123
Expand Down Expand Up @@ -1108,6 +1132,7 @@ impl Application {
| Application::BashShebang
| Application::Fork
| Application::ReadLink
| Application::MakeDir
| Application::Realpath
| Application::Go21Issue834
| Application::Go22Issue834
Expand Down
28 changes: 28 additions & 0 deletions mirrord/layer/tests/mkdir.rs
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 2 additions & 0 deletions mirrord/protocol/src/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ pub enum FileRequest {
CloseDir(CloseDirRequest),
GetDEnts64(GetDEnts64Request),
ReadLink(ReadLinkFileRequest),
MakeDir(MakeDirRequest),

/// `readdir` request.
///
Expand Down Expand Up @@ -126,6 +127,7 @@ pub enum FileResponse {
OpenDir(RemoteResult<OpenDirResponse>),
GetDEnts64(RemoteResult<GetDEnts64Response>),
ReadLink(RemoteResult<ReadLinkFileResponse>),
MakeDir(RemoteResult<MakeDirResponse>),
ReadDirBatch(RemoteResult<ReadDirBatchResponse>),
}

Expand Down
12 changes: 12 additions & 0 deletions mirrord/protocol/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
9 changes: 8 additions & 1 deletion tests/go-e2e-dir/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit f4ff3b9

Please sign in to comment.