]>
Commit | Line | Data |
---|---|---|
064997fb FG |
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 | ||
487cf647 | 8 | use crate::fd::OwnedFd; |
064997fb | 9 | use crate::ffi::{CStr, CString}; |
487cf647 | 10 | #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] |
064997fb FG |
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}; | |
064997fb FG |
19 | use crate::path::SMALL_PATH_BUFFER_SIZE; |
20 | #[cfg(not(target_os = "wasi"))] | |
21 | use crate::process::{Gid, Uid}; | |
487cf647 | 22 | use crate::{backend, io, path}; |
064997fb | 23 | use alloc::vec::Vec; |
487cf647 FG |
24 | use backend::fd::{AsFd, BorrowedFd}; |
25 | use backend::time::types::Nsecs; | |
064997fb | 26 | |
487cf647 | 27 | pub use backend::fs::types::{Dev, RawMode}; |
064997fb FG |
28 | |
29 | /// `UTIME_NOW` for use with [`utimensat`]. | |
30 | /// | |
31 | /// [`utimensat`]: crate::fs::utimensat | |
32 | #[cfg(not(target_os = "redox"))] | |
487cf647 | 33 | pub const UTIME_NOW: Nsecs = backend::fs::types::UTIME_NOW as Nsecs; |
064997fb FG |
34 | |
35 | /// `UTIME_OMIT` for use with [`utimensat`]. | |
36 | /// | |
37 | /// [`utimensat`]: crate::fs::utimensat | |
38 | #[cfg(not(target_os = "redox"))] | |
487cf647 | 39 | pub const UTIME_OMIT: Nsecs = backend::fs::types::UTIME_OMIT as Nsecs; |
064997fb FG |
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] | |
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> { | |
487cf647 FG |
62 | path.into_with_c_str(|path| { |
63 | backend::fs::syscalls::openat(dirfd.as_fd(), path, oflags, create_mode) | |
64 | }) | |
064997fb FG |
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] | |
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 | ||
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 { | |
487cf647 | 94 | let nread = backend::fs::syscalls::readlinkat(dirfd.as_fd(), path, &mut buffer)?; |
064997fb FG |
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] | |
116 | pub fn mkdirat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, mode: Mode) -> io::Result<()> { | |
487cf647 | 117 | path.into_with_c_str(|path| backend::fs::syscalls::mkdirat(dirfd.as_fd(), path, mode)) |
064997fb FG |
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] | |
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| { | |
487cf647 | 139 | backend::fs::syscalls::linkat( |
064997fb FG |
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] | |
163 | pub fn unlinkat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, flags: AtFlags) -> io::Result<()> { | |
487cf647 | 164 | path.into_with_c_str(|path| backend::fs::syscalls::unlinkat(dirfd.as_fd(), path, flags)) |
064997fb FG |
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] | |
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| { | |
487cf647 FG |
185 | backend::fs::syscalls::renameat( |
186 | old_dirfd.as_fd(), | |
187 | old_path, | |
188 | new_dirfd.as_fd(), | |
189 | new_path, | |
190 | ) | |
064997fb FG |
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")] | |
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| { | |
487cf647 | 214 | backend::fs::syscalls::renameat2( |
064997fb FG |
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_dirfd, 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] | |
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| { | |
487cf647 | 241 | backend::fs::syscalls::symlinkat(old_path, new_dirfd.as_fd(), new_path) |
064997fb FG |
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")] | |
261 | pub fn statat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, flags: AtFlags) -> io::Result<Stat> { | |
487cf647 | 262 | path.into_with_c_str(|path| backend::fs::syscalls::statat(dirfd.as_fd(), path, flags)) |
064997fb FG |
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 | |
487cf647 | 274 | #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] |
064997fb FG |
275 | #[inline] |
276 | #[doc(alias = "faccessat")] | |
277 | pub fn accessat<P: path::Arg, Fd: AsFd>( | |
278 | dirfd: Fd, | |
279 | path: P, | |
280 | access: Access, | |
281 | flags: AtFlags, | |
282 | ) -> io::Result<()> { | |
487cf647 | 283 | path.into_with_c_str(|path| backend::fs::syscalls::accessat(dirfd.as_fd(), path, access, flags)) |
064997fb FG |
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] | |
295 | pub fn utimensat<P: path::Arg, Fd: AsFd>( | |
296 | dirfd: Fd, | |
297 | path: P, | |
298 | times: &Timestamps, | |
299 | flags: AtFlags, | |
300 | ) -> io::Result<()> { | |
487cf647 | 301 | path.into_with_c_str(|path| backend::fs::syscalls::utimensat(dirfd.as_fd(), path, times, flags)) |
064997fb FG |
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")] | |
321 | pub fn chmodat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, mode: Mode) -> io::Result<()> { | |
487cf647 | 322 | path.into_with_c_str(|path| backend::fs::syscalls::chmodat(dirfd.as_fd(), path, mode)) |
064997fb FG |
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] | |
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| { | |
487cf647 | 340 | backend::fs::syscalls::fclonefileat(src.as_fd(), dst_dir.as_fd(), dst, flags) |
064997fb FG |
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] | |
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| { | |
487cf647 | 362 | backend::fs::syscalls::mknodat(dirfd.as_fd(), path, file_type, mode, dev) |
064997fb FG |
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(any(target_os = "wasi")))] | |
376 | #[inline] | |
377 | pub fn chownat<P: path::Arg, Fd: AsFd>( | |
378 | dirfd: Fd, | |
379 | path: P, | |
380 | owner: Option<Uid>, | |
381 | group: Option<Gid>, | |
382 | flags: AtFlags, | |
383 | ) -> io::Result<()> { | |
384 | path.into_with_c_str(|path| { | |
487cf647 | 385 | backend::fs::syscalls::chownat(dirfd.as_fd(), path, owner, group, flags) |
064997fb FG |
386 | }) |
387 | } |