• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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