aranya_libc/
api.rs

1use alloc::sync::Arc;
2use core::{
3    ffi::{CStr, c_int},
4    marker::PhantomData,
5};
6
7use cfg_if::cfg_if;
8
9use super::{errno::Errno, path::Path};
10
11cfg_if! {
12    if #[cfg(target_os = "vxworks")] {
13        use super::sys::vxworks as imp;
14    } else if #[cfg(target_os = "linux")] {
15        use super::sys::linux as imp;
16    } else if #[cfg(target_os = "macos")] {
17        use super::sys::macos as imp;
18    } else {
19        compile_error!("unsupported OS");
20    }
21}
22
23pub use imp::{
24    LOCK_EX, LOCK_NB, O_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL, O_RDONLY, O_RDWR, S_IRGRP, S_IRUSR,
25    S_IWGRP, S_IWUSR, mode_t,
26};
27
28/// Allows borrowing the file descriptor.
29pub trait AsFd {
30    /// Borrows the file descriptor.
31    fn as_fd(&self) -> BorrowedFd<'_>;
32}
33
34impl<T> AsFd for &T
35where
36    T: AsFd + ?Sized,
37{
38    fn as_fd(&self) -> BorrowedFd<'_> {
39        T::as_fd(self)
40    }
41}
42
43impl<T> AsFd for Arc<T>
44where
45    T: AsFd + ?Sized,
46{
47    fn as_fd(&self) -> BorrowedFd<'_> {
48        (**self).as_fd()
49    }
50}
51
52/// An owned file descriptor.
53///
54/// It's closed on drop.
55#[derive(Debug, Eq, PartialEq)]
56#[repr(transparent)]
57#[clippy::has_significant_drop]
58pub struct OwnedFd {
59    pub(crate) fd: imp::RawFd,
60}
61
62impl AsFd for OwnedFd {
63    fn as_fd(&self) -> BorrowedFd<'_> {
64        BorrowedFd {
65            fd: self.fd,
66            _lifetime: PhantomData,
67        }
68    }
69}
70
71impl Drop for OwnedFd {
72    fn drop(&mut self) {
73        let _ = imp::close(self.fd);
74    }
75}
76
77/// An owned directory stream.
78///
79/// It's closed on drop.
80#[derive(Debug, Eq, PartialEq)]
81#[repr(transparent)]
82#[clippy::has_significant_drop]
83pub struct OwnedDir {
84    fd: imp::RawDir,
85}
86
87/// Information about an entry in a directory.
88///
89/// This is tied to the lifetime of the OwnedDir, and will be invalidated on the
90/// next call to `readdir`.
91#[derive(Debug, Eq, PartialEq)]
92pub struct DirEntry<'dir> {
93    entry: imp::DirEntry,
94    _phantom: PhantomData<&'dir OwnedDir>,
95}
96
97impl OwnedDir {
98    fn readdir(&mut self) -> Result<Option<DirEntry<'_>>, Errno> {
99        let entry = imp::readdir(self.fd)?;
100        Ok(entry.map(|entry| DirEntry {
101            entry,
102            _phantom: PhantomData,
103        }))
104    }
105
106    fn rewinddir(&mut self) {
107        imp::rewinddir(self.fd);
108    }
109}
110
111impl Drop for OwnedDir {
112    fn drop(&mut self) {
113        let _ = imp::closedir(self.fd);
114    }
115}
116
117impl<'dir> DirEntry<'dir> {
118    /// Returns the name for the current entry.
119    #[allow(clippy::cast_possible_wrap)]
120    pub fn name(&self) -> &'dir CStr {
121        // SAFETY: We're far inside of the bounds of both usize and isize
122        const OFFSET: isize = core::mem::offset_of!(libc::dirent, d_name) as isize;
123        // SAFETY: d_name is guaranteed to be null terminated.
124
125        unsafe { CStr::from_ptr((self.entry.byte_offset(OFFSET)).cast()) }
126    }
127}
128
129/// A borrowed file descriptor.
130#[derive(Copy, Clone, Debug, Eq, PartialEq)]
131#[repr(transparent)]
132pub struct BorrowedFd<'fd> {
133    pub(crate) fd: imp::RawFd,
134    _lifetime: PhantomData<&'fd OwnedFd>,
135}
136
137impl AsFd for BorrowedFd<'_> {
138    fn as_fd(&self) -> BorrowedFd<'_> {
139        *self
140    }
141}
142
143/// The `fd` argument to `openat(2)`, etc.
144pub trait AsAtRoot {
145    /// Returns the `fd` argument.
146    fn as_root(&self) -> imp::AtRoot<'_>;
147}
148
149/// See `open(2)`.
150pub fn open(path: impl AsRef<Path>, oflag: c_int, mode: mode_t) -> Result<OwnedFd, Errno> {
151    let fd = imp::open(path.as_ref(), oflag, mode)?;
152    Ok(OwnedFd { fd })
153}
154
155/// See `open(2)`.
156pub fn openat(
157    fd: impl AsAtRoot,
158    path: impl AsRef<Path>,
159    oflag: c_int,
160    mode: mode_t,
161) -> Result<OwnedFd, Errno> {
162    let fd = imp::openat(fd.as_root(), path.as_ref(), oflag, mode)?;
163    Ok(OwnedFd { fd })
164}
165
166/// See `flock(2)`.
167pub fn flock(fd: impl AsFd, op: c_int) -> Result<(), Errno> {
168    imp::flock(fd.as_fd(), op)
169}
170
171/// See `fallocate(2)`.
172pub fn fallocate(fd: impl AsFd, mode: c_int, off: i64, len: i64) -> Result<(), Errno> {
173    imp::fallocate(fd.as_fd(), mode, off, len)
174}
175
176/// See `read(2)`.
177pub fn pread(fd: impl AsFd, buf: &mut [u8], off: i64) -> Result<usize, Errno> {
178    imp::pread(fd.as_fd(), buf, off)
179}
180
181/// See `write(2)`.
182pub fn pwrite(fd: impl AsFd, buf: &[u8], off: i64) -> Result<usize, Errno> {
183    imp::pwrite(fd.as_fd(), buf, off)
184}
185
186/// See `fsync(2)`.
187pub fn fsync(fd: impl AsFd) -> Result<(), Errno> {
188    imp::fsync(fd.as_fd())
189}
190
191/// See `unlinkat(2)`.
192pub fn unlinkat(fd: impl AsAtRoot, path: impl AsRef<Path>, flags: c_int) -> Result<(), Errno> {
193    imp::unlinkat(fd.as_root(), path.as_ref(), flags)
194}
195
196/// See `dup(2)`.
197pub fn dup(fd: impl AsFd) -> Result<OwnedFd, Errno> {
198    Ok(OwnedFd {
199        fd: imp::dup(fd.as_fd())?,
200    })
201}
202
203/// See `fdopendir(3p)`.
204pub fn fdopendir(fd: OwnedFd) -> Result<OwnedDir, Errno> {
205    let fd = imp::fdopendir(fd)?;
206    Ok(OwnedDir { fd })
207}
208
209/// See `readdir(3p)`.
210pub fn readdir(dir: &mut OwnedDir) -> Result<Option<DirEntry<'_>>, Errno> {
211    dir.readdir()
212}
213
214/// See `rewinddir(3p)`.
215pub fn rewinddir(dir: &mut OwnedDir) {
216    dir.rewinddir();
217}