Coverage Report

Created: 2025-12-28 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rustix-0.38.44/src/fs/at.rs
Line
Count
Source
1
//! POSIX-style `*at` functions.
2
//!
3
//! The `dirfd` argument to these functions may be a file descriptor for a
4
//! directory, the special value [`CWD`], or the special value [`ABS`].
5
//!
6
//! [`CWD`]: crate::fs::CWD
7
//! [`ABS`]: crate::fs::ABS
8
9
use crate::fd::OwnedFd;
10
use crate::ffi::CStr;
11
#[cfg(not(any(target_os = "espidf", target_os = "vita")))]
12
use crate::fs::Access;
13
#[cfg(not(target_os = "espidf"))]
14
use crate::fs::AtFlags;
15
#[cfg(apple)]
16
use crate::fs::CloneFlags;
17
#[cfg(linux_kernel)]
18
use crate::fs::RenameFlags;
19
#[cfg(not(target_os = "espidf"))]
20
use crate::fs::Stat;
21
#[cfg(not(any(apple, target_os = "espidf", target_os = "vita", target_os = "wasi")))]
22
use crate::fs::{Dev, FileType};
23
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
24
use crate::fs::{Gid, Uid};
25
use crate::fs::{Mode, OFlags};
26
use crate::{backend, io, path};
27
use backend::fd::{AsFd, BorrowedFd};
28
use core::mem::MaybeUninit;
29
use core::slice;
30
#[cfg(feature = "alloc")]
31
use {crate::ffi::CString, crate::path::SMALL_PATH_BUFFER_SIZE, alloc::vec::Vec};
32
#[cfg(not(any(target_os = "espidf", target_os = "vita")))]
33
use {crate::fs::Timestamps, crate::timespec::Nsecs};
34
35
/// `UTIME_NOW` for use with [`utimensat`].
36
///
37
/// [`utimensat`]: crate::fs::utimensat
38
#[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "vita")))]
39
pub const UTIME_NOW: Nsecs = backend::c::UTIME_NOW as Nsecs;
40
41
/// `UTIME_OMIT` for use with [`utimensat`].
42
///
43
/// [`utimensat`]: crate::fs::utimensat
44
#[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "vita")))]
45
pub const UTIME_OMIT: Nsecs = backend::c::UTIME_OMIT as Nsecs;
46
47
/// `openat(dirfd, path, oflags, mode)`—Opens a file.
48
///
49
/// POSIX guarantees that `openat` will use the lowest unused file descriptor,
50
/// however it is not safe in general to rely on this, as file descriptors may
51
/// be unexpectedly allocated on other threads or in libraries.
52
///
53
/// The `Mode` argument is only significant when creating a file.
54
///
55
/// # References
56
///  - [POSIX]
57
///  - [Linux]
58
///
59
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/openat.html
60
/// [Linux]: https://man7.org/linux/man-pages/man2/openat.2.html
61
#[inline]
62
0
pub fn openat<P: path::Arg, Fd: AsFd>(
63
0
    dirfd: Fd,
64
0
    path: P,
65
0
    oflags: OFlags,
66
0
    create_mode: Mode,
67
0
) -> io::Result<OwnedFd> {
68
0
    path.into_with_c_str(|path| {
69
0
        backend::fs::syscalls::openat(dirfd.as_fd(), path, oflags, create_mode)
70
0
    })
71
0
}
72
73
/// `readlinkat(fd, path)`—Reads the contents of a symlink.
74
///
75
/// If `reuse` already has available capacity, reuse it if possible.
76
///
77
/// # References
78
///  - [POSIX]
79
///  - [Linux]
80
///
81
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/readlinkat.html
82
/// [Linux]: https://man7.org/linux/man-pages/man2/readlinkat.2.html
83
#[cfg(feature = "alloc")]
84
#[inline]
85
0
pub fn readlinkat<P: path::Arg, Fd: AsFd, B: Into<Vec<u8>>>(
86
0
    dirfd: Fd,
87
0
    path: P,
88
0
    reuse: B,
89
0
) -> io::Result<CString> {
90
0
    path.into_with_c_str(|path| _readlinkat(dirfd.as_fd(), path, reuse.into()))
91
0
}
92
93
#[cfg(feature = "alloc")]
94
#[allow(unsafe_code)]
95
0
fn _readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, mut buffer: Vec<u8>) -> io::Result<CString> {
96
0
    buffer.clear();
97
0
    buffer.reserve(SMALL_PATH_BUFFER_SIZE);
98
99
    loop {
100
0
        let nread =
101
0
            backend::fs::syscalls::readlinkat(dirfd.as_fd(), path, buffer.spare_capacity_mut())?;
102
103
0
        debug_assert!(nread <= buffer.capacity());
104
0
        if nread < buffer.capacity() {
105
            // SAFETY: From the [documentation]: “On success, these calls
106
            // return the number of bytes placed in buf.”
107
            //
108
            // [documentation]: https://man7.org/linux/man-pages/man2/readlinkat.2.html
109
0
            unsafe {
110
0
                buffer.set_len(nread);
111
0
            }
112
113
            // SAFETY:
114
            // - “readlink places the contents of the symbolic link pathname
115
            //   in the buffer buf”
116
            // - [POSIX definition 3.271: Pathname]: “A string that is used
117
            //   to identify a file.”
118
            // - [POSIX definition 3.375: String]: “A contiguous sequence of
119
            //   bytes terminated by and including the first null byte.”
120
            // - “readlink does not append a terminating null byte to buf.”
121
            //
122
            // Thus, there will be no NUL bytes in the string.
123
            //
124
            // [POSIX definition 3.271: Pathname]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_271
125
            // [POSIX definition 3.375: String]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_375
126
            unsafe {
127
0
                return Ok(CString::from_vec_unchecked(buffer));
128
            }
129
0
        }
130
131
        // Use `Vec` reallocation strategy to grow capacity exponentially.
132
0
        buffer.reserve(buffer.capacity() + 1);
133
    }
134
0
}
135
136
/// `readlinkat(fd, path)`—Reads the contents of a symlink, without
137
/// allocating.
138
///
139
/// This is the "raw" version which avoids allocating, but which is
140
/// significantly trickier to use; most users should use plain [`readlinkat`].
141
///
142
/// This version writes bytes into the buffer and returns two slices, one
143
/// containing the written bytes, and one containing the remaining
144
/// uninitialized space. If the number of written bytes is equal to the length
145
/// of the buffer, it means the buffer wasn't big enough to hold the full
146
/// string, and callers should try again with a bigger buffer.
147
///
148
/// # References
149
///  - [POSIX]
150
///  - [Linux]
151
///
152
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/readlinkat.html
153
/// [Linux]: https://man7.org/linux/man-pages/man2/readlinkat.2.html
154
#[inline]
155
0
pub fn readlinkat_raw<P: path::Arg, Fd: AsFd>(
156
0
    dirfd: Fd,
157
0
    path: P,
158
0
    buf: &mut [MaybeUninit<u8>],
159
0
) -> io::Result<(&mut [u8], &mut [MaybeUninit<u8>])> {
160
0
    path.into_with_c_str(|path| _readlinkat_raw(dirfd.as_fd(), path, buf))
161
0
}
162
163
#[allow(unsafe_code)]
164
0
fn _readlinkat_raw<'a>(
165
0
    dirfd: BorrowedFd<'_>,
