1 use {Result, NixPath};
2 use errno::Errno;
3 use libc::{self, c_int, c_uint, c_char, size_t, ssize_t};
4 use sys::stat::Mode;
5 use std::os::raw;
6 use std::os::unix::io::RawFd;
7 use std::ffi::OsString;
8 use std::os::unix::ffi::OsStringExt;
9
10 #[cfg(any(target_os = "android", target_os = "linux"))]
11 use std::ptr; // For splice and copy_file_range
12 #[cfg(any(target_os = "android", target_os = "linux"))]
13 use sys::uio::IoVec; // For vmsplice
14
15 #[cfg(any(target_os = "linux",
16 target_os = "android",
17 target_os = "emscripten",
18 target_os = "fuchsia",
19 any(target_os = "wasi", target_env = "wasi"),
20 target_env = "uclibc",
21 target_env = "freebsd"))]
22 pub use self::posix_fadvise::*;
23
24 libc_bitflags!{
25 pub struct AtFlags: c_int {
26 AT_REMOVEDIR;
27 AT_SYMLINK_FOLLOW;
28 AT_SYMLINK_NOFOLLOW;
29 #[cfg(any(target_os = "android", target_os = "linux"))]
30 AT_NO_AUTOMOUNT;
31 #[cfg(any(target_os = "android", target_os = "linux"))]
32 AT_EMPTY_PATH;
33 }
34 }
35
36 libc_bitflags!(
37 /// Configuration options for opened files.
38 pub struct OFlag: c_int {
39 /// Mask for the access mode of the file.
40 O_ACCMODE;
41 /// Use alternate I/O semantics.
42 #[cfg(target_os = "netbsd")]
43 O_ALT_IO;
44 /// Open the file in append-only mode.
45 O_APPEND;
46 /// Generate a signal when input or output becomes possible.
47 O_ASYNC;
48 /// Closes the file descriptor once an `execve` call is made.
49 ///
50 /// Also sets the file offset to the beginning of the file.
51 O_CLOEXEC;
52 /// Create the file if it does not exist.
53 O_CREAT;
54 /// Try to minimize cache effects of the I/O for this file.
55 #[cfg(any(target_os = "android",
56 target_os = "dragonfly",
57 target_os = "freebsd",
58 target_os = "linux",
59 target_os = "netbsd"))]
60 O_DIRECT;
61 /// If the specified path isn't a directory, fail.
62 O_DIRECTORY;
63 /// Implicitly follow each `write()` with an `fdatasync()`.
64 #[cfg(any(target_os = "android",
65 target_os = "ios",
66 target_os = "linux",
67 target_os = "macos",
68 target_os = "netbsd",
69 target_os = "openbsd"))]
70 O_DSYNC;
71 /// Error out if a file was not created.
72 O_EXCL;
73 /// Open for execute only.
74 #[cfg(target_os = "freebsd")]
75 O_EXEC;
76 /// Open with an exclusive file lock.
77 #[cfg(any(target_os = "dragonfly",
78 target_os = "freebsd",
79 target_os = "ios",
80 target_os = "macos",
81 target_os = "netbsd",
82 target_os = "openbsd"))]
83 O_EXLOCK;
84 /// Same as `O_SYNC`.
85 #[cfg(any(target_os = "dragonfly",
86 target_os = "freebsd",
87 target_os = "ios",
88 all(target_os = "linux", not(target_env = "musl")),
89 target_os = "macos",
90 target_os = "netbsd",
91 target_os = "openbsd"))]
92 O_FSYNC;
93 /// Allow files whose sizes can't be represented in an `off_t` to be opened.
94 #[cfg(any(target_os = "android", target_os = "linux"))]
95 O_LARGEFILE;
96 /// Do not update the file last access time during `read(2)`s.
97 #[cfg(any(target_os = "android", target_os = "linux"))]
98 O_NOATIME;
99 /// Don't attach the device as the process' controlling terminal.
100 O_NOCTTY;
101 /// Same as `O_NONBLOCK`.
102 O_NDELAY;
103 /// `open()` will fail if the given path is a symbolic link.
104 O_NOFOLLOW;
105 /// When possible, open the file in nonblocking mode.
106 O_NONBLOCK;
107 /// Don't deliver `SIGPIPE`.
108 #[cfg(target_os = "netbsd")]
109 O_NOSIGPIPE;
110 /// Obtain a file descriptor for low-level access.
111 ///
112 /// The file itself is not opened and other file operations will fail.
113 #[cfg(any(target_os = "android", target_os = "linux"))]
114 O_PATH;
115 /// Only allow reading.
116 ///
117 /// This should not be combined with `O_WRONLY` or `O_RDWR`.
118 O_RDONLY;
119 /// Allow both reading and writing.
120 ///
121 /// This should not be combined with `O_WRONLY` or `O_RDONLY`.
122 O_RDWR;
123 /// Similar to `O_DSYNC` but applies to `read`s instead.
124 #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
125 O_RSYNC;
126 /// Skip search permission checks.
127 #[cfg(target_os = "netbsd")]
128 O_SEARCH;
129 /// Open with a shared file lock.
130 #[cfg(any(target_os = "dragonfly",
131 target_os = "freebsd",
132 target_os = "ios",
133 target_os = "macos",
134 target_os = "netbsd",
135 target_os = "openbsd"))]
136 O_SHLOCK;
137 /// Implicitly follow each `write()` with an `fsync()`.
138 O_SYNC;
139 /// Create an unnamed temporary file.
140 #[cfg(any(target_os = "android", target_os = "linux"))]
141 O_TMPFILE;
142 /// Truncate an existing regular file to 0 length if it allows writing.
143 O_TRUNC;
144 /// Restore default TTY attributes.
145 #[cfg(target_os = "freebsd")]
146 O_TTY_INIT;
147 /// Only allow writing.
148 ///
149 /// This should not be combined with `O_RDONLY` or `O_RDWR`.
150 O_WRONLY;
151 }
152 );
153
open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd>154 pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
155 let fd = path.with_nix_path(|cstr| {
156 let modebits = c_uint::from(mode.bits());
157 unsafe { libc::open(cstr.as_ptr(), oflag.bits(), modebits) }
158 })?;
159
160 Errno::result(fd)
161 }
162
openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd>163 pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
164 let fd = path.with_nix_path(|cstr| {
165 let modebits = c_uint::from(mode.bits());
166 unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), modebits) }
167 })?;
168 Errno::result(fd)
169 }
170
renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(old_dirfd: Option<RawFd>, old_path: &P1, new_dirfd: Option<RawFd>, new_path: &P2) -> Result<()>171 pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(old_dirfd: Option<RawFd>, old_path: &P1,
172 new_dirfd: Option<RawFd>, new_path: &P2)
173 -> Result<()> {
174 let res = old_path.with_nix_path(|old_cstr| {
175 new_path.with_nix_path(|new_cstr| unsafe {
176 libc::renameat(at_rawfd(old_dirfd), old_cstr.as_ptr(),
177 at_rawfd(new_dirfd), new_cstr.as_ptr())
178 })
179 })??;
180 Errno::result(res).map(drop)
181 }
182
wrap_readlink_result(v: &mut Vec<u8>, res: ssize_t) -> Result<OsString>183 fn wrap_readlink_result(v: &mut Vec<u8>, res: ssize_t) -> Result<OsString> {
184 match Errno::result(res) {
185 Err(err) => Err(err),
186 Ok(len) => {
187 unsafe { v.set_len(len as usize) }
188 Ok(OsString::from_vec(v.to_vec()))
189 }
190 }
191 }
192
readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString>193 pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
194 let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
195 let res = path.with_nix_path(|cstr| {
196 unsafe { libc::readlink(cstr.as_ptr(), v.as_mut_ptr() as *mut c_char, v.capacity() as size_t) }
197 })?;
198
199 wrap_readlink_result(&mut v, res)
200 }
201
202
readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString>203 pub fn readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString> {
204 let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
205 let res = path.with_nix_path(|cstr| {
206 unsafe { libc::readlinkat(dirfd, cstr.as_ptr(), v.as_mut_ptr() as *mut c_char, v.capacity() as size_t) }
207 })?;
208
209 wrap_readlink_result(&mut v, res)
210 }
211
212 /// Computes the raw fd consumed by a function of the form `*at`.
at_rawfd(fd: Option<RawFd>) -> raw::c_int213 pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
214 match fd {
215 None => libc::AT_FDCWD,
216 Some(fd) => fd,
217 }
218 }
219
220 #[cfg(any(target_os = "android", target_os = "linux"))]
221 libc_bitflags!(
222 /// Additional flags for file sealing, which allows for limiting operations on a file.
223 pub struct SealFlag: c_int {
224 /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`.
225 F_SEAL_SEAL;
226 /// The file cannot be reduced in size.
227 F_SEAL_SHRINK;
228 /// The size of the file cannot be increased.
229 F_SEAL_GROW;
230 /// The file contents cannot be modified.
231 F_SEAL_WRITE;
232 }
233 );
234
235 libc_bitflags!(
236 /// Additional configuration flags for `fcntl`'s `F_SETFD`.
237 pub struct FdFlag: c_int {
238 /// The file descriptor will automatically be closed during a successful `execve(2)`.
239 FD_CLOEXEC;
240 }
241 );
242
243 #[derive(Debug, Eq, Hash, PartialEq)]
244 pub enum FcntlArg<'a> {
245 F_DUPFD(RawFd),
246 F_DUPFD_CLOEXEC(RawFd),
247 F_GETFD,
248 F_SETFD(FdFlag), // FD_FLAGS
249 F_GETFL,
250 F_SETFL(OFlag), // O_NONBLOCK
251 F_SETLK(&'a libc::flock),
252 F_SETLKW(&'a libc::flock),
253 F_GETLK(&'a mut libc::flock),
254 #[cfg(any(target_os = "linux", target_os = "android"))]
255 F_OFD_SETLK(&'a libc::flock),
256 #[cfg(any(target_os = "linux", target_os = "android"))]
257 F_OFD_SETLKW(&'a libc::flock),
258 #[cfg(any(target_os = "linux", target_os = "android"))]
259 F_OFD_GETLK(&'a mut libc::flock),
260 #[cfg(any(target_os = "android", target_os = "linux"))]
261 F_ADD_SEALS(SealFlag),
262 #[cfg(any(target_os = "android", target_os = "linux"))]
263 F_GET_SEALS,
264 #[cfg(any(target_os = "macos", target_os = "ios"))]
265 F_FULLFSYNC,
266 #[cfg(any(target_os = "linux", target_os = "android"))]
267 F_GETPIPE_SZ,
268 #[cfg(any(target_os = "linux", target_os = "android"))]
269 F_SETPIPE_SZ(c_int),
270
271 // TODO: Rest of flags
272 }
273 pub use self::FcntlArg::*;
274
275 // TODO: Figure out how to handle value fcntl returns
fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int>276 pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
277 let res = unsafe {
278 match arg {
279 F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
280 F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd),
281 F_GETFD => libc::fcntl(fd, libc::F_GETFD),
282 F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
283 F_GETFL => libc::fcntl(fd, libc::F_GETFL),
284 F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
285 F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
286 F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
287 F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
288 #[cfg(any(target_os = "android", target_os = "linux"))]
289 F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()),
290 #[cfg(any(target_os = "android", target_os = "linux"))]
291 F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
292 #[cfg(any(target_os = "macos", target_os = "ios"))]
293 F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
294 #[cfg(any(target_os = "linux", target_os = "android"))]
295 F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
296 #[cfg(any(target_os = "linux", target_os = "android"))]
297 F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
298 #[cfg(any(target_os = "linux", target_os = "android"))]
299 _ => unimplemented!()
300 }
301 };
302
303 Errno::result(res)
304 }
305
306 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
307 pub enum FlockArg {
308 LockShared,
309 LockExclusive,
310 Unlock,
311 LockSharedNonblock,
312 LockExclusiveNonblock,
313 UnlockNonblock,
314 }
315
flock(fd: RawFd, arg: FlockArg) -> Result<()>316 pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
317 use self::FlockArg::*;
318
319 let res = unsafe {
320 match arg {
321 LockShared => libc::flock(fd, libc::LOCK_SH),
322 LockExclusive => libc::flock(fd, libc::LOCK_EX),
323 Unlock => libc::flock(fd, libc::LOCK_UN),
324 LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB),
325 LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
326 UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
327 }
328 };
329
330 Errno::result(res).map(drop)
331 }
332
333 #[cfg(any(target_os = "android", target_os = "linux"))]
334 libc_bitflags! {
335 /// Additional flags to `splice` and friends.
336 pub struct SpliceFFlags: c_uint {
337 /// Request that pages be moved instead of copied.
338 ///
339 /// Not applicable to `vmsplice`.
340 SPLICE_F_MOVE;
341 /// Do not block on I/O.
342 SPLICE_F_NONBLOCK;
343 /// Hint that more data will be coming in a subsequent splice.
344 ///
345 /// Not applicable to `vmsplice`.
346 SPLICE_F_MORE;
347 /// Gift the user pages to the kernel.
348 ///
349 /// Not applicable to `splice`.
350 SPLICE_F_GIFT;
351 }
352 }
353
354 /// Copy a range of data from one file to another
355 ///
356 /// The `copy_file_range` system call performs an in-kernel copy between
357 /// file descriptors `fd_in` and `fd_out` without the additional cost of
358 /// transferring data from the kernel to user space and then back into the
359 /// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to
360 /// file descriptor `fd_out`, overwriting any data that exists within the
361 /// requested range of the target file.
362 ///
363 /// If the `off_in` and/or `off_out` arguments are used, the values
364 /// will be mutated to reflect the new position within the file after
365 /// copying. If they are not used, the relevant filedescriptors will be seeked
366 /// to the new position.
367 ///
368 /// On successful completion the number of bytes actually copied will be
369 /// returned.
370 #[cfg(any(target_os = "android", target_os = "linux"))]
copy_file_range( fd_in: RawFd, off_in: Option<&mut libc::loff_t>, fd_out: RawFd, off_out: Option<&mut libc::loff_t>, len: usize, ) -> Result<usize>371 pub fn copy_file_range(
372 fd_in: RawFd,
373 off_in: Option<&mut libc::loff_t>,
374 fd_out: RawFd,
375 off_out: Option<&mut libc::loff_t>,
376 len: usize,
377 ) -> Result<usize> {
378 let off_in = off_in
379 .map(|offset| offset as *mut libc::loff_t)
380 .unwrap_or(ptr::null_mut());
381 let off_out = off_out
382 .map(|offset| offset as *mut libc::loff_t)
383 .unwrap_or(ptr::null_mut());
384
385 let ret = unsafe {
386 libc::syscall(
387 libc::SYS_copy_file_range,
388 fd_in,
389 off_in,
390 fd_out,
391 off_out,
392 len,
393 0,
394 )
395 };
396 Errno::result(ret).map(|r| r as usize)
397 }
398
399 #[cfg(any(target_os = "linux", target_os = "android"))]
splice( fd_in: RawFd, off_in: Option<&mut libc::loff_t>, fd_out: RawFd, off_out: Option<&mut libc::loff_t>, len: usize, flags: SpliceFFlags, ) -> Result<usize>400 pub fn splice(
401 fd_in: RawFd,
402 off_in: Option<&mut libc::loff_t>,
403 fd_out: RawFd,
404 off_out: Option<&mut libc::loff_t>,
405 len: usize,
406 flags: SpliceFFlags,
407 ) -> Result<usize> {
408 let off_in = off_in
409 .map(|offset| offset as *mut libc::loff_t)
410 .unwrap_or(ptr::null_mut());
411 let off_out = off_out
412 .map(|offset| offset as *mut libc::loff_t)
413 .unwrap_or(ptr::null_mut());
414
415 let ret = unsafe {
416 libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits())
417 };
418 Errno::result(ret).map(|r| r as usize)
419 }
420
421 #[cfg(any(target_os = "linux", target_os = "android"))]
tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize>422 pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
423 let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
424 Errno::result(ret).map(|r| r as usize)
425 }
426
427 #[cfg(any(target_os = "linux", target_os = "android"))]
vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize>428 pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> {
429 let ret = unsafe {
430 libc::vmsplice(fd, iov.as_ptr() as *const libc::iovec, iov.len(), flags.bits())
431 };
432 Errno::result(ret).map(|r| r as usize)
433 }
434
435 #[cfg(any(target_os = "linux"))]
436 libc_bitflags!(
437 /// Mode argument flags for fallocate determining operation performed on a given range.
438 pub struct FallocateFlags: c_int {
439 /// File size is not changed.
440 ///
441 /// offset + len can be greater than file size.
442 FALLOC_FL_KEEP_SIZE;
443 /// Deallocates space by creating a hole.
444 ///
445 /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes.
446 FALLOC_FL_PUNCH_HOLE;
447 /// Removes byte range from a file without leaving a hole.
448 ///
449 /// Byte range to collapse starts at offset and continues for len bytes.
450 FALLOC_FL_COLLAPSE_RANGE;
451 /// Zeroes space in specified byte range.
452 ///
453 /// Byte range starts at offset and continues for len bytes.
454 FALLOC_FL_ZERO_RANGE;
455 /// Increases file space by inserting a hole within the file size.
456 ///
457 /// Does not overwrite existing data. Hole starts at offset and continues for len bytes.
458 FALLOC_FL_INSERT_RANGE;
459 /// Shared file data extants are made private to the file.
460 ///
461 /// Gaurantees that a subsequent write will not fail due to lack of space.
462 FALLOC_FL_UNSHARE_RANGE;
463 }
464 );
465
466 /// Manipulates file space.
467 ///
468 /// Allows the caller to directly manipulate the allocated disk space for the
469 /// file referred to by fd.
470 #[cfg(any(target_os = "linux"))]
fallocate(fd: RawFd, mode: FallocateFlags, offset: libc::off_t, len: libc::off_t) -> Result<c_int>471 pub fn fallocate(fd: RawFd, mode: FallocateFlags, offset: libc::off_t, len: libc::off_t) -> Result<c_int> {
472 let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
473 Errno::result(res)
474 }
475
476 #[cfg(any(target_os = "linux",
477 target_os = "android",
478 target_os = "emscripten",
479 target_os = "fuchsia",
480 any(target_os = "wasi", target_env = "wasi"),
481 target_env = "uclibc",
482 target_env = "freebsd"))]
483 mod posix_fadvise {
484 use Result;
485 use libc;
486 use errno::Errno;
487 use std::os::unix::io::RawFd;
488
489 libc_enum! {
490 #[repr(i32)]
491 pub enum PosixFadviseAdvice {
492 POSIX_FADV_NORMAL,
493 POSIX_FADV_SEQUENTIAL,
494 POSIX_FADV_RANDOM,
495 POSIX_FADV_NOREUSE,
496 POSIX_FADV_WILLNEED,
497 POSIX_FADV_DONTNEED,
498 }
499 }
500
posix_fadvise(fd: RawFd, offset: libc::off_t, len: libc::off_t, advice: PosixFadviseAdvice) -> Result<libc::c_int>501 pub fn posix_fadvise(fd: RawFd,
502 offset: libc::off_t,
503 len: libc::off_t,
504 advice: PosixFadviseAdvice) -> Result<libc::c_int> {
505 let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
506 Errno::result(res)
507 }
508 }
509
510 #[cfg(any(
511 target_os = "linux",
512 target_os = "android",
513 target_os = "emscripten",
514 target_os = "fuchsia",
515 any(target_os = "wasi", target_env = "wasi"),
516 target_os = "freebsd"
517 ))]
posix_fallocate( fd: RawFd, offset: libc::off_t, len: libc::off_t ) -> Result<()>518 pub fn posix_fallocate(
519 fd: RawFd,
520 offset: libc::off_t,
521 len: libc::off_t
522 ) -> Result<()> {
523 let res = unsafe { libc::posix_fallocate(fd, offset, len) };
524 match Errno::result(res) {
525 Err(err) => Err(err),
526 Ok(0) => Ok(()),
527 Ok(errno) => Err(crate::Error::Sys(Errno::from_i32(errno))),
528 }
529 }
530