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