From 6cd74479264cbd230e47b9d2a572a75aab4d83b1 Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Sat, 20 Jul 2024 11:55:04 +0200 Subject: [PATCH] Add AsyncFd::truncate Calls ftruncate(2), truncating a file to a specified length. --- src/fs.rs | 27 +++++++++++++++++++++++++++ src/op.rs | 6 ++++++ tests/async_fd/fs.rs | 44 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index cf608048..0cbbc9a3 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -365,6 +365,17 @@ impl AsyncFd { ) -> Allocate<'fd, D> { Allocate::new(self, (offset, length, mode)) } + + /// Truncate the file to `length`. + /// + /// If the file previously was larger than this size, the extra data is + /// lost. If the file previously was shorter, it is extended, and the + /// extended part reads as null bytes. + #[doc = man_link!(ftruncate(2))] + #[doc(alias = "ftruncate")] + pub const fn truncate<'fd>(&'fd self, length: u64) -> Truncate<'fd, D> { + Truncate::new(self, length) + } } // SyncData. @@ -430,6 +441,22 @@ op_future! { }, } +// Truncate. +op_future! { + fn AsyncFd::truncate -> (), + struct Truncate<'fd> { + // Doesn't need any fields. + }, + setup_state: flags: u64, + setup: |submission, fd, (), length| unsafe { + submission.ftruncate(fd.fd(), length); + }, + map_result: |this, (), res| { + debug_assert!(res == 0); + Ok(()) + }, +} + /// Metadata information about a file. /// /// See [`AsyncFd::metadata`] and [`Stat`]. diff --git a/src/op.rs b/src/op.rs index fddf1025..5a1df9aa 100644 --- a/src/op.rs +++ b/src/op.rs @@ -745,6 +745,12 @@ impl Submission { }; } + pub(crate) unsafe fn ftruncate(&mut self, fd: RawFd, offset: u64) { + self.inner.opcode = libc::IORING_OP_FTRUNCATE as u8; + self.inner.fd = fd; + self.inner.__bindgen_anon_1 = libc::io_uring_sqe__bindgen_ty_1 { off: offset }; + } + pub(crate) unsafe fn wake(&mut self, ring_fd: RawFd) { self.msg(ring_fd, u64::MAX, 0, 0); self.no_completion_event(); diff --git a/tests/async_fd/fs.rs b/tests/async_fd/fs.rs index 8795b984..c0d58e72 100644 --- a/tests/async_fd/fs.rs +++ b/tests/async_fd/fs.rs @@ -5,13 +5,13 @@ use std::path::{Path, PathBuf}; use std::{panic, str}; use a10::fd::File; -use a10::fs::{self, Advise, Allocate, CreateDir, Delete, Open, OpenOptions, Rename}; +use a10::fs::{self, Advise, Allocate, CreateDir, Delete, Open, OpenOptions, Rename, Truncate}; use a10::io::{Read, ReadVectored, Write, WriteVectored}; use a10::{Extract, SubmissionQueue}; use crate::util::{ - defer, is_send, is_sync, page_size, remove_test_dir, remove_test_file, test_queue, TestFile, - Waker, LOREM_IPSUM_5, LOREM_IPSUM_50, + defer, is_send, is_sync, page_size, remove_test_dir, remove_test_file, require_kernel, + test_queue, TestFile, Waker, LOREM_IPSUM_5, LOREM_IPSUM_50, }; const DATA: &[u8] = b"Hello, World"; @@ -521,6 +521,44 @@ fn fadvise() { assert!(buf == test_file.content, "read content is different"); } +#[test] +fn ftruncate() { + require_kernel!(6, 9); + + let sq = test_queue(); + let waker = Waker::new(); + + is_send::(); + is_sync::(); + + let mut path = temp_dir(); + path.push("ftruncate"); + let _d = defer(|| remove_test_file(&path)); + + let open_file: Open = OpenOptions::new() + .write() + .create() + .truncate() + .open(sq, path.clone()); + let file = waker.block_on(open_file).unwrap(); + + waker + .block_on(file.write_all(LOREM_IPSUM_50.content)) + .unwrap(); + + let metadata = waker.block_on(file.metadata()).unwrap(); + assert_eq!(metadata.len(), LOREM_IPSUM_50.content.len() as u64); + + const SIZE: u64 = 4096; + + waker + .block_on(file.truncate(SIZE)) + .expect("failed fallocate"); + + let metadata = waker.block_on(file.metadata()).unwrap(); + assert_eq!(metadata.len(), SIZE); +} + #[test] fn fallocate() { let sq = test_queue();