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