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