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