use std::ffi::CString;
use std::os::fd::RawFd;
use std::path::PathBuf;
use std::ptr;
use std::time::{Duration, SystemTime};
use crate::fs::{
AdviseFlag, AllocateMode, FileType, Metadata, MetadataInterest, Permissions, RemoveFlag,
SyncDataFlag, path_from_cstring,
};
use crate::io_uring::op::{FdOp, Op, OpExtract, OpReturn};
use crate::io_uring::{libc, sq};
use crate::{AsyncFd, SubmissionQueue, asan, fd};
pub(crate) struct OpenOp;
impl Op for OpenOp {
type Output = AsyncFd;
type Resources = (CString, fd::Kind); type Args = (libc::c_int, libc::mode_t);
#[allow(clippy::cast_sign_loss)]
fn fill_submission(
(path, fd_kind): &mut Self::Resources,
(flags, mode): &mut Self::Args,
submission: &mut sq::Submission,
) {
submission.0.opcode = libc::IORING_OP_OPENAT as u8;
submission.0.fd = libc::AT_FDCWD;
submission.0.__bindgen_anon_2 = libc::io_uring_sqe__bindgen_ty_2 {
addr: path.as_ptr().addr() as u64,
};
asan::poison_cstring(path);
submission.0.len = *mode;
submission.0.__bindgen_anon_3 = libc::io_uring_sqe__bindgen_ty_3 {
open_flags: *flags as u32,
};
fd_kind.create_flags(submission);
}
#[allow(clippy::cast_possible_wrap)]
fn map_ok(sq: &SubmissionQueue, resources: Self::Resources, ret: OpReturn) -> Self::Output {
Self::map_ok_extract(sq, resources, ret).0
}
}
impl OpExtract for OpenOp {
type ExtractOutput = (AsyncFd, PathBuf);
#[allow(clippy::cast_possible_wrap)]
fn map_ok_extract(
sq: &SubmissionQueue,
(path, fd_kind): Self::Resources,
(_, fd): OpReturn,
) -> Self::ExtractOutput {
asan::unpoison_cstring(&path);
let fd = unsafe { AsyncFd::from_raw(fd as RawFd, fd_kind, sq.clone()) };
let path = path_from_cstring(path);
(fd, path)
}
}
pub(crate) struct CreateDirOp;
impl Op for CreateDirOp {
type Output = ();
type Resources = CString; type Args = ();
fn fill_submission(
path: &mut Self::Resources,
(): &mut Self::Args,
submission: &mut sq::Submission,
) {
submission.0.opcode = libc::IORING_OP_MKDIRAT as u8;
submission.0.fd = libc::AT_FDCWD;
submission.0.__bindgen_anon_2 = libc::io_uring_sqe__bindgen_ty_2 {
addr: path.as_ptr().addr() as u64,
};
asan::poison_cstring(path);
submission.0.len = 0o777; }
fn map_ok(_: &SubmissionQueue, path: Self::Resources, (_, n): OpReturn) -> Self::Output {
asan::unpoison_cstring(&path);
debug_assert!(n == 0);
}
}
impl OpExtract for CreateDirOp {
type ExtractOutput = PathBuf;
fn map_ok_extract(
_: &SubmissionQueue,
path: Self::Resources,
(_, n): OpReturn,
) -> Self::ExtractOutput {
asan::unpoison_cstring(&path);
debug_assert!(n == 0);
path_from_cstring(path)
}
}
pub(crate) struct RenameOp;
impl Op for RenameOp {
type Output = ();
type Resources = (CString, CString); type Args = ();
#[allow(clippy::cast_sign_loss)]
fn fill_submission(
(from, to): &mut Self::Resources,
(): &mut Self::Args,
submission: &mut sq::Submission,
) {
submission.0.opcode = libc::IORING_OP_RENAMEAT as u8;
submission.0.fd = libc::AT_FDCWD;
submission.0.__bindgen_anon_1 = libc::io_uring_sqe__bindgen_ty_1 {
off: to.as_ptr().addr() as u64,
};
asan::poison_cstring(to);
submission.0.__bindgen_anon_2 = libc::io_uring_sqe__bindgen_ty_2 {
addr: from.as_ptr().addr() as u64,
};
asan::poison_cstring(from);
submission.0.len = libc::AT_FDCWD as u32;
}
fn map_ok(_: &SubmissionQueue, (from, to): Self::Resources, (_, n): OpReturn) -> Self::Output {
asan::unpoison_cstring(&from);
asan::unpoison_cstring(&to);
debug_assert!(n == 0);
}
}
impl OpExtract for RenameOp {
type ExtractOutput = (PathBuf, PathBuf);
fn map_ok_extract(
_: &SubmissionQueue,
(from, to): Self::Resources,
(_, n): OpReturn,
) -> Self::ExtractOutput {
asan::unpoison_cstring(&from);
asan::unpoison_cstring(&to);
debug_assert!(n == 0);
(path_from_cstring(from), path_from_cstring(to))
}
}
pub(crate) struct DeleteOp;
impl Op for DeleteOp {
type Output = ();
type Resources = CString; type Args = RemoveFlag;
#[allow(clippy::cast_sign_loss)]
fn fill_submission(
path: &mut Self::Resources,
flags: &mut Self::Args,
submission: &mut sq::Submission,
) {
submission.0.opcode = libc::IORING_OP_UNLINKAT as u8;
submission.0.fd = libc::AT_FDCWD;
submission.0.__bindgen_anon_2 = libc::io_uring_sqe__bindgen_ty_2 {
addr: path.as_ptr().addr() as u64,
};
asan::poison_cstring(path);
let flags = match flags {
RemoveFlag::File => 0,
RemoveFlag::Directory => libc::AT_REMOVEDIR,
};
submission.0.__bindgen_anon_3 = libc::io_uring_sqe__bindgen_ty_3 {
unlink_flags: flags as u32,
};
}
fn map_ok(_: &SubmissionQueue, path: Self::Resources, (_, n): OpReturn) -> Self::Output {
asan::unpoison_cstring(&path);
debug_assert!(n == 0);
}
}
impl OpExtract for DeleteOp {
type ExtractOutput = PathBuf;
fn map_ok_extract(
_: &SubmissionQueue,
path: Self::Resources,
(_, n): OpReturn,
) -> Self::ExtractOutput {
asan::unpoison_cstring(&path);
debug_assert!(n == 0);
path_from_cstring(path)
}
}
pub(crate) struct SyncDataOp;
impl FdOp for SyncDataOp {
type Output = ();
type Resources = ();
type Args = SyncDataFlag;
fn fill_submission(
fd: &AsyncFd,
(): &mut Self::Resources,
flags: &mut Self::Args,
submission: &mut sq::Submission,
) {
submission.0.opcode = libc::IORING_OP_FSYNC as u8;
submission.0.fd = fd.fd();
let fsync_flags = match flags {
SyncDataFlag::All => 0,
SyncDataFlag::Data => libc::IORING_FSYNC_DATASYNC,
};
submission.0.__bindgen_anon_3 = libc::io_uring_sqe__bindgen_ty_3 { fsync_flags };
}
fn map_ok(_: &AsyncFd, (): Self::Resources, (_, n): OpReturn) -> Self::Output {
debug_assert!(n == 0);
}
}
pub(crate) fn default_metadata_interest() -> MetadataInterest {
MetadataInterest::TYPE
| MetadataInterest::MODE
| MetadataInterest::ACCESSED_TIME
| MetadataInterest::MODIFIED_TIME
| MetadataInterest::CREATED_TIME
| MetadataInterest::SIZE
| MetadataInterest::BLOCKS
}
pub(crate) struct StatOp;
impl FdOp for StatOp {
type Output = Metadata;
type Resources = Metadata;
type Args = MetadataInterest;
fn fill_submission(
fd: &AsyncFd,
metadata: &mut Self::Resources,
mask: &mut Self::Args,
submission: &mut sq::Submission,
) {
submission.0.opcode = libc::IORING_OP_STATX as u8;
submission.0.fd = fd.fd();
submission.0.__bindgen_anon_1 = libc::io_uring_sqe__bindgen_ty_1 {
off: ptr::from_mut(metadata).addr() as u64,
};
submission.0.__bindgen_anon_2 = libc::io_uring_sqe__bindgen_ty_2 {
addr: c"".as_ptr().addr() as u64, };
submission.0.len = mask.0;
submission.0.__bindgen_anon_3 = libc::io_uring_sqe__bindgen_ty_3 {
statx_flags: libc::AT_EMPTY_PATH as u32,
};
}
fn map_ok(_: &AsyncFd, metadata: Self::Resources, (_, n): OpReturn) -> Self::Output {
debug_assert!(n == 0);
metadata
}
}
pub(crate) struct AdviseOp;
impl FdOp for AdviseOp {
type Output = ();
type Resources = ();
type Args = (u64, u32, AdviseFlag);
#[allow(clippy::cast_sign_loss)]
fn fill_submission(
fd: &AsyncFd,
(): &mut Self::Resources,
(offset, length, advice): &mut Self::Args,
submission: &mut sq::Submission,
) {
submission.0.opcode = libc::IORING_OP_FADVISE as u8;
submission.0.fd = fd.fd();
submission.0.__bindgen_anon_1 = libc::io_uring_sqe__bindgen_ty_1 { off: *offset };
submission.0.len = *length;
submission.0.__bindgen_anon_3 = libc::io_uring_sqe__bindgen_ty_3 {
fadvise_advice: advice.0,
};
}
fn map_ok(_: &AsyncFd, (): Self::Resources, (_, n): OpReturn) -> Self::Output {
debug_assert!(n == 0);
}
}
pub(crate) struct AllocateOp;
impl FdOp for AllocateOp {
type Output = ();
type Resources = ();
type Args = (u64, u32, AllocateMode);
#[allow(clippy::cast_sign_loss)]
fn fill_submission(
fd: &AsyncFd,
(): &mut Self::Resources,
(offset, length, mode): &mut Self::Args,
submission: &mut sq::Submission,
) {
submission.0.opcode = libc::IORING_OP_FALLOCATE as u8;
submission.0.fd = fd.fd();
submission.0.__bindgen_anon_1 = libc::io_uring_sqe__bindgen_ty_1 { off: *offset };
submission.0.__bindgen_anon_2 = libc::io_uring_sqe__bindgen_ty_2 {
addr: (*length).into(),
};
submission.0.len = mode.0;
}
fn map_ok(_: &AsyncFd, (): Self::Resources, (_, n): OpReturn) -> Self::Output {
debug_assert!(n == 0);
}
}
pub(crate) struct TruncateOp;
impl FdOp for TruncateOp {
type Output = ();
type Resources = ();
type Args = u64;
fn fill_submission(
fd: &AsyncFd,
(): &mut Self::Resources,
length: &mut Self::Args,
submission: &mut sq::Submission,
) {
submission.0.opcode = libc::IORING_OP_FTRUNCATE as u8;
submission.0.fd = fd.fd();
submission.0.__bindgen_anon_1 = libc::io_uring_sqe__bindgen_ty_1 { off: *length };
}
fn map_ok(_: &AsyncFd, (): Self::Resources, (_, n): OpReturn) -> Self::Output {
debug_assert!(n == 0);
}
}
pub(crate) use libc::statx as Stat;
pub(crate) const fn filled(stat: &Stat) -> MetadataInterest {
MetadataInterest(stat.stx_mask)
}
pub(crate) const fn file_type(stat: &Stat) -> FileType {
FileType(stat.stx_mode)
}
pub(crate) const fn len(stat: &Stat) -> u64 {
stat.stx_size
}
pub(crate) const fn block_size(stat: &Stat) -> u32 {
stat.stx_blksize
}
pub(crate) const fn permissions(stat: &Stat) -> Permissions {
Permissions(stat.stx_mode)
}
pub(crate) fn modified(stat: &Stat) -> SystemTime {
timestamp(&stat.stx_mtime)
}
pub(crate) fn accessed(stat: &Stat) -> SystemTime {
timestamp(&stat.stx_atime)
}
pub(crate) fn created(stat: &Stat) -> SystemTime {
timestamp(&stat.stx_btime)
}
#[allow(clippy::cast_sign_loss)] fn timestamp(ts: &libc::statx_timestamp) -> SystemTime {
let dur = Duration::new(ts.tv_sec as u64, ts.tv_nsec);
if ts.tv_sec.is_negative() {
SystemTime::UNIX_EPOCH - dur
} else {
SystemTime::UNIX_EPOCH + dur
}
}