From e53acf148b95f16e06d74c8a3edd0ee71f1a1617 Mon Sep 17 00:00:00 2001 From: Ming Chen Date: Thu, 5 Dec 2024 23:34:10 +0000 Subject: [PATCH 1/2] Add `File::truncate` for async file truncation --- Cargo.toml | 4 ++-- src/fs/file.rs | 28 ++++++++++++++++++++++++++++ src/io/ftruncate.rs | 35 +++++++++++++++++++++++++++++++++++ src/io/mod.rs | 2 ++ tests/fs_file.rs | 26 +++++++++++++++++++++++++- 5 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 src/io/ftruncate.rs diff --git a/Cargo.toml b/Cargo.toml index 85ce27dd..d14639fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,8 @@ keywords = ["async", "fs", "io-uring"] [dependencies] tokio = { version = "1.2", features = ["net", "rt", "sync"] } slab = "0.4.2" -libc = "0.2.80" -io-uring = "0.6.0" +libc = "0.2.167" +io-uring = { git="https://github.com/vmingchen/io-uring", branch = "ftruncate" } socket2 = { version = "0.4.4", features = ["all"] } bytes = { version = "1.0", optional = true } futures-util = { version = "0.3.26", default-features = false, features = ["std"] } diff --git a/src/fs/file.rs b/src/fs/file.rs index 9cd47f21..bd32ff85 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -854,6 +854,34 @@ impl File { Op::fallocate(&self.fd, offset, len, flags)?.await } + /// Truncates the file to the specified length. + /// + /// If the file was larger than `len` bytes, then the extra data is removed. + /// + /// If the file was smaller than `len` bytes, then the file is extended and filled with zeros. + /// + /// # Examples + /// + /// ```no_run + /// use tokio_uring::fs::File; + /// + /// fn main() -> Result<(), Box> { + /// tokio_uring::start(async { + /// const PATH: &str = "foo.txt"; + /// std::fs::write(PATH, b"hello world").unwrap(); + /// + /// // Truncate to be shorter. + /// let file = OpenOptions::new().write(true).open(PATH).await.unwrap(); + /// file.ftruncate(5).await.unwrap(); + /// + /// let res = std::fs::read_to_string(PATH).unwrap(); + /// assert_eq!(res, "hello"); + /// }) + /// } + pub async fn ftruncate(&self, len: u64) -> io::Result<()> { + Op::ftruncate(&self.fd, len)?.await + } + /// Closes the file using the uring asynchronous close operation and returns the possible error /// as described in the close(2) man page. /// diff --git a/src/io/ftruncate.rs b/src/io/ftruncate.rs new file mode 100644 index 00000000..b4ff07d6 --- /dev/null +++ b/src/io/ftruncate.rs @@ -0,0 +1,35 @@ +use std::io; + +use io_uring::{opcode, types}; + +use crate::{ + io::SharedFd, + runtime::{ + driver::op::{Completable, CqeResult, Op}, + CONTEXT, + }, +}; + +pub(crate) struct Ftruncate { + fd: SharedFd, +} + +impl Op { + pub(crate) fn ftruncate(fd: &SharedFd, len: u64) -> io::Result> { + CONTEXT.with(|x| { + x.handle() + .expect("not in a runtime context") + .submit_op(Ftruncate { fd: fd.clone() }, |ftruncate| { + opcode::Ftruncate::new(types::Fd(ftruncate.fd.raw_fd()), len).build() + }) + }) + } +} + +impl Completable for Ftruncate { + 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 1afcef22..39019c7c 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -58,3 +58,5 @@ mod writev; mod writev_all; pub(crate) use writev_all::writev_at_all; + +mod ftruncate; diff --git a/tests/fs_file.rs b/tests/fs_file.rs index 096565e6..80adab00 100644 --- a/tests/fs_file.rs +++ b/tests/fs_file.rs @@ -5,9 +5,9 @@ use std::{ use tempfile::NamedTempFile; -use tokio_uring::buf::fixed::FixedBufRegistry; use tokio_uring::buf::{BoundedBuf, BoundedBufMut}; use tokio_uring::fs::File; +use tokio_uring::{buf::fixed::FixedBufRegistry, fs::OpenOptions}; #[path = "../src/future.rs"] #[allow(warnings)] @@ -313,6 +313,30 @@ fn basic_fallocate() { }); } +#[test] +fn basic_ftruncate() { + tokio_uring::start(async { + let tempfile = tempfile(); + std::fs::write(&tempfile, b"hello world").unwrap(); + + // Truncate to be shorter. + let file = OpenOptions::new() + .write(true) + .open(&tempfile) + .await + .unwrap(); + file.ftruncate(5).await.unwrap(); + + let res = std::fs::read_to_string(&tempfile).unwrap(); + assert_eq!(res, "hello"); + + // Truncate to be longer; filled with zero. + file.ftruncate(10).await.unwrap(); + let res = std::fs::read_to_string(&tempfile).unwrap(); + assert_eq!(res, "hello\0\0\0\0\0"); + }) +} + fn tempfile() -> NamedTempFile { NamedTempFile::new().unwrap() } From 6bcc9436ded48732cd7a9a481e1834aa4c3201d9 Mon Sep 17 00:00:00 2001 From: Ming Chen Date: Mon, 30 Dec 2024 15:55:26 +0000 Subject: [PATCH 2/2] update version and fmt --- Cargo.toml | 2 +- tests/fs_file.rs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d14639fc..455ba388 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ keywords = ["async", "fs", "io-uring"] tokio = { version = "1.2", features = ["net", "rt", "sync"] } slab = "0.4.2" libc = "0.2.167" -io-uring = { git="https://github.com/vmingchen/io-uring", branch = "ftruncate" } +io-uring = "0.7.3" socket2 = { version = "0.4.4", features = ["all"] } bytes = { version = "1.0", optional = true } futures-util = { version = "0.3.26", default-features = false, features = ["std"] } diff --git a/tests/fs_file.rs b/tests/fs_file.rs index 80adab00..58d1cc06 100644 --- a/tests/fs_file.rs +++ b/tests/fs_file.rs @@ -5,9 +5,10 @@ use std::{ use tempfile::NamedTempFile; -use tokio_uring::buf::{BoundedBuf, BoundedBufMut}; -use tokio_uring::fs::File; -use tokio_uring::{buf::fixed::FixedBufRegistry, fs::OpenOptions}; +use tokio_uring::{ + buf::{fixed::FixedBufRegistry, BoundedBuf, BoundedBufMut}, + fs::{File, OpenOptions}, +}; #[path = "../src/future.rs"] #[allow(warnings)]