authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-20 15:06:14-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-22 20:57:56-07:00
logd24aaf8847336e12b6571e13d57f6d112452d97d
treea806cdf1d5dc112af321ff54041a4f7f9a247020
parent21bd13626d66c36c327bb317bd09cad979d92327

std.fs.path.resolve: eliminate getcwd() syscall

This is a breaking change to the API. Instead of the first path implicitly being the current working directory, it now asserts that the number of paths passed is greater than zero. Importantly, it never calls getcwd(); instead, it can possibly return ".", or a series of "../". This changes the error set to only be `error{OutOfMemory}`. closes #13613

3 files changed, 183 insertions(+), 216 deletions(-)

lib/std/fs/path.zig+179-211
......@@ -467,55 +467,49 @@ pub fn resolve(allocator: Allocator, paths: []const []const u8) ![]u8 {
467467/// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters.
468468/// Note: all usage of this function should be audited due to the existence of symlinks.
469469/// Without performing actual syscalls, resolving `..` could be incorrect.
470/// This API may break in the future: https://github.com/ziglang/zig/issues/13613
470471pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
471 if (paths.len == 0) {
472 assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd
473 return process.getCwdAlloc(allocator);
474 }
472 assert(paths.len > 0);
475473
476474 // determine which disk designator we will result with, if any
477475 var result_drive_buf = "_:".*;
478 var result_disk_designator: []const u8 = "";
479 var have_drive_kind = WindowsPath.Kind.None;
476 var disk_designator: []const u8 = "";
477 var drive_kind = WindowsPath.Kind.None;
480478 var have_abs_path = false;
481479 var first_index: usize = 0;
482 var max_size: usize = 0;
483480 for (paths) |p, i| {
484481 const parsed = windowsParsePath(p);
485482 if (parsed.is_abs) {
486483 have_abs_path = true;
487484 first_index = i;
488 max_size = result_disk_designator.len;
489485 }
490486 switch (parsed.kind) {
491 WindowsPath.Kind.Drive => {
487 .Drive => {
492488 result_drive_buf[0] = ascii.toUpper(parsed.disk_designator[0]);
493 result_disk_designator = result_drive_buf[0..];
494 have_drive_kind = WindowsPath.Kind.Drive;
489 disk_designator = result_drive_buf[0..];
490 drive_kind = WindowsPath.Kind.Drive;
495491 },
496 WindowsPath.Kind.NetworkShare => {
497 result_disk_designator = parsed.disk_designator;
498 have_drive_kind = WindowsPath.Kind.NetworkShare;
492 .NetworkShare => {
493 disk_designator = parsed.disk_designator;
494 drive_kind = WindowsPath.Kind.NetworkShare;
499495 },
500 WindowsPath.Kind.None => {},
496 .None => {},
501497 }
502 max_size += p.len + 1;
503498 }
504499
505500 // if we will result with a disk designator, loop again to determine
506501 // which is the last time the disk designator is absolutely specified, if any
507502 // and count up the max bytes for paths related to this disk designator
508 if (have_drive_kind != WindowsPath.Kind.None) {
503 if (drive_kind != WindowsPath.Kind.None) {
509504 have_abs_path = false;
510505 first_index = 0;
511 max_size = result_disk_designator.len;
512506 var correct_disk_designator = false;
513507
514508 for (paths) |p, i| {
515509 const parsed = windowsParsePath(p);
516510 if (parsed.kind != WindowsPath.Kind.None) {
517 if (parsed.kind == have_drive_kind) {
518 correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator);
511 if (parsed.kind == drive_kind) {
512 correct_disk_designator = compareDiskDesignators(drive_kind, disk_designator, parsed.disk_designator);
519513 } else {
520514 continue;
521515 }
......@@ -525,92 +519,51 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
525519 }
526520 if (parsed.is_abs) {
527521 first_index = i;
528 max_size = result_disk_designator.len;
529522 have_abs_path = true;
530523 }
531 max_size += p.len + 1;
532524 }
533525 }
534526
535 // Allocate result and fill in the disk designator, calling getCwd if we have to.
536 var result: []u8 = undefined;
537 var result_index: usize = 0;
538
539 if (have_abs_path) {
540 switch (have_drive_kind) {
541 WindowsPath.Kind.Drive => {
542 result = try allocator.alloc(u8, max_size);
527 // Allocate result and fill in the disk designator.
528 var result = std.ArrayList(u8).init(allocator);
529 defer result.deinit();
543530
544 mem.copy(u8, result, result_disk_designator);
545 result_index += result_disk_designator.len;
531 const disk_designator_len: usize = l: {
532 if (!have_abs_path) break :l 0;
533 switch (drive_kind) {
534 .Drive => {
535 try result.appendSlice(disk_designator);
536 break :l disk_designator.len;
546537 },
547 WindowsPath.Kind.NetworkShare => {
548 result = try allocator.alloc(u8, max_size);
538 .NetworkShare => {
549539 var it = mem.tokenize(u8, paths[first_index], "/\\");
550540 const server_name = it.next().?;
551541 const other_name = it.next().?;
552542
553 result[result_index] = '\\';
554 result_index += 1;
555 result[result_index] = '\\';
556 result_index += 1;
557 mem.copy(u8, result[result_index..], server_name);
558 result_index += server_name.len;
559 result[result_index] = '\\';
560 result_index += 1;
561 mem.copy(u8, result[result_index..], other_name);
562 result_index += other_name.len;
563
564 result_disk_designator = result[0..result_index];
543 try result.ensureUnusedCapacity(2 + 1 + server_name.len + other_name.len);
544 result.appendSliceAssumeCapacity("\\\\");
545 result.appendSliceAssumeCapacity(server_name);
546 result.appendAssumeCapacity('\\');
547 result.appendSliceAssumeCapacity(other_name);
548
549 break :l result.items.len;
565550 },
566 WindowsPath.Kind.None => {
567 assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd
568 const cwd = try process.getCwdAlloc(allocator);
569 defer allocator.free(cwd);
570 const parsed_cwd = windowsParsePath(cwd);
571 result = try allocator.alloc(u8, max_size + parsed_cwd.disk_designator.len + 1);
572 mem.copy(u8, result, parsed_cwd.disk_designator);
573 result_index += parsed_cwd.disk_designator.len;
574 result_disk_designator = result[0..parsed_cwd.disk_designator.len];
575 if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
576 result[0] = ascii.toUpper(result[0]);
577 }
578 have_drive_kind = parsed_cwd.kind;
551 .None => {
552 break :l 1;
579553 },
580554 }
581 } else {
582 assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd
583 // TODO call get cwd for the result_disk_designator instead of the global one
584 const cwd = try process.getCwdAlloc(allocator);
585 defer allocator.free(cwd);
586
587 result = try allocator.alloc(u8, max_size + cwd.len + 1);
588
589 mem.copy(u8, result, cwd);
590 result_index += cwd.len;
591 const parsed_cwd = windowsParsePath(result[0..result_index]);
592 result_disk_designator = parsed_cwd.disk_designator;
593 if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
594 result[0] = ascii.toUpper(result[0]);
595 // Remove the trailing slash if present, eg. if the cwd is a root
596 // directory.
597 if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows) {
598 result_index -= 1;
599 }
600 }
601 have_drive_kind = parsed_cwd.kind;
602 }
603 errdefer allocator.free(result);
555 };
604556
605 // Now we know the disk designator to use, if any, and what kind it is. And our result
606 // is big enough to append all the paths to.
607557 var correct_disk_designator = true;
558 var negative_count: usize = 0;
559
608560 for (paths[first_index..]) |p| {
609561 const parsed = windowsParsePath(p);
610562
611 if (parsed.kind != WindowsPath.Kind.None) {
612 if (parsed.kind == have_drive_kind) {
613 correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator);
563 if (parsed.kind != .None) {
564 if (parsed.kind == drive_kind) {
565 const dd = result.items[0..disk_designator_len];
566 correct_disk_designator = compareDiskDesignators(drive_kind, dd, parsed.disk_designator);
614567 } else {
615568 continue;
616569 }
......@@ -619,154 +572,167 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
619572 continue;
620573 }
621574 var it = mem.tokenize(u8, p[parsed.disk_designator.len..], "/\\");
622 while (it.next()) |component| {
575 component: while (it.next()) |component| {
623576 if (mem.eql(u8, component, ".")) {
624577 continue;
625578 } else if (mem.eql(u8, component, "..")) {
626579 while (true) {
627 if (result_index == 0 or result_index == result_disk_designator.len)
628 break;
629 result_index -= 1;
630 if (result[result_index] == '\\' or result[result_index] == '/')
580 if (result.items.len == 0) {
581 negative_count += 1;
582 continue :component;
583 }
584 if (result.items.len == disk_designator_len) {
631585 break;
586 }
587 const end_with_sep = switch (result.items[result.items.len - 1]) {
588 '\\', '/' => true,
589 else => false,
590 };
591 result.items.len -= 1;
592 if (end_with_sep) break;
632593 }
594 } else if (!have_abs_path and result.items.len == 0) {
595 try result.appendSlice(component);
633596 } else {
634 result[result_index] = sep_windows;
635 result_index += 1;
636 mem.copy(u8, result[result_index..], component);
637 result_index += component.len;
597 try result.ensureUnusedCapacity(1 + component.len);
598 result.appendAssumeCapacity('\\');
599 result.appendSliceAssumeCapacity(component);
638600 }
639601 }
640602 }
641603
642 if (result_index == result_disk_designator.len) {
643 result[result_index] = '\\';
644 result_index += 1;
604 if (disk_designator_len != 0 and result.items.len == disk_designator_len) {
605 try result.append('\\');
606 return result.toOwnedSlice();
607 }
608
609 if (result.items.len == 0) {
610 if (negative_count == 0) {
611 return allocator.dupe(u8, ".");
612 } else {
613 const real_result = try allocator.alloc(u8, 3 * negative_count - 1);
614 var count = negative_count - 1;
615 var i: usize = 0;
616 while (count > 0) : (count -= 1) {
617 real_result[i..][0..3].* = "..\\".*;
618 i += 3;
619 }
620 real_result[i..][0..2].* = "..".*;
621 return real_result;
622 }
645623 }
646624
647 return allocator.shrink(result, result_index);
625 if (negative_count == 0) {
626 return result.toOwnedSlice();
627 } else {
628 const real_result = try allocator.alloc(u8, 3 * negative_count + result.items.len);
629 var count = negative_count;
630 var i: usize = 0;
631 while (count > 0) : (count -= 1) {
632 real_result[i..][0..3].* = "..\\".*;
633 i += 3;
634 }
635 mem.copy(u8, real_result[i..], result.items);
636 return real_result;
637 }
648638}
649639
650640/// This function is like a series of `cd` statements executed one after another.
651641/// It resolves "." and "..".
652642/// The result does not have a trailing path separator.
653/// If all paths are relative it uses the current working directory as a starting point.
654/// Note: all usage of this function should be audited due to the existence of symlinks.
655/// Without performing actual syscalls, resolving `..` could be incorrect.
656pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 {
657 if (paths.len == 0) {
658 assert(native_os != .windows); // resolvePosix called on windows can't use getCwd
659 return process.getCwdAlloc(allocator);
660 }
643/// This function does not perform any syscalls. Executing this series of path
644/// lookups on the actual filesystem may produce different results due to
645/// symlinks.
646pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) Allocator.Error![]u8 {
647 assert(paths.len > 0);
661648
662 var first_index: usize = 0;
663 var have_abs = false;
664 var max_size: usize = 0;
665 for (paths) |p, i| {
666 if (isAbsolutePosix(p)) {
667 first_index = i;
668 have_abs = true;
669 max_size = 0;
670 }
671 max_size += p.len + 1;
672 }
673
674 var result: []u8 = undefined;
675 var result_index: usize = 0;
649 var result = std.ArrayList(u8).init(allocator);
650 defer result.deinit();
676651
677 if (have_abs) {
678 result = try allocator.alloc(u8, max_size);
679 } else {
680 assert(native_os != .windows); // resolvePosix called on windows can't use getCwd
681 const cwd = try process.getCwdAlloc(allocator);
682 defer allocator.free(cwd);
683 result = try allocator.alloc(u8, max_size + cwd.len + 1);
684 mem.copy(u8, result, cwd);
685 result_index += cwd.len;
686 }
687 errdefer allocator.free(result);
652 var negative_count: usize = 0;
653 var is_abs = false;
688654
689 for (paths[first_index..]) |p| {
655 for (paths) |p| {
656 if (isAbsolutePosix(p)) {
657 is_abs = true;
658 negative_count = 0;
659 result.clearRetainingCapacity();
660 }
690661 var it = mem.tokenize(u8, p, "/");
691 while (it.next()) |component| {
662 component: while (it.next()) |component| {
692663 if (mem.eql(u8, component, ".")) {
693664 continue;
694665 } else if (mem.eql(u8, component, "..")) {
695666 while (true) {
696 if (result_index == 0)
697 break;
698 result_index -= 1;
699 if (result[result_index] == '/')
700 break;
667 if (result.items.len == 0) {
668 negative_count += @boolToInt(!is_abs);
669 continue :component;
670 }
671 const ends_with_slash = result.items[result.items.len - 1] == '/';
672 result.items.len -= 1;
673 if (ends_with_slash) break;
701674 }
675 } else if (result.items.len > 0 or is_abs) {
676 try result.ensureUnusedCapacity(1 + component.len);
677 result.appendAssumeCapacity('/');
678 result.appendSliceAssumeCapacity(component);
702679 } else {
703 result[result_index] = '/';
704 result_index += 1;
705 mem.copy(u8, result[result_index..], component);
706 result_index += component.len;
680 try result.appendSlice(component);
707681 }
708682 }
709683 }
710684
711 if (result_index == 0) {
712 result[0] = '/';
713 result_index += 1;
685 if (result.items.len == 0) {
686 if (is_abs) {
687 return allocator.dupe(u8, "/");
688 }
689 if (negative_count == 0) {
690 return allocator.dupe(u8, ".");
691 } else {
692 const real_result = try allocator.alloc(u8, 3 * negative_count - 1);
693 var count = negative_count - 1;
694 var i: usize = 0;
695 while (count > 0) : (count -= 1) {
696 real_result[i..][0..3].* = "../".*;
697 i += 3;
698 }
699 real_result[i..][0..2].* = "..".*;
700 return real_result;
701 }
714702 }
715703
716 return allocator.shrink(result, result_index);
704 if (negative_count == 0) {
705 return result.toOwnedSlice();
706 } else {
707 const real_result = try allocator.alloc(u8, 3 * negative_count + result.items.len);
708 var count = negative_count;
709 var i: usize = 0;
710 while (count > 0) : (count -= 1) {
711 real_result[i..][0..3].* = "../".*;
712 i += 3;
713 }
714 mem.copy(u8, real_result[i..], result.items);
715 return real_result;
716 }
717717}
718718
719719test "resolve" {
720 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
721 if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
720 try testResolveWindows(&[_][]const u8{ "a\\b\\c\\", "..\\..\\.." }, "..");
721 try testResolveWindows(&[_][]const u8{"."}, ".");
722722
723 const cwd = try process.getCwdAlloc(testing.allocator);
724 defer testing.allocator.free(cwd);
725 if (native_os == .windows) {
726 if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) {
727 cwd[0] = ascii.toUpper(cwd[0]);
728 }
729 try testResolveWindows(&[_][]const u8{"."}, cwd);
730 } else {
731 try testResolvePosix(&[_][]const u8{ "a/b/c/", "../../.." }, cwd);
732 try testResolvePosix(&[_][]const u8{"."}, cwd);
733 }
723 try testResolvePosix(&[_][]const u8{ "a/b/c/", "../../.." }, "..");
724 try testResolvePosix(&[_][]const u8{"."}, ".");
734725}
735726
736727test "resolveWindows" {
737 if (builtin.target.cpu.arch == .aarch64) {
738 // TODO https://github.com/ziglang/zig/issues/3288
739 return error.SkipZigTest;
740 }
741 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
742 if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
743 if (native_os == .windows) {
744 const cwd = try process.getCwdAlloc(testing.allocator);
745 defer testing.allocator.free(cwd);
746 const parsed_cwd = windowsParsePath(cwd);
747 {
748 const expected = try join(testing.allocator, &[_][]const u8{
749 parsed_cwd.disk_designator,
750 "usr\\local\\lib\\zig\\std\\array_list.zig",
751 });
752 defer testing.allocator.free(expected);
753 if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
754 expected[0] = ascii.toUpper(parsed_cwd.disk_designator[0]);
755 }
756 try testResolveWindows(&[_][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }, expected);
757 }
758 {
759 const expected = try join(testing.allocator, &[_][]const u8{
760 cwd,
761 "usr\\local\\lib\\zig",
762 });
763 defer testing.allocator.free(expected);
764 if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
765 expected[0] = ascii.toUpper(parsed_cwd.disk_designator[0]);
766 }
767 try testResolveWindows(&[_][]const u8{ "usr/local", "lib\\zig" }, expected);
768 }
769 }
728 try testResolveWindows(
729 &[_][]const u8{ "Z:\\", "/usr/local", "lib\\zig\\std\\array_list.zig" },
730 "Z:\\usr\\local\\lib\\zig\\std\\array_list.zig",
731 );
732 try testResolveWindows(
733 &[_][]const u8{ "z:\\", "usr/local", "lib\\zig" },
734 "Z:\\usr\\local\\lib\\zig",
735 );
770736
771737 try testResolveWindows(&[_][]const u8{ "c:\\a\\b\\c", "/hi", "ok" }, "C:\\hi\\ok");
772738 try testResolveWindows(&[_][]const u8{ "c:/blah\\blah", "d:/games", "c:../a" }, "C:\\blah\\a");
......@@ -781,12 +747,12 @@ test "resolveWindows" {
781747 try testResolveWindows(&[_][]const u8{ "c:/", "//server//share" }, "\\\\server\\share\\");
782748 try testResolveWindows(&[_][]const u8{ "c:/", "///some//dir" }, "C:\\some\\dir");
783749 try testResolveWindows(&[_][]const u8{ "C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js" }, "C:\\foo\\tmp.3\\cycles\\root.js");
750
751 // Keep relative paths relative.
752 try testResolveWindows(&[_][]const u8{"a/b"}, "a\\b");
784753}
785754
786755test "resolvePosix" {
787 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
788 if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
789
790756 try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c");
791757 try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e");
792758 try testResolvePosix(&[_][]const u8{ "/a/b/c", "..", "../" }, "/a");
......@@ -797,18 +763,21 @@ test "resolvePosix" {
797763 try testResolvePosix(&[_][]const u8{ "/var/lib", "/../", "file/" }, "/file");
798764 try testResolvePosix(&[_][]const u8{ "/some/dir", ".", "/absolute/" }, "/absolute");
799765 try testResolvePosix(&[_][]const u8{ "/foo/tmp.3/", "../tmp.3/cycles/root.js" }, "/foo/tmp.3/cycles/root.js");
766
767 // Keep relative paths relative.
768 try testResolvePosix(&[_][]const u8{"a/b"}, "a/b");
800769}
801770
802771fn testResolveWindows(paths: []const []const u8, expected: []const u8) !void {
803772 const actual = try resolveWindows(testing.allocator, paths);
804773 defer testing.allocator.free(actual);
805 try testing.expect(mem.eql(u8, actual, expected));
774 try testing.expectEqualStrings(expected, actual);
806775}
807776
808777fn testResolvePosix(paths: []const []const u8, expected: []const u8) !void {
809778 const actual = try resolvePosix(testing.allocator, paths);
810779 defer testing.allocator.free(actual);
811 try testing.expect(mem.eql(u8, actual, expected));
780 try testing.expectEqualStrings(expected, actual);
812781}
813782
814783/// Strip the last component from a file path.
......@@ -1089,13 +1058,15 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) !
10891058 if (parsed_from.kind != parsed_to.kind) {
10901059 break :x true;
10911060 } else switch (parsed_from.kind) {
1092 WindowsPath.Kind.NetworkShare => {
1061 .NetworkShare => {
10931062 break :x !networkShareServersEql(parsed_to.disk_designator, parsed_from.disk_designator);
10941063 },
1095 WindowsPath.Kind.Drive => {
1064 .Drive => {
10961065 break :x ascii.toUpper(parsed_from.disk_designator[0]) != ascii.toUpper(parsed_to.disk_designator[0]);
10971066 },
1098 else => unreachable,
1067 .None => {
1068 break :x false;
1069 },
10991070 }
11001071 };
11011072
......@@ -1194,13 +1165,6 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![]
11941165}
11951166
11961167test "relative" {
1197 if (builtin.target.cpu.arch == .aarch64) {
1198 // TODO https://github.com/ziglang/zig/issues/3288
1199 return error.SkipZigTest;
1200 }
1201 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
1202 if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
1203
12041168 try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");
12051169 try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");
12061170 try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc");
......@@ -1226,6 +1190,10 @@ test "relative" {
12261190 try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz");
12271191 try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz");
12281192
1193 try testRelativeWindows("a/b/c", "a\\b", "..");
1194 try testRelativeWindows("a/b/c", "a", "..\\..");
1195 try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d");
1196
12291197 try testRelativePosix("/var/lib", "/var", "..");
12301198 try testRelativePosix("/var/lib", "/bin", "../../bin");
12311199 try testRelativePosix("/var/lib", "/var/lib", "");
......@@ -1243,13 +1211,13 @@ test "relative" {
12431211fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void {
12441212 const result = try relativePosix(testing.allocator, from, to);
12451213 defer testing.allocator.free(result);
1246 try testing.expectEqualSlices(u8, expected_output, result);
1214 try testing.expectEqualStrings(expected_output, result);
12471215}
12481216
12491217fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void {
12501218 const result = try relativeWindows(testing.allocator, from, to);
12511219 defer testing.allocator.free(result);
1252 try testing.expectEqualSlices(u8, expected_output, result);
1220 try testing.expectEqualStrings(expected_output, result);
12531221}
12541222
12551223/// Returns the extension of the file name (if any).
lib/std/fs/test.zig+3-1
......@@ -1095,7 +1095,9 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
10951095
10961096 const allocator = testing.allocator;
10971097
1098 const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};
1098 const cwd = try std.process.getCwdAlloc(allocator);
1099 defer allocator.free(cwd);
1100 const file_paths: [2][]const u8 = .{ cwd, "zig-test-absolute-paths.txt" };
10991101 const filename = try fs.path.resolve(allocator, &file_paths);
11001102 defer allocator.free(filename);
11011103
lib/std/fs/wasi.zig+1-4
......@@ -202,10 +202,7 @@ pub const PreopenList = struct {
202202 // POSIX paths, relative to "/" or `cwd_root` depending on whether they start with "."
203203 const path = if (cwd_root) |cwd| blk: {
204204 const resolve_paths: []const []const u8 = if (raw_path[0] == '.') &.{ cwd, raw_path } else &.{ "/", raw_path };
205 break :blk fs.path.resolve(self.buffer.allocator, resolve_paths) catch |err| switch (err) {
206 error.CurrentWorkingDirectoryUnlinked => unreachable, // root is absolute, so CWD not queried
207 else => |e| return e,
208 };
205 break :blk try fs.path.resolve(self.buffer.allocator, resolve_paths);
209206 } else blk: {
210207 // If we were provided no CWD root, we preserve the preopen dir without resolving
211208 break :blk try self.buffer.allocator.dupe(u8, raw_path);