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