1use rustix::fd::{AsFd, AsRawFd, OwnedFd}; 2use rustix::fs::{cwd, mkdirat, openat, openat2, symlinkat, Mode, OFlags, ResolveFlags}; 3use rustix::{io, path}; 4 5/// Like `openat2`, but keep retrying until it fails or succeeds. 6fn openat2_more<Fd: AsFd, P: path::Arg>( 7 dirfd: Fd, 8 path: P, 9 oflags: OFlags, 10 mode: Mode, 11 resolve: ResolveFlags, 12) -> io::Result<OwnedFd> { 13 let path = path.as_cow_c_str().unwrap().into_owned(); 14 loop { 15 match openat2(dirfd.as_fd(), &path, oflags, mode, resolve) { 16 Ok(file) => return Ok(file), 17 Err(io::Errno::AGAIN) => continue, 18 Err(err) => return Err(err), 19 } 20 } 21} 22 23#[test] 24fn test_openat2() { 25 let tmp = tempfile::tempdir().unwrap(); 26 let dir = openat(cwd(), tmp.path(), OFlags::RDONLY, Mode::empty()).unwrap(); 27 28 // Detect whether `openat2` is available. 29 match openat2( 30 &dir, 31 ".", 32 OFlags::RDONLY | OFlags::CLOEXEC, 33 Mode::empty(), 34 ResolveFlags::empty(), 35 ) { 36 Ok(_file) => (), 37 Err(io::Errno::NOSYS) => return, 38 Err(_err) => return, 39 } 40 41 // Create a file. 42 let _ = openat2_more( 43 &dir, 44 "test.txt", 45 OFlags::WRONLY | OFlags::CREATE | OFlags::TRUNC | OFlags::CLOEXEC, 46 Mode::RUSR, 47 ResolveFlags::empty(), 48 ) 49 .unwrap(); 50 51 // Test `NO_SYMLINKS`. 52 symlinkat("test.txt", &dir, "symlink.txt").unwrap(); 53 let _ = openat2_more( 54 &dir, 55 "symlink.txt", 56 OFlags::RDONLY | OFlags::CLOEXEC, 57 Mode::empty(), 58 ResolveFlags::empty(), 59 ) 60 .unwrap(); 61 let _ = openat2_more( 62 &dir, 63 "symlink.txt", 64 OFlags::RDONLY | OFlags::CLOEXEC, 65 Mode::empty(), 66 ResolveFlags::NO_MAGICLINKS, 67 ) 68 .unwrap(); 69 let _ = openat2_more( 70 &dir, 71 "symlink.txt", 72 OFlags::RDONLY | OFlags::CLOEXEC, 73 Mode::empty(), 74 ResolveFlags::NO_SYMLINKS, 75 ) 76 .unwrap_err(); 77 78 // Test `NO_MAGICLINKS`. 79 let test = openat2_more( 80 &dir, 81 "test.txt", 82 OFlags::RDONLY | OFlags::CLOEXEC, 83 Mode::empty(), 84 ResolveFlags::empty(), 85 ) 86 .unwrap(); 87 let _ = openat2_more( 88 &dir, 89 &format!("/proc/self/fd/{}", test.as_fd().as_raw_fd()), 90 OFlags::RDONLY | OFlags::CLOEXEC, 91 Mode::empty(), 92 ResolveFlags::empty(), 93 ) 94 .unwrap(); 95 let _ = openat2_more( 96 &dir, 97 &format!("/proc/self/fd/{}", test.as_fd().as_raw_fd()), 98 OFlags::RDONLY | OFlags::CLOEXEC, 99 Mode::empty(), 100 ResolveFlags::NO_SYMLINKS, 101 ) 102 .unwrap_err(); 103 let _ = openat2_more( 104 &dir, 105 &format!("/proc/self/fd/{}", test.as_fd().as_raw_fd()), 106 OFlags::RDONLY | OFlags::CLOEXEC, 107 Mode::empty(), 108 ResolveFlags::NO_MAGICLINKS, 109 ) 110 .unwrap_err(); 111 112 // Test `NO_XDEV`. 113 let root = openat2_more( 114 &dir, 115 "/", 116 OFlags::RDONLY | OFlags::CLOEXEC, 117 Mode::empty(), 118 ResolveFlags::empty(), 119 ) 120 .unwrap(); 121 let _ = openat2_more( 122 &root, 123 "proc", 124 OFlags::RDONLY | OFlags::CLOEXEC, 125 Mode::empty(), 126 ResolveFlags::empty(), 127 ) 128 .unwrap(); 129 let _ = openat2_more( 130 &root, 131 "proc", 132 OFlags::RDONLY | OFlags::CLOEXEC, 133 Mode::empty(), 134 ResolveFlags::NO_XDEV, 135 ) 136 .unwrap_err(); 137 138 // Test `BENEATH`. 139 let _ = openat2_more( 140 &dir, 141 "..", 142 OFlags::RDONLY | OFlags::CLOEXEC, 143 Mode::empty(), 144 ResolveFlags::empty(), 145 ) 146 .unwrap(); 147 let _ = openat2_more( 148 &dir, 149 "..", 150 OFlags::RDONLY | OFlags::CLOEXEC, 151 Mode::empty(), 152 ResolveFlags::BENEATH, 153 ) 154 .unwrap_err(); 155 156 // Test `IN_ROOT`. 157 let _ = openat2_more( 158 &dir, 159 "/proc", 160 OFlags::RDONLY | OFlags::CLOEXEC, 161 Mode::empty(), 162 ResolveFlags::empty(), 163 ) 164 .unwrap(); 165 let _ = openat2_more( 166 &dir, 167 "/proc", 168 OFlags::RDONLY | OFlags::CLOEXEC, 169 Mode::empty(), 170 ResolveFlags::IN_ROOT, 171 ) 172 .unwrap_err(); 173 mkdirat(&dir, "proc", Mode::RUSR | Mode::XUSR).unwrap(); 174 let _ = openat2_more( 175 &dir, 176 "/proc", 177 OFlags::RDONLY | OFlags::CLOEXEC, 178 Mode::empty(), 179 ResolveFlags::IN_ROOT, 180 ) 181 .unwrap(); 182} 183