1 use {Error, 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::OsStr;
8 use std::os::unix::ffi::OsStrExt;
9 
10 #[cfg(any(target_os = "android", target_os = "linux"))]
11 use sys::uio::IoVec;  // For vmsplice
12 
13 libc_bitflags!{
14     pub struct AtFlags: c_int {
15         AT_SYMLINK_NOFOLLOW;
16         #[cfg(any(target_os = "android", target_os = "linux"))]
17         AT_NO_AUTOMOUNT;
18         #[cfg(any(target_os = "android", target_os = "linux"))]
19         AT_EMPTY_PATH;
20     }
21 }
22 
23 libc_bitflags!(
24     /// Configuration options for opened files.
25     pub struct OFlag: c_int {
26         /// Mask for the access mode of the file.
27         O_ACCMODE;
28         /// Use alternate I/O semantics.
29         #[cfg(target_os = "netbsd")]
30         O_ALT_IO;
31         /// Open the file in append-only mode.
32         O_APPEND;
33         /// Generate a signal when input or output becomes possible.
34         O_ASYNC;
35         /// Closes the file descriptor once an `execve` call is made.
36         ///
37         /// Also sets the file offset to the beginning of the file.
38         O_CLOEXEC;
39         /// Create the file if it does not exist.
40         O_CREAT;
41         /// Try to minimize cache effects of the I/O for this file.
42         #[cfg(any(target_os = "android",
43                   target_os = "dragonfly",
44                   target_os = "freebsd",
45                   target_os = "linux",
46                   target_os = "netbsd"))]
47         O_DIRECT;
48         /// If the specified path isn't a directory, fail.
49         O_DIRECTORY;
50         /// Implicitly follow each `write()` with an `fdatasync()`.
51         #[cfg(any(target_os = "android",
52                   target_os = "ios",
53                   target_os = "linux",
54                   target_os = "macos",
55                   target_os = "netbsd",
56                   target_os = "openbsd"))]
57         O_DSYNC;
58         /// Error out if a file was not created.
59         O_EXCL;
60         /// Open for execute only.
61         #[cfg(target_os = "freebsd")]
62         O_EXEC;
63         /// Open with an exclusive file lock.
64         #[cfg(any(target_os = "dragonfly",
65                   target_os = "freebsd",
66                   target_os = "ios",
67                   target_os = "macos",
68                   target_os = "netbsd",
69                   target_os = "openbsd"))]
70         O_EXLOCK;
71         /// Same as `O_SYNC`.
72         #[cfg(any(target_os = "dragonfly",
73                   target_os = "freebsd",
74                   target_os = "ios",
75                   all(target_os = "linux", not(target_env = "musl")),
76                   target_os = "macos",
77                   target_os = "netbsd",
78                   target_os = "openbsd"))]
79         O_FSYNC;
80         /// Allow files whose sizes can't be represented in an `off_t` to be opened.
81         #[cfg(any(target_os = "android", target_os = "linux"))]
82         O_LARGEFILE;
83         /// Do not update the file last access time during `read(2)`s.
84         #[cfg(any(target_os = "android", target_os = "linux"))]
85         O_NOATIME;
86         /// Don't attach the device as the process' controlling terminal.
87         O_NOCTTY;
88         /// Same as `O_NONBLOCK`.
89         O_NDELAY;
90         /// `open()` will fail if the given path is a symbolic link.
91         O_NOFOLLOW;
92         /// When possible, open the file in nonblocking mode.
93         O_NONBLOCK;
94         /// Don't deliver `SIGPIPE`.
95         #[cfg(target_os = "netbsd")]
96         O_NOSIGPIPE;
97         /// Obtain a file descriptor for low-level access.
98         ///
99         /// The file itself is not opened and other file operations will fail.
100         #[cfg(any(target_os = "android", target_os = "linux"))]
101         O_PATH;
102         /// Only allow reading.
103         ///
104         /// This should not be combined with `O_WRONLY` or `O_RDWR`.
105         O_RDONLY;
106         /// Allow both reading and writing.
107         ///
108         /// This should not be combined with `O_WRONLY` or `O_RDONLY`.
109         O_RDWR;
110         /// Similar to `O_DSYNC` but applies to `read`s instead.
111         #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
112         O_RSYNC;
113         /// Skip search permission checks.
114         #[cfg(target_os = "netbsd")]
115         O_SEARCH;
116         /// Open with a shared file lock.
117         #[cfg(any(target_os = "dragonfly",
118                   target_os = "freebsd",
119                   target_os = "ios",
120                   target_os = "macos",
121                   target_os = "netbsd",
122                   target_os = "openbsd"))]
123         O_SHLOCK;
124         /// Implicitly follow each `write()` with an `fsync()`.
125         O_SYNC;
126         /// Create an unnamed temporary file.
127         #[cfg(any(target_os = "android", target_os = "linux"))]
128         O_TMPFILE;
129         /// Truncate an existing regular file to 0 length if it allows writing.
130         O_TRUNC;
131         /// Restore default TTY attributes.
132         #[cfg(target_os = "freebsd")]
133         O_TTY_INIT;
134         /// Only allow writing.
135         ///
136         /// This should not be combined with `O_RDONLY` or `O_RDWR`.
137         O_WRONLY;
138     }
139 );
140 
open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd>141 pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
142     let fd = path.with_nix_path(|cstr| {
143         unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
144     })?;
145 
146     Errno::result(fd)
147 }
148 
openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd>149 pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
150     let fd = path.with_nix_path(|cstr| {
151         unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
152     })?;
153     Errno::result(fd)
154 }
155 
wrap_readlink_result(buffer: &mut[u8], res: ssize_t) -> Result<&OsStr>156 fn wrap_readlink_result(buffer: &mut[u8], res: ssize_t) -> Result<&OsStr> {
157     match Errno::result(res) {
158         Err(err) => Err(err),
159         Ok(len) => {
160             if (len as usize) >= buffer.len() {
161                 Err(Error::Sys(Errno::ENAMETOOLONG))
162             } else {
163                 Ok(OsStr::from_bytes(&buffer[..(len as usize)]))
164             }
165         }
166     }
167 }
168 
readlink<'a, P: ?Sized + NixPath>(path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr>169 pub fn readlink<'a, P: ?Sized + NixPath>(path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
170     let res = path.with_nix_path(|cstr| {
171         unsafe { libc::readlink(cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
172     })?;
173 
174     wrap_readlink_result(buffer, res)
175 }
176 
177 
readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr>178 pub fn readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P, buffer: &'a mut [u8]) -> Result<&'a OsStr> {
179     let res = path.with_nix_path(|cstr| {
180         unsafe { libc::readlinkat(dirfd, cstr.as_ptr(), buffer.as_mut_ptr() as *mut c_char, buffer.len() as size_t) }
181     })?;
182 
183     wrap_readlink_result(buffer, res)
184 }
185 
186 /// Computes the raw fd consumed by a function of the form `*at`.
at_rawfd(fd: Option<RawFd>) -> raw::c_int187 pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
188     match fd {
189         None => libc::AT_FDCWD,
190         Some(fd) => fd,
191     }
192 }
193 
194 #[cfg(any(target_os = "android", target_os = "linux"))]
195 libc_bitflags!(
196     /// Additional flags for file sealing, which allows for limiting operations on a file.
197     pub struct SealFlag: c_int {
198         /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`.
199         F_SEAL_SEAL;
200         /// The file cannot be reduced in size.
201         F_SEAL_SHRINK;
202         /// The size of the file cannot be increased.
203         F_SEAL_GROW;
204         /// The file contents cannot be modified.
205         F_SEAL_WRITE;
206     }
207 );
208 
209 libc_bitflags!(
210     /// Additional configuration flags for `fcntl`'s `F_SETFD`.
211     pub struct FdFlag: c_int {
212         /// The file descriptor will automatically be closed during a successful `execve(2)`.
213         FD_CLOEXEC;
214     }
215 );
216 
217 #[allow(missing_debug_implementations)]
218 pub enum FcntlArg<'a> {
219     F_DUPFD(RawFd),
220     F_DUPFD_CLOEXEC(RawFd),
221     F_GETFD,
222     F_SETFD(FdFlag), // FD_FLAGS
223     F_GETFL,
224     F_SETFL(OFlag), // O_NONBLOCK
225     F_SETLK(&'a libc::flock),
226     F_SETLKW(&'a libc::flock),
227     F_GETLK(&'a mut libc::flock),
228     #[cfg(any(target_os = "linux", target_os = "android"))]
229     F_OFD_SETLK(&'a libc::flock),
230     #[cfg(any(target_os = "linux", target_os = "android"))]
231     F_OFD_SETLKW(&'a libc::flock),
232     #[cfg(any(target_os = "linux", target_os = "android"))]
233     F_OFD_GETLK(&'a mut libc::flock),
234     #[cfg(any(target_os = "android", target_os = "linux"))]
235     F_ADD_SEALS(SealFlag),
236     #[cfg(any(target_os = "android", target_os = "linux"))]
237     F_GET_SEALS,
238     #[cfg(any(target_os = "macos", target_os = "ios"))]
239     F_FULLFSYNC,
240     #[cfg(any(target_os = "linux", target_os = "android"))]
241     F_GETPIPE_SZ,
242     #[cfg(any(target_os = "linux", target_os = "android"))]
243     F_SETPIPE_SZ(c_int),
244 
245     // TODO: Rest of flags
246 }
247 pub use self::FcntlArg::*;
248 
249 // TODO: Figure out how to handle value fcntl returns
fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int>250 pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
251     let res = unsafe {
252         match arg {
253             F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
254             F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd),
255             F_GETFD => libc::fcntl(fd, libc::F_GETFD),
256             F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
257             F_GETFL => libc::fcntl(fd, libc::F_GETFL),
258             F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
259             F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
260             F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
261             F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
262             #[cfg(any(target_os = "android", target_os = "linux"))]
263             F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()),
264             #[cfg(any(target_os = "android", target_os = "linux"))]
265             F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
266             #[cfg(any(target_os = "macos", target_os = "ios"))]
267             F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
268             #[cfg(any(target_os = "linux", target_os = "android"))]
269             F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
270             #[cfg(any(target_os = "linux", target_os = "android"))]
271             F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
272             #[cfg(any(target_os = "linux", target_os = "android"))]
273             _ => unimplemented!()
274         }
275     };
276 
277     Errno::result(res)
278 }
279 
280 #[derive(Clone, Copy)]
281 #[allow(missing_debug_implementations)]
282 pub enum FlockArg {
283     LockShared,
284     LockExclusive,
285     Unlock,
286     LockSharedNonblock,
287     LockExclusiveNonblock,
288     UnlockNonblock,
289 }
290 
flock(fd: RawFd, arg: FlockArg) -> Result<()>291 pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
292     use self::FlockArg::*;
293 
294     let res = unsafe {
295         match arg {
296             LockShared => libc::flock(fd, libc::LOCK_SH),
297             LockExclusive => libc::flock(fd, libc::LOCK_EX),
298             Unlock => libc::flock(fd, libc::LOCK_UN),
299             LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB),
300             LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
301             UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
302         }
303     };
304 
305     Errno::result(res).map(drop)
306 }
307 
308 #[cfg(any(target_os = "android", target_os = "linux"))]
309 libc_bitflags! {
310     /// Additional flags to `splice` and friends.
311     pub struct SpliceFFlags: c_uint {
312         /// Request that pages be moved instead of copied.
313         ///
314         /// Not applicable to `vmsplice`.
315         SPLICE_F_MOVE;
316         /// Do not block on I/O.
317         SPLICE_F_NONBLOCK;
318         /// Hint that more data will be coming in a subsequent splice.
319         ///
320         /// Not applicable to `vmsplice`.
321         SPLICE_F_MORE;
322         /// Gift the user pages to the kernel.
323         ///
324         /// Not applicable to `splice`.
325         SPLICE_F_GIFT;
326     }
327 }
328 
329 #[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>330 pub fn splice(fd_in: RawFd, off_in: Option<&mut libc::loff_t>,
331           fd_out: RawFd, off_out: Option<&mut libc::loff_t>,
332           len: usize, flags: SpliceFFlags) -> Result<usize> {
333     use std::ptr;
334     let off_in = off_in.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());
335     let off_out = off_out.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());
336 
337     let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
338     Errno::result(ret).map(|r| r as usize)
339 }
340 
341 #[cfg(any(target_os = "linux", target_os = "android"))]
tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize>342 pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
343     let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
344     Errno::result(ret).map(|r| r as usize)
345 }
346 
347 #[cfg(any(target_os = "linux", target_os = "android"))]
vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize>348 pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> {
349     let ret = unsafe {
350         libc::vmsplice(fd, iov.as_ptr() as *const libc::iovec, iov.len(), flags.bits())
351     };
352     Errno::result(ret).map(|r| r as usize)
353 }
354 
355 #[cfg(any(target_os = "linux"))]
356 libc_bitflags!(
357     /// Mode argument flags for fallocate determining operation performed on a given range.
358     pub struct FallocateFlags: c_int {
359         /// File size is not changed.
360         ///
361         /// offset + len can be greater than file size.
362         FALLOC_FL_KEEP_SIZE;
363         /// Deallocates space by creating a hole.
364         ///
365         /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes.
366         FALLOC_FL_PUNCH_HOLE;
367         /// Removes byte range from a file without leaving a hole.
368         ///
369         /// Byte range to collapse starts at offset and continues for len bytes.
370         FALLOC_FL_COLLAPSE_RANGE;
371         /// Zeroes space in specified byte range.
372         ///
373         /// Byte range starts at offset and continues for len bytes.
374         FALLOC_FL_ZERO_RANGE;
375         /// Increases file space by inserting a hole within the file size.
376         ///
377         /// Does not overwrite existing data. Hole starts at offset and continues for len bytes.
378         FALLOC_FL_INSERT_RANGE;
379         /// Shared file data extants are made private to the file.
380         ///
381         /// Gaurantees that a subsequent write will not fail due to lack of space.
382         FALLOC_FL_UNSHARE_RANGE;
383     }
384 );
385 
386 /// Manipulates file space.
387 ///
388 /// Allows the caller to directly manipulate the allocated disk space for the
389 /// file referred to by fd.
390 #[cfg(any(target_os = "linux"))]
fallocate(fd: RawFd, mode: FallocateFlags, offset: libc::off_t, len: libc::off_t) -> Result<c_int>391 pub fn fallocate(fd: RawFd, mode: FallocateFlags, offset: libc::off_t, len: libc::off_t) -> Result<c_int> {
392     let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
393     Errno::result(res)
394 }
395