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
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// xtask-fmt allow-target-arch sys-crate
139#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
140pub const O_DIRECTORY: i32 = 0x010000;
141// xtask-fmt allow-target-arch sys-crate
142#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
143pub const O_NOFOLLOW: i32 = 0x020000;
144
145// xtask-fmt allow-target-arch sys-crate
146#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
147pub const O_DIRECTORY: i32 = 0x004000;
148// xtask-fmt allow-target-arch sys-crate
149#[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/// Wraps a Linux error code in a strongly-typed struct.
162#[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    // Map IO errors to the appropriate Linux error code.
168    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    /// Creates an `Error` from the last operating system error.
192    #[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    /// Creates an `Error` from an existing Linux error code.
198    pub fn from_lx(error: i32) -> Self {
199        Self(error)
200    }
201
202    /// Returns the error code value.
203    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
214/// A specialized `Result` type for operations that return Linux error codes.
215pub 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/// A Linux `timespec` structure.
221///
222/// This is similar to `Duration` but matches the memory layout of `timespec`.
223#[repr(C)]
224#[derive(Debug, Eq, PartialEq)]
225pub struct Timespec {
226    pub seconds: usize,
227    pub nanoseconds: usize,
228}
229
230impl Timespec {
231    /// Creates a `Timespec` with the value UTIME_OMIT.
232    pub fn omit() -> Self {
233        Self {
234            seconds: 0,
235            nanoseconds: UTIME_OMIT,
236        }
237    }
238
239    /// Creates a `Timespec` with the value UTIME_NOW.
240    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/// A Linux `stat` structure.
258#[cfg(target_arch = "x86_64")] // xtask-fmt allow-target-arch sys-crate
259#[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/// A Linux `stat` structure.
280#[cfg(target_arch = "aarch64")] // xtask-fmt allow-target-arch sys-crate
281#[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/// A directory entry returned by `LxFile::read_dir`.
320#[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}