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