Coverage Report

Created: 2025-10-29 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/lib.rs
Line
Count
Source
1
//! Rust friendly bindings to the various *nix system functions.
2
//!
3
//! Modules are structured according to the C header file that they would be
4
//! defined in.
5
//!
6
//! # Features
7
//!
8
//! Nix uses the following Cargo features to enable optional functionality.
9
//! They may be enabled in any combination.
10
//! * `acct` - Process accounting
11
//! * `aio` - POSIX AIO
12
//! * `dir` - Stuff relating to directory iteration
13
//! * `env` - Manipulate environment variables
14
//! * `event` - Event-driven APIs, like `kqueue` and `epoll`
15
//! * `fanotify` - Linux's `fanotify` filesystem events monitoring API
16
//! * `feature` - Query characteristics of the OS at runtime
17
//! * `fs` - File system functionality
18
//! * `hostname` - Get and set the system's hostname
19
//! * `inotify` - Linux's `inotify` file system notification API
20
//! * `ioctl` - The `ioctl` syscall, and wrappers for many specific instances
21
//! * `kmod` - Load and unload kernel modules
22
//! * `mman` - Stuff relating to memory management
23
//! * `mount` - Mount and unmount file systems
24
//! * `mqueue` - POSIX message queues
25
//! * `net` - Networking-related functionality
26
//! * `personality` - Set the process execution domain
27
//! * `poll` - APIs like `poll` and `select`
28
//! * `process` - Stuff relating to running processes
29
//! * `pthread` - POSIX threads
30
//! * `ptrace` - Process tracing and debugging
31
//! * `quota` - File system quotas
32
//! * `reboot` - Reboot the system
33
//! * `resource` - Process resource limits
34
//! * `sched` - Manipulate process's scheduling
35
//! * `socket` - Sockets, whether for networking or local use
36
//! * `signal` - Send and receive signals to processes
37
//! * `syslog` - System logging
38
//! * `term` - Terminal control APIs
39
//! * `time` - Query the operating system's clocks
40
//! * `ucontext` - User thread context
41
//! * `uio` - Vectored I/O
42
//! * `user` - Stuff relating to users and groups
43
//! * `zerocopy` - APIs like `sendfile` and `copy_file_range`
44
#![crate_name = "nix"]
45
#![cfg(unix)]
46
#![allow(non_camel_case_types)]
47
// A clear document is a good document no matter if it has a summary in its
48
// first paragraph or not.
49
#![allow(clippy::too_long_first_doc_paragraph)]
50
#![recursion_limit = "500"]
51
#![deny(unused)]
52
#![deny(unexpected_cfgs)]
53
#![allow(unused_macros)]
54
#![cfg_attr(
55
    not(all(
56
        feature = "acct",
57
        feature = "aio",
58
        feature = "dir",
59
        feature = "env",
60
        feature = "event",
61
        feature = "fanotify",
62
        feature = "feature",
63
        feature = "fs",
64
        feature = "hostname",
65
        feature = "inotify",
66
        feature = "ioctl",
67
        feature = "kmod",
68
        feature = "mman",
69
        feature = "mount",
70
        feature = "mqueue",
71
        feature = "net",
72
        feature = "personality",
73
        feature = "poll",
74
        feature = "process",
75
        feature = "pthread",
76
        feature = "ptrace",
77
        feature = "quota",
78
        feature = "reboot",
79
        feature = "resource",
80
        feature = "sched",
81
        feature = "socket",
82
        feature = "signal",
83
        feature = "syslog",
84
        feature = "term",
85
        feature = "time",
86
        feature = "ucontext",
87
        feature = "uio",
88
        feature = "user",
89
        feature = "zerocopy",
90
    )),
91
    allow(unused_imports)
92
)]
93
#![deny(unstable_features)]
94
#![deny(missing_copy_implementations)]
95
#![deny(missing_debug_implementations)]
96
#![warn(missing_docs)]
97
#![cfg_attr(docsrs, feature(doc_cfg))]
98
#![deny(clippy::cast_ptr_alignment)]
99
#![deny(unsafe_op_in_unsafe_fn)]
100
// I found the change suggested by this rules could hurt code readability. I cannot
101
// remeber every type's default value, in such cases, it forces me to open
102
// the std doc to insepct the Default value, which is unnecessary with
103
// `.unwrap_or(value)`.
104
#![allow(clippy::unwrap_or_default)]
105
106
// Re-exported external crates
107
pub use libc;
108
109
// Private internal modules
110
#[macro_use]
111
mod macros;
112
113
// Public crates
114
#[cfg(not(target_os = "redox"))]
115
feature! {
116
    #![feature = "dir"]
117
    pub mod dir;
118
}
119
feature! {
120
    #![feature = "env"]
121
    pub mod env;
122
}
123
#[allow(missing_docs)]
124
pub mod errno;
125
feature! {
126
    #![feature = "feature"]
127
128
    #[deny(missing_docs)]
129
    pub mod features;
130
}
131
pub mod fcntl;
132
feature! {
133
    #![feature = "net"]
134
135
    #[cfg(any(linux_android,
136
              bsd,
137
              solarish))]
