Skip to main content

compio_driver/sys/
unix_op.rs

1use std::{
2    ffi::CString,
3    marker::PhantomPinned,
4    net::Shutdown,
5    os::fd::{AsFd, AsRawFd, OwnedFd},
6    pin::Pin,
7};
8
9use compio_buf::{IntoInner, IoBuf, IoBufMut, IoVectoredBuf, IoVectoredBufMut};
10#[cfg(not(any(
11    all(target_os = "linux", not(target_env = "musl")),
12    target_os = "android",
13    target_os = "l4re",
14    target_os = "hurd"
15)))]
16use libc::{ftruncate as ftruncate64, off_t as off64_t};
17#[cfg(any(
18    all(target_os = "linux", not(target_env = "musl")),
19    target_os = "android",
20    target_os = "l4re",
21    target_os = "hurd"
22))]
23use libc::{ftruncate64, off64_t};
24use pin_project_lite::pin_project;
25use socket2::{SockAddr, SockAddrStorage, socklen_t};
26
27use crate::{op::*, sys::aio::*, sys_slice::*};
28
29/// Open or create a file with flags and mode.
30pub struct OpenFile {
31    pub(crate) path: CString,
32    pub(crate) flags: i32,
33    pub(crate) mode: libc::mode_t,
34}
35
36impl OpenFile {
37    /// Create [`OpenFile`].
38    pub fn new(path: CString, flags: i32, mode: libc::mode_t) -> Self {
39        Self { path, flags, mode }
40    }
41}
42
43#[derive(Debug)]
44///  Truncates or extends the underlying file, updating the size of this file to
45/// become `size`.
46pub struct TruncateFile<S: AsFd> {
47    pub(crate) fd: S,
48    pub(crate) size: u64,
49}
50
51impl<S: AsFd> TruncateFile<S> {
52    /// Create [`TruncateFile`].
53    pub fn new(fd: S, size: u64) -> Self {
54        Self { fd, size }
55    }
56
57    pub(crate) fn truncate(&self) -> std::io::Result<usize> {
58        let size: off64_t = self
59            .size
60            .try_into()
61            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
62        crate::syscall!(ftruncate64(self.fd.as_fd().as_raw_fd(), size)).map(|v| v as _)
63    }
64}
65
66#[cfg(not(gnulinux))]
67pub use libc::stat as Stat;
68#[cfg(gnulinux)]
69pub use libc::stat64 as Stat;
70#[cfg(gnulinux)]
71pub(crate) use libc::statx as Statx;
72
73#[cfg(all(target_os = "linux", not(target_env = "gnu")))]
74#[repr(C)]
75pub(crate) struct StatxTimestamp {
76    pub tv_sec: i64,
77    pub tv_nsec: u32,
78    pub __statx_timestamp_pad1: [i32; 1],
79}
80
81#[cfg(all(target_os = "linux", not(target_env = "gnu")))]
82#[repr(C)]
83pub(crate) struct Statx {
84    pub stx_mask: u32,
85    pub stx_blksize: u32,
86    pub stx_attributes: u64,
87    pub stx_nlink: u32,
88    pub stx_uid: u32,
89    pub stx_gid: u32,
90    pub stx_mode: u16,
91    __statx_pad1: [u16; 1],
92    pub stx_ino: u64,
93    pub stx_size: u64,
94    pub stx_blocks: u64,
95    pub stx_attributes_mask: u64,
96    pub stx_atime: StatxTimestamp,
97    pub stx_btime: StatxTimestamp,
98    pub stx_ctime: StatxTimestamp,
99    pub stx_mtime: StatxTimestamp,
100    pub stx_rdev_major: u32,
101    pub stx_rdev_minor: u32,
102    pub stx_dev_major: u32,
103    pub stx_dev_minor: u32,
104    pub stx_mnt_id: u64,
105    pub stx_dio_mem_align: u32,
106    pub stx_dio_offset_align: u32,
107    __statx_pad3: [u64; 12],
108}
109
110#[cfg(target_os = "linux")]
111pub(crate) const fn statx_mask() -> u32 {
112    // Set mask to ensure all known fields are filled
113    libc::STATX_TYPE
114        | libc::STATX_MODE
115        | libc::STATX_NLINK
116        | libc::STATX_UID
117        | libc::STATX_GID
118        | libc::STATX_ATIME
119        | libc::STATX_MTIME
120        | libc::STATX_CTIME
121        | libc::STATX_INO
122        | libc::STATX_SIZE
123        | libc::STATX_BLOCKS
124        | libc::STATX_BTIME
125        | libc::STATX_MNT_ID
126        | libc::STATX_DIOALIGN
127}
128
129#[cfg(target_os = "linux")]
130pub(crate) const fn statx_to_stat(statx: Statx) -> Stat {
131    let mut stat: Stat = unsafe { std::mem::zeroed() };
132    stat.st_dev = libc::makedev(statx.stx_dev_major, statx.stx_dev_minor) as _;
133    stat.st_ino = statx.stx_ino as _;
134    stat.st_nlink = statx.stx_nlink as _;
135    stat.st_mode = statx.stx_mode as _;
136    stat.st_uid = statx.stx_uid as _;
137    stat.st_gid = statx.stx_gid as _;
138    stat.st_rdev = libc::makedev(statx.stx_rdev_major, statx.stx_rdev_minor) as _;
139    stat.st_size = statx.stx_size as _;
140    stat.st_blksize = statx.stx_blksize as _;
141    stat.st_blocks = statx.stx_blocks as _;
142    stat.st_atime = statx.stx_atime.tv_sec as _;
143    stat.st_atime_nsec = statx.stx_atime.tv_nsec as _;
144    stat.st_mtime = statx.stx_mtime.tv_sec as _;
145    stat.st_mtime_nsec = statx.stx_mtime.tv_nsec as _;
146    stat.st_ctime = statx.stx_btime.tv_sec as _;
147    stat.st_ctime_nsec = statx.stx_btime.tv_nsec as _;
148    stat
149}
150
151pin_project! {
152    /// Read a file at specified position into vectored buffer.
153    pub struct ReadVectoredAt<T: IoVectoredBufMut, S> {
154        pub(crate) fd: S,
155        pub(crate) offset: u64,
156        #[pin]
157        pub(crate) buffer: T,
158        pub(crate) slices: Vec<SysSlice>,
159        pub(crate) aiocb: aiocb,
160        _p: PhantomPinned,
161    }
162}
163
164impl<T: IoVectoredBufMut, S> ReadVectoredAt<T, S> {
165    /// Create [`ReadVectoredAt`].
166    pub fn new(fd: S, offset: u64, buffer: T) -> Self {
167        Self {
168            fd,
169            offset,
170            buffer,
171            slices: vec![],
172            aiocb: new_aiocb(),
173            _p: PhantomPinned,
174        }
175    }
176}
177
178impl<T: IoVectoredBufMut, S> IntoInner for ReadVectoredAt<T, S> {
179    type Inner = T;
180
181    fn into_inner(self) -> Self::Inner {
182        self.buffer
183    }
184}
185
186pin_project! {
187    /// Write a file at specified position from vectored buffer.
188    pub struct WriteVectoredAt<T: IoVectoredBuf, S> {
189        pub(crate) fd: S,
190        pub(crate) offset: u64,
191        #[pin]
192        pub(crate) buffer: T,
193        pub(crate) slices: Vec<SysSlice>,
194        pub(crate) aiocb: aiocb,
195        _p: PhantomPinned,
196    }
197}
198impl<T: IoVectoredBuf, S> WriteVectoredAt<T, S> {
199    /// Create [`WriteVectoredAt`]
200    pub fn new(fd: S, offset: u64, buffer: T) -> Self {
201        Self {
202            fd,
203            offset,
204            buffer,
205            slices: vec![],
206            aiocb: new_aiocb(),
207            _p: PhantomPinned,
208        }
209    }
210}
211
212impl<T: IoVectoredBuf, S> IntoInner for WriteVectoredAt<T, S> {
213    type Inner = T;
214
215    fn into_inner(self) -> Self::Inner {
216        self.buffer
217    }
218}
219
220pin_project! {
221    /// Receive a file into vectored buffer.
222    pub struct ReadVectored<T: IoVectoredBufMut, S> {
223        pub(crate) fd: S,
224        #[pin]
225        pub(crate) buffer: T,
226        pub(crate) slices: Vec<SysSlice>,
227        _p: PhantomPinned,
228    }
229}
230
231impl<T: IoVectoredBufMut, S> ReadVectored<T, S> {
232    /// Create [`ReadVectored`].
233    pub fn new(fd: S, buffer: T) -> Self {
234        Self {
235            fd,
236            buffer,
237            slices: vec![],
238            _p: PhantomPinned,
239        }
240    }
241}
242
243impl<T: IoVectoredBufMut, S> IntoInner for ReadVectored<T, S> {
244    type Inner = T;
245
246    fn into_inner(self) -> Self::Inner {
247        self.buffer
248    }
249}
250
251pin_project! {
252    /// Send to a file from vectored buffer.
253    pub struct WriteVectored<T: IoVectoredBuf, S> {
254        pub(crate) fd: S,
255        #[pin]
256        pub(crate) buffer: T,
257        pub(crate) slices: Vec<SysSlice>,
258        _p: PhantomPinned,
259    }
260}
261
262impl<T: IoVectoredBuf, S> WriteVectored<T, S> {
263    /// Create [`WriteVectored`].
264    pub fn new(fd: S, buffer: T) -> Self {
265        Self {
266            fd,
267            buffer,
268            slices: vec![],
269            _p: PhantomPinned,
270        }
271    }
272}
273
274impl<T: IoVectoredBuf, S> IntoInner for WriteVectored<T, S> {
275    type Inner = T;
276
277    fn into_inner(self) -> Self::Inner {
278        self.buffer
279    }
280}
281
282/// Remove file or directory.
283pub struct Unlink {
284    pub(crate) path: CString,
285    pub(crate) dir: bool,
286}
287
288impl Unlink {
289    /// Create [`Unlink`].
290    pub fn new(path: CString, dir: bool) -> Self {
291        Self { path, dir }
292    }
293}
294
295/// Create a directory.
296pub struct CreateDir {
297    pub(crate) path: CString,
298    pub(crate) mode: libc::mode_t,
299}
300
301impl CreateDir {
302    /// Create [`CreateDir`].
303    pub fn new(path: CString, mode: libc::mode_t) -> Self {
304        Self { path, mode }
305    }
306}
307
308/// Rename a file or directory.
309pub struct Rename {
310    pub(crate) old_path: CString,
311    pub(crate) new_path: CString,
312}
313
314impl Rename {
315    /// Create [`Rename`].
316    pub fn new(old_path: CString, new_path: CString) -> Self {
317        Self { old_path, new_path }
318    }
319}
320
321/// Create a symlink.
322pub struct Symlink {
323    pub(crate) source: CString,
324    pub(crate) target: CString,
325}
326
327impl Symlink {
328    /// Create [`Symlink`]. `target` is a symlink to `source`.
329    pub fn new(source: CString, target: CString) -> Self {
330        Self { source, target }
331    }
332}
333
334/// Create a hard link.
335pub struct HardLink {
336    pub(crate) source: CString,
337    pub(crate) target: CString,
338}
339
340impl HardLink {
341    /// Create [`HardLink`]. `target` is a hard link to `source`.
342    pub fn new(source: CString, target: CString) -> Self {
343        Self { source, target }
344    }
345}
346
347/// Create a socket.
348pub struct CreateSocket {
349    pub(crate) domain: i32,
350    pub(crate) socket_type: i32,
351    pub(crate) protocol: i32,
352}
353
354impl CreateSocket {
355    /// Create [`CreateSocket`].
356    pub fn new(domain: i32, socket_type: i32, protocol: i32) -> Self {
357        Self {
358            domain,
359            socket_type,
360            protocol,
361        }
362    }
363}
364
365impl<S> ShutdownSocket<S> {
366    pub(crate) fn how(&self) -> i32 {
367        match self.how {
368            Shutdown::Write => libc::SHUT_WR,
369            Shutdown::Read => libc::SHUT_RD,
370            Shutdown::Both => libc::SHUT_RDWR,
371        }
372    }
373}
374
375pin_project! {
376    /// Accept a connection.
377    pub struct Accept<S> {
378        pub(crate) fd: S,
379        pub(crate) buffer: SockAddrStorage,
380        pub(crate) addr_len: socklen_t,
381        pub(crate) accepted_fd: Option<OwnedFd>,
382        _p: PhantomPinned,
383    }
384}
385
386impl<S> Accept<S> {
387    /// Create [`Accept`].
388    pub fn new(fd: S) -> Self {
389        let buffer = SockAddrStorage::zeroed();
390        let addr_len = buffer.size_of();
391        Self {
392            fd,
393            buffer,
394            addr_len,
395            accepted_fd: None,
396            _p: PhantomPinned,
397        }
398    }
399
400    /// Get the remote address from the inner buffer.
401    pub fn into_addr(mut self) -> SockAddr {
402        std::mem::forget(self.accepted_fd.take());
403        unsafe { SockAddr::new(self.buffer, self.addr_len) }
404    }
405}
406
407pin_project! {
408    /// Receive data from remote.
409    ///
410    /// It is only used for socket operations. If you want to read from a pipe, use
411    /// [`Read`].
412    pub struct Recv<T: IoBufMut, S> {
413        pub(crate) fd: S,
414        #[pin]
415        pub(crate) buffer: T,
416        pub(crate) flags: i32,
417        _p: PhantomPinned,
418    }
419}
420
421impl<T: IoBufMut, S> Recv<T, S> {
422    /// Create [`Recv`].
423    pub fn new(fd: S, buffer: T, flags: i32) -> Self {
424        Self {
425            fd,
426            buffer,
427            flags,
428            _p: PhantomPinned,
429        }
430    }
431}
432
433impl<T: IoBufMut, S> IntoInner for Recv<T, S> {
434    type Inner = T;
435
436    fn into_inner(self) -> Self::Inner {
437        self.buffer
438    }
439}
440
441pin_project! {
442    /// Send data to remote.
443    ///
444    /// It is only used for socket operations. If you want to write to a pipe, use
445    /// [`Write`].
446    pub struct Send<T: IoBuf, S> {
447        pub(crate) fd: S,
448        #[pin]
449        pub(crate) buffer: T,
450        pub(crate) flags: i32,
451        _p: PhantomPinned,
452    }
453}
454
455impl<T: IoBuf, S> Send<T, S> {
456    /// Create [`Send`].
457    pub fn new(fd: S, buffer: T, flags: i32) -> Self {
458        Self {
459            fd,
460            buffer,
461            flags,
462            _p: PhantomPinned,
463        }
464    }
465}
466
467impl<T: IoBuf, S> IntoInner for Send<T, S> {
468    type Inner = T;
469
470    fn into_inner(self) -> Self::Inner {
471        self.buffer
472    }
473}
474
475pin_project! {
476    /// Receive data from remote into vectored buffer.
477    pub struct RecvVectored<T: IoVectoredBufMut, S> {
478        pub(crate) msg: libc::msghdr,
479        pub(crate) fd: S,
480        #[pin]
481        pub(crate) buffer: T,
482        pub(crate) slices: Vec<SysSlice>,
483        pub(crate) flags: i32,
484        _p: PhantomPinned,
485    }
486}
487
488impl<T: IoVectoredBufMut, S> RecvVectored<T, S> {
489    /// Create [`RecvVectored`].
490    pub fn new(fd: S, buffer: T, flags: i32) -> Self {
491        Self {
492            msg: unsafe { std::mem::zeroed() },
493            fd,
494            buffer,
495            slices: vec![],
496            flags,
497            _p: PhantomPinned,
498        }
499    }
500
501    pub(crate) fn set_msg(self: Pin<&mut Self>) {
502        let this = self.project();
503
504        *this.slices = this.buffer.sys_slices_mut();
505        this.msg.msg_iov = this.slices.as_mut_ptr() as _;
506        this.msg.msg_iovlen = this.slices.len() as _;
507    }
508}
509
510impl<T: IoVectoredBufMut, S> IntoInner for RecvVectored<T, S> {
511    type Inner = T;
512
513    fn into_inner(self) -> Self::Inner {
514        self.buffer
515    }
516}
517
518pin_project! {
519    /// Send data to remote from vectored buffer.
520    pub struct SendVectored<T: IoVectoredBuf, S> {
521        pub(crate) msg: libc::msghdr,
522        pub(crate) fd: S,
523        #[pin]
524        pub(crate) buffer: T,
525        pub(crate) slices: Vec<SysSlice>,
526        pub(crate) flags: i32,
527        _p: PhantomPinned,
528    }
529}
530
531impl<T: IoVectoredBuf, S> SendVectored<T, S> {
532    /// Create [`SendVectored`].
533    pub fn new(fd: S, buffer: T, flags: i32) -> Self {
534        Self {
535            msg: unsafe { std::mem::zeroed() },
536            fd,
537            buffer,
538            slices: vec![],
539            flags,
540            _p: PhantomPinned,
541        }
542    }
543
544    pub(crate) fn set_msg(self: Pin<&mut Self>) {
545        let this = self.project();
546
547        *this.slices = this.buffer.as_ref().sys_slices();
548        this.msg.msg_iov = this.slices.as_mut_ptr() as _;
549        this.msg.msg_iovlen = this.slices.len() as _;
550    }
551}
552
553impl<T: IoVectoredBuf, S> IntoInner for SendVectored<T, S> {
554    type Inner = T;
555
556    fn into_inner(self) -> Self::Inner {
557        self.buffer
558    }
559}
560
561pin_project! {
562    /// Receive data and source address with ancillary data into vectored buffer.
563    pub struct RecvMsg<T: IoVectoredBufMut, C: IoBufMut, S> {
564        pub(crate) msg: libc::msghdr,
565        pub(crate) addr: SockAddrStorage,
566        pub(crate) fd: S,
567        #[pin]
568        pub(crate) buffer: T,
569        pub(crate) control: C,
570        pub(crate) slices: Vec<SysSlice>,
571        pub(crate) flags: i32,
572        _p: PhantomPinned,
573    }
574}
575
576impl<T: IoVectoredBufMut, C: IoBufMut, S> RecvMsg<T, C, S> {
577    /// Create [`RecvMsg`].
578    ///
579    /// # Panics
580    ///
581    /// This function will panic if the control message buffer is misaligned.
582    pub fn new(fd: S, buffer: T, control: C, flags: i32) -> Self {
583        assert!(
584            control.buf_ptr().cast::<libc::cmsghdr>().is_aligned(),
585            "misaligned control message buffer"
586        );
587        Self {
588            msg: unsafe { std::mem::zeroed() },
589            addr: SockAddrStorage::zeroed(),
590            fd,
591            buffer,
592            control,
593            slices: vec![],
594            flags,
595            _p: PhantomPinned,
596        }
597    }
598
599    pub(crate) fn set_msg(self: Pin<&mut Self>) {
600        let this = self.project();
601        *this.slices = this.buffer.sys_slices_mut();
602
603        this.msg.msg_name = this.addr as *mut _ as _;
604        this.msg.msg_namelen = this.addr.size_of() as _;
605        this.msg.msg_iov = this.slices.as_mut_ptr() as _;
606        this.msg.msg_iovlen = this.slices.len() as _;
607        this.msg.msg_control = this.control.buf_mut_ptr() as _;
608        this.msg.msg_controllen = this.control.buf_capacity() as _;
609    }
610}
611
612impl<T: IoVectoredBufMut, C: IoBufMut, S> IntoInner for RecvMsg<T, C, S> {
613    type Inner = ((T, C), SockAddrStorage, socklen_t, usize);
614
615    fn into_inner(self) -> Self::Inner {
616        (
617            (self.buffer, self.control),
618            self.addr,
619            self.msg.msg_namelen,
620            self.msg.msg_controllen as _,
621        )
622    }
623}
624
625pin_project! {
626    /// Send data to specified address accompanied by ancillary data from vectored
627    /// buffer.
628    pub struct SendMsg<T: IoVectoredBuf, C: IoBuf, S> {
629        pub(crate) msg: libc::msghdr,
630        pub(crate) fd: S,
631        #[pin]
632        pub(crate) buffer: T,
633        #[pin]
634        pub(crate) control: C,
635        pub(crate) addr: SockAddr,
636        pub(crate) slices: Vec<SysSlice>,
637        pub(crate) flags: i32,
638        _p: PhantomPinned,
639    }
640}
641
642impl<T: IoVectoredBuf, C: IoBuf, S> SendMsg<T, C, S> {
643    /// Create [`SendMsg`].
644    ///
645    /// # Panics
646    ///
647    /// This function will panic if the control message buffer is misaligned.
648    pub fn new(fd: S, buffer: T, control: C, addr: SockAddr, flags: i32) -> Self {
649        assert!(
650            control.buf_ptr().cast::<libc::cmsghdr>().is_aligned(),
651            "misaligned control message buffer"
652        );
653        Self {
654            msg: unsafe { std::mem::zeroed() },
655            fd,
656            buffer,
657            control,
658            addr,
659            slices: vec![],
660            flags,
661            _p: PhantomPinned,
662        }
663    }
664
665    pub(crate) fn set_msg(self: Pin<&mut Self>) {
666        let this = self.project();
667        *this.slices = this.buffer.as_ref().sys_slices();
668        this.msg.msg_name = this.addr.as_ptr() as _;
669        this.msg.msg_namelen = this.addr.len();
670        this.msg.msg_iov = this.slices.as_ptr() as _;
671        this.msg.msg_iovlen = this.slices.len() as _;
672        this.msg.msg_control = this.control.buf_ptr() as _;
673        this.msg.msg_controllen = this.control.buf_len() as _;
674    }
675}
676
677impl<T: IoVectoredBuf, C: IoBuf, S> IntoInner for SendMsg<T, C, S> {
678    type Inner = (T, C);
679
680    fn into_inner(self) -> Self::Inner {
681        (self.buffer, self.control)
682    }
683}
684
685/// The interest to poll a file descriptor.
686#[derive(Debug, Clone, Copy, PartialEq, Eq)]
687pub enum Interest {
688    /// Represents a read operation.
689    Readable,
690    /// Represents a write operation.
691    Writable,
692}
693
694/// Poll a file descriptor for specified [`Interest`].
695pub struct PollOnce<S> {
696    pub(crate) fd: S,
697    pub(crate) interest: Interest,
698}
699
700impl<S> PollOnce<S> {
701    /// Create [`PollOnce`].
702    pub fn new(fd: S, interest: Interest) -> Self {
703        Self { fd, interest }
704    }
705}
706
707/// Splice data between two file descriptors.
708#[cfg(linux_all)]
709pub struct Splice<S1, S2> {
710    pub(crate) fd_in: S1,
711    pub(crate) offset_in: i64,
712    pub(crate) fd_out: S2,
713    pub(crate) offset_out: i64,
714    pub(crate) len: usize,
715    pub(crate) flags: u32,
716}
717
718#[cfg(linux_all)]
719impl<S1, S2> Splice<S1, S2> {
720    /// Create [`Splice`].
721    ///
722    /// `offset_in` and `offset_out` specify the offset to read from and write
723    /// to. Use `-1` for pipe ends or to use/update the current file
724    /// position.
725    pub fn new(
726        fd_in: S1,
727        offset_in: i64,
728        fd_out: S2,
729        offset_out: i64,
730        len: usize,
731        flags: u32,
732    ) -> Self {
733        Self {
734            fd_in,
735            offset_in,
736            fd_out,
737            offset_out,
738            len,
739            flags,
740        }
741    }
742}