1 //! POSIX-style `*at` functions.
2 //!
3 //! The `dirfd` argument to these functions may be a file descriptor for a
4 //! directory, or the special value returned by [`cwd`].
5 //!
6 //! [`cwd`]: crate::fs::cwd
7
8 use crate::fd::OwnedFd;
9 use crate::ffi::{CStr, CString};
10 #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
11 use crate::fs::Access;
12 #[cfg(any(target_os = "ios", target_os = "macos"))]
13 use crate::fs::CloneFlags;
14 #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "wasi")))]
15 use crate::fs::FileType;
16 #[cfg(any(target_os = "android", target_os = "linux"))]
17 use crate::fs::RenameFlags;
18 use crate::fs::{AtFlags, Mode, OFlags, Stat, Timestamps};
19 use crate::path::SMALL_PATH_BUFFER_SIZE;
20 #[cfg(not(target_os = "wasi"))]
21 use crate::process::{Gid, Uid};
22 use crate::{backend, io, path};
23 use alloc::vec::Vec;
24 use backend::fd::{AsFd, BorrowedFd};
25 use backend::time::types::Nsecs;
26
27 pub use backend::fs::types::{Dev, RawMode};
28
29 /// `UTIME_NOW` for use with [`utimensat`].
30 ///
31 /// [`utimensat`]: crate::fs::utimensat
32 #[cfg(not(target_os = "redox"))]
33 pub const UTIME_NOW: Nsecs = backend::fs::types::UTIME_NOW as Nsecs;
34
35 /// `UTIME_OMIT` for use with [`utimensat`].
36 ///
37 /// [`utimensat`]: crate::fs::utimensat
38 #[cfg(not(target_os = "redox"))]
39 pub const UTIME_OMIT: Nsecs = backend::fs::types::UTIME_OMIT as Nsecs;
40
41 /// `openat(dirfd, path, oflags, mode)`—Opens a file.
42 ///
43 /// POSIX guarantees that `openat` will use the lowest unused file descriptor,
44 /// however it is not safe in general to rely on this, as file descriptors may
45 /// be unexpectedly allocated on other threads or in libraries.
46 ///
47 /// The `Mode` argument is only significant when creating a file.
48 ///
49 /// # References
50 /// - [POSIX]
51 /// - [Linux]
52 ///
53 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/openat.html
54 /// [Linux]: https://man7.org/linux/man-pages/man2/open.2.html
55 #[inline]
openat<P: path::Arg, Fd: AsFd>( dirfd: Fd, path: P, oflags: OFlags, create_mode: Mode, ) -> io::Result<OwnedFd>56 pub fn openat<P: path::Arg, Fd: AsFd>(
57 dirfd: Fd,
58 path: P,
59 oflags: OFlags,
60 create_mode: Mode,
61 ) -> io::Result<OwnedFd> {
62 path.into_with_c_str(|path| {
63 backend::fs::syscalls::openat(dirfd.as_fd(), path, oflags, create_mode)
64 })
65 }
66
67 /// `readlinkat(fd, path)`—Reads the contents of a symlink.
68 ///
69 /// If `reuse` is non-empty, reuse its buffer to store the result if possible.
70 ///
71 /// # References
72 /// - [POSIX]
73 /// - [Linux]
74 ///
75 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlinkat.html
76 /// [Linux]: https://man7.org/linux/man-pages/man2/readlinkat.2.html
77 #[inline]
readlinkat<P: path::Arg, Fd: AsFd, B: Into<Vec<u8>>>( dirfd: Fd, path: P, reuse: B, ) -> io::Result<CString>78 pub fn readlinkat<P: path::Arg, Fd: AsFd, B: Into<Vec<u8>>>(
79 dirfd: Fd,
80 path: P,
81 reuse: B,
82 ) -> io::Result<CString> {
83 path.into_with_c_str(|path| _readlinkat(dirfd.as_fd(), path, reuse.into()))
84 }
85
_readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, mut buffer: Vec<u8>) -> io::Result<CString>86 fn _readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, mut buffer: Vec<u8>) -> io::Result<CString> {
87 // This code would benefit from having a better way to read into
88 // uninitialized memory, but that requires `unsafe`.
89 buffer.clear();
90 buffer.reserve(SMALL_PATH_BUFFER_SIZE);
91 buffer.resize(buffer.capacity(), 0_u8);
92
93 loop {
94 let nread = backend::fs::syscalls::readlinkat(dirfd.as_fd(), path, &mut buffer)?;
95
96 let nread = nread as usize;
97 assert!(nread <= buffer.len());
98 if nread < buffer.len() {
99 buffer.resize(nread, 0_u8);
100 return Ok(CString::new(buffer).unwrap());
101 }
102 buffer.reserve(1); // use `Vec` reallocation strategy to grow capacity exponentially
103 buffer.resize(buffer.capacity(), 0_u8);
104 }
105 }
106
107 /// `mkdirat(fd, path, mode)`—Creates a directory.
108 ///
109 /// # References
110 /// - [POSIX]
111 /// - [Linux]
112 ///
113 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdirat.html
114 /// [Linux]: https://man7.org/linux/man-pages/man2/mkdirat.2.html
115 #[inline]
mkdirat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, mode: Mode) -> io::Result<()>116 pub fn mkdirat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, mode: Mode) -> io::Result<()> {
117 path.into_with_c_str(|path| backend::fs::syscalls::mkdirat(dirfd.as_fd(), path, mode))
118 }
119
120 /// `linkat(old_dirfd, old_path, new_dirfd, new_path, flags)`—Creates a hard
121 /// link.
122 ///
123 /// # References
124 /// - [POSIX]
125 /// - [Linux]
126 ///
127 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/linkat.html
128 /// [Linux]: https://man7.org/linux/man-pages/man2/linkat.2.html
129 #[inline]
linkat<P: path::Arg, Q: path::Arg, PFd: AsFd, QFd: AsFd>( old_dirfd: PFd, old_path: P, new_dirfd: QFd, new_path: Q, flags: AtFlags, ) -> io::Result<()>130 pub fn linkat<P: path::Arg, Q: path::Arg, PFd: AsFd, QFd: AsFd>(
131 old_dirfd: PFd,
132 old_path: P,
133 new_dirfd: QFd,
134 new_path: Q,
135 flags: AtFlags,
136 ) -> io::Result<()> {
137 old_path.into_with_c_str(|old_path| {
138 new_path.into_with_c_str(|new_path| {
139 backend::fs::syscalls::linkat(
140 old_dirfd.as_fd(),
141 old_path,
142 new_dirfd.as_fd(),
143 new_path,
144 flags,
145 )
146 })
147 })
148 }
149
150 /// `unlinkat(fd, path, flags)`—Unlinks a file or remove a directory.
151 ///
152 /// With the [`REMOVEDIR`] flag, this removes a directory. This is in place
153 /// of a `rmdirat` function.
154 ///
155 /// # References
156 /// - [POSIX]
157 /// - [Linux]
158 ///
159 /// [`REMOVEDIR`]: AtFlags::REMOVEDIR
160 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlinkat.html
161 /// [Linux]: https://man7.org/linux/man-pages/man2/unlinkat.2.html
162 #[inline]
unlinkat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, flags: AtFlags) -> io::Result<()>163 pub fn unlinkat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, flags: AtFlags) -> io::Result<()> {
164 path.into_with_c_str(|path| backend::fs::syscalls::unlinkat(dirfd.as_fd(), path, flags))
165 }
166
167 /// `renameat(old_dirfd, old_path, new_dirfd, new_path)`—Renames a file or
168 /// directory.
169 ///
170 /// # References
171 /// - [POSIX]
172 /// - [Linux]
173 ///
174 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/renameat.html
175 /// [Linux]: https://man7.org/linux/man-pages/man2/renameat.2.html
176 #[inline]
renameat<P: path::Arg, Q: path::Arg, PFd: AsFd, QFd: AsFd>( old_dirfd: PFd, old_path: P, new_dirfd: QFd, new_path: Q, ) -> io::Result<()>177 pub fn renameat<P: path::Arg, Q: path::Arg, PFd: AsFd, QFd: AsFd>(
178 old_dirfd: PFd,
179 old_path: P,
180 new_dirfd: QFd,
181 new_path: Q,
182 ) -> io::Result<()> {
183 old_path.into_with_c_str(|old_path| {
184 new_path.into_with_c_str(|new_path| {
185 backend::fs::syscalls::renameat(
186 old_dirfd.as_fd(),
187 old_path,
188 new_dirfd.as_fd(),
189 new_path,
190 )
191 })
192 })
193 }
194
195 /// `renameat2(old_dirfd, old_path, new_dirfd, new_path, flags)`—Renames a
196 /// file or directory.
197 ///
198 /// # References
199 /// - [Linux]
200 ///
201 /// [Linux]: https://man7.org/linux/man-pages/man2/renameat2.2.html
202 #[cfg(any(target_os = "android", target_os = "linux"))]
203 #[inline]
204 #[doc(alias = "renameat2")]
renameat_with<P: path::Arg, Q: path::Arg, PFd: AsFd, QFd: AsFd>( old_dirfd: PFd, old_path: P, new_dirfd: QFd, new_path: Q, flags: RenameFlags, ) -> io::Result<()>205 pub fn renameat_with<P: path::Arg, Q: path::Arg, PFd: AsFd, QFd: AsFd>(
206 old_dirfd: PFd,
207 old_path: P,
208 new_dirfd: QFd,
209 new_path: Q,
210 flags: RenameFlags,
211 ) -> io::Result<()> {
212 old_path.into_with_c_str(|old_path| {
213 new_path.into_with_c_str(|new_path| {
214 backend::fs::syscalls::renameat2(
215 old_dirfd.as_fd(),
216 old_path,
217 new_dirfd.as_fd(),
218 new_path,
219 flags,
220 )
221 })
222 })
223 }
224
225 /// `symlinkat(old_path, new_dirfd, new_path)`—Creates a symlink.
226 ///
227 /// # References
228 /// - [POSIX]
229 /// - [Linux]
230 ///
231 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html
232 /// [Linux]: https://man7.org/linux/man-pages/man2/symlinkat.2.html
233 #[inline]
symlinkat<P: path::Arg, Q: path::Arg, Fd: AsFd>( old_path: P, new_dirfd: Fd, new_path: Q, ) -> io::Result<()>234 pub fn symlinkat<P: path::Arg, Q: path::Arg, Fd: AsFd>(
235 old_path: P,
236 new_dirfd: Fd,
237 new_path: Q,
238 ) -> io::Result<()> {
239 old_path.into_with_c_str(|old_path| {
240 new_path.into_with_c_str(|new_path| {
241 backend::fs::syscalls::symlinkat(old_path, new_dirfd.as_fd(), new_path)
242 })
243 })
244 }
245
246 /// `fstatat(dirfd, path, flags)`—Queries metadata for a file or directory.
247 ///
248 /// [`Mode::from_raw_mode`] and [`FileType::from_raw_mode`] may be used to
249 /// interpret the `st_mode` field.
250 ///
251 /// # References
252 /// - [POSIX]
253 /// - [Linux]
254 ///
255 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatat.html
256 /// [Linux]: https://man7.org/linux/man-pages/man2/fstatat.2.html
257 /// [`Mode::from_raw_mode`]: crate::fs::Mode::from_raw_mode
258 /// [`FileType::from_raw_mode`]: crate::fs::FileType::from_raw_mode
259 #[inline]
260 #[doc(alias = "fstatat")]
statat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, flags: AtFlags) -> io::Result<Stat>261 pub fn statat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, flags: AtFlags) -> io::Result<Stat> {
262 path.into_with_c_str(|path| backend::fs::syscalls::statat(dirfd.as_fd(), path, flags))
263 }
264
265 /// `faccessat(dirfd, path, access, flags)`—Tests permissions for a file or
266 /// directory.
267 ///
268 /// # References
269 /// - [POSIX]
270 /// - [Linux]
271 ///
272 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/faccessat.html
273 /// [Linux]: https://man7.org/linux/man-pages/man2/faccessat.2.html
274 #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
275 #[inline]
276 #[doc(alias = "faccessat")]
accessat<P: path::Arg, Fd: AsFd>( dirfd: Fd, path: P, access: Access, flags: AtFlags, ) -> io::Result<()>277 pub fn accessat<P: path::Arg, Fd: AsFd>(
278 dirfd: Fd,
279 path: P,
280 access: Access,
281 flags: AtFlags,
282 ) -> io::Result<()> {
283 path.into_with_c_str(|path| backend::fs::syscalls::accessat(dirfd.as_fd(), path, access, flags))
284 }
285
286 /// `utimensat(dirfd, path, times, flags)`—Sets file or directory timestamps.
287 ///
288 /// # References
289 /// - [POSIX]
290 /// - [Linux]
291 ///
292 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimensat.html
293 /// [Linux]: https://man7.org/linux/man-pages/man2/utimensat.2.html
294 #[inline]
utimensat<P: path::Arg, Fd: AsFd>( dirfd: Fd, path: P, times: &Timestamps, flags: AtFlags, ) -> io::Result<()>295 pub fn utimensat<P: path::Arg, Fd: AsFd>(
296 dirfd: Fd,
297 path: P,
298 times: &Timestamps,
299 flags: AtFlags,
300 ) -> io::Result<()> {
301 path.into_with_c_str(|path| backend::fs::syscalls::utimensat(dirfd.as_fd(), path, times, flags))
302 }
303
304 /// `fchmodat(dirfd, path, mode, 0)`—Sets file or directory permissions.
305 ///
306 /// The flags argument is fixed to 0, so `AT_SYMLINK_NOFOLLOW` is not
307 /// supported. <details>Platform support for this flag varies widely.</details>
308 ///
309 /// This implementation does not support `O_PATH` file descriptors, even on
310 /// platforms where the host libc emulates it.
311 ///
312 /// # References
313 /// - [POSIX]
314 /// - [Linux]
315 ///
316 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html
317 /// [Linux]: https://man7.org/linux/man-pages/man2/fchmodat.2.html
318 #[cfg(not(target_os = "wasi"))]
319 #[inline]
320 #[doc(alias = "fchmodat")]
chmodat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, mode: Mode) -> io::Result<()>321 pub fn chmodat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, mode: Mode) -> io::Result<()> {
322 path.into_with_c_str(|path| backend::fs::syscalls::chmodat(dirfd.as_fd(), path, mode))
323 }
324
325 /// `fclonefileat(src, dst_dir, dst, flags)`—Efficiently copies between files.
326 ///
327 /// # References
328 /// - [Apple]
329 ///
330 /// [Apple]: https://opensource.apple.com/source/xnu/xnu-3789.21.4/bsd/man/man2/clonefile.2.auto.html
331 #[cfg(any(target_os = "ios", target_os = "macos"))]
332 #[inline]
fclonefileat<Fd: AsFd, DstFd: AsFd, P: path::Arg>( src: Fd, dst_dir: DstFd, dst: P, flags: CloneFlags, ) -> io::Result<()>333 pub fn fclonefileat<Fd: AsFd, DstFd: AsFd, P: path::Arg>(
334 src: Fd,
335 dst_dir: DstFd,
336 dst: P,
337 flags: CloneFlags,
338 ) -> io::Result<()> {
339 dst.into_with_c_str(|dst| {
340 backend::fs::syscalls::fclonefileat(src.as_fd(), dst_dir.as_fd(), dst, flags)
341 })
342 }
343
344 /// `mknodat(dirfd, path, mode, dev)`—Creates special or normal files.
345 ///
346 /// # References
347 /// - [POSIX]
348 /// - [Linux]
349 ///
350 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/mknodat.html
351 /// [Linux]: https://man7.org/linux/man-pages/man2/mknodat.2.html
352 #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "wasi")))]
353 #[inline]
mknodat<P: path::Arg, Fd: AsFd>( dirfd: Fd, path: P, file_type: FileType, mode: Mode, dev: Dev, ) -> io::Result<()>354 pub fn mknodat<P: path::Arg, Fd: AsFd>(
355 dirfd: Fd,
356 path: P,
357 file_type: FileType,
358 mode: Mode,
359 dev: Dev,
360 ) -> io::Result<()> {
361 path.into_with_c_str(|path| {
362 backend::fs::syscalls::mknodat(dirfd.as_fd(), path, file_type, mode, dev)
363 })
364 }
365
366 /// `fchownat(dirfd, path, owner, group, flags)`—Sets file or directory
367 /// ownership.
368 ///
369 /// # References
370 /// - [POSIX]
371 /// - [Linux]
372 ///
373 /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html
374 /// [Linux]: https://man7.org/linux/man-pages/man2/fchownat.2.html
375 #[cfg(not(target_os = "wasi"))]
376 #[inline]
377 #[doc(alias = "fchownat")]
chownat<P: path::Arg, Fd: AsFd>( dirfd: Fd, path: P, owner: Option<Uid>, group: Option<Gid>, flags: AtFlags, ) -> io::Result<()>378 pub fn chownat<P: path::Arg, Fd: AsFd>(
379 dirfd: Fd,
380 path: P,
381 owner: Option<Uid>,
382 group: Option<Gid>,
383 flags: AtFlags,
384 ) -> io::Result<()> {
385 path.into_with_c_str(|path| {
386 backend::fs::syscalls::chownat(dirfd.as_fd(), path, owner, group, flags)
387 })
388 }
389