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