/rust/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/fcntl.rs
Line | Count | Source |
1 | | //! File control options |
2 | | use crate::errno::Errno; |
3 | | #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] |
4 | | use core::slice; |
5 | | use libc::{c_int, c_uint, size_t, ssize_t}; |
6 | | #[cfg(any( |
7 | | target_os = "netbsd", |
8 | | apple_targets, |
9 | | target_os = "dragonfly", |
10 | | all(target_os = "freebsd", target_arch = "x86_64"), |
11 | | ))] |
12 | | use std::ffi::CStr; |
13 | | use std::ffi::OsString; |
14 | | #[cfg(not(any(target_os = "redox", target_os = "solaris")))] |
15 | | use std::ops::{Deref, DerefMut}; |
16 | | use std::os::unix::ffi::OsStringExt; |
17 | | #[cfg(not(target_os = "redox"))] |
18 | | use std::os::unix::io::OwnedFd; |
19 | | use std::os::unix::io::RawFd; |
20 | | #[cfg(any( |
21 | | target_os = "netbsd", |
22 | | apple_targets, |
23 | | target_os = "dragonfly", |
24 | | all(target_os = "freebsd", target_arch = "x86_64"), |
25 | | ))] |
26 | | use std::path::PathBuf; |
27 | | #[cfg(any(linux_android, target_os = "freebsd"))] |
28 | | use std::ptr; |
29 | | |
30 | | #[cfg(feature = "fs")] |
31 | | use crate::{sys::stat::Mode, NixPath, Result}; |
32 | | |
33 | | #[cfg(any( |
34 | | linux_android, |
35 | | target_os = "emscripten", |
36 | | target_os = "fuchsia", |
37 | | target_os = "wasi", |
38 | | target_env = "uclibc", |
39 | | target_os = "freebsd" |
40 | | ))] |
41 | | #[cfg(feature = "fs")] |
42 | | pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice}; |
43 | | |
44 | | /// A file descriptor referring to the working directory of the current process |
45 | | /// **that should be ONLY passed to the `dirfd` argument of those `xxat()` functions**. |
46 | | /// |
47 | | /// # Examples |
48 | | /// |
49 | | /// Use it in [`openat()`]: |
50 | | /// |
51 | | /// ```no_run |
52 | | /// use nix::fcntl::AT_FDCWD; |
53 | | /// use nix::fcntl::openat; |
54 | | /// use nix::fcntl::OFlag; |
55 | | /// use nix::sys::stat::Mode; |
56 | | /// |
57 | | /// let fd = openat(AT_FDCWD, "foo", OFlag::O_RDONLY | OFlag::O_CLOEXEC, Mode::empty()).unwrap(); |
58 | | /// ``` |
59 | | /// |
60 | | /// # WARNING |
61 | | /// |
62 | | /// Do NOT pass this symbol to non-`xxat()` functions, it won't work: |
63 | | /// |
64 | | /// ```should_panic |
65 | | /// use nix::errno::Errno; |
66 | | /// use nix::fcntl::AT_FDCWD; |
67 | | /// use nix::sys::stat::fstat; |
68 | | /// |
69 | | /// let never = fstat(AT_FDCWD).unwrap(); |
70 | | /// ``` |
71 | | // |
72 | | // SAFETY: |
73 | | // 1. `AT_FDCWD` is usable for the whole process life, so it is `'static`. |
74 | | // 2. It is not a valid file descriptor, but OS will handle it for us when passed |
75 | | // to `xxat(2)` calls. |
76 | | #[cfg(not(target_os = "redox"))] // Redox does not have this |
77 | | pub const AT_FDCWD: std::os::fd::BorrowedFd<'static> = |
78 | | unsafe { std::os::fd::BorrowedFd::borrow_raw(libc::AT_FDCWD) }; |
79 | | |
80 | | #[cfg(not(target_os = "redox"))] |
81 | | #[cfg(any(feature = "fs", feature = "process", feature = "user"))] |
82 | | libc_bitflags! { |
83 | | /// Flags that control how the various *at syscalls behave. |
84 | | #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "process"))))] |
85 | | pub struct AtFlags: c_int { |
86 | | #[allow(missing_docs)] |
87 | | #[doc(hidden)] |
88 | | // Should not be used by the public API, but only internally. |
89 | | AT_REMOVEDIR; |
90 | | /// Used with [`linkat`](crate::unistd::linkat`) to create a link to a symbolic link's |
91 | | /// target, instead of to the symbolic link itself. |
92 | | AT_SYMLINK_FOLLOW; |
93 | | /// Used with functions like [`fstatat`](crate::sys::stat::fstatat`) to operate on a link |
94 | | /// itself, instead of the symbolic link's target. |
95 | | AT_SYMLINK_NOFOLLOW; |
96 | | /// Don't automount the terminal ("basename") component of pathname if it is a directory |
97 | | /// that is an automount point. |
98 | | #[cfg(linux_android)] |
99 | | AT_NO_AUTOMOUNT; |
100 | | /// If the provided path is an empty string, operate on the provided directory file |
101 | | /// descriptor instead. |
102 | | #[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))] |
103 | | AT_EMPTY_PATH; |
104 | | /// Used with [`faccessat`](crate::unistd::faccessat), the checks for accessibility are |
105 | | /// performed using the effective user and group IDs instead of the real user and group ID |
106 | | #[cfg(not(target_os = "android"))] |
107 | | AT_EACCESS; |
108 | | } |
109 | | } |
110 | | |
111 | | #[cfg(any( |
112 | | feature = "fs", |
113 | | feature = "term", |
114 | | all(feature = "fanotify", target_os = "linux") |
115 | | ))] |
116 | | libc_bitflags!( |
117 | | /// Configuration options for opened files. |
118 | | #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term", all(feature = "fanotify", target_os = "linux")))))] |
119 | | pub struct OFlag: c_int { |
120 | | /// Mask for the access mode of the file. |
121 | | O_ACCMODE; |
122 | | /// Use alternate I/O semantics. |
123 | | #[cfg(target_os = "netbsd")] |
124 | | O_ALT_IO; |
125 | | /// Open the file in append-only mode. |
126 | | O_APPEND; |
127 | | /// Generate a signal when input or output becomes possible. |
128 | | #[cfg(not(any( |
129 | | solarish, |
130 | | target_os = "aix", |
131 | | target_os = "haiku", |
132 | | target_os = "cygwin" |
133 | | )))] |
134 | | O_ASYNC; |
135 | | /// Closes the file descriptor once an `execve` call is made. |
136 | | /// |
137 | | /// Also sets the file offset to the beginning of the file. |
138 | | O_CLOEXEC; |
139 | | /// Create the file if it does not exist. |
140 | | O_CREAT; |
141 | | /// Try to minimize cache effects of the I/O for this file. |
142 | | #[cfg(any( |
143 | | freebsdlike, |
144 | | linux_android, |
145 | | target_os = "illumos", |
146 | | target_os = "netbsd" |
147 | | ))] |
148 | | O_DIRECT; |
149 | | /// If the specified path isn't a directory, fail. |
150 | | O_DIRECTORY; |
151 | | /// Implicitly follow each `write()` with an `fdatasync()`. |
152 | | #[cfg(any(linux_android, apple_targets, target_os = "freebsd", netbsdlike))] |
153 | | O_DSYNC; |
154 | | /// Error out if a file was not created. |
155 | | O_EXCL; |
156 | | /// Open for execute only. |
157 | | #[cfg(target_os = "freebsd")] |
158 | | O_EXEC; |
159 | | /// Open with an exclusive file lock. |
160 | | #[cfg(any(bsd, target_os = "redox"))] |
161 | | O_EXLOCK; |
162 | | /// Same as `O_SYNC`. |
163 | | #[cfg(any(bsd, |
164 | | all(target_os = "linux", not(target_env = "musl"), not(target_env = "ohos")), |
165 | | target_os = "redox"))] |
166 | | O_FSYNC; |
167 | | /// Allow files whose sizes can't be represented in an `off_t` to be opened. |
168 | | #[cfg(linux_android)] |
169 | | O_LARGEFILE; |
170 | | /// Do not update the file last access time during `read(2)`s. |
171 | | #[cfg(linux_android)] |
172 | | O_NOATIME; |
173 | | /// Don't attach the device as the process' controlling terminal. |
174 | | #[cfg(not(target_os = "redox"))] |
175 | | O_NOCTTY; |
176 | | /// Same as `O_NONBLOCK`. |
177 | | #[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "cygwin")))] |
178 | | O_NDELAY; |
179 | | /// `open()` will fail if the given path is a symbolic link. |
180 | | O_NOFOLLOW; |
181 | | /// When possible, open the file in nonblocking mode. |
182 | | O_NONBLOCK; |
183 | | /// Don't deliver `SIGPIPE`. |
184 | | #[cfg(target_os = "netbsd")] |
185 | | O_NOSIGPIPE; |
186 | | /// Obtain a file descriptor for low-level access. |
187 | | /// |
188 | | /// The file itself is not opened and other file operations will fail. |
189 | | #[cfg(any(linux_android, target_os = "redox", target_os = "freebsd", target_os = "fuchsia"))] |
190 | | O_PATH; |
191 | | /// Only allow reading. |
192 | | /// |
193 | | /// This should not be combined with `O_WRONLY` or `O_RDWR`. |
194 | | O_RDONLY; |
195 | | /// Allow both reading and writing. |
196 | | /// |
197 | | /// This should not be combined with `O_WRONLY` or `O_RDONLY`. |
198 | | O_RDWR; |
199 | | /// Similar to `O_DSYNC` but applies to `read`s instead. |
200 | | #[cfg(any(target_os = "linux", netbsdlike))] |
201 | | O_RSYNC; |
202 | | /// Open directory for search only. Skip search permission checks on |
203 | | /// later `openat()` calls using the obtained file descriptor. |
204 | | #[cfg(any( |
205 | | apple_targets, |
206 | | solarish, |
207 | | target_os = "netbsd", |
208 | | target_os = "freebsd", |
209 | | target_os = "fuchsia", |
210 | | target_os = "emscripten", |
211 | | target_os = "aix", |
212 | | target_os = "wasi" |
213 | | ))] |
214 | | O_SEARCH; |
215 | | /// Open with a shared file lock. |
216 | | #[cfg(any(bsd, target_os = "redox"))] |
217 | | O_SHLOCK; |
218 | | /// Implicitly follow each `write()` with an `fsync()`. |
219 | | #[cfg(not(target_os = "redox"))] |
220 | | O_SYNC; |
221 | | /// Create an unnamed temporary file. |
222 | | #[cfg(linux_android)] |
223 | | O_TMPFILE; |
224 | | /// Truncate an existing regular file to 0 length if it allows writing. |
225 | | O_TRUNC; |
226 | | /// Restore default TTY attributes. |
227 | | #[cfg(target_os = "freebsd")] |
228 | | O_TTY_INIT; |
229 | | /// Only allow writing. |
230 | | /// |
231 | | /// This should not be combined with `O_RDONLY` or `O_RDWR`. |
232 | | O_WRONLY; |
233 | | } |
234 | | ); |
235 | | |
236 | | feature! { |
237 | | #![feature = "fs"] |
238 | | |
239 | | /// open or create a file for reading, writing or executing |
240 | | /// |
241 | | /// # See Also |
242 | | /// [`open`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html) |
243 | | // The conversion is not identical on all operating systems. |
244 | | #[allow(clippy::useless_conversion)] |
245 | 0 | pub fn open<P: ?Sized + NixPath>( |
246 | 0 | path: &P, |
247 | 0 | oflag: OFlag, |
248 | 0 | mode: Mode, |
249 | 0 | ) -> Result<std::os::fd::OwnedFd> { |
250 | | use std::os::fd::FromRawFd; |
251 | | |
252 | 0 | let fd = path.with_nix_path(|cstr| unsafe { |
253 | 0 | libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) |
254 | 0 | })?; |
255 | 0 | Errno::result(fd)?; |
256 | | |
257 | | // SAFETY: |
258 | | // |
259 | | // `open(2)` should return a valid owned fd on success |
260 | 0 | Ok( unsafe { std::os::fd::OwnedFd::from_raw_fd(fd) } ) |
261 | 0 | } |
262 | | |
263 | | /// open or create a file for reading, writing or executing |
264 | | /// |
265 | | /// The `openat` function is equivalent to the [`open`] function except in the case where the path |
266 | | /// specifies a relative path. In that case, the file to be opened is determined relative to the |
267 | | /// directory associated with the file descriptor `dirfd`. |
268 | | /// |
269 | | /// # See Also |
270 | | /// [`openat`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/openat.html) |
271 | | // The conversion is not identical on all operating systems. |
272 | | #[allow(clippy::useless_conversion)] |
273 | | #[cfg(not(target_os = "redox"))] |
274 | 0 | pub fn openat<P: ?Sized + NixPath, Fd: std::os::fd::AsFd>( |
275 | 0 | dirfd: Fd, |
276 | 0 | path: &P, |
277 | 0 | oflag: OFlag, |
278 | 0 | mode: Mode, |
279 | 0 | ) -> Result<OwnedFd> { |
280 | | use std::os::fd::AsRawFd; |
281 | | use std::os::fd::FromRawFd; |
282 | | |
283 | 0 | let fd = path.with_nix_path(|cstr| unsafe { |
284 | 0 | libc::openat(dirfd.as_fd().as_raw_fd(), cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) |
285 | 0 | })?; |
286 | 0 | Errno::result(fd)?; |
287 | | |
288 | | // SAFETY: |
289 | | // |
290 | | // `openat(2)` should return a valid owned fd on success |
291 | 0 | Ok( unsafe { OwnedFd::from_raw_fd(fd) } ) |
292 | 0 | } |
293 | | |
294 | | cfg_if::cfg_if! { |
295 | | if #[cfg(target_os = "linux")] { |
296 | | libc_bitflags! { |
297 | | /// Path resolution flags. |
298 | | /// |
299 | | /// See [path resolution(7)](https://man7.org/linux/man-pages/man7/path_resolution.7.html) |
300 | | /// for details of the resolution process. |
301 | | pub struct ResolveFlag: libc::c_ulonglong { |
302 | | /// Do not permit the path resolution to succeed if any component of |
303 | | /// the resolution is not a descendant of the directory indicated by |
304 | | /// dirfd. This causes absolute symbolic links (and absolute values of |
305 | | /// pathname) to be rejected. |
306 | | RESOLVE_BENEATH; |
307 | | |
308 | | /// Treat the directory referred to by dirfd as the root directory |
309 | | /// while resolving pathname. |
310 | | RESOLVE_IN_ROOT; |
311 | | |
312 | | /// Disallow all magic-link resolution during path resolution. Magic |
313 | | /// links are symbolic link-like objects that are most notably found |
314 | | /// in proc(5); examples include `/proc/[pid]/exe` and `/proc/[pid]/fd/*`. |
315 | | /// |
316 | | /// See symlink(7) for more details. |
317 | | RESOLVE_NO_MAGICLINKS; |
318 | | |
319 | | /// Disallow resolution of symbolic links during path resolution. This |
320 | | /// option implies RESOLVE_NO_MAGICLINKS. |
321 | | RESOLVE_NO_SYMLINKS; |
322 | | |
323 | | /// Disallow traversal of mount points during path resolution (including |
324 | | /// all bind mounts). |
325 | | RESOLVE_NO_XDEV; |
326 | | } |
327 | | } |
328 | | |
329 | | /// Specifies how [`openat2()`] should open a pathname. |
330 | | /// |
331 | | /// # Reference |
332 | | /// |
333 | | /// * [Linux](https://man7.org/linux/man-pages/man2/open_how.2type.html) |
334 | | #[repr(transparent)] |
335 | | #[derive(Clone, Copy, Debug)] |
336 | | pub struct OpenHow(libc::open_how); |
337 | | |
338 | | impl OpenHow { |
339 | | /// Create a new zero-filled `open_how`. |
340 | 0 | pub fn new() -> Self { |
341 | | // safety: according to the man page, open_how MUST be zero-initialized |
342 | | // on init so that unknown fields are also zeroed. |
343 | 0 | Self(unsafe { |
344 | 0 | std::mem::MaybeUninit::zeroed().assume_init() |
345 | 0 | }) |
346 | 0 | } |
347 | | |
348 | | /// Set the open flags used to open a file, completely overwriting any |
349 | | /// existing flags. |
350 | 0 | pub fn flags(mut self, flags: OFlag) -> Self { |
351 | 0 | let flags = flags.bits() as libc::c_ulonglong; |
352 | 0 | self.0.flags = flags; |
353 | 0 | self |
354 | 0 | } |
355 | | |
356 | | /// Set the file mode new files will be created with, overwriting any |
357 | | /// existing flags. |
358 | 0 | pub fn mode(mut self, mode: Mode) -> Self { |
359 | 0 | let mode = mode.bits() as libc::c_ulonglong; |
360 | 0 | self.0.mode = mode; |
361 | 0 | self |
362 | 0 | } |
363 | | |
364 | | /// Set resolve flags, completely overwriting any existing flags. |
365 | | /// |
366 | | /// See [ResolveFlag] for more detail. |
367 | 0 | pub fn resolve(mut self, resolve: ResolveFlag) -> Self { |
368 | 0 | let resolve = resolve.bits(); |
369 | 0 | self.0.resolve = resolve; |
370 | 0 | self |
371 | 0 | } |
372 | | } |
373 | | |
374 | | // safety: default isn't derivable because libc::open_how must be zeroed |
375 | | impl Default for OpenHow { |
376 | 0 | fn default() -> Self { |
377 | 0 | Self::new() |
378 | 0 | } |
379 | | } |
380 | | |
381 | | /// Open or create a file for reading, writing or executing. |
382 | | /// |
383 | | /// `openat2` is an extension of the [`openat`] function that allows the caller |
384 | | /// to control how path resolution happens. |
385 | | /// |
386 | | /// # See also |
387 | | /// |
388 | | /// [openat2](https://man7.org/linux/man-pages/man2/openat2.2.html) |
389 | 0 | pub fn openat2<P: ?Sized + NixPath, Fd: std::os::fd::AsFd>( |
390 | 0 | dirfd: Fd, |
391 | 0 | path: &P, |
392 | 0 | mut how: OpenHow, |
393 | 0 | ) -> Result<OwnedFd> { |
394 | | use std::os::fd::AsRawFd; |
395 | | use std::os::fd::FromRawFd; |
396 | | |
397 | 0 | let fd = path.with_nix_path(|cstr| unsafe { |
398 | 0 | libc::syscall( |
399 | | libc::SYS_openat2, |
400 | 0 | dirfd.as_fd().as_raw_fd(), |
401 | 0 | cstr.as_ptr(), |
402 | 0 | &mut how as *mut OpenHow, |
403 | 0 | std::mem::size_of::<libc::open_how>(), |
404 | | ) |
405 | 0 | })? as RawFd; |
406 | 0 | Errno::result(fd)?; |
407 | | |
408 | | // SAFETY: |
409 | | // |
410 | | // `openat2(2)` should return a valid owned fd on success |
411 | 0 | Ok( unsafe { OwnedFd::from_raw_fd(fd) } ) |
412 | 0 | } |
413 | | } |
414 | | } |
415 | | |
416 | | /// Change the name of a file. |
417 | | /// |
418 | | /// The `renameat` function is equivalent to `rename` except in the case where either `old_path` |
419 | | /// or `new_path` specifies a relative path. In such cases, the file to be renamed (or the its new |
420 | | /// name, respectively) is located relative to `old_dirfd` or `new_dirfd`, respectively |
421 | | /// |
422 | | /// # See Also |
423 | | /// [`renameat`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html) |
424 | | #[cfg(not(target_os = "redox"))] |
425 | 0 | pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath, Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>( |
426 | 0 | old_dirfd: Fd1, |
427 | 0 | old_path: &P1, |
428 | 0 | new_dirfd: Fd2, |
429 | 0 | new_path: &P2, |
430 | 0 | ) -> Result<()> { |
431 | | use std::os::fd::AsRawFd; |
432 | | |
433 | 0 | let res = old_path.with_nix_path(|old_cstr| { |
434 | 0 | new_path.with_nix_path(|new_cstr| unsafe { |
435 | 0 | libc::renameat( |
436 | 0 | old_dirfd.as_fd().as_raw_fd(), |
437 | 0 | old_cstr.as_ptr(), |
438 | 0 | new_dirfd.as_fd().as_raw_fd(), |
439 | 0 | new_cstr.as_ptr(), |
440 | | ) |
441 | 0 | }) |
442 | 0 | })??; |
443 | 0 | Errno::result(res).map(drop) |
444 | 0 | } |
445 | | } |
446 | | |
447 | | #[cfg(all(target_os = "linux", target_env = "gnu"))] |
448 | | #[cfg(feature = "fs")] |
449 | | libc_bitflags! { |
450 | | /// Flags for use with [`renameat2`]. |
451 | | #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] |
452 | | pub struct RenameFlags: u32 { |
453 | | /// Atomically exchange `old_path` and `new_path`. |
454 | | RENAME_EXCHANGE; |
455 | | /// Don't overwrite `new_path` of the rename. Return an error if `new_path` already |
456 | | /// exists. |
457 | | RENAME_NOREPLACE; |
458 | | /// creates a "whiteout" object at the source of the rename at the same time as performing |
459 | | /// the rename. |
460 | | /// |
461 | | /// This operation makes sense only for overlay/union filesystem implementations. |
462 | | RENAME_WHITEOUT; |
463 | | } |
464 | | } |
465 | | |
466 | | feature! { |
467 | | #![feature = "fs"] |
468 | | /// Like [`renameat`], but with an additional `flags` argument. |
469 | | /// |
470 | | /// A `renameat2` call with an empty flags argument is equivalent to `renameat`. |
471 | | /// |
472 | | /// # See Also |
473 | | /// * [`rename`](https://man7.org/linux/man-pages/man2/rename.2.html) |
474 | | #[cfg(all(target_os = "linux", target_env = "gnu"))] |
475 | 0 | pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath, Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>( |
476 | 0 | old_dirfd: Fd1, |
477 | 0 | old_path: &P1, |
478 | 0 | new_dirfd: Fd2, |
479 | 0 | new_path: &P2, |
480 | 0 | flags: RenameFlags, |
481 | 0 | ) -> Result<()> { |
482 | | use std::os::fd::AsRawFd; |
483 | | |
484 | 0 | let res = old_path.with_nix_path(|old_cstr| { |
485 | 0 | new_path.with_nix_path(|new_cstr| unsafe { |
486 | 0 | libc::renameat2( |
487 | 0 | old_dirfd.as_fd().as_raw_fd(), |
488 | 0 | old_cstr.as_ptr(), |
489 | 0 | new_dirfd.as_fd().as_raw_fd(), |
490 | 0 | new_cstr.as_ptr(), |
491 | 0 | flags.bits(), |
492 | | ) |
493 | 0 | }) |
494 | 0 | })??; |
495 | 0 | Errno::result(res).map(drop) |
496 | 0 | } |
497 | | |
498 | 0 | fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> { |
499 | 0 | unsafe { v.set_len(len as usize) } |
500 | 0 | v.shrink_to_fit(); |
501 | 0 | Ok(OsString::from_vec(v.to_vec())) |
502 | 0 | } |
503 | | |
504 | | /// Read the symlink specified by `path` and `dirfd` and put the contents in `v`. |
505 | | /// Return the number of bytes placed in `v`. |
506 | | /// |
507 | | /// This function can call `readlink(2)` or `readlinkat(2)` depending on if `dirfd` |
508 | | /// is some, if it is, then `readlinkat(2)` is called, otherwise, call `readlink(2)`. |
509 | | /// |
510 | | /// # Safety |
511 | | /// |
512 | | /// This function is not I/O-safe considering it employs the `RawFd` type. |
513 | 0 | unsafe fn readlink_maybe_at<P: ?Sized + NixPath>( |
514 | 0 | dirfd: Option<RawFd>, |
515 | 0 | path: &P, |
516 | 0 | v: &mut Vec<u8>, |
517 | 0 | ) -> Result<libc::ssize_t> { |
518 | 0 | path.with_nix_path(|cstr| unsafe { |
519 | 0 | match dirfd { |
520 | | #[cfg(target_os = "redox")] |
521 | | Some(_) => unreachable!("redox does not have readlinkat(2)"), |
522 | | #[cfg(not(target_os = "redox"))] |
523 | 0 | Some(dirfd) => libc::readlinkat( |
524 | 0 | dirfd, |
525 | 0 | cstr.as_ptr(), |
526 | 0 | v.as_mut_ptr().cast(), |
527 | 0 | v.capacity() as size_t, |
528 | | ), |
529 | 0 | None => libc::readlink( |
530 | 0 | cstr.as_ptr(), |
531 | 0 | v.as_mut_ptr().cast(), |
532 | 0 | v.capacity() as size_t, |
533 | | ), |
534 | | } |
535 | 0 | }) |
536 | 0 | } |
537 | | |
538 | | /// The actual implementation of [`readlink(2)`] or [`readlinkat(2)`]. |
539 | | /// |
540 | | /// This function can call `readlink(2)` or `readlinkat(2)` depending on if `dirfd` |
541 | | /// is some, if it is, then `readlinkat(2)` is called, otherwise, call `readlink(2)`. |
542 | | /// |
543 | | /// # Safety |
544 | | /// |
545 | | /// This function is marked unsafe because it uses `RawFd`. |
546 | 0 | unsafe fn inner_readlink<P: ?Sized + NixPath>( |
547 | 0 | dirfd: Option<RawFd>, |
548 | 0 | path: &P, |
549 | 0 | ) -> Result<OsString> { |
550 | | #[cfg(not(target_os = "hurd"))] |
551 | | const PATH_MAX: usize = libc::PATH_MAX as usize; |
552 | | #[cfg(target_os = "hurd")] |
553 | | const PATH_MAX: usize = 1024; // Hurd does not define a hard limit, so try a guess first |
554 | 0 | let mut v = Vec::with_capacity(PATH_MAX); |
555 | | |
556 | | { |
557 | | // simple case: result is strictly less than `PATH_MAX` |
558 | | |
559 | | // SAFETY: |
560 | | // |
561 | | // If this call of `readlink_maybe_at()` is safe or not depends on the |
562 | | // usage of `unsafe fn inner_readlink()`. |
563 | 0 | let res = unsafe { readlink_maybe_at(dirfd, path, &mut v)? }; |
564 | 0 | let len = Errno::result(res)?; |
565 | 0 | debug_assert!(len >= 0); |
566 | 0 | if (len as usize) < v.capacity() { |
567 | 0 | return wrap_readlink_result(v, res); |
568 | 0 | } |
569 | | } |
570 | | |
571 | | // Uh oh, the result is too long... |
572 | | // Let's try to ask lstat how many bytes to allocate. |
573 | 0 | let mut try_size = { |
574 | 0 | let reported_size = match dirfd { |
575 | | #[cfg(target_os = "redox")] |
576 | | Some(_) => unreachable!("redox does not have readlinkat(2)"), |
577 | | #[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))] |
578 | 0 | Some(dirfd) => { |
579 | | // SAFETY: |
580 | | // |
581 | | // If this call of `borrow_raw()` is safe or not depends on the |
582 | | // usage of `unsafe fn inner_readlink()`. |
583 | 0 | let dirfd = unsafe { |
584 | 0 | std::os::fd::BorrowedFd::borrow_raw(dirfd) |
585 | | }; |
586 | 0 | let flags = if path.is_empty() { |
587 | 0 | AtFlags::AT_EMPTY_PATH |
588 | | } else { |
589 | 0 | AtFlags::empty() |
590 | | }; |
591 | 0 | super::sys::stat::fstatat( |
592 | 0 | dirfd, |
593 | 0 | path, |
594 | 0 | flags | AtFlags::AT_SYMLINK_NOFOLLOW, |
595 | | ) |
596 | | } |
597 | | #[cfg(not(any( |
598 | | linux_android, |
599 | | target_os = "redox", |
600 | | target_os = "freebsd", |
601 | | target_os = "hurd" |
602 | | )))] |
603 | | Some(dirfd) => { |
604 | | // SAFETY: |
605 | | // |
606 | | // If this call of `borrow_raw()` is safe or not depends on the |
607 | | // usage of `unsafe fn inner_readlink()`. |
608 | | let dirfd = unsafe { |
609 | | std::os::fd::BorrowedFd::borrow_raw(dirfd) |
610 | | }; |
611 | | super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW) |
612 | | }, |
613 | 0 | None => super::sys::stat::lstat(path), |
614 | | } |
615 | 0 | .map(|x| x.st_size) |
616 | 0 | .unwrap_or(0); |
617 | | |
618 | 0 | if reported_size > 0 { |
619 | | // Note: even if `lstat`'s apparently valid answer turns out to be |
620 | | // wrong, we will still read the full symlink no matter what. |
621 | 0 | reported_size as usize + 1 |
622 | | } else { |
623 | | // If lstat doesn't cooperate, or reports an error, be a little less |
624 | | // precise. |
625 | 0 | PATH_MAX.max(128) << 1 |
626 | | } |
627 | | }; |
628 | | |
629 | | loop { |
630 | | { |
631 | 0 | v.reserve_exact(try_size); |
632 | | // SAFETY: |
633 | | // |
634 | | // If this call of `readlink_maybe_at()` is safe or not depends on the |
635 | | // usage of `unsafe fn inner_readlink()`. |
636 | 0 | let res = unsafe { readlink_maybe_at(dirfd, path, &mut v)? }; |
637 | 0 | let len = Errno::result(res)?; |
638 | 0 | debug_assert!(len >= 0); |
639 | 0 | if (len as usize) < v.capacity() { |
640 | 0 | return wrap_readlink_result(v, res); |
641 | 0 | } |
642 | | } |
643 | | |
644 | | // Ugh! Still not big enough! |
645 | 0 | match try_size.checked_shl(1) { |
646 | 0 | Some(next_size) => try_size = next_size, |
647 | | // It's absurd that this would happen, but handle it sanely |
648 | | // anyway. |
649 | 0 | None => break Err(Errno::ENAMETOOLONG), |
650 | | } |
651 | | } |
652 | 0 | } |
653 | | |
654 | | /// Read value of a symbolic link |
655 | | /// |
656 | | /// # See Also |
657 | | /// * [`readlink`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html) |
658 | 0 | pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> { |
659 | | // argument `dirfd` should be `None` since we call it from `readlink()` |
660 | | // |
661 | | // Do NOT call it with `Some(AT_CWD)` as in that way, we are emulating |
662 | | // `readlink(2)` with `readlinkat(2)`, which will make us lose `readlink(2)` |
663 | | // on Redox. |
664 | | // |
665 | | // SAFETY: |
666 | | // |
667 | | // It is definitely safe because the argument involving `RawFd` is `None` |
668 | 0 | unsafe { inner_readlink(None, path) } |
669 | 0 | } |
670 | | |
671 | | /// Read value of a symbolic link. |
672 | | /// |
673 | | /// Equivalent to [`readlink` ] except for the case where `path` specifies a |
674 | | /// relative path, `path` will be interpreted relative to the path specified |
675 | | /// by `dirfd`. (Use [`AT_FDCWD`] to make it relative to the working directory). |
676 | | /// |
677 | | /// # See Also |
678 | | /// * [`readlink`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html) |
679 | | #[cfg(not(target_os = "redox"))] |
680 | 0 | pub fn readlinkat<Fd: std::os::fd::AsFd,P: ?Sized + NixPath>( |
681 | 0 | dirfd: Fd, |
682 | 0 | path: &P, |
683 | 0 | ) -> Result<OsString> { |
684 | | use std::os::fd::AsRawFd; |
685 | | |
686 | | // argument `dirfd` should be `Some` since we call it from `readlinkat()` |
687 | | // |
688 | | // SAFETY: |
689 | | // |
690 | | // The passed `RawFd` should be valid since it is borrowed from `Fd: AsFd`. |
691 | 0 | unsafe { inner_readlink(Some(dirfd.as_fd().as_raw_fd()), path) } |
692 | 0 | } |
693 | | } |
694 | | |
695 | | #[cfg(any(linux_android, target_os = "freebsd"))] |
696 | | #[cfg(feature = "fs")] |
697 | | libc_bitflags!( |
698 | | /// Additional flags for file sealing, which allows for limiting operations on a file. |
699 | | #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] |
700 | | pub struct SealFlag: c_int { |
701 | | /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`. |
702 | | F_SEAL_SEAL; |
703 | | /// The file cannot be reduced in size. |
704 | | F_SEAL_SHRINK; |
705 | | /// The size of the file cannot be increased. |
706 | | F_SEAL_GROW; |
707 | | /// The file contents cannot be modified. |
708 | | F_SEAL_WRITE; |
709 | | /// The file contents cannot be modified, except via shared writable mappings that were |
710 | | /// created prior to the seal being set. Since Linux 5.1. |
711 | | #[cfg(linux_android)] |
712 | | F_SEAL_FUTURE_WRITE; |
713 | | } |
714 | | ); |
715 | | |
716 | | #[cfg(feature = "fs")] |
717 | | libc_bitflags!( |
718 | | /// Additional configuration flags for `fcntl`'s `F_SETFD`. |
719 | | #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] |
720 | | pub struct FdFlag: c_int { |
721 | | /// The file descriptor will automatically be closed during a successful `execve(2)`. |
722 | | FD_CLOEXEC; |
723 | | } |
724 | | ); |
725 | | |
726 | | feature! { |
727 | | #![feature = "fs"] |
728 | | |
729 | | /// Commands for use with [`fcntl`]. |
730 | | #[cfg(not(target_os = "redox"))] |
731 | | #[derive(Debug, Eq, Hash, PartialEq)] |
732 | | #[non_exhaustive] |
733 | | pub enum FcntlArg<'a> { |
734 | | /// Duplicate the provided file descriptor |
735 | | F_DUPFD(RawFd), |
736 | | /// Duplicate the provided file descriptor and set the `FD_CLOEXEC` flag on it. |
737 | | F_DUPFD_CLOEXEC(RawFd), |
738 | | /// Get the close-on-exec flag associated with the file descriptor |
739 | | F_GETFD, |
740 | | /// Set the close-on-exec flag associated with the file descriptor |
741 | | F_SETFD(FdFlag), // FD_FLAGS |
742 | | /// Get descriptor status flags |
743 | | F_GETFL, |
744 | | /// Set descriptor status flags |
745 | | F_SETFL(OFlag), // O_NONBLOCK |
746 | | /// Set or clear a file segment lock |
747 | | F_SETLK(&'a libc::flock), |
748 | | /// Like [`F_SETLK`](FcntlArg::F_SETLK) except that if a shared or exclusive lock is blocked by |
749 | | /// other locks, the process waits until the request can be satisfied. |
750 | | F_SETLKW(&'a libc::flock), |
751 | | /// Get the first lock that blocks the lock description |
752 | | F_GETLK(&'a mut libc::flock), |
753 | | /// Acquire or release an open file description lock |
754 | | #[cfg(linux_android)] |
755 | | F_OFD_SETLK(&'a libc::flock), |
756 | | /// Like [`F_OFD_SETLK`](FcntlArg::F_OFD_SETLK) except that if a conflicting lock is held on |
757 | | /// the file, then wait for that lock to be released. |
758 | | #[cfg(linux_android)] |
759 | | F_OFD_SETLKW(&'a libc::flock), |
760 | | /// Determine whether it would be possible to create the given lock. If not, return details |
761 | | /// about one existing lock that would prevent it. |
762 | | #[cfg(linux_android)] |
763 | | F_OFD_GETLK(&'a mut libc::flock), |
764 | | /// Add seals to the file |
765 | | #[cfg(any( |
766 | | linux_android, |
767 | | target_os = "freebsd" |
768 | | ))] |
769 | | F_ADD_SEALS(SealFlag), |
770 | | /// Get seals associated with the file |
771 | | #[cfg(any( |
772 | | linux_android, |
773 | | target_os = "freebsd" |
774 | | ))] |
775 | | F_GET_SEALS, |
776 | | /// Asks the drive to flush all buffered data to permanent storage. |
777 | | #[cfg(apple_targets)] |
778 | | F_FULLFSYNC, |
779 | | /// fsync + issue barrier to drive |
780 | | #[cfg(apple_targets)] |
781 | | F_BARRIERFSYNC, |
782 | | /// Return the capacity of a pipe |
783 | | #[cfg(linux_android)] |
784 | | F_GETPIPE_SZ, |
785 | | /// Change the capacity of a pipe |
786 | | #[cfg(linux_android)] |
787 | | F_SETPIPE_SZ(c_int), |
788 | | /// Look up the path of an open file descriptor, if possible. |
789 | | #[cfg(any( |
790 | | target_os = "netbsd", |
791 | | target_os = "dragonfly", |
792 | | apple_targets, |
793 | | ))] |
794 | | F_GETPATH(&'a mut PathBuf), |
795 | | /// Look up the path of an open file descriptor, if possible. |
796 | | #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] |
797 | | F_KINFO(&'a mut PathBuf), |
798 | | /// Return the full path without firmlinks of the fd. |
799 | | #[cfg(apple_targets)] |
800 | | F_GETPATH_NOFIRMLINK(&'a mut PathBuf), |
801 | | /// Issue an advisory read async with no copy to user |
802 | | #[cfg(apple_targets)] |
803 | | F_RDADVISE(libc::radvisory), |
804 | | /// Turn read ahead off/on |
805 | | #[cfg(apple_targets)] |
806 | | F_RDAHEAD(bool), |
807 | | /// Pre-allocate storage with different policies on fd. |
808 | | /// Note that we want a mutable reference for the OUT |
809 | | /// fstore_t field fst_bytesalloc. |
810 | | #[cfg(apple_targets)] |
811 | | F_PREALLOCATE(&'a mut libc::fstore_t), |
812 | | #[cfg(apple_targets)] |
813 | | /// Get disk device information. In practice, |
814 | | /// only the file offset data is set. |
815 | | F_LOG2PHYS(&'a mut libc::off_t), |
816 | | #[cfg(apple_targets)] |
817 | | /// Get disk device information. In practice, |
818 | | /// only the file offset data is set. |
819 | | /// The difference with F_LOG2PHYS is the struct passed |
820 | | /// is used as both IN/OUT as both its l2p_devoffset and |
821 | | /// l2p_contigbytes can be used for more specific queries. |
822 | | F_LOG2PHYS_EXT(&'a mut libc::log2phys), |
823 | | /// Transfer any extra space in the file past the logical EOF |
824 | | /// (as previously allocated via F_PREALLOCATE) to another file. |
825 | | /// The other file is specified via a file descriptor as the lone extra argument. |
826 | | /// Both descriptors must reference regular files in the same volume. |
827 | | #[cfg(apple_targets)] |
828 | | F_TRANSFEREXTENTS(RawFd), |
829 | | /// Set or clear the read ahead (pre-fetch) amount for sequential access or |
830 | | /// disable it with 0 or to system default for any value < 0. |
831 | | /// It manages how the kernel caches file data. |
832 | | #[cfg(target_os = "freebsd")] |
833 | | F_READAHEAD(c_int), |
834 | | // TODO: Rest of flags |
835 | | } |
836 | | |
837 | | /// Commands for use with [`fcntl`]. |
838 | | #[cfg(target_os = "redox")] |
839 | | #[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] |
840 | | #[non_exhaustive] |
841 | | pub enum FcntlArg { |
842 | | /// Duplicate the provided file descriptor |
843 | | F_DUPFD(RawFd), |
844 | | /// Duplicate the provided file descriptor and set the `FD_CLOEXEC` flag on it. |
845 | | F_DUPFD_CLOEXEC(RawFd), |
846 | | /// Get the close-on-exec flag associated with the file descriptor |
847 | | F_GETFD, |
848 | | /// Set the close-on-exec flag associated with the file descriptor |
849 | | F_SETFD(FdFlag), // FD_FLAGS |
850 | | /// Get descriptor status flags |
851 | | F_GETFL, |
852 | | /// Set descriptor status flags |
853 | | F_SETFL(OFlag), // O_NONBLOCK |
854 | | } |
855 | | pub use self::FcntlArg::*; |
856 | | |
857 | | /// Perform various operations on open file descriptors. |
858 | | /// |
859 | | /// # See Also |
860 | | /// * [`fcntl`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html) |
861 | | // TODO: Figure out how to handle value fcntl returns |
862 | 0 | pub fn fcntl<Fd: std::os::fd::AsFd>(fd: Fd, arg: FcntlArg) -> Result<c_int> { |
863 | | use std::os::fd::AsRawFd; |
864 | | |
865 | 0 | let fd = fd.as_fd().as_raw_fd(); |
866 | 0 | let res = unsafe { |
867 | 0 | match arg { |
868 | 0 | F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd), |
869 | 0 | F_DUPFD_CLOEXEC(rawfd) => { |
870 | 0 | libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd) |
871 | | } |
872 | 0 | F_GETFD => libc::fcntl(fd, libc::F_GETFD), |
873 | 0 | F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()), |
874 | 0 | F_GETFL => libc::fcntl(fd, libc::F_GETFL), |
875 | 0 | F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()), |
876 | | #[cfg(not(target_os = "redox"))] |
877 | 0 | F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock), |
878 | | #[cfg(not(target_os = "redox"))] |
879 | 0 | F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock), |
880 | | #[cfg(not(target_os = "redox"))] |
881 | 0 | F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock), |
882 | | #[cfg(linux_android)] |
883 | 0 | F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock), |
884 | | #[cfg(linux_android)] |
885 | 0 | F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock), |
886 | | #[cfg(linux_android)] |
887 | 0 | F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock), |
888 | | #[cfg(any( |
889 | | linux_android, |
890 | | target_os = "freebsd" |
891 | | ))] |
892 | 0 | F_ADD_SEALS(flag) => { |
893 | 0 | libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()) |
894 | | } |
895 | | #[cfg(any( |
896 | | linux_android, |
897 | | target_os = "freebsd" |
898 | | ))] |
899 | 0 | F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS), |
900 | | #[cfg(apple_targets)] |
901 | | F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC), |
902 | | #[cfg(apple_targets)] |
903 | | F_BARRIERFSYNC => libc::fcntl(fd, libc::F_BARRIERFSYNC), |
904 | | #[cfg(linux_android)] |
905 | 0 | F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ), |
906 | | #[cfg(linux_android)] |
907 | 0 | F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size), |
908 | | #[cfg(any( |
909 | | target_os = "dragonfly", |
910 | | target_os = "netbsd", |
911 | | apple_targets, |
912 | | ))] |
913 | | F_GETPATH(path) => { |
914 | | let mut buffer = vec![0; libc::PATH_MAX as usize]; |
915 | | let res = libc::fcntl(fd, libc::F_GETPATH, buffer.as_mut_ptr()); |
916 | | let ok_res = Errno::result(res)?; |
917 | | let optr = CStr::from_bytes_until_nul(&buffer).unwrap(); |
918 | | *path = PathBuf::from(OsString::from(optr.to_str().unwrap())); |
919 | | return Ok(ok_res) |
920 | | }, |
921 | | #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] |
922 | | F_KINFO(path) => { |
923 | | let mut info: libc::kinfo_file = std::mem::zeroed(); |
924 | | info.kf_structsize = std::mem::size_of::<libc::kinfo_file>() as i32; |
925 | | let res = libc::fcntl(fd, libc::F_KINFO, &mut info); |
926 | | let ok_res = Errno::result(res)?; |
927 | | let p = info.kf_path; |
928 | | let u8_slice = slice::from_raw_parts(p.as_ptr().cast(), p.len()); |
929 | | let optr = CStr::from_bytes_until_nul(u8_slice).unwrap(); |
930 | | *path = PathBuf::from(OsString::from(optr.to_str().unwrap())); |
931 | | return Ok(ok_res) |
932 | | }, |
933 | | #[cfg(apple_targets)] |
934 | | F_GETPATH_NOFIRMLINK(path) => { |
935 | | let mut buffer = vec![0; libc::PATH_MAX as usize]; |
936 | | let res = libc::fcntl(fd, libc::F_GETPATH_NOFIRMLINK, buffer.as_mut_ptr()); |
937 | | let ok_res = Errno::result(res)?; |
938 | | let optr = CStr::from_bytes_until_nul(&buffer).unwrap(); |
939 | | *path = PathBuf::from(OsString::from(optr.to_str().unwrap())); |
940 | | return Ok(ok_res) |
941 | | }, |
942 | | #[cfg(apple_targets)] |
943 | | F_RDADVISE(rad) => { |
944 | | libc::fcntl(fd, libc::F_RDADVISE, &rad) |
945 | | }, |
946 | | #[cfg(apple_targets)] |
947 | | F_LOG2PHYS(offset) => { |
948 | | let mut info: libc::log2phys = std::mem::zeroed(); |
949 | | let res = libc::fcntl(fd, libc::F_LOG2PHYS, &mut info); |
950 | | let ok_res = Errno::result(res)?; |
951 | | *offset = info.l2p_devoffset; |
952 | | return Ok(ok_res) |
953 | | } |
954 | | #[cfg(apple_targets)] |
955 | | F_LOG2PHYS_EXT(info) => { |
956 | | libc::fcntl(fd, libc::F_LOG2PHYS_EXT, info) |
957 | | } |
958 | | #[cfg(apple_targets)] |
959 | | F_RDAHEAD(on) => { |
960 | | let val = if on { 1 } else { 0 }; |
961 | | libc::fcntl(fd, libc::F_RDAHEAD, val) |
962 | | }, |
963 | | #[cfg(apple_targets)] |
964 | | F_PREALLOCATE(st) => { |
965 | | libc::fcntl(fd, libc::F_PREALLOCATE, st) |
966 | | }, |
967 | | #[cfg(apple_targets)] |
968 | | F_TRANSFEREXTENTS(rawfd) => { |
969 | | libc::fcntl(fd, libc::F_TRANSFEREXTENTS, rawfd) |
970 | | }, |
971 | | #[cfg(target_os = "freebsd")] |
972 | | F_READAHEAD(val) => { |
973 | | libc::fcntl(fd, libc::F_READAHEAD, val) |
974 | | }, |
975 | | } |
976 | | }; |
977 | | |
978 | 0 | Errno::result(res) |
979 | 0 | } |
980 | | |
981 | | /// Operations for use with [`Flock::lock`]. |
982 | | #[cfg(not(any(target_os = "redox", target_os = "solaris")))] |
983 | | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
984 | | #[non_exhaustive] |
985 | | pub enum FlockArg { |
986 | | /// shared file lock |
987 | | LockShared, |
988 | | /// exclusive file lock |
989 | | LockExclusive, |
990 | | /// Unlock file |
991 | | Unlock, |
992 | | /// Shared lock. Do not block when locking. |
993 | | LockSharedNonblock, |
994 | | /// Exclusive lock. Do not block when locking. |
995 | | LockExclusiveNonblock, |
996 | | #[allow(missing_docs)] |
997 | | #[deprecated(since = "0.28.0", note = "Use FlockArg::Unlock instead")] |
998 | | UnlockNonblock, |
999 | | } |
1000 | | |
1001 | | #[allow(missing_docs)] |
1002 | | #[cfg(not(any(target_os = "redox", target_os = "solaris")))] |
1003 | | #[deprecated(since = "0.28.0", note = "`fcntl::Flock` should be used instead.")] |
1004 | 0 | pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> { |
1005 | | use self::FlockArg::*; |
1006 | | |
1007 | 0 | let res = unsafe { |
1008 | 0 | match arg { |
1009 | 0 | LockShared => libc::flock(fd, libc::LOCK_SH), |
1010 | 0 | LockExclusive => libc::flock(fd, libc::LOCK_EX), |
1011 | 0 | Unlock => libc::flock(fd, libc::LOCK_UN), |
1012 | | LockSharedNonblock => { |
1013 | 0 | libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB) |
1014 | | } |
1015 | | LockExclusiveNonblock => { |
1016 | 0 | libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) |
1017 | | } |
1018 | | #[allow(deprecated)] |
1019 | 0 | UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB), |
1020 | | } |
1021 | | }; |
1022 | | |
1023 | 0 | Errno::result(res).map(drop) |
1024 | 0 | } |
1025 | | |
1026 | | /// Represents valid types for flock. |
1027 | | /// |
1028 | | /// # Safety |
1029 | | /// Types implementing this must not be `Clone`. |
1030 | | #[cfg(not(any(target_os = "redox", target_os = "solaris")))] |
1031 | | pub unsafe trait Flockable: std::os::fd::AsRawFd {} |
1032 | | |
1033 | | /// Represents an owned flock, which unlocks on drop. |
1034 | | /// |
1035 | | /// See [flock(2)](https://linux.die.net/man/2/flock) for details on locking semantics. |
1036 | | #[cfg(not(any(target_os = "redox", target_os = "solaris")))] |
1037 | | #[derive(Debug)] |
1038 | | pub struct Flock<T: Flockable>(T); |
1039 | | |
1040 | | #[cfg(not(any(target_os = "redox", target_os = "solaris")))] |
1041 | | impl<T: Flockable> Drop for Flock<T> { |
1042 | 0 | fn drop(&mut self) { |
1043 | 0 | let res = Errno::result(unsafe { libc::flock(self.0.as_raw_fd(), libc::LOCK_UN) }); |
1044 | 0 | if res.is_err() && !std::thread::panicking() { |
1045 | 0 | panic!("Failed to remove flock: {}", res.unwrap_err()); |
1046 | 0 | } |
1047 | 0 | } |
1048 | | } |
1049 | | |
1050 | | #[cfg(not(any(target_os = "redox", target_os = "solaris")))] |
1051 | | impl<T: Flockable> Deref for Flock<T> { |
1052 | | type Target = T; |
1053 | | |
1054 | 0 | fn deref(&self) -> &Self::Target { |
1055 | 0 | &self.0 |
1056 | 0 | } |
1057 | | } |
1058 | | #[cfg(not(any(target_os = "redox", target_os = "solaris")))] |
1059 | | impl<T: Flockable> DerefMut for Flock<T> { |
1060 | 0 | fn deref_mut(&mut self) -> &mut Self::Target { |
1061 | 0 | &mut self.0 |
1062 | 0 | } |
1063 | | } |
1064 | | |
1065 | | #[cfg(not(any(target_os = "redox", target_os = "solaris")))] |
1066 | | impl<T: Flockable> Flock<T> { |
1067 | | /// Obtain a/an flock. |
1068 | | /// |
1069 | | /// # Example |
1070 | | /// ``` |
1071 | | /// # use std::io::Write; |
1072 | | /// # use std::fs::File; |
1073 | | /// # use nix::fcntl::{Flock, FlockArg}; |
1074 | | /// # fn do_stuff(file: File) { |
1075 | | /// let mut file = match Flock::lock(file, FlockArg::LockExclusive) { |
1076 | | /// Ok(l) => l, |
1077 | | /// Err(_) => return, |
1078 | | /// }; |
1079 | | /// |
1080 | | /// // Do stuff |
1081 | | /// let data = "Foo bar"; |
1082 | | /// _ = file.write(data.as_bytes()); |
1083 | | /// _ = file.sync_data(); |
1084 | | /// # } |
1085 | 0 | pub fn lock(t: T, args: FlockArg) -> std::result::Result<Self, (T, Errno)> { |
1086 | 0 | let flags = match args { |
1087 | 0 | FlockArg::LockShared => libc::LOCK_SH, |
1088 | 0 | FlockArg::LockExclusive => libc::LOCK_EX, |
1089 | 0 | FlockArg::LockSharedNonblock => libc::LOCK_SH | libc::LOCK_NB, |
1090 | 0 | FlockArg::LockExclusiveNonblock => libc::LOCK_EX | libc::LOCK_NB, |
1091 | | #[allow(deprecated)] |
1092 | 0 | FlockArg::Unlock | FlockArg::UnlockNonblock => return Err((t, Errno::EINVAL)), |
1093 | | }; |
1094 | 0 | match Errno::result(unsafe { libc::flock(t.as_raw_fd(), flags) }) { |
1095 | 0 | Ok(_) => Ok(Self(t)), |
1096 | 0 | Err(errno) => Err((t, errno)), |
1097 | | } |
1098 | 0 | } |
1099 | | |
1100 | | /// Remove the lock and return the object wrapped within. |
1101 | | /// |
1102 | | /// # Example |
1103 | | /// ``` |
1104 | | /// # use std::fs::File; |
1105 | | /// # use nix::fcntl::{Flock, FlockArg}; |
1106 | | /// fn do_stuff(file: File) -> nix::Result<()> { |
1107 | | /// let mut lock = match Flock::lock(file, FlockArg::LockExclusive) { |
1108 | | /// Ok(l) => l, |
1109 | | /// Err((_,e)) => return Err(e), |
1110 | | /// }; |
1111 | | /// |
1112 | | /// // Do critical section |
1113 | | /// |
1114 | | /// // Unlock |
1115 | | /// let file = match lock.unlock() { |
1116 | | /// Ok(f) => f, |
1117 | | /// Err((_, e)) => return Err(e), |
1118 | | /// }; |
1119 | | /// |
1120 | | /// // Do anything else |
1121 | | /// |
1122 | | /// Ok(()) |
1123 | | /// } |
1124 | 0 | pub fn unlock(self) -> std::result::Result<T, (Self, Errno)> { |
1125 | 0 | let inner = unsafe { match Errno::result(libc::flock(self.0.as_raw_fd(), libc::LOCK_UN)) { |
1126 | 0 | Ok(_) => std::ptr::read(&self.0), |
1127 | 0 | Err(errno) => return Err((self, errno)), |
1128 | | }}; |
1129 | | |
1130 | 0 | std::mem::forget(self); |
1131 | 0 | Ok(inner) |
1132 | 0 | } |
1133 | | |
1134 | | /// Relock the file. This can upgrade or downgrade the lock type. |
1135 | | /// |
1136 | | /// # Example |
1137 | | /// ``` |
1138 | | /// # use std::fs::File; |
1139 | | /// # use nix::fcntl::{Flock, FlockArg}; |
1140 | | /// # use tempfile::tempfile; |
1141 | | /// let f: std::fs::File = tempfile().unwrap(); |
1142 | | /// let locked_file = Flock::lock(f, FlockArg::LockExclusive).unwrap(); |
1143 | | /// // Do stuff, then downgrade the lock |
1144 | | /// locked_file.relock(FlockArg::LockShared).unwrap(); |
1145 | | /// ``` |
1146 | 0 | pub fn relock(&self, arg: FlockArg) -> Result<()> { |
1147 | 0 | let flags = match arg { |
1148 | 0 | FlockArg::LockShared => libc::LOCK_SH, |
1149 | 0 | FlockArg::LockExclusive => libc::LOCK_EX, |
1150 | 0 | FlockArg::LockSharedNonblock => libc::LOCK_SH | libc::LOCK_NB, |
1151 | 0 | FlockArg::LockExclusiveNonblock => libc::LOCK_EX | libc::LOCK_NB, |
1152 | | #[allow(deprecated)] |
1153 | 0 | FlockArg::Unlock | FlockArg::UnlockNonblock => return Err(Errno::EINVAL), |
1154 | | }; |
1155 | 0 | Errno::result(unsafe { libc::flock(self.as_raw_fd(), flags) }).map(drop) |
1156 | 0 | } |
1157 | | } |
1158 | | |
1159 | | // Safety: `File` is not [std::clone::Clone]. |
1160 | | #[cfg(not(any(target_os = "redox", target_os = "solaris")))] |
1161 | | unsafe impl Flockable for std::fs::File {} |
1162 | | |
1163 | | // Safety: `OwnedFd` is not [std::clone::Clone]. |
1164 | | #[cfg(not(any(target_os = "redox", target_os = "solaris")))] |
1165 | | unsafe impl Flockable for OwnedFd {} |
1166 | | } |
1167 | | |
1168 | | #[cfg(linux_android)] |
1169 | | #[cfg(feature = "zerocopy")] |
1170 | | libc_bitflags! { |
1171 | | /// Additional flags to `splice` and friends. |
1172 | | #[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))] |
1173 | | pub struct SpliceFFlags: c_uint { |
1174 | | /// Request that pages be moved instead of copied. |
1175 | | /// |
1176 | | /// Not applicable to `vmsplice`. |
1177 | | SPLICE_F_MOVE; |
1178 | | /// Do not block on I/O. |
1179 | | SPLICE_F_NONBLOCK; |
1180 | | /// Hint that more data will be coming in a subsequent splice. |
1181 | | /// |
1182 | | /// Not applicable to `vmsplice`. |
1183 | | SPLICE_F_MORE; |
1184 | | /// Gift the user pages to the kernel. |
1185 | | /// |
1186 | | /// Not applicable to `splice`. |
1187 | | SPLICE_F_GIFT; |
1188 | | } |
1189 | | } |
1190 | | |
1191 | | feature! { |
1192 | | #![feature = "zerocopy"] |
1193 | | |
1194 | | /// Copy a range of data from one file to another |
1195 | | /// |
1196 | | /// The `copy_file_range` system call performs an in-kernel copy between |
1197 | | /// file descriptors `fd_in` and `fd_out` without the additional cost of |
1198 | | /// transferring data from the kernel to user space and back again. There may be |
1199 | | /// additional optimizations for specific file systems. It copies up to `len` |
1200 | | /// bytes of data from file descriptor `fd_in` to file descriptor `fd_out`, |
1201 | | /// overwriting any data that exists within the requested range of the target |
1202 | | /// file. |
1203 | | /// |
1204 | | /// If the `off_in` and/or `off_out` arguments are used, the values |
1205 | | /// will be mutated to reflect the new position within the file after |
1206 | | /// copying. If they are not used, the relevant file descriptors will be seeked |
1207 | | /// to the new position. |
1208 | | /// |
1209 | | /// On successful completion the number of bytes actually copied will be |
1210 | | /// returned. |
1211 | | // Note: FreeBSD defines the offset argument as "off_t". Linux and Android |
1212 | | // define it as "loff_t". But on both OSes, on all supported platforms, those |
1213 | | // are 64 bits. So Nix uses i64 to make the docs simple and consistent. |
1214 | | #[cfg(any(linux_android, target_os = "freebsd"))] |
1215 | | pub fn copy_file_range<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>( |
1216 | | fd_in: Fd1, |
1217 | | off_in: Option<&mut i64>, |
1218 | | fd_out: Fd2, |
1219 | | off_out: Option<&mut i64>, |
1220 | | len: usize, |
1221 | | ) -> Result<usize> { |
1222 | | use std::os::fd::AsRawFd; |
1223 | | |
1224 | | let off_in = off_in |
1225 | | .map(|offset| offset as *mut i64) |
1226 | | .unwrap_or(ptr::null_mut()); |
1227 | | let off_out = off_out |
1228 | | .map(|offset| offset as *mut i64) |
1229 | | .unwrap_or(ptr::null_mut()); |
1230 | | |
1231 | | cfg_if::cfg_if! { |
1232 | | if #[cfg(target_os = "freebsd")] { |
1233 | | let ret = unsafe { |
1234 | | libc::copy_file_range( |
1235 | | fd_in.as_fd().as_raw_fd(), |
1236 | | off_in, |
1237 | | fd_out.as_fd().as_raw_fd(), |
1238 | | off_out, |
1239 | | len, |
1240 | | 0, |
1241 | | ) |
1242 | | }; |
1243 | | } else { |
1244 | | // May Linux distros still don't include copy_file_range in their |
1245 | | // libc implementations, so we need to make a direct syscall. |
1246 | | let ret = unsafe { |
1247 | | libc::syscall( |
1248 | | libc::SYS_copy_file_range, |
1249 | | fd_in.as_fd().as_raw_fd(), |
1250 | | off_in, |
1251 | | fd_out.as_fd().as_raw_fd(), |
1252 | | off_out, |
1253 | | len, |
1254 | | 0, |
1255 | | ) |
1256 | | }; |
1257 | | } |
1258 | | } |
1259 | | Errno::result(ret).map(|r| r as usize) |
1260 | | } |
1261 | | |
1262 | | /// Splice data to/from a pipe |
1263 | | /// |
1264 | | /// # See Also |
1265 | | /// *[`splice`](https://man7.org/linux/man-pages/man2/splice.2.html) |
1266 | | #[cfg(linux_android)] |
1267 | | pub fn splice<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>( |
1268 | | fd_in: Fd1, |
1269 | | off_in: Option<&mut libc::loff_t>, |
1270 | | fd_out: Fd2, |
1271 | | off_out: Option<&mut libc::loff_t>, |
1272 | | len: usize, |
1273 | | flags: SpliceFFlags, |
1274 | | ) -> Result<usize> { |
1275 | | use std::os::fd::AsRawFd; |
1276 | | |
1277 | | let off_in = off_in |
1278 | | .map(|offset| offset as *mut libc::loff_t) |
1279 | | .unwrap_or(ptr::null_mut()); |
1280 | | let off_out = off_out |
1281 | | .map(|offset| offset as *mut libc::loff_t) |
1282 | | .unwrap_or(ptr::null_mut()); |
1283 | | |
1284 | | let ret = unsafe { |
1285 | | libc::splice(fd_in.as_fd().as_raw_fd(), off_in, fd_out.as_fd().as_raw_fd(), off_out, len, flags.bits()) |
1286 | | }; |
1287 | | Errno::result(ret).map(|r| r as usize) |
1288 | | } |
1289 | | |
1290 | | /// Duplicate pipe content |
1291 | | /// |
1292 | | /// # See Also |
1293 | | /// *[`tee`](https://man7.org/linux/man-pages/man2/tee.2.html) |
1294 | | #[cfg(linux_android)] |
1295 | | pub fn tee<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>( |
1296 | | fd_in: Fd1, |
1297 | | fd_out: Fd2, |
1298 | | len: usize, |
1299 | | flags: SpliceFFlags, |
1300 | | ) -> Result<usize> { |
1301 | | use std::os::fd::AsRawFd; |
1302 | | |
1303 | | let ret = unsafe { libc::tee(fd_in.as_fd().as_raw_fd(), fd_out.as_fd().as_raw_fd(), len, flags.bits()) }; |
1304 | | Errno::result(ret).map(|r| r as usize) |
1305 | | } |
1306 | | |
1307 | | /// Splice user pages to/from a pipe |
1308 | | /// |
1309 | | /// # See Also |
1310 | | /// *[`vmsplice`](https://man7.org/linux/man-pages/man2/vmsplice.2.html) |
1311 | | #[cfg(linux_android)] |
1312 | | pub fn vmsplice<F: std::os::fd::AsFd>( |
1313 | | fd: F, |
1314 | | iov: &[std::io::IoSlice<'_>], |
1315 | | flags: SpliceFFlags, |
1316 | | ) -> Result<usize> { |
1317 | | use std::os::fd::AsRawFd; |
1318 | | |
1319 | | let ret = unsafe { |
1320 | | libc::vmsplice( |
1321 | | fd.as_fd().as_raw_fd(), |
1322 | | iov.as_ptr().cast(), |
1323 | | iov.len(), |
1324 | | flags.bits(), |
1325 | | ) |
1326 | | }; |
1327 | | Errno::result(ret).map(|r| r as usize) |
1328 | | } |
1329 | | } |
1330 | | |
1331 | | #[cfg(target_os = "linux")] |
1332 | | #[cfg(feature = "fs")] |
1333 | | libc_bitflags!( |
1334 | | /// Mode argument flags for fallocate determining operation performed on a given range. |
1335 | | #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] |
1336 | | pub struct FallocateFlags: c_int { |
1337 | | /// File size is not changed. |
1338 | | /// |
1339 | | /// offset + len can be greater than file size. |
1340 | | FALLOC_FL_KEEP_SIZE; |
1341 | | /// Deallocates space by creating a hole. |
1342 | | /// |
1343 | | /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes. |
1344 | | FALLOC_FL_PUNCH_HOLE; |
1345 | | /// Removes byte range from a file without leaving a hole. |
1346 | | /// |
1347 | | /// Byte range to collapse starts at offset and continues for len bytes. |
1348 | | FALLOC_FL_COLLAPSE_RANGE; |
1349 | | /// Zeroes space in specified byte range. |
1350 | | /// |
1351 | | /// Byte range starts at offset and continues for len bytes. |
1352 | | FALLOC_FL_ZERO_RANGE; |
1353 | | /// Increases file space by inserting a hole within the file size. |
1354 | | /// |
1355 | | /// Does not overwrite existing data. Hole starts at offset and continues for len bytes. |
1356 | | FALLOC_FL_INSERT_RANGE; |
1357 | | /// Shared file data extants are made private to the file. |
1358 | | /// |
1359 | | /// Guarantees that a subsequent write will not fail due to lack of space. |
1360 | | FALLOC_FL_UNSHARE_RANGE; |
1361 | | } |
1362 | | ); |
1363 | | |
1364 | | feature! { |
1365 | | #![feature = "fs"] |
1366 | | |
1367 | | /// Manipulates file space. |
1368 | | /// |
1369 | | /// Allows the caller to directly manipulate the allocated disk space for the |
1370 | | /// file referred to by fd. |
1371 | | #[cfg(target_os = "linux")] |
1372 | | #[cfg(feature = "fs")] |
1373 | 0 | pub fn fallocate<Fd: std::os::fd::AsFd>( |
1374 | 0 | fd: Fd, |
1375 | 0 | mode: FallocateFlags, |
1376 | 0 | offset: libc::off_t, |
1377 | 0 | len: libc::off_t, |
1378 | 0 | ) -> Result<()> { |
1379 | | use std::os::fd::AsRawFd; |
1380 | | |
1381 | 0 | let res = unsafe { libc::fallocate(fd.as_fd().as_raw_fd(), mode.bits(), offset, len) }; |
1382 | 0 | Errno::result(res).map(drop) |
1383 | 0 | } |
1384 | | |
1385 | | /// Argument to [`fspacectl`] describing the range to zero. The first member is |
1386 | | /// the file offset, and the second is the length of the region. |
1387 | | #[cfg(any(target_os = "freebsd"))] |
1388 | | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
1389 | | pub struct SpacectlRange(pub libc::off_t, pub libc::off_t); |
1390 | | |
1391 | | #[cfg(any(target_os = "freebsd"))] |
1392 | | impl SpacectlRange { |
1393 | | /// Is the range empty? |
1394 | | /// |
1395 | | /// After a successful call to [`fspacectl`], A value of `true` for `SpacectlRange::is_empty` |
1396 | | /// indicates that the operation is complete. |
1397 | | #[inline] |
1398 | | pub fn is_empty(&self) -> bool { |
1399 | | self.1 == 0 |
1400 | | } |
1401 | | |
1402 | | /// Remaining length of the range |
1403 | | #[inline] |
1404 | | pub fn len(&self) -> libc::off_t { |
1405 | | self.1 |
1406 | | } |
1407 | | |
1408 | | /// Next file offset to operate on |
1409 | | #[inline] |
1410 | | pub fn offset(&self) -> libc::off_t { |
1411 | | self.0 |
1412 | | } |
1413 | | } |
1414 | | |
1415 | | /// Punch holes in a file. |
1416 | | /// |
1417 | | /// `fspacectl` instructs the file system to deallocate a portion of a file. |
1418 | | /// After a successful operation, this region of the file will return all zeroes |
1419 | | /// if read. If the file system supports deallocation, then it may free the |
1420 | | /// underlying storage, too. |
1421 | | /// |
1422 | | /// # Arguments |
1423 | | /// |
1424 | | /// - `fd` - File to operate on |
1425 | | /// - `range.0` - File offset at which to begin deallocation |
1426 | | /// - `range.1` - Length of the region to deallocate |
1427 | | /// |
1428 | | /// # Returns |
1429 | | /// |
1430 | | /// The operation may deallocate less than the entire requested region. On |
1431 | | /// success, it returns the region that still remains to be deallocated. The |
1432 | | /// caller should loop until the returned region is empty. |
1433 | | /// |
1434 | | /// # Example |
1435 | | /// |
1436 | | #[cfg_attr(fbsd14, doc = " ```")] |
1437 | | #[cfg_attr(not(fbsd14), doc = " ```no_run")] |
1438 | | /// # use std::io::Write; |
1439 | | /// # use std::os::unix::fs::FileExt; |
1440 | | /// # use std::os::unix::io::AsRawFd; |
1441 | | /// # use nix::fcntl::*; |
1442 | | /// # use tempfile::tempfile; |
1443 | | /// const INITIAL: &[u8] = b"0123456789abcdef"; |
1444 | | /// let mut f = tempfile().unwrap(); |
1445 | | /// f.write_all(INITIAL).unwrap(); |
1446 | | /// let mut range = SpacectlRange(3, 6); |
1447 | | /// while (!range.is_empty()) { |
1448 | | /// range = fspacectl(&f, range).unwrap(); |
1449 | | /// } |
1450 | | /// let mut buf = vec![0; INITIAL.len()]; |
1451 | | /// f.read_exact_at(&mut buf, 0).unwrap(); |
1452 | | /// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef"); |
1453 | | /// ``` |
1454 | | #[cfg(target_os = "freebsd")] |
1455 | | #[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined |
1456 | | pub fn fspacectl<Fd: std::os::fd::AsFd>(fd: Fd, range: SpacectlRange) -> Result<SpacectlRange> { |
1457 | | use std::os::fd::AsRawFd; |
1458 | | |
1459 | | let mut rqsr = libc::spacectl_range { |
1460 | | r_offset: range.0, |
1461 | | r_len: range.1, |
1462 | | }; |
1463 | | let res = unsafe { |
1464 | | libc::fspacectl( |
1465 | | fd.as_fd().as_raw_fd(), |
1466 | | libc::SPACECTL_DEALLOC, // Only one command is supported ATM |
1467 | | &rqsr, |
1468 | | 0, // No flags are currently supported |
1469 | | &mut rqsr, |
1470 | | ) |
1471 | | }; |
1472 | | Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len)) |
1473 | | } |
1474 | | |
1475 | | /// Like [`fspacectl`], but will never return incomplete. |
1476 | | /// |
1477 | | /// # Arguments |
1478 | | /// |
1479 | | /// - `fd` - File to operate on |
1480 | | /// - `offset` - File offset at which to begin deallocation |
1481 | | /// - `len` - Length of the region to deallocate |
1482 | | /// |
1483 | | /// # Returns |
1484 | | /// |
1485 | | /// Returns `()` on success. On failure, the region may or may not be partially |
1486 | | /// deallocated. |
1487 | | /// |
1488 | | /// # Example |
1489 | | /// |
1490 | | #[cfg_attr(fbsd14, doc = " ```")] |
1491 | | #[cfg_attr(not(fbsd14), doc = " ```no_run")] |
1492 | | /// # use std::io::Write; |
1493 | | /// # use std::os::unix::fs::FileExt; |
1494 | | /// # use std::os::unix::io::AsRawFd; |
1495 | | /// # use nix::fcntl::*; |
1496 | | /// # use tempfile::tempfile; |
1497 | | /// const INITIAL: &[u8] = b"0123456789abcdef"; |
1498 | | /// let mut f = tempfile().unwrap(); |
1499 | | /// f.write_all(INITIAL).unwrap(); |
1500 | | /// fspacectl_all(&f, 3, 6).unwrap(); |
1501 | | /// let mut buf = vec![0; INITIAL.len()]; |
1502 | | /// f.read_exact_at(&mut buf, 0).unwrap(); |
1503 | | /// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef"); |
1504 | | /// ``` |
1505 | | #[cfg(target_os = "freebsd")] |
1506 | | #[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined |
1507 | | pub fn fspacectl_all<Fd: std::os::fd::AsFd>( |
1508 | | fd: Fd, |
1509 | | offset: libc::off_t, |
1510 | | len: libc::off_t, |
1511 | | ) -> Result<()> { |
1512 | | use std::os::fd::AsRawFd; |
1513 | | |
1514 | | let mut rqsr = libc::spacectl_range { |
1515 | | r_offset: offset, |
1516 | | r_len: len, |
1517 | | }; |
1518 | | while rqsr.r_len > 0 { |
1519 | | let res = unsafe { |
1520 | | libc::fspacectl( |
1521 | | fd.as_fd().as_raw_fd(), |
1522 | | libc::SPACECTL_DEALLOC, // Only one command is supported ATM |
1523 | | &rqsr, |
1524 | | 0, // No flags are currently supported |
1525 | | &mut rqsr, |
1526 | | ) |
1527 | | }; |
1528 | | Errno::result(res)?; |
1529 | | } |
1530 | | Ok(()) |
1531 | | } |
1532 | | |
1533 | | #[cfg(any( |
1534 | | linux_android, |
1535 | | target_os = "emscripten", |
1536 | | target_os = "fuchsia", |
1537 | | target_os = "wasi", |
1538 | | target_env = "uclibc", |
1539 | | target_os = "freebsd" |
1540 | | ))] |
1541 | | mod posix_fadvise { |
1542 | | use crate::errno::Errno; |
1543 | | use crate::Result; |
1544 | | |
1545 | | #[cfg(feature = "fs")] |
1546 | | libc_enum! { |
1547 | | /// The specific advice provided to [`posix_fadvise`]. |
1548 | | #[repr(i32)] |
1549 | | #[non_exhaustive] |
1550 | | #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] |
1551 | | pub enum PosixFadviseAdvice { |
1552 | | /// Revert to the default data access behavior. |
1553 | | POSIX_FADV_NORMAL, |
1554 | | /// The file data will be accessed sequentially. |
1555 | | POSIX_FADV_SEQUENTIAL, |
1556 | | /// A hint that file data will be accessed randomly, and prefetching is likely not |
1557 | | /// advantageous. |
1558 | | POSIX_FADV_RANDOM, |
1559 | | /// The specified data will only be accessed once and then not reused. |
1560 | | POSIX_FADV_NOREUSE, |
1561 | | /// The specified data will be accessed in the near future. |
1562 | | POSIX_FADV_WILLNEED, |
1563 | | /// The specified data will not be accessed in the near future. |
1564 | | POSIX_FADV_DONTNEED, |
1565 | | } |
1566 | | } |
1567 | | |
1568 | | feature! { |
1569 | | #![feature = "fs"] |
1570 | | /// Allows a process to describe to the system its data access behavior for an open file |
1571 | | /// descriptor. |
1572 | | /// |
1573 | | /// # See Also |
1574 | | /// * [`posix_fadvise`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html) |
1575 | 0 | pub fn posix_fadvise<Fd: std::os::fd::AsFd>( |
1576 | 0 | fd: Fd, |
1577 | 0 | offset: libc::off_t, |
1578 | 0 | len: libc::off_t, |
1579 | 0 | advice: PosixFadviseAdvice, |
1580 | 0 | ) -> Result<()> { |
1581 | | use std::os::fd::AsRawFd; |
1582 | | |
1583 | 0 | let res = unsafe { libc::posix_fadvise(fd.as_fd().as_raw_fd(), offset, len, advice as libc::c_int) }; |
1584 | | |
1585 | 0 | if res == 0 { |
1586 | 0 | Ok(()) |
1587 | | } else { |
1588 | 0 | Err(Errno::from_raw(res)) |
1589 | | } |
1590 | 0 | } |
1591 | | } |
1592 | | } |
1593 | | |
1594 | | /// Pre-allocate storage for a range in a file |
1595 | | /// |
1596 | | /// # See Also |
1597 | | /// * [`posix_fallocate`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fallocate.html) |
1598 | | #[cfg(any( |
1599 | | linux_android, |
1600 | | freebsdlike, |
1601 | | target_os = "emscripten", |
1602 | | target_os = "fuchsia", |
1603 | | target_os = "wasi", |
1604 | | ))] |
1605 | 0 | pub fn posix_fallocate<Fd: std::os::fd::AsFd>( |
1606 | 0 | fd: Fd, |
1607 | 0 | offset: libc::off_t, |
1608 | 0 | len: libc::off_t, |
1609 | 0 | ) -> Result<()> { |
1610 | | use std::os::fd::AsRawFd; |
1611 | | |
1612 | 0 | let res = unsafe { libc::posix_fallocate(fd.as_fd().as_raw_fd(), offset, len) }; |
1613 | 0 | match Errno::result(res) { |
1614 | 0 | Err(err) => Err(err), |
1615 | 0 | Ok(0) => Ok(()), |
1616 | 0 | Ok(errno) => Err(Errno::from_raw(errno)), |
1617 | | } |
1618 | 0 | } |
1619 | | } |