Skip to content

Commit

Permalink
Implement chmod and fchmod in Unix shims
Browse files Browse the repository at this point in the history
  • Loading branch information
el-ev committed Feb 26, 2025
1 parent 76a3d31 commit 5c1befb
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 3 deletions.
10 changes: 10 additions & 0 deletions src/shims/unix/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let result = this.mkstemp(template)?;
this.write_scalar(result, dest)?;
}
"chmod" => {
let [path, mode] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.chmod(path, mode)?;
this.write_scalar(result, dest)?;
},
"fchmod" => {
let [fd, mode] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.fchmod(fd, mode)?;
this.write_scalar(result, dest)?;
}

// Unnamed sockets and pipes
"socketpair" => {
Expand Down
75 changes: 72 additions & 3 deletions src/shims/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
use std::borrow::Cow;
use std::fs::{
DirBuilder, File, FileType, Metadata, OpenOptions, ReadDir, read_dir, remove_dir, remove_file,
rename,
DirBuilder, File, FileType, Metadata, OpenOptions, Permissions, ReadDir, read_dir, remove_dir,
remove_file, rename,
};
use std::io::{self, ErrorKind, IsTerminal, Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -834,7 +834,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {

// If the path is empty, and the AT_EMPTY_PATH flag is set, we query the open file
// represented by dirfd, whether it's a directory or otherwise.
let metadata = if path.as_os_str().is_empty() && empty_path_flag {
let metadata: Result<!, _> = if path.as_os_str().is_empty() && empty_path_flag {
FileMetadata::from_fd_num(this, dirfd)?
} else {
FileMetadata::from_path(this, &path, follow_symlink)?
Expand Down Expand Up @@ -1664,6 +1664,75 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// We ran out of attempts to create the file, return an error.
this.set_last_error_and_return_i32(LibcError("EEXIST"))
}

fn chmod(&mut self, path_op: &OpTy<'tcx>, perm_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();

// Permissions::from_mode is Unix-specific.
this.assert_target_os_is_unix("chmod");

#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;

let pathname = this.read_path_from_c_str(this.read_pointer(path_op)?)?;
let perm = this.read_scalar(perm_op)?.to_u32()?;

// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`chmod`", reject_with)?;
return this.set_last_error_and_return_i32(LibcError("EACCES"));
}

let result = std::fs::set_permissions(pathname, Permissions::from_mode(perm));
let result = this.try_unwrap_io_result(result.map(|_| 0i32))?;

interp_ok(Scalar::from_i32(result))
}
#[cfg(not(unix))]
{
unreachable!()
}
}

fn fchmod(&mut self, fd_op: &OpTy<'tcx>, perm_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();

// `Permissions::from_mode` is Unix-specific.
this.assert_target_os_is_unix("fchmod");

#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;

let fd = this.read_scalar(fd_op)?.to_i32()?;
let perm = this.read_scalar(perm_op)?.to_u32()?;

// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`fchmod`", reject_with)?;
// Set error code as "EBADF" (bad fd)
return this.set_last_error_and_return_i32(LibcError("EBADF"));
}

let Some(fd) = this.machine.fds.get(fd) else {
return this.set_last_error_and_return_i32(LibcError("EBADF"));
};

let file = fd.downcast::<FileHandle>().ok_or_else(|| {
err_unsup_format!("`fchmod` is only supported on file-backed file descriptors")
})?;

let result = file.file.set_permissions(Permissions::from_mode(perm));
let result = this.try_unwrap_io_result(result.map(|_| 0i32))?;

interp_ok(Scalar::from_i32(result))
}
#[cfg(not(unix))]
{
unreachable!()
}
}
}

/// Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when
Expand Down

0 comments on commit 5c1befb

Please sign in to comment.