| ... | ... | @@ -44,13 +44,12 @@ test "check WASI CWD" { |
| 44 | 44 | } |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | | test "chdir smoke test" { |
| 47 | test "chdir absolute parent" { |
| 48 | 48 | if (native_os == .wasi) return error.SkipZigTest; |
| 49 | 49 | |
| 50 | | if (true) { |
| 51 | | // https://github.com/ziglang/zig/issues/14968 |
| 52 | | return error.SkipZigTest; |
| 53 | | } |
| 50 | // Restore default CWD at end of test. |
| 51 | const orig_cwd = try fs.cwd().openDir(".", .{}); |
| 52 | defer orig_cwd.setAsCwd() catch unreachable; |
| 54 | 53 | |
| 55 | 54 | // Get current working directory path |
| 56 | 55 | var old_cwd_buf: [fs.max_path_bytes]u8 = undefined; |
| ... | ... | @@ -65,47 +64,46 @@ test "chdir smoke test" { |
| 65 | 64 | } |
| 66 | 65 | |
| 67 | 66 | // Next, change current working directory to one level above |
| 68 | | if (native_os != .wasi) { // WASI does not support navigating outside of Preopens |
| 69 | | const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute |
| 70 | | try posix.chdir(parent); |
| 67 | const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute |
| 68 | try posix.chdir(parent); |
| 71 | 69 | |
| 72 | | // Restore cwd because process may have other tests that do not tolerate chdir. |
| 73 | | defer posix.chdir(old_cwd) catch unreachable; |
| 70 | var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; |
| 71 | const new_cwd = try posix.getcwd(new_cwd_buf[0..]); |
| 72 | try expect(mem.eql(u8, parent, new_cwd)); |
| 73 | } |
| 74 | 74 | |
| 75 | | var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; |
| 76 | | const new_cwd = try posix.getcwd(new_cwd_buf[0..]); |
| 77 | | try expect(mem.eql(u8, parent, new_cwd)); |
| 78 | | } |
| 75 | test "chdir relative" { |
| 76 | if (native_os == .wasi) return error.SkipZigTest; |
| 79 | 77 | |
| 80 | | // Next, change current working directory to a temp directory one level below |
| 81 | | { |
| 82 | | // Create a tmp directory |
| 83 | | var tmp_dir_buf: [fs.max_path_bytes]u8 = undefined; |
| 84 | | const tmp_dir_path = path: { |
| 85 | | var allocator = std.heap.FixedBufferAllocator.init(&tmp_dir_buf); |
| 86 | | break :path try fs.path.resolve(allocator.allocator(), &[_][]const u8{ old_cwd, "zig-test-tmp" }); |
| 87 | | }; |
| 88 | | var tmp_dir = try fs.cwd().makeOpenPath("zig-test-tmp", .{}); |
| 78 | var tmp = tmpDir(.{}); |
| 79 | defer tmp.cleanup(); |
| 89 | 80 | |
| 90 | | // Change current working directory to tmp directory |
| 91 | | try posix.chdir("zig-test-tmp"); |
| 81 | // Restore default CWD at end of test. |
| 82 | const orig_cwd = try fs.cwd().openDir(".", .{}); |
| 83 | defer orig_cwd.setAsCwd() catch unreachable; |
| 92 | 84 | |
| 93 | | var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; |
| 94 | | const new_cwd = try posix.getcwd(new_cwd_buf[0..]); |
| 85 | // Use the tmpDir parent_dir as the "base" for the test. Then cd into the child |
| 86 | try tmp.parent_dir.setAsCwd(); |
| 95 | 87 | |
| 96 | | // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase |
| 97 | | var resolved_cwd_buf: [fs.max_path_bytes]u8 = undefined; |
| 98 | | const resolved_cwd = path: { |
| 99 | | var allocator = std.heap.FixedBufferAllocator.init(&resolved_cwd_buf); |
| 100 | | break :path try fs.path.resolve(allocator.allocator(), &[_][]const u8{new_cwd}); |
| 101 | | }; |
| 102 | | try expect(mem.eql(u8, tmp_dir_path, resolved_cwd)); |
| 88 | // Capture base working directory path, to build expected full path |
| 89 | var base_cwd_buf: [fs.max_path_bytes]u8 = undefined; |
| 90 | const base_cwd = try posix.getcwd(base_cwd_buf[0..]); |
| 103 | 91 | |
| 104 | | // Restore cwd because process may have other tests that do not tolerate chdir. |
| 105 | | tmp_dir.close(); |
| 106 | | posix.chdir(old_cwd) catch unreachable; |
| 107 | | try fs.cwd().deleteDir("zig-test-tmp"); |
| 108 | | } |
| 92 | const dir_name = &tmp.sub_path; |
| 93 | const expected_path = try fs.path.resolve(a, &.{ base_cwd, dir_name }); |
| 94 | defer a.free(expected_path); |
| 95 | |
| 96 | // change current working directory to new directory |
| 97 | try posix.chdir(dir_name); |
| 98 | |
| 99 | var new_cwd_buf: [fs.max_path_bytes]u8 = undefined; |
| 100 | const new_cwd = try posix.getcwd(new_cwd_buf[0..]); |
| 101 | |
| 102 | // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase |
| 103 | const resolved_cwd = try fs.path.resolve(a, &.{new_cwd}); |
| 104 | defer a.free(resolved_cwd); |
| 105 | |
| 106 | try expect(mem.eql(u8, expected_path, resolved_cwd)); |
| 109 | 107 | } |
| 110 | 108 | |
| 111 | 109 | test "open smoke test" { |
| ... | ... | @@ -222,44 +220,42 @@ test "openat smoke test" { |
| 222 | 220 | } |
| 223 | 221 | |
| 224 | 222 | test "symlink with relative paths" { |
| 225 | | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 223 | if (native_os == .wasi) return error.SkipZigTest; // Can symlink, but can't change into tmpDir |
| 226 | 224 | |
| 227 | | if (true) { |
| 228 | | // https://github.com/ziglang/zig/issues/14968 |
| 229 | | return error.SkipZigTest; |
| 230 | | } |
| 231 | | const cwd = fs.cwd(); |
| 232 | | cwd.deleteFile("file.txt") catch {}; |
| 233 | | cwd.deleteFile("symlinked") catch {}; |
| 225 | var tmp = tmpDir(.{}); |
| 226 | defer tmp.cleanup(); |
| 227 | |
| 228 | const target_name = "symlink-target"; |
| 229 | const symlink_name = "symlinker"; |
| 234 | 230 | |
| 235 | | // First, try relative paths in cwd |
| 236 | | try cwd.writeFile(.{ .sub_path = "file.txt", .data = "nonsense" }); |
| 231 | // Restore default CWD at end of test. |
| 232 | const orig_cwd = try fs.cwd().openDir(".", .{}); |
| 233 | defer orig_cwd.setAsCwd() catch unreachable; |
| 234 | |
| 235 | // Create the target file |
| 236 | try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "nonsense" }); |
| 237 | |
| 238 | // Want to test relative paths, so cd into the tmpdir for this test |
| 239 | try tmp.dir.setAsCwd(); |
| 237 | 240 | |
| 238 | 241 | if (native_os == .windows) { |
| 239 | | std.os.windows.CreateSymbolicLink( |
| 240 | | cwd.fd, |
| 241 | | &[_]u16{ 's', 'y', 'm', 'l', 'i', 'n', 'k', 'e', 'd' }, |
| 242 | | &[_:0]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, |
| 243 | | false, |
| 244 | | ) catch |err| switch (err) { |
| 242 | const wtarget_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, target_name); |
| 243 | const wsymlink_name = try std.unicode.wtf8ToWtf16LeAllocZ(a, symlink_name); |
| 244 | defer a.free(wtarget_name); |
| 245 | defer a.free(wsymlink_name); |
| 246 | |
| 247 | std.os.windows.CreateSymbolicLink(tmp.dir.fd, wsymlink_name, wtarget_name, false) catch |err| switch (err) { |
| 245 | 248 | // Symlink requires admin privileges on windows, so this test can legitimately fail. |
| 246 | | error.AccessDenied => { |
| 247 | | try cwd.deleteFile("file.txt"); |
| 248 | | try cwd.deleteFile("symlinked"); |
| 249 | | return error.SkipZigTest; |
| 250 | | }, |
| 249 | error.AccessDenied => return error.SkipZigTest, |
| 251 | 250 | else => return err, |
| 252 | 251 | }; |
| 253 | 252 | } else { |
| 254 | | try posix.symlink("file.txt", "symlinked"); |
| 253 | try posix.symlink(target_name, symlink_name); |
| 255 | 254 | } |
| 256 | 255 | |
| 257 | 256 | var buffer: [fs.max_path_bytes]u8 = undefined; |
| 258 | | const given = try posix.readlink("symlinked", buffer[0..]); |
| 259 | | try expect(mem.eql(u8, "file.txt", given)); |
| 260 | | |
| 261 | | try cwd.deleteFile("file.txt"); |
| 262 | | try cwd.deleteFile("symlinked"); |
| 257 | const given = try posix.readlink(symlink_name, buffer[0..]); |
| 258 | try expect(mem.eql(u8, target_name, given)); |
| 263 | 259 | } |
| 264 | 260 | |
| 265 | 261 | test "readlink on Windows" { |
| ... | ... | @@ -277,90 +273,95 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { |
| 277 | 273 | } |
| 278 | 274 | |
| 279 | 275 | test "link with relative paths" { |
| 280 | | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 276 | if (native_os == .wasi) return error.SkipZigTest; // Can link, but can't change into tmpDir |
| 277 | if (builtin.cpu.arch == .riscv32 and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstat()`. |
| 281 | 278 | |
| 282 | 279 | switch (native_os) { |
| 283 | 280 | .wasi, .linux, .solaris, .illumos => {}, |
| 284 | 281 | else => return error.SkipZigTest, |
| 285 | 282 | } |
| 286 | | if (true) { |
| 287 | | // https://github.com/ziglang/zig/issues/14968 |
| 288 | | return error.SkipZigTest; |
| 289 | | } |
| 290 | | var cwd = fs.cwd(); |
| 291 | 283 | |
| 292 | | cwd.deleteFile("example.txt") catch {}; |
| 293 | | cwd.deleteFile("new.txt") catch {}; |
| 284 | var tmp = tmpDir(.{}); |
| 285 | defer tmp.cleanup(); |
| 286 | |
| 287 | // Restore default CWD at end of test. |
| 288 | const orig_cwd = try fs.cwd().openDir(".", .{}); |
| 289 | defer orig_cwd.setAsCwd() catch unreachable; |
| 290 | |
| 291 | const target_name = "link-target"; |
| 292 | const link_name = "newlink"; |
| 293 | |
| 294 | try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" }); |
| 294 | 295 | |
| 295 | | try cwd.writeFile(.{ .sub_path = "example.txt", .data = "example" }); |
| 296 | | try posix.link("example.txt", "new.txt"); |
| 296 | // Test 1: create the relative link from inside tmp |
| 297 | try tmp.dir.setAsCwd(); |
| 298 | try posix.link(target_name, link_name); |
| 297 | 299 | |
| 298 | | const efd = try cwd.openFile("example.txt", .{}); |
| 300 | // Verify |
| 301 | const efd = try tmp.dir.openFile(target_name, .{}); |
| 299 | 302 | defer efd.close(); |
| 300 | 303 | |
| 301 | | const nfd = try cwd.openFile("new.txt", .{}); |
| 304 | const nfd = try tmp.dir.openFile(link_name, .{}); |
| 302 | 305 | defer nfd.close(); |
| 303 | 306 | |
| 304 | 307 | { |
| 305 | 308 | const estat = try posix.fstat(efd.handle); |
| 306 | 309 | const nstat = try posix.fstat(nfd.handle); |
| 307 | | |
| 308 | 310 | try testing.expectEqual(estat.ino, nstat.ino); |
| 309 | 311 | try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); |
| 310 | 312 | } |
| 311 | 313 | |
| 312 | | try posix.unlink("new.txt"); |
| 314 | // Test 2: Remove the link and see the stats update |
| 315 | try posix.unlink(link_name); |
| 313 | 316 | |
| 314 | 317 | { |
| 315 | 318 | const estat = try posix.fstat(efd.handle); |
| 316 | 319 | try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); |
| 317 | 320 | } |
| 318 | | |
| 319 | | try cwd.deleteFile("example.txt"); |
| 320 | 321 | } |
| 321 | 322 | |
| 322 | 323 | test "linkat with different directories" { |
| 323 | | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 324 | if (builtin.cpu.arch == .riscv32 and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstatat()`. |
| 324 | 325 | |
| 325 | 326 | switch (native_os) { |
| 326 | 327 | .wasi, .linux, .solaris, .illumos => {}, |
| 327 | 328 | else => return error.SkipZigTest, |
| 328 | 329 | } |
| 329 | | if (true) { |
| 330 | | // https://github.com/ziglang/zig/issues/14968 |
| 331 | | return error.SkipZigTest; |
| 332 | | } |
| 333 | | var cwd = fs.cwd(); |
| 330 | |
| 334 | 331 | var tmp = tmpDir(.{}); |
| 332 | defer tmp.cleanup(); |
| 333 | |
| 334 | const target_name = "link-target"; |
| 335 | const link_name = "newlink"; |
| 335 | 336 | |
| 336 | | cwd.deleteFile("example.txt") catch {}; |
| 337 | | tmp.dir.deleteFile("new.txt") catch {}; |
| 337 | const subdir = try tmp.dir.makeOpenPath("subdir", .{}); |
| 338 | 338 | |
| 339 | | try cwd.writeFile(.{ .sub_path = "example.txt", .data = "example" }); |
| 340 | | try posix.linkat(cwd.fd, "example.txt", tmp.dir.fd, "new.txt", 0); |
| 339 | defer tmp.dir.deleteFile(target_name) catch {}; |
| 340 | try tmp.dir.writeFile(.{ .sub_path = target_name, .data = "example" }); |
| 341 | 341 | |
| 342 | | const efd = try cwd.openFile("example.txt", .{}); |
| 342 | // Test 1: link from file in subdir back up to target in parent directory |
| 343 | try posix.linkat(tmp.dir.fd, target_name, subdir.fd, link_name, 0); |
| 344 | |
| 345 | const efd = try tmp.dir.openFile(target_name, .{}); |
| 343 | 346 | defer efd.close(); |
| 344 | 347 | |
| 345 | | const nfd = try tmp.dir.openFile("new.txt", .{}); |
| 348 | const nfd = try subdir.openFile(link_name, .{}); |
| 349 | defer nfd.close(); |
| 346 | 350 | |
| 347 | 351 | { |
| 348 | | defer nfd.close(); |
| 349 | 352 | const estat = try posix.fstat(efd.handle); |
| 350 | 353 | const nstat = try posix.fstat(nfd.handle); |
| 351 | | |
| 352 | 354 | try testing.expectEqual(estat.ino, nstat.ino); |
| 353 | 355 | try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); |
| 354 | 356 | } |
| 355 | 357 | |
| 356 | | try posix.unlinkat(tmp.dir.fd, "new.txt", 0); |
| 358 | // Test 2: remove link |
| 359 | try posix.unlinkat(subdir.fd, link_name, 0); |
| 357 | 360 | |
| 358 | 361 | { |
| 359 | 362 | const estat = try posix.fstat(efd.handle); |
| 360 | 363 | try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); |
| 361 | 364 | } |
| 362 | | |
| 363 | | try cwd.deleteFile("example.txt"); |
| 364 | 365 | } |
| 365 | 366 | |
| 366 | 367 | test "fstatat" { |
| ... | ... | @@ -979,7 +980,7 @@ test "POSIX file locking with fcntl" { |
| 979 | 980 | return error.SkipZigTest; |
| 980 | 981 | } |
| 981 | 982 | |
| 982 | | var tmp = std.testing.tmpDir(.{}); |
| 983 | var tmp = tmpDir(.{}); |
| 983 | 984 | defer tmp.cleanup(); |
| 984 | 985 | |
| 985 | 986 | // Create a temporary lock file |