From 8f02617f68a6bd261d6d78620d5e9ed2b03dc305 Mon Sep 17 00:00:00 2001 From: Chris Heller Date: Thu, 19 Jan 2023 12:11:58 -0500 Subject: [PATCH 1/4] [io] add Ops for fallocate and statx --- src/io/fallocate.rs | 44 +++++++++++++++++++++++++++++++++++++++++ src/io/mod.rs | 4 ++++ src/io/statx.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 src/io/fallocate.rs create mode 100644 src/io/statx.rs diff --git a/src/io/fallocate.rs b/src/io/fallocate.rs new file mode 100644 index 00000000..fa932124 --- /dev/null +++ b/src/io/fallocate.rs @@ -0,0 +1,44 @@ +use std::io; + +use io_uring::{opcode, types}; + +use crate::{ + io::SharedFd, + runtime::{ + driver::op::{Completable, CqeResult, Op}, + CONTEXT, + }, +}; + +pub(crate) struct Fallocate { + fd: SharedFd, +} + +impl Op { + pub(crate) fn fallocate( + fd: &SharedFd, + offset: u64, + len: u64, + flags: i32, + ) -> io::Result> { + CONTEXT.with(|x| { + x.handle().expect("not in a runtime context").submit_op( + Fallocate { fd: fd.clone() }, + |fallocate| { + opcode::Fallocate64::new(types::Fd(fallocate.fd.raw_fd()), len as _) + .offset64(offset as _) + .mode(flags) + .build() + }, + ) + }) + } +} + +impl Completable for Fallocate { + type Output = io::Result<()>; + + fn complete(self, cqe: CqeResult) -> Self::Output { + cqe.result.map(|_| ()) + } +} diff --git a/src/io/mod.rs b/src/io/mod.rs index c92687ed..bff7d92e 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -5,6 +5,8 @@ pub(crate) use close::Close; mod connect; +mod fallocate; + mod fsync; mod mkdir_at; @@ -34,6 +36,8 @@ pub(crate) use shared_fd::SharedFd; mod socket; pub(crate) use socket::Socket; +mod statx; + mod unlink_at; mod util; diff --git a/src/io/statx.rs b/src/io/statx.rs new file mode 100644 index 00000000..08c20b6f --- /dev/null +++ b/src/io/statx.rs @@ -0,0 +1,48 @@ +use std::{ffi::CStr, io}; + +use io_uring::{opcode, types}; + +use crate::runtime::{ + driver::op::{Completable, CqeResult, Op}, + CONTEXT, +}; + +use super::SharedFd; + +pub(crate) struct Statx { + fd: SharedFd, + statx: Box, +} + +impl Op { + pub(crate) fn statx(fd: &SharedFd) -> io::Result> { + CONTEXT.with(|x| { + let empty_path = CStr::from_bytes_with_nul(b"\0").unwrap(); + x.handle().expect("not in a runtime context").submit_op( + Statx { + fd: fd.clone(), + statx: unsafe { Box::new(std::mem::zeroed()) }, + }, + |statx| { + opcode::Statx::new( + types::Fd(statx.fd.raw_fd()), + empty_path.as_ptr(), + &mut *statx.statx as *mut libc::statx as *mut types::statx, + ) + .flags(libc::AT_EMPTY_PATH) + .mask(libc::STATX_ALL) + .build() + }, + ) + }) + } +} + +impl Completable for Statx { + type Output = io::Result; + + fn complete(self, cqe: CqeResult) -> Self::Output { + cqe.result?; + Ok(*self.statx) + } +} From 987a39a63a1dd37d29537fc2409b885d05366828 Mon Sep 17 00:00:00 2001 From: Chris Heller Date: Thu, 19 Jan 2023 12:12:54 -0500 Subject: [PATCH 2/4] [fs] extend File with fallocate and statx methods --- src/fs/file.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/fs/file.rs b/src/fs/file.rs index 5b6265f5..eee65263 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -772,6 +772,58 @@ impl File { Op::datasync(&self.fd)?.await } + /// Manipulate the allocated disk space of the file. + /// + /// The manipulated range starts at the `offset` and continues for `len` bytes. + /// + /// The specific manipulation to the allocated disk space are specified by + /// the `flags`, to understand what are the possible values here check + /// the `fallocate(2)` man page. + /// + /// # Examples + /// + /// ```no_run + /// use tokio_uring::fs::File; + /// + /// fn main() -> Result<(), Box> { + /// tokio_uring::start(async { + /// let f = File::create("foo.txt").await?; + /// + /// // Allocate a 1024 byte file setting all the bytes to zero + /// f.fallocate(0, 1024, libc::FALLOC_FL_ZERO_RANGE).await?; + /// + /// // Close the file + /// f.close().await?; + /// Ok(()) + /// }) + /// } + pub async fn fallocate(&self, offset: u64, len: u64, flags: i32) -> io::Result<()> { + Op::fallocate(&self.fd, offset, len, flags)?.await + } + + /// Metadata information about a file. + /// + /// # Examples + /// + /// ```no_run + /// use tokio_uring::fs::File; + /// + /// fn main() -> Result<(), Box> { + /// tokio_uring::start(async { + /// let f = File::create("foo.txt").await?; + /// + /// // Fetch file metadata + /// let statx = f.statx().await?; + /// + /// // Close the file + /// f.close().await?; + /// Ok(()) + /// }) + /// } + pub async fn statx(&self) -> io::Result { + Op::statx(&self.fd)?.await + } + /// Closes the file. /// /// The method completes once the close operation has completed, From 995d6600acfa4c963bbe4fc0142aa47ac1fa5019 Mon Sep 17 00:00:00 2001 From: Chris Heller Date: Thu, 19 Jan 2023 12:13:15 -0500 Subject: [PATCH 3/4] [tests] add test which uses both fallocate and statx --- tests/fs_file.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/fs_file.rs b/tests/fs_file.rs index a5e968d0..d9cb7f7e 100644 --- a/tests/fs_file.rs +++ b/tests/fs_file.rs @@ -3,6 +3,8 @@ use std::{ os::unix::io::{AsRawFd, FromRawFd, RawFd}, }; +use libc; + use tempfile::NamedTempFile; use tokio_uring::buf::fixed::FixedBufRegistry; @@ -281,6 +283,38 @@ fn write_fixed() { }); } +#[test] +fn basic_fallocate() { + tokio_uring::start(async { + let tempfile = tempfile(); + + let file = File::create(tempfile.path()).await.unwrap(); + + file.fallocate(0, 1024, libc::FALLOC_FL_ZERO_RANGE) + .await + .unwrap(); + file.sync_all().await.unwrap(); + + let statx = file.statx().await.unwrap(); + let size = statx.stx_size; + assert_eq!(size, 1024); + + // using the FALLOC_FL_KEEP_SIZE flag causes the file metadata to reflect the previous size + file.fallocate( + 0, + 2048, + libc::FALLOC_FL_ZERO_RANGE | libc::FALLOC_FL_KEEP_SIZE, + ) + .await + .unwrap(); + file.sync_all().await.unwrap(); + + let statx = file.statx().await.unwrap(); + let size = statx.stx_size; + assert_eq!(size, 1024); + }); +} + fn tempfile() -> NamedTempFile { NamedTempFile::new().unwrap() } From d689a0a1eaa1ed0669fac9be3ec24806b7299246 Mon Sep 17 00:00:00 2001 From: Chris Heller Date: Thu, 19 Jan 2023 16:11:34 -0500 Subject: [PATCH 4/4] [PR FEEDBACK] move unsafe inside Box::new, matching other instances --- src/io/statx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/statx.rs b/src/io/statx.rs index 08c20b6f..69d05cf7 100644 --- a/src/io/statx.rs +++ b/src/io/statx.rs @@ -21,7 +21,7 @@ impl Op { x.handle().expect("not in a runtime context").submit_op( Statx { fd: fd.clone(), - statx: unsafe { Box::new(std::mem::zeroed()) }, + statx: Box::new(unsafe { std::mem::zeroed() }), }, |statx| { opcode::Statx::new(