1#![expect(missing_docs)]
7#[macro_use]
8mod macros;
9mod string;
10
11use std::io;
12use thiserror::Error;
13
14pub use string::LxStr;
15pub use string::LxString;
16
17#[allow(non_camel_case_types)]
18pub type uid_t = u32;
19#[allow(non_camel_case_types)]
20pub type gid_t = u32;
21#[allow(non_camel_case_types)]
22pub type mode_t = u32;
23#[allow(non_camel_case_types)]
24pub type ino_t = u64;
25#[allow(non_camel_case_types)]
26pub type off_t = i64;
27#[allow(non_camel_case_types)]
28pub type dev_t = usize;
29
30pub const MODE_INVALID: mode_t = mode_t::MAX;
31pub const MODE_VALID_BITS: mode_t = S_IFMT | 0o7777;
32pub const UID_INVALID: uid_t = uid_t::MAX;
33pub const GID_INVALID: gid_t = gid_t::MAX;
34
35pub const S_IFIFO: u32 = 0x1000;
36pub const S_IFCHR: u32 = 0x2000;
37pub const S_IFDIR: u32 = 0x4000;
38pub const S_IFBLK: u32 = 0x6000;
39pub const S_IFREG: u32 = 0x8000;
40pub const S_IFLNK: u32 = 0xa000;
41pub const S_IFSOCK: u32 = 0xc000;
42pub const S_IFMT: u32 = 0xf000;
43pub const S_ISUID: u32 = 0o4000;
44pub const S_ISGID: u32 = 0o2000;
45pub const S_IXGRP: u32 = 0o010;
46
47pub const DT_UNK: u8 = 0;
48pub const DT_FIFO: u8 = 1;
49pub const DT_CHR: u8 = 2;
50pub const DT_DIR: u8 = 4;
51pub const DT_BLK: u8 = 6;
52pub const DT_REG: u8 = 8;
53pub const DT_LNK: u8 = 10;
54pub const DT_SOCK: u8 = 12;
55pub const DT_WHT: u8 = 14;
56
57lx_errors! {
58 EPERM = 1;
59 ENOENT = 2;
60 ESRCH = 3;
61 EINTR = 4;
62 EIO = 5;
63 ENXIO = 6;
64 E2BIG = 7;
65 ENOEXEC = 8;
66 EBADF = 9;
67 ECHILD = 10;
68 EAGAIN = 11;
69 ENOMEM = 12;
70 EACCES = 13;
71 EFAULT = 14;
72 EBUSY = 16;
73 EEXIST = 17;
74 EXDEV = 18;
75 ENODEV = 19;
76 ENOTDIR = 20;
77 EISDIR = 21;
78 EINVAL = 22;
79 ENFILE = 23;
80 EMFILE = 24;
81 ENOTTY = 25;
82 EFBIG = 27;
83 ENOSPC = 28;
84 ESPIPE = 29;
85 EROFS = 30;
86 EMLINK = 31;
87 EPIPE = 32;
88 ERANGE = 34;
89 EDEADLK = 35;
90 ENAMETOOLONG = 36;
91 ENOLCK = 37;
92 ENOSYS = 38;
93 ENOTEMPTY = 39;
94 ELOOP = 40;
95 EIDRM = 43;
96 ENODATA = 61;
97 EPROTO = 71;
98 EOVERFLOW = 75;
99 EUSERS = 87;
100 ENOTSOCK = 88;
101 EDESTADDRREQ = 89;
102 EMSGSIZE = 90;
103 EPROTOTYPE = 91;
104 ENOPROTOOPT = 92;
105 EPROTONOSUPPORT = 93;
106 ESOCKTNOSUPPORT = 94;
107 ENOTSUP = 95;
108 EAFNOSUPPORT = 97;
109 EADDRINUSE = 98;
110 EADDRNOTAVAIL = 99;
111 ENETUNREACH = 101;
112 ECONNABORTED = 103;
113 ECONNRESET = 104;
114 ENOBUFS = 105;
115 EISCONN = 106;
116 ENOTCONN = 107;
117 ETIMEDOUT = 110;
118 ECONNREFUSED = 111;
119 EHOSTDOWN = 112;
120 EHOSTUNREACH = 113;
121 EALREADY = 114;
122 EINPROGRESS = 115;
123 ENOMEDIUM = 123;
124 EMEDIUMTYPE = 124;
125 ENOKEY = 126;
126}
127
128pub const O_RDONLY: i32 = 0x000000;
129pub const O_WRONLY: i32 = 0x000001;
130pub const O_RDWR: i32 = 0x000002;
131pub const O_NOACCESS: i32 = 0x000003;
132pub const O_CREAT: i32 = 0x000040;
133pub const O_EXCL: i32 = 0x000080;
134pub const O_TRUNC: i32 = 0x000200;
135pub const O_APPEND: i32 = 0x000400;
136
137#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
139pub const O_DIRECTORY: i32 = 0x010000;
140#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
142pub const O_NOFOLLOW: i32 = 0x020000;
143
144#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
146pub const O_DIRECTORY: i32 = 0x004000;
147#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
149pub const O_NOFOLLOW: i32 = 0x008000;
150
151pub const O_NOATIME: i32 = 0x040000;
152
153pub const O_ACCESS_MASK: i32 = 0x000003;
154
155pub const AT_REMOVEDIR: i32 = 0x200;
156
157pub const XATTR_CREATE: i32 = 0x1;
158pub const XATTR_REPLACE: i32 = 0x2;
159
160#[derive(Copy, Clone, Error, Eq, PartialEq)]
162#[error("{err} ({0})", err = str_error(*.0))]
163pub struct Error(i32);
164
165impl From<io::Error> for Error {
166 fn from(error: io::Error) -> Self {
168 let e = match error.kind() {
169 io::ErrorKind::NotFound => ENOENT,
170 io::ErrorKind::PermissionDenied => EACCES,
171 io::ErrorKind::ConnectionRefused => ECONNREFUSED,
172 io::ErrorKind::ConnectionReset => ECONNRESET,
173 io::ErrorKind::ConnectionAborted => ECONNABORTED,
174 io::ErrorKind::NotConnected => ENOTCONN,
175 io::ErrorKind::AddrInUse => EADDRINUSE,
176 io::ErrorKind::AddrNotAvailable => EADDRNOTAVAIL,
177 io::ErrorKind::BrokenPipe => EPIPE,
178 io::ErrorKind::AlreadyExists => EEXIST,
179 io::ErrorKind::WouldBlock => EAGAIN,
180 io::ErrorKind::TimedOut => ETIMEDOUT,
181 io::ErrorKind::Interrupted => EINTR,
182 _ => EINVAL,
183 };
184
185 Error(e)
186 }
187}
188
189impl Error {
190 #[cfg(target_os = "linux")]
192 pub fn last_os_error() -> Self {
193 Self(io::Error::last_os_error().raw_os_error().unwrap())
194 }
195
196 pub fn from_lx(error: i32) -> Self {
198 Self(error)
199 }
200
201 pub fn value(&self) -> i32 {
203 self.0
204 }
205}
206
207impl std::fmt::Debug for Error {
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 f.write_fmt(format_args!("Error({} ({}))", str_error(self.0), self.0))
210 }
211}
212
213pub type Result<T> = std::result::Result<T, Error>;
215
216const UTIME_NOW: usize = (1 << 30) - 1;
217const UTIME_OMIT: usize = (1 << 30) - 2;
218
219#[repr(C)]
223#[derive(Debug, Eq, PartialEq)]
224pub struct Timespec {
225 pub seconds: usize,
226 pub nanoseconds: usize,
227}
228
229impl Timespec {
230 pub fn omit() -> Self {
232 Self {
233 seconds: 0,
234 nanoseconds: UTIME_OMIT,
235 }
236 }
237
238 pub fn now() -> Self {
240 Self {
241 seconds: 0,
242 nanoseconds: UTIME_NOW,
243 }
244 }
245}
246
247impl From<&std::time::Duration> for Timespec {
248 fn from(time: &std::time::Duration) -> Self {
249 Self {
250 seconds: time.as_secs() as usize,
251 nanoseconds: time.subsec_nanos() as usize,
252 }
253 }
254}
255
256#[cfg(target_arch = "x86_64")] #[repr(C)]
259#[derive(Debug, Eq, PartialEq)]
260pub struct Stat {
261 pub device_nr: u64,
262 pub inode_nr: ino_t,
263 pub link_count: usize,
264 pub mode: mode_t,
265 pub uid: uid_t,
266 pub gid: gid_t,
267 pub pad0: u32,
268 pub device_nr_special: u64,
269 pub file_size: u64,
270 pub block_size: isize,
271 pub block_count: u64,
272 pub access_time: Timespec,
273 pub write_time: Timespec,
274 pub change_time: Timespec,
275 pub pad1: [isize; 3],
276}
277
278#[cfg(target_arch = "aarch64")] #[repr(C)]
281#[derive(Debug, Eq, PartialEq)]
282pub struct Stat {
283 pub device_nr: u64,
284 pub inode_nr: ino_t,
285 pub mode: mode_t,
286 pub link_count: u32,
287 pub uid: uid_t,
288 pub gid: gid_t,
289 pub device_nr_special: u64,
290 pub pad0: u32,
291 pub file_size: u64,
292 pub block_size: u32,
293 pub pad2: u32,
294 pub block_count: u64,
295 pub access_time: Timespec,
296 pub write_time: Timespec,
297 pub change_time: Timespec,
298 pub unused: [u32; 2],
299}
300
301#[repr(C)]
302#[derive(Debug, Eq, PartialEq)]
303pub struct StatFs {
304 pub fs_type: usize,
305 pub block_size: usize,
306 pub block_count: u64,
307 pub free_block_count: u64,
308 pub available_block_count: u64,
309 pub file_count: u64,
310 pub available_file_count: u64,
311 pub file_system_id: [u8; 8],
312 pub maximum_file_name_length: usize,
313 pub file_record_size: usize,
314 pub flags: usize,
315 pub spare: [usize; 4],
316}
317
318#[derive(Debug)]
320pub struct DirEntry {
321 pub name: LxString,
322 pub inode_nr: ino_t,
323 pub offset: off_t,
324 pub file_type: u8,
325}
326
327pub fn s_isreg(mode: mode_t) -> bool {
328 mode & S_IFMT == S_IFREG
329}
330
331pub fn s_isdir(mode: mode_t) -> bool {
332 mode & S_IFMT == S_IFDIR
333}
334
335pub fn s_ischr(mode: mode_t) -> bool {
336 mode & S_IFMT == S_IFCHR
337}
338
339pub fn s_isblk(mode: mode_t) -> bool {
340 mode & S_IFMT == S_IFBLK
341}
342
343pub fn s_isfifo(mode: mode_t) -> bool {
344 mode & S_IFMT == S_IFIFO
345}
346
347pub fn s_issock(mode: mode_t) -> bool {
348 mode & S_IFMT == S_IFSOCK
349}
350
351pub fn s_islnk(mode: mode_t) -> bool {
352 mode & S_IFMT == S_IFLNK
353}
354
355pub fn major32(dev: dev_t) -> u32 {
356 ((dev & 0xfff00) >> 8) as u32
357}
358
359pub fn make_major32(major: u32) -> dev_t {
360 ((major as dev_t) & 0xfff) << 8
361}
362
363pub fn major64(dev: dev_t) -> u32 {
364 (((dev >> 32) & 0xfffff000) | major32(dev) as dev_t) as u32
365}
366
367pub fn make_major64(major: u32) -> dev_t {
368 (((major as dev_t) & !0xfff) << 32) | make_major32(major)
369}
370
371pub fn minor(dev: dev_t) -> u32 {
372 (((dev >> 12) & 0xffffff00) | (dev & 0xff)) as u32
373}
374
375pub fn make_minor(minor: u32) -> dev_t {
376 ((minor as dev_t & 0xffffff00) << 12) | (minor as dev_t & 0xff)
377}
378
379pub fn make_dev(major: u32, minor: u32) -> dev_t {
380 make_major64(major) | make_minor(minor)
381}