readdir_sys/
lib.rs

1/*
2 * Description: Codify OS resources into traits and configure based on platform.
3 * This code is largely copied from the rust compiler's sys/fs/unix.rs: https://github.com/rust-lang/rust/blob/cbfdf0b014cb04982a9cbeec1578001001167f6e/library/std/src/sys/fs/unix.rs.
4 * The rust compiler is distributed under both the MIT and Apache v2 licenses.
5 *
6 * Copyright (C) 2025 d@nny mc² <dmc2@hypnicjerk.ai>
7 * SPDX-License-Identifier: LGPL-3.0-or-later
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published
11 * by the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#![allow(non_camel_case_types)]
24#![cfg(unix)]
25
26//! Description: Codify OS resources into traits and configure based on platform.
27//!
28//! This code is largely copied from the rust compiler's [`sys/fs/unix.rs`](https://github.com/rust-lang/rust/blob/cbfdf0b014cb04982a9cbeec1578001001167f6e/library/std/src/sys/fs/unix.rs).
29
30use measure_null_str::{MeasuredNullTermStr, NullTermStr};
31
32
33pub trait HasInode {
34  fn ino(&self) -> libc::ino_t;
35}
36pub trait HasEagerType {
37  fn eager_file_type(&self) -> libc::c_uchar;
38}
39pub trait HasNoEagerType {}
40pub trait HasNameLen {
41  fn name_len(&self) -> usize;
42}
43pub trait MeasureNameLen {
44  fn measure_name_len<'s>(&self, s: NullTermStr<'s>) -> &'s MeasuredNullTermStr { s.measure() }
45}
46impl<M> MeasureNameLen for M
47where M: HasNameLen
48{
49  fn measure_name_len<'s>(&self, s: NullTermStr<'s>) -> &'s MeasuredNullTermStr {
50    let n = self.name_len();
51    unsafe { MeasuredNullTermStr::given_measurement(s, n) }
52  }
53}
54
55macro_rules! rename_imports {
56  (use64) => {
57    pub use libc::{dirent64, fstatat64, ino64_t, openat64, readdir64, stat64};
58  };
59  (rename64) => {
60    pub use libc::{
61      dirent as dirent64, fstatat as fstatat64, ino_t as ino64_t, openat as openat64,
62      readdir as readdir64, stat as stat64,
63    };
64  };
65}
66macro_rules! generate_dirent {
67    () => {
68      #[derive(Copy, Clone, Debug)]
69      pub struct dirent64_min {}
70      impl dirent64_min {
71        pub unsafe const fn new(_entry_ptr: *const dirent64) -> Self { Self {} }
72      }
73    };
74    (d_ino) => {
75      #[derive(Copy, Clone, Debug)]
76      pub struct dirent64_min {
77        pub d_ino: libc::ino64_t,
78      }
79      impl dirent64_min {
80        pub unsafe fn new(entry_ptr: *const dirent64) -> Self {
81          unsafe {
82            Self {
83              // When loading from a field, we can skip the `&raw const`; `(*entry_ptr).d_ino` as
84              // a value expression will do the right thing: `byte_offset` to the field and then
85              // only access those bytes.
86              d_ino: (*entry_ptr).ino(),
87            }
88          }
89        }
90      }
91      impl HasInode for dirent64_min {
92        fn ino(&self) -> libc::ino64_t { self.d_ino }
93      }
94    };
95    (d_ino, d_type) => {
96      #[derive(Copy, Clone, Debug)]
97      pub struct dirent64_min {
98        pub d_ino: libc::ino64_t,
99        pub d_type: libc::c_uchar,
100      }
101      impl dirent64_min {
102        pub unsafe fn new(entry_ptr: *const dirent64) -> Self {
103          unsafe {
104            Self {
105              d_ino: (*entry_ptr).ino(),
106              d_type: (*entry_ptr).eager_file_type(),
107            }
108          }
109        }
110      }
111      impl HasInode for dirent64_min {
112        fn ino(&self) -> libc::ino64_t { self.d_ino }
113      }
114      impl HasEagerType for dirent64_min {
115        fn eager_file_type(&self) -> libc::c_uchar { self.d_type }
116      }
117    };
118    (d_ino, d_type, d_namlen) => {
119      #[derive(Copy, Clone, Debug)]
120      pub struct dirent64_min {
121        pub d_ino: libc::ino64_t,
122        pub d_type: libc::c_uchar,
123        pub d_namlen: libc::c_uchar,
124      }
125      impl dirent64_min {
126        pub unsafe fn new(entry_ptr: *const dirent64) -> Self {
127          unsafe {
128            Self {
129              d_ino: (*entry_ptr).ino(),
130              d_type: (*entry_ptr).eager_file_type(),
131              d_namlen: (*entry_ptr).name_len(),
132            }
133          }
134        }
135      }
136      impl HasInode for dirent64_min {
137        fn ino(&self) -> libc::ino64_t { self.d_ino }
138      }
139      impl HasEagerType for dirent64_min {
140        fn eager_file_type(&self) -> libc::c_uchar { self.d_type }
141      }
142      impl HasNameLen for dirent64_min {
143        fn name_len(&self) -> usize { self.d_namlen }
144      }
145    };
146  }
147macro_rules! generate_imports {
148  (no_r, $use64:tt) => {
149    generate_dirent!();
150    rename_imports!($use64);
151  };
152  (no_r,d_ino, $use64:tt) => {
153    generate_dirent!(d_ino);
154    rename_imports!($use64);
155  };
156  (no_r,d_ino,d_type, $use64:tt) => {
157    generate_dirent!(d_ino, d_type);
158    rename_imports!($use64);
159  };
160  (no_r,d_ino,d_type,d_namlen, $use64:tt) => {
161    generate_dirent!(d_ino, d_type, d_namlen);
162    rename_imports!($use64);
163  };
164}
165
166cfg_if::cfg_if! {
167  if #[cfg(target_os = "android")] {
168    generate_imports!(no_r, d_ino, d_type, rename64);
169  } else if #[cfg(target_os = "vita")] {
170    generate_imports!(no_r, rename64);
171  } else if #[cfg(any(
172    target_os = "solaris",
173    target_os = "illumos",
174    target_os = "aix",
175    target_os = "nto",
176  ))] {
177    generate_imports!(no_r, d_ino, rename64);
178  } else if #[cfg(any(target_os = "fuchsia", target_os = "redox"))] {
179    generate_imports!(no_r, d_ino, d_type, rename64);
180  } else if #[cfg(target_os = "hurd")] {
181    generate_imports!(no_r, d_ino, d_type, d_namlen, use64);
182  } else if #[cfg(target_os = "linux")] {
183    cfg_if::cfg_if! {
184      if #[cfg(target_env = "musl")] {
185        generate_imports!(no_r, d_ino, d_type, rename64);
186      } else {
187        generate_imports!(no_r, d_ino, d_type, use64);
188      }
189    }
190  } else if #[cfg(target_os = "l4re")] {
191    generate_imports!(no_r, d_ino, d_type, use64);
192  } else if #[cfg(target_os = "nuttx")] {
193    generate_imports!(no_r, rename64);
194  } else if #[cfg(any(target_os = "haiku", target_os = "vxworks"))] {
195    generate_imports!(no_r, d_ino, rename64);
196  } else if #[cfg(any(
197    target_os = "netbsd",
198    target_os = "openbsd",
199    target_os = "freebsd",
200    target_os = "dragonfly",
201    target_vendor = "apple",
202  ))] {
203    generate_imports!(no_r, d_ino, d_type, d_namlen, rename64);
204  } else {
205    generate_imports!(no_r, d_ino, d_type, rename64);
206  }
207}
208
209cfg_if::cfg_if! {
210  if #[cfg(any(
211    target_os = "freebsd",
212    target_os = "openbsd",
213    target_os = "netbsd",
214    target_os = "dragonfly"
215  ))] {
216    impl HasInode for dirent64 {
217      fn ino(&self) -> libc::ino64_t { self.d_fileno }
218    }
219  } else if #[cfg(any(target_os = "vita", target_os = "nuttx"))] {
220  } else {
221    impl HasInode for dirent64 {
222      fn ino(&self) -> libc::ino64_t { self.d_ino }
223    }
224  }
225}
226
227cfg_if::cfg_if! {
228  if #[cfg(any(
229      target_os = "solaris",
230      target_os = "illumos",
231      target_os = "haiku",
232      target_os = "vxworks",
233      target_os = "aix",
234      target_os = "nto",
235      target_os = "vita",
236  ))] {
237    impl HasNoEagerType for dirent64 {}
238  } else {
239    impl HasEagerType for dirent64 {
240      fn eager_file_type(&self) -> libc::c_uchar { self.d_type }
241    }
242  }
243}
244
245cfg_if::cfg_if! {
246  if #[cfg(any(
247    target_os = "hurd",
248    target_os = "netbsd",
249    target_os = "openbsd",
250    target_os = "freebsd",
251    target_os = "dragonfly",
252    target_vendor = "apple",
253  ))] {
254    impl HasNameLen for dirent64 {
255      fn name_len(&self) -> usize { self.d_namlen }
256    }
257  } else {
258    impl MeasureNameLen for dirent64 {}
259    impl MeasureNameLen for dirent64_min {}
260  }
261}