/rust/registry/src/index.crates.io-6f17d22bba15001f/nix-0.27.1/src/fcntl.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use crate::errno::Errno; |
2 | | use libc::{self, c_char, c_int, c_uint, size_t, ssize_t}; |
3 | | use std::ffi::OsString; |
4 | | #[cfg(not(target_os = "redox"))] |
5 | | use std::os::raw; |
6 | | use std::os::unix::ffi::OsStringExt; |
7 | | use std::os::unix::io::RawFd; |
8 | | // For splice and copy_file_range |
9 | | #[cfg(any( |
10 | | target_os = "android", |
11 | | target_os = "freebsd", |
12 | | target_os = "linux" |
13 | | ))] |
14 | | use std::{ |
15 | | os::unix::io::{AsFd, AsRawFd}, |
16 | | ptr, |
17 | | }; |
18 | | |
19 | | #[cfg(feature = "fs")] |
20 | | use crate::{sys::stat::Mode, NixPath, Result}; |
21 | | |
22 | | #[cfg(any( |
23 | | target_os = "linux", |
24 | | target_os = "android", |
25 | | target_os = "emscripten", |
26 | | target_os = "fuchsia", |
27 | | target_os = "wasi", |
28 | | target_env = "uclibc", |
29 | | target_os = "freebsd" |
30 | | ))] |
31 | | #[cfg(feature = "fs")] |
32 | | pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice}; |
33 | | |
34 | | #[cfg(not(target_os = "redox"))] |
35 | | #[cfg(any(feature = "fs", feature = "process"))] |
36 | | libc_bitflags! { |
37 | | #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "process"))))] |
38 | | pub struct AtFlags: c_int { |
39 | | AT_REMOVEDIR; |
40 | | AT_SYMLINK_FOLLOW; |
41 | | AT_SYMLINK_NOFOLLOW; |
42 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
43 | | AT_NO_AUTOMOUNT; |
44 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
45 | | AT_EMPTY_PATH; |
46 | | #[cfg(not(target_os = "android"))] |
47 | | AT_EACCESS; |
48 | | } |
49 | | } |
50 | | |
51 | | #[cfg(any(feature = "fs", feature = "term"))] |
52 | | libc_bitflags!( |
53 | | /// Configuration options for opened files. |
54 | | #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term"))))] |
55 | | pub struct OFlag: c_int { |
56 | | /// Mask for the access mode of the file. |
57 | | O_ACCMODE; |
58 | | /// Use alternate I/O semantics. |
59 | | #[cfg(target_os = "netbsd")] |
60 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
61 | | O_ALT_IO; |
62 | | /// Open the file in append-only mode. |
63 | | O_APPEND; |
64 | | /// Generate a signal when input or output becomes possible. |
65 | | #[cfg(not(any(target_os = "aix", |
66 | | target_os = "illumos", |
67 | | target_os = "solaris", |
68 | | target_os = "haiku")))] |
69 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
70 | | O_ASYNC; |
71 | | /// Closes the file descriptor once an `execve` call is made. |
72 | | /// |
73 | | /// Also sets the file offset to the beginning of the file. |
74 | | O_CLOEXEC; |
75 | | /// Create the file if it does not exist. |
76 | | O_CREAT; |
77 | | /// Try to minimize cache effects of the I/O for this file. |
78 | | #[cfg(any(target_os = "android", |
79 | | target_os = "dragonfly", |
80 | | target_os = "freebsd", |
81 | | target_os = "linux", |
82 | | target_os = "netbsd"))] |
83 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
84 | | O_DIRECT; |
85 | | /// If the specified path isn't a directory, fail. |
86 | | #[cfg(not(any(target_os = "illumos", target_os = "solaris")))] |
87 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
88 | | O_DIRECTORY; |
89 | | /// Implicitly follow each `write()` with an `fdatasync()`. |
90 | | #[cfg(any(target_os = "android", |
91 | | target_os = "ios", |
92 | | target_os = "linux", |
93 | | target_os = "macos", |
94 | | target_os = "netbsd", |
95 | | target_os = "openbsd"))] |
96 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
97 | | O_DSYNC; |
98 | | /// Error out if a file was not created. |
99 | | O_EXCL; |
100 | | /// Open for execute only. |
101 | | #[cfg(target_os = "freebsd")] |
102 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
103 | | O_EXEC; |
104 | | /// Open with an exclusive file lock. |
105 | | #[cfg(any(target_os = "dragonfly", |
106 | | target_os = "freebsd", |
107 | | target_os = "ios", |
108 | | target_os = "macos", |
109 | | target_os = "netbsd", |
110 | | target_os = "openbsd", |
111 | | target_os = "redox"))] |
112 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
113 | | O_EXLOCK; |
114 | | /// Same as `O_SYNC`. |
115 | | #[cfg(any(target_os = "dragonfly", |
116 | | target_os = "freebsd", |
117 | | target_os = "ios", |
118 | | all(target_os = "linux", not(target_env = "musl")), |
119 | | target_os = "macos", |
120 | | target_os = "netbsd", |
121 | | target_os = "openbsd", |
122 | | target_os = "redox"))] |
123 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
124 | | O_FSYNC; |
125 | | /// Allow files whose sizes can't be represented in an `off_t` to be opened. |
126 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
127 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
128 | | O_LARGEFILE; |
129 | | /// Do not update the file last access time during `read(2)`s. |
130 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
131 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
132 | | O_NOATIME; |
133 | | /// Don't attach the device as the process' controlling terminal. |
134 | | #[cfg(not(target_os = "redox"))] |
135 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
136 | | O_NOCTTY; |
137 | | /// Same as `O_NONBLOCK`. |
138 | | #[cfg(not(any(target_os = "redox", target_os = "haiku")))] |
139 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
140 | | O_NDELAY; |
141 | | /// `open()` will fail if the given path is a symbolic link. |
142 | | O_NOFOLLOW; |
143 | | /// When possible, open the file in nonblocking mode. |
144 | | O_NONBLOCK; |
145 | | /// Don't deliver `SIGPIPE`. |
146 | | #[cfg(target_os = "netbsd")] |
147 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
148 | | O_NOSIGPIPE; |
149 | | /// Obtain a file descriptor for low-level access. |
150 | | /// |
151 | | /// The file itself is not opened and other file operations will fail. |
152 | | #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))] |
153 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
154 | | O_PATH; |
155 | | /// Only allow reading. |
156 | | /// |
157 | | /// This should not be combined with `O_WRONLY` or `O_RDWR`. |
158 | | O_RDONLY; |
159 | | /// Allow both reading and writing. |
160 | | /// |
161 | | /// This should not be combined with `O_WRONLY` or `O_RDONLY`. |
162 | | O_RDWR; |
163 | | /// Similar to `O_DSYNC` but applies to `read`s instead. |
164 | | #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))] |
165 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
166 | | O_RSYNC; |
167 | | /// Skip search permission checks. |
168 | | #[cfg(target_os = "netbsd")] |
169 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
170 | | O_SEARCH; |
171 | | /// Open with a shared file lock. |
172 | | #[cfg(any(target_os = "dragonfly", |
173 | | target_os = "freebsd", |
174 | | target_os = "ios", |
175 | | target_os = "macos", |
176 | | target_os = "netbsd", |
177 | | target_os = "openbsd", |
178 | | target_os = "redox"))] |
179 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
180 | | O_SHLOCK; |
181 | | /// Implicitly follow each `write()` with an `fsync()`. |
182 | | #[cfg(not(target_os = "redox"))] |
183 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
184 | | O_SYNC; |
185 | | /// Create an unnamed temporary file. |
186 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
187 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
188 | | O_TMPFILE; |
189 | | /// Truncate an existing regular file to 0 length if it allows writing. |
190 | | O_TRUNC; |
191 | | /// Restore default TTY attributes. |
192 | | #[cfg(target_os = "freebsd")] |
193 | | #[cfg_attr(docsrs, doc(cfg(all())))] |
194 | | O_TTY_INIT; |
195 | | /// Only allow writing. |
196 | | /// |
197 | | /// This should not be combined with `O_RDONLY` or `O_RDWR`. |
198 | | O_WRONLY; |
199 | | } |
200 | | ); |
201 | | |
202 | | feature! { |
203 | | #![feature = "fs"] |
204 | | |
205 | | // The conversion is not identical on all operating systems. |
206 | | #[allow(clippy::useless_conversion)] |
207 | 0 | pub fn open<P: ?Sized + NixPath>( |
208 | 0 | path: &P, |
209 | 0 | oflag: OFlag, |
210 | 0 | mode: Mode, |
211 | 0 | ) -> Result<RawFd> { |
212 | 0 | let fd = path.with_nix_path(|cstr| unsafe { |
213 | 0 | libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) |
214 | 0 | })?; |
215 | | |
216 | 0 | Errno::result(fd) |
217 | 0 | } |
218 | | |
219 | | // The conversion is not identical on all operating systems. |
220 | | #[allow(clippy::useless_conversion)] |
221 | | #[cfg(not(target_os = "redox"))] |
222 | 0 | pub fn openat<P: ?Sized + NixPath>( |
223 | 0 | dirfd: RawFd, |
224 | 0 | path: &P, |
225 | 0 | oflag: OFlag, |
226 | 0 | mode: Mode, |
227 | 0 | ) -> Result<RawFd> { |
228 | 0 | let fd = path.with_nix_path(|cstr| unsafe { |
229 | 0 | libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) |
230 | 0 | })?; |
231 | 0 | Errno::result(fd) |
232 | 0 | } |
233 | | |
234 | | #[cfg(not(target_os = "redox"))] |
235 | 0 | pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( |
236 | 0 | old_dirfd: Option<RawFd>, |
237 | 0 | old_path: &P1, |
238 | 0 | new_dirfd: Option<RawFd>, |
239 | 0 | new_path: &P2, |
240 | 0 | ) -> Result<()> { |
241 | 0 | let res = old_path.with_nix_path(|old_cstr| { |
242 | 0 | new_path.with_nix_path(|new_cstr| unsafe { |
243 | 0 | libc::renameat( |
244 | 0 | at_rawfd(old_dirfd), |
245 | 0 | old_cstr.as_ptr(), |
246 | 0 | at_rawfd(new_dirfd), |
247 | 0 | new_cstr.as_ptr(), |
248 | 0 | ) |
249 | 0 | }) |
250 | 0 | })??; |
251 | 0 | Errno::result(res).map(drop) |
252 | 0 | } |
253 | | } |
254 | | |
255 | | #[cfg(all(target_os = "linux", target_env = "gnu"))] |
256 | | #[cfg(feature = "fs")] |
257 | | libc_bitflags! { |
258 | | #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] |
259 | | pub struct RenameFlags: u32 { |
260 | | RENAME_EXCHANGE; |
261 | | RENAME_NOREPLACE; |
262 | | RENAME_WHITEOUT; |
263 | | } |
264 | | } |
265 | | |
266 | | feature! { |
267 | | #![feature = "fs"] |
268 | | #[cfg(all(target_os = "linux", target_env = "gnu"))] |
269 | 0 | pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( |
270 | 0 | old_dirfd: Option<RawFd>, |
271 | 0 | old_path: &P1, |
272 | 0 | new_dirfd: Option<RawFd>, |
273 | 0 | new_path: &P2, |
274 | 0 | flags: RenameFlags, |
275 | 0 | ) -> Result<()> { |
276 | 0 | let res = old_path.with_nix_path(|old_cstr| { |
277 | 0 | new_path.with_nix_path(|new_cstr| unsafe { |
278 | 0 | libc::renameat2( |
279 | 0 | at_rawfd(old_dirfd), |
280 | 0 | old_cstr.as_ptr(), |
281 | 0 | at_rawfd(new_dirfd), |
282 | 0 | new_cstr.as_ptr(), |
283 | 0 | flags.bits(), |
284 | 0 | ) |
285 | 0 | }) |
286 | 0 | })??; |
287 | 0 | Errno::result(res).map(drop) |
288 | 0 | } |
289 | | |
290 | 0 | fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> { |
291 | 0 | unsafe { v.set_len(len as usize) } |
292 | 0 | v.shrink_to_fit(); |
293 | 0 | Ok(OsString::from_vec(v.to_vec())) |
294 | 0 | } |
295 | | |
296 | 0 | fn readlink_maybe_at<P: ?Sized + NixPath>( |
297 | 0 | dirfd: Option<RawFd>, |
298 | 0 | path: &P, |
299 | 0 | v: &mut Vec<u8>, |
300 | 0 | ) -> Result<libc::ssize_t> { |
301 | 0 | path.with_nix_path(|cstr| unsafe { |
302 | 0 | match dirfd { |
303 | | #[cfg(target_os = "redox")] |
304 | | Some(_) => unreachable!(), |
305 | | #[cfg(not(target_os = "redox"))] |
306 | 0 | Some(dirfd) => libc::readlinkat( |
307 | 0 | dirfd, |
308 | 0 | cstr.as_ptr(), |
309 | 0 | v.as_mut_ptr() as *mut c_char, |
310 | 0 | v.capacity() as size_t, |
311 | 0 | ), |
312 | 0 | None => libc::readlink( |
313 | 0 | cstr.as_ptr(), |
314 | 0 | v.as_mut_ptr() as *mut c_char, |
315 | 0 | v.capacity() as size_t, |
316 | 0 | ), |
317 | | } |
318 | 0 | }) |
319 | 0 | } |
320 | | |
321 | 0 | fn inner_readlink<P: ?Sized + NixPath>( |
322 | 0 | dirfd: Option<RawFd>, |
323 | 0 | path: &P, |
324 | 0 | ) -> Result<OsString> { |
325 | 0 | let mut v = Vec::with_capacity(libc::PATH_MAX as usize); |
326 | | |
327 | | { |
328 | | // simple case: result is strictly less than `PATH_MAX` |
329 | 0 | let res = readlink_maybe_at(dirfd, path, &mut v)?; |
330 | 0 | let len = Errno::result(res)?; |
331 | 0 | debug_assert!(len >= 0); |
332 | 0 | if (len as usize) < v.capacity() { |
333 | 0 | return wrap_readlink_result(v, res); |
334 | 0 | } |
335 | | } |
336 | | |
337 | | // Uh oh, the result is too long... |
338 | | // Let's try to ask lstat how many bytes to allocate. |
339 | 0 | let mut try_size = { |
340 | 0 | let reported_size = match dirfd { |
341 | | #[cfg(target_os = "redox")] |
342 | | Some(_) => unreachable!(), |
343 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
344 | 0 | Some(dirfd) => { |
345 | 0 | let flags = if path.is_empty() { |
346 | 0 | AtFlags::AT_EMPTY_PATH |
347 | | } else { |
348 | 0 | AtFlags::empty() |
349 | | }; |
350 | 0 | super::sys::stat::fstatat( |
351 | 0 | dirfd, |
352 | 0 | path, |
353 | 0 | flags | AtFlags::AT_SYMLINK_NOFOLLOW, |
354 | 0 | ) |
355 | | } |
356 | | #[cfg(not(any( |
357 | | target_os = "android", |
358 | | target_os = "linux", |
359 | | target_os = "redox" |
360 | | )))] |
361 | | Some(dirfd) => super::sys::stat::fstatat( |
362 | | dirfd, |
363 | | path, |
364 | | AtFlags::AT_SYMLINK_NOFOLLOW, |
365 | | ), |
366 | 0 | None => super::sys::stat::lstat(path), |
367 | | } |
368 | 0 | .map(|x| x.st_size) |
369 | 0 | .unwrap_or(0); |
370 | 0 |
|
371 | 0 | if reported_size > 0 { |
372 | | // Note: even if `lstat`'s apparently valid answer turns out to be |
373 | | // wrong, we will still read the full symlink no matter what. |
374 | 0 | reported_size as usize + 1 |
375 | | } else { |
376 | | // If lstat doesn't cooperate, or reports an error, be a little less |
377 | | // precise. |
378 | 0 | (libc::PATH_MAX as usize).max(128) << 1 |
379 | | } |
380 | | }; |
381 | | |
382 | | loop { |
383 | | { |
384 | 0 | v.reserve_exact(try_size); |
385 | 0 | let res = readlink_maybe_at(dirfd, path, &mut v)?; |
386 | 0 | let len = Errno::result(res)?; |
387 | 0 | debug_assert!(len >= 0); |
388 | 0 | if (len as usize) < v.capacity() { |
389 | 0 | return wrap_readlink_result(v, res); |
390 | 0 | } |
391 | 0 | } |
392 | 0 |
|
393 | 0 | // Ugh! Still not big enough! |
394 | 0 | match try_size.checked_shl(1) { |
395 | 0 | Some(next_size) => try_size = next_size, |
396 | | // It's absurd that this would happen, but handle it sanely |
397 | | // anyway. |
398 | 0 | None => break Err(Errno::ENAMETOOLONG), |
399 | | } |
400 | | } |
401 | 0 | } |
402 | | |
403 | 0 | pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> { |
404 | 0 | inner_readlink(None, path) |
405 | 0 | } |
406 | | |
407 | | #[cfg(not(target_os = "redox"))] |
408 | 0 | pub fn readlinkat<P: ?Sized + NixPath>( |
409 | 0 | dirfd: RawFd, |
410 | 0 | path: &P, |
411 | 0 | ) -> Result<OsString> { |
412 | 0 | inner_readlink(Some(dirfd), path) |
413 | 0 | } |
414 | | |
415 | | /// Computes the raw fd consumed by a function of the form `*at`. |
416 | | #[cfg(not(target_os = "redox"))] |
417 | 0 | pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int { |
418 | 0 | match fd { |
419 | 0 | None => libc::AT_FDCWD, |
420 | 0 | Some(fd) => fd, |
421 | | } |
422 | 0 | } |
423 | | } |
424 | | |
425 | | #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] |
426 | | #[cfg(feature = "fs")] |
427 | | libc_bitflags!( |
428 | | /// Additional flags for file sealing, which allows for limiting operations on a file. |
429 | | #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] |
430 | | pub struct SealFlag: c_int { |
431 | | /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`. |
432 | | F_SEAL_SEAL; |
433 | | /// The file cannot be reduced in size. |
434 | | F_SEAL_SHRINK; |
435 | | /// The size of the file cannot be increased. |
436 | | F_SEAL_GROW; |
437 | | /// The file contents cannot be modified. |
438 | | F_SEAL_WRITE; |
439 | | } |
440 | | ); |
441 | | |
442 | | #[cfg(feature = "fs")] |
443 | | libc_bitflags!( |
444 | | /// Additional configuration flags for `fcntl`'s `F_SETFD`. |
445 | | #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] |
446 | | pub struct FdFlag: c_int { |
447 | | /// The file descriptor will automatically be closed during a successful `execve(2)`. |
448 | | FD_CLOEXEC; |
449 | | } |
450 | | ); |
451 | | |
452 | | feature! { |
453 | | #![feature = "fs"] |
454 | | |
455 | | #[cfg(not(target_os = "redox"))] |
456 | | #[derive(Debug, Eq, Hash, PartialEq)] |
457 | | #[non_exhaustive] |
458 | | pub enum FcntlArg<'a> { |
459 | | F_DUPFD(RawFd), |
460 | | F_DUPFD_CLOEXEC(RawFd), |
461 | | F_GETFD, |
462 | | F_SETFD(FdFlag), // FD_FLAGS |
463 | | F_GETFL, |
464 | | F_SETFL(OFlag), // O_NONBLOCK |
465 | | F_SETLK(&'a libc::flock), |
466 | | F_SETLKW(&'a libc::flock), |
467 | | F_GETLK(&'a mut libc::flock), |
468 | | #[cfg(any(target_os = "linux", target_os = "android"))] |
469 | | F_OFD_SETLK(&'a libc::flock), |
470 | | #[cfg(any(target_os = "linux", target_os = "android"))] |
471 | | F_OFD_SETLKW(&'a libc::flock), |
472 | | #[cfg(any(target_os = "linux", target_os = "android"))] |
473 | | F_OFD_GETLK(&'a mut libc::flock), |
474 | | #[cfg(any( |
475 | | target_os = "android", |
476 | | target_os = "linux", |
477 | | target_os = "freebsd" |
478 | | ))] |
479 | | F_ADD_SEALS(SealFlag), |
480 | | #[cfg(any( |
481 | | target_os = "android", |
482 | | target_os = "linux", |
483 | | target_os = "freebsd" |
484 | | ))] |
485 | | F_GET_SEALS, |
486 | | #[cfg(any(target_os = "macos", target_os = "ios"))] |
487 | | F_FULLFSYNC, |
488 | | #[cfg(any(target_os = "linux", target_os = "android"))] |
489 | | F_GETPIPE_SZ, |
490 | | #[cfg(any(target_os = "linux", target_os = "android"))] |
491 | | F_SETPIPE_SZ(c_int), |
492 | | // TODO: Rest of flags |
493 | | } |
494 | | |
495 | | #[cfg(target_os = "redox")] |
496 | | #[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] |
497 | | #[non_exhaustive] |
498 | | pub enum FcntlArg { |
499 | | F_DUPFD(RawFd), |
500 | | F_DUPFD_CLOEXEC(RawFd), |
501 | | F_GETFD, |
502 | | F_SETFD(FdFlag), // FD_FLAGS |
503 | | F_GETFL, |
504 | | F_SETFL(OFlag), // O_NONBLOCK |
505 | | } |
506 | | pub use self::FcntlArg::*; |
507 | | |
508 | | // TODO: Figure out how to handle value fcntl returns |
509 | 0 | pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> { |
510 | 0 | let res = unsafe { |
511 | 0 | match arg { |
512 | 0 | F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd), |
513 | 0 | F_DUPFD_CLOEXEC(rawfd) => { |
514 | 0 | libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd) |
515 | | } |
516 | 0 | F_GETFD => libc::fcntl(fd, libc::F_GETFD), |
517 | 0 | F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()), |
518 | 0 | F_GETFL => libc::fcntl(fd, libc::F_GETFL), |
519 | 0 | F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()), |
520 | | #[cfg(not(target_os = "redox"))] |
521 | 0 | F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock), |
522 | | #[cfg(not(target_os = "redox"))] |
523 | 0 | F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock), |
524 | | #[cfg(not(target_os = "redox"))] |
525 | 0 | F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock), |
526 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
527 | 0 | F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock), |
528 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
529 | 0 | F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock), |
530 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
531 | 0 | F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock), |
532 | | #[cfg(any( |
533 | | target_os = "android", |
534 | | target_os = "linux", |
535 | | target_os = "freebsd" |
536 | | ))] |
537 | 0 | F_ADD_SEALS(flag) => { |
538 | 0 | libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()) |
539 | | } |
540 | | #[cfg(any( |
541 | | target_os = "android", |
542 | | target_os = "linux", |
543 | | target_os = "freebsd" |
544 | | ))] |
545 | 0 | F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS), |
546 | | #[cfg(any(target_os = "macos", target_os = "ios"))] |
547 | | F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC), |
548 | | #[cfg(any(target_os = "linux", target_os = "android"))] |
549 | 0 | F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ), |
550 | | #[cfg(any(target_os = "linux", target_os = "android"))] |
551 | 0 | F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size), |
552 | | } |
553 | | }; |
554 | | |
555 | 0 | Errno::result(res) |
556 | 0 | } |
557 | | |
558 | | // TODO: convert to libc_enum |
559 | | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
560 | | #[non_exhaustive] |
561 | | pub enum FlockArg { |
562 | | LockShared, |
563 | | LockExclusive, |
564 | | Unlock, |
565 | | LockSharedNonblock, |
566 | | LockExclusiveNonblock, |
567 | | UnlockNonblock, |
568 | | } |
569 | | |
570 | | #[cfg(not(any(target_os = "redox", target_os = "solaris")))] |
571 | 0 | pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> { |
572 | | use self::FlockArg::*; |
573 | | |
574 | 0 | let res = unsafe { |
575 | 0 | match arg { |
576 | 0 | LockShared => libc::flock(fd, libc::LOCK_SH), |
577 | 0 | LockExclusive => libc::flock(fd, libc::LOCK_EX), |
578 | 0 | Unlock => libc::flock(fd, libc::LOCK_UN), |
579 | | LockSharedNonblock => { |
580 | 0 | libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB) |
581 | | } |
582 | | LockExclusiveNonblock => { |
583 | 0 | libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) |
584 | | } |
585 | 0 | UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB), |
586 | | } |
587 | | }; |
588 | | |
589 | 0 | Errno::result(res).map(drop) |
590 | 0 | } |
591 | | } |
592 | | |
593 | | #[cfg(any(target_os = "android", target_os = "linux"))] |
594 | | #[cfg(feature = "zerocopy")] |
595 | | libc_bitflags! { |
596 | | /// Additional flags to `splice` and friends. |
597 | | #[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))] |
598 | | pub struct SpliceFFlags: c_uint { |
599 | | /// Request that pages be moved instead of copied. |
600 | | /// |
601 | | /// Not applicable to `vmsplice`. |
602 | | SPLICE_F_MOVE; |
603 | | /// Do not block on I/O. |
604 | | SPLICE_F_NONBLOCK; |
605 | | /// Hint that more data will be coming in a subsequent splice. |
606 | | /// |
607 | | /// Not applicable to `vmsplice`. |
608 | | SPLICE_F_MORE; |
609 | | /// Gift the user pages to the kernel. |
610 | | /// |
611 | | /// Not applicable to `splice`. |
612 | | SPLICE_F_GIFT; |
613 | | } |
614 | | } |
615 | | |
616 | | feature! { |
617 | | #![feature = "zerocopy"] |
618 | | |
619 | | /// Copy a range of data from one file to another |
620 | | /// |
621 | | /// The `copy_file_range` system call performs an in-kernel copy between |
622 | | /// file descriptors `fd_in` and `fd_out` without the additional cost of |
623 | | /// transferring data from the kernel to user space and back again. There may be |
624 | | /// additional optimizations for specific file systems. It copies up to `len` |
625 | | /// bytes of data from file descriptor `fd_in` to file descriptor `fd_out`, |
626 | | /// overwriting any data that exists within the requested range of the target |
627 | | /// file. |
628 | | /// |
629 | | /// If the `off_in` and/or `off_out` arguments are used, the values |
630 | | /// will be mutated to reflect the new position within the file after |
631 | | /// copying. If they are not used, the relevant file descriptors will be seeked |
632 | | /// to the new position. |
633 | | /// |
634 | | /// On successful completion the number of bytes actually copied will be |
635 | | /// returned. |
636 | | // Note: FreeBSD defines the offset argument as "off_t". Linux and Android |
637 | | // define it as "loff_t". But on both OSes, on all supported platforms, those |
638 | | // are 64 bits. So Nix uses i64 to make the docs simple and consistent. |
639 | | #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] |
640 | | pub fn copy_file_range<Fd1: AsFd, Fd2: AsFd>( |
641 | | fd_in: Fd1, |
642 | | off_in: Option<&mut i64>, |
643 | | fd_out: Fd2, |
644 | | off_out: Option<&mut i64>, |
645 | | len: usize, |
646 | | ) -> Result<usize> { |
647 | | let off_in = off_in |
648 | | .map(|offset| offset as *mut i64) |
649 | | .unwrap_or(ptr::null_mut()); |
650 | | let off_out = off_out |
651 | | .map(|offset| offset as *mut i64) |
652 | | .unwrap_or(ptr::null_mut()); |
653 | | |
654 | | cfg_if::cfg_if! { |
655 | | if #[cfg(target_os = "freebsd")] { |
656 | | let ret = unsafe { |
657 | | libc::copy_file_range( |
658 | | fd_in.as_fd().as_raw_fd(), |
659 | | off_in, |
660 | | fd_out.as_fd().as_raw_fd(), |
661 | | off_out, |
662 | | len, |
663 | | 0, |
664 | | ) |
665 | | }; |
666 | | } else { |
667 | | // May Linux distros still don't include copy_file_range in their |
668 | | // libc implementations, so we need to make a direct syscall. |
669 | | let ret = unsafe { |
670 | | libc::syscall( |
671 | | libc::SYS_copy_file_range, |
672 | | fd_in, |
673 | | off_in, |
674 | | fd_out.as_fd().as_raw_fd(), |
675 | | off_out, |
676 | | len, |
677 | | 0, |
678 | | ) |
679 | | }; |
680 | | } |
681 | | } |
682 | | Errno::result(ret).map(|r| r as usize) |
683 | | } |
684 | | |
685 | | #[cfg(any(target_os = "linux", target_os = "android"))] |
686 | | pub fn splice( |
687 | | fd_in: RawFd, |
688 | | off_in: Option<&mut libc::loff_t>, |
689 | | fd_out: RawFd, |
690 | | off_out: Option<&mut libc::loff_t>, |
691 | | len: usize, |
692 | | flags: SpliceFFlags, |
693 | | ) -> Result<usize> { |
694 | | let off_in = off_in |
695 | | .map(|offset| offset as *mut libc::loff_t) |
696 | | .unwrap_or(ptr::null_mut()); |
697 | | let off_out = off_out |
698 | | .map(|offset| offset as *mut libc::loff_t) |
699 | | .unwrap_or(ptr::null_mut()); |
700 | | |
701 | | let ret = unsafe { |
702 | | libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) |
703 | | }; |
704 | | Errno::result(ret).map(|r| r as usize) |
705 | | } |
706 | | |
707 | | #[cfg(any(target_os = "linux", target_os = "android"))] |
708 | | pub fn tee( |
709 | | fd_in: RawFd, |
710 | | fd_out: RawFd, |
711 | | len: usize, |
712 | | flags: SpliceFFlags, |
713 | | ) -> Result<usize> { |
714 | | let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) }; |
715 | | Errno::result(ret).map(|r| r as usize) |
716 | | } |
717 | | |
718 | | #[cfg(any(target_os = "linux", target_os = "android"))] |
719 | | pub fn vmsplice( |
720 | | fd: RawFd, |
721 | | iov: &[std::io::IoSlice<'_>], |
722 | | flags: SpliceFFlags, |
723 | | ) -> Result<usize> { |
724 | | let ret = unsafe { |
725 | | libc::vmsplice( |
726 | | fd, |
727 | | iov.as_ptr() as *const libc::iovec, |
728 | | iov.len(), |
729 | | flags.bits(), |
730 | | ) |
731 | | }; |
732 | | Errno::result(ret).map(|r| r as usize) |
733 | | } |
734 | | } |
735 | | |
736 | | #[cfg(target_os = "linux")] |
737 | | #[cfg(feature = "fs")] |
738 | | libc_bitflags!( |
739 | | /// Mode argument flags for fallocate determining operation performed on a given range. |
740 | | #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] |
741 | | pub struct FallocateFlags: c_int { |
742 | | /// File size is not changed. |
743 | | /// |
744 | | /// offset + len can be greater than file size. |
745 | | FALLOC_FL_KEEP_SIZE; |
746 | | /// Deallocates space by creating a hole. |
747 | | /// |
748 | | /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes. |
749 | | FALLOC_FL_PUNCH_HOLE; |
750 | | /// Removes byte range from a file without leaving a hole. |
751 | | /// |
752 | | /// Byte range to collapse starts at offset and continues for len bytes. |
753 | | FALLOC_FL_COLLAPSE_RANGE; |
754 | | /// Zeroes space in specified byte range. |
755 | | /// |
756 | | /// Byte range starts at offset and continues for len bytes. |
757 | | FALLOC_FL_ZERO_RANGE; |
758 | | /// Increases file space by inserting a hole within the file size. |
759 | | /// |
760 | | /// Does not overwrite existing data. Hole starts at offset and continues for len bytes. |
761 | | FALLOC_FL_INSERT_RANGE; |
762 | | /// Shared file data extants are made private to the file. |
763 | | /// |
764 | | /// Gaurantees that a subsequent write will not fail due to lack of space. |
765 | | FALLOC_FL_UNSHARE_RANGE; |
766 | | } |
767 | | ); |
768 | | |
769 | | feature! { |
770 | | #![feature = "fs"] |
771 | | |
772 | | /// Manipulates file space. |
773 | | /// |
774 | | /// Allows the caller to directly manipulate the allocated disk space for the |
775 | | /// file referred to by fd. |
776 | | #[cfg(target_os = "linux")] |
777 | | #[cfg(feature = "fs")] |
778 | 0 | pub fn fallocate( |
779 | 0 | fd: RawFd, |
780 | 0 | mode: FallocateFlags, |
781 | 0 | offset: libc::off_t, |
782 | 0 | len: libc::off_t, |
783 | 0 | ) -> Result<()> { |
784 | 0 | let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) }; |
785 | 0 | Errno::result(res).map(drop) |
786 | 0 | } |
787 | | |
788 | | /// Argument to [`fspacectl`] describing the range to zero. The first member is |
789 | | /// the file offset, and the second is the length of the region. |
790 | | #[cfg(any(target_os = "freebsd"))] |
791 | | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
792 | | pub struct SpacectlRange(pub libc::off_t, pub libc::off_t); |
793 | | |
794 | | #[cfg(any(target_os = "freebsd"))] |
795 | | impl SpacectlRange { |
796 | | #[inline] |
797 | | pub fn is_empty(&self) -> bool { |
798 | | self.1 == 0 |
799 | | } |
800 | | |
801 | | #[inline] |
802 | | pub fn len(&self) -> libc::off_t { |
803 | | self.1 |
804 | | } |
805 | | |
806 | | #[inline] |
807 | | pub fn offset(&self) -> libc::off_t { |
808 | | self.0 |
809 | | } |
810 | | } |
811 | | |
812 | | /// Punch holes in a file. |
813 | | /// |
814 | | /// `fspacectl` instructs the file system to deallocate a portion of a file. |
815 | | /// After a successful operation, this region of the file will return all zeroes |
816 | | /// if read. If the file system supports deallocation, then it may free the |
817 | | /// underlying storage, too. |
818 | | /// |
819 | | /// # Arguments |
820 | | /// |
821 | | /// - `fd` - File to operate on |
822 | | /// - `range.0` - File offset at which to begin deallocation |
823 | | /// - `range.1` - Length of the region to deallocate |
824 | | /// |
825 | | /// # Returns |
826 | | /// |
827 | | /// The operation may deallocate less than the entire requested region. On |
828 | | /// success, it returns the region that still remains to be deallocated. The |
829 | | /// caller should loop until the returned region is empty. |
830 | | /// |
831 | | /// # Example |
832 | | /// |
833 | | #[cfg_attr(fbsd14, doc = " ```")] |
834 | | #[cfg_attr(not(fbsd14), doc = " ```no_run")] |
835 | | /// # use std::io::Write; |
836 | | /// # use std::os::unix::fs::FileExt; |
837 | | /// # use std::os::unix::io::AsRawFd; |
838 | | /// # use nix::fcntl::*; |
839 | | /// # use tempfile::tempfile; |
840 | | /// const INITIAL: &[u8] = b"0123456789abcdef"; |
841 | | /// let mut f = tempfile().unwrap(); |
842 | | /// f.write_all(INITIAL).unwrap(); |
843 | | /// let mut range = SpacectlRange(3, 6); |
844 | | /// while (!range.is_empty()) { |
845 | | /// range = fspacectl(f.as_raw_fd(), range).unwrap(); |
846 | | /// } |
847 | | /// let mut buf = vec![0; INITIAL.len()]; |
848 | | /// f.read_exact_at(&mut buf, 0).unwrap(); |
849 | | /// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef"); |
850 | | /// ``` |
851 | | #[cfg(target_os = "freebsd")] |
852 | | pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> { |
853 | | let mut rqsr = libc::spacectl_range { |
854 | | r_offset: range.0, |
855 | | r_len: range.1, |
856 | | }; |
857 | | let res = unsafe { |
858 | | libc::fspacectl( |
859 | | fd, |
860 | | libc::SPACECTL_DEALLOC, // Only one command is supported ATM |
861 | | &rqsr, |
862 | | 0, // No flags are currently supported |
863 | | &mut rqsr, |
864 | | ) |
865 | | }; |
866 | | Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len)) |
867 | | } |
868 | | |
869 | | /// Like [`fspacectl`], but will never return incomplete. |
870 | | /// |
871 | | /// # Arguments |
872 | | /// |
873 | | /// - `fd` - File to operate on |
874 | | /// - `offset` - File offset at which to begin deallocation |
875 | | /// - `len` - Length of the region to deallocate |
876 | | /// |
877 | | /// # Returns |
878 | | /// |
879 | | /// Returns `()` on success. On failure, the region may or may not be partially |
880 | | /// deallocated. |
881 | | /// |
882 | | /// # Example |
883 | | /// |
884 | | #[cfg_attr(fbsd14, doc = " ```")] |
885 | | #[cfg_attr(not(fbsd14), doc = " ```no_run")] |
886 | | /// # use std::io::Write; |
887 | | /// # use std::os::unix::fs::FileExt; |
888 | | /// # use std::os::unix::io::AsRawFd; |
889 | | /// # use nix::fcntl::*; |
890 | | /// # use tempfile::tempfile; |
891 | | /// const INITIAL: &[u8] = b"0123456789abcdef"; |
892 | | /// let mut f = tempfile().unwrap(); |
893 | | /// f.write_all(INITIAL).unwrap(); |
894 | | /// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap(); |
895 | | /// let mut buf = vec![0; INITIAL.len()]; |
896 | | /// f.read_exact_at(&mut buf, 0).unwrap(); |
897 | | /// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef"); |
898 | | /// ``` |
899 | | #[cfg(target_os = "freebsd")] |
900 | | pub fn fspacectl_all( |
901 | | fd: RawFd, |
902 | | offset: libc::off_t, |
903 | | len: libc::off_t, |
904 | | ) -> Result<()> { |
905 | | let mut rqsr = libc::spacectl_range { |
906 | | r_offset: offset, |
907 | | r_len: len, |
908 | | }; |
909 | | while rqsr.r_len > 0 { |
910 | | let res = unsafe { |
911 | | libc::fspacectl( |
912 | | fd, |
913 | | libc::SPACECTL_DEALLOC, // Only one command is supported ATM |
914 | | &rqsr, |
915 | | 0, // No flags are currently supported |
916 | | &mut rqsr, |
917 | | ) |
918 | | }; |
919 | | Errno::result(res)?; |
920 | | } |
921 | | Ok(()) |
922 | | } |
923 | | |
924 | | #[cfg(any( |
925 | | target_os = "linux", |
926 | | target_os = "android", |
927 | | target_os = "emscripten", |
928 | | target_os = "fuchsia", |
929 | | target_os = "wasi", |
930 | | target_env = "uclibc", |
931 | | target_os = "freebsd" |
932 | | ))] |
933 | | mod posix_fadvise { |
934 | | use crate::errno::Errno; |
935 | | use crate::Result; |
936 | | use std::os::unix::io::RawFd; |
937 | | |
938 | | #[cfg(feature = "fs")] |
939 | | libc_enum! { |
940 | | #[repr(i32)] |
941 | | #[non_exhaustive] |
942 | | #[cfg_attr(docsrs, doc(cfg(feature = "fs")))] |
943 | | pub enum PosixFadviseAdvice { |
944 | | POSIX_FADV_NORMAL, |
945 | | POSIX_FADV_SEQUENTIAL, |
946 | | POSIX_FADV_RANDOM, |
947 | | POSIX_FADV_NOREUSE, |
948 | | POSIX_FADV_WILLNEED, |
949 | | POSIX_FADV_DONTNEED, |
950 | | } |
951 | | } |
952 | | |
953 | | feature! { |
954 | | #![feature = "fs"] |
955 | 0 | pub fn posix_fadvise( |
956 | 0 | fd: RawFd, |
957 | 0 | offset: libc::off_t, |
958 | 0 | len: libc::off_t, |
959 | 0 | advice: PosixFadviseAdvice, |
960 | 0 | ) -> Result<()> { |
961 | 0 | let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) }; |
962 | 0 |
|
963 | 0 | if res == 0 { |
964 | 0 | Ok(()) |
965 | | } else { |
966 | 0 | Err(Errno::from_i32(res)) |
967 | | } |
968 | 0 | } |
969 | | } |
970 | | } |
971 | | |
972 | | #[cfg(any( |
973 | | target_os = "linux", |
974 | | target_os = "android", |
975 | | target_os = "dragonfly", |
976 | | target_os = "emscripten", |
977 | | target_os = "fuchsia", |
978 | | target_os = "wasi", |
979 | | target_os = "freebsd" |
980 | | ))] |
981 | 0 | pub fn posix_fallocate( |
982 | 0 | fd: RawFd, |
983 | 0 | offset: libc::off_t, |
984 | 0 | len: libc::off_t, |
985 | 0 | ) -> Result<()> { |
986 | 0 | let res = unsafe { libc::posix_fallocate(fd, offset, len) }; |
987 | 0 | match Errno::result(res) { |
988 | 0 | Err(err) => Err(err), |
989 | 0 | Ok(0) => Ok(()), |
990 | 0 | Ok(errno) => Err(Errno::from_i32(errno)), |
991 | | } |
992 | 0 | } |
993 | | } |