From 5fb23b9296c5c7509b98a26e82f0b85d8eeb77ed Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 6 Sep 2023 04:18:14 -0800 Subject: [PATCH] bun install progress --- src/bun.js/node/types.zig | 5 + src/bun.zig | 24 +++++ src/cli.zig | 2 +- src/cli/create_command.zig | 6 +- src/cli/install_completions_command.zig | 14 +-- src/env_loader.zig | 4 +- src/fs.zig | 119 ++++++++++++++++++++---- src/install/install.zig | 18 ++-- src/install/lockfile.zig | 16 ++-- src/report.zig | 17 ++-- src/resolver/resolve_path.zig | 6 +- src/string_immutable.zig | 6 ++ src/sys.zig | 29 +++++- src/windows.zig | 11 +++ 14 files changed, 226 insertions(+), 51 deletions(-) diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 0dd503a5c0..a42841d514 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -89,6 +89,11 @@ pub fn Maybe(comptime ResultType: type) type { } } + pub fn unwrap(this: @This()) !ReturnType { + try this.throw(); + return this.result; + } + pub fn toJS(this: @This(), globalThis: *JSC.JSGlobalObject) JSC.JSValue { switch (this) { .err => |e| { diff --git a/src/bun.zig b/src/bun.zig index 0aee7a70b9..755affc005 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -740,6 +740,19 @@ pub const DateTime = @import("./deps/zig-datetime/src/datetime.zig"); pub var start_time: i128 = 0; +pub fn openFileZ(pathZ: [:0]const u8, open_flags: std.fs.File.OpenFlags) !std.fs.File { + var flags: Mode = 0; + switch (open_flags.mode) { + .read_only => flags |= std.os.O.RDONLY, + .write_only => flags |= std.os.O.WRONLY, + .read_write => flags |= std.os.O.RDWR, + } + + const res = sys.open(pathZ, flags, 0); + try res.throw(); + return std.fs.File{ .handle = fdcast(res.result) }; +} + pub fn openDir(dir: std.fs.Dir, path_: [:0]const u8) !std.fs.IterableDir { if (comptime Environment.isWindows) { const res = sys.openDirAtWindowsA(toFD(dir.fd), path_, true, false); @@ -751,6 +764,17 @@ pub fn openDir(dir: std.fs.Dir, path_: [:0]const u8) !std.fs.IterableDir { } } +pub fn openDirA(dir: std.fs.Dir, path_: []const u8) !std.fs.IterableDir { + if (comptime Environment.isWindows) { + const res = sys.openDirAtWindowsA(toFD(dir.fd), path_, true, false); + try res.throw(); + return std.fs.IterableDir{ .dir = .{ .fd = fdcast(res.result) } }; + } else { + const fd = try std.os.openat(dir.fd, path_, std.os.O.DIRECTORY | std.os.O.CLOEXEC | 0, 0); + return std.fs.IterableDir{ .dir = .{ .fd = fd } }; + } +} + pub fn openDirAbsolute(path_: []const u8) !std.fs.Dir { if (comptime Environment.isWindows) { const res = sys.openDirAtWindowsA(invalid_fd, path_, true, false); diff --git a/src/cli.zig b/src/cli.zig index f9f82dea80..493d91c8b7 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -278,7 +278,7 @@ pub const Arguments = struct { } fn getHomeConfigPath(buf: *[bun.MAX_PATH_BYTES]u8) ?[:0]const u8 { - if (bun.getenvZ("XDG_CONFIG_HOME") orelse bun.getenvZ("HOME")) |data_dir| { + if (bun.getenvZ("XDG_CONFIG_HOME") orelse bun.getenvZ(bun.DotEnv.home_env)) |data_dir| { var paths = [_]string{".bunfig.toml"}; return resolve_path.joinAbsStringBufZ(data_dir, buf, &paths, .auto); } diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig index 3edd89d592..9a01968686 100644 --- a/src/cli/create_command.zig +++ b/src/cli/create_command.zig @@ -306,7 +306,7 @@ pub const CreateCommand = struct { } outer: { - if (env_loader.map.get("HOME")) |home_dir| { + if (env_loader.map.get(bun.DotEnv.home_env)) |home_dir| { var parts = [_]string{ home_dir, BUN_CREATE_DIR, positional }; var outdir_path = filesystem.absBuf(&parts, &home_dir_buf); home_dir_buf[outdir_path.len] = 0; @@ -1754,7 +1754,7 @@ pub const Example = struct { folders[1] = std.fs.cwd().openIterableDir(outdir_path, .{}) catch .{ .dir = .{ .fd = bun.fdcast(bun.invalid_fd) } }; } - if (env_loader.map.get("HOME")) |home_dir| { + if (env_loader.map.get(bun.DotEnv.home_env)) |home_dir| { var parts = [_]string{ home_dir, BUN_CREATE_DIR }; var outdir_path = filesystem.absBuf(&parts, &home_dir_buf); folders[2] = std.fs.cwd().openIterableDir(outdir_path, .{}) catch .{ .dir = .{ .fd = bun.fdcast(bun.invalid_fd) } }; @@ -2187,7 +2187,7 @@ pub const CreateListExamplesCommand = struct { Output.prettyln("# You can also paste a GitHub repository:\n\n bun create ahfarmer/calculator calc\n\n", .{}); - if (env_loader.map.get("HOME")) |homedir| { + if (env_loader.map.get(bun.DotEnv.home_env)) |homedir| { Output.prettyln( "This command is completely optional. To add a new local template, create a folder in {s}/.bun-create/. To publish a new template, git clone https://github.com/oven-sh/bun, add a new folder to the \"examples\" folder, and submit a PR.", .{homedir}, diff --git a/src/cli/install_completions_command.zig b/src/cli/install_completions_command.zig index 3e12d33d1f..9eb253b90a 100644 --- a/src/cli/install_completions_command.zig +++ b/src/cli/install_completions_command.zig @@ -72,7 +72,7 @@ pub const InstallCompletionsCommand = struct { // if that fails, try $HOME/.bun/bin outer: { - if (bun.getenvZ("HOME")) |home_dir| { + if (bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { target = std.fmt.bufPrint(&target_buf, "{s}/.bun/bin/" ++ bunx_name, .{home_dir}) catch unreachable; std.os.symlink(exe, target) catch break :outer; return; @@ -81,7 +81,7 @@ pub const InstallCompletionsCommand = struct { // if that fails, try $HOME/.local/bin outer: { - if (bun.getenvZ("HOME")) |home_dir| { + if (bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { target = std.fmt.bufPrint(&target_buf, "{s}/.local/bin/" ++ bunx_name, .{home_dir}) catch unreachable; std.os.symlink(exe, target) catch break :outer; return; @@ -189,7 +189,7 @@ pub const InstallCompletionsCommand = struct { } } - if (bun.getenvZ("HOME")) |home_dir| { + if (bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { outer: { var paths = [_]string{ home_dir, "./.config/fish/completions" }; completions_dir = resolve_path.joinAbsString(cwd, &paths, .auto); @@ -247,7 +247,7 @@ pub const InstallCompletionsCommand = struct { } } - if (bun.getenvZ("HOME")) |home_dir| { + if (bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { { outer: { var paths = [_]string{ home_dir, "./.oh-my-zsh/completions" }; @@ -299,7 +299,7 @@ pub const InstallCompletionsCommand = struct { } } - if (bun.getenvZ("HOME")) |home_dir| { + if (bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { { outer: { var paths = [_]string{ home_dir, "./.oh-my-bash/custom/completions" }; @@ -409,7 +409,7 @@ pub const InstallCompletionsCommand = struct { } second: { - if (bun.getenvZ("HOME")) |zdot_dir| { + if (bun.getenvZ(bun.DotEnv.home_env)) |zdot_dir| { bun.copy(u8, &zshrc_filepath, zdot_dir); bun.copy(u8, zshrc_filepath[zdot_dir.len..], "/.zshrc"); zshrc_filepath[zdot_dir.len + "/.zshrc".len] = 0; @@ -419,7 +419,7 @@ pub const InstallCompletionsCommand = struct { } third: { - if (bun.getenvZ("HOME")) |zdot_dir| { + if (bun.getenvZ(bun.DotEnv.home_env)) |zdot_dir| { bun.copy(u8, &zshrc_filepath, zdot_dir); bun.copy(u8, zshrc_filepath[zdot_dir.len..], "/.zshenv"); zshrc_filepath[zdot_dir.len + "/.zshenv".len] = 0; diff --git a/src/env_loader.zig b/src/env_loader.zig index 7c2c38a99f..260d777ea5 100644 --- a/src/env_loader.zig +++ b/src/env_loader.zig @@ -350,7 +350,7 @@ pub const Loader = struct { } this.did_load_process = true; - if (this.map.get("HOME")) |home_folder| { + if (this.map.get(bun.DotEnv.home_env)) |home_folder| { Analytics.username_only_for_determining_project_id_and_never_sent = home_folder; } else if (this.map.get("USER")) |home_folder| { Analytics.username_only_for_determining_project_id_and_never_sent = home_folder; @@ -1121,3 +1121,5 @@ test "DotEnv Loader - copyForDefine" { try expectString(env_defines.get("process.env.HOSTNAME").?.value.e_string.data, "example.com"); try expect(env_defines.get("process.env.THIS_SHOULDNT_BE_IN_DEFINES_MAP") == null); } + +pub const home_env = if (Environment.isWindows) "USERPROFILE" else "HOME"; diff --git a/src/fs.zig b/src/fs.zig index 5cb34ed9bc..2ef023494c 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -530,11 +530,34 @@ pub const FileSystem = struct { else => "/tmp", }; + fn platformTempDir() []const u8 { + if (comptime Environment.isWindows) { + // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw#remarks + return bun.getenvZ("TMP") orelse bun.getenvZ("TEMP") orelse brk: { + if (bun.getenvZ("USERPROFILE")) |profile| { + var buf: [bun.MAX_PATH_BYTES]u8 = undefined; + var parts = [_]string{"AppData/Local/Temp"}; + var out = bun.path.joinAbsStringBuf(profile, &buf, &parts, .loose); + break :brk bun.default_allocator.dupe(u8, out) catch unreachable; + } + + return "C:/Windows/Temp"; + }; + } + + return PLATFORM_TMP_DIR; + } + + pub const Tmpfile = switch (Environment.os) { + .windows => TmpfileWindows, + else => TmpfilePosix, + }; + pub var tmpdir_path: []const u8 = undefined; pub var tmpdir_path_set = false; pub fn tmpdirPath(_: *const @This()) []const u8 { if (!tmpdir_path_set) { - tmpdir_path = bun.getenvZ("BUN_TMPDIR") orelse bun.getenvZ("TMPDIR") orelse PLATFORM_TMP_DIR; + tmpdir_path = bun.getenvZ("BUN_TMPDIR") orelse bun.getenvZ("TMPDIR") orelse platformTempDir(); tmpdir_path_set = true; } @@ -543,20 +566,18 @@ pub const FileSystem = struct { pub fn openTmpDir(_: *const RealFS) !std.fs.Dir { if (!tmpdir_path_set) { - tmpdir_path = bun.getenvZ("BUN_TMPDIR") orelse bun.getenvZ("TMPDIR") orelse PLATFORM_TMP_DIR; + tmpdir_path = bun.getenvZ("BUN_TMPDIR") orelse bun.getenvZ("TMPDIR") orelse platformTempDir(); tmpdir_path_set = true; } - return (try std.fs.cwd().openIterableDir(tmpdir_path, .{ - .access_sub_paths = true, - })).dir; + return try bun.openDirAbsolute(tmpdir_path); } pub fn entriesAt(this: *RealFS, index: allocators.IndexType, generation: bun.Generation) ?*EntriesOption { var existing = this.entries.atIndex(index) orelse return null; if (existing.* == .entries) { if (existing.entries.generation < generation) { - var handle = std.fs.Dir.openIterableDir(std.fs.cwd(), existing.entries.dir, .{}) catch |err| { + var handle = bun.openDirA(std.fs.cwd(), existing.entries.dir) catch |err| { existing.entries.data.clearAndFree(bun.fs_allocator); return this.readDirectoryError(existing.entries.dir, err) catch unreachable; @@ -601,44 +622,47 @@ pub const FileSystem = struct { return file; } - pub const Tmpfile = struct { + pub const TmpfilePosix = struct { fd: bun.FileDescriptor = bun.invalid_fd, dir_fd: bun.FileDescriptor = bun.invalid_fd, - pub inline fn dir(this: *Tmpfile) std.fs.Dir { + pub inline fn dir(this: *TmpfilePosix) std.fs.Dir { return std.fs.Dir{ .fd = bun.fdcast(this.dir_fd), }; } - pub inline fn file(this: *Tmpfile) std.fs.File { + pub inline fn file(this: *TmpfilePosix) std.fs.File { return std.fs.File{ .handle = bun.fdcast(this.fd), }; } - pub fn close(this: *Tmpfile) void { + pub fn close(this: *TmpfilePosix) void { if (this.fd != bun.invalid_fd) _ = bun.sys.close(this.fd); } - pub fn create(this: *Tmpfile, rfs: *RealFS, name: [*:0]const u8) !void { + pub fn create(this: *TmpfilePosix, rfs: *RealFS, name: [:0]const u8) !void { var tmpdir_ = try rfs.openTmpDir(); const flags = std.os.O.CREAT | std.os.O.RDWR | std.os.O.CLOEXEC; this.dir_fd = bun.toFD(tmpdir_.fd); - this.fd = bun.toFD(try std.os.openatZ(tmpdir_.fd, name, flags, if (comptime Environment.isPosix) std.os.S.IRWXU else 0)); + const result = bun.sys.openat(tmpdir_.fd, name, flags, std.os.S.IRWXU); + try result.throw(); + + this.fd = bun.toFD(result.result); } - pub fn promote(this: *Tmpfile, from_name: [*:0]const u8, destination_fd: std.os.fd_t, name: [*:0]const u8) !void { + pub fn promoteToCWD(this: *TmpfilePosix, from_name: [*:0]const u8, name: [*:0]const u8) !void { std.debug.assert(this.fd != bun.invalid_fd); std.debug.assert(this.dir_fd != bun.invalid_fd); - try C.moveFileZWithHandle(bun.fdcast(this.fd), bun.fdcast(this.dir_fd), from_name, destination_fd, name); + try C.moveFileZWithHandle(bun.fdcast(this.fd), bun.fdcast(this.dir_fd), from_name, std.fs.cwd(), name); this.close(); } - pub fn closeAndDelete(this: *Tmpfile, name: [*:0]const u8) void { + pub fn closeAndDelete(this: *TmpfilePosix, name: [*:0]const u8) void { this.close(); if (comptime !Environment.isLinux) { @@ -649,6 +673,62 @@ pub const FileSystem = struct { } }; + pub const TmpfileWindows = struct { + fd: bun.FileDescriptor = bun.invalid_fd, + existing_path: []const u8 = "", + + pub inline fn dir(_: *TmpfileWindows) std.fs.Dir { + return Fs.FileSystem.instance.tmpdir(); + } + + pub inline fn file(this: *TmpfileWindows) std.fs.File { + return std.fs.File{ + .handle = bun.fdcast(this.fd), + }; + } + + pub fn close(this: *TmpfileWindows) void { + if (this.fd != bun.invalid_fd) _ = bun.sys.close(this.fd); + } + + pub fn create(this: *TmpfileWindows, rfs: *RealFS, name: [:0]const u8) !void { + var tmpdir_ = try rfs.openTmpDir(); + + const flags = std.os.O.CREAT | std.os.O.WRONLY | std.os.O.CLOEXEC; + + var result = bun.sys.openat(bun.toFD(tmpdir_.fd), name, flags, 0); + try result.throw(); + + this.fd = bun.toFD(result.result); + var buf: [bun.MAX_PATH_BYTES]u8 = undefined; + const existing_path = try bun.getFdPath(this.fd, &buf); + this.existing_path = try bun.default_allocator.dupe(u8, existing_path); + } + + pub fn promoteToCWD(this: *TmpfileWindows, from_name: [*:0]const u8, name: [:0]const u8) !void { + _ = from_name; + var existing_buf: bun.MAX_WPATH = undefined; + var new_buf: bun.MAX_WPATH = undefined; + this.close(); + const existing = bun.strings.toExtendedPathNormalized(&new_buf, this.existing_path); + const new = if (std.fs.path.isAbsoluteWindows(name)) + bun.strings.toExtendedPathNormalized(&existing_buf, name) else + bun.strings.toWPathNormalized(&existing_buf, name); + if (comptime Environment.allow_assert) { + debug("moveFileExW({s}, {s})", .{strings.fmtUTF16(existing), strings.fmtUTF16( new)}); + } + + if (bun.windows.kernel32.MoveFileExW(existing.ptr, new.ptr, bun.windows.MOVEFILE_COPY_ALLOWED | bun.windows.MOVEFILE_REPLACE_EXISTING | bun.windows.MOVEFILE_WRITE_THROUGH) == bun.windows.FALSE) { + try bun.windows.Win32Error.get().throw(); + } + } + + pub fn closeAndDelete(this: *TmpfileWindows, name: [*:0]const u8) void { + _ = name; + this.close(); + } + }; + inline fn _fetchCacheFile(fs: *RealFS, basename: string) !std.fs.File { var parts = [_]string{ "node_modules", ".cache", basename }; var path = fs.parent_fs.join(&parts); @@ -833,7 +913,14 @@ pub const FileSystem = struct { }; pub fn openDir(_: *RealFS, unsafe_dir_string: string) !std.fs.Dir { - const dirfd = bun.sys.openDirAtWindowsA(bun.invalid_fd, unsafe_dir_string, true, true); + const dirfd = if (Environment.isWindows) + bun.sys.openDirAtWindowsA(bun.invalid_fd, unsafe_dir_string, true, true) + else + bun.sys.openAtA( + std.fs.cwd().fd, + unsafe_dir_string, + std.os.O.DIRECTORY, + ); try dirfd.throw(); return std.fs.Dir{ .fd = bun.fdcast(dirfd.result), diff --git a/src/install/install.zig b/src/install/install.zig index 941b28c4b9..741c70b061 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -849,9 +849,15 @@ const PackageInstall = struct { .symlink = true, }); + pub const windows = BackendSupport.initDefault(false, .{ + .hardlink = true, + .copyfile = true, + }); + pub inline fn isSupported(this: Method) bool { if (comptime Environment.isMac) return macOS.get(this); if (comptime Environment.isLinux) return linux.get(this); + if (comptime Environment.isWindows) return windows.get(this); return false; } @@ -1604,7 +1610,7 @@ const PackageInstall = struct { supported_method = .copyfile; supported_method_to_use = .copyfile; }, - error.FileNotFound => return Result{ + error.NOENT, error.FileNotFound => return Result{ .fail = .{ .err = error.FileNotFound, .step = .opening_cache_dir }, }, else => return Result{ @@ -2910,7 +2916,7 @@ pub const PackageManager = struct { ); } - try tmpfile.promote(tmpname, std.fs.cwd().fd, "yarn.lock"); + try tmpfile.promoteToCWD(tmpname, "yarn.lock"); } pub fn isRootDependency(this: *const PackageManager, id: DependencyID) bool { @@ -3740,7 +3746,7 @@ pub const PackageManager = struct { return CacheDir{ .path = Fs.FileSystem.instance.abs(&parts), .is_node_modules = false }; } - if (env.map.get("HOME")) |dir| { + if (env.map.get(bun.DotEnv.home_env)) |dir| { var parts = [_]string{ dir, ".bun/", "install/", "cache/" }; return CacheDir{ .path = Fs.FileSystem.instance.abs(&parts), .is_node_modules = false }; } @@ -4521,7 +4527,7 @@ pub const PackageManager = struct { return try std.fs.cwd().makeOpenPathIterable(path, .{}); } - if (bun.getenvZ("XDG_CACHE_HOME") orelse bun.getenvZ("HOME")) |home_dir| { + if (bun.getenvZ("XDG_CACHE_HOME") orelse bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { var buf: [bun.MAX_PATH_BYTES]u8 = undefined; var parts = [_]string{ ".bun", @@ -7249,7 +7255,7 @@ pub const PackageManager = struct { const cwd = std.fs.cwd(); var node_modules_folder = cwd.openIterableDir("node_modules", .{}) catch brk: { skip_verify_installed_version_number = true; - (if (comptime Environment.isWindows) std.os.mkdiratW(cwd.fd, bun.strings.w("node_modules"), 0) else cwd.makeDirZ("node_modules")) catch |err| { + bun.sys.mkdir("node_modules", 0).throw() catch |err| { Output.prettyErrorln("error: {s} creating node_modules folder", .{@errorName(err)}); Global.crash(); }; @@ -7308,7 +7314,7 @@ pub const PackageManager = struct { // We use this file descriptor to know where to put it. installer.node_modules_folder = cwd.openIterableDir(node_modules.relative_path, .{}) catch brk: { // Avoid extra mkdir() syscall - try cwd.makePath(bun.span(node_modules.relative_path)); + try cwd.makePath(node_modules.relative_path); break :brk try cwd.openIterableDir(node_modules.relative_path, .{}); }; diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index 927825ca77..92c4b5800c 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -181,9 +181,9 @@ pub fn loadFromDisk(this: *Lockfile, allocator: Allocator, log: *logger.Log, fil var file = std.io.getStdIn(); if (filename.len > 0) - file = std.fs.cwd().openFileZ(filename, .{ .mode = .read_only }) catch |err| { + file = bun.openFileZ(filename, .{ .mode = .read_only }) catch |err| { return switch (err) { - error.FileNotFound, error.AccessDenied, error.BadPathName => LoadFromDiskResult{ .not_found = {} }, + error.NOENT, error.PERM, error.INVAL => LoadFromDiskResult{ .not_found = {} }, else => LoadFromDiskResult{ .err = .{ .step = .open_file, .value = err } }, }; }; @@ -1455,7 +1455,7 @@ pub fn saveToDisk(this: *Lockfile, filename: stringZ) void { var tmpfile = FileSystem.RealFS.Tmpfile{}; var secret: [32]u8 = undefined; std.mem.writeIntNative(u64, secret[0..8], @as(u64, @intCast(std.time.milliTimestamp()))); - var base64_bytes: [64]u8 = undefined; + var base64_bytes: [16]u8 = undefined; std.crypto.random.bytes(&base64_bytes); const tmpname__ = std.fmt.bufPrint(tmpname_buf[8..], "{s}", .{std.fmt.fmtSliceHexLower(&base64_bytes)}) catch unreachable; @@ -1486,7 +1486,7 @@ pub fn saveToDisk(this: *Lockfile, filename: stringZ) void { ); } - tmpfile.promote(tmpname, std.fs.cwd().fd, filename) catch |err| { + tmpfile.promoteToCWD(tmpname, filename) catch |err| { tmpfile.dir().deleteFileZ(tmpname) catch {}; Output.prettyErrorln("error: failed to save lockfile: {s}", .{@errorName(err)}); Global.crash(); @@ -2453,7 +2453,7 @@ pub const Package = extern struct { if (switch (version.tag) { .workspace => if (to_lockfile.workspace_paths.getPtr(@truncate(from_dep.name_hash))) |path_ptr| brk: { const path = to_lockfile.str(path_ptr); - var file = std.fs.cwd().openFile(Path.join( + var file = bun.openFileZ(Path.joinZ( &[_]string{ path, "package.json" }, .auto, ), .{ .mode = .read_only }) catch break :brk false; @@ -2858,6 +2858,7 @@ pub const Package = extern struct { const paths = [_]string{ path, "package.json" }; break :brk bun.path.joinStringBuf(path_buf, &paths, .auto); }; + // TODO: windows var workspace_file = try dir.openFile(path_to_use, .{ .mode = .read_only }); defer workspace_file.close(); @@ -3072,12 +3073,11 @@ pub const Package = extern struct { ); if (entry.cache.fd == 0) { - entry.cache.fd = bun.toFD(std.os.openatZ( - std.fs.cwd().fd, + entry.cache.fd = bun.toFD(bun.sys.open( entry_path, std.os.O.DIRECTORY | std.os.O.CLOEXEC | std.os.O.NOCTTY, 0, - ) catch continue); + ).unwrap() catch continue); } const dir_fd = entry.cache.fd; diff --git a/src/report.zig b/src/report.zig index 5bdd5e2219..c956c679f5 100644 --- a/src/report.zig +++ b/src/report.zig @@ -62,9 +62,9 @@ pub const CrashReportWriter = struct { var base_dir: []const u8 = "."; if (bun.getenvZ("BUN_INSTALL")) |install_dir| { - base_dir = std.mem.trimRight(u8, install_dir, std.fs.path.sep_str); - } else if (bun.getenvZ("HOME")) |home_dir| { - base_dir = std.mem.trimRight(u8, home_dir, std.fs.path.sep_str); + base_dir = strings.withoutTrailingSlash(install_dir); + } else if (bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { + base_dir = strings.withoutTrailingSlash(home_dir); } const file_path = std.fmt.bufPrintZ( &crash_reporter_path, @@ -72,8 +72,13 @@ pub const CrashReportWriter = struct { .{ base_dir, Global.package_json_version, @as(u64, @intCast(@max(std.time.milliTimestamp(), 0))) }, ) catch return; - std.fs.cwd().makeDir(std.fs.path.dirname(bun.asByteSlice(file_path)).?) catch {}; - var file = std.fs.cwd().createFileZ(file_path, .{ .truncate = true }) catch return; + if (bun.path.nextDirname(file_path)) |dirname| { + _ = bun.sys.mkdirA(dirname, 0); + } + + const call = bun.sys.open(file_path, std.os.O.TRUNC, 0); + call.throw() catch return; + var file = std.fs.File{ .handle = bun.fdcast(call.result) }; this.file = std.io.bufferedWriter( file.writer(), ); @@ -85,7 +90,7 @@ pub const CrashReportWriter = struct { if (this.file_path.len > 0) { var tilda = false; - if (bun.getenvZ("HOME")) |home_dir| { + if (bun.getenvZ(bun.DotEnv.home_env)) |home_dir| { if (strings.hasPrefix(display_path, home_dir)) { display_path = display_path[home_dir.len..]; tilda = true; diff --git a/src/resolver/resolve_path.zig b/src/resolver/resolve_path.zig index 4474458993..deb84602c2 100644 --- a/src/resolver/resolve_path.zig +++ b/src/resolver/resolve_path.zig @@ -821,7 +821,11 @@ threadlocal var join_buf: [4096]u8 = undefined; pub fn join(_parts: anytype, comptime _platform: Platform) []const u8 { return joinStringBuf(&join_buf, _parts, _platform); } - +pub fn joinZ(_parts: anytype, comptime _platform: Platform) [:0]const u8 { + var joined = joinStringBuf(join_buf[0 .. join_buf.len - 1], _parts, _platform); + join_buf[joined.len] = 0; + return join_buf[0..joined.len :0]; +} pub fn joinStringBuf(buf: []u8, _parts: anytype, comptime _platform: Platform) []const u8 { if (FeatureFlags.use_std_path_join) { var alloc = std.heap.FixedBufferAllocator.init(buf); diff --git a/src/string_immutable.zig b/src/string_immutable.zig index d17c4df8f3..db181e695c 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -1615,6 +1615,12 @@ pub fn toNTPath(wbuf: []u16, utf8: []const u8) [:0]const u16 { // These are the same because they don't have rules like needing a trailing slash pub const toNTDir = toNTPath; +pub fn toExtendedPathNormalized(wbuf: []u16, utf8: []const u8) [:0]const u16 { + std.debug.assert(wbuf.len > 4); + wbuf[0..4].* = [_]u16{ '\\', '\\', '?', '\\' }; + return wbuf[0 .. toWPathNormalized(wbuf[4..], utf8).len + 4 :0]; +} + pub fn toWPathNormalized(wbuf: []u16, utf8: []const u8) [:0]const u16 { var renormalized: [bun.MAX_PATH_BYTES]u8 = undefined; var path_to_use = utf8; diff --git a/src/sys.zig b/src/sys.zig index dde6bb1567..6b80a2aea0 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -235,7 +235,32 @@ pub fn mkdir(file_path: [:0]const u8, flags: bun.Mode) Maybe(void) { return Maybe(void).errnoSysP(linux.mkdir(file_path, flags), .mkdir, file_path) orelse Maybe(void).success; } var wbuf: bun.MAX_WPATH = undefined; - _ = kernel32.CreateDirectoryW(bun.strings.toNTDir(&wbuf, file_path).ptr, null); + _ = kernel32.CreateDirectoryW(bun.strings.toWPath(&wbuf, file_path).ptr, null); + + return Maybe(void).errnoSysP(0, .mkdir, file_path) orelse Maybe(void).success; +} + +pub fn mkdirA(file_path: []const u8, flags: bun.Mode) Maybe(void) { + if (comptime Environment.isMac) { + return Maybe(void).errnoSysP(system.mkdir(std.os.toPosixPath(file_path) catch return Maybe(void){ + .err = .{ + .errno = @intFromEnum(bun.C.E.NOMEM), + .syscall = .open, + }, + }, flags), .mkdir, file_path) orelse Maybe(void).success; + } + + if (comptime Environment.isLinux) { + return Maybe(void).errnoSysP(linux.mkdir(std.os.toPosixPath(file_path) catch return Maybe(void){ + .err = .{ + .errno = @intFromEnum(bun.C.E.NOMEM), + .syscall = .open, + }, + }, flags), .mkdir, file_path) orelse Maybe(void).success; + } + + var wbuf: bun.MAX_WPATH = undefined; + _ = kernel32.CreateDirectoryW(bun.strings.toWPath(&wbuf, file_path).ptr, null); return Maybe(void).errnoSysP(0, .mkdir, file_path) orelse Maybe(void).success; } @@ -386,7 +411,6 @@ pub fn openatWindows(dirfD: bun.FileDescriptor, path: []const u16, flags: bun.Mo const nonblock = flags & O.NONBLOCK != 0; var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE; - if (flags & O.RDWR != 0) { access_mask |= w.GENERIC_READ | w.GENERIC_WRITE; } else if (flags & O.WRONLY != 0) { @@ -431,6 +455,7 @@ pub fn openatWindows(dirfD: bun.FileDescriptor, path: []const u16, flags: bun.Mo if (flags & O.EXCL != 0) { break :blk w.FILE_CREATE; } + break :blk w.FILE_OPEN_IF; } break :blk w.FILE_OPEN; }; diff --git a/src/windows.zig b/src/windows.zig index ca17e10dd2..cfbdaddcb7 100644 --- a/src/windows.zig +++ b/src/windows.zig @@ -23,6 +23,10 @@ pub const UNICODE_STRING = windows.UNICODE_STRING; pub const NTSTATUS = windows.NTSTATUS; pub const NT_SUCCESS = windows.NT_SUCCESS; pub const STATUS_SUCCESS = windows.STATUS_SUCCESS; +pub const MOVEFILE_COPY_ALLOWED = 0x2; +pub const MOVEFILE_REPLACE_EXISTING = 0x1; +pub const MOVEFILE_WRITE_THROUGH = 0x8; + pub const DUPLICATE_SAME_ACCESS = windows.DUPLICATE_SAME_ACCESS; pub const OBJECT_ATTRIBUTES = windows.OBJECT_ATTRIBUTES; pub const kernel32 = windows.kernel32; @@ -2908,6 +2912,13 @@ pub const Win32Error = enum(u16) { return @enumFromInt(@intFromEnum(bun.windows.kernel32.GetLastError())); } + pub fn throw(this: @This()) !void { + if (this == .SUCCESS) return; + if (this.toSystemErrno()) |err| { + return err.toError(); + } + } + pub fn toSystemErrno(this: Win32Error) ?SystemErrno { return SystemErrno.init(this); }