lx/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Provides constants and types derived from Linux without requiring libc.
5
6#![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// xtask-fmt allow-target-arch sys-crate
138#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
139pub const O_DIRECTORY: i32 = 0x010000;
140// xtask-fmt allow-target-arch sys-crate
141#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
142pub const O_NOFOLLOW: i32 = 0x020000;
143
144// xtask-fmt allow-target-arch sys-crate
145#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
146pub const O_DIRECTORY: i32 = 0x004000;
147// xtask-fmt allow-target-arch sys-crate
148#[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/// Wraps a Linux error code in a strongly-typed struct.
161#[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    // Map IO errors to the appropriate Linux error code.
167    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    /// Creates an `Error` from the last operating system error.
191    #[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    /// Creates an `Error` from an existing Linux error code.
197    pub fn from_lx(error: i32) -> Self {
198        Self(error)
199    }
200
201    /// Returns the error code value.
202    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
213/// A specialized `Result` type for operations that return Linux error codes.
214pub 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/// A Linux `timespec` structure.
220///
221/// This is similar to `Duration` but matches the memory layout of `timespec`.
222#[repr(C)]
223#[derive(Debug, Eq, PartialEq)]
224pub struct Timespec {
225    pub seconds: usize,
226    pub nanoseconds: usize,
227}
228
229impl Timespec {
230    /// Creates a `Timespec` with the value UTIME_OMIT.
231    pub fn omit() -> Self {
232        Self {
233            seconds: 0,
234            nanoseconds: UTIME_OMIT,
235        }
236    }
237
238    /// Creates a `Timespec` with the value UTIME_NOW.
239    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/// A Linux `stat` structure.
257#[cfg(target_arch = "x86_64")] // xtask-fmt allow-target-arch sys-crate
258#[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/// A Linux `stat` structure.
279#[cfg(target_arch = "aarch64")] // xtask-fmt allow-target-arch sys-crate
280#[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/// A directory entry returned by `LxFile::read_dir`.
319#[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}