166
0
    path: &CStr,
167
0
    buf: &'a mut [MaybeUninit<u8>],
168
0
) -> io::Result<(&'a mut [u8], &'a mut [MaybeUninit<u8>])> {
169
0
    let n = backend::fs::syscalls::readlinkat(dirfd.as_fd(), path, buf)?;
170
    unsafe {
171
0
        Ok((
172
0
            slice::from_raw_parts_mut(buf.as_mut_ptr().cast::<u8>(), n),
173
0
            &mut buf[n..],
174
0
        ))
175
    }
176
0
}
177
178
/// `mkdirat(fd, path, mode)`—Creates a directory.
179
///
180
/// # References
181
///  - [POSIX]
182
///  - [Linux]
183
///
184
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdirat.html
185
/// [Linux]: https://man7.org/linux/man-pages/man2/mkdirat.2.html
186
#[inline]
187
0
pub fn mkdirat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, mode: Mode) -> io::Result<()> {
188
0
    path.into_with_c_str(|path| backend::fs::syscalls::mkdirat(dirfd.as_fd(), path, mode))
189
0
}
190
191
/// `linkat(old_dirfd, old_path, new_dirfd, new_path, flags)`—Creates a hard
192
/// link.
193
///
194
/// # References
195
///  - [POSIX]
196
///  - [Linux]
197
///
198
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/linkat.html
199
/// [Linux]: https://man7.org/linux/man-pages/man2/linkat.2.html
200
#[cfg(not(target_os = "espidf"))]
201
#[inline]
202
0
pub fn linkat<P: path::Arg, Q: path::Arg, PFd: AsFd, QFd: AsFd>(
203
0
    old_dirfd: PFd,
204
0
    old_path: P,
205
0
    new_dirfd: QFd,
206
0
    new_path: Q,
207
0
    flags: AtFlags,
208
0
) -> io::Result<()> {
209
0
    old_path.into_with_c_str(|old_path| {
210
0
        new_path.into_with_c_str(|new_path| {
211
0
            backend::fs::syscalls::linkat(
212
0
                old_dirfd.as_fd(),
213
0
                old_path,
214
0
                new_dirfd.as_fd(),
215
0
                new_path,
216
0
                flags,
217
            )
218
0
        })
219
0
    })
220
0
}
221
222
/// `unlinkat(fd, path, flags)`—Unlinks a file or remove a directory.
223
///
224
/// With the [`REMOVEDIR`] flag, this removes a directory. This is in place of
225
/// a `rmdirat` function.
226
///
227
/// # References
228
///  - [POSIX]
229
///  - [Linux]
230
///
231
/// [`REMOVEDIR`]: AtFlags::REMOVEDIR
232
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/unlinkat.html
233
/// [Linux]: https://man7.org/linux/man-pages/man2/unlinkat.2.html
234
#[cfg(not(target_os = "espidf"))]
235
#[inline]
236
0
pub fn unlinkat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, flags: AtFlags) -> io::Result<()> {
237
0
    path.into_with_c_str(|path| backend::fs::syscalls::unlinkat(dirfd.as_fd(), path, flags))
238
0
}
239
240
/// `renameat(old_dirfd, old_path, new_dirfd, new_path)`—Renames a file or
241
/// directory.
242
///
243
/// # References
244
///  - [POSIX]
245
///  - [Linux]
246
///
247
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/renameat.html
248
/// [Linux]: https://man7.org/linux/man-pages/man2/renameat.2.html
249
#[inline]
250
0
pub fn renameat<P: path::Arg, Q: path::Arg, PFd: AsFd, QFd: AsFd>(
251
0
    old_dirfd: PFd,
252
0
    old_path: P,
253
0
    new_dirfd: QFd,
254
0
    new_path: Q,
255
0
) -> io::Result<()> {
256
0
    old_path.into_with_c_str(|old_path| {
257
0
        new_path.into_with_c_str(|new_path| {
258
0
            backend::fs::syscalls::renameat(
259
0
                old_dirfd.as_fd(),
260
0
                old_path,
261
0
                new_dirfd.as_fd(),
262
0
                new_path,
263
            )
264
0
        })
265
0
    })
266
0
}
267
268
/// `renameat2(old_dirfd, old_path, new_dirfd, new_path, flags)`—Renames a
269
/// file or directory.
270
///
271
/// # References
272
///  - [Linux]
273
///
274
/// [Linux]: https://man7.org/linux/man-pages/man2/renameat2.2.html
275
#[cfg(linux_kernel)]
276
#[inline]
277
#[doc(alias = "renameat2")]
278
0
pub fn renameat_with<P: path::Arg, Q: path::Arg, PFd: AsFd, QFd: AsFd>(
279
0
    old_dirfd: PFd,
280
0
    old_path: P,
281
0
    new_dirfd: QFd,
282
0
    new_path: Q,
283
0
    flags: RenameFlags,
284
0
) -> io::Result<()> {
285
0
    old_path.into_with_c_str(|old_path| {
286
0
        new_path.into_with_c_str(|new_path| {
287
0
            backend::fs::syscalls::renameat2(
288
0
                old_dirfd.as_fd(),
289
0
                old_path,
290
0
                new_dirfd.as_fd(),
291
0
                new_path,
292
0
                flags,
293
            )
294
0
        })
Unexecuted instantiation: rustix::fs::at::renameat_with::<&std::path::Path, &std::path::Path, std::os::fd::owned::BorrowedFd, std::os::fd::owned::BorrowedFd>::{closure#0}::{closure#0}
Unexecuted instantiation: rustix::fs::at::renameat_with::<_, _, _, _>::{closure#0}::{closure#0}
295
0
    })
Unexecuted instantiation: rustix::fs::at::renameat_with::<&std::path::Path, &std::path::Path, std::os::fd::owned::BorrowedFd, std::os::fd::owned::BorrowedFd>::{closure#0}
Unexecuted instantiation: rustix::fs::at::renameat_with::<_, _, _, _>::{closure#0}
296
0
}
Unexecuted instantiation: rustix::fs::at::renameat_with::<&std::path::Path, &std::path::Path, std::os::fd::owned::BorrowedFd, std::os::fd::owned::BorrowedFd>
Unexecuted instantiation: rustix::fs::at::renameat_with::<_, _, _, _>
297
298
/// `symlinkat(old_path, new_dirfd, new_path)`—Creates a symlink.
299
///
300
/// # References
301
///  - [POSIX]
302
///  - [Linux]
303
///
304
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/symlinkat.html
305
/// [Linux]: https://man7.org/linux/man-pages/man2/symlinkat.2.html
306
#[inline]
307
0
pub fn symlinkat<P: path::Arg, Q: path::Arg, Fd: AsFd>(
308
0
    old_path: P,
309
0
    new_dirfd: Fd,
310
0
    new_path: Q,
311
0
) -> io::Result<()> {
312
0
    old_path.into_with_c_str(|old_path| {
313
0
        new_path.into_with_c_str(|new_path| {
314
0
            backend::fs::syscalls::symlinkat(old_path, new_dirfd.as_fd(), new_path)
315
0
        })
316
0
    })
317
0
}
318
319
/// `fstatat(dirfd, path, flags)`—Queries metadata for a file or directory.
320
///
321
/// [`Mode::from_raw_mode`] and [`FileType::from_raw_mode`] may be used to
322
/// interpret the `st_mode` field.
323
///
324
/// # References
325
///  - [POSIX]
326
///  - [Linux]
327
///
328
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fstatat.html
329
/// [Linux]: https://man7.org/linux/man-pages/man2/fstatat.2.html
330
/// [`Mode::from_raw_mode`]: crate::fs::Mode::from_raw_mode
331
/// [`FileType::from_raw_mode`]: crate::fs::FileType::from_raw_mode
332
#[cfg(not(target_os = "espidf"))]
333
#[inline]
334
#[doc(alias = "fstatat")]
335
0
pub fn statat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, flags: AtFlags) -> io::Result<Stat> {
336
0
    path.into_with_c_str(|path| backend::fs::syscalls::statat(dirfd.as_fd(), path, flags))
337
0
}
338
339
/// `faccessat(dirfd, path, access, flags)`—Tests permissions for a file or
340
/// directory.
341
///
342
/// On Linux before 5.8, this function uses the `faccessat` system call which
343
/// doesn't support any flags. This function emulates support for the
344
/// [`AtFlags::EACCESS`] flag by checking whether the uid and gid of the
345
/// process match the effective uid and gid, in which case the `EACCESS` flag
346
/// can be ignored. In Linux 5.8 and beyond `faccessat2` is used, which
347
/// supports flags.
348
///
349
/// # References
350
///  - [POSIX]
351
///  - [Linux]
352
///
353
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/faccessat.html
354
/// [Linux]: https://man7.org/linux/man-pages/man2/faccessat.2.html
355
#[cfg(not(any(target_os = "espidf", target_os = "vita")))]
356
#[inline]
357
#[doc(alias = "faccessat")]
358
0
pub fn accessat<P: path::Arg, Fd: AsFd>(
359
0
    dirfd: Fd,
360
0
    path: P,
361
0
    access: Access,
362
0
    flags: AtFlags,
363
0
) -> io::Result<()> {
364
0
    path.into_with_c_str(|path| backend::fs::syscalls::accessat(dirfd.as_fd(), path, access, flags))
365
0
}
366
367
/// `utimensat(dirfd, path, times, flags)`—Sets file or directory timestamps.
368
///
369
/// # References
370
///  - [POSIX]
371
///  - [Linux]
372
///
373
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/utimensat.html
374
/// [Linux]: https://man7.org/linux/man-pages/man2/utimensat.2.html
375
#[cfg(not(any(target_os = "espidf", target_os = "vita")))]
376
#[inline]
377
0
pub fn utimensat<P: path::Arg, Fd: AsFd>(
378
0
    dirfd: Fd,
379
0
    path: P,
380
0
    times: &Timestamps,
381
0
    flags: AtFlags,
382
0
) -> io::Result<()> {
383
0
    path.into_with_c_str(|path| backend::fs::syscalls::utimensat(dirfd.as_fd(), path, times, flags))
384
0
}
385
386
/// `fchmodat(dirfd, path, mode, flags)`—Sets file or directory permissions.
387
///
388
/// Platform support for flags varies widely, for example on Linux
389
/// [`AtFlags::SYMLINK_NOFOLLOW`] is not implemented and therefore
390
/// [`io::Errno::OPNOTSUPP`] will be returned.
391
///
392
/// # References
393
///  - [POSIX]
394
///  - [Linux]
395
///
396
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchmodat.html
397
/// [Linux]: https://man7.org/linux/man-pages/man2/fchmodat.2.html
398
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
399
#[inline]
400
#[doc(alias = "fchmodat")]
401
0
pub fn chmodat<P: path::Arg, Fd: AsFd>(
402
0
    dirfd: Fd,
403
0
    path: P,
404
0
    mode: Mode,
405
0
    flags: AtFlags,
406
0
) -> io::Result<()> {
407
0
    path.into_with_c_str(|path| backend::fs::syscalls::chmodat(dirfd.as_fd(), path, mode, flags))
408
0
}
409
410
/// `fclonefileat(src, dst_dir, dst, flags)`—Efficiently copies between files.
411
///
412
/// # References
413
///  - [Apple]
414
///
415
/// [Apple]: https://opensource.apple.com/source/xnu/xnu-3789.21.4/bsd/man/man2/clonefile.2.auto.html
416
#[cfg(apple)]
417
#[inline]
418
pub fn fclonefileat<Fd: AsFd, DstFd: AsFd, P: path::Arg>(
419
    src: Fd,
420
    dst_dir: DstFd,
421
    dst: P,
422
    flags: CloneFlags,
423
) -> io::Result<()> {
424
    dst.into_with_c_str(|dst| {
425
        backend::fs::syscalls::fclonefileat(src.as_fd(), dst_dir.as_fd(), dst, flags)
426
    })
427
}
428
429
/// `mknodat(dirfd, path, mode, dev)`—Creates special or normal files.
430
///
431
/// # References
432
///  - [POSIX]
433
///  - [Linux]
434
///
435
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/mknodat.html
436
/// [Linux]: https://man7.org/linux/man-pages/man2/mknodat.2.html
437
#[cfg(not(any(apple, target_os = "espidf", target_os = "vita", target_os = "wasi")))]
438
#[inline]
439
0
pub fn mknodat<P: path::Arg, Fd: AsFd>(
440
0
    dirfd: Fd,
441
0
    path: P,
442
0
    file_type: FileType,
443
0
    mode: Mode,
444
0
    dev: Dev,
445
0
) -> io::Result<()> {
446
0
    path.into_with_c_str(|path| {
447
0
        backend::fs::syscalls::mknodat(dirfd.as_fd(), path, file_type, mode, dev)
448
0
    })
449
0
}
450
451
/// `fchownat(dirfd, path, owner, group, flags)`—Sets file or directory
452
/// ownership.
453
///
454
/// # References
455
///  - [POSIX]
456
///  - [Linux]
457
///
458
/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchownat.html
459
/// [Linux]: https://man7.org/linux/man-pages/man2/fchownat.2.html
460
#[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
461
#[inline]
462
#[doc(alias = "fchownat")]
463
0
pub fn chownat<P: path::Arg, Fd: AsFd>(
464
0
    dirfd: Fd,
465
0
    path: P,
466
0
    owner: Option<Uid>,
467
0
    group: Option<Gid>,
468
0
    flags: AtFlags,
469
0
) -> io::Result<()> {
470
0
    path.into_with_c_str(|path| {
471
0
        backend::fs::syscalls::chownat(dirfd.as_fd(), path, owner, group, flags)
472
0
    })
473
0
}