twizzler_rt_abi/
fd.rs

1//! Runtime interface for file descriptors.
2
3use core::time::Duration;
4
5pub use crate::bindings::descriptor as RawFd;
6use crate::{
7    error::{ArgumentError, RawTwzError, TwzError},
8    Result,
9};
10
11bitflags::bitflags! {
12    /// Flags for file descriptors.
13    #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
14pub struct FdFlags : crate::bindings::fd_flags {
15    /// The file descriptor refers to a terminal.
16    const IS_TERMINAL = crate::bindings::FD_IS_TERMINAL;
17}
18}
19
20#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default)]
21#[repr(u32)]
22/// Possible Fd Kinds
23pub enum FdKind {
24    #[default]
25    Regular = crate::bindings::fd_kind_FdKind_Regular,
26    Directory = crate::bindings::fd_kind_FdKind_Directory,
27    SymLink = crate::bindings::fd_kind_FdKind_SymLink,
28    Other = u32::MAX,
29}
30
31impl From<u32> for FdKind {
32    fn from(value: u32) -> Self {
33        match value {
34            crate::bindings::fd_kind_FdKind_Regular => Self::Regular,
35            crate::bindings::fd_kind_FdKind_Directory => Self::Directory,
36            crate::bindings::fd_kind_FdKind_SymLink => Self::SymLink,
37            _ => Self::Other,
38        }
39    }
40}
41
42impl Into<u32> for FdKind {
43    fn into(self) -> u32 {
44        match self {
45            Self::Regular => crate::bindings::fd_kind_FdKind_Regular,
46            Self::Directory => crate::bindings::fd_kind_FdKind_Directory,
47            Self::SymLink => crate::bindings::fd_kind_FdKind_SymLink,
48            Self::Other => u32::MAX,
49        }
50    }
51}
52
53/// Information about an open file descriptor.
54#[derive(Copy, Clone, Debug, Default)]
55pub struct FdInfo {
56    /// Flags for this descriptor
57    pub flags: FdFlags,
58    /// Length of underlying object
59    pub size: u64,
60    /// Kind of file
61    pub kind: FdKind,
62    /// Object ID
63    pub id: twizzler_types::ObjID,
64    /// Created time
65    pub created: Duration,
66    /// Accessed time
67    pub accessed: Duration,
68    /// Modified time
69    pub modified: Duration,
70    /// Unix mode
71    pub unix_mode: u32,
72}
73
74impl From<crate::bindings::fd_info> for FdInfo {
75    fn from(value: crate::bindings::fd_info) -> Self {
76        Self {
77            flags: FdFlags::from_bits_truncate(value.flags),
78            size: value.len,
79            kind: FdKind::from(value.kind),
80            id: value.id,
81            created: value.created.into(),
82            accessed: value.accessed.into(),
83            modified: value.modified.into(),
84            unix_mode: value.unix_mode,
85        }
86    }
87}
88
89impl From<FdInfo> for crate::bindings::fd_info {
90    fn from(value: FdInfo) -> Self {
91        Self {
92            flags: value.flags.bits(),
93            len: value.size,
94            kind: value.kind.into(),
95            id: value.id,
96            created: value.created.into(),
97            accessed: value.accessed.into(),
98            modified: value.modified.into(),
99            unix_mode: value.unix_mode,
100        }
101    }
102}
103
104impl From<Result<RawFd>> for crate::bindings::open_result {
105    fn from(value: Result<RawFd>) -> Self {
106        match value {
107            Ok(fd) => Self {
108                fd,
109                err: RawTwzError::success().raw(),
110            },
111            Err(e) => Self {
112                fd: 0,
113                err: e.raw(),
114            },
115        }
116    }
117}
118
119impl From<crate::bindings::open_result> for Result<RawFd> {
120    fn from(value: crate::bindings::open_result) -> Self {
121        let raw = RawTwzError::new(value.err);
122        if raw.is_success() {
123            Ok(value.fd)
124        } else {
125            Err(raw.error())
126        }
127    }
128}
129
130/// Get information about an open file descriptor. If the fd is invalid or closed, returns None.
131pub fn twz_rt_fd_get_info(fd: RawFd) -> Result<FdInfo> {
132    let mut info = core::mem::MaybeUninit::uninit();
133    unsafe {
134        if crate::bindings::twz_rt_fd_get_info(fd, info.as_mut_ptr()) {
135            return Ok(info.assume_init().into());
136        }
137    }
138    Err(TwzError::Argument(ArgumentError::BadHandle))
139}
140
141pub use crate::bindings::name_entry as NameEntry;
142
143impl NameEntry {
144    pub const NAME_MAX_LEN: usize = crate::bindings::NAME_ENTRY_LEN as usize;
145    pub fn new(iname: &[u8], info: crate::bindings::fd_info) -> Self {
146        let nl = iname.len().min(Self::NAME_MAX_LEN);
147        let mut name = [0; Self::NAME_MAX_LEN];
148        name[0..nl].copy_from_slice(&iname[0..nl]);
149        Self {
150            name,
151            info,
152            name_len: nl as u32,
153            linkname_len: 0,
154        }
155    }
156
157    pub fn new_symlink(iname: &[u8], ilinkname: &[u8], info: crate::bindings::fd_info) -> Self {
158        let nl = iname.len().min(Self::NAME_MAX_LEN);
159        let linknl = ilinkname.len().min(Self::NAME_MAX_LEN - nl);
160        let mut name = [0; Self::NAME_MAX_LEN];
161        name[0..nl].copy_from_slice(&iname[0..nl]);
162        name[nl..(nl + linknl)].copy_from_slice(&ilinkname[0..linknl]);
163        Self {
164            name,
165            info,
166            name_len: nl as u32,
167            linkname_len: linknl as u32,
168        }
169    }
170
171    pub fn name_bytes(&self) -> &[u8] {
172        &self.name[0..self.name_len as usize]
173    }
174
175    pub fn linkname_bytes(&self) -> &[u8] {
176        &self.name[self.name_len as usize..(self.name_len + self.linkname_len) as usize]
177    }
178}
179
180/// Enumerate sub-names for an fd (e.g. directory entries). Returns Some(n) on success, None if no
181/// names can be enumerated. Return of Some(n) indicates number of items read into the buffer, 0 if
182/// end of name list. Offset argument specifies number of entries to skip.
183pub fn twz_rt_fd_enumerate_names(
184    fd: RawFd,
185    entries: &mut [NameEntry],
186    off: usize,
187) -> Result<usize> {
188    unsafe {
189        crate::bindings::twz_rt_fd_enumerate_names(fd, entries.as_mut_ptr(), entries.len(), off)
190            .into()
191    }
192}
193
194/// Open a file descriptor by name, as a C-string.
195pub fn twz_rt_fd_copen(
196    name: &core::ffi::CStr,
197    create: crate::bindings::create_options,
198    flags: u32,
199) -> Result<RawFd> {
200    let info = crate::bindings::open_info {
201        name: name.as_ptr().cast(),
202        len: name.count_bytes(),
203        create,
204        flags,
205    };
206    unsafe { crate::bindings::twz_rt_fd_open(info).into() }
207}
208
209/// Open a file descriptor by name, as a Rust-string.
210pub fn twz_rt_fd_open(
211    name: &str,
212    create: crate::bindings::create_options,
213    flags: u32,
214) -> Result<RawFd> {
215    let info = crate::bindings::open_info {
216        name: name.as_ptr().cast(),
217        len: name.len(),
218        create,
219        flags,
220    };
221    unsafe { crate::bindings::twz_rt_fd_open(info).into() }
222}
223
224/// Remove a name
225pub fn twz_rt_fd_remove(name: &str) -> Result<()> {
226    unsafe {
227        RawTwzError::new(crate::bindings::twz_rt_fd_remove(
228            name.as_ptr().cast(),
229            name.len(),
230        ))
231        .result()
232    }
233}
234
235/// Make a new namespace
236pub fn twz_rt_fd_mkns(name: &str) -> Result<()> {
237    unsafe {
238        RawTwzError::new(crate::bindings::twz_rt_fd_mkns(
239            name.as_ptr().cast(),
240            name.len(),
241        ))
242        .result()
243    }
244}
245
246/// Make a new symlink
247pub fn twz_rt_fd_symlink(name: &str, target: &str) -> Result<()> {
248    unsafe {
249        RawTwzError::new(crate::bindings::twz_rt_fd_symlink(
250            name.as_ptr().cast(),
251            name.len(),
252            target.as_ptr().cast(),
253            target.len(),
254        ))
255        .result()
256    }
257}
258
259pub fn twz_rt_fd_readlink(name: &str, buf: &mut [u8]) -> Result<usize> {
260    let mut len: u64 = 0;
261    unsafe {
262        RawTwzError::new(crate::bindings::twz_rt_fd_readlink(
263            name.as_ptr().cast(),
264            name.len(),
265            buf.as_mut_ptr().cast(),
266            buf.len(),
267            &mut len,
268        ))
269        .result()?;
270    }
271    Ok(len as usize)
272}
273
274#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
275#[repr(u32)]
276pub enum OpenAnonKind {
277    Pipe = crate::bindings::open_anon_kind_AnonKind_Pipe,
278    SocketConnect = crate::bindings::open_anon_kind_AnonKind_SocketConnect,
279    SocketBind = crate::bindings::open_anon_kind_AnonKind_SocketBind,
280}
281
282impl TryFrom<u32> for OpenAnonKind {
283    type Error = ();
284
285    fn try_from(val: u32) -> core::result::Result<OpenAnonKind, Self::Error> {
286        match val {
287            crate::bindings::open_anon_kind_AnonKind_Pipe => Ok(Self::Pipe),
288            crate::bindings::open_anon_kind_AnonKind_SocketConnect => Ok(Self::SocketConnect),
289            crate::bindings::open_anon_kind_AnonKind_SocketBind => Ok(Self::SocketBind),
290            _ => Err(()),
291        }
292    }
293}
294
295impl From<OpenAnonKind> for u32 {
296    fn from(val: OpenAnonKind) -> u32 {
297        match val {
298            OpenAnonKind::Pipe => crate::bindings::open_anon_kind_AnonKind_Pipe,
299            OpenAnonKind::SocketBind => crate::bindings::open_anon_kind_AnonKind_SocketBind,
300            OpenAnonKind::SocketConnect => crate::bindings::open_anon_kind_AnonKind_SocketConnect,
301        }
302    }
303}
304
305#[derive(Clone, Copy)]
306#[repr(transparent)]
307pub struct SocketAddress(pub(crate) crate::bindings::socket_address);
308
309impl SocketAddress {
310    fn new_v4(octets: [u8; 4], port: u16) -> Self {
311        Self(crate::bindings::socket_address {
312            kind: crate::bindings::addr_kind_AddrKind_Ipv4,
313            addr_octets: crate::bindings::socket_address_addrs { v4: octets },
314            port,
315            flowinfo: 0,
316            scope_id: 0,
317        })
318    }
319
320    fn new_v6(octets: [u8; 16], port: u16, flowinfo: u32, scope_id: u32) -> Self {
321        Self(crate::bindings::socket_address {
322            kind: crate::bindings::addr_kind_AddrKind_Ipv6,
323            addr_octets: crate::bindings::socket_address_addrs { v6: octets },
324            port,
325            flowinfo,
326            scope_id,
327        })
328    }
329
330    fn v4_octets(&self) -> [u8; 4] {
331        assert!(self.is_v4());
332        unsafe { self.0.addr_octets.v4 }
333    }
334
335    fn v6_octets(&self) -> [u8; 16] {
336        assert!(!self.is_v4());
337        unsafe { self.0.addr_octets.v6 }
338    }
339
340    fn is_v4(&self) -> bool {
341        self.0.kind == crate::bindings::addr_kind_AddrKind_Ipv4
342    }
343}
344
345impl From<SocketAddress> for core::net::IpAddr {
346    fn from(value: SocketAddress) -> Self {
347        if value.is_v4() {
348            Self::V4(core::net::Ipv4Addr::from_octets(value.v4_octets()))
349        } else {
350            Self::V6(core::net::Ipv6Addr::from_octets(value.v6_octets()))
351        }
352    }
353}
354
355impl From<SocketAddress> for core::net::SocketAddr {
356    fn from(value: SocketAddress) -> Self {
357        if value.is_v4() {
358            Self::V4(core::net::SocketAddrV4::new(
359                core::net::Ipv4Addr::from_octets(value.v4_octets()),
360                value.0.port,
361            ))
362        } else {
363            Self::V6(core::net::SocketAddrV6::new(
364                core::net::Ipv6Addr::from_octets(value.v6_octets()),
365                value.0.port,
366                value.0.flowinfo,
367                value.0.scope_id,
368            ))
369        }
370    }
371}
372
373impl From<core::net::Ipv4Addr> for SocketAddress {
374    fn from(value: core::net::Ipv4Addr) -> Self {
375        Self::new_v4(value.octets(), 0)
376    }
377}
378
379impl From<core::net::Ipv6Addr> for SocketAddress {
380    fn from(value: core::net::Ipv6Addr) -> Self {
381        Self::new_v6(value.octets(), 0, 0, 0)
382    }
383}
384
385impl From<core::net::SocketAddrV4> for SocketAddress {
386    fn from(value: core::net::SocketAddrV4) -> Self {
387        Self::new_v4(value.ip().octets(), value.port())
388    }
389}
390
391impl From<core::net::SocketAddrV6> for SocketAddress {
392    fn from(value: core::net::SocketAddrV6) -> Self {
393        Self::new_v6(
394            value.ip().octets(),
395            value.port(),
396            value.flowinfo(),
397            value.scope_id(),
398        )
399    }
400}
401
402impl From<core::net::SocketAddr> for SocketAddress {
403    fn from(value: core::net::SocketAddr) -> Self {
404        match value {
405            core::net::SocketAddr::V4(v4) => v4.into(),
406            core::net::SocketAddr::V6(v6) => v6.into(),
407        }
408    }
409}
410
411impl From<core::net::IpAddr> for SocketAddress {
412    fn from(value: core::net::IpAddr) -> Self {
413        match value {
414            core::net::IpAddr::V4(v4) => v4.into(),
415            core::net::IpAddr::V6(v6) => v6.into(),
416        }
417    }
418}
419
420/// Open an anonymous file descriptor.
421pub fn twz_rt_fd_open_socket_bind(mut addr: SocketAddress, flags: u32) -> Result<RawFd> {
422    unsafe {
423        crate::bindings::twz_rt_fd_open_anon(
424            OpenAnonKind::SocketBind.into(),
425            flags,
426            ((&mut addr.0) as *mut crate::bindings::socket_address).cast(),
427            core::mem::size_of::<crate::bindings::socket_address>(),
428        )
429        .into()
430    }
431}
432
433/// Open an anonymous file descriptor.
434pub fn twz_rt_fd_open_socket_connect(mut addr: SocketAddress, flags: u32) -> Result<RawFd> {
435    unsafe {
436        crate::bindings::twz_rt_fd_open_anon(
437            OpenAnonKind::SocketConnect.into(),
438            flags,
439            ((&mut addr.0) as *mut crate::bindings::socket_address).cast(),
440            core::mem::size_of::<crate::bindings::socket_address>(),
441        )
442        .into()
443    }
444}
445
446/// Open an anonymous file descriptor.
447pub fn twz_rt_fd_open_pipe(flags: u32) -> Result<RawFd> {
448    unsafe {
449        crate::bindings::twz_rt_fd_open_anon(
450            OpenAnonKind::Pipe.into(),
451            flags,
452            core::ptr::null_mut(),
453            0,
454        )
455        .into()
456    }
457}
458
459/// Duplicate a file descriptor.
460pub fn twz_rt_fd_dup(fd: RawFd) -> Result<RawFd> {
461    let mut new_fd = core::mem::MaybeUninit::<RawFd>::uninit();
462    unsafe {
463        let e = crate::bindings::twz_rt_fd_cmd(
464            fd,
465            crate::bindings::FD_CMD_DUP,
466            core::ptr::null_mut(),
467            new_fd.as_mut_ptr().cast(),
468        );
469        let raw = RawTwzError::new(e);
470        if raw.is_success() {
471            Ok(new_fd.assume_init())
472        } else {
473            Err(raw.error())
474        }
475    }
476}
477
478/// Sync a file descriptor.
479pub fn twz_rt_fd_sync(fd: RawFd) {
480    unsafe {
481        crate::bindings::twz_rt_fd_cmd(
482            fd,
483            crate::bindings::FD_CMD_SYNC,
484            core::ptr::null_mut(),
485            core::ptr::null_mut(),
486        );
487    }
488}
489
490/// Truncate a file descriptor.
491pub fn twz_rt_fd_truncate(fd: RawFd, mut len: u64) -> Result<()> {
492    unsafe {
493        let e = crate::bindings::twz_rt_fd_cmd(
494            fd,
495            crate::bindings::FD_CMD_TRUNCATE,
496            (&mut len as *mut u64).cast(),
497            core::ptr::null_mut(),
498        );
499        let raw = RawTwzError::new(e);
500        if !raw.is_success() {
501            return Err(raw.error());
502        }
503    }
504    Ok(())
505}
506
507/// Close a file descriptor. If the fd is already closed, or invalid, this function has no effect.
508pub fn twz_rt_fd_close(fd: RawFd) {
509    unsafe { crate::bindings::twz_rt_fd_close(fd) }
510}