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