138
    #[deny(missing_docs)]
139
    pub mod ifaddrs;
140
    #[cfg(not(target_os = "redox"))]
141
    #[deny(missing_docs)]
142
    pub mod net;
143
}
144
#[cfg(linux_android)]
145
feature! {
146
    #![feature = "kmod"]
147
    pub mod kmod;
148
}
149
feature! {
150
    #![feature = "mount"]
151
    pub mod mount;
152
}
153
#[cfg(any(
154
    freebsdlike,
155
    all(target_os = "linux", not(target_env = "ohos")),
156
    target_os = "netbsd"
157
))]
158
feature! {
159
    #![feature = "mqueue"]
160
    pub mod mqueue;
161
}
162
feature! {
163
    #![feature = "poll"]
164
    pub mod poll;
165
}
166
#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
167
feature! {
168
    #![feature = "term"]
169
    #[deny(missing_docs)]
170
    pub mod pty;
171
}
172
feature! {
173
    #![feature = "sched"]
174
    pub mod sched;
175
}
176
pub mod sys;
177
feature! {
178
    #![feature = "time"]
179
    pub mod time;
180
}
181
// This can be implemented for other platforms as soon as libc
182
// provides bindings for them.
183
#[cfg(all(
184
    target_os = "linux",
185
    any(
186
        target_arch = "aarch64",
187
        target_arch = "s390x",
188
        target_arch = "x86",
189
        target_arch = "x86_64"
190
    )
191
))]
192
feature! {
193
    #![feature = "ucontext"]
194
    #[allow(missing_docs)]
195
    pub mod ucontext;
196
}
197
pub mod unistd;
198
199
#[cfg(any(feature = "poll", feature = "event"))]
200
mod poll_timeout;
201
202
#[cfg(any(
203
    target_os = "freebsd",
204
    target_os = "haiku",
205
    target_os = "linux",
206
    target_os = "netbsd",
207
    apple_targets
