blob: 48d46626b6f7be2b09173d35e5c39154190dda37 [file] [log] [blame]
Andrew Walbran12f61402020-10-14 11:10:53 +01001#[cfg(not(target_os = "redox"))]
2use nix::Error;
3#[cfg(not(target_os = "redox"))]
4use nix::errno::*;
5#[cfg(not(target_os = "redox"))]
6use nix::fcntl::{open, OFlag, readlink};
7#[cfg(not(target_os = "redox"))]
8use nix::fcntl::{openat, readlinkat, renameat};
9#[cfg(not(target_os = "redox"))]
10use nix::sys::stat::Mode;
11#[cfg(not(target_os = "redox"))]
12use nix::unistd::{close, read};
13#[cfg(not(target_os = "redox"))]
14use tempfile::{self, NamedTempFile};
15#[cfg(not(target_os = "redox"))]
16use std::fs::File;
17#[cfg(not(target_os = "redox"))]
18use std::io::prelude::*;
19#[cfg(not(target_os = "redox"))]
20use std::os::unix::fs;
21
Joel Galenson4727c112021-04-02 12:22:24 -070022use crate::*;
23
Andrew Walbran12f61402020-10-14 11:10:53 +010024#[test]
25#[cfg(not(target_os = "redox"))]
26fn test_openat() {
27 const CONTENTS: &[u8] = b"abcd";
28 let mut tmp = NamedTempFile::new().unwrap();
29 tmp.write_all(CONTENTS).unwrap();
30
31 let dirfd = open(tmp.path().parent().unwrap(),
32 OFlag::empty(),
33 Mode::empty()).unwrap();
34 let fd = openat(dirfd,
35 tmp.path().file_name().unwrap(),
36 OFlag::O_RDONLY,
37 Mode::empty()).unwrap();
38
39 let mut buf = [0u8; 1024];
40 assert_eq!(4, read(fd, &mut buf).unwrap());
41 assert_eq!(CONTENTS, &buf[0..4]);
42
43 close(fd).unwrap();
44 close(dirfd).unwrap();
45}
46
47#[test]
48#[cfg(not(target_os = "redox"))]
49fn test_renameat() {
50 let old_dir = tempfile::tempdir().unwrap();
51 let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
52 let old_path = old_dir.path().join("old");
53 File::create(&old_path).unwrap();
54 let new_dir = tempfile::tempdir().unwrap();
55 let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
56 renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
57 assert_eq!(renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
58 Error::Sys(Errno::ENOENT));
59 close(old_dirfd).unwrap();
60 close(new_dirfd).unwrap();
61 assert!(new_dir.path().join("new").exists());
62}
63
64#[test]
65#[cfg(not(target_os = "redox"))]
66fn test_readlink() {
67 let tempdir = tempfile::tempdir().unwrap();
68 let src = tempdir.path().join("a");
69 let dst = tempdir.path().join("b");
70 println!("a: {:?}, b: {:?}", &src, &dst);
71 fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
72 let dirfd = open(tempdir.path(),
73 OFlag::empty(),
74 Mode::empty()).unwrap();
75 let expected_dir = src.to_str().unwrap();
76
77 assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
78 assert_eq!(readlinkat(dirfd, "b").unwrap().to_str().unwrap(), expected_dir);
79
80}
81
82#[cfg(any(target_os = "linux", target_os = "android"))]
83mod linux_android {
Andrew Walbran12f61402020-10-14 11:10:53 +010084 use std::io::prelude::*;
Joel Galensone7950d92021-06-21 14:41:02 -070085 use std::io::SeekFrom;
Andrew Walbran12f61402020-10-14 11:10:53 +010086 use std::os::unix::prelude::*;
Andrew Walbran12f61402020-10-14 11:10:53 +010087 use libc::loff_t;
88
89 use nix::fcntl::*;
Andrew Walbran12f61402020-10-14 11:10:53 +010090 use nix::sys::uio::IoVec;
91 use nix::unistd::{close, pipe, read, write};
92
Joel Galensone7950d92021-06-21 14:41:02 -070093 use tempfile::tempfile;
94 #[cfg(any(target_os = "linux"))]
95 use tempfile::NamedTempFile;
Andrew Walbran12f61402020-10-14 11:10:53 +010096
Joel Galenson4727c112021-04-02 12:22:24 -070097 use crate::*;
98
Andrew Walbran12f61402020-10-14 11:10:53 +010099 /// This test creates a temporary file containing the contents
100 /// 'foobarbaz' and uses the `copy_file_range` call to transfer
101 /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
102 /// resulting file is read and should contain the contents `bar`.
103 /// The from_offset should be updated by the call to reflect
104 /// the 3 bytes read (6).
105 ///
106 /// FIXME: This test is disabled for linux based builds, because Travis
107 /// Linux version is too old for `copy_file_range`.
108 #[test]
109 #[ignore]
110 fn test_copy_file_range() {
111 const CONTENTS: &[u8] = b"foobarbaz";
112
113 let mut tmp1 = tempfile().unwrap();
114 let mut tmp2 = tempfile().unwrap();
115
116 tmp1.write_all(CONTENTS).unwrap();
117 tmp1.flush().unwrap();
118
119 let mut from_offset: i64 = 3;
120 copy_file_range(
121 tmp1.as_raw_fd(),
122 Some(&mut from_offset),
123 tmp2.as_raw_fd(),
124 None,
125 3,
126 )
127 .unwrap();
128
129 let mut res: String = String::new();
130 tmp2.seek(SeekFrom::Start(0)).unwrap();
131 tmp2.read_to_string(&mut res).unwrap();
132
133 assert_eq!(res, String::from("bar"));
134 assert_eq!(from_offset, 6);
135 }
136
137 #[test]
138 fn test_splice() {
139 const CONTENTS: &[u8] = b"abcdef123456";
140 let mut tmp = tempfile().unwrap();
141 tmp.write_all(CONTENTS).unwrap();
142
143 let (rd, wr) = pipe().unwrap();
144 let mut offset: loff_t = 5;
145 let res = splice(tmp.as_raw_fd(), Some(&mut offset),
146 wr, None, 2, SpliceFFlags::empty()).unwrap();
147
148 assert_eq!(2, res);
149
150 let mut buf = [0u8; 1024];
151 assert_eq!(2, read(rd, &mut buf).unwrap());
152 assert_eq!(b"f1", &buf[0..2]);
153 assert_eq!(7, offset);
154
155 close(rd).unwrap();
156 close(wr).unwrap();
157 }
158
159 #[test]
160 fn test_tee() {
161 let (rd1, wr1) = pipe().unwrap();
162 let (rd2, wr2) = pipe().unwrap();
163
164 write(wr1, b"abc").unwrap();
165 let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();
166
167 assert_eq!(2, res);
168
169 let mut buf = [0u8; 1024];
170
171 // Check the tee'd bytes are at rd2.
172 assert_eq!(2, read(rd2, &mut buf).unwrap());
173 assert_eq!(b"ab", &buf[0..2]);
174
175 // Check all the bytes are still at rd1.
176 assert_eq!(3, read(rd1, &mut buf).unwrap());
177 assert_eq!(b"abc", &buf[0..3]);
178
179 close(rd1).unwrap();
180 close(wr1).unwrap();
181 close(rd2).unwrap();
182 close(wr2).unwrap();
183 }
184
185 #[test]
186 fn test_vmsplice() {
187 let (rd, wr) = pipe().unwrap();
188
189 let buf1 = b"abcdef";
190 let buf2 = b"defghi";
191 let mut iovecs = Vec::with_capacity(2);
192 iovecs.push(IoVec::from_slice(&buf1[0..3]));
193 iovecs.push(IoVec::from_slice(&buf2[0..3]));
194
195 let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();
196
197 assert_eq!(6, res);
198
199 // Check the bytes can be read at rd.
200 let mut buf = [0u8; 32];
201 assert_eq!(6, read(rd, &mut buf).unwrap());
202 assert_eq!(b"abcdef", &buf[0..6]);
203
204 close(rd).unwrap();
205 close(wr).unwrap();
206 }
207
Joel Galensone7950d92021-06-21 14:41:02 -0700208 #[cfg(any(target_os = "linux"))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100209 #[test]
210 fn test_fallocate() {
211 let tmp = NamedTempFile::new().unwrap();
212
213 let fd = tmp.as_raw_fd();
214 fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();
215
216 // Check if we read exactly 100 bytes
217 let mut buf = [0u8; 200];
218 assert_eq!(100, read(fd, &mut buf).unwrap());
219 }
220
221 // The tests below are disabled for the listed targets
222 // due to OFD locks not being available in the kernel/libc
223 // versions used in the CI environment, probably because
224 // they run under QEMU.
225
226 #[test]
Joel Galensone7950d92021-06-21 14:41:02 -0700227 #[cfg(all(target_os = "linux", not(target_env = "musl")))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100228 fn test_ofd_write_lock() {
Joel Galensone7950d92021-06-21 14:41:02 -0700229 use nix::sys::stat::fstat;
230 use std::mem;
231
Andrew Walbran12f61402020-10-14 11:10:53 +0100232 let tmp = NamedTempFile::new().unwrap();
233
234 let fd = tmp.as_raw_fd();
235 let statfs = nix::sys::statfs::fstatfs(&tmp).unwrap();
236 if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC {
237 // OverlayFS is a union file system. It returns one inode value in
238 // stat(2), but a different one shows up in /proc/locks. So we must
239 // skip the test.
240 skip!("/proc/locks does not work on overlayfs");
241 }
242 let inode = fstat(fd).expect("fstat failed").st_ino as usize;
243
Joel Galensone7950d92021-06-21 14:41:02 -0700244 let mut flock: libc::flock = unsafe {
245 mem::zeroed() // required for Linux/mips
Andrew Walbran12f61402020-10-14 11:10:53 +0100246 };
Joel Galensone7950d92021-06-21 14:41:02 -0700247 flock.l_type = libc::F_WRLCK as libc::c_short;
248 flock.l_whence = libc::SEEK_SET as libc::c_short;
249 flock.l_start = 0;
250 flock.l_len = 0;
251 flock.l_pid = 0;
Andrew Walbran12f61402020-10-14 11:10:53 +0100252 fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
253 assert_eq!(
254 Some(("OFDLCK".to_string(), "WRITE".to_string())),
255 lock_info(inode)
256 );
257
258 flock.l_type = libc::F_UNLCK as libc::c_short;
259 fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed");
260 assert_eq!(None, lock_info(inode));
261 }
262
263 #[test]
Joel Galensone7950d92021-06-21 14:41:02 -0700264 #[cfg(all(target_os = "linux", not(target_env = "musl")))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100265 fn test_ofd_read_lock() {
Joel Galensone7950d92021-06-21 14:41:02 -0700266 use nix::sys::stat::fstat;
267 use std::mem;
268
Andrew Walbran12f61402020-10-14 11:10:53 +0100269 let tmp = NamedTempFile::new().unwrap();
270
271 let fd = tmp.as_raw_fd();
272 let statfs = nix::sys::statfs::fstatfs(&tmp).unwrap();
273 if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC {
274 // OverlayFS is a union file system. It returns one inode value in
275 // stat(2), but a different one shows up in /proc/locks. So we must
276 // skip the test.
277 skip!("/proc/locks does not work on overlayfs");
278 }
279 let inode = fstat(fd).expect("fstat failed").st_ino as usize;
280
Joel Galensone7950d92021-06-21 14:41:02 -0700281 let mut flock: libc::flock = unsafe {
282 mem::zeroed() // required for Linux/mips
Andrew Walbran12f61402020-10-14 11:10:53 +0100283 };
Joel Galensone7950d92021-06-21 14:41:02 -0700284 flock.l_type = libc::F_RDLCK as libc::c_short;
285 flock.l_whence = libc::SEEK_SET as libc::c_short;
286 flock.l_start = 0;
287 flock.l_len = 0;
288 flock.l_pid = 0;
Andrew Walbran12f61402020-10-14 11:10:53 +0100289 fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
290 assert_eq!(
291 Some(("OFDLCK".to_string(), "READ".to_string())),
292 lock_info(inode)
293 );
294
295 flock.l_type = libc::F_UNLCK as libc::c_short;
296 fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed");
297 assert_eq!(None, lock_info(inode));
298 }
299
Joel Galensone7950d92021-06-21 14:41:02 -0700300 #[cfg(all(target_os = "linux", not(target_env = "musl")))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100301 fn lock_info(inode: usize) -> Option<(String, String)> {
Joel Galensone7950d92021-06-21 14:41:02 -0700302 use std::{
303 fs::File,
304 io::BufReader
305 };
306
Andrew Walbran12f61402020-10-14 11:10:53 +0100307 let file = File::open("/proc/locks").expect("open /proc/locks failed");
308 let buf = BufReader::new(file);
309
310 for line in buf.lines() {
311 let line = line.unwrap();
312 let parts: Vec<_> = line.split_whitespace().collect();
313 let lock_type = parts[1];
314 let lock_access = parts[3];
315 let ino_parts: Vec<_> = parts[5].split(':').collect();
316 let ino: usize = ino_parts[2].parse().unwrap();
317 if ino == inode {
318 return Some((lock_type.to_string(), lock_access.to_string()));
319 }
320 }
321 None
322 }
323}
324
325#[cfg(any(target_os = "linux",
326 target_os = "android",
327 target_os = "emscripten",
328 target_os = "fuchsia",
329 any(target_os = "wasi", target_env = "wasi"),
330 target_env = "uclibc",
Joel Galenson4727c112021-04-02 12:22:24 -0700331 target_os = "freebsd"))]
Andrew Walbran12f61402020-10-14 11:10:53 +0100332mod test_posix_fadvise {
333
334 use tempfile::NamedTempFile;
335 use std::os::unix::io::{RawFd, AsRawFd};
336 use nix::errno::Errno;
337 use nix::fcntl::*;
338 use nix::unistd::pipe;
339
340 #[test]
341 fn test_success() {
342 let tmp = NamedTempFile::new().unwrap();
343 let fd = tmp.as_raw_fd();
344 let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();
345
346 assert_eq!(res, 0);
347 }
348
349 #[test]
350 fn test_errno() {
351 let (rd, _wr) = pipe().unwrap();
352 let errno = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
353 .unwrap();
354 assert_eq!(errno, Errno::ESPIPE as i32);
355 }
356}
357
358#[cfg(any(target_os = "linux",
359 target_os = "android",
360 target_os = "emscripten",
361 target_os = "fuchsia",
362 any(target_os = "wasi", target_env = "wasi"),
363 target_os = "freebsd"))]
364mod test_posix_fallocate {
365
366 use tempfile::NamedTempFile;
367 use std::{io::Read, os::unix::io::{RawFd, AsRawFd}};
368 use nix::errno::Errno;
369 use nix::fcntl::*;
370 use nix::unistd::pipe;
371
372 #[test]
373 fn success() {
374 const LEN: usize = 100;
375 let mut tmp = NamedTempFile::new().unwrap();
376 let fd = tmp.as_raw_fd();
377 let res = posix_fallocate(fd, 0, LEN as libc::off_t);
378 match res {
379 Ok(_) => {
380 let mut data = [1u8; LEN];
381 assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
382 assert_eq!(&data[..], &[0u8; LEN][..]);
383 }
384 Err(nix::Error::Sys(Errno::EINVAL)) => {
385 // POSIX requires posix_fallocate to return EINVAL both for
386 // invalid arguments (i.e. len < 0) and if the operation is not
387 // supported by the file system.
388 // There's no way to tell for sure whether the file system
389 // supports posix_fallocate, so we must pass the test if it
390 // returns EINVAL.
391 }
392 _ => res.unwrap(),
393 }
394 }
395
396 #[test]
397 fn errno() {
398 let (rd, _wr) = pipe().unwrap();
399 let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
400 use nix::Error::Sys;
401 match err {
402 Sys(Errno::EINVAL)
403 | Sys(Errno::ENODEV)
404 | Sys(Errno::ESPIPE)
405 | Sys(Errno::EBADF) => (),
406 errno =>
407 panic!(
408 "unexpected errno {}",
409 errno,
410 ),
411 }
412 }
413}