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 | pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> { |
197 | let fd = path.with_nix_path(|cstr| { |
198 | unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) } |
199 | })?; |
200 | |
201 | Errno::result(fd) |
202 | } |
203 | |
204 | // The conversion is not identical on all operating systems. |
205 | #[allow (clippy::useless_conversion)] |
206 | #[cfg (not(target_os = "redox" ))] |
207 | pub fn openat<P: ?Sized + NixPath>( |
208 | dirfd: RawFd, |
209 | path: &P, |
210 | oflag: OFlag, |
211 | mode: Mode, |
212 | ) -> Result<RawFd> { |
213 | let fd = path.with_nix_path(|cstr| { |
214 | unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) } |
215 | })?; |
216 | Errno::result(fd) |
217 | } |
218 | |
219 | #[cfg (not(target_os = "redox" ))] |
220 | pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( |
221 | old_dirfd: Option<RawFd>, |
222 | old_path: &P1, |
223 | new_dirfd: Option<RawFd>, |
224 | new_path: &P2, |
225 | ) -> Result<()> { |
226 | let res = old_path.with_nix_path(|old_cstr| { |
227 | new_path.with_nix_path(|new_cstr| unsafe { |
228 | libc::renameat( |
229 | at_rawfd(old_dirfd), |
230 | old_cstr.as_ptr(), |
231 | at_rawfd(new_dirfd), |
232 | new_cstr.as_ptr(), |
233 | ) |
234 | }) |
235 | })??; |
236 | Errno::result(res).map(drop) |
237 | } |
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 | pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( |
258 | old_dirfd: Option<RawFd>, |
259 | old_path: &P1, |
260 | new_dirfd: Option<RawFd>, |
261 | new_path: &P2, |
262 | flags: RenameFlags, |
263 | ) -> Result<()> { |
264 | let res = old_path.with_nix_path(|old_cstr| { |
265 | new_path.with_nix_path(|new_cstr| unsafe { |
266 | libc::renameat2( |
267 | at_rawfd(old_dirfd), |
268 | old_cstr.as_ptr(), |
269 | at_rawfd(new_dirfd), |
270 | new_cstr.as_ptr(), |
271 | flags.bits(), |
272 | ) |
273 | }) |
274 | })??; |
275 | Errno::result(res).map(drop) |
276 | } |
277 | |
278 | fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> { |
279 | unsafe { v.set_len(len as usize) } |
280 | v.shrink_to_fit(); |
281 | Ok(OsString::from_vec(v.to_vec())) |
282 | } |
283 | |
284 | fn readlink_maybe_at<P: ?Sized + NixPath>( |
285 | dirfd: Option<RawFd>, |
286 | path: &P, |
287 | v: &mut Vec<u8>, |
288 | ) -> Result<libc::ssize_t> { |
289 | path.with_nix_path(|cstr| unsafe { |
290 | match dirfd { |
291 | #[cfg (target_os = "redox" )] |
292 | Some(_) => unreachable!(), |
293 | #[cfg (not(target_os = "redox" ))] |
294 | Some(dirfd) => libc::readlinkat( |
295 | dirfd, |
296 | cstr.as_ptr(), |
297 | v.as_mut_ptr() as *mut c_char, |
298 | v.capacity() as size_t, |
299 | ), |
300 | None => libc::readlink( |
301 | cstr.as_ptr(), |
302 | v.as_mut_ptr() as *mut c_char, |
303 | v.capacity() as size_t, |
304 | ), |
305 | } |
306 | }) |
307 | } |
308 | |
309 | fn inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result<OsString> { |
310 | let mut v = Vec::with_capacity(libc::PATH_MAX as usize); |
311 | // simple case: result is strictly less than `PATH_MAX` |
312 | let res = readlink_maybe_at(dirfd, path, &mut v)?; |
313 | let len = Errno::result(res)?; |
314 | debug_assert!(len >= 0); |
315 | if (len as usize) < v.capacity() { |
316 | return wrap_readlink_result(v, res); |
317 | } |
318 | // Uh oh, the result is too long... |
319 | // Let's try to ask lstat how many bytes to allocate. |
320 | let reported_size = match dirfd { |
321 | #[cfg (target_os = "redox" )] |
322 | Some(_) => unreachable!(), |
323 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
324 | Some(dirfd) => { |
325 | let flags = if path.is_empty() { AtFlags::AT_EMPTY_PATH } else { AtFlags::empty() }; |
326 | 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 | None => super::sys::stat::lstat(path) |
331 | } |
332 | .map(|x| x.st_size) |
333 | .unwrap_or(0); |
334 | 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 | reported_size as usize + 1 |
338 | } else { |
339 | // If lstat doesn't cooperate, or reports an error, be a little less |
340 | // precise. |
341 | (libc::PATH_MAX as usize).max(128) << 1 |
342 | }; |
343 | loop { |
344 | v.reserve_exact(try_size); |
345 | let res = readlink_maybe_at(dirfd, path, &mut v)?; |
346 | let len = Errno::result(res)?; |
347 | debug_assert!(len >= 0); |
348 | if (len as usize) < v.capacity() { |
349 | break wrap_readlink_result(v, res); |
350 | } else { |
351 | // Ugh! Still not big enough! |
352 | match try_size.checked_shl(1) { |
353 | Some(next_size) => try_size = next_size, |
354 | // It's absurd that this would happen, but handle it sanely |
355 | // anyway. |
356 | None => break Err(Errno::ENAMETOOLONG), |
357 | } |
358 | } |
359 | } |
360 | } |
361 | |
362 | pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> { |
363 | inner_readlink(None, path) |
364 | } |
365 | |
366 | #[cfg (not(target_os = "redox" ))] |
367 | pub fn readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString> { |
368 | inner_readlink(Some(dirfd), path) |
369 | } |
370 | |
371 | /// Computes the raw fd consumed by a function of the form `*at`. |
372 | #[cfg (not(target_os = "redox" ))] |
373 | pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int { |
374 | match fd { |
375 | None => libc::AT_FDCWD, |
376 | Some(fd) => fd, |
377 | } |
378 | } |
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 | pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> { |
458 | let res = unsafe { |
459 | match arg { |
460 | F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd), |
461 | F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd), |
462 | F_GETFD => libc::fcntl(fd, libc::F_GETFD), |
463 | F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()), |
464 | F_GETFL => libc::fcntl(fd, libc::F_GETFL), |
465 | F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()), |
466 | #[cfg (not(target_os = "redox" ))] |
467 | F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock), |
468 | #[cfg (not(target_os = "redox" ))] |
469 | F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock), |
470 | #[cfg (not(target_os = "redox" ))] |
471 | F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock), |
472 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
473 | F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock), |
474 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
475 | F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock), |
476 | #[cfg (any(target_os = "android" , target_os = "linux" ))] |
477 | 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 | 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 | 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 | F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ), |
486 | #[cfg (any(target_os = "linux" , target_os = "android" ))] |
487 | F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size), |
488 | } |
489 | }; |
490 | |
491 | Errno::result(res) |
492 | } |
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 | pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> { |
508 | use self::FlockArg::*; |
509 | |
510 | let res = unsafe { |
511 | match arg { |
512 | LockShared => libc::flock(fd, libc::LOCK_SH), |
513 | LockExclusive => libc::flock(fd, libc::LOCK_EX), |
514 | Unlock => libc::flock(fd, libc::LOCK_UN), |
515 | LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB), |
516 | LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB), |
517 | UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB), |
518 | } |
519 | }; |
520 | |
521 | Errno::result(res).map(drop) |
522 | } |
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 | pub fn fallocate( |
684 | fd: RawFd, |
685 | mode: FallocateFlags, |
686 | offset: libc::off_t, |
687 | len: libc::off_t, |
688 | ) -> Result<()> { |
689 | let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) }; |
690 | Errno::result(res).map(drop) |
691 | } |
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 | pub fn posix_fadvise( |
849 | fd: RawFd, |
850 | offset: libc::off_t, |
851 | len: libc::off_t, |
852 | advice: PosixFadviseAdvice, |
853 | ) -> Result<()> { |
854 | let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) }; |
855 | |
856 | if res == 0 { |
857 | Ok(()) |
858 | } else { |
859 | Err(Errno::from_i32(res)) |
860 | } |
861 | } |
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 | pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()> { |
875 | let res = unsafe { libc::posix_fallocate(fd, offset, len) }; |
876 | match Errno::result(res) { |
877 | Err(err) => Err(err), |
878 | Ok(0) => Ok(()), |
879 | Ok(errno) => Err(Errno::from_i32(errno)), |
880 | } |
881 | } |
882 | } |
883 | |