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
28pub trait AsFd {
30 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#[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#[derive(Debug, Eq, PartialEq)]
81#[repr(transparent)]
82#[clippy::has_significant_drop]
83pub struct OwnedDir {
84 fd: imp::RawDir,
85}
86
87#[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 #[allow(clippy::cast_possible_wrap)]
120 pub fn name(&self) -> &'dir CStr {
121 const OFFSET: isize = core::mem::offset_of!(libc::dirent, d_name) as isize;
123 unsafe { CStr::from_ptr((self.entry.byte_offset(OFFSET)).cast()) }
126 }
127}
128
129#[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
143pub trait AsAtRoot {
145 fn as_root(&self) -> imp::AtRoot<'_>;
147}
148
149pub 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
155pub 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
166pub fn flock(fd: impl AsFd, op: c_int) -> Result<(), Errno> {
168 imp::flock(fd.as_fd(), op)
169}
170
171pub 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
176pub fn pread(fd: impl AsFd, buf: &mut [u8], off: i64) -> Result<usize, Errno> {
178 imp::pread(fd.as_fd(), buf, off)
179}
180
181pub fn pwrite(fd: impl AsFd, buf: &[u8], off: i64) -> Result<usize, Errno> {
183 imp::pwrite(fd.as_fd(), buf, off)
184}
185
186pub fn fsync(fd: impl AsFd) -> Result<(), Errno> {
188 imp::fsync(fd.as_fd())
189}
190
191pub 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
196pub fn dup(fd: impl AsFd) -> Result<OwnedFd, Errno> {
198 Ok(OwnedFd {
199 fd: imp::dup(fd.as_fd())?,
200 })
201}
202
203pub fn fdopendir(fd: OwnedFd) -> Result<OwnedDir, Errno> {
205 let fd = imp::fdopendir(fd)?;
206 Ok(OwnedDir { fd })
207}
208
209pub fn readdir(dir: &mut OwnedDir) -> Result<Option<DirEntry<'_>>, Errno> {
211 dir.readdir()
212}
213
214pub fn rewinddir(dir: &mut OwnedDir) {
216 dir.rewinddir();
217}