1 #[cfg(not(target_os = "redox"))]
2 use nix::errno::*;
3 #[cfg(not(target_os = "redox"))]
4 use nix::fcntl::{open, readlink, OFlag};
5 #[cfg(not(target_os = "redox"))]
6 use nix::fcntl::{openat, readlinkat, renameat};
7 #[cfg(all(
8 target_os = "linux",
9 target_env = "gnu",
10 any(
11 target_arch = "x86_64",
12 target_arch = "x32",
13 target_arch = "powerpc",
14 target_arch = "s390x"
15 )
16 ))]
17 use nix::fcntl::{renameat2, RenameFlags};
18 #[cfg(not(target_os = "redox"))]
19 use nix::sys::stat::Mode;
20 #[cfg(not(target_os = "redox"))]
21 use nix::unistd::{close, read};
22 #[cfg(not(target_os = "redox"))]
23 use std::fs::File;
24 #[cfg(not(target_os = "redox"))]
25 use std::io::prelude::*;
26 #[cfg(not(target_os = "redox"))]
27 use std::os::unix::fs;
28 #[cfg(not(target_os = "redox"))]
29 use tempfile::NamedTempFile;
30
31 #[test]
32 #[cfg(not(target_os = "redox"))]
33 // QEMU does not handle openat well enough to satisfy this test
34 // https://gitlab.com/qemu-project/qemu/-/issues/829
35 #[cfg_attr(qemu, ignore)]
test_openat()36 fn test_openat() {
37 const CONTENTS: &[u8] = b"abcd";
38 let mut tmp = NamedTempFile::new().unwrap();
39 tmp.write_all(CONTENTS).unwrap();
40
41 let dirfd =
42 open(tmp.path().parent().unwrap(), OFlag::empty(), Mode::empty())
43 .unwrap();
44 let fd = openat(
45 Some(dirfd),
46 tmp.path().file_name().unwrap(),
47 OFlag::O_RDONLY,
48 Mode::empty(),
49 )
50 .unwrap();
51
52 let mut buf = [0u8; 1024];
53 assert_eq!(4, read(fd, &mut buf).unwrap());
54 assert_eq!(CONTENTS, &buf[0..4]);
55
56 close(fd).unwrap();
57 close(dirfd).unwrap();
58 }
59
60 #[test]
61 #[cfg(not(target_os = "redox"))]
test_renameat()62 fn test_renameat() {
63 let old_dir = tempfile::tempdir().unwrap();
64 let old_dirfd =
65 open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
66 let old_path = old_dir.path().join("old");
67 File::create(old_path).unwrap();
68 let new_dir = tempfile::tempdir().unwrap();
69 let new_dirfd =
70 open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
71 renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
72 assert_eq!(
73 renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
74 Errno::ENOENT
75 );
76 close(old_dirfd).unwrap();
77 close(new_dirfd).unwrap();
78 assert!(new_dir.path().join("new").exists());
79 }
80
81 #[test]
82 #[cfg(all(
83 target_os = "linux",
84 target_env = "gnu",
85 any(
86 target_arch = "x86_64",
87 target_arch = "x32",
88 target_arch = "powerpc",
89 target_arch = "s390x"
90 )
91 ))]
test_renameat2_behaves_like_renameat_with_no_flags()92 fn test_renameat2_behaves_like_renameat_with_no_flags() {
93 let old_dir = tempfile::tempdir().unwrap();
94 let old_dirfd =
95 open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
96 let old_path = old_dir.path().join("old");
97 File::create(old_path).unwrap();
98 let new_dir = tempfile::tempdir().unwrap();
99 let new_dirfd =
100 open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
101 renameat2(
102 Some(old_dirfd),
103 "old",
104 Some(new_dirfd),
105 "new",
106 RenameFlags::empty(),
107 )
108 .unwrap();
109 assert_eq!(
110 renameat2(
111 Some(old_dirfd),
112 "old",
113 Some(new_dirfd),
114 "new",
115 RenameFlags::empty()
116 )
117 .unwrap_err(),
118 Errno::ENOENT
119 );
120 close(old_dirfd).unwrap();
121 close(new_dirfd).unwrap();
122 assert!(new_dir.path().join("new").exists());
123 }
124
125 #[test]
126 #[cfg(all(
127 target_os = "linux",
128 target_env = "gnu",
129 any(
130 target_arch = "x86_64",
131 target_arch = "x32",
132 target_arch = "powerpc",
133 target_arch = "s390x"
134 )
135 ))]
test_renameat2_exchange()136 fn test_renameat2_exchange() {
137 let old_dir = tempfile::tempdir().unwrap();
138 let old_dirfd =
139 open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
140 let old_path = old_dir.path().join("old");
141 {
142 let mut old_f = File::create(&old_path).unwrap();
143 old_f.write_all(b"old").unwrap();
144 }
145 let new_dir = tempfile::tempdir().unwrap();
146 let new_dirfd =
147 open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
148 let new_path = new_dir.path().join("new");
149 {
150 let mut new_f = File::create(&new_path).unwrap();
151 new_f.write_all(b"new").unwrap();
152 }
153 renameat2(
154 Some(old_dirfd),
155 "old",
156 Some(new_dirfd),
157 "new",
158 RenameFlags::RENAME_EXCHANGE,
159 )
160 .unwrap();
161 let mut buf = String::new();
162 let mut new_f = File::open(&new_path).unwrap();
163 new_f.read_to_string(&mut buf).unwrap();
164 assert_eq!(buf, "old");
165 buf = "".to_string();
166 let mut old_f = File::open(&old_path).unwrap();
167 old_f.read_to_string(&mut buf).unwrap();
168 assert_eq!(buf, "new");
169 close(old_dirfd).unwrap();
170 close(new_dirfd).unwrap();
171 }
172
173 #[test]
174 #[cfg(all(
175 target_os = "linux",
176 target_env = "gnu",
177 any(
178 target_arch = "x86_64",
179 target_arch = "x32",
180 target_arch = "powerpc",
181 target_arch = "s390x"
182 )
183 ))]
test_renameat2_noreplace()184 fn test_renameat2_noreplace() {
185 let old_dir = tempfile::tempdir().unwrap();
186 let old_dirfd =
187 open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
188 let old_path = old_dir.path().join("old");
189 File::create(old_path).unwrap();
190 let new_dir = tempfile::tempdir().unwrap();
191 let new_dirfd =
192 open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
193 let new_path = new_dir.path().join("new");
194 File::create(new_path).unwrap();
195 assert_eq!(
196 renameat2(
197 Some(old_dirfd),
198 "old",
199 Some(new_dirfd),
200 "new",
201 RenameFlags::RENAME_NOREPLACE
202 )
203 .unwrap_err(),
204 Errno::EEXIST
205 );
206 close(old_dirfd).unwrap();
207 close(new_dirfd).unwrap();
208 assert!(new_dir.path().join("new").exists());
209 assert!(old_dir.path().join("old").exists());
210 }
211
212 #[test]
213 #[cfg(not(target_os = "redox"))]
test_readlink()214 fn test_readlink() {
215 let tempdir = tempfile::tempdir().unwrap();
216 let src = tempdir.path().join("a");
217 let dst = tempdir.path().join("b");
218 println!("a: {:?}, b: {:?}", &src, &dst);
219 fs::symlink(src.as_path(), dst.as_path()).unwrap();
220 let dirfd = open(tempdir.path(), OFlag::empty(), Mode::empty()).unwrap();
221 let expected_dir = src.to_str().unwrap();
222
223 assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
224 assert_eq!(
225 readlinkat(Some(dirfd), "b").unwrap().to_str().unwrap(),
226 expected_dir
227 );
228 }
229
230 /// This test creates a temporary file containing the contents
231 /// 'foobarbaz' and uses the `copy_file_range` call to transfer
232 /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
233 /// resulting file is read and should contain the contents `bar`.
234 /// The from_offset should be updated by the call to reflect
235 /// the 3 bytes read (6).
236 #[cfg(any(
237 linux_android,
238 // Not available until FreeBSD 13.0
239 all(target_os = "freebsd", fbsd14),
240 ))]
241 #[test]
242 // QEMU does not support copy_file_range. Skip under qemu
243 #[cfg_attr(qemu, ignore)]
test_copy_file_range()244 fn test_copy_file_range() {
245 use nix::fcntl::copy_file_range;
246 use std::os::unix::io::AsFd;
247
248 const CONTENTS: &[u8] = b"foobarbaz";
249
250 let mut tmp1 = tempfile::tempfile().unwrap();
251 let mut tmp2 = tempfile::tempfile().unwrap();
252
253 tmp1.write_all(CONTENTS).unwrap();
254 tmp1.flush().unwrap();
255
256 let mut from_offset: i64 = 3;
257 copy_file_range(
258 tmp1.as_fd(),
259 Some(&mut from_offset),
260 tmp2.as_fd(),
261 None,
262 3,
263 )
264 .unwrap();
265
266 let mut res: String = String::new();
267 tmp2.rewind().unwrap();
268 tmp2.read_to_string(&mut res).unwrap();
269
270 assert_eq!(res, String::from("bar"));
271 assert_eq!(from_offset, 6);
272 }
273
274 #[cfg(linux_android)]
275 mod linux_android {
276 use libc::loff_t;
277 use std::io::prelude::*;
278 use std::io::IoSlice;
279 use std::os::unix::prelude::*;
280
281 use nix::fcntl::*;
282 use nix::unistd::{pipe, read, write};
283
284 use tempfile::tempfile;
285 #[cfg(target_os = "linux")]
286 use tempfile::NamedTempFile;
287
288 use crate::*;
289
290 #[test]
test_splice()291 fn test_splice() {
292 const CONTENTS: &[u8] = b"abcdef123456";
293 let mut tmp = tempfile().unwrap();
294 tmp.write_all(CONTENTS).unwrap();
295
296 let (rd, wr) = pipe().unwrap();
297 let mut offset: loff_t = 5;
298 let res = splice(
299 tmp.as_raw_fd(),
300 Some(&mut offset),
301 wr.as_raw_fd(),
302 None,
303 2,
304 SpliceFFlags::empty(),
305 )
306 .unwrap();
307
308 assert_eq!(2, res);
309
310 let mut buf = [0u8; 1024];
311 assert_eq!(2, read(rd.as_raw_fd(), &mut buf).unwrap());
312 assert_eq!(b"f1", &buf[0..2]);
313 assert_eq!(7, offset);
314 }
315
316 #[test]
test_tee()317 fn test_tee() {
318 let (rd1, wr1) = pipe().unwrap();
319 let (rd2, wr2) = pipe().unwrap();
320
321 write(wr1, b"abc").unwrap();
322 let res =
323 tee(rd1.as_raw_fd(), wr2.as_raw_fd(), 2, SpliceFFlags::empty())
324 .unwrap();
325
326 assert_eq!(2, res);
327
328 let mut buf = [0u8; 1024];
329
330 // Check the tee'd bytes are at rd2.
331 assert_eq!(2, read(rd2.as_raw_fd(), &mut buf).unwrap());
332 assert_eq!(b"ab", &buf[0..2]);
333
334 // Check all the bytes are still at rd1.
335 assert_eq!(3, read(rd1.as_raw_fd(), &mut buf).unwrap());
336 assert_eq!(b"abc", &buf[0..3]);
337 }
338
339 #[test]
test_vmsplice()340 fn test_vmsplice() {
341 let (rd, wr) = pipe().unwrap();
342
343 let buf1 = b"abcdef";
344 let buf2 = b"defghi";
345 let iovecs = [IoSlice::new(&buf1[0..3]), IoSlice::new(&buf2[0..3])];
346
347 let res = vmsplice(wr.as_raw_fd(), &iovecs[..], SpliceFFlags::empty())
348 .unwrap();
349
350 assert_eq!(6, res);
351
352 // Check the bytes can be read at rd.
353 let mut buf = [0u8; 32];
354 assert_eq!(6, read(rd.as_raw_fd(), &mut buf).unwrap());
355 assert_eq!(b"abcdef", &buf[0..6]);
356 }
357
358 #[cfg(target_os = "linux")]
359 #[test]
test_fallocate()360 fn test_fallocate() {
361 let tmp = NamedTempFile::new().unwrap();
362
363 let fd = tmp.as_raw_fd();
364 fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();
365
366 // Check if we read exactly 100 bytes
367 let mut buf = [0u8; 200];
368 assert_eq!(100, read(fd, &mut buf).unwrap());
369 }
370
371 // The tests below are disabled for the listed targets
372 // due to OFD locks not being available in the kernel/libc
373 // versions used in the CI environment, probably because
374 // they run under QEMU.
375
376 #[test]
377 #[cfg(all(target_os = "linux", not(target_env = "musl")))]
378 #[cfg_attr(target_env = "uclibc", ignore)] // uclibc doesn't support OFD locks, but the test should still compile
test_ofd_write_lock()379 fn test_ofd_write_lock() {
380 use nix::sys::stat::fstat;
381 use std::mem;
382
383 let tmp = NamedTempFile::new().unwrap();
384
385 let fd = tmp.as_raw_fd();
386 let statfs = nix::sys::statfs::fstatfs(tmp.as_file()).unwrap();
387 if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC {
388 // OverlayFS is a union file system. It returns one inode value in
389 // stat(2), but a different one shows up in /proc/locks. So we must
390 // skip the test.
391 skip!("/proc/locks does not work on overlayfs");
392 }
393 let inode = fstat(fd).expect("fstat failed").st_ino as usize;
394
395 let mut flock: libc::flock = unsafe {
396 mem::zeroed() // required for Linux/mips
397 };
398 flock.l_type = libc::F_WRLCK as libc::c_short;
399 flock.l_whence = libc::SEEK_SET as libc::c_short;
400 flock.l_start = 0;
401 flock.l_len = 0;
402 flock.l_pid = 0;
403 fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write lock failed");
404 assert_eq!(
405 Some(("OFDLCK".to_string(), "WRITE".to_string())),
406 lock_info(inode)
407 );
408
409 flock.l_type = libc::F_UNLCK as libc::c_short;
410 fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("write unlock failed");
411 assert_eq!(None, lock_info(inode));
412 }
413
414 #[test]
415 #[cfg(all(target_os = "linux", not(target_env = "musl")))]
416 #[cfg_attr(target_env = "uclibc", ignore)] // uclibc doesn't support OFD locks, but the test should still compile
test_ofd_read_lock()417 fn test_ofd_read_lock() {
418 use nix::sys::stat::fstat;
419 use std::mem;
420
421 let tmp = NamedTempFile::new().unwrap();
422
423 let fd = tmp.as_raw_fd();
424 let statfs = nix::sys::statfs::fstatfs(tmp.as_file()).unwrap();
425 if statfs.filesystem_type() == nix::sys::statfs::OVERLAYFS_SUPER_MAGIC {
426 // OverlayFS is a union file system. It returns one inode value in
427 // stat(2), but a different one shows up in /proc/locks. So we must
428 // skip the test.
429 skip!("/proc/locks does not work on overlayfs");
430 }
431 let inode = fstat(fd).expect("fstat failed").st_ino as usize;
432
433 let mut flock: libc::flock = unsafe {
434 mem::zeroed() // required for Linux/mips
435 };
436 flock.l_type = libc::F_RDLCK as libc::c_short;
437 flock.l_whence = libc::SEEK_SET as libc::c_short;
438 flock.l_start = 0;
439 flock.l_len = 0;
440 flock.l_pid = 0;
441 fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read lock failed");
442 assert_eq!(
443 Some(("OFDLCK".to_string(), "READ".to_string())),
444 lock_info(inode)
445 );
446
447 flock.l_type = libc::F_UNLCK as libc::c_short;
448 fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).expect("read unlock failed");
449 assert_eq!(None, lock_info(inode));
450 }
451
452 #[cfg(all(target_os = "linux", not(target_env = "musl")))]
lock_info(inode: usize) -> Option<(String, String)>453 fn lock_info(inode: usize) -> Option<(String, String)> {
454 use std::{fs::File, io::BufReader};
455
456 let file = File::open("/proc/locks").expect("open /proc/locks failed");
457 let buf = BufReader::new(file);
458
459 for line in buf.lines() {
460 let line = line.unwrap();
461 let parts: Vec<_> = line.split_whitespace().collect();
462 let lock_type = parts[1];
463 let lock_access = parts[3];
464 let ino_parts: Vec<_> = parts[5].split(':').collect();
465 let ino: usize = ino_parts[2].parse().unwrap();
466 if ino == inode {
467 return Some((lock_type.to_string(), lock_access.to_string()));
468 }
469 }
470 None
471 }
472 }
473
474 #[cfg(any(
475 linux_android,
476 target_os = "emscripten",
477 target_os = "fuchsia",
478 target_os = "wasi",
479 target_env = "uclibc",
480 target_os = "freebsd"
481 ))]
482 mod test_posix_fadvise {
483
484 use nix::errno::Errno;
485 use nix::fcntl::*;
486 use nix::unistd::pipe;
487 use std::os::unix::io::AsRawFd;
488 use tempfile::NamedTempFile;
489
490 #[test]
test_success()491 fn test_success() {
492 let tmp = NamedTempFile::new().unwrap();
493 let fd = tmp.as_raw_fd();
494 posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
495 .expect("posix_fadvise failed");
496 }
497
498 #[test]
test_errno()499 fn test_errno() {
500 let (rd, _wr) = pipe().unwrap();
501 let res = posix_fadvise(
502 rd.as_raw_fd(),
503 0,
504 100,
505 PosixFadviseAdvice::POSIX_FADV_WILLNEED,
506 );
507 assert_eq!(res, Err(Errno::ESPIPE));
508 }
509 }
510
511 #[cfg(any(
512 linux_android,
513 freebsdlike,
514 target_os = "emscripten",
515 target_os = "fuchsia",
516 target_os = "wasi",
517 ))]
518 mod test_posix_fallocate {
519
520 use nix::errno::Errno;
521 use nix::fcntl::*;
522 use nix::unistd::pipe;
523 use std::{io::Read, os::unix::io::AsRawFd};
524 use tempfile::NamedTempFile;
525
526 #[test]
success()527 fn success() {
528 const LEN: usize = 100;
529 let mut tmp = NamedTempFile::new().unwrap();
530 let fd = tmp.as_raw_fd();
531 let res = posix_fallocate(fd, 0, LEN as libc::off_t);
532 match res {
533 Ok(_) => {
534 let mut data = [1u8; LEN];
535 assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
536 assert_eq!(&data[..], &[0u8; LEN][..]);
537 }
538 Err(Errno::EINVAL) => {
539 // POSIX requires posix_fallocate to return EINVAL both for
540 // invalid arguments (i.e. len < 0) and if the operation is not
541 // supported by the file system.
542 // There's no way to tell for sure whether the file system
543 // supports posix_fallocate, so we must pass the test if it
544 // returns EINVAL.
545 }
546 _ => res.unwrap(),
547 }
548 }
549
550 #[test]
errno()551 fn errno() {
552 let (rd, _wr) = pipe().unwrap();
553 let err = posix_fallocate(rd.as_raw_fd(), 0, 100).unwrap_err();
554 match err {
555 Errno::EINVAL | Errno::ENODEV | Errno::ESPIPE | Errno::EBADF => (),
556 errno => panic!("unexpected errno {errno}",),
557 }
558 }
559 }
560
561 #[cfg(any(target_os = "dragonfly", target_os = "netbsd", apple_targets))]
562 #[test]
test_f_get_path()563 fn test_f_get_path() {
564 use nix::fcntl::*;
565 use std::{os::unix::io::AsRawFd, path::PathBuf};
566
567 let tmp = NamedTempFile::new().unwrap();
568 let fd = tmp.as_raw_fd();
569 let mut path = PathBuf::new();
570 let res =
571 fcntl(fd, FcntlArg::F_GETPATH(&mut path)).expect("get path failed");
572 assert_ne!(res, -1);
573 assert_eq!(
574 path.as_path().canonicalize().unwrap(),
575 tmp.path().canonicalize().unwrap()
576 );
577 }
578
579 #[cfg(apple_targets)]
580 #[test]
test_f_get_path_nofirmlink()581 fn test_f_get_path_nofirmlink() {
582 use nix::fcntl::*;
583 use std::{os::unix::io::AsRawFd, path::PathBuf};
584
585 let tmp = NamedTempFile::new().unwrap();
586 let fd = tmp.as_raw_fd();
587 let mut path = PathBuf::new();
588 let res = fcntl(fd, FcntlArg::F_GETPATH_NOFIRMLINK(&mut path))
589 .expect("get path failed");
590 let mut tmpstr = String::from("/System/Volumes/Data");
591 tmpstr.push_str(
592 &tmp.path()
593 .canonicalize()
594 .unwrap()
595 .into_os_string()
596 .into_string()
597 .unwrap(),
598 );
599 assert_ne!(res, -1);
600 assert_eq!(
601 path.as_path()
602 .canonicalize()
603 .unwrap()
604 .into_os_string()
605 .into_string()
606 .unwrap(),
607 tmpstr
608 );
609 }
610
611 #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
612 #[test]
test_f_kinfo()613 fn test_f_kinfo() {
614 use nix::fcntl::*;
615 use std::{os::unix::io::AsRawFd, path::PathBuf};
616
617 let tmp = NamedTempFile::new().unwrap();
618 // With TMPDIR set with UFS, the vnode name is not entered
619 // into the name cache thus path is always empty.
620 // Therefore, we reopen the tempfile a second time for the test
621 // to pass.
622 let tmp2 = File::open(tmp.path()).unwrap();
623 let fd = tmp2.as_raw_fd();
624 let mut path = PathBuf::new();
625 let res = fcntl(fd, FcntlArg::F_KINFO(&mut path)).expect("get path failed");
626 assert_ne!(res, -1);
627 assert_eq!(path, tmp.path());
628 }
629
630 /// Test `Flock` and associated functions.
631 ///
632 #[cfg(not(any(target_os = "redox", target_os = "solaris")))]
633 mod test_flock {
634 use nix::fcntl::*;
635 use tempfile::NamedTempFile;
636
637 /// Verify that `Flock::lock()` correctly obtains a lock, and subsequently unlocks upon drop.
638 #[test]
verify_lock_and_drop()639 fn verify_lock_and_drop() {
640 // Get 2 `File` handles to same underlying file.
641 let file1 = NamedTempFile::new().unwrap();
642 let file2 = file1.reopen().unwrap();
643 let file1 = file1.into_file();
644
645 // Lock first handle
646 let lock1 = Flock::lock(file1, FlockArg::LockExclusive).unwrap();
647
648 // Attempt to lock second handle
649 let file2 = match Flock::lock(file2, FlockArg::LockExclusiveNonblock) {
650 Ok(_) => panic!("Expected second exclusive lock to fail."),
651 Err((f, _)) => f,
652 };
653
654 // Drop first lock
655 std::mem::drop(lock1);
656
657 // Attempt to lock second handle again (but successfully)
658 if Flock::lock(file2, FlockArg::LockExclusiveNonblock).is_err() {
659 panic!("Expected locking to be successful.");
660 }
661 }
662
663 /// Verify that `Flock::unlock()` correctly obtains unlocks.
664 #[test]
verify_unlock()665 fn verify_unlock() {
666 // Get 2 `File` handles to same underlying file.
667 let file1 = NamedTempFile::new().unwrap();
668 let file2 = file1.reopen().unwrap();
669 let file1 = file1.into_file();
670
671 // Lock first handle
672 let lock1 = Flock::lock(file1, FlockArg::LockExclusive).unwrap();
673
674 // Unlock and retain file so any erroneous flocks also remain present.
675 let _file1 = lock1.unlock().unwrap();
676
677 // Attempt to lock second handle.
678 if Flock::lock(file2, FlockArg::LockExclusiveNonblock).is_err() {
679 panic!("Expected locking to be successful.");
680 }
681 }
682 }
683