208
))]
209
feature! {
210
    #![feature = "process"]
211
    pub mod spawn;
212
}
213
214
feature! {
215
    #![feature = "syslog"]
216
    pub mod syslog;
217
}
218
219
use std::ffi::{CStr, CString, OsStr};
220
use std::mem::MaybeUninit;
221
use std::os::unix::ffi::OsStrExt;
222
use std::path::{Path, PathBuf};
223
use std::{ptr, result, slice};
224
225
use errno::Errno;
226
227
/// Nix Result Type
228
pub type Result<T> = result::Result<T, Errno>;
229
230
/// Nix's main error type.
231
///
232
/// It's a wrapper around Errno.  As such, it's very interoperable with
233
/// [`std::io::Error`], but it has the advantages of:
234
/// * `Clone`
235
/// * `Copy`
236
/// * `Eq`
237
/// * Small size
238
/// * Represents all of the system's errnos, instead of just the most common
239
///   ones.
240
pub type Error = Errno;
241
242
/// Common trait used to represent file system paths by many Nix functions.
243
pub trait NixPath {
244
    /// Is the path empty?
245
    fn is_empty(&self) -> bool;
246
247
    /// Length of the path in bytes
248
    fn len(&self) -> usize;
249
250
    /// Execute a function with this path as a `CStr`.
251
    ///
252
    /// Mostly used internally by Nix.
253
    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
254
    where
255
        F: FnOnce(&CStr) -> T;
256
}
257
258
impl NixPath for str {
259
0
    fn is_empty(&self) -> bool {
260
0
        NixPath::is_empty(OsStr::new(self))
261
0
    }
262
263
0
    fn len(&self) -> usize {
264
0
        NixPath::len(OsStr::new(self))
265
0
    }
266
267
0
    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
268
0
    where
269
0
        F: FnOnce(&CStr) -> T,
270
    {
271
0
        OsStr::new(self).with_nix_path(f)
272
0
    }
273
}
274
275
impl NixPath for OsStr {
276
0
    fn is_empty(&self) -> bool {
277
0
        self.as_bytes().is_empty()
278
0
    }
279
280
0
    fn len(&self) -> usize {
281
0
        self.as_bytes().len()
282
0
    }
283
284
0
    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
285
0
    where
286
0
        F: FnOnce(&CStr) -> T,
287
    {
288
0
        self.as_bytes().with_nix_path(f)
289
0
    }
Unexecuted instantiation: <std::ffi::os_str::OsStr as nix::NixPath>::with_nix_path::<core::result::Result<nix::sys::socket::addr::UnixAddr, nix::errno::consts::Errno>, <nix::sys::socket::addr::UnixAddr>::new<std::path::Path>::{closure#0}>
Unexecuted instantiation: <std::ffi::os_str::OsStr as nix::NixPath>::with_nix_path::<_, _>
290
}
291
292
impl NixPath for CStr {
293
0
    fn is_empty(&self) -> bool {
294
0
        self.to_bytes().is_empty()
295
0
    }
296
297
0
    fn len(&self) -> usize {
298
0
        self.to_bytes().len()
299
0
    }
300
301
0
    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
302
0
    where
303
0
        F: FnOnce(&CStr) -> T,
304
    {
305
0
        Ok(f(self))
306
0
    }
307
}
308
309
impl NixPath for [u8] {
310
0
    fn is_empty(&self) -> bool {
311
0
        self.is_empty()
312
0
    }
313
314
0
    fn len(&self) -> usize {
315
0
        self.len()
316
0
    }
317
318
0
    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
319
0
    where
320
0
        F: FnOnce(&CStr) -> T,
321
    {
322
        // The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
323
        // longer than ~300 bytes. See the PR description to get stats for your own machine.
324
        // https://github.com/nix-rust/nix/pull/1656
325
        //
326
        // By being smaller than a memory page, we also avoid the compiler inserting a probe frame:
327
        // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
328
        const MAX_STACK_ALLOCATION: usize = 1024;
329
330
0
        if self.len() >= MAX_STACK_ALLOCATION {
331
0
            return with_nix_path_allocating(self, f);
332
0
        }
333
334
0
        let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
335
0
        let buf_ptr = buf.as_mut_ptr().cast();
336
337
0
        unsafe {
338
0
            ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
339
0
            buf_ptr.add(self.len()).write(0);
340
0
        }
341
342
0
        match CStr::from_bytes_with_nul(unsafe {
343
0
            slice::from_raw_parts(buf_ptr, self.len() + 1)
344
0
        }) {
345
0
            Ok(s) => Ok(f(s)),
346
0
            Err(_) => Err(Errno::EINVAL),
347
        }
348
0
    }
Unexecuted instantiation: <[u8] as nix::NixPath>::with_nix_path::<core::result::Result<nix::sys::socket::addr::UnixAddr, nix::errno::consts::Errno>, <nix::sys::socket::addr::UnixAddr>::new<std::path::Path>::{closure#0}>
Unexecuted instantiation: <[u8] as nix::NixPath>::with_nix_path::<_, _>
349
}
350
351
#[cold]
352
#[inline(never)]
353
0
fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
354
0
where
355
0
    F: FnOnce(&CStr) -> T,
356
{
357
0
    match CString::new(from) {
358
0
        Ok(s) => Ok(f(&s)),
359
0
        Err(_) => Err(Errno::EINVAL),
360
    }
361
0
}
Unexecuted instantiation: nix::with_nix_path_allocating::<core::result::Result<nix::sys::socket::addr::UnixAddr, nix::errno::consts::Errno>, <nix::sys::socket::addr::UnixAddr>::new<std::path::Path>::{closure#0}>
Unexecuted instantiation: nix::with_nix_path_allocating::<_, _>
362
363
impl NixPath for Path {
364
0
    fn is_empty(&self) -> bool {
365
0
        NixPath::is_empty(self.as_os_str())
366
0
    }
367
368
0
    fn len(&self) -> usize {
369
0
        NixPath::len(self.as_os_str())
370
0
    }
371
372
0
    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
373
0
    where
374
0
        F: FnOnce(&CStr) -> T,
375
    {
376
0
        self.as_os_str().with_nix_path(f)
377
0
    }
Unexecuted instantiation: <std::path::Path as nix::NixPath>::with_nix_path::<core::result::Result<nix::sys::socket::addr::UnixAddr, nix::errno::consts::Errno>, <nix::sys::socket::addr::UnixAddr>::new<std::path::Path>::{closure#0}>
Unexecuted instantiation: <std::path::Path as nix::NixPath>::with_nix_path::<_, _>
378
}
379
380
impl NixPath for PathBuf {
381
0
    fn is_empty(&self) -> bool {
382
0
        NixPath::is_empty(self.as_os_str())
383
0
    }
384
385
0
    fn len(&self) -> usize {
386
0
        NixPath::len(self.as_os_str())
387
0
    }
388
389
0
    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
390
0
    where
391
0
        F: FnOnce(&CStr) -> T,
392
    {
393
0
        self.as_os_str().with_nix_path(f)
394
0
    }
395
}
396
397
/// Like `NixPath::with_nix_path()`, but allow the `path` argument to be optional.
398
///
399
/// A NULL pointer will be provided if `path.is_none()`.
400
#[cfg(any(
401
    all(apple_targets, feature = "mount"),
402
    all(linux_android, any(feature = "mount", feature = "fanotify"))
403
))]
404
0
pub(crate) fn with_opt_nix_path<P, T, F>(path: Option<&P>, f: F) -> Result<T>
405
0
where
406
0
    P: ?Sized + NixPath,
407
0
    F: FnOnce(*const libc::c_char) -> T,
408
{
409
0
    match path {
410
0
        Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
411
0
        None => Ok(f(ptr::null())),
412
    }
413
0
}