diff --git a/src/OutputFile.zig b/src/OutputFile.zig index 22a30d06d1..b81eacbe22 100644 --- a/src/OutputFile.zig +++ b/src/OutputFile.zig @@ -46,10 +46,10 @@ pub const FileOperation = struct { close_handle_on_complete: bool = false, autowatch: bool = true, - pub fn fromFile(fd: anytype, pathname: string) FileOperation { + pub fn fromFile(fd: bun.FD, pathname: string) FileOperation { return .{ + .fd = fd, .pathname = pathname, - .fd = bun.toFD(fd), }; } @@ -166,7 +166,7 @@ pub fn initFile(file: std.fs.File, pathname: string, size: usize) OutputFile { pub fn initFileWithDir(file: std.fs.File, pathname: string, size: usize, dir: std.fs.Dir) OutputFile { var res = initFile(file, pathname, size); - res.value.copy.dir_handle = bun.toFD(dir.fd); + res.value.copy.dir_handle = .fromStdDir(dir); return res; } @@ -220,8 +220,8 @@ pub fn init(options: Options) OutputFile { .buffer => |buffer| Value{ .buffer = .{ .allocator = buffer.allocator, .bytes = buffer.data } }, .file => |file| Value{ .copy = brk: { - var op = FileOperation.fromFile(file.file.handle, options.output_path); - op.dir = bun.toFD(file.dir.fd); + var op = FileOperation.fromFile(.fromStdFile(file.file), options.output_path); + op.dir = .fromStdDir(file.dir); break :brk op; }, }, @@ -261,17 +261,17 @@ pub fn writeToDisk(f: OutputFile, root_dir: std.fs.Dir, root_dir_path: []const u } }, .encoding = .buffer, .mode = if (f.is_executable) 0o755 else 0o644, - .dirfd = bun.toFD(root_dir.fd), + .dirfd = .fromStdDir(root_dir), .file = .{ .path = .{ .string = JSC.PathString.init(rel_path), } }, }).unwrap(); }, .move => |value| { - try f.moveTo(root_dir_path, value.pathname, bun.toFD(root_dir.fd)); + try f.moveTo(root_dir_path, value.pathname, .fromStdDir(root_dir)); }, .copy => |value| { - try f.copyTo(root_dir_path, value.pathname, bun.toFD(root_dir.fd)); + try f.copyTo(root_dir_path, value.pathname, .fromStdDir(root_dir)); }, .pending => unreachable, } @@ -282,15 +282,11 @@ pub fn moveTo(file: *const OutputFile, _: string, rel_path: []const u8, dir: Fil } pub fn copyTo(file: *const OutputFile, _: string, rel_path: []const u8, dir: FileDescriptorType) !void { - const file_out = (try dir.asDir().createFile(rel_path, .{})); - - const fd_out = file_out.handle; + const fd_out = bun.FD.fromStdFile(try dir.stdDir().createFile(rel_path, .{})); var do_close = false; - const fd_in = (try std.fs.openFileAbsolute(file.src_path.text, .{ .mode = .read_only })).handle; + const fd_in = bun.FD.fromStdFile(try std.fs.openFileAbsolute(file.src_path.text, .{ .mode = .read_only })); if (Environment.isWindows) { - Fs.FileSystem.setMaxFd(fd_out); - Fs.FileSystem.setMaxFd(fd_in); do_close = Fs.FileSystem.instance.fs.needToCloseFiles(); // use paths instead of bun.getFdPathW() @@ -299,8 +295,8 @@ pub fn copyTo(file: *const OutputFile, _: string, rel_path: []const u8, dir: Fil defer { if (do_close) { - _ = bun.sys.close(bun.toFD(fd_out)); - _ = bun.sys.close(bun.toFD(fd_in)); + fd_out.close(); + fd_in.close(); } } @@ -317,7 +313,7 @@ pub fn toJS( .noop => JSC.JSValue.undefined, .copy => |copy| brk: { const file_blob = JSC.WebCore.Blob.Store.initFile( - if (copy.fd != .zero) + if (copy.fd.isValid()) JSC.Node.PathOrFileDescriptor{ .fd = copy.fd, } @@ -421,7 +417,7 @@ pub fn toBlob( .noop => @panic("Cannot convert noop output file to blob"), .copy => |copy| brk: { const file_blob = try JSC.WebCore.Blob.Store.initFile( - if (copy.fd != .zero) + if (copy.fd.isValid()) JSC.Node.PathOrFileDescriptor{ .fd = copy.fd, } diff --git a/src/StandaloneModuleGraph.zig b/src/StandaloneModuleGraph.zig index 779e48a288..736942da24 100644 --- a/src/StandaloneModuleGraph.zig +++ b/src/StandaloneModuleGraph.zig @@ -486,7 +486,7 @@ pub const StandaloneModuleGraph = struct { // Make the file writable so we can delete it _ = Syscall.fchmod(fd, 0o777); } - _ = Syscall.close(fd); + fd.close(); _ = Syscall.unlink(name); } }.toClean; @@ -579,7 +579,7 @@ pub const StandaloneModuleGraph = struct { } unreachable; }; - const self_fd = brk2: { + const self_fd: bun.FileDescriptor = brk2: { for (0..3) |retry| { switch (Syscall.open(self_exe, bun.O.CLOEXEC | bun.O.RDONLY, 0)) { .result => |res| break :brk2 res, @@ -601,9 +601,9 @@ pub const StandaloneModuleGraph = struct { unreachable; }; - defer _ = Syscall.close(self_fd); + defer self_fd.close(); - bun.copyFile(self_fd.cast(), fd.cast()).unwrap() catch |err| { + bun.copyFile(self_fd, fd).unwrap() catch |err| { Output.prettyErrorln("error: failed to copy bun executable into temporary file: {s}", .{@errorName(err)}); cleanup(zname, fd); Global.exit(1); @@ -659,7 +659,7 @@ pub const StandaloneModuleGraph = struct { Global.exit(1); }; if (comptime !Environment.isWindows) { - _ = bun.C.fchmod(cloned_executable_fd.int(), 0o777); + _ = bun.C.fchmod(cloned_executable_fd.native(), 0o777); } return cloned_executable_fd; }, @@ -727,7 +727,7 @@ pub const StandaloneModuleGraph = struct { // the final 8 bytes in the file are the length of the module graph with padding, excluding the trailer and offsets _ = Syscall.write(cloned_executable_fd, std.mem.asBytes(&total_byte_count)); if (comptime !Environment.isWindows) { - _ = bun.C.fchmod(cloned_executable_fd.int(), 0o777); + _ = bun.C.fchmod(cloned_executable_fd.native(), 0o777); } return cloned_executable_fd; @@ -791,7 +791,7 @@ pub const StandaloneModuleGraph = struct { .{ .windows_hide_console = windows_hide_console }, target, ); - fd.assertKind(.system); + bun.debugAssert(fd.kind == .system); if (Environment.isWindows) { var outfile_buf: bun.OSPathBuffer = undefined; @@ -803,7 +803,7 @@ pub const StandaloneModuleGraph = struct { break :brk outfile_buf_u16[0..outfile_w.len :0]; }; - bun.C.moveOpenedFileAtLoose(fd, bun.toFD(root_dir.fd), outfile_slice, true).unwrap() catch |err| { + bun.C.moveOpenedFileAtLoose(fd, .fromStdDir(root_dir), outfile_slice, true).unwrap() catch |err| { if (err == error.EISDIR) { Output.errGeneric("{} is a directory. Please choose a different --outfile or delete the directory", .{bun.fmt.utf16(outfile_slice)}); } else { @@ -814,7 +814,7 @@ pub const StandaloneModuleGraph = struct { Global.exit(1); }; - _ = bun.sys.close(fd); + fd.close(); if (windows_icon) |icon_utf8| { var icon_buf: bun.OSPathBuffer = undefined; @@ -836,7 +836,7 @@ pub const StandaloneModuleGraph = struct { fd, bun.FD.cwd(), bun.sliceTo(&(try std.posix.toPosixPath(temp_location)), 0), - bun.toFD(root_dir.fd), + .fromStdDir(root_dir), bun.sliceTo(&(try std.posix.toPosixPath(std.fs.path.basename(outfile))), 0), ) catch |err| { if (err == error.IsDir) { @@ -871,7 +871,7 @@ pub const StandaloneModuleGraph = struct { // Do not invoke libuv here. const self_exe = openSelf() catch return null; - defer _ = Syscall.close(self_exe); + defer self_exe.close(); var trailer_bytes: [4096]u8 = undefined; std.posix.lseek_END(self_exe.cast(), -4096) catch return null; @@ -1010,7 +1010,7 @@ pub const StandaloneModuleGraph = struct { switch (Environment.os) { .linux => { if (std.fs.openFileAbsoluteZ("/proc/self/exe", .{})) |easymode| { - return bun.toFD(easymode.handle); + return .fromStdFile(easymode); } else |_| { if (bun.argv.len > 0) { // The user doesn't have /proc/ mounted, so now we just guess and hope for the best. @@ -1021,7 +1021,7 @@ pub const StandaloneModuleGraph = struct { "", bun.argv[0], )) |path| { - return bun.toFD((try std.fs.cwd().openFileZ(path, .{})).handle); + return .fromStdFile(try std.fs.cwd().openFileZ(path, .{})); } } @@ -1033,7 +1033,7 @@ pub const StandaloneModuleGraph = struct { // opened with no modification. const self_exe_path = try bun.selfExePath(); const file = try std.fs.openFileAbsoluteZ(self_exe_path.ptr, .{}); - return bun.toFD(file.handle); + return .fromStdFile(file); }, .windows => { const image_path_unicode_string = std.os.windows.peb().ProcessParameters.ImagePathName; @@ -1050,7 +1050,7 @@ pub const StandaloneModuleGraph = struct { } return bun.sys.openFileAtWindows( - bun.FileDescriptor.cwd(), + .cwd(), nt_path, .{ .access_mask = w.SYNCHRONIZE | w.GENERIC_READ, diff --git a/src/Watcher.zig b/src/Watcher.zig index 46c0a1c83d..fc8ce70e77 100644 --- a/src/Watcher.zig +++ b/src/Watcher.zig @@ -111,7 +111,7 @@ pub fn deinit(this: *Watcher, close_descriptors: bool) void { if (close_descriptors and this.running) { const fds = this.watchlist.items(.fd); for (fds) |fd| { - _ = bun.sys.close(fd); + fd.close(); } } this.watchlist.deinit(this.allocator); @@ -236,7 +236,7 @@ fn threadMain(this: *Watcher) !void { if (this.close_descriptors) { const fds = this.watchlist.items(.fd); for (fds) |fd| { - _ = bun.sys.close(fd); + fd.close(); } } this.watchlist.deinit(this.allocator); @@ -271,7 +271,7 @@ pub fn flushEvictions(this: *Watcher) void { // on mac and linux we can just close the file descriptor // we don't need to call inotify_rm_watch on linux because it gets removed when the file descriptor is closed if (fds[item].isValid()) { - _ = bun.sys.close(fds[item]); + fds[item].close(); } } last_item = item; @@ -347,7 +347,7 @@ fn appendFileAssumeCapacity( event.fflags = std.c.NOTE.WRITE | std.c.NOTE.RENAME | std.c.NOTE.DELETE; // id - event.ident = @intCast(fd.int()); + event.ident = @intCast(fd.native()); // Store the hash for fast filtering later event.udata = @as(usize, @intCast(watchlist_id)); @@ -358,7 +358,7 @@ fn appendFileAssumeCapacity( // - We register the event here. // our while(true) loop above receives notification of changes to any of the events created here. _ = std.posix.system.kevent( - this.platform.fd.cast(), + this.platform.fd.unwrap().?.native(), @as([]KEvent, events[0..1]).ptr, 1, @as([]KEvent, events[0..1]).ptr, @@ -399,7 +399,7 @@ fn appendDirectoryAssumeCapacity( } const fd = brk: { - if (stored_fd != .zero) break :brk stored_fd; + if (stored_fd.isValid()) break :brk stored_fd; break :brk switch (bun.sys.openA(file_path, 0, 0)) { .err => |err| return .{ .err = err }, .result => |fd| fd, @@ -443,7 +443,7 @@ fn appendDirectoryAssumeCapacity( event.fflags = std.c.NOTE.WRITE | std.c.NOTE.RENAME | std.c.NOTE.DELETE; // id - event.ident = @intCast(fd.int()); + event.ident = @intCast(fd.native()); // Store the hash for fast filtering later event.udata = @as(usize, @intCast(watchlist_id)); @@ -454,7 +454,7 @@ fn appendDirectoryAssumeCapacity( // - We register the event here. // our while(true) loop above receives notification of changes to any of the events created here. _ = std.posix.system.kevent( - this.platform.fd.cast(), + this.platform.fd.unwrap().?.native(), @as([]KEvent, events[0..1]).ptr, 1, @as([]KEvent, events[0..1]).ptr, @@ -505,7 +505,7 @@ pub fn appendFileMaybeLock( if (autowatch_parent_dir) { var watchlist_slice = this.watchlist.slice(); - if (dir_fd != .zero) { + if (dir_fd.isValid()) { const fds = watchlist_slice.items(.fd); if (std.mem.indexOfScalar(bun.FileDescriptor, fds, dir_fd)) |i| { parent_watch_item = @as(WatchItemIndex, @truncate(i)); @@ -607,7 +607,7 @@ pub fn addFile( if (this.indexOf(hash)) |index| { if (comptime FeatureFlags.atomic_file_watcher) { // On Linux, the file descriptor might be out of date. - if (fd.int() > 0) { + if (fd.isValid()) { var fds = this.watchlist.items(.fd); fds[index] = fd; } diff --git a/src/allocators/linux_memfd_allocator.zig b/src/allocators/linux_memfd_allocator.zig index dfff46427f..76f7be3a81 100644 --- a/src/allocators/linux_memfd_allocator.zig +++ b/src/allocators/linux_memfd_allocator.zig @@ -23,14 +23,14 @@ pub const LinuxMemFdAllocator = struct { pub const ref = RefCount.ref; pub const deref = RefCount.deref; - fd: bun.FileDescriptor = .zero, ref_count: RefCount, + fd: bun.FileDescriptor = .invalid, size: usize = 0, var memfd_counter = std.atomic.Value(usize).init(0); fn deinit(this: *LinuxMemFdAllocator) void { - _ = bun.sys.close(this.fd); + this.fd.close(); bun.destroy(this); } @@ -153,13 +153,13 @@ pub const LinuxMemFdAllocator = struct { } bun.Output.debugWarn("Failed to write to memfd: {}", .{err}); - _ = bun.sys.close(fd); + fd.close(); return .{ .err = err }; }, .result => |result| { if (result == 0) { bun.Output.debugWarn("Failed to write to memfd: EOF", .{}); - _ = bun.sys.close(fd); + fd.close(); return .{ .err = bun.sys.Error.fromCode(.NOMEM, .write) }; } written += @intCast(result); diff --git a/src/async/posix_event_loop.zig b/src/async/posix_event_loop.zig index 343843ca63..59f88fcdcc 100644 --- a/src/async/posix_event_loop.zig +++ b/src/async/posix_event_loop.zig @@ -957,7 +957,7 @@ pub const FilePoll = struct { pub fn unregisterWithFd(this: *FilePoll, loop: *Loop, fd: bun.FileDescriptor, force_unregister: bool) JSC.Maybe(void) { if (Environment.allow_assert) { - bun.assert(fd.int() >= 0 and fd != bun.invalid_fd); + bun.assert(fd.native() >= 0 and fd != bun.invalid_fd); } defer this.deactivate(loop); @@ -1105,7 +1105,7 @@ pub const LinuxWaker = struct { fd: bun.FileDescriptor, pub fn init() !Waker { - return initWithFileDescriptor(bun.toFD(try std.posix.eventfd(0, 0))); + return initWithFileDescriptor(.fromNative(try std.posix.eventfd(0, 0))); } pub fn getFd(this: *const Waker) bun.FileDescriptor { @@ -1151,7 +1151,7 @@ pub const KEventWaker = struct { } pub fn getFd(this: *const Waker) bun.FileDescriptor { - return bun.toFD(this.kq); + return .fromNative(this.kq); } pub fn wait(this: Waker) void { @@ -1210,15 +1210,15 @@ pub const Closer = struct { pub fn close( fd: bun.FileDescriptor, /// for compatibility with windows version - _: anytype, + _: void, ) void { - bun.assert(fd != bun.invalid_fd); + bun.assert(fd.isValid()); JSC.WorkPool.schedule(&Closer.new(.{ .fd = fd }).task); } fn onClose(task: *JSC.WorkPoolTask) void { const closer: *Closer = @fieldParentPtr("task", task); defer bun.destroy(closer); - _ = bun.sys.close(closer.fd); + closer.fd.close(); } }; diff --git a/src/async/windows_event_loop.zig b/src/async/windows_event_loop.zig index ecb3b2c21a..3d2d8359e5 100644 --- a/src/async/windows_event_loop.zig +++ b/src/async/windows_event_loop.zig @@ -190,7 +190,7 @@ pub const FilePoll = struct { // TODO(@paperclover): This cast is extremely suspicious. At best, `fd` is // the wrong type (it should be a uv handle), at worst this code is a // crash due to invalid memory access. - uv.uv_unref(@ptrFromInt(@intFromEnum(this.fd))); + uv.uv_unref(@ptrFromInt(@as(u64, @bitCast(this.fd)))); return true; } @@ -394,12 +394,11 @@ pub const Waker = struct { pub const Closer = struct { io_request: uv.fs_t, - pub fn close(fd: uv.uv_file, loop: *uv.Loop) void { + pub fn close(fd: bun.FileDescriptor, loop: *uv.Loop) void { const closer = bun.new(Closer, .{ .io_request = std.mem.zeroes(uv.fs_t) }); - // data is not overridden by libuv when calling uv_fs_close, its ok to set it here closer.io_request.data = closer; - if (uv.uv_fs_close(loop, &closer.io_request, fd, onClose).errEnum()) |err| { + if (uv.uv_fs_close(loop, &closer.io_request, fd.uv(), onClose).errEnum()) |err| { Output.debugWarn("libuv close() failed = {}", .{err}); bun.destroy(closer); } @@ -408,7 +407,7 @@ pub const Closer = struct { fn onClose(req: *uv.fs_t) callconv(.C) void { var closer: *Closer = @fieldParentPtr("io_request", req); bun.assert(closer == @as(*Closer, @alignCast(@ptrCast(req.data.?)))); - bun.sys.syslog("uv_fs_close({}) = {}", .{ bun.toFD(req.file.fd), req.result }); + bun.sys.syslog("uv_fs_close({}) = {}", .{ bun.FD.fromUV(req.file.fd), req.result }); if (comptime Environment.allow_assert) { if (closer.io_request.result.errEnum()) |err| { diff --git a/src/bake/DevServer.zig b/src/bake/DevServer.zig index 95d68ea7cb..d1f47c470e 100644 --- a/src/bake/DevServer.zig +++ b/src/bake/DevServer.zig @@ -5262,10 +5262,10 @@ const DirectoryWatchStore = struct { errdefer store.watches.swapRemoveAt(gop.index); // Try to use an existing open directory handle - const cache_fd = if (dev.server_transpiler.resolver.readDirInfo(dir_name_to_watch) catch null) |cache| fd: { - const fd = cache.getFileDescriptor(); - break :fd if (fd == .zero) null else fd; - } else null; + const cache_fd = if (dev.server_transpiler.resolver.readDirInfo(dir_name_to_watch) catch null) |cache| + cache.getFileDescriptor().unwrapValid() + else + null; const fd, const owned_fd = if (Watcher.requires_file_descriptors) if (cache_fd) |fd| .{ fd, false } @@ -5296,7 +5296,7 @@ const DirectoryWatchStore = struct { }, }, } else .{ bun.invalid_fd, false }; - errdefer _ = if (Watcher.requires_file_descriptors) if (owned_fd) bun.sys.close(fd); + errdefer if (Watcher.requires_file_descriptors) if (owned_fd) fd.close(); if (Watcher.requires_file_descriptors) debug.log("-> fd: {} ({s})", .{ fd, @@ -5351,7 +5351,7 @@ const DirectoryWatchStore = struct { store.owner().bun_watcher.removeAtIndex(entry.watch_index, 0, &.{}, .file); - defer _ = if (entry.dir_fd_owned) bun.sys.close(entry.dir); + defer if (entry.dir_fd_owned) entry.dir.close(); alloc.free(store.watches.keys()[entry_index]); store.watches.swapRemoveAt(entry_index); diff --git a/src/bun.js/ConsoleObject.zig b/src/bun.js/ConsoleObject.zig index a26b673496..2219530341 100644 --- a/src/bun.js/ConsoleObject.zig +++ b/src/bun.js/ConsoleObject.zig @@ -1003,6 +1003,9 @@ pub const Formatter = struct { stack_check: bun.StackCheck = .{ .cached_stack_end = std.math.maxInt(usize) }, can_throw_stack_overflow: bool = false, error_display_level: FormatOptions.ErrorDisplayLevel = .full, + /// If ArrayBuffer-like objects contain ascii text, the buffer is printed as a string. + /// Set true in the error printer so that ShellError prints a more readable message. + format_buffer_as_text: bool = false, pub fn deinit(this: *Formatter) void { if (bun.take(&this.map_node)) |node| { @@ -3342,6 +3345,17 @@ pub const Formatter = struct { const arrayBuffer = value.asArrayBuffer(this.globalThis).?; const slice = arrayBuffer.byteSlice(); + if (this.format_buffer_as_text and jsType == .Uint8Array and bun.strings.isValidUTF8(slice)) { + if (comptime enable_ansi_colors) { + writer.writeAll(Output.prettyFmt("", true)); + } + JSPrinter.writeJSONString(slice, Writer, writer_, .utf8) catch {}; + if (comptime enable_ansi_colors) { + writer.writeAll(Output.prettyFmt("", true)); + } + return; + } + writer.writeAll( if (arrayBuffer.typed_array_type == .Uint8Array and arrayBuffer.value.isBuffer(this.globalThis)) @@ -3353,6 +3367,7 @@ pub const Formatter = struct { writer.print("({d}) []", .{arrayBuffer.len}); return; } + writer.print("({d}) [ ", .{arrayBuffer.len}); switch (jsType) { diff --git a/src/bun.js/RuntimeTranspilerCache.zig b/src/bun.js/RuntimeTranspilerCache.zig index 22d93515e2..f1b3f81fde 100644 --- a/src/bun.js/RuntimeTranspilerCache.zig +++ b/src/bun.js/RuntimeTranspilerCache.zig @@ -172,7 +172,7 @@ pub const RuntimeTranspilerCache = struct { // First we open the tmpfile, to avoid any other work in the event of failure. var tmpfile = try bun.Tmpfile.create(destination_dir, tmpfilename).unwrap(); defer { - _ = bun.sys.close(tmpfile.fd); + tmpfile.fd.close(); } { errdefer { @@ -483,13 +483,13 @@ pub const RuntimeTranspilerCache = struct { ) !Entry { var metadata_bytes_buf: [Metadata.size * 2]u8 = undefined; const cache_fd = try bun.sys.open(cache_file_path.sliceAssumeZ(), bun.O.RDONLY, 0).unwrap(); - defer _ = bun.sys.close(cache_fd); + defer cache_fd.close(); errdefer { // On any error, we delete the cache file _ = bun.sys.unlink(cache_file_path.sliceAssumeZ()); } - const file = cache_fd.asFile(); + const file = cache_fd.stdFile(); const metadata_bytes = try file.preadAll(&metadata_bytes_buf, 0); if (comptime bun.Environment.isWindows) try file.seekTo(0); var metadata_stream = std.io.fixedBufferStream(metadata_bytes_buf[0..metadata_bytes]); @@ -551,13 +551,13 @@ pub const RuntimeTranspilerCache = struct { if (std.fs.path.dirname(cache_file_path)) |dirname| { var dir = try std.fs.cwd().makeOpenPath(dirname, .{ .access_sub_paths = true }); errdefer dir.close(); - break :brk try bun.toLibUVOwnedFD(dir.fd); + break :brk try bun.FD.fromStdDir(dir).makeLibUVOwned(); } break :brk bun.FD.cwd(); }; defer { - if (cache_dir_fd != bun.FD.cwd()) _ = bun.sys.close(cache_dir_fd); + if (cache_dir_fd != bun.FD.cwd()) cache_dir_fd.close(); } try Entry.save( diff --git a/src/bun.js/api/BunObject.zig b/src/bun.js/api/BunObject.zig index 1bcec556f8..88776ac62f 100644 --- a/src/bun.js/api/BunObject.zig +++ b/src/bun.js/api/BunObject.zig @@ -595,7 +595,7 @@ pub fn getMain(globalThis: *JSC.JSGlobalObject, _: *JSC.JSObject) JSC.JSValue { 0, ).unwrap() catch break :use_resolved_path; - defer _ = bun.sys.close(fd); + defer fd.close(); if (comptime Environment.isWindows) { var wpath: bun.WPathBuffer = undefined; const fdpath = bun.getFdPathW(fd, &wpath) catch break :use_resolved_path; diff --git a/src/bun.js/api/JSBundler.zig b/src/bun.js/api/JSBundler.zig index ebf5b15dd7..f06803a94b 100644 --- a/src/bun.js/api/JSBundler.zig +++ b/src/bun.js/api/JSBundler.zig @@ -341,13 +341,13 @@ pub const JSBundler = struct { defer path.deinit(); - var dir = std.fs.cwd().openDir(path.slice(), .{}) catch |err| { + var dir = bun.FD.fromStdDir(std.fs.cwd().openDir(path.slice(), .{}) catch |err| { return globalThis.throwPretty("{s}: failed to open root directory: {s}", .{ @errorName(err), path.slice() }); - }; + }); defer dir.close(); var rootdir_buf: bun.PathBuffer = undefined; - const rootdir = bun.getFdPath(bun.toFD(dir.fd), &rootdir_buf) catch |err| { + const rootdir = dir.getFdPath(&rootdir_buf) catch |err| { return globalThis.throwPretty("{s}: failed to get full root directory path: {s}", .{ @errorName(err), path.slice() }); }; try this.rootdir.appendSliceExact(rootdir); diff --git a/src/bun.js/api/JSTranspiler.zig b/src/bun.js/api/JSTranspiler.zig index 7332f95548..673b76d7d7 100644 --- a/src/bun.js/api/JSTranspiler.zig +++ b/src/bun.js/api/JSTranspiler.zig @@ -159,7 +159,7 @@ pub const TransformTask = struct { const parse_options = Transpiler.Transpiler.ParseOptions{ .allocator = allocator, .macro_remappings = this.macro_map, - .dirname_fd = .zero, + .dirname_fd = .invalid, .file_descriptor = null, .loader = this.loader, .jsx = jsx, @@ -807,7 +807,7 @@ fn getParseResult(this: *JSTranspiler, allocator: std.mem.Allocator, code: []con const parse_options = Transpiler.Transpiler.ParseOptions{ .allocator = allocator, .macro_remappings = this.transpiler_options.macro_map, - .dirname_fd = .zero, + .dirname_fd = .invalid, .file_descriptor = null, .loader = loader orelse this.transpiler_options.default_loader, .jsx = jsx, diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig index fc2d5f6a5c..27f486877c 100644 --- a/src/bun.js/api/bun/dns_resolver.zig +++ b/src/bun.js/api/bun/dns_resolver.zig @@ -120,13 +120,20 @@ const LibInfo = struct { } bun.assert(request.backend.libinfo.machport != null); - var poll = bun.Async.FilePoll.init(this.vm, bun.toFD(std.math.maxInt(i32) - 1), .{}, GetAddrInfoRequest, request); + var poll = bun.Async.FilePoll.init( + this.vm, + // TODO: WHAT????????? + .fromNative(std.math.maxInt(i32) - 1), + .{}, + GetAddrInfoRequest, + request, + ); request.backend.libinfo.file_poll = poll; const rc = poll.registerWithFd( this.vm.event_loop_handle.?, .machport, .one_shot, - bun.toFD(@as(i32, @intCast(@intFromPtr(request.backend.libinfo.machport)))), + .fromNative(@intCast(@intFromPtr(request.backend.libinfo.machport))), ); bun.assert(rc == .result); @@ -1572,7 +1579,7 @@ pub const InternalDNS = struct { } const fake_fd: i32 = @intCast(@intFromPtr(machport)); - var poll = bun.Async.FilePoll.init(loop, bun.toFD(fake_fd), .{}, InternalDNSRequest, req); + var poll = bun.Async.FilePoll.init(loop, .fromNative(fake_fd), .{}, InternalDNSRequest, req); const rc = poll.register(loop.loop(), .machport, true); if (rc == .err) { @@ -2366,7 +2373,7 @@ pub const DNSResolver = struct { vm.eventLoop().enter(); defer vm.eventLoop().exit(); var channel = this.channel orelse { - _ = this.polls.orderedRemove(poll.fd.int()); + _ = this.polls.orderedRemove(poll.fd.native()); poll.deinit(); return; }; @@ -2375,7 +2382,7 @@ pub const DNSResolver = struct { defer this.deref(); channel.process( - poll.fd.int(), + poll.fd.native(), poll.isReadable(), poll.isWritable(), ); @@ -2436,7 +2443,7 @@ pub const DNSResolver = struct { const poll_entry = this.polls.getOrPut(fd) catch unreachable; if (!poll_entry.found_existing) { - poll_entry.value_ptr.* = Async.FilePoll.init(vm, bun.toFD(fd), .{}, DNSResolver, this); + poll_entry.value_ptr.* = Async.FilePoll.init(vm, .fromNative(fd), .{}, DNSResolver, this); } var poll = poll_entry.value_ptr.*; diff --git a/src/bun.js/api/bun/process.zig b/src/bun.js/api/bun/process.zig index f955d7f7b5..c36292cee5 100644 --- a/src/bun.js/api/bun/process.zig +++ b/src/bun.js/api/bun/process.zig @@ -339,7 +339,7 @@ pub const Process = struct { const poll = if (this.poller == .fd) this.poller.fd else - bun.Async.FilePoll.init(this.event_loop, bun.toFD(watchfd), .{}, Process, this); + bun.Async.FilePoll.init(this.event_loop, .fromNative(watchfd), .{}, Process, this); this.poller = .{ .fd = poll }; this.poller.fd.enableKeepingProcessAlive(this.event_loop); @@ -462,9 +462,9 @@ pub const Process = struct { } if (comptime Environment.isLinux) { - if (this.pidfd != bun.invalid_fd.int() and this.pidfd > 0) { - _ = bun.sys.close(bun.toFD(this.pidfd)); - this.pidfd = @intCast(bun.invalid_fd.int()); + if (this.pidfd != bun.invalid_fd.value.as_system and this.pidfd > 0) { + bun.FD.fromNative(this.pidfd).close(); + this.pidfd = bun.invalid_fd.value.as_system; } } } @@ -904,7 +904,7 @@ const WaiterThreadPosix = struct { if (comptime Environment.isLinux) { const linux = std.os.linux; - instance.eventfd = bun.toFD(try std.posix.eventfd(0, linux.EFD.NONBLOCK | linux.EFD.CLOEXEC | 0)); + instance.eventfd = .fromNative(try std.posix.eventfd(0, linux.EFD.NONBLOCK | linux.EFD.CLOEXEC | 0)); } var thread = try std.Thread.spawn(.{ .stack_size = stack_size }, loop, .{}); @@ -1101,7 +1101,7 @@ pub const PosixSpawnResult = struct { pub fn close(this: *WindowsSpawnResult) void { for (this.extra_pipes.items) |fd| { - _ = bun.sys.close(fd); + fd.close(); } this.extra_pipes.clearAndFree(); @@ -1257,7 +1257,7 @@ pub fn spawnProcessPosix( to_set_cloexec.clearAndFree(); for (to_close_at_end.items) |fd| { - _ = bun.sys.close(fd); + fd.close(); } to_close_at_end.clearAndFree(); } @@ -1266,7 +1266,7 @@ pub fn spawnProcessPosix( errdefer { for (to_close_on_error.items) |fd| { - _ = bun.sys.close(fd); + fd.close(); } } defer to_close_on_error.clearAndFree(); @@ -1286,7 +1286,7 @@ pub fn spawnProcessPosix( for (0..3) |i| { const stdio = stdios[i]; - const fileno = bun.toFD(@as(i32, @intCast(i))); + const fileno = bun.FD.fromNative(@intCast(i)); const flag = if (i == 0) @as(u32, bun.O.RDONLY) else @as(u32, bun.O.WRONLY); switch (stdio_options[i]) { @@ -1335,8 +1335,7 @@ pub fn spawnProcessPosix( const fds: [2]bun.FileDescriptor = brk: { const pair = try bun.sys.socketpair(std.posix.AF.UNIX, std.posix.SOCK.STREAM, 0, .blocking).unwrap(); - - break :brk .{ bun.toFD(pair[if (i == 0) 1 else 0]), bun.toFD(pair[if (i == 0) 0 else 1]) }; + break :brk .{ pair[if (i == 0) 1 else 0], pair[if (i == 0) 0 else 1] }; }; if (i == 0) { @@ -1399,7 +1398,7 @@ pub fn spawnProcessPosix( } for (options.extra_fds, 0..) |ipc, i| { - const fileno = bun.toFD(@as(i32, @intCast(3 + i))); + const fileno = bun.FD.fromNative(@intCast(3 + i)); switch (ipc) { .dup2 => @panic("TODO dup2 extra fd"), @@ -1452,7 +1451,7 @@ pub fn spawnProcessPosix( defer { if (failed_after_spawn) { for (to_close_on_error.items) |fd| { - _ = bun.sys.close(fd); + fd.close(); } to_close_on_error.clearAndFree(); } @@ -1526,7 +1525,7 @@ pub fn spawnProcessWindows( defer { for (uv_files_to_close.items) |fd| { - bun.Async.Closer.close(fd, loop); + bun.Async.Closer.close(.fromUV(fd), loop); } uv_files_to_close.clearAndFree(); } @@ -1613,7 +1612,7 @@ pub fn spawnProcessWindows( }, .pipe => |fd| { stdio.flags = uv.UV_INHERIT_FD; - stdio.data.fd = bun.uvfdcast(fd); + stdio.data.fd = fd.uv(); }, } @@ -1669,7 +1668,7 @@ pub fn spawnProcessWindows( }, .pipe => |fd| { stdio.flags = uv.StdioFlags.inherit_fd; - stdio.data.fd = bun.uvfdcast(fd); + stdio.data.fd = fd.uv(); }, } } @@ -1701,14 +1700,12 @@ pub fn spawnProcessWindows( if (failed) { if (dup_fds[0] != -1) { - const r = bun.FDImpl.fromUV(dup_fds[0]).encode(); - _ = bun.sys.close(r); + bun.FD.fromUV(dup_fds[0]).close(); } } if (dup_fds[1] != -1) { - const w = bun.FDImpl.fromUV(dup_fds[1]).encode(); - _ = bun.sys.close(w); + bun.FD.fromUV(dup_fds[1]).close(); } } if (process.poller.uv.spawn(loop, &uv_process_options).toError(.uv_spawn)) |err| { @@ -1732,9 +1729,7 @@ pub fn spawnProcessWindows( if (dup_src != null and i == dup_src.?) { result_stdio.* = .unavailable; } else if (dup_tgt != null and i == dup_tgt.?) { - result_stdio.* = .{ - .buffer_fd = bun.FDImpl.fromUV(dup_fds[0]).encode(), - }; + result_stdio.* = .{ .buffer_fd = .fromUV(dup_fds[0]) }; } else switch (stdio_options[i]) { .buffer => { result_stdio.* = .{ .buffer = @ptrCast(stdio.data.stream) }; @@ -1832,8 +1827,8 @@ pub const sync = struct { err: bun.C.E = .SUCCESS, context: *SyncWindowsProcess, - onDoneCallback: *const fn (*SyncWindowsProcess, tag: bun.FDTag, chunks: []const []u8, err: bun.C.E) void = &SyncWindowsProcess.onReaderDone, - tag: bun.FDTag = .none, + onDoneCallback: *const fn (*SyncWindowsProcess, tag: SyncWindowsProcess.OutFd, chunks: []const []u8, err: bun.C.E) void = &SyncWindowsProcess.onReaderDone, + tag: SyncWindowsProcess.OutFd, pub const new = bun.TrivialNew(@This()); @@ -1873,6 +1868,8 @@ pub const sync = struct { pub const new = bun.TrivialNew(@This()); pub const deinit = bun.TrivialDeinit(@This()); + const OutFd = enum { stdout, stderr }; + stderr: []const []u8 = &.{}, stdout: []const []u8 = &.{}, err: bun.C.E = .SUCCESS, @@ -1887,7 +1884,7 @@ pub const sync = struct { this.process.deref(); } - pub fn onReaderDone(this: *SyncWindowsProcess, tag: bun.FDTag, chunks: []const []u8, err: bun.C.E) void { + pub fn onReaderDone(this: *SyncWindowsProcess, tag: OutFd, chunks: []const []u8, err: bun.C.E) void { switch (tag) { .stderr => { this.stderr = chunks; @@ -1895,7 +1892,6 @@ pub const sync = struct { .stdout => { this.stdout = chunks; }, - else => unreachable, } if (err != .SUCCESS) { this.err = err; @@ -2098,13 +2094,13 @@ pub const sync = struct { for (out_fds) |fd| { if (fd != bun.invalid_fd) { - _ = bun.sys.close(fd); + fd.close(); } } if (comptime Environment.isLinux) { if (process.pidfd) |pidfd| { - _ = bun.sys.close(bun.toFD(pidfd)); + bun.FD.fromNative(pidfd).close(); } } } @@ -2139,7 +2135,7 @@ pub const sync = struct { .result => |bytes_read| { bytes.items.len += bytes_read; if (bytes_read == 0) { - _ = bun.sys.close(fd.*); + fd.*.close(); fd.* = bun.invalid_fd; out_fd.* = bun.invalid_fd; break; diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig index 8f198ae70c..258fd03de3 100644 --- a/src/bun.js/api/bun/socket.zig +++ b/src/bun.js/api/bun/socket.zig @@ -1107,7 +1107,7 @@ pub const Listener = struct { break :brk (pipe_name != null); }, .fd => |fd| brk: { - const uvfd = bun.uvfdcast(fd); + const uvfd = fd.uv(); const fd_type = uv.uv_guess_handle(uvfd); if (fd_type == uv.Handle.Type.named_pipe) { break :brk true; @@ -1117,7 +1117,7 @@ pub const Listener = struct { const osfd: uv.uv_os_fd_t = @ptrFromInt(@as(usize, @intCast(uvfd))); if (bun.windows.GetFileType(osfd) == bun.windows.FILE_TYPE_PIPE) { // yay its a named pipe lets make it a libuv fd - connection.fd = bun.FDImpl.fromUV(uv.uv_open_osfhandle(osfd)).encode(); + connection.fd = bun.FD.fromNative(osfd).makeLibUVOwned() catch @panic("failed to allocate file descriptor"); break :brk true; } } @@ -4497,8 +4497,8 @@ pub fn jsCreateSocketPair(global: *JSC.JSGlobalObject, _: *JSC.CallFrame) bun.JS return global.throwValue(err.toJSC(global)); } - _ = bun.sys.setNonblocking(bun.toFD(fds_[0])); - _ = bun.sys.setNonblocking(bun.toFD(fds_[1])); + _ = bun.FD.fromNative(fds_[0]).updateNonblocking(true); + _ = bun.FD.fromNative(fds_[1]).updateNonblocking(true); const array = JSC.JSValue.createEmptyArray(global, 2); array.putIndex(global, 0, JSC.jsNumber(fds_[0])); diff --git a/src/bun.js/api/bun/spawn.zig b/src/bun.js/api/bun/spawn.zig index ffdcce65fd..6add9273ad 100644 --- a/src/bun.js/api/bun/spawn.zig +++ b/src/bun.js/api/bun/spawn.zig @@ -77,14 +77,14 @@ pub const BunSpawn = struct { .path = (try bun.default_allocator.dupeZ(u8, bun.span(path))).ptr, .flags = @intCast(flags), .mode = @intCast(mode), - .fds = .{ fd, bun.toFD(0) }, + .fds = .{ fd, .fromUV(0) }, }); } pub fn close(self: *Actions, fd: bun.FileDescriptor) !void { try self.actions.append(bun.default_allocator, .{ .kind = .close, - .fds = .{ fd, bun.toFD(0) }, + .fds = .{ fd, .fromUV(0) }, }); } diff --git a/src/bun.js/api/bun/spawn/stdio.zig b/src/bun.js/api/bun/spawn/stdio.zig index 61697c94f7..b7b42bd37b 100644 --- a/src/bun.js/api/bun/spawn/stdio.zig +++ b/src/bun.js/api/bun/spawn/stdio.zig @@ -14,9 +14,9 @@ const os = std.os; const uv = bun.windows.libuv; pub const Stdio = union(enum) { - inherit: void, + inherit, capture: struct { fd: bun.FileDescriptor, buf: *bun.ByteList }, - ignore: void, + ignore, fd: bun.FileDescriptor, dup2: struct { out: bun.JSC.Subprocess.StdioKind, @@ -26,8 +26,8 @@ pub const Stdio = union(enum) { blob: JSC.WebCore.AnyBlob, array_buffer: JSC.ArrayBuffer.Strong, memfd: bun.FileDescriptor, - pipe: void, - ipc: void, + pipe, + ipc, const log = bun.sys.syslog; @@ -81,7 +81,7 @@ pub const Stdio = union(enum) { blob.detach(); }, .memfd => |fd| { - _ = bun.sys.close(fd); + fd.close(); }, else => {}, } @@ -129,13 +129,13 @@ pub const Stdio = union(enum) { } Output.debugWarn("Failed to write to memfd: {s}", .{@tagName(err.getErrno())}); - _ = bun.sys.close(fd); + fd.close(); return; }, .result => |result| { if (result == 0) { Output.debugWarn("Failed to write to memfd: EOF", .{}); - _ = bun.sys.close(fd); + fd.close(); return; } written += @intCast(result); @@ -155,12 +155,12 @@ pub const Stdio = union(enum) { fn toPosix( stdio: *@This(), - i: u32, + i: i32, ) Result { return .{ .result = switch (stdio.*) { .blob => |blob| brk: { - const fd = bun.stdio(i); + const fd = bun.FD.Stdio.fromInt(i).?.fd(); if (blob.needsToReadFile()) { if (blob.store()) |store| { if (store.data.file.pathlike == .fd) { @@ -168,19 +168,18 @@ pub const Stdio = union(enum) { break :brk .{ .inherit = {} }; } - switch (bun.FDTag.get(store.data.file.pathlike.fd)) { - .stdin => { + if (store.data.file.pathlike.fd.stdioTag()) |tag| switch (tag) { + .std_in => { if (i == 1 or i == 2) { return .{ .err = .stdin_used_as_out }; } }, - .stdout, .stderr => { + .std_out, .std_err => { if (i == 0) { return .{ .err = .out_used_as_stdin }; } }, - else => {}, - } + }; break :brk .{ .pipe = store.data.file.pathlike.fd }; } @@ -209,12 +208,12 @@ pub const Stdio = union(enum) { fn toWindows( stdio: *@This(), - i: u32, + i: i32, ) Result { return .{ .result = switch (stdio.*) { .blob => |blob| brk: { - const fd = bun.stdio(i); + const fd = bun.FD.Stdio.fromInt(i).?.fd(); if (blob.needsToReadFile()) { if (blob.store()) |store| { if (store.data.file.pathlike == .fd) { @@ -222,19 +221,18 @@ pub const Stdio = union(enum) { break :brk .{ .inherit = {} }; } - switch (bun.FDTag.get(store.data.file.pathlike.fd)) { - .stdin => { + if (store.data.file.pathlike.fd.stdioTag()) |tag| switch (tag) { + .std_in => { if (i == 1 or i == 2) { return .{ .err = .stdin_used_as_out }; } }, - .stdout, .stderr => { + .std_out, .std_err => { if (i == 0) { return .{ .err = .out_used_as_stdin }; } }, - else => {}, - } + }; break :brk .{ .pipe = store.data.file.pathlike.fd }; } @@ -272,7 +270,7 @@ pub const Stdio = union(enum) { /// On windows this function allocate memory ensure that .deinit() is called or ownership is passed for all *uv.Pipe pub fn asSpawnOption( stdio: *@This(), - i: u32, + i: i32, ) Stdio.Result { if (comptime Environment.isWindows) { return stdio.toWindows(i); @@ -289,7 +287,7 @@ pub const Stdio = union(enum) { }; } - pub fn extract(out_stdio: *Stdio, globalThis: *JSC.JSGlobalObject, i: u32, value: JSValue) bun.JSError!void { + pub fn extract(out_stdio: *Stdio, globalThis: *JSC.JSGlobalObject, i: i32, value: JSValue) bun.JSError!void { switch (value) { // undefined: default .undefined, .zero => return, @@ -317,7 +315,7 @@ pub const Stdio = union(enum) { return; } else if (value.isNumber()) { const fd = value.asFileDescriptor(); - const file_fd = bun.uvfdcast(fd); + const file_fd = fd.uv(); if (file_fd < 0) { return globalThis.throwInvalidArguments("file descriptor must be a positive integer", .{}); } @@ -328,8 +326,8 @@ pub const Stdio = union(enum) { return globalThis.throwInvalidArguments("file descriptor must be a valid integer, received: {}", .{value.toFmt(&formatter)}); } - switch (bun.FDTag.get(fd)) { - .stdin => { + if (fd.stdioTag()) |tag| switch (tag) { + .std_in => { if (i == 1 or i == 2) { return globalThis.throwInvalidArguments("stdin cannot be used for stdout or stderr", .{}); } @@ -337,20 +335,19 @@ pub const Stdio = union(enum) { out_stdio.* = Stdio{ .inherit = {} }; return; }, - .stdout, .stderr => |tag| { + .std_out, .std_err => { if (i == 0) { return globalThis.throwInvalidArguments("stdout and stderr cannot be used for stdin", .{}); } - if (i == 1 and tag == .stdout) { + if (i == 1 and tag == .std_out) { out_stdio.* = .{ .inherit = {} }; return; - } else if (i == 2 and tag == .stderr) { + } else if (i == 2 and tag == .std_err) { out_stdio.* = .{ .inherit = {} }; return; } }, - else => {}, - } + }; out_stdio.* = Stdio{ .fd = fd }; return; @@ -403,31 +400,29 @@ pub const Stdio = union(enum) { return globalThis.throwInvalidArguments("stdio must be an array of 'inherit', 'ignore', or null", .{}); } - pub fn extractBlob(stdio: *Stdio, globalThis: *JSC.JSGlobalObject, blob: JSC.WebCore.AnyBlob, i: u32) bun.JSError!void { - const fd = bun.stdio(i); + pub fn extractBlob(stdio: *Stdio, globalThis: *JSC.JSGlobalObject, blob: JSC.WebCore.AnyBlob, i: i32) bun.JSError!void { + const fd = bun.FD.Stdio.fromInt(i).?.fd(); if (blob.needsToReadFile()) { if (blob.store()) |store| { if (store.data.file.pathlike == .fd) { if (store.data.file.pathlike.fd == fd) { - stdio.* = Stdio{ .inherit = {} }; + stdio.* = .inherit; } else { - switch (bun.FDTag.get(i)) { - .stdin => { - if (i == 1 or i == 2) { - return globalThis.throwInvalidArguments("stdin cannot be used for stdout or stderr", .{}); - } + // TODO: is this supposed to be `store.data.file.pathlike.fd`? + if (bun.FD.Stdio.fromInt(i)) |tag| switch (tag) { + .std_in, + => if (i == 1 or i == 2) { + return globalThis.throwInvalidArguments("stdin cannot be used for stdout or stderr", .{}); }, - - .stdout, .stderr => { - if (i == 0) { - return globalThis.throwInvalidArguments("stdout and stderr cannot be used for stdin", .{}); - } + .std_out, + .std_err, + => if (i == 0) { + return globalThis.throwInvalidArguments("stdout and stderr cannot be used for stdin", .{}); }, - else => {}, - } + }; - stdio.* = Stdio{ .fd = store.data.file.pathlike.fd }; + stdio.* = .{ .fd = store.data.file.pathlike.fd }; } return; diff --git a/src/bun.js/api/bun/subprocess.zig b/src/bun.js/api/bun/subprocess.zig index c6a1b16e6f..2a031f59c9 100644 --- a/src/bun.js/api/bun/subprocess.zig +++ b/src/bun.js/api/bun/subprocess.zig @@ -191,9 +191,9 @@ pub const StdioKind = enum { pub fn toFd(this: @This()) bun.FileDescriptor { return switch (this) { - .stdin => bun.STDIN_FD, - .stdout => bun.STDOUT_FD, - .stderr => bun.STDERR_FD, + .stdin => .stdin(), + .stdout => .stdout(), + .stderr => .stderr(), }; } @@ -453,7 +453,7 @@ const Readable = union(enum) { switch (this.*) { .memfd => |fd| { this.* = .{ .closed = {} }; - _ = bun.sys.close(fd); + fd.close(); }, .fd => |_| { this.* = .{ .closed = {} }; @@ -469,7 +469,7 @@ const Readable = union(enum) { switch (this.*) { .memfd => |fd| { this.* = .{ .closed = {} }; - _ = bun.sys.close(fd); + fd.close(); }, .fd => { this.* = .{ .closed = {} }; @@ -1498,7 +1498,7 @@ const Writable = union(enum) { this.buffer.deref(); }, .memfd => |fd| { - _ = bun.sys.close(fd); + fd.close(); this.* = .{ .ignore = {} }; }, .ignore => {}, @@ -1512,7 +1512,7 @@ const Writable = union(enum) { _ = pipe.end(null); }, .memfd => |fd| { - _ = bun.sys.close(fd); + fd.close(); this.* = .{ .ignore = {} }; }, .fd => { @@ -1718,7 +1718,7 @@ pub fn finalizeStreams(this: *Subprocess) void { item.buffer.close(onPipeClose); } } else { - _ = bun.sys.close(item); + item.close(); } } this.stdio_pipes.clearAndFree(bun.default_allocator); @@ -2078,7 +2078,7 @@ pub fn spawnMaybeSync( if (!stdio_val.isEmptyOrUndefinedOrNull()) { if (stdio_val.jsType().isArray()) { var stdio_iter = stdio_val.arrayIterator(globalThis); - var i: u32 = 0; + var i: u31 = 0; while (stdio_iter.next()) |value| : (i += 1) { try stdio[i].extract(globalThis, i, value); if (i == 2) @@ -2185,7 +2185,7 @@ pub fn spawnMaybeSync( if (should_close_memfd) { inline for (0..stdio.len) |fd_index| { if (stdio[fd_index] == .memfd) { - _ = bun.sys.close(stdio[fd_index].memfd); + stdio[fd_index].memfd.close(); stdio[fd_index] = .ignore; } } @@ -2205,12 +2205,12 @@ pub fn spawnMaybeSync( // // When Bun.spawn() is given an `.ipc` callback, it enables IPC as follows: env_array.ensureUnusedCapacity(allocator, 3) catch |err| return globalThis.throwError(err, "in Bun.spawn") catch return .zero; - const ipc_fd: u32 = brk: { + const ipc_fd: i32 = brk: { if (ipc_channel == -1) { // If the user didn't specify an IPC channel, we need to add one ipc_channel = @intCast(extra_fds.items.len); var ipc_extra_fd_default = Stdio{ .ipc = {} }; - const fd: u32 = @intCast(ipc_channel + 3); + const fd: i32 = ipc_channel + 3; switch (ipc_extra_fd_default.asSpawnOption(fd)) { .result => |opt| { try extra_fds.append(opt); diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig index 4a08c77d5f..fa96c42682 100644 --- a/src/bun.js/api/ffi.zig +++ b/src/bun.js/api/ffi.zig @@ -280,27 +280,27 @@ pub const FFI = struct { // On Alpine and RHEL-based distros, the paths are not suffixed if (Environment.isX64) { - if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/include/x86_64-linux-gnu").isTrue()) { + if (bun.FD.cwd().directoryExistsAt("/usr/include/x86_64-linux-gnu").isTrue()) { cached_default_system_include_dir = "/usr/include/x86_64-linux-gnu"; - } else if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/include").isTrue()) { + } else if (bun.FD.cwd().directoryExistsAt("/usr/include").isTrue()) { cached_default_system_include_dir = "/usr/include"; } - if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/lib/x86_64-linux-gnu").isTrue()) { + if (bun.FD.cwd().directoryExistsAt("/usr/lib/x86_64-linux-gnu").isTrue()) { cached_default_system_library_dir = "/usr/lib/x86_64-linux-gnu"; - } else if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/lib64").isTrue()) { + } else if (bun.FD.cwd().directoryExistsAt("/usr/lib64").isTrue()) { cached_default_system_library_dir = "/usr/lib64"; } } else if (Environment.isAarch64) { - if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/include/aarch64-linux-gnu").isTrue()) { + if (bun.FD.cwd().directoryExistsAt("/usr/include/aarch64-linux-gnu").isTrue()) { cached_default_system_include_dir = "/usr/include/aarch64-linux-gnu"; - } else if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/include").isTrue()) { + } else if (bun.FD.cwd().directoryExistsAt("/usr/include").isTrue()) { cached_default_system_include_dir = "/usr/include"; } - if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/lib/aarch64-linux-gnu").isTrue()) { + if (bun.FD.cwd().directoryExistsAt("/usr/lib/aarch64-linux-gnu").isTrue()) { cached_default_system_library_dir = "/usr/lib/aarch64-linux-gnu"; - } else if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/lib64").isTrue()) { + } else if (bun.FD.cwd().directoryExistsAt("/usr/lib64").isTrue()) { cached_default_system_library_dir = "/usr/lib64"; } } @@ -368,13 +368,13 @@ pub const FFI = struct { } if (Environment.isAarch64) { - if (bun.sys.directoryExistsAt(std.fs.cwd(), "/opt/homebrew/include").isTrue()) { + if (bun.FD.cwd().directoryExistsAt("/opt/homebrew/include").isTrue()) { state.addSysIncludePath("/opt/homebrew/include") catch { debug("TinyCC failed to add library path", .{}); }; } - if (bun.sys.directoryExistsAt(std.fs.cwd(), "/opt/homebrew/lib").isTrue()) { + if (bun.FD.cwd().directoryExistsAt("/opt/homebrew/lib").isTrue()) { state.addLibraryPath("/opt/homebrew/lib") catch { debug("TinyCC failed to add library path", .{}); }; @@ -395,13 +395,13 @@ pub const FFI = struct { } if (Environment.isPosix) { - if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/local/include").isTrue()) { + if (bun.FD.cwd().directoryExistsAt("/usr/local/include").isTrue()) { state.addSysIncludePath("/usr/local/include") catch { debug("TinyCC failed to add sysinclude path", .{}); }; } - if (bun.sys.directoryExistsAt(std.fs.cwd(), "/usr/local/lib").isTrue()) { + if (bun.FD.cwd().directoryExistsAt("/usr/local/lib").isTrue()) { state.addLibraryPath("/usr/local/lib") catch { debug("TinyCC failed to add library path", .{}); }; @@ -2333,7 +2333,7 @@ const CompilerRT = struct { }) catch {}; } var path_buf: [bun.MAX_PATH_BYTES]u8 = undefined; - compiler_rt_dir = bun.default_allocator.dupeZ(u8, bun.getFdPath(bunCC, &path_buf) catch return) catch bun.outOfMemory(); + compiler_rt_dir = bun.default_allocator.dupeZ(u8, bun.getFdPath(.fromStdDir(bunCC), &path_buf) catch return) catch bun.outOfMemory(); } var create_compiler_rt_dir_once = std.once(createCompilerRTDir); diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 04651d4573..a378add093 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -2905,7 +2905,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // use node syscall so that we don't segfault on BADF if (sendfile.auto_close) - _ = bun.sys.close(sendfile.fd); + sendfile.fd.close(); } const separator: string = "\r\n"; const separator_iovec = [1]std.posix.iovec_const{.{ @@ -3056,7 +3056,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp .err => |err| { this.runErrorHandler(err.withPathLike(file.pathlike).toJSC(globalThis)); if (auto_close) { - _ = bun.sys.close(fd); + fd.close(); } return; }, @@ -3065,7 +3065,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (Environment.isMac) { if (!bun.isRegularFile(stat.mode)) { if (auto_close) { - _ = bun.sys.close(fd); + fd.close(); } var err = bun.sys.Error{ @@ -3084,7 +3084,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (Environment.isLinux) { if (!(bun.isRegularFile(stat.mode) or std.posix.S.ISFIFO(stat.mode) or std.posix.S.ISSOCK(stat.mode))) { if (auto_close) { - _ = bun.sys.close(fd); + fd.close(); } var err = bun.sys.Error{ diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig index a0c4cb8401..f0eee24a4a 100644 --- a/src/bun.js/base.zig +++ b/src/bun.js/base.zig @@ -326,7 +326,7 @@ pub const ArrayBuffer = extern struct { pub fn toJSBufferFromMemfd(fd: bun.FileDescriptor, globalObject: *JSC.JSGlobalObject) bun.JSError!JSC.JSValue { const stat = switch (bun.sys.fstat(fd)) { .err => |err| { - _ = bun.sys.close(fd); + fd.close(); return globalObject.throwValue(err.toJSC(globalObject)); }, .result => |fstat| fstat, @@ -335,7 +335,7 @@ pub const ArrayBuffer = extern struct { const size = stat.size; if (size == 0) { - _ = bun.sys.close(fd); + fd.close(); return createBuffer(globalObject, ""); } @@ -345,7 +345,7 @@ pub const ArrayBuffer = extern struct { // So we clone it when it's small. if (size < mmap_threshold) { const result = toJSBufferFromFd(fd, @intCast(size), globalObject); - _ = bun.sys.close(fd); + fd.close(); return result; } @@ -357,7 +357,7 @@ pub const ArrayBuffer = extern struct { fd, 0, ); - _ = bun.sys.close(fd); + fd.close(); switch (result) { .result => |buf| { diff --git a/src/bun.js/bindings/JSValue.zig b/src/bun.js/bindings/JSValue.zig index e2c890883c..a0b82b5141 100644 --- a/src/bun.js/bindings/JSValue.zig +++ b/src/bun.js/bindings/JSValue.zig @@ -2426,7 +2426,7 @@ pub const JSValue = enum(i64) { pub fn asFileDescriptor(this: JSValue) bun.FileDescriptor { bun.assert(this.isNumber()); - return bun.FDImpl.fromUV(this.toInt32()).encode(); + return .fromUV(this.toInt32()); } pub inline fn toU16(this: JSValue) u16 { diff --git a/src/bun.js/bindings/SystemError.zig b/src/bun.js/bindings/SystemError.zig index 34b9fa57c9..01c2621532 100644 --- a/src/bun.js/bindings/SystemError.zig +++ b/src/bun.js/bindings/SystemError.zig @@ -9,13 +9,14 @@ const JSGlobalObject = JSC.JSGlobalObject; pub const SystemError = extern struct { errno: c_int = 0, /// label for errno - code: String = String.empty, - message: String = String.empty, - path: String = String.empty, - syscall: String = String.empty, - hostname: String = String.empty, - fd: bun.FileDescriptor = bun.toFD(-1), - dest: String = String.empty, + code: String = .empty, + message: String = .empty, + path: String = .empty, + syscall: String = .empty, + hostname: String = .empty, + /// MinInt = no file descriptor + fd: c_int = std.math.minInt(c_int), + dest: String = .empty, pub fn Maybe(comptime Result: type) type { return union(enum) { diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 4666d5c40c..4ab3ed9cea 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -2077,7 +2077,7 @@ JSC__JSValue SystemError__toErrorInstance(const SystemError* arg0, result->putDirect(vm, clientData->builtinNames().destPublicName(), dest, JSC::PropertyAttribute::DontDelete | 0); } - if (err.fd != -1) { + if (err.fd >= 0) { JSC::JSValue fd = JSC::JSValue(jsNumber(err.fd)); result->putDirect(vm, names.fdPublicName(), fd, JSC::PropertyAttribute::DontDelete | 0); diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index b0513402e3..1fe5690be7 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -132,6 +132,7 @@ typedef struct SystemError { BunString path; BunString syscall; BunString hostname; + /// MinInt if not specified int fd; BunString dest; } SystemError; diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index b7332b3434..43777db6aa 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -2120,7 +2120,7 @@ pub const MiniEventLoop = struct { pub fn stderr(this: *MiniEventLoop) *JSC.WebCore.Blob.Store { return this.stderr_store orelse brk: { var mode: bun.Mode = 0; - const fd = if (Environment.isWindows) bun.FDImpl.fromUV(2).encode() else bun.STDERR_FD; + const fd = bun.FD.fromUV(2); switch (bun.sys.fstat(fd)) { .result => |stat| { @@ -2151,7 +2151,7 @@ pub const MiniEventLoop = struct { pub fn stdout(this: *MiniEventLoop) *JSC.WebCore.Blob.Store { return this.stdout_store orelse brk: { var mode: bun.Mode = 0; - const fd = if (Environment.isWindows) bun.FDImpl.fromUV(1).encode() else bun.STDOUT_FD; + const fd = bun.FD.stdout(); switch (bun.sys.fstat(fd)) { .result => |stat| { diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 900a96669a..b9d3fa55e7 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -1189,8 +1189,8 @@ pub const VirtualMachine = struct { .json; IPC.log("IPC environment variables: NODE_CHANNEL_FD={s}, NODE_CHANNEL_SERIALIZATION_MODE={s}", .{ fd_s, @tagName(mode) }); - if (std.fmt.parseInt(i32, fd_s, 10)) |fd| { - this.initIPCInstance(bun.toFD(fd), mode); + if (std.fmt.parseInt(u31, fd_s, 10)) |fd| { + this.initIPCInstance(.fromUV(fd), mode); } else |_| { Output.warn("Failed to parse IPC channel number '{s}'", .{fd_s}); } @@ -4082,12 +4082,15 @@ pub const VirtualMachine = struct { const prev_disable_inspect_custom = formatter.disable_inspect_custom; const prev_quote_strings = formatter.quote_strings; const prev_max_depth = formatter.max_depth; + const prev_format_buffer_as_text = formatter.format_buffer_as_text; formatter.depth += 1; + formatter.format_buffer_as_text = true; defer { formatter.depth -= 1; formatter.max_depth = prev_max_depth; formatter.quote_strings = prev_quote_strings; formatter.disable_inspect_custom = prev_disable_inspect_custom; + formatter.format_buffer_as_text = prev_format_buffer_as_text; } formatter.max_depth = 1; formatter.quote_strings = true; @@ -4420,7 +4423,8 @@ pub const VirtualMachine = struct { /// IPC is put in this "enabled but not started" state when IPC is detected /// but the client JavaScript has not yet done `.on("message")` waiting: struct { - info: IPCInfoType, + // TODO: rename to `fd` + info: bun.FD, mode: IPC.Mode, }, initialized: *IPCInstance, @@ -4495,8 +4499,7 @@ pub const VirtualMachine = struct { pub const Handlers = IPC.NewIPCHandler(IPCInstance); }; - const IPCInfoType = bun.FileDescriptor; - pub fn initIPCInstance(this: *VirtualMachine, info: IPCInfoType, mode: IPC.Mode) void { + pub fn initIPCInstance(this: *VirtualMachine, info: bun.FD, mode: IPC.Mode) void { IPC.log("initIPCInstance {}", .{info}); this.ipc = .{ .waiting = .{ .info = info, .mode = mode } }; } @@ -4929,13 +4932,13 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime const abs_path: string = brk: { if (dir_ent.entries.get(@as([]const u8, @ptrCast(changed_name)))) |file_ent| { // reset the file descriptor - file_ent.entry.cache.fd = .zero; + file_ent.entry.cache.fd = .invalid; file_ent.entry.need_stat = true; path_string = file_ent.entry.abs_path; file_hash = Watcher.getHash(path_string.slice()); for (hashes, 0..) |hash, entry_id| { if (hash == file_hash) { - if (file_descriptors[entry_id] != .zero) { + if (file_descriptors[entry_id].isValid()) { if (prev_entry_id != entry_id) { current_task.append(hashes[entry_id]); ctx.removeAtIndex( diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index 024ad6cdcb..a64b65fac9 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -424,8 +424,8 @@ pub const RuntimeTranspilerStore = struct { switch (vm.bun_watcher) { .hot, .watch => { if (vm.bun_watcher.indexOf(hash)) |index| { - const _fd = vm.bun_watcher.watchlist().items(.fd)[index]; - fd = if (!_fd.isStdio()) _fd else null; + const watcher_fd = vm.bun_watcher.watchlist().items(.fd)[index]; + fd = if (watcher_fd.stdioTag() == null) watcher_fd else null; package_json = vm.bun_watcher.watchlist().items(.package_json)[index]; } }, @@ -449,7 +449,7 @@ pub const RuntimeTranspilerStore = struct { // var should_close_input_file_fd = fd == null; - var input_file_fd: StoredFileDescriptorType = .zero; + var input_file_fd: StoredFileDescriptorType = .invalid; const is_main = vm.main.len == path.text.len and vm.main_hash == hash and @@ -465,7 +465,7 @@ pub const RuntimeTranspilerStore = struct { .allocator = allocator, .path = path, .loader = loader, - .dirname_fd = .zero, + .dirname_fd = .invalid, .file_descriptor = fd, .file_fd_ptr = &input_file_fd, .file_hash = hash, @@ -487,9 +487,9 @@ pub const RuntimeTranspilerStore = struct { }; defer { - if (should_close_input_file_fd and input_file_fd != .zero) { - _ = bun.sys.close(input_file_fd); - input_file_fd = .zero; + if (should_close_input_file_fd and input_file_fd.isValid()) { + input_file_fd.close(); + input_file_fd = .invalid; } } @@ -508,7 +508,7 @@ pub const RuntimeTranspilerStore = struct { false, ) orelse { if (vm.isWatcherEnabled()) { - if (input_file_fd != .zero) { + if (input_file_fd.isValid()) { if (!is_node_override and std.fs.path.isAbsolute(path.text) and !strings.contains(path.text, "node_modules")) { should_close_input_file_fd = false; _ = vm.bun_watcher.addFile( @@ -516,7 +516,7 @@ pub const RuntimeTranspilerStore = struct { path.text, hash, loader, - .zero, + .invalid, package_json, true, ); @@ -529,7 +529,7 @@ pub const RuntimeTranspilerStore = struct { }; if (vm.isWatcherEnabled()) { - if (input_file_fd != .zero) { + if (input_file_fd.isValid()) { if (!is_node_override and std.fs.path.isAbsolute(path.text) and !strings.contains(path.text, "node_modules")) { @@ -539,7 +539,7 @@ pub const RuntimeTranspilerStore = struct { path.text, hash, loader, - .zero, + .invalid, package_json, true, ); @@ -719,13 +719,11 @@ pub const ModuleLoader = struct { var tmpname_buf: bun.PathBuffer = undefined; const tmpfilename = bun.sliceTo(bun.fs.FileSystem.instance.tmpname(extname, &tmpname_buf, bun.hash(file.name)) catch return null, 0); - const tmpdir = bun.fs.FileSystem.instance.tmpdir() catch return null; + const tmpdir: bun.FD = .fromStdDir(bun.fs.FileSystem.instance.tmpdir() catch return null); // First we open the tmpfile, to avoid any other work in the event of failure. - const tmpfile = bun.Tmpfile.create(bun.toFD(tmpdir.fd), tmpfilename).unwrap() catch return null; - defer { - _ = bun.sys.close(tmpfile.fd); - } + const tmpfile = bun.Tmpfile.create(tmpdir, tmpfilename).unwrap() catch return null; + defer tmpfile.fd.close(); switch (JSC.Node.NodeFS.writeFileWithPathBuffer( &tmpname_buf, // not used @@ -734,10 +732,8 @@ pub const ModuleLoader = struct { .data = .{ .encoded_slice = ZigString.Slice.fromUTF8NeverFree(file.contents), }, - .dirfd = bun.toFD(tmpdir.fd), - .file = .{ - .fd = tmpfile.fd, - }, + .dirfd = tmpdir, + .file = .{ .fd = tmpfile.fd }, .encoding = .buffer, }, )) { @@ -1426,7 +1422,7 @@ pub const ModuleLoader = struct { path.text, this.hash, options.Loader.fromAPI(this.loader), - .zero, + .invalid, this.package_json, true, ); @@ -1563,8 +1559,7 @@ pub const ModuleLoader = struct { var package_json: ?*PackageJSON = null; if (jsc_vm.bun_watcher.indexOf(hash)) |index| { - const maybe_fd = jsc_vm.bun_watcher.watchlist().items(.fd)[index]; - fd = if (maybe_fd != .zero) maybe_fd else null; + fd = jsc_vm.bun_watcher.watchlist().items(.fd)[index].unwrapValid(); package_json = jsc_vm.bun_watcher.watchlist().items(.package_json)[index]; } @@ -1641,7 +1636,7 @@ pub const ModuleLoader = struct { }; defer { if (should_close_input_file_fd and input_file_fd != bun.invalid_fd) { - _ = bun.sys.close(input_file_fd); + input_file_fd.close(); input_file_fd = bun.invalid_fd; } } @@ -1664,7 +1659,7 @@ pub const ModuleLoader = struct { ) orelse { if (comptime !disable_transpilying) { if (jsc_vm.isWatcherEnabled()) { - if (input_file_fd != .zero) { + if (input_file_fd.isValid()) { if (!is_node_override and std.fs.path.isAbsolute(path.text) and !strings.contains(path.text, "node_modules")) { should_close_input_file_fd = false; _ = jsc_vm.bun_watcher.addFile( @@ -1672,7 +1667,7 @@ pub const ModuleLoader = struct { path.text, hash, loader, - .zero, + .invalid, package_json, true, ); @@ -1707,7 +1702,7 @@ pub const ModuleLoader = struct { if (comptime !disable_transpilying) { if (jsc_vm.isWatcherEnabled()) { - if (input_file_fd != .zero) { + if (input_file_fd.isValid()) { if (!is_node_override and std.fs.path.isAbsolute(path.text) and !strings.contains(path.text, "node_modules")) { should_close_input_file_fd = false; _ = jsc_vm.bun_watcher.addFile( @@ -1715,7 +1710,7 @@ pub const ModuleLoader = struct { path.text, hash, loader, - .zero, + .invalid, package_json, true, ); @@ -2134,11 +2129,11 @@ pub const ModuleLoader = struct { 0, )) { .err => break :auto_watch, - .result => |fd| break :brk @enumFromInt(fd.cast()), + .result => |fd| break :brk fd, } } else { // Otherwise, don't even bother opening it. - break :brk .zero; + break :brk .invalid; } }; const hash = bun.Watcher.getHash(path.text); @@ -2147,7 +2142,7 @@ pub const ModuleLoader = struct { path.text, hash, loader, - .zero, + .invalid, null, true, )) { @@ -2157,8 +2152,8 @@ pub const ModuleLoader = struct { // opened the file descriptor to // receive event notifications on // it, we should close it. - if (input_fd != .zero) { - _ = bun.sys.close(bun.toFD(input_fd)); + if (input_fd.isValid()) { + input_fd.close(); } } diff --git a/src/bun.js/node/dir_iterator.zig b/src/bun.js/node/dir_iterator.zig index 7b6b89286e..3bfdb577bf 100644 --- a/src/bun.js/node/dir_iterator.zig +++ b/src/bun.js/node/dir_iterator.zig @@ -260,14 +260,14 @@ pub fn NewIterator(comptime use_windows_ospath: bool) type { self.first = false; if (io.Information == 0) { - bun.sys.syslog("NtQueryDirectoryFile({}) = 0", .{bun.toFD(self.dir.fd)}); + bun.sys.syslog("NtQueryDirectoryFile({}) = 0", .{bun.FD.fromStdDir(self.dir)}); return .{ .result = null }; } self.index = 0; self.end_index = io.Information; // If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER. if (rc == .INVALID_PARAMETER) { - bun.sys.syslog("NtQueryDirectoryFile({}) = {s}", .{ bun.toFD(self.dir.fd), @tagName(rc) }); + bun.sys.syslog("NtQueryDirectoryFile({}) = {s}", .{ bun.FD.fromStdDir(self.dir), @tagName(rc) }); return .{ .err = .{ .errno = @intFromEnum(bun.C.SystemErrno.ENOTDIR), @@ -277,13 +277,13 @@ pub fn NewIterator(comptime use_windows_ospath: bool) type { } if (rc == .NO_MORE_FILES) { - bun.sys.syslog("NtQueryDirectoryFile({}) = {s}", .{ bun.toFD(self.dir.fd), @tagName(rc) }); + bun.sys.syslog("NtQueryDirectoryFile({}) = {s}", .{ bun.FD.fromStdDir(self.dir), @tagName(rc) }); self.end_index = self.index; return .{ .result = null }; } if (rc != .SUCCESS) { - bun.sys.syslog("NtQueryDirectoryFile({}) = {s}", .{ bun.toFD(self.dir.fd), @tagName(rc) }); + bun.sys.syslog("NtQueryDirectoryFile({}) = {s}", .{ bun.FD.fromStdDir(self.dir), @tagName(rc) }); if ((bun.windows.Win32Error.fromNTStatus(rc).toSystemErrno())) |errno| { return .{ @@ -302,7 +302,7 @@ pub fn NewIterator(comptime use_windows_ospath: bool) type { }; } - bun.sys.syslog("NtQueryDirectoryFile({}) = {d}", .{ bun.toFD(self.dir.fd), self.end_index }); + bun.sys.syslog("NtQueryDirectoryFile({}) = {d}", .{ bun.FD.fromStdDir(self.dir), self.end_index }); } const dir_info: FILE_DIRECTORY_INFORMATION_PTR = @ptrCast(@alignCast(&self.buf[self.index])); diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index ffcb30f44e..5b4e40f36a 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -16,7 +16,8 @@ const Encoding = JSC.Node.Encoding; const PosixToWinNormalizer = bun.path.PosixToWinNormalizer; const FileDescriptor = bun.FileDescriptor; -const FDImpl = bun.FDImpl; +const FD = bun.FD; + const AbortSignal = JSC.AbortSignal; const Syscall = if (Environment.isWindows) bun.sys.sys_uv else bun.sys; @@ -214,7 +215,7 @@ pub const Async = struct { }, .close => { const args: Arguments.Close = task.args; - const fd = args.fd.impl().uv(); + const fd = args.fd.uv(); if (fd == 1 or fd == 2) { log("uv close({}) SKIPPED", .{fd}); @@ -230,7 +231,7 @@ pub const Async = struct { .read => { const args: Arguments.Read = task.args; const B = uv.uv_buf_t.init; - const fd = args.fd.impl().uv(); + const fd = args.fd.uv(); var buf = args.buffer.slice(); buf = buf[@min(buf.len, args.offset)..]; @@ -243,7 +244,7 @@ pub const Async = struct { .write => { const args: Arguments.Write = task.args; const B = uv.uv_buf_t.init; - const fd = args.fd.impl().uv(); + const fd = args.fd.uv(); var buf = args.buffer.slice(); buf = buf[@min(buf.len, args.offset)..]; @@ -255,7 +256,7 @@ pub const Async = struct { }, .readv => { const args: Arguments.Readv = task.args; - const fd = args.fd.impl().uv(); + const fd = args.fd.uv(); const bufs = args.buffers.buffers.items; const pos: i64 = args.position orelse -1; @@ -268,7 +269,7 @@ pub const Async = struct { }, .writev => { const args_: Arguments.Writev = task.args; - const fd = args_.fd.impl().uv(); + const fd = args_.fd.uv(); const bufs = args_.buffers.buffers.items; if (bufs.len == 0) { @@ -878,7 +879,7 @@ pub fn NewAsyncCpTask(comptime is_shell: bool) type { }, .result => |fd_| fd_, }; - defer _ = Syscall.close(fd); + defer fd.close(); var buf: bun.OSPathBuffer = undefined; @@ -904,7 +905,7 @@ pub fn NewAsyncCpTask(comptime is_shell: bool) type { }, } - const dir = fd.asDir(); + const dir = fd.stdDir(); var iterator = DirIterator.iterate(dir, if (Environment.isWindows) .u16 else .u8); var entry = iterator.next(); while (switch (entry) { @@ -1199,7 +1200,7 @@ pub const AsyncReaddirRecursiveTask = struct { const root_fd = this.root_fd; if (root_fd != bun.invalid_fd) { this.root_fd = bun.invalid_fd; - _ = Syscall.close(root_fd); + root_fd.close(); bun.default_allocator.free(this.root_path.slice()); this.root_path = PathString.empty; } @@ -1401,7 +1402,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice) bun.JSError!Writev { const fd_value = arguments.nextEat() orelse JSC.JSValue.undefined; - const fd = try JSC.Node.fileDescriptorFromJS(ctx, fd_value) orelse { + const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -1455,7 +1456,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice) bun.JSError!Readv { const fd_value = arguments.nextEat() orelse JSC.JSValue.undefined; - const fd = try JSC.Node.fileDescriptorFromJS(ctx, fd_value) orelse { + const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -1501,7 +1502,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice) bun.JSError!FTruncate { const fd_value = arguments.nextEat() orelse JSC.JSValue.undefined; - const fd = try JSC.Node.fileDescriptorFromJS(ctx, fd_value) orelse { + const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -1572,7 +1573,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice) bun.JSError!Fchown { const fd_value = arguments.nextEat() orelse JSC.JSValue.undefined; - const fd = try JSC.Node.fileDescriptorFromJS(ctx, fd_value) orelse { + const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -1690,7 +1691,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice) bun.JSError!FChmod { const fd_value = arguments.nextEat() orelse JSC.JSValue.undefined; - const fd = try JSC.Node.fileDescriptorFromJS(ctx, fd_value) orelse { + const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -1804,7 +1805,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice) bun.JSError!Fstat { const fd_value = arguments.nextEat() orelse JSC.JSValue.undefined; - const fd = try JSC.Node.fileDescriptorFromJS(ctx, fd_value) orelse { + const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -2319,7 +2320,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice) bun.JSError!Close { const fd_value = arguments.nextEat() orelse JSC.JSValue.undefined; - const fd = try JSC.Node.fileDescriptorFromJS(ctx, fd_value) orelse { + const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -2406,7 +2407,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice) bun.JSError!Futimes { const fd_value = arguments.nextEat() orelse JSC.JSValue.undefined; - const fd = try JSC.Node.fileDescriptorFromJS(ctx, fd_value) orelse { + const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -2478,7 +2479,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice) bun.JSError!Write { const fd_value = arguments.nextEat() orelse JSC.JSValue.undefined; - const fd = try JSC.Node.fileDescriptorFromJS(ctx, fd_value) orelse { + const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -2583,7 +2584,7 @@ pub const Arguments = struct { // fd = getValidatedFd(fd); const fd_value = arguments.nextEat() orelse JSC.JSValue.undefined; - const fd = try JSC.Node.fileDescriptorFromJS(ctx, fd_value) orelse { + const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -3021,7 +3022,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice) bun.JSError!FdataSync { const fd_value = arguments.nextEat() orelse JSC.JSValue.undefined; - const fd = try JSC.Node.fileDescriptorFromJS(ctx, fd_value) orelse { + const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -3171,7 +3172,7 @@ pub const Arguments = struct { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice) bun.JSError!Fsync { const fd_value = arguments.nextEat() orelse JSC.JSValue.undefined; - const fd = try JSC.Node.fileDescriptorFromJS(ctx, fd_value) orelse { + const fd = try bun.FD.fromJSValidated(fd_value, ctx) orelse { return throwInvalidFdError(ctx, fd_value); }; @@ -3240,7 +3241,7 @@ const Return = struct { pub const Lstat = StatOrNotFound; pub const Mkdir = StringOrUndefined; pub const Mkdtemp = JSC.ZigString; - pub const Open = FDImpl; + pub const Open = FD; pub const WriteFile = void; pub const Readv = Read; pub const StatFS = bun.JSC.Node.StatFS; @@ -3421,7 +3422,7 @@ pub const NodeFS = struct { }; defer { - _ = Syscall.close(fd); + fd.close(); } while (data.len > 0) { @@ -3438,7 +3439,10 @@ pub const NodeFS = struct { } pub fn close(_: *NodeFS, args: Arguments.Close, _: Flavor) Maybe(Return.Close) { - return if (Syscall.close(args.fd)) |err| .{ .err = err } else Maybe(Return.Close).success; + return if (args.fd.closeAllowingBadFileDescriptor(null)) |err| + .{ .err = err } + else + .success; } pub fn uv_close(_: *NodeFS, args: Arguments.Close, rc: i64) Maybe(Return.Close) { @@ -3607,7 +3611,7 @@ pub const NodeFS = struct { .err => |err| return .{ .err = err.withPath(args.src.slice()) }, }; defer { - _ = Syscall.close(src_fd); + src_fd.close(); } var flags: Mode = bun.O.CREAT | bun.O.WRONLY; @@ -3623,7 +3627,7 @@ pub const NodeFS = struct { defer { _ = Syscall.ftruncate(dest_fd, @as(std.c.off_t, @intCast(@as(u63, @truncate(wrote))))); _ = Syscall.fchmod(dest_fd, stat_.mode); - _ = Syscall.close(dest_fd); + dest_fd.close(); } return copyFileUsingReadWriteLoop(src, dest, src_fd, dest_fd, @intCast(@max(stat_.size, 0)), &wrote); @@ -3652,7 +3656,7 @@ pub const NodeFS = struct { .err => |err| return .{ .err = err }, }; defer { - _ = Syscall.close(src_fd); + src_fd.close(); } const stat_: linux.Stat = switch (Syscall.fstat(src_fd)) { @@ -3680,13 +3684,13 @@ pub const NodeFS = struct { // https://manpages.debian.org/testing/manpages-dev/ioctl_ficlone.2.en.html if (args.mode.isForceClone()) { if (ret.errnoSysP(bun.C.linux.ioctl_ficlone(dest_fd, src_fd), .ioctl_ficlone, dest)) |err| { - _ = Syscall.close(dest_fd); + dest_fd.close(); // This is racey, but it's the best we can do _ = bun.sys.unlink(dest); return err; } _ = Syscall.fchmod(dest_fd, stat_.mode); - _ = Syscall.close(dest_fd); + dest_fd.close(); return ret.success; } @@ -3695,7 +3699,7 @@ pub const NodeFS = struct { const rc = bun.C.linux.ioctl_ficlone(dest_fd, src_fd); if (rc == 0) { _ = Syscall.fchmod(dest_fd, stat_.mode); - _ = Syscall.close(dest_fd); + dest_fd.close(); return ret.success; } @@ -3707,7 +3711,7 @@ pub const NodeFS = struct { defer { _ = linux.ftruncate(dest_fd.cast(), @as(i64, @intCast(@as(u63, @truncate(wrote))))); _ = linux.fchmod(dest_fd.cast(), stat_.mode); - _ = Syscall.close(dest_fd); + dest_fd.close(); } var off_in_copy = @as(i64, @bitCast(@as(u64, 0))); @@ -3843,7 +3847,7 @@ pub const NodeFS = struct { if (Environment.isWindows) { return Syscall.fdatasync(args.fd); } - return Maybe(Return.Fdatasync).errnoSysFd(system.fdatasync(args.fd.int()), .fdatasync, args.fd) orelse Maybe(Return.Fdatasync).success; + return Maybe(Return.Fdatasync).errnoSysFd(system.fdatasync(args.fd.native()), .fdatasync, args.fd) orelse Maybe(Return.Fdatasync).success; } pub fn fstat(_: *NodeFS, args: Arguments.Fstat, _: Flavor) Maybe(Return.Fstat) { @@ -3857,7 +3861,7 @@ pub const NodeFS = struct { if (Environment.isWindows) { return Syscall.fsync(args.fd); } - return Maybe(Return.Fsync).errnoSys(system.fsync(args.fd.int()), .fsync) orelse + return Maybe(Return.Fsync).errnoSys(system.fsync(args.fd.native()), .fsync) orelse Maybe(Return.Fsync).success; } @@ -3869,7 +3873,7 @@ pub const NodeFS = struct { if (comptime Environment.isWindows) { var req: uv.fs_t = uv.fs_t.uninitialized; defer req.deinit(); - const rc = uv.uv_fs_futime(uv.Loop.get(), &req, bun.uvfdcast(args.fd), args.atime, args.mtime, null); + const rc = uv.uv_fs_futime(uv.Loop.get(), &req, args.fd.uv(), args.atime, args.mtime, null); return if (rc.errno()) |e| .{ .err = .{ .errno = e, @@ -4204,9 +4208,7 @@ pub const NodeFS = struct { .err => |err| .{ .err = err.withPath(args.path.slice()), }, - .result => |fd| fd: { - break :fd .{ .result = FDImpl.decode(fd) }; - }, + .result => |fd| .{ .result = fd }, }; } @@ -4220,7 +4222,7 @@ pub const NodeFS = struct { .from_libuv = true, } }; } - return Maybe(Return.Open).initResult(FDImpl.decode(bun.toFD(@as(u32, @intCast(rc))))); + return Maybe(Return.Open).initResult(.fromUV(@intCast(rc))); } pub fn uv_statfs(_: *NodeFS, args: Arguments.StatFS, req: *uv.fs_t, rc: i64) Maybe(Return.StatFS) { @@ -4467,7 +4469,7 @@ pub const NodeFS = struct { comptime ExpectedType: type, entries: *std.ArrayList(ExpectedType), ) Maybe(void) { - const dir = fd.asDir(); + const dir = fd.stdDir(); const is_u16 = comptime Environment.isWindows and (ExpectedType == bun.String or ExpectedType == bun.JSC.Node.Dirent); var dirent_path: bun.String = bun.String.dead; @@ -4611,11 +4613,11 @@ pub const NodeFS = struct { defer { if (comptime !is_root) { - _ = Syscall.close(fd); + fd.close(); } } - var iterator = DirIterator.iterate(fd.asDir(), .u8); + var iterator = DirIterator.iterate(fd.stdDir(), .u8); var entry = iterator.next(); var dirent_path_prev: bun.String = bun.String.empty; defer { @@ -4724,7 +4726,7 @@ pub const NodeFS = struct { // all other paths are relative to the root directory // so we can only close it once we're 100% done if (root_fd != bun.invalid_fd) { - _ = Syscall.close(root_fd); + root_fd.close(); } } @@ -4765,11 +4767,11 @@ pub const NodeFS = struct { defer { if (fd != root_fd) { - _ = Syscall.close(fd); + fd.close(); } } - var iterator = DirIterator.iterate(fd.asDir(), .u8); + var iterator = DirIterator.iterate(fd.stdDir(), .u8); var entry = iterator.next(); var dirent_path_prev: bun.String = bun.String.dead; defer { @@ -4925,7 +4927,7 @@ pub const NodeFS = struct { .result => |fd_| fd_, }; - defer _ = Syscall.close(fd); + defer fd.close(); var entries = std.ArrayList(ExpectedType).init(bun.default_allocator); return switch (readdirWithEntries(args, fd, path, ExpectedType, &entries)) { @@ -5016,9 +5018,9 @@ pub const NodeFS = struct { }, .fd => |fd| fd, }; - const fd = bun.toLibUVOwnedFD(fd_maybe_windows) catch { + const fd = fd_maybe_windows.makeLibUVOwned() catch { if (args.path == .path) - _ = Syscall.close(fd_maybe_windows); + fd_maybe_windows.close(); return .{ .err = .{ @@ -5030,7 +5032,7 @@ pub const NodeFS = struct { defer { if (args.path == .path) - _ = Syscall.close(fd); + fd.close(); } if (args.aborted()) return Maybe(Return.ReadFileWithOptions).aborted; @@ -5325,7 +5327,7 @@ pub const NodeFS = struct { defer { if (args.file == .path) - _ = bun.sys.close(fd); + fd.close(); } if (args.aborted()) return Maybe(Return.WriteFile).aborted; @@ -5531,9 +5533,7 @@ pub const NodeFS = struct { .result => |fd_| fd_, }; - defer { - _ = Syscall.close(fd); - } + defer fd.close(); const buf = switch (Syscall.getFdPath(fd, &outbuf)) { .err => |err| return .{ .err = err.withPath(path) }, @@ -5874,7 +5874,7 @@ pub const NodeFS = struct { .syscall = .truncate, } }; } - defer _ = Syscall.close(file.result); + defer file.result.close(); const ret = Syscall.ftruncate(file.result, len); return switch (ret) { .result => ret, @@ -6126,7 +6126,7 @@ pub const NodeFS = struct { } const fd = switch (Syscall.openatOSPath( - bun.toFD((std.fs.cwd().fd)), + .cwd(), src, bun.O.DIRECTORY | bun.O.RDONLY, 0, @@ -6136,7 +6136,7 @@ pub const NodeFS = struct { }, .result => |fd_| fd_, }; - defer _ = Syscall.close(fd); + defer fd.close(); switch (this.mkdirRecursiveOSPath(dest, Arguments.Mkdir.DefaultMode, false)) { .err => |err| return .{ .err = err }, @@ -6144,7 +6144,7 @@ pub const NodeFS = struct { } var iterator = iterator: { - const dir = fd.asDir(); + const dir = fd.stdDir(); break :iterator DirIterator.iterate(dir, if (Environment.isWindows) .u16 else .u8); }; var entry = iterator.next(); @@ -6299,7 +6299,7 @@ pub const NodeFS = struct { }, }; defer { - _ = Syscall.close(src_fd); + src_fd.close(); } var flags: Mode = bun.O.CREAT | bun.O.WRONLY; @@ -6340,7 +6340,7 @@ pub const NodeFS = struct { defer { _ = Syscall.ftruncate(dest_fd, @intCast(@as(u63, @truncate(wrote)))); _ = Syscall.fchmod(dest_fd, stat_.mode); - _ = Syscall.close(dest_fd); + dest_fd.close(); } return copyFileUsingReadWriteLoop(src, dest, src_fd, dest_fd, @intCast(@max(stat_.size, 0)), &wrote); @@ -6382,7 +6382,7 @@ pub const NodeFS = struct { }, }; defer { - _ = Syscall.close(src_fd); + src_fd.close(); } const stat_: linux.Stat = switch (Syscall.fstat(src_fd)) { @@ -6439,7 +6439,7 @@ pub const NodeFS = struct { const rc = bun.C.linux.ioctl_ficlone(dest_fd, src_fd); if (rc == 0) { _ = Syscall.fchmod(dest_fd, stat_.mode); - _ = Syscall.close(dest_fd); + dest_fd.close(); return ret.success; } @@ -6449,7 +6449,7 @@ pub const NodeFS = struct { defer { _ = Syscall.ftruncate(dest_fd, @as(i64, @intCast(@as(u63, @truncate(wrote))))); _ = Syscall.fchmod(dest_fd, stat_.mode); - _ = Syscall.close(dest_fd); + dest_fd.close(); } var off_in_copy = @as(i64, @bitCast(@as(u64, 0))); diff --git a/src/bun.js/node/path_watcher.zig b/src/bun.js/node/path_watcher.zig index c98fed2202..9cf3c7efcf 100644 --- a/src/bun.js/node/path_watcher.zig +++ b/src/bun.js/node/path_watcher.zig @@ -9,7 +9,8 @@ const FSEvents = @import("./fs_events.zig"); const bun = @import("root").bun; const Output = bun.Output; const Environment = bun.Environment; -const StoredFileDescriptorType = bun.StoredFileDescriptorType; +const StoredFileDescriptorType = bun.FD; +const FD = bun.FD; const string = bun.string; const JSC = bun.JSC; const VirtualMachine = JSC.VirtualMachine; @@ -42,7 +43,7 @@ pub const PathWatcherManager = struct { has_pending_tasks: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), mutex: Mutex, const PathInfo = struct { - fd: StoredFileDescriptorType = .zero, + fd: FD = .invalid, is_file: bool = true, path: [:0]const u8, dirname: string, @@ -437,7 +438,7 @@ pub const PathWatcherManager = struct { const manager = this.manager; const path = this.path; const fd = path.fd; - var iter = fd.asDir().iterate(); + var iter = fd.stdDir().iterate(); // now we iterate over all files and directories while (iter.next() catch |err| { @@ -491,7 +492,7 @@ pub const PathWatcherManager = struct { child_path.path, child_path.hash, options.Loader.file, - .zero, + .invalid, null, false, )) { @@ -584,7 +585,7 @@ pub const PathWatcherManager = struct { const path = watcher.path; if (path.is_file) { - try this.main_watcher.addFile(path.fd, path.path, path.hash, .file, .zero, null, false).unwrap(); + try this.main_watcher.addFile(path.fd, path.path, path.hash, .file, .invalid, null, false).unwrap(); } else { if (comptime Environment.isMac) { if (watcher.fsevents_watcher != null) { @@ -707,7 +708,7 @@ pub const PathWatcherManager = struct { var it = this.file_paths.iterator(); while (it.next()) |*entry| { const path = entry.value_ptr.*; - _ = bun.sys.close(path.fd); + path.fd.close(); bun.default_allocator.free(path.path); } diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 2c2edb9cdb..bfd32981fe 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -1238,13 +1238,6 @@ pub const ArgumentsSlice = struct { } }; -pub fn fileDescriptorFromJS(ctx: JSC.C.JSContextRef, value: JSC.JSValue) bun.JSError!?bun.FileDescriptor { - return if (try bun.FDImpl.fromJSValidated(value, ctx)) |fd| - fd.encode() - else - null; -} - // Equivalent to `toUnixTimestamp` // // Node.js docs: @@ -1427,9 +1420,9 @@ pub const PathOrFileDescriptor = union(Tag) { pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, allocator: std.mem.Allocator) bun.JSError!?JSC.Node.PathOrFileDescriptor { const first = arguments.next() orelse return null; - if (try bun.FDImpl.fromJSValidated(first, ctx)) |fd| { + if (try bun.FD.fromJSValidated(first, ctx)) |fd| { arguments.eat(); - return JSC.Node.PathOrFileDescriptor{ .fd = fd.encode() }; + return .{ .fd = fd }; } return JSC.Node.PathOrFileDescriptor{ diff --git a/src/bun.js/rare_data.zig b/src/bun.js/rare_data.zig index de0f7ada7c..81fc01587b 100644 --- a/src/bun.js/rare_data.zig +++ b/src/bun.js/rare_data.zig @@ -9,7 +9,6 @@ const JSC = bun.JSC; const std = @import("std"); const BoringSSL = bun.BoringSSL.c; const bun = @import("root").bun; -const FDImpl = bun.FDImpl; const Environment = bun.Environment; const WebSocketClientMask = @import("../http/websocket_http_client.zig").Mask; const UUID = @import("./uuid.zig"); @@ -134,7 +133,7 @@ pub fn closeAllListenSocketsForWatchMode(this: *RareData) void { for (this.listening_sockets_for_watch_mode.items) |socket| { // Prevent TIME_WAIT state Syscall.disableLinger(socket); - _ = Syscall.close(socket); + socket.close(); } this.listening_sockets_for_watch_mode = .{}; } @@ -323,7 +322,7 @@ pub fn stderr(rare: *RareData) *Blob.Store { bun.Analytics.Features.@"Bun.stderr" += 1; return rare.stderr_store orelse brk: { var mode: bun.Mode = 0; - const fd = if (Environment.isWindows) FDImpl.fromUV(2).encode() else bun.STDERR_FD; + const fd = bun.FD.fromUV(2); switch (Syscall.fstat(fd)) { .result => |stat| { @@ -355,7 +354,7 @@ pub fn stdout(rare: *RareData) *Blob.Store { bun.Analytics.Features.@"Bun.stdout" += 1; return rare.stdout_store orelse brk: { var mode: bun.Mode = 0; - const fd = if (Environment.isWindows) FDImpl.fromUV(1).encode() else bun.STDOUT_FD; + const fd = bun.FD.fromUV(1); switch (Syscall.fstat(fd)) { .result => |stat| { @@ -385,7 +384,7 @@ pub fn stdin(rare: *RareData) *Blob.Store { bun.Analytics.Features.@"Bun.stdin" += 1; return rare.stdin_store orelse brk: { var mode: bun.Mode = 0; - const fd = if (Environment.isWindows) FDImpl.fromUV(0).encode() else bun.STDIN_FD; + const fd = bun.FD.fromUV(0); switch (Syscall.fstat(fd)) { .result => |stat| { @@ -398,10 +397,8 @@ pub fn stdin(rare: *RareData) *Blob.Store { .ref_count = std.atomic.Value(u32).init(2), .data = .{ .file = Blob.FileStore{ - .pathlike = .{ - .fd = fd, - }, - .is_atty = if (bun.STDIN_FD.isValid()) std.posix.isatty(bun.STDIN_FD.cast()) else false, + .pathlike = .{ .fd = fd }, + .is_atty = if (fd.unwrapValid()) |valid| std.posix.isatty(valid.native()) else false, .mode = mode, }, }, diff --git a/src/bun.js/test/snapshot.zig b/src/bun.js/test/snapshot.zig index becdc04039..558a37e945 100644 --- a/src/bun.js/test/snapshot.zig +++ b/src/bun.js/test/snapshot.zig @@ -253,7 +253,7 @@ pub const Snapshots = struct { }; var file: File = .{ .id = file_id, - .file = fd.asFile(), + .file = fd.stdFile(), }; errdefer file.file.close(); @@ -521,7 +521,7 @@ pub const Snapshots = struct { var file: File = .{ .id = file_id, - .file = fd.asFile(), + .file = fd.stdFile(), }; errdefer file.file.close(); diff --git a/src/bun.js/webcore.zig b/src/bun.js/webcore.zig index 4c81c6350a..81be0163df 100644 --- a/src/bun.js/webcore.zig +++ b/src/bun.js/webcore.zig @@ -268,11 +268,11 @@ pub const Prompt = struct { // unset `ENABLE_VIRTUAL_TERMINAL_INPUT` on windows. This prevents backspace from // deleting the entire line const original_mode: if (Environment.isWindows) ?bun.windows.DWORD else void = if (comptime Environment.isWindows) - bun.win32.updateStdioModeFlags(0, .{ .unset = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT }) catch null; + bun.win32.updateStdioModeFlags(.std_in, .{ .unset = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT }) catch null; defer if (comptime Environment.isWindows) { if (original_mode) |mode| { - _ = bun.windows.SetConsoleMode(bun.win32.STDIN_FD.cast(), mode); + _ = bun.windows.SetConsoleMode(bun.FD.stdin().native(), mode); } }; diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig index 6428483265..c9b04722fd 100644 --- a/src/bun.js/webcore/blob.zig +++ b/src/bun.js/webcore/blob.zig @@ -476,7 +476,7 @@ pub const Blob = struct { switch (pathlike_tag) { .fd => { - const fd = try bun.FileDescriptor.readFrom(reader, .little); + const fd = try reader.readStruct(bun.FD); var path_or_fd = JSC.Node.PathOrFileDescriptor{ .fd = fd, @@ -719,27 +719,26 @@ pub const Blob = struct { ); }, .fd => |fd| { - const fd_impl = bun.FDImpl.decode(fd); - if (comptime Environment.isWindows) { - if (fd_impl.kind == .uv) { - try writer.print( + if (Environment.isWindows) { + switch (fd.decodeWindows()) { + .uv => |uv_file| try writer.print( comptime Output.prettyFmt(" (fd: {d})", enable_ansi_colors), - .{fd_impl.uv()}, - ); - } else { - if (Environment.allow_assert) { - comptime assert(Environment.isWindows); - @panic("this shouldn't be reachable."); - } - try writer.print( - comptime Output.prettyFmt(" (fd: {any})", enable_ansi_colors), - .{fd_impl.system()}, - ); + .{uv_file}, + ), + .windows => |handle| { + if (Environment.isDebug) { + @panic("this shouldn't be reachable."); + } + try writer.print( + comptime Output.prettyFmt(" (fd: 0x{x})", enable_ansi_colors), + .{@intFromPtr(handle)}, + ); + }, } } else { try writer.print( comptime Output.prettyFmt(" (fd: {d})", enable_ansi_colors), - .{fd_impl.system()}, + .{fd.native()}, ); } }, @@ -937,7 +936,7 @@ pub const Blob = struct { break :err; }, .result => |fd| { - _ = bun.sys.close(fd); + fd.close(); return JSC.JSPromise.resolvedPromiseValue(ctx, .jsNumber(0)); }, } @@ -1614,11 +1613,10 @@ pub const Blob = struct { // we only truncate if it's a path // if it's a file descriptor, we assume they want manual control over that behavior if (truncate) { - _ = bun.sys.ftruncate(fd, @as(i64, @intCast(written))); + _ = fd.truncate(@intCast(written)); } - if (needs_open) { - _ = bun.sys.close(fd); + fd.close(); } } if (!str.isEmpty()) { @@ -1697,11 +1695,7 @@ pub const Blob = struct { const truncate = needs_open or bytes.len == 0; var written: usize = 0; - defer { - if (needs_open) { - _ = bun.sys.close(fd); - } - } + defer if (needs_open) fd.close(); var remain = bytes; const end = remain.ptr + remain.len; @@ -1992,19 +1986,14 @@ pub const Blob = struct { break :brk copy; }, .fd => { - const optional_store: ?*Store = switch (bun.FDTag.get(path_or_fd.fd)) { - .stdin => vm.rareData().stdin(), - .stderr => vm.rareData().stderr(), - .stdout => vm.rareData().stdout(), - else => null, - }; - - if (optional_store) |store| { + if (path_or_fd.fd.stdioTag()) |tag| { + const store = switch (tag) { + .std_in => vm.rareData().stdin(), + .std_err => vm.rareData().stderr(), + .std_out => vm.rareData().stdout(), + }; store.ref(); - return Blob.initWithStore( - store, - globalThis, - ); + return Blob.initWithStore(store, globalThis); } break :brk path_or_fd.*; }, @@ -2228,7 +2217,7 @@ pub const Blob = struct { switch (file.pathlike) { .fd => |fd| { - try fd.writeTo(writer, .little); + try writer.writeStruct(fd); }, .path => |path| { const path_slice = path.slice(); @@ -2426,12 +2415,12 @@ pub const Blob = struct { if (is_allowed_to_close_fd and this.opened_fd != invalid_fd and - !this.opened_fd.isStdio()) + this.opened_fd.stdioTag() == null) { if (comptime Environment.isWindows) { - bun.Async.Closer.close(bun.uvfdcast(this.opened_fd), this.loop); + bun.Async.Closer.close(this.opened_fd, this.loop); } else { - _ = bun.sys.close(this.opened_fd); + _ = this.opened_fd.closeAllowingBadFileDescriptor(null); } this.opened_fd = invalid_fd; } @@ -2492,7 +2481,7 @@ pub const Blob = struct { const rc = libuv.uv_fs_read( loop, &this.io_request, - bun.uvfdcast(read_write_loop.source_fd), + read_write_loop.source_fd.uv(), @ptrCast(&read_write_loop.uv_buf), 1, -1, @@ -2539,7 +2528,7 @@ pub const Blob = struct { const rc2 = libuv.uv_fs_write( event_loop.virtual_machine.event_loop_handle.?, &this.io_request, - bun.uvfdcast(destination_fd), + destination_fd.uv(), @ptrCast(&this.read_write_loop.uv_buf), 1, -1, @@ -2590,7 +2579,7 @@ pub const Blob = struct { const rc2 = libuv.uv_fs_write( this.event_loop.virtual_machine.event_loop_handle.?, &this.io_request, - bun.uvfdcast(destination_fd), + destination_fd.uv(), @ptrCast(&this.read_write_loop.uv_buf), 1, -1, @@ -2618,26 +2607,26 @@ pub const Blob = struct { pub fn close(this: *ReadWriteLoop) void { if (this.must_close_source_fd) { - if (bun.toLibUVOwnedFD(this.source_fd)) |fd| { + if (this.source_fd.makeLibUVOwned()) |fd| { bun.Async.Closer.close( - bun.uvfdcast(fd), + fd, bun.Async.Loop.get(), ); } else |_| { - _ = bun.sys.close(this.source_fd); + this.source_fd.close(); } this.must_close_source_fd = false; this.source_fd = invalid_fd; } if (this.must_close_destination_fd) { - if (bun.toLibUVOwnedFD(this.destination_fd)) |fd| { + if (this.destination_fd.makeLibUVOwned()) |fd| { bun.Async.Closer.close( - bun.uvfdcast(fd), + fd, bun.Async.Loop.get(), ); } else |_| { - _ = bun.sys.close(this.destination_fd); + this.destination_fd.close(); } this.must_close_destination_fd = false; this.destination_fd = invalid_fd; @@ -2700,8 +2689,8 @@ pub const Blob = struct { bun.O.WRONLY | bun.O.CREAT, 0, )) { - .result => |result| bun.toLibUVOwnedFD(result) catch { - _ = bun.sys.close(result); + .result => |result| result.makeLibUVOwned() catch { + result.close(); return .{ .err = .{ .errno = @as(c_int, @intCast(@intFromEnum(bun.C.SystemErrno.EMFILE))), @@ -3135,14 +3124,14 @@ pub const Blob = struct { pub fn doCloseFile(this: *CopyFile, comptime which: IOWhich) void { switch (which) { .both => { - _ = bun.sys.close(this.destination_fd); - _ = bun.sys.close(this.source_fd); + this.destination_fd.close(); + this.source_fd.close(); }, .destination => { - _ = bun.sys.close(this.destination_fd); + this.destination_fd.close(); }, .source => { - _ = bun.sys.close(this.source_fd); + this.source_fd.close(); }, } } @@ -3161,7 +3150,7 @@ pub const Blob = struct { open_source_flags, 0, )) { - .result => |result| switch (bun.sys.toLibUVOwnedFD(result, .open, .close_on_fail)) { + .result => |result| switch (result.makeLibUVOwnedForSyscall(.open, .close_on_fail)) { .result => |result_fd| result_fd, .err => |errno| { this.system_error = errno.toSystemError(); @@ -3183,7 +3172,7 @@ pub const Blob = struct { open_destination_flags, JSC.Node.default_permission, )) { - .result => |result| switch (bun.sys.toLibUVOwnedFD(result, .open, .close_on_fail)) { + .result => |result| switch (result.makeLibUVOwnedForSyscall(.open, .close_on_fail)) { .result => |result_fd| result_fd, .err => |errno| { this.system_error = errno.toSystemError(); @@ -3195,8 +3184,8 @@ pub const Blob = struct { .@"continue" => continue, .fail => { if (which == .both) { - _ = bun.sys.close(this.source_fd); - this.source_fd = .zero; + this.source_fd.close(); + this.source_fd = .invalid; } return bun.errnoToZigErr(errno.errno); }, @@ -3204,8 +3193,8 @@ pub const Blob = struct { } if (which == .both) { - _ = bun.sys.close(this.source_fd); - this.source_fd = .zero; + this.source_fd.close(); + this.source_fd = .invalid; } this.system_error = errno.withPath(this.destination_file_store.pathlike.path.slice()).toSystemError(); @@ -4370,10 +4359,10 @@ pub const Blob = struct { } } - break :brk switch (bun.FDTag.get(fd)) { - .stdout, .stderr => true, + break :brk if (fd.stdioTag()) |tag| switch (tag) { + .std_out, .std_err => true, else => false, - }; + } else false; }; var sink = JSC.WebCore.FileSink.init(fd, this.globalThis.bunVM().eventLoop()); sink.writer.owns_fd = pathlike != .fd; @@ -4613,10 +4602,10 @@ pub const Blob = struct { } } - break :brk switch (bun.FDTag.get(fd)) { - .stdout, .stderr => true, + break :brk if (fd.stdioTag()) |tag| switch (tag) { + .std_out, .std_err => true, else => false, - }; + } else false; }; var sink = JSC.WebCore.FileSink.init(fd, this.globalThis.bunVM().eventLoop()); sink.writer.owns_fd = pathlike != .fd; diff --git a/src/bun.js/webcore/blob/ReadFile.zig b/src/bun.js/webcore/blob/ReadFile.zig index 040ff469b4..6d17f00d24 100644 --- a/src/bun.js/webcore/blob/ReadFile.zig +++ b/src/bun.js/webcore/blob/ReadFile.zig @@ -632,7 +632,7 @@ pub const ReadFileUV = struct { this.req.deinit(); this.req.data = this; - if (libuv.uv_fs_fstat(this.loop, &this.req, bun.uvfdcast(opened_fd), &onFileInitialStat).errEnum()) |errno| { + if (libuv.uv_fs_fstat(this.loop, &this.req, opened_fd.uv(), &onFileInitialStat).errEnum()) |errno| { this.errno = bun.errnoToZigErr(errno); this.system_error = bun.sys.Error.fromCode(errno, .fstat).toSystemError(); this.onFinish(); @@ -756,7 +756,7 @@ pub const ReadFileUV = struct { const res = libuv.uv_fs_read( this.loop, &this.req, - bun.uvfdcast(this.opened_fd), + this.opened_fd.uv(), &bufs, bufs.len, @as(i64, @intCast(this.offset + this.read_off)), diff --git a/src/bun.js/webcore/blob/WriteFile.zig b/src/bun.js/webcore/blob/WriteFile.zig index 7c13a9a18e..79e87d4846 100644 --- a/src/bun.js/webcore/blob/WriteFile.zig +++ b/src/bun.js/webcore/blob/WriteFile.zig @@ -411,7 +411,7 @@ pub const WriteFileWindows = struct { } // The file stored descriptor is not stdin, stdout, or stderr. - break :brk bun.uvfdcast(file_blob.store.?.data.file.pathlike.fd); + break :brk file_blob.store.?.data.file.pathlike.fd.uv(); }; write_file.doWriteLoop(write_file.loop()); @@ -623,7 +623,7 @@ pub const WriteFileWindows = struct { pub fn deinit(this: *@This()) void { const fd = this.fd; if (fd > 0 and this.owned_fd) { - bun.Async.Closer.close(fd, this.io_request.loop); + bun.Async.Closer.close(.fromUV(fd), this.io_request.loop); } this.file_blob.store.?.deref(); this.bytes_blob.store.?.deref(); diff --git a/src/bun.js/webcore/fetch.zig b/src/bun.js/webcore/fetch.zig index 28767cd2e0..187d05d6c8 100644 --- a/src/bun.js/webcore/fetch.zig +++ b/src/bun.js/webcore/fetch.zig @@ -178,7 +178,7 @@ pub const FetchTasklet = struct { }, .Sendfile => { if (@max(this.Sendfile.offset, this.Sendfile.remain) > 0) - _ = bun.sys.close(this.Sendfile.fd); + this.Sendfile.fd.close(); this.Sendfile.offset = 0; this.Sendfile.remain = 0; }, @@ -2475,7 +2475,7 @@ pub fn Bun__fetch_( ); if (body.store().?.data.file.pathlike == .path) { - _ = bun.sys.close(opened_fd); + opened_fd.close(); } switch (res) { diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index bf2d171d50..e491f79ab3 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -628,22 +628,18 @@ pub const StreamStart = union(Tag) { }; } - if (bun.FDImpl.fromJS(fd_value)) |fd| { + if (bun.FD.fromJS(fd_value)) |fd| { return .{ .FileSink = .{ .chunk_size = chunk_size, - .input_path = .{ - .fd = fd.encode(), - }, + .input_path = .{ .fd = fd }, }, }; } else { - return .{ - .err = Syscall.Error{ - .errno = @intFromEnum(bun.C.SystemErrno.EBADF), - .syscall = .write, - }, - }; + return .{ .err = Syscall.Error{ + .errno = @intFromEnum(bun.C.SystemErrno.EBADF), + .syscall = .write, + } }; } } @@ -3634,13 +3630,13 @@ pub const FileSink = struct { if (comptime Environment.isPosix) { switch (bun.sys.fstat(fd)) { .err => |err| { - _ = bun.sys.close(fd); + fd.close(); return .{ .err = err }; }, .result => |stat| { this.pollable = bun.sys.isPollable(stat.mode); if (!this.pollable) { - isatty = std.posix.isatty(fd.int()); + isatty = std.posix.isatty(fd.native()); } if (isatty) { @@ -3664,7 +3660,7 @@ pub const FileSink = struct { const flags = switch (bun.sys.getFcntlFlags(fd)) { .result => |flags| flags, .err => |err| { - _ = bun.sys.close(fd); + fd.close(); return .{ .err = err }; }, }; @@ -3694,7 +3690,7 @@ pub const FileSink = struct { this.pollable, )) { .err => |err| { - _ = bun.sys.close(fd); + fd.close(); return .{ .err = err }; }, .result => { @@ -3710,7 +3706,7 @@ pub const FileSink = struct { this.pollable, )) { .err => |err| { - _ = bun.sys.close(fd); + fd.close(); return .{ .err = err }; }, .result => { @@ -3996,10 +3992,9 @@ pub const FileSink = struct { fn getFd(this: *const @This()) i32 { if (Environment.isWindows) { - const fd_impl = this.fd.impl(); - return switch (fd_impl.kind) { - .system => -1, // TODO: - .uv => fd_impl.value.as_uv, + return switch (this.fd.decodeWindows()) { + .windows => -1, // TODO: + .uv => |num| num, }; } return this.fd.cast(); @@ -4094,14 +4089,14 @@ pub const FileReader = struct { var file_buf: bun.PathBuffer = undefined; var is_nonblocking = false; - const fd = if (file.pathlike == .fd) - if (file.pathlike.fd.isStdio()) brk: { + const fd: bun.FD = if (file.pathlike == .fd) + if (file.pathlike.fd.stdioTag() != null) brk: { if (comptime Environment.isPosix) { - const rc = bun.C.open_as_nonblocking_tty(file.pathlike.fd.int(), bun.O.RDONLY); + const rc = bun.C.open_as_nonblocking_tty(file.pathlike.fd.native(), bun.O.RDONLY); if (rc > -1) { is_nonblocking = true; file.is_atty = true; - break :brk bun.toFD(rc); + break :brk .fromNative(rc); } } break :brk file.pathlike.fd; @@ -4112,18 +4107,17 @@ pub const FileReader = struct { return .{ .err = duped.err.withFd(file.pathlike.fd) }; } - const fd = duped.result; - + const fd: bun.FD = duped.result; if (comptime Environment.isPosix) { - if (bun.FDTag.get(fd) == .none) { - is_nonblocking = switch (bun.sys.getFcntlFlags(fd)) { + if (fd.stdioTag() == null) { + is_nonblocking = switch (fd.getFcntlFlags()) { .result => |flags| (flags & bun.O.NONBLOCK) != 0, .err => false, }; } } - break :brk switch (bun.sys.toLibUVOwnedFD(fd, .dup, .close_on_fail)) { + break :brk switch (fd.makeLibUVOwnedForSyscall(.dup, .close_on_fail)) { .result => |owned_fd| owned_fd, .err => |err| { return .{ .err = err }; @@ -4139,9 +4133,9 @@ pub const FileReader = struct { if (comptime Environment.isPosix) { if ((file.is_atty orelse false) or - (fd.int() < 3 and std.posix.isatty(fd.cast())) or + (fd.stdioTag() != null and std.posix.isatty(fd.cast())) or (file.pathlike == .fd and - bun.FDTag.get(file.pathlike.fd) != .none and + file.pathlike.fd.stdioTag() != null and std.posix.isatty(file.pathlike.fd.cast()))) { // var termios = std.mem.zeroes(std.posix.termios); @@ -4154,7 +4148,7 @@ pub const FileReader = struct { const stat: bun.Stat = switch (Syscall.fstat(fd)) { .result => |result| result, .err => |err| { - _ = Syscall.close(fd); + fd.close(); return .{ .err = err }; }, }; diff --git a/src/bun.zig b/src/bun.zig index 1c9f0301b7..4cfe9e1f31 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -147,133 +147,15 @@ pub const shell = @import("./shell/shell.zig"); pub const Output = @import("./output.zig"); pub const Global = @import("./Global.zig"); -// make this non-pub after https://github.com/ziglang/zig/issues/18462 is resolved -pub const FileDescriptorInt = if (Environment.isBrowser) - u0 -else if (Environment.isWindows) - // On windows, this is a bitcast "bun.FDImpl" struct - // Do not bitcast it to *anyopaque manually, but instead use `fdcast()` - u64 -else - std.posix.fd_t; +pub const FD = @import("fd.zig").FD; -pub const FD = FileDescriptor; -pub const FileDescriptor = enum(FileDescriptorInt) { - /// Zero is used in old filesystem code to indicate "no file descriptor" - /// This is problematic because on POSIX, this is ambiguous with stdin being 0. - /// All code that uses this should migrate to invalid_fd to represent invalid states. - zero = 0, - // Represents an invalid file descriptor. This is used instead of null to - // avoid an extra bit. - // invalid = @intFromEnum(invalid_fd), - _, - - /// Do not use this function in new code. - /// - /// Interpreting a FD as an integer is almost certainly a mistake. - /// On Windows, it is always a mistake, as the integer is bitcast of a tagged packed struct. - /// - /// TODO(@paperclover): remove this API. - pub fn int(self: FileDescriptor) std.posix.fd_t { - if (Environment.isWindows) - @compileError("FileDescriptor.int() is not allowed on Windows."); - return @intFromEnum(self); - } - - pub fn writeTo(fd: FileDescriptor, writer: anytype, endian: std.builtin.Endian) !void { - try writer.writeInt(FileDescriptorInt, @intFromEnum(fd), endian); - } - - pub fn readFrom(reader: anytype, endian: std.builtin.Endian) !FileDescriptor { - return @enumFromInt(try reader.readInt(FileDescriptorInt, endian)); - } - - /// converts a `bun.FileDescriptor` into the native operating system fd - /// - /// On non-windows this does nothing, but on windows it converts UV descriptors - /// to Windows' *HANDLE, and casts the types for proper usage. - /// - /// This may be needed in places where a FileDescriptor is given to `std` or `kernel32` apis - pub fn cast(fd: FileDescriptor) std.posix.fd_t { - if (!Environment.isWindows) return fd.int(); - // if not having this check, the cast may crash zig compiler? - if (@inComptime() and fd == invalid_fd) return FDImpl.invalid.system(); - return fd.impl().system(); - } - - pub fn asDir(fd: FileDescriptor) std.fs.Dir { - return std.fs.Dir{ .fd = fd.cast() }; - } - - pub fn asFile(fd: FileDescriptor) std.fs.File { - return std.fs.File{ .handle = fd.cast() }; - } - - pub fn format(fd: FileDescriptor, comptime fmt_: string, options_: std.fmt.FormatOptions, writer: anytype) !void { - try FDImpl.format(fd.impl(), fmt_, options_, writer); - } - - pub fn assertValid(fd: FileDescriptor) void { - fd.impl().assertValid(); - } - - pub fn isValid(fd: FileDescriptor) bool { - return fd.impl().isValid(); - } - - pub fn assertKind(fd: FileDescriptor, kind: FDImpl.Kind) void { - assert(fd.impl().kind == kind); - } - - pub fn cwd() FileDescriptor { - return toFD(std.fs.cwd().fd); - } - - pub fn eq(this: FileDescriptor, that: FileDescriptor) bool { - if (Environment.isPosix) return this.int() == that.int(); - - const this_ = FDImpl.decode(this); - const that_ = FDImpl.decode(that); - return switch (this_.kind) { - .system => switch (that_.kind) { - .system => this_.value.as_system == that_.value.as_system, - .uv => false, - }, - .uv => switch (that_.kind) { - .system => false, - .uv => this_.value.as_uv == that_.value.as_uv, - }, - }; - } - - pub fn isStdio(fd: FileDescriptor) bool { - // fd.assertValid(); - const decoded = fd.impl(); - return switch (Environment.os) { - else => decoded.value.as_system < 3, - .windows => switch (decoded.kind) { - .system => fd == win32.STDIN_FD or - fd == win32.STDOUT_FD or - fd == win32.STDERR_FD, - .uv => decoded.value.as_uv < 3, - }, - }; - } - - pub fn toJS(value: FileDescriptor, global: *JSC.JSGlobalObject) JSC.JSValue { - return FDImpl.decode(value).toJS(global); - } - - pub fn impl(fd: FileDescriptor) FDImpl { - return FDImpl.decode(fd); - } -}; - -pub const FDImpl = @import("./fd.zig").FDImpl; +/// Deprecated: Use `FD` instead. +pub const FileDescriptor = FD; // When we are on a computer with an absurdly high number of max open file handles // such is often the case with macOS // As a useful optimization, we can store file descriptors and just keep them open...forever +/// Deprecated: Rename to use `FD` instead. pub const StoredFileDescriptorType = FileDescriptor; /// Thin wrapper around iovec / libuv buffer @@ -616,7 +498,7 @@ pub fn isWritable(fd: FileDescriptor) PollFlag { if (comptime Environment.isWindows) { var polls = [_]std.os.windows.ws2_32.WSAPOLLFD{ .{ - .fd = socketcast(fd), + .fd = fd.asSocketFd(), .events = std.posix.POLL.WRNORM, .revents = 0, }, @@ -737,9 +619,9 @@ pub fn rangeOfSliceInBuffer(slice: []const u8, buffer: []const u8) ?[2]u32 { return r; } -/// on unix, this == std.math.maxInt(i32) -/// on windows, this is encode(.{ .system, std.math.maxInt(u63) }) -pub const invalid_fd: FileDescriptor = FDImpl.invalid.encode(); +// TODO: prefer .invalid decl literal over this +// Please prefer `bun.FD.Optional.none` over this +pub const invalid_fd: FileDescriptor = .invalid; pub const simdutf = @import("./bun.js/bindings/bun-simdutf.zig"); @@ -780,7 +662,7 @@ pub fn openFile(path_: []const u8, open_flags: std.fs.File.OpenFlags) !std.fs.Fi } const fd = try sys.openA(path_, flags, 0).unwrap(); - return fd.asFile(); + return fd.stdFile(); } return try openFileZ(&try std.posix.toPosixPath(path_), open_flags); @@ -788,37 +670,37 @@ pub fn openFile(path_: []const u8, open_flags: std.fs.File.OpenFlags) !std.fs.Fi pub fn openDir(dir: std.fs.Dir, path_: [:0]const u8) !std.fs.Dir { if (comptime Environment.isWindows) { - const res = try sys.openDirAtWindowsA(toFD(dir.fd), path_, .{ .iterable = true, .can_rename_or_delete = true, .read_only = true }).unwrap(); - return res.asDir(); + const res = try sys.openDirAtWindowsA(.fromStdDir(dir), path_, .{ .iterable = true, .can_rename_or_delete = true, .read_only = true }).unwrap(); + return res.stdDir(); } else { - const fd = try sys.openat(toFD(dir.fd), path_, O.DIRECTORY | O.CLOEXEC | O.RDONLY, 0).unwrap(); - return fd.asDir(); + const fd = try sys.openat(.fromStdDir(dir), path_, O.DIRECTORY | O.CLOEXEC | O.RDONLY, 0).unwrap(); + return fd.stdDir(); } } pub fn openDirNoRenamingOrDeletingWindows(dir: FileDescriptor, path_: [:0]const u8) !std.fs.Dir { if (comptime !Environment.isWindows) @compileError("use openDir!"); const res = try sys.openDirAtWindowsA(dir, path_, .{ .iterable = true, .can_rename_or_delete = false, .read_only = true }).unwrap(); - return res.asDir(); + return res.stdDir(); } pub fn openDirA(dir: std.fs.Dir, path_: []const u8) !std.fs.Dir { if (comptime Environment.isWindows) { - const res = try sys.openDirAtWindowsA(toFD(dir.fd), path_, .{ .iterable = true, .can_rename_or_delete = true, .read_only = true }).unwrap(); - return res.asDir(); + const res = try sys.openDirAtWindowsA(.fromStdDir(dir), path_, .{ .iterable = true, .can_rename_or_delete = true, .read_only = true }).unwrap(); + return res.stdDir(); } else { - const fd = try sys.openatA(toFD(dir.fd), path_, O.DIRECTORY | O.CLOEXEC | O.RDONLY, 0).unwrap(); - return fd.asDir(); + const fd = try sys.openatA(.fromStdDir(dir), path_, O.DIRECTORY | O.CLOEXEC | O.RDONLY, 0).unwrap(); + return fd.stdDir(); } } pub fn openDirForIteration(dir: std.fs.Dir, path_: []const u8) !std.fs.Dir { if (comptime Environment.isWindows) { - const res = try sys.openDirAtWindowsA(toFD(dir.fd), path_, .{ .iterable = true, .can_rename_or_delete = false, .read_only = true }).unwrap(); - return res.asDir(); + const res = try sys.openDirAtWindowsA(.fromStdDir(dir), path_, .{ .iterable = true, .can_rename_or_delete = false, .read_only = true }).unwrap(); + return res.stdDir(); } else { - const fd = try sys.openatA(toFD(dir.fd), path_, O.DIRECTORY | O.CLOEXEC | O.RDONLY, 0).unwrap(); - return fd.asDir(); + const fd = try sys.openatA(.fromStdDir(dir), path_, O.DIRECTORY | O.CLOEXEC | O.RDONLY, 0).unwrap(); + return fd.stdDir(); } } @@ -828,7 +710,7 @@ pub fn openDirAbsolute(path_: []const u8) !std.fs.Dir { else try sys.openA(path_, O.DIRECTORY | O.CLOEXEC | O.RDONLY, 0).unwrap(); - return fd.asDir(); + return fd.stdDir(); } pub fn openDirAbsoluteNotForDeletingOrRenaming(path_: []const u8) !std.fs.Dir { @@ -837,7 +719,7 @@ pub fn openDirAbsoluteNotForDeletingOrRenaming(path_: []const u8) !std.fs.Dir { else try sys.openA(path_, O.DIRECTORY | O.CLOEXEC | O.RDONLY, 0).unwrap(); - return fd.asDir(); + return fd.stdDir(); } pub const MimallocArena = @import("./allocators/mimalloc_arena.zig").Arena; @@ -894,38 +776,6 @@ pub fn getenvTruthy(key: [:0]const u8) bool { return false; } -pub const FDHashMapContext = struct { - pub fn hash(_: @This(), fd: FileDescriptor) u64 { - // a file descriptor is i32 on linux, u64 on windows - // the goal here is to do zero work and widen the 32 bit type to 64 - // this should compile error if FileDescriptor somehow is larger than 64 bits. - comptime assert(@bitSizeOf(FileDescriptor) <= 64); - return @intCast(fd.int()); - } - pub fn eql(_: @This(), a: FileDescriptor, b: FileDescriptor) bool { - return a == b; - } - pub fn pre(input: FileDescriptor) Prehashed { - return Prehashed{ - .value = @This().hash(.{}, input), - .input = input, - }; - } - - pub const Prehashed = struct { - value: u64, - input: FileDescriptor, - pub fn hash(this: @This(), fd: FileDescriptor) u64 { - if (fd == this.input) return this.value; - return fd; - } - - pub fn eql(_: @This(), a: FileDescriptor, b: FileDescriptor) bool { - return a == b; - } - }; -}; - pub const U32HashMapContext = struct { pub fn hash(_: @This(), value: u32) u64 { return @intCast(value); @@ -1108,7 +958,7 @@ pub fn StringHashMapUnmanaged(comptime Type: type) type { } pub fn FDHashMap(comptime Type: type) type { - return std.HashMap(StoredFileDescriptorType, Type, FDHashMapContext, std.hash_map.default_max_load_percentage); + return std.HashMap(FD, Type, FD.HashMapContext, std.hash_map.default_max_load_percentage); } pub fn U32HashMap(comptime Type: type) type { @@ -1335,6 +1185,7 @@ pub const FormData = @import("./url.zig").FormData; var needs_proc_self_workaround: bool = false; +/// TODO: move to bun.sys // This is our "polyfill" when /proc/self/fd is not available it's only // necessary on linux because other platforms don't have an optional // /proc/self/fd @@ -1358,14 +1209,13 @@ pub fn getcwdAlloc(allocator: std.mem.Allocator) ![:0]u8 { return allocator.dupeZ(u8, temp_slice); } +/// TODO: move to bun.sys and add a method onto FileDescriptor /// Get the absolute path to a file descriptor. /// On Linux, when `/proc/self/fd` is not available, this function will attempt to use `fchdir` and `getcwd` to get the path instead. -pub fn getFdPath(fd_: anytype, buf: *bun.PathBuffer) ![]u8 { - const fd = toFD(fd_).cast(); - +pub fn getFdPath(fd: FileDescriptor, buf: *bun.PathBuffer) ![]u8 { if (comptime Environment.isWindows) { var wide_buf: WPathBuffer = undefined; - const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]); + const wide_slice = try windows.GetFinalPathNameByHandle(fd.native(), .{}, wide_buf[0..]); const res = strings.copyUTF16IntoUTF8(buf[0..], @TypeOf(wide_slice), wide_slice, true); return buf[0..res.written]; } @@ -1382,36 +1232,34 @@ pub fn getFdPath(fd_: anytype, buf: *bun.PathBuffer) ![]u8 { needs_proc_self_workaround = strings.eql(getenvZ("BUN_NEEDS_PROC_SELF_WORKAROUND") orelse "0", "1"); } } else if (comptime !Environment.isLinux) { - return try std.os.getFdPath(fd, buf); + return try std.os.getFdPath(fd.native(), buf); } if (needs_proc_self_workaround) { - return getFdPathViaCWD(fd, buf); + return getFdPathViaCWD(fd.native(), buf); } - return std.os.getFdPath(fd, buf) catch |err| { + return std.os.getFdPath(fd.native(), buf) catch |err| { if (err == error.FileNotFound and !needs_proc_self_workaround) { needs_proc_self_workaround = true; - return getFdPathViaCWD(fd, buf); + return getFdPathViaCWD(fd.native(), buf); } return err; }; } -pub fn getFdPathZ(fd_: anytype, buf: *PathBuffer) ![:0]u8 { - const path_ = try getFdPath(fd_, buf); - buf[path_.len] = 0; - return buf[0..path_.len :0]; +/// TODO: move to bun.sys and add a method onto FileDescriptor +pub fn getFdPathZ(fd: FileDescriptor, buf: *PathBuffer) ![:0]u8 { + const fd_path = try getFdPath(fd, buf); + buf[fd_path.len] = 0; + return buf[0..fd_path.len :0]; } -pub fn getFdPathW(fd_: anytype, buf: *WPathBuffer) ![]u16 { - const fd = toFD(fd_).cast(); - +/// TODO: move to bun.sys and add a method onto FileDescriptor +pub fn getFdPathW(fd: FileDescriptor, buf: *WPathBuffer) ![]u16 { if (comptime Environment.isWindows) { - const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, buf); - - return wide_slice; + return try windows.GetFinalPathNameByHandle(fd.native(), .{}, buf); } @panic("TODO unsupported platform for getFdPathW"); @@ -1744,9 +1592,9 @@ pub fn reloadProcess( // macOS doesn't have CLOEXEC, so we must go through posix_spawn if (comptime Environment.isMac) { var actions = PosixSpawn.Actions.init() catch unreachable; - actions.inherit(posix.STDIN_FD) catch unreachable; - actions.inherit(posix.STDOUT_FD) catch unreachable; - actions.inherit(posix.STDERR_FD) catch unreachable; + actions.inherit(.stdin()) catch unreachable; + actions.inherit(.stdout()) catch unreachable; + actions.inherit(.stderr()) catch unreachable; var attrs = PosixSpawn.Attr.init() catch unreachable; attrs.resetSignals() catch {}; @@ -2039,110 +1887,6 @@ pub inline fn todo(src: std.builtin.SourceLocation, value: anytype) @TypeOf(valu return value; } -/// Converts a native file descriptor into a `bun.FileDescriptor` -/// -/// Accepts either a UV descriptor (i32) or a windows handle (*anyopaque) -pub fn toFD(fd: anytype) callconv(callconv_inline) FileDescriptor { - const T = @TypeOf(fd); - if (Environment.isWindows) { - return (switch (T) { - FDImpl => fd, // TODO: remove the toFD call from these places and make this a @compileError - FDImpl.System => FDImpl.fromSystem(fd), - FDImpl.UV, i32, comptime_int => FDImpl.fromUV(fd), - FileDescriptor => FDImpl.decode(fd), - std.fs.Dir => FDImpl.fromSystem(fd.fd), - sys.File, std.fs.File => FDImpl.fromSystem(fd.handle), - // TODO: remove u32 - u32 => FDImpl.fromUV(@intCast(fd)), - else => @compileError("toFD() does not support type \"" ++ @typeName(T) ++ "\""), - }).encode(); - } else { - // TODO: remove intCast. we should not be casting u32 -> i32 - // even though file descriptors are always positive, linux/mac represents them as signed integers - return switch (T) { - FileDescriptor => fd, // TODO: remove the toFD call from these places and make this a @compileError - sys.File => fd.handle, - std.fs.File => @enumFromInt(fd.handle), - std.fs.Dir => @enumFromInt(@as(i32, @intCast(fd.fd))), - c_int, i32, u32, comptime_int => @enumFromInt(fd), - else => @compileError("bun.toFD() not implemented for: " ++ @typeName(T)), - }; - } -} - -/// Converts a native file descriptor into a `bun.FileDescriptor` -/// -/// Accepts either a UV descriptor (i32) or a windows handle (*anyopaque) -/// -/// On windows, this file descriptor will always be backed by libuv, so calling .close() is safe. -pub inline fn toLibUVOwnedFD(fd: anytype) !FileDescriptor { - const T = @TypeOf(fd); - if (Environment.isWindows) { - return (switch (T) { - FDImpl.System => try FDImpl.fromSystem(fd).makeLibUVOwned(), - FDImpl.UV => FDImpl.fromUV(fd), - FileDescriptor => try FDImpl.decode(fd).makeLibUVOwned(), - FDImpl => fd.makeLibUVOwned(), - else => @compileError("toLibUVOwnedFD() does not support type \"" ++ @typeName(T) ++ "\""), - }).encode(); - } else { - return toFD(fd); - } -} - -/// Converts FileDescriptor into a UV file descriptor. -/// -/// This explicitly is setup to disallow converting a Windows descriptor into a UV -/// descriptor. If this was allowed, then it would imply the caller still owns the -/// windows handle, but Win->UV will always invalidate the handle. -/// -/// In that situation, it is almost impossible to close the handle properly, -/// you want to use `bun.FDImpl.decode(fd)` or `bun.toLibUVOwnedFD` instead. -/// -/// This way, you can call .close() on the libuv descriptor. -pub inline fn uvfdcast(fd: anytype) FDImpl.UV { - const T = @TypeOf(fd); - if (Environment.isWindows) { - const decoded = (switch (T) { - FDImpl.System => @compileError("This cast (FDImpl.System -> FDImpl.UV) makes this file descriptor very hard to close. Use toLibUVOwnedFD() and FileDescriptor instead. If you truly need to do this conversion (Chloe will probably reject your PR), use bun.FDImpl.fromSystem(fd).uv()"), - FDImpl => fd, - FDImpl.UV => return fd, - FileDescriptor => FDImpl.decode(fd), - else => @compileError("uvfdcast() does not support type \"" ++ @typeName(T) ++ "\""), - }); - - // Specifically allow these anywhere: - if (fd == win32.STDIN_FD) { - return 0; - } - - if (fd == win32.STDOUT_FD) { - return 1; - } - - if (fd == win32.STDERR_FD) { - return 2; - } - - if (Environment.allow_assert) { - if (decoded.kind != .uv) { - std.debug.panic("uvfdcast({}) called on an windows handle", .{decoded}); - } - } - return decoded.uv(); - } else { - return fd.cast(); - } -} - -pub inline fn socketcast(fd: anytype) std.posix.socket_t { - if (Environment.isWindows) { - return @ptrCast(FDImpl.decode(fd).system()); - } else { - return fd.cast(); - } -} - pub const HOST_NAME_MAX = if (Environment.isWindows) // On Windows the maximum length, in bytes, of the string returned in the buffer pointed to by the name parameter is dependent on the namespace provider, but this string must be 256 bytes or less. // So if a buffer of 256 bytes is passed in the name parameter and the namelen parameter is set to 256, the buffer size will always be adequate. @@ -2256,31 +2000,15 @@ pub fn initArgv(allocator: std.mem.Allocator) !void { } pub const posix = struct { - pub const STDIN_FD = toFD(0); - pub const STDOUT_FD = toFD(1); - pub const STDERR_FD = toFD(2); - - pub fn stdio(i: anytype) FileDescriptor { - return switch (i) { - 1 => STDOUT_FD, - 2 => STDERR_FD, - 0 => STDIN_FD, - else => @panic("Invalid stdio fd"), - }; - } - pub const spawn = @import("./bun.js/api/bun/spawn.zig").PosixSpawn; }; pub const win32 = struct { const w = std.os.windows; - pub var STDOUT_FD: FileDescriptor = undefined; - pub var STDERR_FD: FileDescriptor = undefined; - pub var STDIN_FD: FileDescriptor = undefined; /// Returns the original mode, or null on failure - pub fn updateStdioModeFlags(i: anytype, opts: struct { set: w.DWORD = 0, unset: w.DWORD = 0 }) !w.DWORD { - const fd = stdio(i); + pub fn updateStdioModeFlags(i: bun.FD.Stdio, opts: struct { set: w.DWORD = 0, unset: w.DWORD = 0 }) !w.DWORD { + const fd = i.fd(); var original_mode: w.DWORD = 0; if (windows.GetConsoleMode(fd.cast(), &original_mode) != 0) { if (windows.SetConsoleMode(fd.cast(), (original_mode | opts.set) & ~opts.unset) == 0) { @@ -2295,15 +2023,6 @@ pub const win32 = struct { // this was randomly generated - we need to avoid using a common exit code that might be used by the script itself const watcher_reload_exit: w.DWORD = 3224497970; - pub fn stdio(i: anytype) FileDescriptor { - return switch (i) { - 0 => STDIN_FD, - 1 => STDOUT_FD, - 2 => STDERR_FD, - else => @panic("Invalid stdio fd"), - }; - } - pub const spawn = @import("./bun.js/api/bun/spawn.zig").PosixSpawn; pub fn isWatcherChild() bool { @@ -2487,56 +2206,6 @@ pub const Mode = C.Mode; pub const windows = @import("./windows.zig"); -pub const FDTag = enum { - none, - stderr, - stdin, - stdout, - pub fn get(fd_: anytype) FDTag { - const fd = toFD(fd_); - const T = @TypeOf(fd_); - if (comptime Environment.isWindows) { - if (@typeInfo(T) == .int or @typeInfo(T) == .comptime_int) { - switch (fd_) { - 0 => return .stdin, - 1 => return .stdout, - 2 => return .stderr, - else => {}, - } - } - - if (fd == win32.STDOUT_FD) { - return .stdout; - } else if (fd == win32.STDERR_FD) { - return .stderr; - } else if (fd == win32.STDIN_FD) { - return .stdin; - } - - return .none; - } else { - return switch (fd) { - posix.STDIN_FD => FDTag.stdin, - posix.STDOUT_FD => FDTag.stdout, - posix.STDERR_FD => FDTag.stderr, - else => .none, - }; - } - } -}; - -pub fn fdi32(fd_: anytype) i32 { - if (comptime Environment.isPosix) { - return @intCast(toFD(fd_)); - } - - if (comptime @TypeOf(fd_) == *anyopaque) { - return @intCast(@intFromPtr(fd_)); - } - - return @intCast(fd_); -} - pub const LazyBoolValue = enum { unknown, no, diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index 91cd87aa8a..4e67fa70e6 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -3434,7 +3434,7 @@ pub const BundleV2 = struct { // To minimize contention, watchers are appended by the transpiler thread. if (this.bun_watcher) |watcher| { - if (parse_result.watcher_data.fd != bun.invalid_fd and parse_result.watcher_data.fd != .zero) { + if (parse_result.watcher_data.fd != bun.invalid_fd) { const source = switch (parse_result.value) { inline .empty, .err => |data| graph.input_files.items(.source)[data.source_index.get()], .success => |val| val.source, @@ -4590,10 +4590,7 @@ pub const ParseTask = struct { file_path.text, task.contents_or_fd.fd.dir, false, - if (contents.file != bun.invalid_fd and contents.file != .zero) - contents.file - else - null, + contents.file.unwrapValid(), ) catch |err| { const source = &Logger.Source.initEmptyFile(log.msgs.allocator.dupe(u8, file_path.text) catch unreachable); switch (err) { @@ -5051,7 +5048,8 @@ pub const ParseTask = struct { } const will_close_file_descriptor = task.contents_or_fd == .fd and - entry.fd.isValid() and !entry.fd.isStdio() and + entry.fd.isValid() and + entry.fd.stdioTag() == null and this.ctx.bun_watcher == null; if (will_close_file_descriptor) { _ = entry.closeFD(); @@ -7015,7 +7013,7 @@ pub const LinkerContext = struct { }; defer dir.close(); - break :dir try bun.getFdPath(bun.toFD(dir.fd), &real_path_buf); + break :dir try bun.FD.fromStdDir(dir).getFdPath(&real_path_buf); }; chunk.template.placeholder.dir = try resolve_path.relativeAlloc(this.allocator, this.resolver.opts.root_dir, dir); @@ -15268,7 +15266,7 @@ pub const LinkerContext = struct { }, }, .encoding = .buffer, - .dirfd = bun.toFD(root_dir.fd), + .dirfd = .fromStdDir(root_dir), .file = .{ .path = JSC.Node.PathLike{ .string = JSC.PathString.init(source_map_final_rel_path), @@ -15360,7 +15358,7 @@ pub const LinkerContext = struct { .encoding = .buffer, .mode = if (chunk.is_executable) 0o755 else 0o644, - .dirfd = bun.toFD(root_dir.fd), + .dirfd = .fromStdDir(root_dir), .file = .{ .path = JSC.Node.PathLike{ .string = JSC.PathString.init(fdpath[0 .. chunk.final_rel_path.len + bun.bytecode_extension.len]), @@ -15417,7 +15415,7 @@ pub const LinkerContext = struct { .encoding = .buffer, .mode = if (chunk.is_executable) 0o755 else 0o644, - .dirfd = bun.toFD(root_dir.fd), + .dirfd = .fromStdDir(root_dir), .file = .{ .path = JSC.Node.PathLike{ .string = JSC.PathString.init(rel_path), @@ -15534,7 +15532,7 @@ pub const LinkerContext = struct { }, }, .encoding = .buffer, - .dirfd = bun.toFD(root_dir.fd), + .dirfd = .fromStdDir(root_dir), .file = .{ .path = JSC.Node.PathLike{ .string = JSC.PathString.init(src.dest_path), diff --git a/src/c.zig b/src/c.zig index e05bddf14e..96d5e0bdc5 100644 --- a/src/c.zig +++ b/src/c.zig @@ -161,8 +161,8 @@ pub fn moveFileZSlowMaybe(from_dir: bun.FileDescriptor, filename: [:0]const u8, .result => |f| f, .err => |e| return .{ .err = e }, }; - defer _ = bun.sys.close(in_handle); - _ = bun.sys.unlinkat(from_dir, filename); + defer in_handle.close(); + _ = from_dir.unlinkat(filename); return copyFileZSlowWithHandle(in_handle, to_dir, destination); } @@ -202,13 +202,13 @@ pub fn copyFileZSlowWithHandle(in_handle: bun.FileDescriptor, to_dir: bun.FileDe .result => |fd| fd, .err => |e| return .{ .err = e }, }; - defer _ = bun.sys.close(out_handle); + defer out_handle.close(); if (comptime Environment.isLinux) { _ = std.os.linux.fallocate(out_handle.cast(), 0, 0, @intCast(stat_.size)); } - switch (bun.copyFile(in_handle.cast(), out_handle.cast())) { + switch (bun.copyFile(in_handle, out_handle)) { .err => |e| return .{ .err = e }, .result => {}, } diff --git a/src/cache.zig b/src/cache.zig index a56fc9d863..ee236c0d3e 100644 --- a/src/cache.zig +++ b/src/cache.zig @@ -73,11 +73,9 @@ pub const Fs = struct { } pub fn closeFD(entry: *Entry) ?bun.sys.Error { - if (entry.fd != bun.invalid_fd) { - defer { - entry.fd = bun.invalid_fd; - } - return bun.sys.close(entry.fd); + if (entry.fd.isValid()) { + defer entry.fd = .invalid; + return entry.fd.closeAllowingBadFileDescriptor(@returnAddress()); } return null; } @@ -183,10 +181,10 @@ pub const Fs = struct { ) !Entry { var rfs = _fs.fs; - var file_handle: std.fs.File = if (_file_handle) |__file| __file.asFile() else undefined; + var file_handle: std.fs.File = if (_file_handle) |__file| __file.stdFile() else undefined; if (_file_handle == null) { - if (FeatureFlags.store_file_descriptors and dirname_fd != bun.invalid_fd and dirname_fd != .zero) { + if (FeatureFlags.store_file_descriptors and dirname_fd.isValid()) { file_handle = (bun.sys.openatA(dirname_fd, std.fs.path.basename(path), bun.O.RDONLY, 0).unwrap() catch |err| brk: { switch (err) { error.ENOENT => { @@ -195,11 +193,11 @@ pub const Fs = struct { "Internal error: directory mismatch for directory \"{s}\", fd {}. You don't need to do anything, but this indicates a bug.", .{ path, dirname_fd }, ); - break :brk bun.toFD(handle.handle); + break :brk bun.FD.fromStdFile(handle); }, else => return err, } - }).asFile(); + }).stdFile(); } else { file_handle = try bun.openFile(path, .{ .mode = .read_only }); } @@ -208,7 +206,7 @@ pub const Fs = struct { } if (comptime !Environment.isWindows) // skip on Windows because NTCreateFile will do it. - debug("openat({}, {s}) = {}", .{ dirname_fd, path, bun.toFD(file_handle.handle) }); + debug("openat({}, {s}) = {}", .{ dirname_fd, path, bun.FD.fromStdFile(file_handle) }); const will_close = rfs.needToCloseFiles() and _file_handle == null; defer { @@ -235,7 +233,7 @@ pub const Fs = struct { return Entry{ .contents = file.contents, - .fd = if (FeatureFlags.store_file_descriptors and !will_close) bun.toFD(file_handle.handle) else bun.invalid_fd, + .fd = if (FeatureFlags.store_file_descriptors and !will_close) .fromStdFile(file_handle) else bun.invalid_fd, }; } }; diff --git a/src/cli.zig b/src/cli.zig index 1953fc2ae8..51a8162fb0 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -335,7 +335,7 @@ pub const Arguments = struct { pub fn loadConfigPath(allocator: std.mem.Allocator, auto_loaded: bool, config_path: [:0]const u8, ctx: Command.Context, comptime cmd: Command.Tag) !void { var config_file = switch (bun.sys.openA(config_path, bun.O.RDONLY, 0)) { - .result => |fd| fd.asFile(), + .result => |fd| fd.stdFile(), .err => |err| { if (auto_loaded) return; Output.prettyErrorln("{}\nwhile opening config \"{s}\"", .{ diff --git a/src/cli/build_command.zig b/src/cli/build_command.zig index e281ffbd53..0edc2013da 100644 --- a/src/cli/build_command.zig +++ b/src/cli/build_command.zig @@ -205,13 +205,13 @@ pub const BuildCommand = struct { break :brk2 resolve_path.getIfExistsLongestCommonPath(this_transpiler.options.entry_points) orelse "."; }; - var dir = bun.openDirForPath(&(try std.posix.toPosixPath(path))) catch |err| { + var dir = bun.FD.fromStdDir(bun.openDirForPath(&(try std.posix.toPosixPath(path))) catch |err| { Output.prettyErrorln("{s} opening root directory {}", .{ @errorName(err), bun.fmt.quote(path) }); Global.exit(1); - }; + }); defer dir.close(); - break :brk1 bun.getFdPath(bun.toFD(dir.fd), &src_root_dir_buf) catch |err| { + break :brk1 dir.getFdPath(&src_root_dir_buf) catch |err| { Output.prettyErrorln("{s} resolving root directory {}", .{ @errorName(err), bun.fmt.quote(path) }); Global.exit(1); }; diff --git a/src/cli/bunx_command.zig b/src/cli/bunx_command.zig index e5210fb97d..f813655499 100644 --- a/src/cli/bunx_command.zig +++ b/src/cli/bunx_command.zig @@ -197,7 +197,7 @@ pub const BunxCommand = struct { if (dirs.expr.asProperty("bin")) |bin_prop| { if (bin_prop.expr.asString(transpiler.allocator)) |dir_name| { const bin_dir = try bun.sys.openatA(dir_fd, dir_name, bun.O.RDONLY | bun.O.DIRECTORY, 0).unwrap(); - defer _ = bun.sys.close(bin_dir); + defer bin_dir.close(); const dir = std.fs.Dir{ .fd = bin_dir.cast() }; var iterator = bun.DirIterator.iterate(dir, .u8); var entry = iterator.next(); @@ -277,7 +277,7 @@ pub const BunxCommand = struct { /// Check the enclosing package.json for a matching "bin" /// If not found, check bunx cache dir fn getBinName(transpiler: *bun.Transpiler, toplevel_fd: bun.FileDescriptor, tempdir_name: []const u8, package_name: []const u8) error{ NoBinFound, NeedToInstall }![]const u8 { - toplevel_fd.assertValid(); + bun.assert(toplevel_fd.isValid()); return getBinNameFromProjectDirectory(transpiler, toplevel_fd, package_name) catch |err| { if (err == error.NoBinFound) { return error.NoBinFound; @@ -548,7 +548,7 @@ pub const BunxCommand = struct { // and that error message is likely going to be better than the one from `bun add` break :is_stale false; }; - defer _ = bun.sys.close(fd); + defer fd.close(); var io_status_block: std.os.windows.IO_STATUS_BLOCK = undefined; var info: std.os.windows.FILE_BASIC_INFORMATION = undefined; @@ -599,7 +599,7 @@ pub const BunxCommand = struct { // 2. The "bin" is possibly not the same as the package name, so we load the package.json to figure out what "bin" to use const root_dir_fd = root_dir_info.getFileDescriptor(); - bun.assert(root_dir_fd != .zero); + bun.assert(root_dir_fd.isValid()); if (getBinName(&this_transpiler, root_dir_fd, bunx_cache_dir, initial_bin_name)) |package_name_for_bin| { // if we check the bin name and its actually the same, we don't need to check $PATH here again if (!strings.eqlLong(package_name_for_bin, initial_bin_name, true)) { diff --git a/src/cli/create_command.zig b/src/cli/create_command.zig index eb1b9a3016..1bd903dfd9 100644 --- a/src/cli/create_command.zig +++ b/src/cli/create_command.zig @@ -444,12 +444,12 @@ pub const CreateCommand = struct { if (!create_options.skip_package_json) { const plucker = pluckers[0]; - if (plucker.found and plucker.fd != .zero) { + if (plucker.found and plucker.fd.isValid()) { node.name = "Updating package.json"; progress.refresh(); package_json_contents = plucker.contents; - package_json_file = plucker.fd.asFile(); + package_json_file = plucker.fd.stdFile(); } } }, @@ -557,7 +557,7 @@ pub const CreateCommand = struct { } if (entry.kind != .file) continue; - var outfile = destination_dir_.createFile(entry.path, .{}) catch brk: { + var outfile = bun.FD.fromStdFile(destination_dir_.createFile(entry.path, .{}) catch brk: { if (bun.Dirname.dirname(bun.OSPathChar, entry.path)) |entry_dirname| { bun.MakePath.makePath(bun.OSPathChar, destination_dir_, entry_dirname) catch {}; } @@ -567,22 +567,22 @@ pub const CreateCommand = struct { Output.err(err, "failed to copy file {}", .{bun.fmt.fmtOSPath(entry.path, .{})}); Global.crash(); }; - }; + }); defer outfile.close(); defer node_.completeOne(); - var infile = try entry.dir.openFile(entry.basename, .{ .mode = .read_only }); + const infile = bun.FD.fromStdFile(try entry.dir.openFile(entry.basename, .{ .mode = .read_only })); defer infile.close(); // Assumption: you only really care about making sure something that was executable is still executable - switch (bun.sys.fstat(bun.toFD(infile.handle))) { + switch (infile.stat()) { .err => {}, .result => |stat| { - _ = bun.sys.fchmod(bun.toFD(outfile.handle), @intCast(stat.mode)); + _ = outfile.chmod(@intCast(stat.mode)); }, } - CopyFile.copyFile(infile.handle, outfile.handle).unwrap() catch |err| { + CopyFile.copyFile(infile, outfile).unwrap() catch |err| { node_.end(); progress_.refresh(); Output.err(err, "failed to copy file {}", .{bun.fmt.fmtOSPath(entry.path, .{})}); @@ -1429,7 +1429,7 @@ pub const CreateCommand = struct { package_json_expr.data.e_object.properties = js_ast.G.Property.List.init(package_json_expr.data.e_object.properties.ptr[0..property_i]); } - const file = package_json_file.?; + const file: bun.FD = .fromStdFile(package_json_file.?); var buffer_writer = JSPrinter.BufferWriter.init(bun.default_allocator); buffer_writer.append_newline = true; @@ -1446,14 +1446,13 @@ pub const CreateCommand = struct { package_json_file = null; break :process_package_json; }; - const fd = bun.toFD(file); const written = package_json_writer.ctx.getWritten(); - bun.sys.File.writeAll(.{ .handle = fd }, written).unwrap() catch |err| { + bun.sys.File.writeAll(.{ .handle = file }, written).unwrap() catch |err| { Output.prettyErrorln("package.json failed to write due to error {s}", .{@errorName(err)}); package_json_file = null; break :process_package_json; }; - bun.sys.ftruncate(fd, @intCast(written.len)).unwrap() catch |err| { + file.truncate(@intCast(written.len)).unwrap() catch |err| { Output.prettyErrorln("package.json failed to write due to error {s}", .{@errorName(err)}); package_json_file = null; break :process_package_json; @@ -1741,7 +1740,7 @@ pub const CreateCommand = struct { const outdir_path_ = home_dir_buf[0..outdir_path.len :0]; if (bun.path.hasAnyIllegalChars(outdir_path_)) break :outer; - if (bun.sys.existsAtType(bun.toFD(std.fs.cwd()), outdir_path_).asValue()) |exists_at_type| { + if (bun.FD.cwd().existsAtType(outdir_path_).asValue()) |exists_at_type| { if (exists_at_type == .file) { const extension = std.fs.path.extension(positional); if (Example.Tag.fromFileExtension(extension)) |tag| { @@ -1765,7 +1764,7 @@ pub const CreateCommand = struct { home_dir_buf[outdir_path.len] = 0; const outdir_path_ = home_dir_buf[0..outdir_path.len :0]; if (bun.path.hasAnyIllegalChars(outdir_path_)) break :outer; - if (bun.sys.directoryExistsAt(bun.toFD(std.fs.cwd()), outdir_path_).isTrue()) { + if (bun.FD.cwd().directoryExistsAt(outdir_path_).isTrue()) { example_tag = Example.Tag.local_folder; break :brk outdir_path; } @@ -1778,7 +1777,7 @@ pub const CreateCommand = struct { home_dir_buf[outdir_path.len] = 0; const outdir_path_ = home_dir_buf[0..outdir_path.len :0]; if (bun.path.hasAnyIllegalChars(outdir_path_)) break :outer; - if (bun.sys.directoryExistsAt(bun.toFD(std.fs.cwd()), outdir_path_).isTrue()) { + if (bun.FD.cwd().directoryExistsAt(outdir_path_).isTrue()) { example_tag = Example.Tag.local_folder; break :brk outdir_path; } @@ -1791,7 +1790,7 @@ pub const CreateCommand = struct { home_dir_buf[outdir_path.len] = 0; const outdir_path_ = home_dir_buf[0..outdir_path.len :0]; if (bun.path.hasAnyIllegalChars(outdir_path_)) break :outer; - if (bun.sys.directoryExistsAt(bun.toFD(std.fs.cwd()), outdir_path_).isTrue()) { + if (bun.FD.cwd().directoryExistsAt(outdir_path_).isTrue()) { example_tag = Example.Tag.local_folder; break :brk outdir_path; } @@ -1911,26 +1910,26 @@ pub const Example = struct { var examples = std.ArrayList(Example).fromOwnedSlice(ctx.allocator, remote_examples); { var folders = [3]std.fs.Dir{ - bun.invalid_fd.asDir(), - bun.invalid_fd.asDir(), - bun.invalid_fd.asDir(), + bun.invalid_fd.stdDir(), + bun.invalid_fd.stdDir(), + bun.invalid_fd.stdDir(), }; if (env_loader.map.get("BUN_CREATE_DIR")) |home_dir| { var parts = [_]string{home_dir}; const outdir_path = filesystem.absBuf(&parts, &home_dir_buf); - folders[0] = std.fs.cwd().openDir(outdir_path, .{}) catch bun.invalid_fd.asDir(); + folders[0] = std.fs.cwd().openDir(outdir_path, .{}) catch bun.invalid_fd.stdDir(); } { var parts = [_]string{ filesystem.top_level_dir, BUN_CREATE_DIR }; const outdir_path = filesystem.absBuf(&parts, &home_dir_buf); - folders[1] = std.fs.cwd().openDir(outdir_path, .{}) catch bun.invalid_fd.asDir(); + folders[1] = std.fs.cwd().openDir(outdir_path, .{}) catch bun.invalid_fd.stdDir(); } if (env_loader.map.get(bun.DotEnv.home_env)) |home_dir| { var parts = [_]string{ home_dir, BUN_CREATE_DIR }; const outdir_path = filesystem.absBuf(&parts, &home_dir_buf); - folders[2] = std.fs.cwd().openDir(outdir_path, .{}) catch bun.invalid_fd.asDir(); + folders[2] = std.fs.cwd().openDir(outdir_path, .{}) catch bun.invalid_fd.stdDir(); } // subfolders with package.json diff --git a/src/cli/filter_run.zig b/src/cli/filter_run.zig index 6fc5c53f0b..4cf555481c 100644 --- a/src/cli/filter_run.zig +++ b/src/cli/filter_run.zig @@ -430,7 +430,7 @@ const AbortHandler = struct { }; fn windowsIsTerminal() bool { - const res = bun.windows.GetFileType(bun.STDOUT_FD.cast()); + const res = bun.windows.GetFileType(bun.FD.stdout().native()); return res == bun.windows.FILE_TYPE_CHAR; } @@ -470,7 +470,7 @@ pub fn runScriptsWithFilter(ctx: Command.Context) !noreturn { const dirpath = std.fs.path.dirname(package_json_path) orelse Global.crash(); const path = bun.strings.withoutTrailingSlash(dirpath); - const pkgjson = bun.PackageJSON.parse(&this_transpiler.resolver, dirpath, .zero, null, .include_scripts, .main) orelse { + const pkgjson = bun.PackageJSON.parse(&this_transpiler.resolver, dirpath, .invalid, null, .include_scripts, .main) orelse { Output.warn("Failed to read package.json\n", .{}); continue; }; diff --git a/src/cli/init_command.zig b/src/cli/init_command.zig index c0e5263eff..ca5c5923d1 100644 --- a/src/cli/init_command.zig +++ b/src/cli/init_command.zig @@ -41,11 +41,11 @@ pub const InitCommand = struct { // unset `ENABLE_VIRTUAL_TERMINAL_INPUT` on windows. This prevents backspace from // deleting the entire line const original_mode: if (Environment.isWindows) ?bun.windows.DWORD else void = if (comptime Environment.isWindows) - bun.win32.updateStdioModeFlags(0, .{ .unset = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT }) catch null; + bun.win32.updateStdioModeFlags(.std_in, .{ .unset = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT }) catch null; defer if (comptime Environment.isWindows) { if (original_mode) |mode| { - _ = bun.windows.SetConsoleMode(bun.win32.STDIN_FD.cast(), mode); + _ = bun.windows.SetConsoleMode(bun.FD.stdin().native(), mode); } }; @@ -206,7 +206,7 @@ pub const InitCommand = struct { // Set raw mode to read single characters without echo const original_mode: if (Environment.isWindows) ?bun.windows.DWORD else void = if (comptime Environment.isWindows) - bun.win32.updateStdioModeFlags(0, .{ + bun.win32.updateStdioModeFlags(.std_in, .{ // virtual terminal input enables arrow keys, processed input lets ctrl+c kill the program .set = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT | bun.windows.ENABLE_PROCESSED_INPUT, // disabling line input sends keys immediately, disabling echo input makes sure it doesn't print to the terminal @@ -220,7 +220,7 @@ pub const InitCommand = struct { if (comptime Environment.isWindows) { if (original_mode) |mode| { _ = bun.windows.SetConsoleMode( - bun.win32.STDIN_FD.cast(), + bun.FD.stdin().native(), mode, ); } @@ -757,8 +757,8 @@ pub const InitCommand = struct { } write_package_json: { - var file = package_json_file orelse try std.fs.cwd().createFileZ("package.json", .{}); - defer file.close(); + var fd = bun.FD.fromStdFile(package_json_file orelse try std.fs.cwd().createFileZ("package.json", .{})); + defer fd.close(); var buffer_writer = JSPrinter.BufferWriter.init(bun.default_allocator); buffer_writer.append_newline = true; var package_json_writer = JSPrinter.BufferPrinter.init(buffer_writer); @@ -774,7 +774,6 @@ pub const InitCommand = struct { package_json_file = null; break :write_package_json; }; - const fd = bun.toFD(file); const written = package_json_writer.ctx.getWritten(); bun.sys.File.writeAll(.{ .handle = fd }, written).unwrap() catch |err| { Output.prettyErrorln("package.json failed to write due to error {s}", .{@errorName(err)}); diff --git a/src/cli/install_completions_command.zig b/src/cli/install_completions_command.zig index 28109a6131..877ec3a189 100644 --- a/src/cli/install_completions_command.zig +++ b/src/cli/install_completions_command.zig @@ -467,7 +467,7 @@ pub const InstallCompletionsCommand = struct { // Check if they need to load the zsh completions file into their .zshrc if (shell == .zsh) { var completions_absolute_path_buf: bun.PathBuffer = undefined; - const completions_path = bun.getFdPath(output_file.handle, &completions_absolute_path_buf) catch unreachable; + const completions_path = bun.getFdPath(.fromStdFile(output_file), &completions_absolute_path_buf) catch unreachable; var zshrc_filepath: bun.PathBuffer = undefined; const needs_to_tell_them_to_add_completions_file = brk: { var dot_zshrc: std.fs.File = zshrc: { diff --git a/src/cli/pack_command.zig b/src/cli/pack_command.zig index 63e114b4fd..cdadfcd707 100644 --- a/src/cli/pack_command.zig +++ b/src/cli/pack_command.zig @@ -1623,17 +1623,17 @@ pub const PackCommand = struct { while (pack_queue.removeOrNull()) |pathname| { defer if (log_level.showProgress()) node.completeOne(); - const file = bun.sys.openat(bun.toFD(root_dir.fd), pathname, bun.O.RDONLY, 0).unwrap() catch |err| { + const file = bun.sys.openat(.fromStdDir(root_dir), pathname, bun.O.RDONLY, 0).unwrap() catch |err| { Output.err(err, "failed to open file: \"{s}\"", .{pathname}); Global.crash(); }; - const fd = bun.sys.toLibUVOwnedFD(file, .open, .close_on_fail).unwrap() catch |err| { + const fd = file.makeLibUVOwnedForSyscall(.open, .close_on_fail).unwrap() catch |err| { Output.err(err, "failed to open file: \"{s}\"", .{pathname}); Global.crash(); }; - defer _ = bun.sys.close(fd); + defer fd.close(); const stat = bun.sys.sys_uv.fstat(fd).unwrap() catch |err| { Output.err(err, "failed to stat file: \"{s}\"", .{pathname}); @@ -1659,7 +1659,7 @@ pub const PackCommand = struct { while (bundled_pack_queue.removeOrNull()) |pathname| { defer if (log_level.showProgress()) node.completeOne(); - const file = File.openat(root_dir, pathname, bun.O.RDONLY, 0).unwrap() catch |err| { + const file = File.openat(.fromStdDir(root_dir), pathname, bun.O.RDONLY, 0).unwrap() catch |err| { Output.err(err, "failed to open file: \"{s}\"", .{pathname}); Global.crash(); }; @@ -1945,7 +1945,7 @@ pub const PackCommand = struct { root_dir: std.fs.Dir, edited_package_json: string, ) OOM!*Archive.Entry { - const stat = bun.sys.fstatat(bun.toFD(root_dir), "package.json").unwrap() catch |err| { + const stat = bun.sys.fstatat(.fromStdDir(root_dir), "package.json").unwrap() catch |err| { Output.err(err, "failed to stat package.json", .{}); Global.crash(); }; @@ -2279,7 +2279,7 @@ pub const PackCommand = struct { default, @".npmignore", @".gitignore", - /// Exlusion pattern in "files" field within `package.json` + /// Exclusion pattern in "files" field within `package.json` @"package.json", }; @@ -2287,7 +2287,7 @@ pub const PackCommand = struct { fn ignoreFileFail(dir: std.fs.Dir, ignore_kind: Kind, reason: enum { read, open }, err: anyerror) noreturn { var buf: PathBuffer = undefined; - const dir_path = bun.getFdPath(dir, &buf) catch ""; + const dir_path = bun.getFdPath(.fromStdDir(dir), &buf) catch ""; Output.err(err, "failed to {s} {s} at: \"{s}{s}{s}\"", .{ @tagName(reason), @tagName(ignore_kind), @@ -2384,16 +2384,17 @@ pub const PackCommand = struct { fn printArchivedFilesAndPackages( ctx: *Context, - root_dir: std.fs.Dir, + root_dir_std: std.fs.Dir, comptime is_dry_run: bool, pack_list: if (is_dry_run) *PackQueue else PackList, package_json_len: usize, ) void { + const root_dir = bun.FD.fromStdDir(root_dir_std); if (ctx.manager.options.log_level == .silent) return; const packed_fmt = "packed {} {s}"; if (comptime is_dry_run) { - const package_json_stat = bun.sys.fstatat(bun.toFD(root_dir), "package.json").unwrap() catch |err| { + const package_json_stat = root_dir.statat("package.json").unwrap() catch |err| { Output.err(err, "failed to stat package.json", .{}); Global.crash(); }; @@ -2406,7 +2407,7 @@ pub const PackCommand = struct { }); while (pack_list.removeOrNull()) |filename| { - const stat = bun.sys.fstatat(bun.toFD(root_dir), filename).unwrap() catch |err| { + const stat = root_dir.statat(filename).unwrap() catch |err| { Output.err(err, "failed to stat file: \"{s}\"", .{filename}); Global.crash(); }; diff --git a/src/cli/package_manager_command.zig b/src/cli/package_manager_command.zig index 16a8a3f570..e0279aac5f 100644 --- a/src/cli/package_manager_command.zig +++ b/src/cli/package_manager_command.zig @@ -247,7 +247,7 @@ pub const PackageManagerCommand = struct { } else if (strings.eqlComptime(subcommand, "cache")) { var dir: bun.PathBuffer = undefined; var fd = pm.getCacheDirectory(); - const outpath = bun.getFdPath(fd.fd, &dir) catch |err| { + const outpath = bun.getFdPath(.fromStdDir(fd), &dir) catch |err| { Output.prettyErrorln("{s} getting cache directory", .{@errorName(err)}); Global.crash(); }; diff --git a/src/cli/publish_command.zig b/src/cli/publish_command.zig index a87e32b858..e453516414 100644 --- a/src/cli/publish_command.zig +++ b/src/cli/publish_command.zig @@ -680,11 +680,11 @@ pub const PublishCommand = struct { // unset `ENABLE_VIRTUAL_TERMINAL_INPUT` on windows. This prevents backspace from // deleting the entire line const original_mode: if (Environment.isWindows) ?bun.windows.DWORD else void = if (comptime Environment.isWindows) - bun.win32.updateStdioModeFlags(0, .{ .unset = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT }) catch null; + bun.win32.updateStdioModeFlags(.std_in, .{ .unset = bun.windows.ENABLE_VIRTUAL_TERMINAL_INPUT }) catch null; defer if (comptime Environment.isWindows) { if (original_mode) |mode| { - _ = bun.windows.SetConsoleMode(bun.win32.STDIN_FD.cast(), mode); + _ = bun.windows.SetConsoleMode(bun.FD.stdin().native(), mode); } }; @@ -951,7 +951,7 @@ pub const PublishCommand = struct { Output.err(err, "failed to open workspace directory", .{}); Global.crash(); }; - defer _ = bun.sys.close(workspace_root); + defer workspace_root.close(); try normalizeBin( allocator, @@ -1145,7 +1145,7 @@ pub const PublishCommand = struct { var dirs: std.ArrayListUnmanaged(struct { std.fs.Dir, string, bool }) = .{}; defer dirs.deinit(allocator); - try dirs.append(allocator, .{ bin_dir.asDir(), normalized_bin_dir, false }); + try dirs.append(allocator, .{ bin_dir.stdDir(), normalized_bin_dir, false }); while (dirs.pop()) |dir_info| { var dir, const dir_subpath, const close_dir = dir_info; diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index 74d0233ff3..ece5cd3a50 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -327,10 +327,10 @@ pub const RunCommand = struct { copy_script.items, }; - const ipc_fd = if (!Environment.isWindows) blk: { + const ipc_fd: ?bun.FD = if (!Environment.isWindows) blk: { const node_ipc_fd = bun.getenvZ("NODE_CHANNEL_FD") orelse break :blk null; - const fd = std.fmt.parseInt(u32, node_ipc_fd, 10) catch break :blk null; - break :blk bun.toFD(@as(i32, @intCast(fd))); + const fd = std.fmt.parseInt(u31, node_ipc_fd, 10) catch break :blk null; + break :blk bun.FD.fromNative(fd); } else null; // TODO: implement on Windows const spawn_result = switch ((bun.spawnSync(&.{ @@ -1288,7 +1288,7 @@ pub const RunCommand = struct { // TODO: optimize this pass for Windows. we can make better use of system apis available var file_path = script_name_to_search; { - const file = bun.toLibUVOwnedFD(((brk: { + const file = bun.FD.fromStdFile((brk: { if (std.fs.path.isAbsolute(script_name_to_search)) { var win_resolver = resolve_path.PosixToWinNormalizer{}; var resolved = win_resolver.resolveCWD(script_name_to_search) catch @panic("Could not resolve path"); @@ -1323,8 +1323,8 @@ pub const RunCommand = struct { const file_pathZ = script_name_buf[0..file_path.len :0]; break :brk bun.openFileZ(file_pathZ, .{ .mode = .read_only }); } - }) catch return false).handle) catch return false; - defer _ = bun.sys.close(file); + }) catch return false).makeLibUVOwnedForSyscall(.open, .close_on_fail).unwrap() catch return false; + defer file.close(); switch (bun.sys.fstat(file)) { .result => |stat| { @@ -1741,7 +1741,7 @@ pub const BunXFastPath = struct { }; if (Environment.isDebug) { - debug("run_ctx.handle: '{}'", .{bun.FDImpl.fromSystem(handle)}); + debug("run_ctx.handle: '{}'", .{bun.FD.fromSystem(handle)}); debug("run_ctx.base_path: '{}'", .{bun.fmt.utf16(run_ctx.base_path)}); debug("run_ctx.arguments: '{}'", .{bun.fmt.utf16(run_ctx.arguments)}); debug("run_ctx.force_use_bun: '{}'", .{run_ctx.force_use_bun}); diff --git a/src/cli/test/Scanner.zig b/src/cli/test/Scanner.zig index 087f36e9c4..f79f03f2d6 100644 --- a/src/cli/test/Scanner.zig +++ b/src/cli/test/Scanner.zig @@ -102,9 +102,9 @@ pub fn scan(this: *Scanner, path_literal: []const u8) Error!void { } while (this.dirs_to_scan.readItem()) |entry| { + bun.assert(entry.relative_dir.isValid()); if (!bun.Environment.isWindows) { - const dir = entry.relative_dir.asDir(); - bun.assert(bun.toFD(dir.fd) != bun.invalid_fd); + const dir = entry.relative_dir.stdDir(); const parts2 = &[_][]const u8{ entry.dir_path, entry.name.slice() }; var path2 = this.fs.absBuf(parts2, &this.open_dir_buf); @@ -115,9 +115,6 @@ pub fn scan(this: *Scanner, path_literal: []const u8) Error!void { FileSystem.setMaxFd(child_dir.fd); _ = this.readDirWithName(path2, child_dir) catch return error.OutOfMemory; } else { - const dir = entry.relative_dir.asDir(); - bun.assert(bun.toFD(dir.fd) != bun.invalid_fd); - const parts2 = &[_][]const u8{ entry.dir_path, entry.name.slice() }; const path2 = this.fs.absBufZ(parts2, &this.open_dir_buf); const child_dir = bun.openDirNoRenamingOrDeletingWindows(bun.invalid_fd, path2) catch continue; diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index 091359cb57..32586b1731 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -451,7 +451,7 @@ pub const JunitReporter = struct { @memcpy(junit_path_buf[0..path.len], path); junit_path_buf[path.len] = 0; - switch (bun.sys.File.openat(std.fs.cwd(), junit_path_buf[0..path.len :0], bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, 0o664)) { + switch (bun.sys.File.openat(.cwd(), junit_path_buf[0..path.len :0], bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, 0o664)) { .err => |err| { Output.err(error.JUnitReportFailed, "Failed to write JUnit report to {s}\n{}", .{ path, err }); }, @@ -840,7 +840,7 @@ pub const CommandLineReporter = struct { const tmpname = std.fmt.bufPrintZ(&shortname_buf, ".lcov.info.{s}.tmp", .{std.fmt.fmtSliceHexLower(&base64_bytes)}) catch unreachable; const path = bun.path.joinAbsStringBufZ(relative_dir, &lcov_name_buf, &.{ opts.reports_directory, tmpname }, .auto); const file = bun.sys.File.openat( - std.fs.cwd(), + .cwd(), path, bun.O.CREAT | bun.O.WRONLY | bun.O.TRUNC | bun.O.CLOEXEC, 0o644, @@ -943,10 +943,11 @@ pub const CommandLineReporter = struct { if (comptime reporters.lcov) { try lcov_buffered_writer.flush(); lcov_file.close(); + const cwd = bun.FD.cwd(); bun.C.moveFileZ( - bun.toFD(std.fs.cwd()), + cwd, lcov_name, - bun.toFD(std.fs.cwd()), + cwd, bun.path.joinAbsStringZ( relative_dir, &.{ opts.reports_directory, "lcov.info" }, diff --git a/src/cli/upgrade_command.zig b/src/cli/upgrade_command.zig index 29d9a89239..8a85e93fde 100644 --- a/src/cli/upgrade_command.zig +++ b/src/cli/upgrade_command.zig @@ -530,7 +530,7 @@ pub const UpgradeCommand = struct { Global.exit(1); }; const save_dir = save_dir_it; - const tmpdir_path = bun.getFdPath(save_dir.fd, &tmpdir_path_buf) catch |err| { + const tmpdir_path = bun.FD.fromStdDir(save_dir).getFdPath(&tmpdir_path_buf) catch |err| { Output.errGeneric("Failed to read temporary directory: {s}", .{@errorName(err)}); Global.exit(1); }; @@ -809,7 +809,7 @@ pub const UpgradeCommand = struct { current_executable_buf[target_dir_.len] = 0; } - C.moveFileZ(bun.toFD(save_dir.fd), exe, bun.toFD(target_dir.fd), target_filename) catch |err| { + C.moveFileZ(.fromStdDir(save_dir), exe, .fromStdDir(target_dir), target_filename) catch |err| { defer save_dir_.deleteTree(version_name) catch {}; if (comptime Environment.isWindows) { @@ -997,7 +997,7 @@ pub const upgrade_js_bindings = struct { ); switch (bun.windows.Win32Error.fromNTStatus(rc)) { - .SUCCESS => tempdir_fd = bun.toFD(fd), + .SUCCESS => tempdir_fd = .fromNative(fd), else => {}, } @@ -1008,7 +1008,7 @@ pub const upgrade_js_bindings = struct { if (comptime !Environment.isWindows) return .undefined; if (tempdir_fd) |fd| { - _ = bun.sys.close(fd); + fd.close(); } return .undefined; diff --git a/src/compile_target.zig b/src/compile_target.zig index 5e4484d2b0..c8e64dd418 100644 --- a/src/compile_target.zig +++ b/src/compile_target.zig @@ -123,7 +123,7 @@ pub fn exePath(this: *const CompileTarget, buf: *bun.PathBuffer, version_str: [: return buf[0..self_exe_path.len :0]; } - if (bun.sys.existsAt(bun.toFD(std.fs.cwd()), version_str)) { + if (bun.FD.cwd().existsAt(version_str)) { needs_download.* = false; return version_str; } @@ -138,7 +138,7 @@ pub fn exePath(this: *const CompileTarget, buf: *bun.PathBuffer, version_str: [: .auto, ); - if (bun.sys.existsAt(bun.toFD(std.fs.cwd()), dest)) { + if (bun.FD.cwd().existsAt(dest)) { needs_download.* = false; } @@ -297,7 +297,7 @@ pub fn downloadToPath(this: *const CompileTarget, env: *bun.DotEnv.Loader, alloc var did_retry = false; while (true) { - bun.C.moveFileZ(bun.toFD(tmpdir), if (this.os == .windows) "bun.exe" else "bun", bun.invalid_fd, dest_z) catch |err| { + bun.C.moveFileZ(.fromStdDir(tmpdir), if (this.os == .windows) "bun.exe" else "bun", bun.invalid_fd, dest_z) catch |err| { if (!did_retry) { did_retry = true; const dirname = bun.path.dirname(dest_z, .loose); diff --git a/src/copy_file.zig b/src/copy_file.zig index f4f47d60bc..31acb88c18 100644 --- a/src/copy_file.zig +++ b/src/copy_file.zig @@ -22,7 +22,7 @@ pub const CopyFileRangeError = error{ FileBusy, } || posix.PReadError || posix.PWriteError || posix.UnexpectedError; -const InputType = if (Environment.isWindows) bun.OSPathSliceZ else posix.fd_t; +const InputType = if (Environment.isWindows) bun.OSPathSliceZ else bun.FD; /// In a `bun install` with prisma, this reduces the system call count from ~18,000 to ~12,000 /// @@ -61,7 +61,7 @@ const CopyFileReturnType = bun.sys.Maybe(void); pub fn copyFileWithState(in: InputType, out: InputType, copy_file_state: *CopyFileState) CopyFileReturnType { if (comptime Environment.isMac) { - const rc = posix.system.fcopyfile(in, out, null, posix.system.COPYFILE{ .DATA = true }); + const rc = posix.system.fcopyfile(in.native(), out.native(), null, posix.system.COPYFILE{ .DATA = true }); switch (posix.errno(rc)) { .SUCCESS => return CopyFileReturnType.success, @@ -76,9 +76,9 @@ pub fn copyFileWithState(in: InputType, out: InputType, copy_file_state: *CopyFi if (can_use_ioctl_ficlone() and !copy_file_state.has_seen_exdev and !copy_file_state.has_ioctl_ficlone_failed) { // We only check once if the ioctl is supported, and cache the result. // EXT4 does not support FICLONE. - const rc = bun.C.linux.ioctl_ficlone(bun.toFD(out), bun.toFD(in)); + const rc = bun.C.linux.ioctl_ficlone(out, in); // the ordering is flipped but it is consistent with other system calls. - bun.sys.syslog("ioctl_ficlone({d}, {d}) = {d}", .{ in, out, rc }); + bun.sys.syslog("ioctl_ficlone({}, {}) = {d}", .{ in, out, rc }); switch (bun.C.getErrno(rc)) { .SUCCESS => return CopyFileReturnType.success, .XDEV => { @@ -107,7 +107,7 @@ pub fn copyFileWithState(in: InputType, out: InputType, copy_file_state: *CopyFi // The kernel checks the u64 value `offset+count` for overflow, use // a 32 bit value so that the syscall won't return EINVAL except for // impossibly large files (> 2^64-1 - 2^32-1). - const amt = switch (copyFileRange(in, out, math.maxInt(i32) - 1, 0, copy_file_state)) { + const amt = switch (copyFileRange(in.native(), out.native(), math.maxInt(i32) - 1, 0, copy_file_state)) { .result => |a| a, .err => |err| return .{ .err = err }, }; @@ -127,7 +127,7 @@ pub fn copyFileWithState(in: InputType, out: InputType, copy_file_state: *CopyFi } while (true) { - switch (copyFileReadWriteLoop(in, out, math.maxInt(i32) - 1)) { + switch (copyFileReadWriteLoop(in.native(), out.native(), math.maxInt(i32) - 1)) { .err => |err| return .{ .err = err }, .result => |amt| { if (amt == 0) break; @@ -280,13 +280,13 @@ pub fn copyFileReadWriteLoop( ) Maybe(usize) { var buf: [8 * 4096]u8 = undefined; const adjusted_count = @min(buf.len, len); - switch (bun.sys.read(bun.toFD(in), buf[0..adjusted_count])) { + switch (bun.sys.read(.fromNative(in), buf[0..adjusted_count])) { .result => |amt_read| { var amt_written: usize = 0; if (amt_read == 0) return .{ .result = 0 }; while (amt_written < amt_read) { - switch (bun.sys.write(bun.toFD(out), buf[amt_written..amt_read])) { + switch (bun.sys.write(.fromNative(out), buf[amt_written..amt_read])) { .result => |wrote| { if (wrote == 0) { return .{ .result = amt_written }; diff --git a/src/crash_handler.zig b/src/crash_handler.zig index e8cef84648..a2d32828ad 100644 --- a/src/crash_handler.zig +++ b/src/crash_handler.zig @@ -1415,9 +1415,9 @@ fn report(url: []const u8) void { .hStdInput = null, .hStdOutput = null, .hStdError = null, - // .hStdInput = bun.win32.STDIN_FD.cast(), - // .hStdOutput = bun.win32.STDOUT_FD.cast(), - // .hStdError = bun.win32.STDERR_FD.cast(), + // .hStdInput = bun.FD.stdin().native(), + // .hStdOutput = bun.FD.stdout().native(), + // .hStdError = bun.FD.stderr().native(), }; var cmd_line = std.BoundedArray(u16, 4096){}; cmd_line.appendSliceAssumeCapacity(std.unicode.utf8ToUtf16LeStringLiteral("powershell -ExecutionPolicy Bypass -Command \"try{Invoke-RestMethod -Uri '")); diff --git a/src/create/SourceFileProjectGenerator.zig b/src/create/SourceFileProjectGenerator.zig index 0bca6ee48f..39aa3d42ec 100644 --- a/src/create/SourceFileProjectGenerator.zig +++ b/src/create/SourceFileProjectGenerator.zig @@ -79,7 +79,7 @@ pub fn generate(_: Command.Context, _: Example.Tag, entry_point: string, result: // Create a file with given contents, returns if file was newly created fn createFile(filename: []const u8, contents: []const u8) bun.JSC.Maybe(bool) { // Check if file exists and has same contents - if (bun.sys.File.readFrom(bun.toFD(std.fs.cwd()), filename, default_allocator).asValue()) |source_contents| { + if (bun.sys.File.readFrom(bun.FD.cwd(), filename, default_allocator).asValue()) |source_contents| { defer default_allocator.free(source_contents); if (strings.eqlLong(source_contents, contents, true)) { return .{ .result = false }; @@ -92,11 +92,11 @@ fn createFile(filename: []const u8, contents: []const u8) bun.JSC.Maybe(bool) { } // Open file for writing - const fd = switch (bun.sys.openatA(bun.toFD(std.fs.cwd()), filename, bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, 0o644)) { + const fd = switch (bun.sys.openatA(.cwd(), filename, bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, 0o644)) { .result => |fd| fd, .err => |err| return .{ .err = err }, }; - defer _ = bun.sys.close(fd); + defer fd.close(); // Write contents switch (bun.sys.File.writeAll(.{ .handle = fd }, contents)) { diff --git a/src/deps/libuv.zig b/src/deps/libuv.zig index 9f534148ef..e9093f12c4 100644 --- a/src/deps/libuv.zig +++ b/src/deps/libuv.zig @@ -459,7 +459,7 @@ fn HandleMixin(comptime Type: type) type { if (fd_ == windows.INVALID_HANDLE_VALUE) return bun.invalid_fd; - return bun.FDImpl.fromSystem(fd_).encode(); + return .fromNative(fd_); } }; } @@ -1324,7 +1324,7 @@ pub const Pipe = extern struct { } pub fn open(this: *Pipe, file: bun.FileDescriptor) Maybe(void) { - const uv_fd = bun.uvfdcast(file); + const uv_fd = file.uv(); if (uv_pipe_open(this, uv_fd).toError(.open)) |err| return .{ .err = err }; return .{ .result = {} }; @@ -2327,8 +2327,6 @@ pub extern fn uv_get_process_title(buffer: [*]u8, size: usize) c_int; pub extern fn uv_set_process_title(title: [*]const u8) c_int; pub extern fn uv_resident_set_memory(rss: [*c]usize) c_int; pub extern fn uv_uptime(uptime: [*c]f64) c_int; -pub extern fn uv_get_osfhandle(fd: c_int) uv_os_fd_t; -pub extern fn uv_open_osfhandle(os_fd: uv_os_fd_t) c_int; pub const uv_rusage_t = extern struct { ru_utime: uv_timeval_t, ru_stime: uv_timeval_t, @@ -2834,7 +2832,7 @@ pub const ReturnCodeI64 = enum(i64) { } pub fn toFD(this: ReturnCodeI64) bun.FileDescriptor { - return bun.toFD(@as(i32, @truncate(this.int()))); + return .fromUV(@truncate(this.int())); } }; diff --git a/src/deps/uws.zig b/src/deps/uws.zig index 5cfb04a79b..2c0eb2e72b 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -1489,11 +1489,11 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { } const socket = this.socket.get() orelse return bun.invalid_fd; + // on windows uSockets exposes SOCKET return if (comptime Environment.isWindows) - // on windows uSockets exposes SOCKET - bun.toFD(@as(bun.FDImpl.System, @ptrCast(socket.getNativeHandle(is_ssl).?))) + .fromNative(@ptrCast(socket.getNativeHandle(is_ssl).?)) else - bun.toFD(@as(i32, @intCast(@intFromPtr(socket.getNativeHandle(is_ssl))))); + .fromNative(@intCast(@intFromPtr(socket.getNativeHandle(is_ssl)))); } pub fn markNeedsMoreForSendfile(this: ThisSocket) void { @@ -1763,7 +1763,7 @@ pub fn NewSocketHandler(comptime is_ssl: bool) type { this: *This, comptime socket_field_name: ?[]const u8, ) ?ThisSocket { - const socket_ = ThisSocket{ .socket = .{ .connected = us_socket_from_fd(ctx, @sizeOf(*anyopaque), bun.socketcast(handle)) orelse return null } }; + const socket_ = ThisSocket{ .socket = .{ .connected = us_socket_from_fd(ctx, @sizeOf(*anyopaque), handle.asSocketFd()) orelse return null } }; if (socket_.ext(*anyopaque)) |holder| { holder.* = this; @@ -3638,10 +3638,10 @@ pub fn NewApp(comptime ssl: bool) type { pub fn getNativeHandle(res: *Response) bun.FileDescriptor { if (comptime Environment.isWindows) { // on windows uSockets exposes SOCKET - return bun.toFD(@as(bun.FDImpl.System, @ptrCast(uws_res_get_native_handle(ssl_flag, res.downcast())))); + return .fromNative(@ptrCast(uws_res_get_native_handle(ssl_flag, res.downcast()))); } - return bun.toFD(@as(i32, @intCast(@intFromPtr(uws_res_get_native_handle(ssl_flag, res.downcast()))))); + return .fromNative(@intCast(@intFromPtr(uws_res_get_native_handle(ssl_flag, res.downcast())))); } pub fn getRemoteAddressAsText(res: *Response) ?[]const u8 { var buf: [*]const u8 = undefined; diff --git a/src/fd.zig b/src/fd.zig index 6ee1fd9f2e..98f6f1b0c3 100644 --- a/src/fd.zig +++ b/src/fd.zig @@ -1,30 +1,616 @@ -const std = @import("std"); -const posix = std.posix; +const backing_int = if (is_posix) c_int else u64; +const WindowsHandleNumber = u63; +const HandleNumber = if (is_posix) c_int else WindowsHandleNumber; +/// Abstraction over file descriptors. On POSIX, fd is a wrapper around a "fd_t", +/// and there is no special behavior. In return for using fd, you get access to +/// a 'close' method, and a handful of decl literals like '.cwd()' and '.stdin()'. +/// +/// On Windows, a tag differentiates two sources: +/// - system: A "std.os.windows.HANDLE" that windows APIs can interact with. +/// In fd case it is actually just an "*anyopaque" that points to some windows internals. +/// - uv: A c-runtime file descriptor that looks like a linux file descriptor. +/// ("uv", "uv_file", "c runtime file descriptor", "crt fd" are interchangeable terms) +/// +/// When a Windows HANDLE is converted to a UV descriptor, it +/// becomes owned by the C runtime, in which it can only be properly freed by +/// closing it. fd is problematic because it means that calling a libuv +/// function with a windows handle is impossible since the conversion will +/// make it impossible for the caller to close it. In these siutations, +/// the descriptor must be converted much higher up in the call stack. +pub const FD = packed struct(backing_int) { + value: Value, + kind: Kind, + pub const Kind = if (is_posix) + enum(u0) { system } + else + enum(u1) { system = 0, uv = 1 }; + pub const Value = if (is_posix) + packed union { as_system: fd_t } + else + packed union { as_system: WindowsHandleNumber, as_uv: uv_file }; -const bun = @import("root").bun; -const environment = bun.Environment; -const JSC = bun.JSC; -const JSValue = JSC.JSValue; -const libuv = bun.windows.libuv; + /// An invalid file descriptor. + /// Avoid in new code. Prefer `bun.FD.Optional` and `.none` instead. + pub const invalid: FD = .{ .kind = .system, .value = .{ .as_system = invalid_value } }; + const invalid_value = std.math.minInt(@FieldType(Value, "as_system")); -const allow_assert = environment.allow_assert; + // NOTE: there is no universal anytype init function. please annotate at each + // call site the source of the file descriptor you are initializing. with + // heavy decl literal usage, it can be confusing if you just see `.from()`, + // especially since numerical values have very subtle differences on Windows. -const log = bun.sys.syslog; -fn handleToNumber(handle: FDImpl.System) FDImpl.SystemAsInt { - if (environment.os == .windows) { + /// Initialize using the native system handle + pub fn fromNative(value: fd_t) FD { + if (os == .windows) { + // the current process fd is max usize + // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess + bun.assert(@intFromPtr(value) <= std.math.maxInt(u63)); + } + return .{ .kind = .system, .value = .{ .as_system = handleToNumber(value) } }; + } + pub const fromSystem = fromNative; + + /// Initialize using the c-runtime / libuv file descriptor + pub fn fromUV(value: uv_file) FD { + return if (is_posix) + .{ .kind = .system, .value = .{ .as_system = value } } + else + .{ .kind = .uv, .value = .{ .as_uv = value } }; + } + + pub fn cwd() FD { + return .fromNative(std.fs.cwd().fd); + } + + pub fn stdin() FD { + if (os != .windows) return .fromUV(0); + const in_comptime = @inComptime(); + comptime assert(!in_comptime); // windows std handles are not known at build time + return windows_cached_stdin; + } + + pub fn stdout() FD { + if (os != .windows) return .fromUV(1); + const in_comptime = @inComptime(); + comptime assert(!in_comptime); // windows std handles are not known at build time + return windows_cached_stdout; + } + + pub fn stderr() FD { + if (os != .windows) return .fromUV(2); + const in_comptime = @inComptime(); + comptime assert(!in_comptime); // windows std handles are not known at build time + return windows_cached_stderr; + } + + pub fn fromStdFile(file: std.fs.File) FD { + return .fromNative(file.handle); + } + + pub fn fromStdDir(dir: std.fs.Dir) FD { + return .fromNative(dir.fd); + } + + pub fn stdFile(fd: FD) std.fs.File { + return .{ .handle = fd.native() }; + } + + pub fn stdDir(fd: FD) std.fs.Dir { + return .{ .fd = fd.native() }; + } + + /// Perform different logic for each kind of windows file descriptor + pub fn decodeWindows(fd: FD) DecodeWindows { + return switch (fd.kind) { + .system => .{ .windows = numberToHandle(fd.value.as_system) }, + .uv => .{ .uv = fd.value.as_uv }, + }; + } + + pub fn isValid(fd: FD) bool { + return switch (os) { + else => fd.value.as_system != invalid_value, + .windows => switch (fd.kind) { + .system => fd.value.as_system != invalid_value, + .uv => true, + }, + }; + } + pub fn unwrapValid(fd: FD) ?FD { + return if (fd.isValid()) fd else null; + } + + /// When calling fd function, you may not be able to close the returned fd. + /// To close the fd, you have to call `.close()` on the `bun.FD`. + pub fn native(fd: FD) fd_t { + if (Environment.isDebug and !@inComptime()) bun.assert(fd.isValid()); + return switch (os) { + else => fd.value.as_system, + .windows => switch (fd.decodeWindows()) { + .windows => |handle| handle, + .uv => |file_number| uv_get_osfhandle(file_number), + }, + }; + } + /// Deprecated: renamed to `native` because it is unclear what `cast` would cast to. + pub const cast = native; + + /// When calling fd function, you should consider the FD struct to now be + /// invalid. Calling `.close()` on the FD at that point may not work. + pub fn uv(fd: FD) uv_file { + return switch (os) { + else => fd.value.as_system, + .windows => switch (fd.decodeWindows()) { + .windows => |handle| { + if (isStdioHandle(std.os.windows.STD_INPUT_HANDLE, handle)) return 0; + if (isStdioHandle(std.os.windows.STD_OUTPUT_HANDLE, handle)) return 1; + if (isStdioHandle(std.os.windows.STD_ERROR_HANDLE, handle)) return 2; + std.debug.panic( + \\Cast bun.FD.uv({}) makes closing impossible! + \\ + \\The supplier of fd FD should call 'FD.makeLibUVOwned', + \\probably where open() was called. + , + .{fd}, + ); + }, + .uv => fd.value.as_uv, + }, + }; + } + + pub fn asSocketFd(fd: FD) std.posix.socket_t { + return switch (os) { + .windows => @ptrCast(fd.native()), + else => fd.native(), + }; + } + + /// Assumes given a valid file descriptor + /// If error, the handle has not been closed + pub fn makeLibUVOwned(fd: FD) !FD { + if (allow_assert) bun.assert(fd.isValid()); + return switch (os) { + else => fd, + .windows => switch (fd.kind) { + .system => fd: { + break :fd FD.fromUV(try uv_open_osfhandle(numberToHandle(fd.value.as_system))); + }, + .uv => fd, + }, + }; + } + pub fn makeLibUVOwnedForSyscall( + maybe_windows_fd: bun.FileDescriptor, + comptime syscall_tag: bun.sys.Tag, + comptime error_case: enum { close_on_fail, leak_fd_on_fail }, + ) bun.sys.Maybe(bun.FileDescriptor) { + if (os != .windows) { + return .{ .result = maybe_windows_fd }; + } + return .{ .result = maybe_windows_fd.makeLibUVOwned() catch |err| switch (err) { + error.SystemFdQuotaExceeded => { + if (error_case == .close_on_fail) { + maybe_windows_fd.close(); + } + return .{ .err = .{ + .errno = @intFromEnum(bun.C.E.MFILE), + .syscall = syscall_tag, + } }; + }, + } }; + } + + /// fd function will NOT CLOSE stdin/stdout/stderr. + /// Expects a VALID file descriptor object. + /// + /// Do not use fd on JS-provided file descriptors (e.g. in + /// `fs.closeSync`). For those cases, the developer may provide a faulty + /// value, and we must forward EBADF to them. For internal situations, we + /// should never hit EBADF since it means we could have replaced the file + /// descriptor, closing something completely unrelated; fd would cause + /// weird behavior as you see EBADF errors in unrelated places. + /// + /// One day, we can add code to track file descriptor allocations and frees. + /// In debug, fd assertion failure can print where the FD was actually + /// closed. + pub fn close(fd: FD) void { + bun.debugAssert(fd.closeAllowingBadFileDescriptor(@returnAddress()) == null); // use after close! + } + + /// fd function will NOT CLOSE stdin/stdout/stderr. + /// + /// Use fd API to implement `node:fs` close. + /// Prefer asserting that EBADF does not happen with `.close()` + pub fn closeAllowingBadFileDescriptor(fd: FD, return_address: ?usize) ?bun.sys.Error { + if (fd.stdioTag() != null) { + log("close({}) SKIPPED", .{fd}); + return null; + } + return fd.closeAllowingStandardIo(return_address orelse @returnAddress()); + } + + /// fd allows you to close standard io. It also returns the error. + /// Consider fd the raw close method. + pub fn closeAllowingStandardIo(fd: FD, return_address: ?usize) ?bun.sys.Error { + if (allow_assert) bun.assert(fd.isValid()); // probably a UAF + + // Format the file descriptor for logging BEFORE closing it. + // Otherwise the file descriptor is always invalid after closing it. + var buf: if (Environment.isDebug) [1050]u8 else void = undefined; + const fd_fmt = if (Environment.isDebug) std.fmt.bufPrint(&buf, "{}", .{fd}) catch buf[0..]; + + const result: ?bun.sys.Error = switch (os) { + .linux => result: { + bun.assert(fd.native() >= 0); + break :result switch (bun.C.getErrno(bun.sys.syscall.close(fd.native()))) { + .BADF => .{ .errno = @intFromEnum(E.BADF), .syscall = .close, .fd = fd }, + else => null, + }; + }, + .mac => result: { + bun.assert(fd.native() >= 0); + break :result switch (bun.C.getErrno(bun.sys.syscall.@"close$NOCANCEL"(fd.native()))) { + .BADF => .{ .errno = @intFromEnum(E.BADF), .syscall = .close, .fd = fd }, + else => null, + }; + }, + .windows => switch (fd.decodeWindows()) { + .uv => |file_number| result: { + var req: libuv.fs_t = libuv.fs_t.uninitialized; + defer req.deinit(); + const rc = libuv.uv_fs_close(libuv.Loop.get(), &req, file_number, null); + break :result if (rc.errno()) |errno| + .{ .errno = errno, .syscall = .close, .fd = fd, .from_libuv = true } + else + null; + }, + .windows => |handle| result: { + break :result switch (bun.windows.NtClose(handle)) { + .SUCCESS => null, + else => |rc| bun.sys.Error{ + .errno = if (bun.windows.Win32Error.fromNTStatus(rc).toSystemErrno()) |errno| @intFromEnum(errno) else 1, + .syscall = .CloseHandle, + .fd = fd, + }, + }; + }, + }, + else => @compileError("FD.close() not implemented for fd platform"), + }; + if (Environment.isDebug) { + if (result) |err| { + if (err.errno == @intFromEnum(E.BADF)) { + bun.Output.debugWarn("close({s}) = EBADF. This is an indication of a file descriptor UAF", .{fd_fmt}); + bun.crash_handler.dumpCurrentStackTrace(return_address orelse @returnAddress(), .{ .frame_count = 4, .stop_at_jsc_llint = true }); + } else { + log("close({s}) = {}", .{ fd_fmt, err }); + } + } else { + log("close({s})", .{fd_fmt}); + } + } + return result; + } + + /// fd "fails" if not given an int32, returning null in that case + pub fn fromJS(value: JSValue) ?FD { + if (!value.isAnyInt()) return null; + const fd64 = value.toInt64(); + if (fd64 < 0 or fd64 > std.math.maxInt(i32)) { + return null; + } + const fd: i32 = @intCast(fd64); + if (os == .windows) { + return switch (fd) { + 0 => .stdin(), + 1 => .stdout(), + 2 => .stderr(), + else => .fromUV(fd), + }; + } + return .fromUV(fd); + } + // If a non-number is given, returns null. + // If the given number is not an fd (negative), an error is thrown and error.JSException is returned. + pub fn fromJSValidated(value: JSValue, global: *JSC.JSGlobalObject) bun.JSError!?FD { + if (!value.isNumber()) + return null; + const float = value.asNumber(); + if (@mod(float, 1) != 0) { + return global.throwRangeError(float, .{ .field_name = "fd", .msg = "an integer" }); + } + const int: i64 = @intFromFloat(float); + if (int < 0 or int > std.math.maxInt(i32)) { + return global.throwRangeError(int, .{ .field_name = "fd", .min = 0, .max = std.math.maxInt(i32) }); + } + const fd: c_int = @intCast(int); + if (os == .windows) { + if (Stdio.fromInt(fd)) |stdio| { + return stdio.fd(); + } + } + return .fromUV(fd); + } + /// After calling, the input file descriptor is no longer valid and must not be used. + /// If an error is thrown, the file descriptor is cleaned up for you. + pub fn toJS(any_fd: FD, global: *JSC.JSGlobalObject) JSValue { + const uv_owned_fd = any_fd.makeLibUVOwned() catch { + any_fd.close(); + return global.throwValue((JSC.SystemError{ + .message = bun.String.static("EMFILE, too many open files"), + .code = bun.String.static("EMFILE"), + }).toErrorInstance(global)) catch .zero; + }; + return JSValue.jsNumberFromInt32(uv_owned_fd.uv()); + } + + pub const Stdio = enum(u8) { + std_in = 0, + std_out = 1, + std_err = 2, + pub fn fd(tag: Stdio) FD { + return switch (tag) { + .std_in => .stdin(), + .std_out => .stdout(), + .std_err => .stderr(), + }; + } + pub fn fromInt(value: i32) ?Stdio { + if (value < 0 or value > 2) return null; + return @enumFromInt(value); + } + pub fn toInt(tag: Stdio) i32 { + return @intFromEnum(tag); + } + }; + pub fn stdioTag(fd: FD) ?Stdio { + return if (os == .windows) switch (fd.decodeWindows()) { + .windows => |handle| { + const process = std.os.windows.peb().ProcessParameters; + if (handle == process.hStdInput) { + return .std_in; + } else if (handle == process.hStdOutput) { + return .std_out; + } else if (handle == process.hStdError) { + return .std_err; + } + return null; + }, + .uv => |file_number| switch (file_number) { + 0 => .std_in, + 1 => .std_out, + 2 => .std_err, + else => null, + }, + } else switch (fd.value.as_system) { + 0 => .std_in, + 1 => .std_out, + 2 => .std_err, + else => null, + }; + } + + pub const HashMapContext = struct { + pub fn hash(_: @This(), fd: FD) u64 { + // a file descriptor is i32 on linux, u64 on windows + // the goal here is to do zero work and widen the 32 bit type to 64 + return @as(if (os == .windows) u64 else u32, @bitCast(fd)); + } + + pub fn eql(_: @This(), a: FD, b: FD) bool { + return a == b; + } + + pub fn pre(input: FD) Prehashed { + return Prehashed{ + .value = hash(.{}, input), + .input = input, + }; + } + + pub const Prehashed = struct { + value: u64, + input: FD, + + pub fn hash(ctx: @This(), fd: FD) u64 { + if (fd == ctx.input) return ctx.value; + return fd; + } + + pub fn eql(_: @This(), a: FD, b: FD) bool { + return a == b; + } + }; + }; + + pub fn format(fd: FD, comptime fmt: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + if (!fd.isValid()) { + try writer.writeAll("[invalid_fd]"); + return; + } + + if (fmt.len != 0) { + // The reason for fd error is because formatting FD as an integer on windows is + // ambiguous and almost certainly a mistake. You probably meant to format fd.cast(). + // + // Remember fd formatter will + // - on posix, print the number + // - on windows, print if it is a handle or a libuv file descriptor + // - in debug on all platforms, print the path of the file descriptor + // + // Not having fd error caused a linux+debug only crash in bun.sys.getFdPath because + // we forgot to change the thing being printed to "fd.native()" when the FD was introduced. + @compileError("invalid format string for bun.FD.format. must be empty like '{}'"); + } + + switch (os) { + else => { + const fd_native = fd.native(); + try writer.print("{d}", .{fd_native}); + if (Environment.isDebug and fd_native >= 3) print_with_path: { + var path_buf: bun.PathBuffer = undefined; + // NOTE: Bun's `fd.getFdPath`, while supporting some + // situations the standard library does not, hits EINVAL + // instead of gracefully handling invalid file descriptors. + // It is assumed that debug builds are ran on systems that + // support the standard library functions (since they would + // likely have run the Zig compiler, and it's not the end of + // the world if this fails. + const path = std.os.getFdPath(fd_native, &path_buf) catch |err| switch (err) { + error.FileNotFound => { + try writer.writeAll("[BADF]"); + break :print_with_path; + }, + else => |e| { + try writer.print("[unknown: error.{s}]", .{@errorName(e)}); + break :print_with_path; + }, + }; + try writer.print("[{s}]", .{path}); + } + }, + .windows => switch (fd.decodeWindows()) { + .windows => |handle| { + if (Environment.isDebug) { + const peb = std.os.windows.peb(); + if (handle == peb.ProcessParameters.hStdInput) { + return try writer.print("{d}[stdin handle]", .{fd.value.as_system}); + } else if (handle == peb.ProcessParameters.hStdOutput) { + return try writer.print("{d}[stdout handle]", .{fd.value.as_system}); + } else if (handle == peb.ProcessParameters.hStdError) { + return try writer.print("{d}[stderr handle]", .{fd.value.as_system}); + } else if (handle == peb.ProcessParameters.CurrentDirectory.Handle) { + return try writer.print("{d}[cwd handle]", .{fd.value.as_system}); + } else print_with_path: { + var fd_path: bun.WPathBuffer = undefined; + const path = std.os.windows.GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &fd_path) catch break :print_with_path; + return try writer.print("{d}[{}]", .{ + fd.value.as_system, + bun.fmt.utf16(path), + }); + } + } + try writer.print("{d}[handle]", .{fd.value.as_system}); + }, + .uv => |file_number| try writer.print("{d}[libuv]", .{file_number}), + }, + } + } + + pub const DecodeWindows = union(enum) { + windows: HANDLE, + uv: uv_file, + }; + + /// Note that currently FD can encode the invalid file descriptor value. + /// Obviously, prefer fd instead of that. + pub const Optional = enum(backing_int) { + none = @bitCast(invalid), + _, + pub fn init(maybe: ?FD) Optional { + return if (maybe) |fd| fd.toOptional() else .none; + } + pub fn close(optional: Optional) void { + if (optional.unwrap()) |fd| + fd.close(); + } + pub fn unwrap(optional: Optional) ?FD { + return if (optional == .none) null else @bitCast(@intFromEnum(optional)); + } + pub fn take(optional: *Optional) ?FD { + defer optional.* = .none; + return optional.unwrap(); + } + }; + /// Properly converts FD.invalid into FD.Optional.none + pub fn toOptional(fd: FD) Optional { + return @enumFromInt(@as(backing_int, @bitCast(fd))); + } + + // The following functions are from bun.sys but with the 'f' prefix dropped + // where it is relevant. These functions all take FD as the first argument, + // so that makes them Zig methods, even when declared in a separate file. + pub const chmod = bun.sys.fchmod; + pub const chmodat = bun.sys.fchmodat; + pub const chown = bun.sys.fchown; + pub const directoryExistsAt = bun.sys.directoryExistsAt; + pub const dup = bun.sys.dup; + pub const dupWithFlags = bun.sys.dupWithFlags; + pub const existsAt = bun.sys.existsAt; + pub const existsAtType = bun.sys.existsAtType; + pub const fcntl = bun.sys.fcntl; + pub const getFcntlFlags = bun.sys.getFcntlFlags; + pub const getFileSize = bun.sys.getFileSize; + pub const linkat = bun.sys.linkat; + pub const linkatTmpfile = bun.sys.linkatTmpfile; + pub const lseek = bun.sys.lseek; + pub const mkdirat = bun.sys.mkdirat; + pub const mkdiratA = bun.sys.mkdiratA; + pub const mkdiratW = bun.sys.mkdiratW; + pub const mkdiratZ = bun.sys.mkdiratZ; + pub const openat = bun.sys.openat; + pub const pread = bun.sys.pread; + pub const preadv = bun.sys.preadv; + pub const pwrite = bun.sys.pwrite; + pub const pwritev = bun.sys.pwritev; + pub const read = bun.sys.read; + pub const readNonblocking = bun.sys.readNonblocking; + pub const readlinkat = bun.sys.readlinkat; + pub const readv = bun.sys.readv; + pub const recv = bun.sys.recv; + pub const recvNonBlock = bun.sys.recvNonBlock; + pub const renameat = bun.sys.renameat; + pub const renameat2 = bun.sys.renameat2; + pub const send = bun.sys.send; + pub const sendNonBlock = bun.sys.sendNonBlock; + pub const sendfile = bun.sys.sendfile; + pub const stat = bun.sys.fstat; + pub const statat = bun.sys.fstatat; + pub const symlinkat = bun.sys.symlinkat; + pub const truncate = bun.sys.ftruncate; + pub const unlinkat = bun.sys.unlinkat; + pub const updateNonblocking = bun.sys.updateNonblocking; + pub const write = bun.sys.write; + pub const writeNonblocking = bun.sys.writeNonblocking; + pub const writev = bun.sys.writev; + + pub const getFdPath = bun.getFdPath; + pub const getFdPathW = bun.getFdPathW; + pub const getFdPathZ = bun.getFdPathZ; + + // TODO: move these methods defined in bun.sys.File to bun.sys. follow + // similar pattern as above. then delete bun.sys.File + pub fn quietWriter(fd: FD) bun.sys.File.QuietWriter { + return .{ .context = .{ .handle = fd } }; + } + + comptime { + if (os == .windows) { + // The conversion from FD to fd_t should be an integer truncate + bun.assert(@as(FD, @bitCast(@as(u64, 512))).value.as_system == 512); + } + } +}; + +fn isStdioHandle(id: std.os.windows.DWORD, handle: HANDLE) bool { + const h = std.os.windows.GetStdHandle(id) catch return false; + return handle == h; +} + +fn handleToNumber(handle: fd_t) HandleNumber { + if (is_posix) { + return handle; + } else { // intCast fails if 'fd > 2^62' // possible with handleToNumber(GetCurrentProcess()); return @intCast(@intFromPtr(handle)); - } else { - return handle; } } -fn numberToHandle(handle: FDImpl.SystemAsInt) FDImpl.System { - if (environment.os == .windows) { - if (!@inComptime()) { - bun.assert(handle != FDImpl.invalid_value); - } +fn numberToHandle(handle: HandleNumber) fd_t { + if (os == .windows) { + if (handle == 0) return std.os.windows.INVALID_HANDLE_VALUE; return @ptrFromInt(handle); } else { return handle; @@ -32,396 +618,43 @@ fn numberToHandle(handle: FDImpl.SystemAsInt) FDImpl.System { } pub fn uv_get_osfhandle(in: c_int) libuv.uv_os_fd_t { - const out = libuv.uv_get_osfhandle(in); + const out = libuv_private.uv_get_osfhandle(in); return out; } pub fn uv_open_osfhandle(in: libuv.uv_os_fd_t) error{SystemFdQuotaExceeded}!c_int { - const out = libuv.uv_open_osfhandle(in); + const out = libuv_private.uv_open_osfhandle(in); bun.assert(out >= -1); if (out == -1) return error.SystemFdQuotaExceeded; return out; } -/// Abstraction over file descriptors. This struct does nothing on non-windows operating systems. -/// -/// bun.FileDescriptor is the bitcast of this struct, which is essentially a tagged pointer. -/// -/// You can acquire one with FDImpl.decode(fd), and convert back to it with FDImpl.encode(fd). -/// -/// On Windows builds we have two kinds of file descriptors: -/// - system: A "std.os.windows.HANDLE" that windows APIs can interact with. -/// In this case it is actually just an "*anyopaque" that points to some windows internals. -/// - uv: A libuv file descriptor that looks like a linux file descriptor. -/// (technically a c runtime file descriptor, libuv might do extra stuff though) -/// -/// When converting UVFDs into Windows FDs, they are still said to be owned by libuv, -/// and they say to NOT close the handle. -pub const FDImpl = packed struct { - value: Value, - kind: Kind, +pub var windows_cached_fd_set: if (Environment.isDebug) bool else void = if (Environment.isDebug) false; +pub var windows_cached_stdin: FD = undefined; +pub var windows_cached_stdout: FD = undefined; +pub var windows_cached_stderr: FD = undefined; - const invalid_value = std.math.maxInt(SystemAsInt); - pub const invalid = FDImpl{ - .kind = .system, - .value = .{ .as_system = invalid_value }, - }; +const fd_t = std.posix.fd_t; +const HANDLE = bun.windows.HANDLE; +const uv_file = bun.windows.libuv.uv_file; +const assert = bun.assert; +const E = std.posix.E; - pub const System = posix.fd_t; +const bun = @import("root").bun; - pub const SystemAsInt = switch (environment.os) { - .windows => u63, - else => System, - }; +const Environment = bun.Environment; +const is_posix = Environment.isPosix; +const os = Environment.os; - pub const UV = switch (environment.os) { - .windows => bun.windows.libuv.uv_file, - else => System, - }; +const std = @import("std"); - pub const Value = if (environment.os == .windows) - packed union { as_system: SystemAsInt, as_uv: UV } - else - packed union { as_system: SystemAsInt }; - - pub const Kind = if (environment.os == .windows) - enum(u1) { system = 0, uv = 1 } - else - enum(u0) { system }; - - comptime { - bun.assert(@sizeOf(FDImpl) == @sizeOf(System)); - - if (environment.os == .windows) { - // we want the conversion from FD to fd_t to be a integer truncate - bun.assert(@as(FDImpl, @bitCast(@as(u64, 512))).value.as_system == 512); - } - } - - pub fn fromSystemWithoutAssertion(system_fd: System) FDImpl { - return FDImpl{ - .kind = .system, - .value = .{ .as_system = handleToNumber(system_fd) }, - }; - } - - pub fn fromSystem(system_fd: System) FDImpl { - if (environment.os == .windows) { - // the current process fd is max usize - // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess - if (@intFromPtr(system_fd) > std.math.maxInt(SystemAsInt)) { - bun.Output.panic( - \\FDImpl.fromSystem() called with a fd that is too large: 0x{x} - , - .{@intFromPtr(system_fd)}, - ); - return invalid; - } - } - - return fromSystemWithoutAssertion(system_fd); - } - - pub fn fromUV(uv_fd: UV) FDImpl { - return switch (environment.os) { - else => FDImpl{ - .kind = .system, - .value = .{ .as_system = uv_fd }, - }, - .windows => FDImpl{ - .kind = .uv, - .value = .{ .as_uv = uv_fd }, - }, - }; - } - - pub fn isValid(this: FDImpl) bool { - return switch (environment.os) { - // the 'zero' value on posix is debatable. it can be standard in. - // TODO(@paperclover): steamroll away every use of bun.FileDescriptor.zero - else => this.value.as_system != invalid_value, - .windows => switch (this.kind) { - // zero is not allowed in addition to the invalid value (zero would be a null ptr) - .system => this.value.as_system != invalid_value and this.value.as_system != 0, - // the libuv tag is always fine - .uv => true, - }, - }; - } - - /// When calling this function, you may not be able to close the returned fd. - /// To close the fd, you have to call `.close()` on the FD. - pub fn system(this: FDImpl) System { - return switch (environment.os == .windows) { - false => numberToHandle(this.value.as_system), - true => switch (this.kind) { - .system => numberToHandle(this.value.as_system), - .uv => uv_get_osfhandle(this.value.as_uv), - }, - }; - } - - /// Convert to bun.FileDescriptor - pub fn encode(this: FDImpl) bun.FileDescriptor { - // https://github.com/ziglang/zig/issues/18462 - return @enumFromInt(@as(bun.FileDescriptorInt, @bitCast(this))); - } - - pub fn decode(fd: bun.FileDescriptor) FDImpl { - return @bitCast(@intFromEnum(fd)); - } - - /// When calling this function, you should consider the FD struct to now be invalid. - /// Calling `.close()` on the FD at that point may not work. - pub fn uv(this: FDImpl) UV { - return switch (environment.os) { - else => numberToHandle(this.value.as_system), - .windows => switch (this.kind) { - .system => { - const w = std.os.windows; - - const S = struct { - fn is_stdio_handle(id: w.DWORD, handle: w.HANDLE) bool { - const h = w.GetStdHandle(id) catch return false; - return handle == h; - } - }; - const handle = this.encode().cast(); - if (S.is_stdio_handle(w.STD_INPUT_HANDLE, handle)) return 0; - if (S.is_stdio_handle(w.STD_OUTPUT_HANDLE, handle)) return 1; - if (S.is_stdio_handle(w.STD_ERROR_HANDLE, handle)) return 2; - - std.debug.panic( - \\Cast {} -> FDImpl.UV makes closing impossible! - \\ - \\The supplier of this FileDescriptor should call 'bun.toLibUVOwnedFD' - \\or 'FDImpl.makeLibUVOwned', probably where open() was called. - , - .{this}, - ); - }, - .uv => this.value.as_uv, - }, - }; - } - - /// This function will prevent stdin, stdout, and stderr from being closed. - pub fn close(this: FDImpl) ?bun.sys.Error { - if (environment.os != .windows or this.kind == .uv) { - // This branch executes always on linux (uv() is no-op), - // or on Windows when given a UV file descriptor. - const fd = this.uv(); - if (fd == 0 or fd == 1 or fd == 2) { - log("close({}) SKIPPED", .{fd}); - return null; - } - } - return this.closeAllowingStdinStdoutAndStderr(); - } - - /// Assumes given a valid file descriptor - /// If error, the handle has not been closed - pub fn makeLibUVOwned(this: FDImpl) !FDImpl { - this.assertValid(); - return switch (environment.os) { - else => this, - .windows => switch (this.kind) { - .system => fd: { - break :fd FDImpl.fromUV(try uv_open_osfhandle(numberToHandle(this.value.as_system))); - }, - .uv => this, - }, - }; - } - - pub fn closeAllowingStdinStdoutAndStderr(this: FDImpl) ?bun.sys.Error { - if (allow_assert) { - bun.assert(this.value.as_system != invalid_value); // probably a UAF - } - - // Format the file descriptor for logging BEFORE closing it. - // Otherwise the file descriptor is always invalid after closing it. - var buf: if (environment.isDebug) [1050]u8 else void = undefined; - const this_fmt = if (environment.isDebug) std.fmt.bufPrint(&buf, "{}", .{this}) catch unreachable; - - const result: ?bun.sys.Error = switch (environment.os) { - .linux, .mac => result: { - const fd = this.encode(); - bun.assert(fd != bun.invalid_fd); - bun.assert(fd.cast() >= 0); - break :result switch (bun.C.getErrno(bun.sys.syscall.close(fd.cast()))) { - .BADF => bun.sys.Error{ .errno = @intFromEnum(posix.E.BADF), .syscall = .close, .fd = fd }, - else => null, - }; - }, - .windows => result: { - switch (this.kind) { - .uv => { - var req: libuv.fs_t = libuv.fs_t.uninitialized; - defer req.deinit(); - const rc = libuv.uv_fs_close(libuv.Loop.get(), &req, this.value.as_uv, null); - break :result if (rc.errno()) |errno| - .{ .errno = errno, .syscall = .close, .fd = this.encode(), .from_libuv = true } - else - null; - }, - .system => { - bun.assert(this.value.as_system != 0); - const handle: System = @ptrFromInt(@as(u64, this.value.as_system)); - break :result switch (bun.windows.NtClose(handle)) { - .SUCCESS => null, - else => |rc| bun.sys.Error{ - .errno = if (bun.windows.Win32Error.fromNTStatus(rc).toSystemErrno()) |errno| @intFromEnum(errno) else 1, - .syscall = .CloseHandle, - .fd = this.encode(), - }, - }; - }, - } - }, - else => @compileError("FD.close() not implemented for this platform"), - }; - - if (environment.isDebug) { - if (result) |err| { - if (err.errno == @intFromEnum(posix.E.BADF)) { - bun.Output.debugWarn("close({s}) = EBADF. This is an indication of a file descriptor UAF", .{this_fmt}); - } else { - log("close({s}) = {}", .{ this_fmt, err }); - } - } else { - log("close({s})", .{this_fmt}); - } - } - - return result; - } - - /// This "fails" if not given an int32, returning null in that case - pub fn fromJS(value: JSValue) ?FDImpl { - if (!value.isAnyInt()) return null; - const fd64 = value.toInt64(); - if (fd64 < 0 or fd64 > std.math.maxInt(i32)) { - return null; - } - const fd: i32 = @intCast(fd64); - if (comptime environment.isWindows) { - return switch (bun.FDTag.get(fd)) { - .stdin => FDImpl.decode(bun.STDIN_FD), - .stdout => FDImpl.decode(bun.STDOUT_FD), - .stderr => FDImpl.decode(bun.STDERR_FD), - else => FDImpl.fromUV(fd), - }; - } - return FDImpl.fromUV(fd); - } - - // If a non-number is given, returns null. - // If the given number is not an fd (negative), an error is thrown and error.JSException is returned. - pub fn fromJSValidated(value: JSValue, global: *JSC.JSGlobalObject) bun.JSError!?FDImpl { - if (!value.isNumber()) { - return null; - } - - const float = value.asNumber(); - if (@mod(float, 1) != 0) { - return global.throwRangeError(float, .{ .field_name = "fd", .msg = "an integer" }); - } - - const int: i64 = @intFromFloat(float); - if (int < 0 or int > std.math.maxInt(i32)) { - return global.throwRangeError(int, .{ .field_name = "fd", .min = 0, .max = std.math.maxInt(i32) }); - } - - const fd: c_int = @intCast(int); - - if (comptime environment.isWindows) { - return switch (bun.FDTag.get(fd)) { - .stdin => FDImpl.decode(bun.STDIN_FD), - .stdout => FDImpl.decode(bun.STDOUT_FD), - .stderr => FDImpl.decode(bun.STDERR_FD), - else => FDImpl.fromUV(fd), - }; - } - return FDImpl.fromUV(fd); - } - - /// After calling, the input file descriptor is no longer valid and must not be used. - /// If an error is thrown, the file descriptor is cleaned up for you. - pub fn toJS(value: FDImpl, global: *JSC.JSGlobalObject) JSValue { - const fd = value.makeLibUVOwned() catch { - _ = value.close(); - return global.throwValue((JSC.SystemError{ - .message = bun.String.static("EMFILE, too many open files"), - .code = bun.String.static("EMFILE"), - }).toErrorInstance(global)) catch .zero; - }; - return JSValue.jsNumberFromInt32(fd.uv()); - } - - pub fn format(this: FDImpl, comptime fmt: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { - if (!this.isValid()) { - try writer.writeAll("[invalid_fd]"); - return; - } - - if (fmt.len != 0) { - // The reason for this error is because formatting FD as an integer on windows is - // ambiguous and almost certainly a mistake. You probably meant to format fd.cast(). - // - // Remember this formatter will - // - on posix, print the number - // - on windows, print if it is a handle or a libuv file descriptor - // - in debug on all platforms, print the path of the file descriptor - // - // Not having this error caused a linux+debug only crash in bun.sys.getFdPath because - // we forgot to change the thing being printed to "fd.cast()" when FDImpl was introduced. - @compileError("invalid format string for FDImpl.format. must be empty like '{}'"); - } - - switch (environment.os) { - else => { - const fd = this.system(); - try writer.print("{d}", .{fd}); - if (environment.isDebug and fd >= 3) print_with_path: { - var path_buf: bun.PathBuffer = undefined; - const path = std.os.getFdPath(fd, &path_buf) catch break :print_with_path; - try writer.print("[{s}]", .{path}); - } - }, - .windows => { - switch (this.kind) { - .system => { - if (environment.isDebug) { - const peb = std.os.windows.peb(); - const handle = this.system(); - if (handle == peb.ProcessParameters.hStdInput) { - return try writer.print("{d}[stdin handle]", .{this.value.as_system}); - } else if (handle == peb.ProcessParameters.hStdOutput) { - return try writer.print("{d}[stdout handle]", .{this.value.as_system}); - } else if (handle == peb.ProcessParameters.hStdError) { - return try writer.print("{d}[stderr handle]", .{this.value.as_system}); - } else if (handle == peb.ProcessParameters.CurrentDirectory.Handle) { - return try writer.print("{d}[cwd handle]", .{this.value.as_system}); - } else print_with_path: { - var fd_path: bun.WPathBuffer = undefined; - const path = std.os.windows.GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &fd_path) catch break :print_with_path; - return try writer.print("{d}[{}]", .{ - this.value.as_system, - bun.fmt.utf16(path), - }); - } - } - - try writer.print("{d}[handle]", .{this.value.as_system}); - }, - .uv => try writer.print("{d}[libuv]", .{this.value.as_uv}), - } - }, - } - } - - pub fn assertValid(this: FDImpl) void { - bun.assert(this.isValid()); - } +const JSC = bun.JSC; +const JSValue = JSC.JSValue; +const libuv = bun.windows.libuv; +const libuv_private = struct { + extern fn uv_get_osfhandle(fd: c_int) fd_t; + extern fn uv_open_osfhandle(os_fd: fd_t) c_int; }; +const allow_assert = Environment.allow_assert; + +const log = bun.sys.syslog; diff --git a/src/fs.zig b/src/fs.zig index 1878b97d72..4f7cd1778e 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -20,6 +20,7 @@ const path_handler = @import("./resolver/resolve_path.zig"); const PathString = bun.PathString; const allocators = bun.allocators; const OOM = bun.OOM; +const FD = bun.FD; const MAX_PATH_BYTES = bun.MAX_PATH_BYTES; const PathBuffer = bun.PathBuffer; @@ -136,8 +137,9 @@ pub const FileSystem = struct { pub const DirEntry = struct { pub const EntryMap = bun.StringHashMapUnmanaged(*Entry); pub const EntryStore = allocators.BSSList(Entry, Preallocate.Counts.files); + dir: string, - fd: StoredFileDescriptorType = .zero, + fd: FD = .invalid, generation: bun.Generation = 0, data: EntryMap, @@ -390,7 +392,7 @@ pub const FileSystem = struct { symlink: PathString = PathString.empty, /// Too much code expects this to be 0 /// don't make it bun.invalid_fd - fd: StoredFileDescriptorType = .zero, + fd: FD = .invalid, kind: Kind = .file, }; @@ -616,7 +618,7 @@ pub const FileSystem = struct { // we will not delete the temp directory .can_rename_or_delete = false, .read_only = true, - }).unwrap()).asDir(); + }).unwrap()).stdDir(); } return try bun.openDirAbsolute(tmpdir_path); @@ -668,26 +670,24 @@ pub const FileSystem = struct { dir_fd: bun.FileDescriptor = bun.invalid_fd, pub inline fn dir(this: *TmpfilePosix) std.fs.Dir { - return this.dir_fd.asDir(); + return this.dir_fd.stdDir(); } pub inline fn file(this: *TmpfilePosix) std.fs.File { - return this.fd.asFile(); + return this.fd.stdFile(); } pub fn close(this: *TmpfilePosix) void { - if (this.fd != bun.invalid_fd) _ = bun.sys.close(this.fd); + if (this.fd.isValid()) this.fd.close(); } pub fn create(this: *TmpfilePosix, _: *RealFS, name: [:0]const u8) !void { // We originally used a temporary directory, but it caused EXDEV. - const dir_fd = std.fs.cwd().fd; + const dir_fd = bun.FD.cwd(); + this.dir_fd = dir_fd; const flags = bun.O.CREAT | bun.O.RDWR | bun.O.CLOEXEC; - this.dir_fd = bun.toFD(dir_fd); - - const result = try bun.sys.openat(bun.toFD(dir_fd), name, flags, std.posix.S.IRWXU).unwrap(); - this.fd = bun.toFD(result); + this.fd = try bun.sys.openat(dir_fd, name, flags, std.posix.S.IRWXU).unwrap(); } pub fn promoteToCWD(this: *TmpfilePosix, from_name: [*:0]const u8, name: [*:0]const u8) !void { @@ -718,19 +718,19 @@ pub const FileSystem = struct { } pub inline fn file(this: *TmpfileWindows) std.fs.File { - return this.fd.asFile(); + return this.fd.stdFile(); } pub fn close(this: *TmpfileWindows) void { - if (this.fd != bun.invalid_fd) _ = bun.sys.close(this.fd); + if (this.fd.isValid()) this.fd.close(); } pub fn create(this: *TmpfileWindows, rfs: *RealFS, name: [:0]const u8) !void { - const tmpdir_ = try rfs.openTmpDir(); + const tmp_dir = try rfs.openTmpDir(); const flags = bun.O.CREAT | bun.O.WRONLY | bun.O.CLOEXEC; - this.fd = try bun.sys.openat(bun.toFD(tmpdir_.fd), name, flags, 0).unwrap(); + this.fd = try bun.sys.openat(.fromStdDir(tmp_dir), name, flags, 0).unwrap(); var buf: bun.PathBuffer = undefined; const existing_path = try bun.getFdPath(this.fd, &buf); this.existing_path = try bun.default_allocator.dupe(u8, existing_path); @@ -986,7 +986,7 @@ pub const FileSystem = struct { 0, ); const fd = try dirfd.unwrap(); - return fd.asDir(); + return fd.stdDir(); } fn readdir( @@ -1008,7 +1008,7 @@ pub const FileSystem = struct { if (store_fd) { FileSystem.setMaxFd(handle.fd); - dir.fd = bun.toFD(handle.fd); + dir.fd = .fromStdDir(handle); } while (try iter.next().unwrap()) |*_entry| { @@ -1133,8 +1133,8 @@ pub const FileSystem = struct { if (in_place) |original| { original.data.clearAndFree(bun.fs_allocator); } - if (store_fd and entries.fd == .zero) - entries.fd = bun.toFD(handle.fd); + if (store_fd and !entries.fd.isValid()) + entries.fd = .fromStdDir(handle); entries_ptr.* = entries; const result = EntriesOption{ @@ -1441,36 +1441,34 @@ pub const FileSystem = struct { const stat = try C.lstat_absolute(absolute_path_c); const is_symlink = stat.kind == std.fs.File.Kind.sym_link; - var _kind = stat.kind; + var file_kind = stat.kind; var symlink: []const u8 = ""; if (is_symlink) { - var file = try if (existing_fd != .zero) - std.fs.File{ .handle = existing_fd.int() } + var file: bun.FD = if (existing_fd.unwrapValid()) |valid| + valid else if (store_fd) - std.fs.openFileAbsoluteZ(absolute_path_c, .{ .mode = .read_only }) + .fromStdFile(try std.fs.openFileAbsoluteZ(absolute_path_c, .{ .mode = .read_only })) else - bun.openFileForPath(absolute_path_c); - setMaxFd(file.handle); + .fromStdFile(try bun.openFileForPath(absolute_path_c)); + setMaxFd(file.native()); defer { - if ((!store_fd or fs.needToCloseFiles()) and existing_fd == .zero) { + if ((!store_fd or fs.needToCloseFiles()) and !existing_fd.isValid()) { file.close(); } else if (comptime FeatureFlags.store_file_descriptors) { - cache.fd = bun.toFD(file.handle); + cache.fd = file; } } - const _stat = try file.stat(); - - symlink = try bun.getFdPath(file.handle, &outpath); - - _kind = _stat.kind; + const file_stat = try file.stdFile().stat(); + symlink = try file.getFdPath(&outpath); + file_kind = file_stat.kind; } - bun.assert(_kind != .sym_link); + bun.assert(file_kind != .sym_link); - if (_kind == .directory) { + if (file_kind == .directory) { cache.kind = .dir; } else { cache.kind = .file; diff --git a/src/glob/GlobWalker.zig b/src/glob/GlobWalker.zig index e30476f086..1a6a64c398 100644 --- a/src/glob/GlobWalker.zig +++ b/src/glob/GlobWalker.zig @@ -144,10 +144,10 @@ pub const SyscallAccessor = struct { const Handle = struct { value: bun.FileDescriptor, - const zero = Handle{ .value = bun.FileDescriptor.zero }; + const empty: Handle = .{ .value = .invalid }; - pub fn isZero(this: Handle) bool { - return this.value == bun.FileDescriptor.zero; + pub fn isEmpty(this: Handle) bool { + return !this.value.isValid(); } pub fn eql(this: Handle, other: Handle) bool { @@ -163,7 +163,7 @@ pub const SyscallAccessor = struct { } pub inline fn iterate(dir: Handle) DirIter { - return .{ .value = DirIterator.WrappedIterator.init(dir.value.asDir()) }; + return .{ .value = DirIterator.WrappedIterator.init(dir.value.stdDir()) }; } }; @@ -190,7 +190,7 @@ pub const SyscallAccessor = struct { } pub fn close(handle: Handle) ?Syscall.Error { - return Syscall.close(handle.value); + return handle.value.closeAllowingBadFileDescriptor(@returnAddress()); } pub fn getcwd(path_buf: *bun.PathBuffer) Maybe([]const u8) { @@ -206,9 +206,9 @@ pub const DirEntryAccessor = struct { const Handle = struct { value: ?*FS.DirEntry, - const zero = Handle{ .value = null }; + const empty: Handle = .{ .value = null }; - pub fn isZero(this: Handle) bool { + pub fn isEmpty(this: Handle) bool { return this.value == null; } @@ -276,7 +276,7 @@ pub const DirEntryAccessor = struct { } pub fn open(path: [:0]const u8) !Maybe(Handle) { - return openat(Handle.zero, path); + return openat(.empty, path); } pub fn openat(handle: Handle, path_: [:0]const u8) !Maybe(Handle) { @@ -296,7 +296,7 @@ pub const DirEntryAccessor = struct { }; switch (res.*) { .entries => |entry| { - return .{ .result = Handle{ .value = entry } }; + return .{ .result = .{ .value = entry } }; }, .err => |err| { return err.original_err; @@ -434,7 +434,7 @@ pub fn GlobWalker_( pub const Iterator = struct { walker: *GlobWalker, iter_state: IterState = .get_next, - cwd_fd: Accessor.Handle = Accessor.Handle.zero, + cwd_fd: Accessor.Handle = .empty, empty_dir_path: [0:0]u8 = [0:0]u8{}, /// This is to make sure in debug/tests that we are closing file descriptors /// We should only have max 2 open at a time. One for the cwd, and one for the @@ -561,13 +561,13 @@ pub fn GlobWalker_( } pub fn closeCwdFd(this: *Iterator) void { - if (this.cwd_fd.isZero()) return; + if (this.cwd_fd.isEmpty()) return; _ = Accessor.close(this.cwd_fd); if (comptime count_fds) this.fds_open -= 1; } pub fn closeDisallowingCwd(this: *Iterator, fd: Accessor.Handle) void { - if (fd.isZero() or fd.eql(this.cwd_fd)) return; + if (fd.isEmpty() or fd.eql(this.cwd_fd)) return; _ = Accessor.close(fd); if (comptime count_fds) this.fds_open -= 1; } @@ -587,7 +587,7 @@ pub fn GlobWalker_( ) !Maybe(void) { log("transition => {s}", .{work_item.path}); this.iter_state = .{ .directory = .{ - .fd = Accessor.Handle.zero, + .fd = .empty, .iter = undefined, .path = undefined, .dir_path = undefined, @@ -693,7 +693,7 @@ pub fn GlobWalker_( this.iter_state.directory.next_pattern = if (component_idx + 1 < this.walker.patternComponents.items.len) &this.walker.patternComponents.items[component_idx + 1] else null; this.iter_state.directory.is_last = component_idx == this.walker.patternComponents.items.len - 1; this.iter_state.directory.at_cwd = false; - this.iter_state.directory.fd = Accessor.Handle.zero; + this.iter_state.directory.fd = .empty; log("Transition(dirpath={s}, fd={}, component_idx={d})", .{ dir_path, fd, component_idx }); diff --git a/src/install/bin.zig b/src/install/bin.zig index 4da91e8a0b..462f3c089b 100644 --- a/src/install/bin.zig +++ b/src/install/bin.zig @@ -456,7 +456,7 @@ pub const Bin = extern struct { done: bool = false, dir_iterator: ?std.fs.Dir.Iterator = null, package_name: String, - destination_node_modules: std.fs.Dir = bun.invalid_fd.asDir(), + destination_node_modules: std.fs.Dir = bun.invalid_fd.stdDir(), buf: bun.PathBuffer = undefined, string_buffer: []const u8, extern_string_buf: []const ExternalString, @@ -647,7 +647,7 @@ pub const Bin = extern struct { } return; }; - defer _ = bun.sys.close(target); + defer target.close(); this.createWindowsShim(target, abs_target, abs_dest, global); } @@ -672,7 +672,7 @@ pub const Bin = extern struct { if (strings.indexOfChar(chunk, '\n')) |newline| { if (newline > 0 and chunk[newline - 1] == '\r') { const pos = newline - 1; - bin.handle.asFile().seekTo(pos) catch return; + bin.handle.stdFile().seekTo(pos) catch return; bin.writeAll("\n").unwrap() catch return; } } @@ -698,7 +698,7 @@ pub const Bin = extern struct { return; } - bun.makePath(this.node_modules.asDir(), ".bin") catch {}; + bun.makePath(this.node_modules.stdDir(), ".bin") catch {}; break :bunx_file bun.sys.File.openatOSPath(bun.invalid_fd, abs_bunx_file, bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, 0o664).unwrap() catch |real_err| { this.err = real_err; return; @@ -713,7 +713,7 @@ pub const Bin = extern struct { const shebang = shebang: { const first_content_chunk = contents: { - const reader = target.asFile().reader(); + const reader = target.stdFile().reader(); const read = reader.read(&read_in_buf) catch break :contents null; if (read == 0) break :contents null; break :contents read_in_buf[0..read]; @@ -791,7 +791,7 @@ pub const Bin = extern struct { return; } - bun.makePath(this.node_modules.asDir(), ".bin") catch {}; + bun.makePath(this.node_modules.stdDir(), ".bin") catch {}; switch (bun.sys.symlink(rel_target, abs_dest)) { .err => |real_error| { // It was just created, no need to delete destination and symlink again diff --git a/src/install/extract_tarball.zig b/src/install/extract_tarball.zig index 1c39805991..9850bac0a9 100644 --- a/src/install/extract_tarball.zig +++ b/src/install/extract_tarball.zig @@ -327,7 +327,7 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD const path_to_use = path2; while (true) { - const dir_to_move = bun.sys.openDirAtWindowsA(bun.toFD(this.temp_dir.fd), bun.span(tmpname), .{ + const dir_to_move = bun.sys.openDirAtWindowsA(.fromStdDir(this.temp_dir), bun.span(tmpname), .{ .can_rename_or_delete = true, .create = false, .iterable = false, @@ -344,14 +344,14 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD return error.InstallFailed; }; - switch (bun.C.moveOpenedFileAt(dir_to_move, bun.toFD(cache_dir.fd), path_to_use, true)) { + switch (bun.C.moveOpenedFileAt(dir_to_move, .fromStdDir(cache_dir), path_to_use, true)) { .err => |err| { if (!did_retry) { switch (err.getErrno()) { .NOTEMPTY, .PERM, .BUSY, .EXIST => { // before we attempt to delete the destination, let's close the source dir. - _ = bun.sys.close(dir_to_move); + dir_to_move.close(); // We tried to move the folder over // but it didn't work! @@ -365,9 +365,9 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD tmpname_bytes[tmpname_len..][0..4].* = .{ 't', 'm', 'p', 0 }; const tempdest = tmpname_bytes[0 .. tmpname_len + 3 :0]; switch (bun.sys.renameat( - bun.toFD(cache_dir.fd), + .fromStdDir(cache_dir), folder_name, - bun.toFD(tmpdir.fd), + .fromStdDir(tmpdir), tempdest, )) { .err => {}, @@ -382,7 +382,7 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD else => {}, } } - _ = bun.sys.close(dir_to_move); + dir_to_move.close(); this.package_manager.log.addErrorFmt( null, logger.Loc.Empty, @@ -393,7 +393,7 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD return error.InstallFailed; }, .result => { - _ = bun.sys.close(dir_to_move); + dir_to_move.close(); }, } @@ -417,9 +417,9 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD } if (bun.sys.renameatConcurrently( - bun.toFD(tmpdir.fd), + .fromStdDir(tmpdir), src, - bun.toFD(cache_dir.fd), + .fromStdDir(cache_dir), folder_name, .{ .move_fallback = true }, ).asErr()) |err| { @@ -449,7 +449,7 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD defer final_dir.close(); // and get the fd path const final_path = bun.getFdPathZ( - final_dir.fd, + .fromStdDir(final_dir), &final_path_buf, ) catch |err| { this.package_manager.log.addErrorFmt( @@ -473,7 +473,7 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD this.package_manager.lockfile.trusted_dependencies.?.contains(@truncate(Semver.String.Builder.stringHash(name))), }) { const json_file, json_buf = bun.sys.File.readFileFrom( - bun.toFD(cache_dir.fd), + bun.FD.fromStdDir(cache_dir), bun.path.joinZ(&[_]string{ folder_name, "package.json" }, .auto), bun.default_allocator, ).unwrap() catch |err| { @@ -536,10 +536,10 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD break :create_index; }; } else { - var index_dir = bun.MakePath.makeOpenPath(cache_dir, name, .{}) catch break :create_index; + var index_dir = bun.FD.fromStdDir(bun.MakePath.makeOpenPath(cache_dir, name, .{}) catch break :create_index); defer index_dir.close(); - bun.sys.symlinkat(final_path, bun.toFD(index_dir), dest_name).unwrap() catch break :create_index; + bun.sys.symlinkat(final_path, index_dir, dest_name).unwrap() catch break :create_index; } } diff --git a/src/install/install.zig b/src/install/install.zig index 43a4f82997..5378ad1a33 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -839,9 +839,7 @@ pub const Task = struct { return; }; - this.data = .{ - .git_clone = bun.toFD(dir.fd), - }; + this.data = .{ .git_clone = .fromStdDir(dir) }; this.status = Status.success; }, .git_checkout => { @@ -851,7 +849,7 @@ pub const Task = struct { this.request.git_checkout.env, manager.log, manager.getCacheDirectory(), - git_checkout.repo_dir.asDir(), + git_checkout.repo_dir.stdDir(), git_checkout.name.slice(), git_checkout.url.slice(), git_checkout.resolved.slice(), @@ -1003,6 +1001,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { const do_progress = kind != .patch; const ProgressT = if (do_progress) *Progress else struct {}; return struct { + /// TODO: Change to bun.FD.Dir cache_dir: std.fs.Dir, cache_dir_subpath: stringZ = "", destination_dir_subpath: stringZ = "", @@ -1132,11 +1131,11 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { } if (comptime bun.Environment.isPosix) { - _ = bun.sys.fstatat(bun.toFD(destination_dir.fd), patch_tag_path).unwrap() catch return false; + _ = bun.sys.fstatat(.fromStdDir(destination_dir), patch_tag_path).unwrap() catch return false; } else { - switch (bun.sys.openat(bun.toFD(destination_dir.fd), patch_tag_path, bun.O.RDONLY, 0)) { + switch (bun.sys.openat(.fromStdDir(destination_dir), patch_tag_path, bun.O.RDONLY, 0)) { .err => return false, - .result => |fd| _ = bun.sys.close(fd), + .result => |fd| fd.close(), } } @@ -1405,7 +1404,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { while (try walker.next()) |entry| { switch (entry.kind) { .directory => { - _ = bun.sys.mkdirat(bun.toFD(destination_dir_.fd), entry.path, 0o755); + _ = bun.sys.mkdirat(.fromStdDir(destination_dir_), entry.path, 0o755); }, .file => { bun.copy(u8, &stackpath, entry.path); @@ -1514,7 +1513,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { state.cached_package_dir = (if (comptime Environment.isWindows) if (method == .symlink) - bun.openDirNoRenamingOrDeletingWindows(bun.toFD(this.cache_dir), this.cache_dir_subpath) + bun.openDirNoRenamingOrDeletingWindows(.fromStdDir(this.cache_dir), this.cache_dir_subpath) else bun.openDir(this.cache_dir, this.cache_dir_subpath) else @@ -1689,7 +1688,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { _ = C.fchmod(outfile.handle, @intCast(stat.mode)); } - bun.copyFileWithState(in_file.handle, outfile.handle, ©_file_state).unwrap() catch |err| { + bun.copyFileWithState(.fromStdFile(in_file), .fromStdFile(outfile), ©_file_state).unwrap() catch |err| { if (do_progress) { progress_.root.end(); progress_.refresh(); @@ -1973,7 +1972,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { var buf2: bun.PathBuffer = undefined; var to_copy_buf2: []u8 = undefined; if (Environment.isPosix) { - const cache_dir_path = try bun.getFdPath(state.cached_package_dir.fd, &buf2); + const cache_dir_path = try bun.FD.fromStdDir(state.cached_package_dir).getFdPath(&buf2); if (cache_dir_path.len > 0 and cache_dir_path[cache_dir_path.len - 1] != std.fs.path.sep) { buf2[cache_dir_path.len] = std.fs.path.sep; to_copy_buf2 = buf2[cache_dir_path.len + 1 ..]; @@ -2111,7 +2110,12 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { pub fn uninstallBeforeInstall(this: *@This(), destination_dir: std.fs.Dir) void { var rand_path_buf: [48]u8 = undefined; const temp_path = std.fmt.bufPrintZ(&rand_path_buf, ".old-{}", .{std.fmt.fmtSliceHexUpper(std.mem.asBytes(&bun.fastRandom()))}) catch unreachable; - switch (bun.sys.renameat(bun.toFD(destination_dir), this.destination_dir_subpath, bun.toFD(destination_dir), temp_path)) { + switch (bun.sys.renameat( + .fromStdDir(destination_dir), + this.destination_dir_subpath, + .fromStdDir(destination_dir), + temp_path, + )) { .err => { // if it fails, that means the directory doesn't exist or was inaccessible }, @@ -2166,7 +2170,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { } return; }; - defer _ = bun.sys.close(bun.toFD(dir.fd)); + defer bun.FD.fromStdDir(dir).close(); dir.deleteTree(basename) catch |err| { if (comptime Environment.isDebug) { @@ -2199,7 +2203,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { switch (Syscall.open(path, bun.O.PATH, @as(u32, 0))) { .err => return true, .result => |fd| { - _ = bun.sys.close(fd); + fd.close(); return false; }, } @@ -2209,7 +2213,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { return true; }, .result => |fd| { - _ = bun.sys.close(fd); + fd.close(); return false; }, } @@ -2217,7 +2221,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { switch (Syscall.open(path, bun.O.PATH, @as(u32, 0))) { .err => return true, .result => |fd| { - _ = bun.sys.close(fd); + fd.close(); return false; }, } @@ -2228,8 +2232,8 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { const WinBinLinkingShim = @import("./windows-shim/BinLinkingShim.zig"); const bin_path = bin_path: { const fd = bun.sys.openatWindows(node_mod_fd, path, bun.O.RDONLY).unwrap() catch return true; - defer _ = bun.sys.close(fd); - const size = fd.asFile().readAll(temp_buffer) catch return true; + defer fd.close(); + const size = fd.stdFile().readAll(temp_buffer) catch return true; const decoded = WinBinLinkingShim.looseDecode(temp_buffer[0..size]) orelse return true; bun.assert(decoded.flags.isValid()); // looseDecode ensures valid flags break :bin_path decoded.bin_path; @@ -2237,7 +2241,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { { const fd = bun.sys.openatWindows(node_mod_fd, bin_path, bun.O.RDONLY).unwrap() catch return true; - _ = bun.sys.close(fd); + fd.close(); } return false; @@ -2306,7 +2310,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { .err => |err_| brk: { var err = err_; if (err.getErrno() == .EXIST) { - _ = bun.sys.rmdirat(bun.toFD(destination_dir), this.destination_dir_subpath); + _ = bun.sys.rmdirat(.fromStdDir(destination_dir), this.destination_dir_subpath); switch (bun.sys.symlinkOrJunction(dest_z, target_z)) { .err => |e| err = e, .result => break :brk, @@ -2325,7 +2329,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { if (subdir != null) dest_dir.close(); } - const dest_dir_path = bun.getFdPath(dest_dir.fd, &dest_buf) catch |err| return Result.fail(err, .linking_dependency, @errorReturnTrace()); + const dest_dir_path = bun.getFdPath(.fromStdDir(dest_dir), &dest_buf) catch |err| return Result.fail(err, .linking_dependency, @errorReturnTrace()); const target = Path.relative(dest_dir_path, to_path); std.posix.symlinkat(target, dest_dir.fd, dest) catch |err| return Result.fail(err, .linking_dependency, null); @@ -2362,9 +2366,9 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { defer buf[subpath_len] = 0; @memcpy(buf[subpath_len + 1 ..][0.."package.json\x00".len], "package.json\x00"); const subpath = buf[0 .. subpath_len + 1 + "package.json".len :0]; - break :package_json_exists Syscall.existsAt(bun.toFD(this.cache_dir.fd), subpath); + break :package_json_exists Syscall.existsAt(.fromStdDir(this.cache_dir), subpath); }, - else => Syscall.directoryExistsAt(this.cache_dir.fd, this.cache_dir_subpath).unwrap() catch false, + else => Syscall.directoryExistsAt(.fromStdDir(this.cache_dir), this.cache_dir_subpath).unwrap() catch false, }; if (exists) manager.setPreinstallState(package_id, manager.lockfile, .done); break :brk !exists; @@ -2372,7 +2376,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { const cache_dir_subpath_without_patch_hash = this.cache_dir_subpath[0 .. std.mem.lastIndexOf(u8, this.cache_dir_subpath, "_patch_hash=") orelse @panic("Patched dependency cache dir subpath does not have the \"_patch_hash=HASH\" suffix. This is a bug, please file a GitHub issue.")]; @memcpy(bun.path.join_buf[0..cache_dir_subpath_without_patch_hash.len], cache_dir_subpath_without_patch_hash); bun.path.join_buf[cache_dir_subpath_without_patch_hash.len] = 0; - const exists = Syscall.directoryExistsAt(this.cache_dir.fd, bun.path.join_buf[0..cache_dir_subpath_without_patch_hash.len :0]).unwrap() catch false; + const exists = Syscall.directoryExistsAt(.fromStdDir(this.cache_dir), bun.path.join_buf[0..cache_dir_subpath_without_patch_hash.len :0]).unwrap() catch false; if (exists) manager.setPreinstallState(package_id, manager.lockfile, .done); break :brk !exists; }, @@ -2389,7 +2393,7 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { // @memcpy(bun.path.join_buf[this.cache_dir_subpath.len .. this.cache_dir_subpath.len + patch_hash_part.len], patch_hash_part); // bun.path.join_buf[this.cache_dir_subpath.len + patch_hash_part.len] = 0; // const patch_cache_dir_subpath = bun.path.join_buf[0 .. this.cache_dir_subpath.len + patch_hash_part.len :0]; - const exists = Syscall.directoryExistsAt(this.cache_dir.fd, this.cache_dir_subpath).unwrap() catch false; + const exists = Syscall.directoryExistsAt(.fromStdDir(this.cache_dir), this.cache_dir_subpath).unwrap() catch false; if (exists) manager.setPreinstallState(package_id, manager.lockfile, .done); return !exists; } @@ -2407,14 +2411,15 @@ pub fn NewPackageInstall(comptime kind: PkgInstallKind) type { const result = this.installImpl(skip_delete, destination_dir, this.getInstallMethod(), resolution_tag); if (result == .fail) return result; - const fd = bun.toFD(destination_dir.fd); + const fd = bun.FD.fromStdDir(destination_dir); const subpath = bun.path.joinZ(&[_][]const u8{ this.destination_dir_subpath, ".bun-patch-tag" }); - const tag_fd = switch (bun.sys.openat(fd, subpath, bun.O.CREAT | bun.O.WRONLY, 0o666)) { - .err => |e| return .fail(bun.errnoToZigErr(e.getErrno()), .patching, @errorReturnTrace()), + const tag_fd = switch (fd.openat(subpath, bun.O.CREAT | bun.O.WRONLY, 0o666)) { .result => |f| f, + .err => |e| return .fail(bun.errnoToZigErr(e.getErrno()), .patching, @errorReturnTrace()), }; - defer _ = bun.sys.close(tag_fd); - if (bun.sys.File.writeAll(.{ .handle = tag_fd }, this.package_version).asErr()) |e| return .fail(bun.errnoToZigErr(e.getErrno()), .patching, @errorReturnTrace()); + defer tag_fd.close(); + if (bun.sys.File.writeAll(.{ .handle = tag_fd }, this.package_version).asErr()) |e| + return .fail(bun.errnoToZigErr(e.getErrno()), .patching, @errorReturnTrace()); return result; }, } @@ -3383,7 +3388,7 @@ pub const PackageManager = struct { this.global_dir = global_dir; this.global_link_dir = try global_dir.makeOpenPath("node_modules", .{}); var buf: bun.PathBuffer = undefined; - const _path = try bun.getFdPath(this.global_link_dir.?.fd, &buf); + const _path = try bun.getFdPath(.fromStdDir(this.global_link_dir.?), &buf); this.global_link_dir_path = try Fs.FileSystem.DirnameStore.instance.append([]const u8, _path); break :brk this.global_link_dir.?; }; @@ -3582,7 +3587,7 @@ pub const PackageManager = struct { return this.temp_dir_ orelse brk: { this.temp_dir_ = this.ensureTemporaryDirectory(); var pathbuf: bun.PathBuffer = undefined; - const temp_dir_path = bun.getFdPathZ(bun.toFD(this.temp_dir_.?), &pathbuf) catch Output.panic("Unable to read temporary directory path", .{}); + const temp_dir_path = bun.getFdPathZ(.fromStdDir(this.temp_dir_.?), &pathbuf) catch Output.panic("Unable to read temporary directory path", .{}); this.temp_dir_path = bun.default_allocator.dupeZ(u8, temp_dir_path) catch bun.outOfMemory(); break :brk this.temp_dir_.?; }; @@ -3695,7 +3700,7 @@ pub const PackageManager = struct { const elapsed = timer.read(); if (elapsed > std.time.ns_per_ms * 100) { var path_buf: bun.PathBuffer = undefined; - const cache_dir_path = bun.getFdPath(cache_directory.fd, &path_buf) catch "it"; + const cache_dir_path = bun.getFdPath(.fromStdDir(cache_directory), &path_buf) catch "it"; Output.prettyErrorln( "warn: Slow filesystem detected. If {s} is a network drive, consider setting $BUN_INSTALL_CACHE_DIR to a local folder.", .{cache_dir_path}, @@ -4039,7 +4044,7 @@ pub const PackageManager = struct { } pub fn isFolderInCache(this: *PackageManager, folder_path: stringZ) bool { - return bun.sys.directoryExistsAt(this.getCacheDirectory(), folder_path).unwrap() catch false; + return bun.sys.directoryExistsAt(.fromStdDir(this.getCacheDirectory()), folder_path).unwrap() catch false; } pub fn pathForCachedNPMPath( @@ -4058,7 +4063,7 @@ pub const PackageManager = struct { cache_path_buf[package_name.len] = std.fs.path.sep; - const cache_dir = bun.toFD(this.getCacheDirectory()); + const cache_dir: bun.FD = .fromStdDir(this.getCacheDirectory()); if (comptime Environment.isWindows) { var path_buf: bun.PathBuffer = undefined; @@ -4069,10 +4074,10 @@ pub const PackageManager = struct { }; } - return bun.sys.readlinkat(cache_dir, cache_path, buf).unwrap() catch |err| { + return cache_dir.readlinkat(cache_path, buf).unwrap() catch |err| { // if we run into an error, delete the symlink // so that we don't repeatedly try to read it - _ = bun.sys.unlinkat(cache_dir, cache_path); + _ = cache_dir.unlinkat(cache_path); return err; }; } @@ -5517,7 +5522,7 @@ pub const PackageManager = struct { this.allocator, this.env, this.log, - repo_fd.asDir(), + repo_fd.stdDir(), alias, this.lockfile.str(&dep.committish), clone_id, @@ -7006,7 +7011,7 @@ pub const PackageManager = struct { manager.allocator, manager.env, manager.log, - task.data.git_clone.asDir(), + task.data.git_clone.stdDir(), dep_name, committish, task.id, @@ -7139,7 +7144,7 @@ pub const PackageManager = struct { log_level: LogLevel = .default, global: bool = false, - global_bin_dir: std.fs.Dir = bun.invalid_fd.asDir(), + global_bin_dir: std.fs.Dir = bun.FD.invalid.stdDir(), explicit_global_directory: string = "", /// destination directory to link bins into // must be a variable due to global installs and bunx @@ -8838,7 +8843,7 @@ pub const PackageManager = struct { const json_buf = try ctx.allocator.alloc(u8, json_stat_size + 64); defer ctx.allocator.free(json_buf); const json_len = try json_file.preadAll(json_buf, 0); - const json_path = try bun.getFdPath(json_file.handle, &package_json_cwd_buf); + const json_path = try bun.getFdPath(.fromStdFile(json_file), &package_json_cwd_buf); const json_source = logger.Source.initPathString(json_path, json_buf[0..json_len]); initializeStore(); const json = try JSON.parsePackageJSONUTF8(&json_source, ctx.log, ctx.allocator); @@ -8903,7 +8908,7 @@ pub const PackageManager = struct { bun.copy(u8, &cwd_buf, fs.top_level_dir); cwd_buf[fs.top_level_dir.len] = 0; fs.top_level_dir = cwd_buf[0..fs.top_level_dir.len :0]; - package_json_cwd = try bun.getFdPath(root_package_json_file.handle, &package_json_cwd_buf); + package_json_cwd = try bun.getFdPath(.fromStdFile(root_package_json_file), &package_json_cwd_buf); const entries_option = try fs.fs.readDirectory(fs.top_level_dir, null, 0, true); @@ -9420,8 +9425,8 @@ pub const PackageManager = struct { var node_modules_path_buf: bun.PathBuffer = undefined; var bin_linker = Bin.Linker{ .bin = package.bin, - .node_modules = bun.toFD(node_modules.fd), - .node_modules_path = bun.getFdPath(node_modules, &node_modules_path_buf) catch |err| { + .node_modules = .fromStdDir(node_modules), + .node_modules_path = bun.getFdPath(.fromStdDir(node_modules), &node_modules_path_buf) catch |err| { if (manager.options.log_level != .silent) { Output.err(err, "failed to link binary", .{}); } @@ -9565,8 +9570,8 @@ pub const PackageManager = struct { var bin_linker = Bin.Linker{ .bin = package.bin, - .node_modules = bun.toFD(node_modules.fd), - .node_modules_path = bun.getFdPath(node_modules, &node_modules_path_buf) catch |err| { + .node_modules = .fromStdDir(node_modules), + .node_modules_path = bun.getFdPath(.fromStdDir(node_modules), &node_modules_path_buf) catch |err| { if (manager.options.log_level != .silent) { Output.err(err, "failed to link binary", .{}); } @@ -11188,7 +11193,7 @@ pub const PackageManager = struct { path, bun.O.RDWR, 0, - ).unwrap()).handle.asFile(); + ).unwrap()).handle.stdFile(); try workspace_package_json_file.pwriteAll(source, 0); std.posix.ftruncate(workspace_package_json_file.handle, source.len) catch {}; @@ -11687,9 +11692,9 @@ pub const PackageManager = struct { const entrypathZ = pathbuf[0..entrypath.len :0]; if (bun.sys.renameatConcurrently( - bun.toFD(destination_dir_.fd), + .fromStdDir(destination_dir_), entrypathZ, - bun.toFD(tmpdir_in_node_modules.fd), + .fromStdDir(tmpdir_in_node_modules), tmpname, .{ .move_fallback = true }, ).asErr()) |e| { @@ -11718,7 +11723,7 @@ pub const PackageManager = struct { pathbuf[entry.path.len] = 0; if (bun.sys.unlinkat( - bun.toFD(destination_dir_.fd), + .fromStdDir(destination_dir_), pathbuf[0..entry.path.len :0], ).asErr()) |e| { Output.prettyError("error: copying file {}", .{e.withPath(entry.path)}); @@ -11731,7 +11736,7 @@ pub const PackageManager = struct { const stat = in_file.stat() catch continue; _ = C.fchmod(outfile.handle, @intCast(stat.mode)); - bun.copyFileWithState(in_file.handle, outfile.handle, ©_file_state).unwrap() catch |err| { + bun.copyFileWithState(.fromStdFile(in_file), .fromStdFile(outfile), ©_file_state).unwrap() catch |err| { Output.prettyError("{s}: copying file {}", .{ @errorName(err), bun.fmt.fmtOSPath(entry.path, .{}) }); Global.crash(); }; @@ -11996,7 +12001,7 @@ pub const PackageManager = struct { var buf2: bun.PathBuffer = undefined; var buf3: bun.PathBuffer = undefined; const old_folder = old_folder: { - const cache_dir_path = switch (bun.sys.getFdPath(bun.toFD(cache_dir.fd), &buf2)) { + const cache_dir_path = switch (bun.sys.getFdPath(.fromStdDir(cache_dir), &buf2)) { .result => |s| s, .err => |e| { Output.err(e, "failed to read from cache", .{}); @@ -12027,9 +12032,9 @@ pub const PackageManager = struct { defer new_folder_handle.close(); if (bun.sys.renameatConcurrently( - bun.toFD(new_folder_handle.fd), + .fromStdDir(new_folder_handle), "node_modules", - bun.toFD(root_node_modules.fd), + .fromStdDir(root_node_modules), random_tempdir, .{ .move_fallback = true }, ).asErr()) |_| break :has_nested_node_modules false; @@ -12062,9 +12067,9 @@ pub const PackageManager = struct { defer new_folder_handle.close(); if (bun.sys.renameatConcurrently( - bun.toFD(new_folder_handle.fd), + .fromStdDir(new_folder_handle), patch_tag, - bun.toFD(root_node_modules.fd), + .fromStdDir(root_node_modules), patch_tag_tmpname, .{ .move_fallback = true }, ).asErr()) |e| { @@ -12086,9 +12091,9 @@ pub const PackageManager = struct { if (has_nested_node_modules) { if (bun.sys.renameatConcurrently( - bun.toFD(root_node_modules.fd), + .fromStdDir(root_node_modules), random_tempdir, - bun.toFD(new_folder_handle.fd), + .fromStdDir(new_folder_handle), "node_modules", .{ .move_fallback = true }, ).asErr()) |e| { @@ -12098,9 +12103,9 @@ pub const PackageManager = struct { if (bun_patch_tag) |patch_tag| { if (bun.sys.renameatConcurrently( - bun.toFD(root_node_modules.fd), + .fromStdDir(root_node_modules), patch_tag_tmpname, - bun.toFD(new_folder_handle.fd), + .fromStdDir(new_folder_handle), patch_tag, .{ .move_fallback = true }, ).asErr()) |e| { @@ -12200,7 +12205,7 @@ pub const PackageManager = struct { const tempfile_name = bun.span(try bun.fs.FileSystem.instance.tmpname("tmp", &tmpname_buf, bun.fastRandom())); const tmpdir = manager.getTemporaryDirectory(); const tmpfd = switch (bun.sys.openat( - bun.toFD(tmpdir.fd), + .fromStdDir(tmpdir), tempfile_name, bun.O.RDWR | bun.O.CREAT, 0o666, @@ -12211,7 +12216,7 @@ pub const PackageManager = struct { Global.crash(); }, }; - defer _ = bun.sys.close(tmpfd); + defer tmpfd.close(); if (bun.sys.File.writeAll(.{ .handle = tmpfd }, patchfile_contents.items).asErr()) |e| { Output.err(e, "failed to write patch to temp file", .{}); @@ -12246,7 +12251,7 @@ pub const PackageManager = struct { // rename to patches dir if (bun.sys.renameatConcurrently( - bun.toFD(tmpdir.fd), + .fromStdDir(tmpdir), tempfile_name, bun.FD.cwd(), path_in_patches_dir, @@ -12276,7 +12281,7 @@ pub const PackageManager = struct { .err => |e| return .{ .err = e }, }; defer { - _ = bun.sys.close(patch_tag_fd); + patch_tag_fd.close(); // we actually need to delete this _ = bun.sys.unlink(patch_tag_path); } @@ -12468,7 +12473,7 @@ pub const PackageManager = struct { noinline fn directoryExistsAtWithoutOpeningDirectories(this: *const NodeModulesFolder, root_node_modules_dir: std.fs.Dir, file_path: [:0]const u8) bool { var path_buf: bun.PathBuffer = undefined; const parts: [2][]const u8 = .{ this.path.items, file_path }; - return bun.sys.directoryExistsAt(bun.toFD(root_node_modules_dir), bun.path.joinZBuf(&path_buf, &parts, .auto)).unwrapOr(false); + return bun.sys.directoryExistsAt(.fromStdDir(root_node_modules_dir), bun.path.joinZBuf(&path_buf, &parts, .auto)).unwrapOr(false); } pub fn directoryExistsAt(this: *const NodeModulesFolder, root_node_modules_dir: std.fs.Dir, file_path: [:0]const u8) bool { @@ -12476,19 +12481,16 @@ pub const PackageManager = struct { return this.directoryExistsAtWithoutOpeningDirectories(root_node_modules_dir, file_path); } - const dir = this.openDir(root_node_modules_dir) catch return false; - defer { - _ = bun.sys.close(bun.toFD(dir)); - } - - return bun.sys.directoryExistsAt(bun.toFD(dir), file_path).unwrapOr(false); + const dir = FD.fromStdDir(this.openDir(root_node_modules_dir) catch return false); + defer dir.close(); + return dir.directoryExistsAt(file_path).unwrapOr(false); } // Since the stack size of these functions are rather large, let's not let them be inlined. noinline fn openFileWithoutOpeningDirectories(this: *const NodeModulesFolder, root_node_modules_dir: std.fs.Dir, file_path: [:0]const u8) bun.sys.Maybe(bun.sys.File) { var path_buf: bun.PathBuffer = undefined; const parts: [2][]const u8 = .{ this.path.items, file_path }; - return bun.sys.File.openat(bun.toFD(root_node_modules_dir), bun.path.joinZBuf(&path_buf, &parts, .auto), bun.O.RDONLY, 0); + return bun.sys.File.openat(.fromStdDir(root_node_modules_dir), bun.path.joinZBuf(&path_buf, &parts, .auto), bun.O.RDONLY, 0); } pub fn readFile(this: *const NodeModulesFolder, root_node_modules_dir: std.fs.Dir, file_path: [:0]const u8, allocator: std.mem.Allocator) !bun.sys.File.ReadToEndResult { @@ -12520,24 +12522,22 @@ pub const PackageManager = struct { } } - const dir = try this.openDir(root_node_modules_dir); - defer { - _ = bun.sys.close(bun.toFD(dir)); - } + const dir = bun.FD.fromStdDir(try this.openDir(root_node_modules_dir)); + defer dir.close(); - return try bun.sys.File.openat(bun.toFD(dir), file_path, bun.O.RDONLY, 0).unwrap(); + return try bun.sys.File.openat(dir, file_path, bun.O.RDONLY, 0).unwrap(); } pub fn openDir(this: *const NodeModulesFolder, root: std.fs.Dir) !std.fs.Dir { if (comptime Environment.isPosix) { - return (try bun.sys.openat(bun.toFD(root), &try std.posix.toPosixPath(this.path.items), bun.O.DIRECTORY, 0).unwrap()).asDir(); + return (try bun.sys.openat(.fromStdDir(root), &try std.posix.toPosixPath(this.path.items), bun.O.DIRECTORY, 0).unwrap()).stdDir(); } - return (try bun.sys.openDirAtWindowsA(bun.toFD(root), this.path.items, .{ + return (try bun.sys.openDirAtWindowsA(.fromStdDir(root), this.path.items, .{ .can_rename_or_delete = false, .create = false, .read_only = false, - }).unwrap()).asDir(); + }).unwrap()).stdDir(); } pub fn makeAndOpenDir(this: *NodeModulesFolder, root: std.fs.Dir) !std.fs.Dir { @@ -12548,11 +12548,11 @@ pub const PackageManager = struct { // TODO: is this `makePath` necessary with `.create = true` below try bun.MakePath.makePath(u8, root, this.path.items); - break :brk (try bun.sys.openDirAtWindowsA(bun.toFD(root), this.path.items, .{ + break :brk (try bun.sys.openDirAtWindowsA(.fromStdDir(root), this.path.items, .{ .can_rename_or_delete = false, .create = true, .read_only = false, - }).unwrap()).asDir(); + }).unwrap()).stdDir(); }; return out; } @@ -12712,7 +12712,7 @@ pub const PackageManager = struct { .extern_string_buf = lockfile.buffers.extern_strings.items, .seen = &this.seen_bin_links, .node_modules_path = this.node_modules.path.items, - .node_modules = bun.toFD(destination_dir), + .node_modules = .fromStdDir(destination_dir), .abs_target_buf = link_target_buf, .abs_dest_buf = link_dest_buf, .rel_buf = link_rel_buf, @@ -13654,7 +13654,7 @@ pub const PackageManager = struct { }; if (!Singleton.node_modules_is_ok) { if (!Environment.isWindows) { - const stat = bun.sys.fstat(bun.toFD(lazy_package_dir.getDir() catch |err| { + const stat = bun.sys.fstat(.fromStdDir(lazy_package_dir.getDir() catch |err| { Output.err("EACCES", "Permission denied while installing {s}", .{ this.names[package_id].slice(this.lockfile.buffers.string_bytes.items), }); @@ -14101,10 +14101,10 @@ pub const PackageManager = struct { // we want to check lazily though // no need to download packages you've already installed!! var new_node_modules = false; - const cwd = std.fs.cwd(); + const cwd = bun.FD.cwd(); const node_modules_folder = brk: { // Attempt to open the existing node_modules folder - switch (bun.sys.openatOSPath(bun.toFD(cwd), bun.OSPathLiteral("node_modules"), bun.O.DIRECTORY | bun.O.RDONLY, 0o755)) { + switch (bun.sys.openatOSPath(cwd, bun.OSPathLiteral("node_modules"), bun.O.DIRECTORY | bun.O.RDONLY, 0o755)) { .result => |fd| break :brk std.fs.Dir{ .fd = fd.cast() }, .err => {}, } @@ -14118,7 +14118,7 @@ pub const PackageManager = struct { Global.crash(); } } - break :brk bun.openDir(cwd, "node_modules") catch |err| { + break :brk bun.openDir(cwd.stdDir(), "node_modules") catch |err| { Output.err(err, "could not open the \"node_modules\" directory", .{}); Global.crash(); }; @@ -14456,7 +14456,7 @@ pub const PackageManager = struct { pub fn setupGlobalDir(manager: *PackageManager, ctx: Command.Context) !void { manager.options.global_bin_dir = try Options.openGlobalBinDir(ctx.install); var out_buffer: bun.PathBuffer = undefined; - const result = try bun.getFdPathZ(manager.options.global_bin_dir.fd, &out_buffer); + const result = try bun.getFdPathZ(.fromStdDir(manager.options.global_bin_dir), &out_buffer); const path = try FileSystem.instance.dirname_store.append([:0]u8, result); manager.options.bin_path = path.ptr[0..path.len :0]; } @@ -15710,7 +15710,7 @@ pub const bun_install_js_bindings = struct { // as long as we aren't migration from `package-lock.json`, leaving this undefined is okay const manager = globalObject.bunVM().transpiler.resolver.getPackageManager(); - const load_result: Lockfile.LoadResult = lockfile.loadFromDir(bun.toFD(dir), manager, allocator, &log, true); + const load_result: Lockfile.LoadResult = lockfile.loadFromDir(.fromStdDir(dir), manager, allocator, &log, true); switch (load_result) { .err => |err| { diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index f2c84a7c7d..785537ed25 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -2556,7 +2556,7 @@ pub fn saveToDisk(this: *Lockfile, load_result: *const LoadResult, options: *con else std.fmt.bufPrintZ(&tmpname_buf, ".lockb-{s}.tmp", .{std.fmt.fmtSliceHexLower(&base64_bytes)}) catch unreachable; - const file = switch (File.openat(std.fs.cwd(), tmpname, bun.O.CREAT | bun.O.WRONLY, 0o777)) { + const file = switch (File.openat(.cwd(), tmpname, bun.O.CREAT | bun.O.WRONLY, 0o777)) { .err => |err| { Output.err(err, "failed to create temporary file to save lockfile", .{}); Global.crash(); diff --git a/src/install/npm.zig b/src/install/npm.zig index 87160eaf94..75370d3493 100644 --- a/src/install/npm.zig +++ b/src/install/npm.zig @@ -1077,9 +1077,10 @@ pub const PackageManifest = struct { scope: *const Registry.Scope, tmp_path: [:0]const u8, tmpdir: std.fs.Dir, - cache_dir: std.fs.Dir, + cache_dir_std: std.fs.Dir, outpath: [:0]const u8, ) !void { + const cache_dir: bun.FD = .fromStdDir(cache_dir_std); // 64 KB sounds like a lot but when you consider that this is only about 6 levels deep in the stack, it's not that much. var stack_fallback = std.heap.stackFallback(64 * 1024, bun.default_allocator); @@ -1148,7 +1149,7 @@ pub const PackageManifest = struct { } break :brk try bun.sys.File.openat( - tmpdir, + .fromStdDir(tmpdir), path_to_use_for_opening_file, flags | bun.O.CREAT | bun.O.TRUNC, if (Environment.isPosix) 0o664 else 0, @@ -1172,24 +1173,24 @@ pub const PackageManifest = struct { } else if (Environment.isLinux and is_using_o_tmpfile) { defer file.close(); // Attempt #1. - bun.sys.linkatTmpfile(file.handle, bun.toFD(cache_dir), outpath).unwrap() catch { + bun.sys.linkatTmpfile(file.handle, cache_dir, outpath).unwrap() catch { // Attempt #2: the file may already exist. Let's unlink and try again. - bun.sys.unlinkat(bun.toFD(cache_dir), outpath).unwrap() catch {}; - try bun.sys.linkatTmpfile(file.handle, bun.toFD(cache_dir), outpath).unwrap(); + bun.sys.unlinkat(cache_dir, outpath).unwrap() catch {}; + try bun.sys.linkatTmpfile(file.handle, cache_dir, outpath).unwrap(); // There is no attempt #3. This is a cache, so it's not essential. }; } else { defer file.close(); // Attempt #1. Rename the file. - const rc = bun.sys.renameat(bun.toFD(tmpdir), tmp_path, bun.toFD(cache_dir), outpath); + const rc = bun.sys.renameat(.fromStdDir(tmpdir), tmp_path, cache_dir, outpath); switch (rc) { .err => |err| { // Fallback path: atomically swap from /*.npm -> /*.npm, then unlink the temporary file. defer { // If atomically swapping fails, then we should still unlink the temporary file as a courtesy. - bun.sys.unlinkat(bun.toFD(tmpdir), tmp_path).unwrap() catch {}; + bun.sys.unlinkat(.fromStdDir(tmpdir), tmp_path).unwrap() catch {}; } if (switch (err.getErrno()) { @@ -1198,9 +1199,15 @@ pub const PackageManifest = struct { }) { // Atomically swap the old file with the new file. - try bun.sys.renameat2(bun.toFD(tmpdir.fd), tmp_path, bun.toFD(cache_dir.fd), outpath, .{ - .exchange = true, - }).unwrap(); + try bun.sys.renameat2( + .fromStdDir(tmpdir), + tmp_path, + cache_dir, + outpath, + .{ + .exchange = true, + }, + ).unwrap(); // Success. return; @@ -1283,7 +1290,7 @@ pub const PackageManifest = struct { pub fn loadByFileID(allocator: std.mem.Allocator, scope: *const Registry.Scope, cache_dir: std.fs.Dir, file_id: u64) !?PackageManifest { var file_path_buf: [512 + 64]u8 = undefined; const file_name = try manifestFileName(&file_path_buf, file_id, scope); - const cache_file = File.openat(cache_dir, file_name, bun.O.RDONLY, 0).unwrap() catch return null; + const cache_file = File.openat(.fromStdDir(cache_dir), file_name, bun.O.RDONLY, 0).unwrap() catch return null; defer cache_file.close(); delete: { @@ -1291,7 +1298,7 @@ pub const PackageManifest = struct { } // delete the outdated/invalid manifest - try bun.sys.unlinkat(bun.toFD(cache_dir), file_name).unwrap(); + try bun.sys.unlinkat(.fromStdDir(cache_dir), file_name).unwrap(); return null; } diff --git a/src/install/patch_install.zig b/src/install/patch_install.zig index 1688b99139..17b971b26f 100644 --- a/src/install/patch_install.zig +++ b/src/install/patch_install.zig @@ -350,7 +350,7 @@ pub const PatchTask = struct { { const patch_pkg_dir = switch (bun.sys.openat( - bun.toFD(system_tmpdir.fd), + .fromStdDir(system_tmpdir), tempdir_name, bun.O.RDONLY | bun.O.DIRECTORY, 0, @@ -363,7 +363,7 @@ pub const PatchTask = struct { .{resolution_label}, ), }; - defer _ = bun.sys.close(patch_pkg_dir); + defer patch_pkg_dir.close(); // 4. apply patch if (patchfile.apply(this.manager.allocator, patch_pkg_dir)) |e| { @@ -397,7 +397,7 @@ pub const PatchTask = struct { ); }, }; - _ = bun.sys.close(buntagfd); + buntagfd.close(); } // 6. rename to cache dir @@ -412,9 +412,9 @@ pub const PatchTask = struct { ); if (bun.sys.renameatConcurrently( - bun.toFD(system_tmpdir.fd), + .fromStdDir(system_tmpdir), path_in_tmpdir, - bun.toFD(this.callback.apply.cache_dir.fd), + .fromStdDir(this.callback.apply.cache_dir), this.callback.apply.cache_dir_subpath, .{ .move_fallback = true }, ).asErr()) |e| return try log.addErrorFmtOpts( @@ -490,7 +490,7 @@ pub const PatchTask = struct { }, .result => |fd| fd, }; - defer _ = bun.sys.close(fd); + defer fd.close(); var hasher = bun.Wyhash11.init(0); diff --git a/src/install/repository.zig b/src/install/repository.zig index 3ca3c3c79d..625d49abab 100644 --- a/src/install/repository.zig +++ b/src/install/repository.zig @@ -582,7 +582,7 @@ pub const Repository = extern struct { "-c core.longpaths=true", "--quiet", "--no-checkout", - try bun.getFdPath(repo_dir.fd, &final_path_buf), + try bun.getFdPath(.fromStdDir(repo_dir), &final_path_buf), target, }) catch |err| { log.addErrorFmt( diff --git a/src/install/windows-shim/bun_shim_impl.zig b/src/install/windows-shim/bun_shim_impl.zig index 31205915e6..956fa9e9ac 100644 --- a/src/install/windows-shim/bun_shim_impl.zig +++ b/src/install/windows-shim/bun_shim_impl.zig @@ -771,9 +771,9 @@ fn launcher(comptime mode: LauncherMode, bun_ctx: anytype) mode.RetType() { .cbReserved2 = 0, .lpReserved2 = null, // The standard handles outside of standalone may be tampered with. - .hStdInput = if (is_standalone) ProcessParameters.hStdInput else bun.win32.STDIN_FD.cast(), - .hStdOutput = if (is_standalone) ProcessParameters.hStdOutput else bun.win32.STDOUT_FD.cast(), - .hStdError = if (is_standalone) ProcessParameters.hStdError else bun.win32.STDERR_FD.cast(), + .hStdInput = if (is_standalone) ProcessParameters.hStdInput else bun.FD.stdin().native(), + .hStdOutput = if (is_standalone) ProcessParameters.hStdOutput else bun.FD.stdout().native(), + .hStdError = if (is_standalone) ProcessParameters.hStdError else bun.FD.stderr().native(), }; inline for (.{ 0, 1 }) |attempt_number| iteration: { diff --git a/src/io/PipeReader.zig b/src/io/PipeReader.zig index 8111df38f7..35ead12dbe 100644 --- a/src/io/PipeReader.zig +++ b/src/io/PipeReader.zig @@ -917,7 +917,7 @@ pub const WindowsBufferedReader = struct { fn onFileRead(fs: *uv.fs_t) callconv(.C) void { const result = fs.result; const nread_int = result.int(); - bun.sys.syslog("onFileRead({}) = {d}", .{ bun.toFD(fs.file.fd), nread_int }); + bun.sys.syslog("onFileRead({}) = {d}", .{ bun.FD.fromUV(fs.file.fd), nread_int }); if (nread_int == uv.UV_ECANCELED) { fs.deinit(); return; diff --git a/src/io/PipeWriter.zig b/src/io/PipeWriter.zig index 0d81d90468..2976f9b621 100644 --- a/src/io/PipeWriter.zig +++ b/src/io/PipeWriter.zig @@ -1382,10 +1382,10 @@ pub fn WindowsStreamingWriter( return .{ .err = bun.sys.Error.oom }; }; const initial_len = remain.len; - const fd = bun.toFD(this.source.?.sync_file.file); + const fd: bun.FD = .fromUV(this.source.?.sync_file.file); while (remain.len > 0) { - switch (bun.sys.write(fd, remain)) { + switch (fd.write(remain)) { .err => |err| { return .{ .err = err }; }, diff --git a/src/io/io.zig b/src/io/io.zig index 12f14b11ba..0dfabbbec1 100644 --- a/src/io/io.zig +++ b/src/io/io.zig @@ -16,7 +16,7 @@ pub const Source = @import("./source.zig").Source; pub const Loop = struct { pending: Request.Queue = .{}, waker: bun.Async.Waker, - epoll_fd: if (Environment.isLinux) bun.FileDescriptor else u0 = if (Environment.isLinux) .zero else 0, + epoll_fd: if (Environment.isLinux) bun.FileDescriptor else void = if (Environment.isLinux) .invalid, cached_now: posix.timespec = .{ .nsec = 0, @@ -31,7 +31,7 @@ pub const Loop = struct { .waker = bun.Async.Waker.init() catch @panic("failed to initialize waker"), }; if (comptime Environment.isLinux) { - loop.epoll_fd = bun.toFD(std.posix.epoll_create1(std.os.linux.EPOLL.CLOEXEC | 0) catch @panic("Failed to create epoll file descriptor")); + loop.epoll_fd = .fromNative(std.posix.epoll_create1(std.os.linux.EPOLL.CLOEXEC | 0) catch @panic("Failed to create epoll file descriptor")); { var epoll = std.mem.zeroes(std.os.linux.epoll_event); @@ -526,7 +526,7 @@ pub const Poll = struct { kqueue_event.* = switch (comptime action) { .readable => .{ - .ident = @as(u64, @intCast(fd.int())), + .ident = @as(u64, @intCast(fd.native())), .filter = std.posix.system.EVFILT.READ, .data = 0, .fflags = 0, @@ -535,7 +535,7 @@ pub const Poll = struct { .ext = .{ generation_number_monotonic, 0 }, }, .writable => .{ - .ident = @as(u64, @intCast(fd.int())), + .ident = @as(u64, @intCast(fd.native())), .filter = std.posix.system.EVFILT.WRITE, .data = 0, .fflags = 0, @@ -544,7 +544,7 @@ pub const Poll = struct { .ext = .{ generation_number_monotonic, 0 }, }, .cancel => if (poll.flags.contains(.poll_readable)) .{ - .ident = @as(u64, @intCast(fd.int())), + .ident = @as(u64, @intCast(fd.native())), .filter = std.posix.system.EVFILT.READ, .data = 0, .fflags = 0, @@ -552,7 +552,7 @@ pub const Poll = struct { .flags = std.c.EV.DELETE, .ext = .{ poll.generation_number, 0 }, } else if (poll.flags.contains(.poll_writable)) .{ - .ident = @as(u64, @intCast(fd.int())), + .ident = @as(u64, @intCast(fd.native())), .filter = std.posix.system.EVFILT.WRITE, .data = 0, .fflags = 0, diff --git a/src/io/pipes.zig b/src/io/pipes.zig index f6fb46053a..835734dc8e 100644 --- a/src/io/pipes.zig +++ b/src/io/pipes.zig @@ -59,11 +59,11 @@ pub const PollOrFd = union(enum) { //TODO: We should make this call compatible using bun.FileDescriptor if (Environment.isWindows) { - bun.Async.Closer.close(bun.uvfdcast(fd), bun.windows.libuv.Loop.get()); + bun.Async.Closer.close(fd, bun.windows.libuv.Loop.get()); } else if (close_async and close_fd) { bun.Async.Closer.close(fd, {}); } else { - if (close_fd) _ = bun.sys.close(fd); + if (close_fd) _ = fd.closeAllowingBadFileDescriptor(null); } if (comptime @TypeOf(onCloseFn) != void) onCloseFn(@alignCast(@ptrCast(ctx.?))); diff --git a/src/io/source.zig b/src/io/source.zig index 99447a44b8..2b1a9b2620 100644 --- a/src/io/source.zig +++ b/src/io/source.zig @@ -58,7 +58,7 @@ pub const Source = union(enum) { return switch (this) { .pipe => |pipe| pipe.fd(), .tty => |tty| tty.fd(), - .sync_file, .file => |file| bun.FDImpl.fromUV(file.file).encode(), + .sync_file, .file => |file| .fromUV(file.file), }; } @@ -129,7 +129,7 @@ pub const Source = union(enum) { pub fn openTty(loop: *uv.Loop, fd: bun.FileDescriptor) bun.JSC.Maybe(*Source.Tty) { log("openTTY (fd = {})", .{fd}); - const uv_fd = bun.uvfdcast(fd); + const uv_fd = fd.uv(); if (uv_fd == 0) { if (!stdin_tty_init) { @@ -151,17 +151,17 @@ pub const Source = union(enum) { } pub fn openFile(fd: bun.FileDescriptor) *Source.File { - bun.assert(fd.isValid() and bun.uvfdcast(fd) != -1); + bun.assert(fd.isValid() and fd.uv() != -1); log("openFile (fd = {})", .{fd}); const file = bun.default_allocator.create(Source.File) catch bun.outOfMemory(); file.* = std.mem.zeroes(Source.File); - file.file = bun.uvfdcast(fd); + file.file = fd.uv(); return file; } pub fn open(loop: *uv.Loop, fd: bun.FileDescriptor) bun.JSC.Maybe(Source) { - const rc = bun.windows.libuv.uv_guess_handle(bun.uvfdcast(fd)); + const rc = bun.windows.libuv.uv_guess_handle(fd.uv()); log("open(fd: {}, type: {d})", .{ fd, @tagName(rc) }); switch (rc) { @@ -224,7 +224,7 @@ pub const Source = union(enum) { }; export fn Source__setRawModeStdin(raw: bool) c_int { - const tty = switch (Source.openTty(bun.JSC.VirtualMachine.get().uvLoop(), bun.STDIN_FD)) { + const tty = switch (Source.openTty(bun.JSC.VirtualMachine.get().uvLoop(), .stdin())) { .result => |tty| tty, .err => |e| return e.errno, }; diff --git a/src/libarchive/libarchive.zig b/src/libarchive/libarchive.zig index d64b2284fc..0886851822 100644 --- a/src/libarchive/libarchive.zig +++ b/src/libarchive/libarchive.zig @@ -213,12 +213,13 @@ pub const Archiver = struct { contents: MutableString, filename_hash: u64 = 0, found: bool = false, - fd: FileDescriptorType = .zero, + fd: FileDescriptorType, + pub fn init(filepath: bun.OSPathSlice, estimated_size: usize, allocator: std.mem.Allocator) !Plucker { return Plucker{ .contents = try MutableString.init(allocator, estimated_size), .filename_hash = bun.hash(std.mem.sliceAsBytes(filepath)), - .fd = .zero, + .fd = .invalid, .found = false, }; } @@ -450,11 +451,11 @@ pub const Archiver = struct { .sym_link => { const link_target = entry.symlink(); if (Environment.isPosix) { - bun.sys.symlinkat(link_target, bun.toFD(dir_fd), path).unwrap() catch |err| brk: { + bun.sys.symlinkat(link_target, .fromNative(dir_fd), path).unwrap() catch |err| brk: { switch (err) { error.EPERM, error.ENOENT => { dir.makePath(std.fs.path.dirname(path_slice) orelse return err) catch {}; - break :brk try bun.sys.symlinkat(link_target, bun.toFD(dir_fd), path).unwrap(); + break :brk try bun.sys.symlinkat(link_target, .fromNative(dir_fd), path).unwrap(); }, else => return err, } @@ -470,41 +471,39 @@ pub const Archiver = struct { // we simplify and turn it into `entry.mode || 0o666` because we aren't accepting a umask or fmask option. const mode: bun.Mode = if (comptime Environment.isWindows) 0 else @intCast(entry.perm() | 0o666); - const file_handle_native = brk: { - if (Environment.isWindows) { - const flags = bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC; - switch (bun.sys.openatWindows(bun.toFD(dir_fd), path, flags, 0)) { - .result => |fd| break :brk fd, - .err => |e| switch (e.errno) { - @intFromEnum(bun.C.E.PERM), @intFromEnum(bun.C.E.NOENT) => { - bun.MakePath.makePath(u16, dir, bun.Dirname.dirname(u16, path_slice) orelse return bun.errnoToZigErr(e.errno)) catch {}; - break :brk try bun.sys.openatWindows(bun.toFD(dir_fd), path, flags, 0).unwrap(); - }, - else => { - return bun.errnoToZigErr(e.errno); - }, + const flags = bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC; + const file_handle_native: bun.FD = if (Environment.isWindows) + switch (bun.sys.openatWindows(.fromNative(dir_fd), path, flags, 0)) { + .result => |fd| fd, + .err => |e| switch (e.errno) { + @intFromEnum(bun.C.E.PERM), + @intFromEnum(bun.C.E.NOENT), + => brk: { + bun.MakePath.makePath(u16, dir, bun.Dirname.dirname(u16, path_slice) orelse return bun.errnoToZigErr(e.errno)) catch {}; + break :brk try bun.sys.openatWindows(.fromNative(dir_fd), path, flags, 0).unwrap(); }, - } - } else { - break :brk (dir.createFileZ(path, .{ .truncate = true, .mode = mode }) catch |err| { - switch (err) { - error.AccessDenied, error.FileNotFound => { - dir.makePath(std.fs.path.dirname(path_slice) orelse return err) catch {}; - break :brk (try dir.createFileZ(path, .{ - .truncate = true, - .mode = mode, - })).handle; - }, - else => { - return err; - }, - } - }).handle; + else => return bun.errnoToZigErr(e.errno), + }, } - }; + else + .fromStdFile(dir.createFileZ(path, .{ + .truncate = true, + .mode = mode, + }) catch |err| + switch (err) { + error.AccessDenied, error.FileNotFound => brk: { + dir.makePath(std.fs.path.dirname(path_slice) orelse return err) catch {}; + break :brk try dir.createFileZ(path, .{ + .truncate = true, + .mode = mode, + }); + }, + else => return err, + }); + const file_handle = brk: { - errdefer _ = bun.sys.close(file_handle_native); - break :brk try bun.toLibUVOwnedFD(file_handle_native); + errdefer _ = file_handle_native.close(); + break :brk try file_handle_native.makeLibUVOwned(); }; var plucked_file = false; @@ -522,7 +521,7 @@ pub const Archiver = struct { // But this approach does not actually solve the problem, it just // defers the close to a different thread. And since we are already // on a worker thread, that doesn't help us. - _ = bun.sys.close(file_handle); + file_handle.close(); }; const size: usize = @intCast(@max(entry.size(), 0)); @@ -567,7 +566,7 @@ pub const Archiver = struct { var retries_remaining: u8 = 5; possibly_retry: while (retries_remaining != 0) : (retries_remaining -= 1) { - switch (archive.readDataIntoFd(bun.uvfdcast(file_handle))) { + switch (archive.readDataIntoFd(file_handle.uv())) { .eof => break :loop, .ok => break :possibly_retry, .retry => { diff --git a/src/linker.zig b/src/linker.zig index 6997bc3e8b..663b020a77 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -101,7 +101,7 @@ pub const Linker = struct { file_path: Fs.Path, fd: ?FileDescriptorType, ) !Fs.FileSystem.RealFS.ModKey { - var file: std.fs.File = if (fd) |_fd| _fd.asFile() else try std.fs.openFileAbsolute(file_path.text, .{ .mode = .read_only }); + var file: std.fs.File = if (fd) |_fd| _fd.stdFile() else try std.fs.openFileAbsolute(file_path.text, .{ .mode = .read_only }); Fs.FileSystem.setMaxFd(file.handle); const modkey = try Fs.FileSystem.RealFS.ModKey.generate(&this.fs.fs, file_path.text, file); diff --git a/src/linux_c.zig b/src/linux_c.zig index 52588080ed..475ac24f0c 100644 --- a/src/linux_c.zig +++ b/src/linux_c.zig @@ -547,7 +547,7 @@ pub const linux_fs = bun.c; /// /// Support for FICLONE is dependent on the filesystem driver. pub fn ioctl_ficlone(dest_fd: bun.FileDescriptor, srcfd: bun.FileDescriptor) usize { - return std.os.linux.ioctl(dest_fd.cast(), bun.c.FICLONE, @intCast(srcfd.int())); + return std.os.linux.ioctl(dest_fd.cast(), bun.c.FICLONE, @intCast(srcfd.native())); } pub const RWFFlagSupport = enum(u8) { diff --git a/src/options.zig b/src/options.zig index 5b4b76cc18..0eacf1611e 100644 --- a/src/options.zig +++ b/src/options.zig @@ -2032,7 +2032,7 @@ pub const BundleOptions = struct { if (opts.write and opts.output_dir.len > 0) { opts.output_dir_handle = try openOutputDir(opts.output_dir); - opts.output_dir = try fs.getFdPath(bun.toFD(opts.output_dir_handle.?.fd)); + opts.output_dir = try fs.getFdPath(.fromStdDir(opts.output_dir_handle.?)); } opts.polyfill_node_globals = opts.target == .browser; @@ -2351,12 +2351,6 @@ pub const RouteConfig = struct { extensions: []const string = &[_]string{}, routes_enabled: bool = false, - static_dir: string = "", - static_dir_handle: ?std.fs.Dir = null, - static_dir_enabled: bool = false, - single_page_app_routing: bool = false, - single_page_app_fd: StoredFileDescriptorType = .zero, - pub fn toAPI(this: *const RouteConfig) Api.LoadedRouteConfig { return .{ .asset_prefix = this.asset_prefix_path, diff --git a/src/output.zig b/src/output.zig index b9b3e429ab..cff81f62e6 100644 --- a/src/output.zig +++ b/src/output.zig @@ -188,11 +188,14 @@ pub const Source = struct { const stdout = std.os.windows.GetStdHandle(std.os.windows.STD_OUTPUT_HANDLE) catch w.INVALID_HANDLE_VALUE; const stderr = std.os.windows.GetStdHandle(std.os.windows.STD_ERROR_HANDLE) catch w.INVALID_HANDLE_VALUE; - bun.win32.STDERR_FD = if (stderr != std.os.windows.INVALID_HANDLE_VALUE) bun.toFD(stderr) else bun.invalid_fd; - bun.win32.STDOUT_FD = if (stdout != std.os.windows.INVALID_HANDLE_VALUE) bun.toFD(stdout) else bun.invalid_fd; - bun.win32.STDIN_FD = if (stdin != std.os.windows.INVALID_HANDLE_VALUE) bun.toFD(stdin) else bun.invalid_fd; + const fd_internals = @import("fd.zig"); + const INVALID_HANDLE_VALUE = std.os.windows.INVALID_HANDLE_VALUE; + fd_internals.windows_cached_stderr = if (stderr != INVALID_HANDLE_VALUE) .fromSystem(stderr) else .invalid; + fd_internals.windows_cached_stdout = if (stdout != INVALID_HANDLE_VALUE) .fromSystem(stdout) else .invalid; + fd_internals.windows_cached_stdin = if (stdin != INVALID_HANDLE_VALUE) .fromSystem(stdin) else .invalid; + if (Environment.isDebug) fd_internals.windows_cached_fd_set = true; - buffered_stdin.unbuffered_reader.context.handle = bun.win32.STDIN_FD; + buffered_stdin.unbuffered_reader.context.handle = .stdin(); // https://learn.microsoft.com/en-us/windows/console/setconsoleoutputcp const CP_UTF8 = 65001; @@ -1201,13 +1204,13 @@ pub fn initScopedDebugWriterAtStartup() void { const path_fmt = std.mem.replaceOwned(u8, bun.default_allocator, path, "{pid}", pid) catch @panic("failed to allocate path"); defer bun.default_allocator.free(path_fmt); - const fd = std.fs.cwd().createFile(path_fmt, .{ + const fd: bun.FD = .fromStdFile(std.fs.cwd().createFile(path_fmt, .{ .mode = if (Environment.isPosix) 0o644 else 0, }) catch |open_err| { panic("Failed to open file for debug output: {s} ({s})", .{ @errorName(open_err), path }); - }; - _ = bun.sys.ftruncate(bun.toFD(fd), 0); // windows - ScopedDebugWriter.scoped_file_writer = File.from(fd).quietWriter(); + }); + _ = fd.truncate(0); // windows + ScopedDebugWriter.scoped_file_writer = fd.quietWriter(); return; } } @@ -1232,8 +1235,6 @@ pub inline fn errFmt(formatter: anytype) void { return errGeneric("{}", .{formatter}); } -/// This struct is a workaround a Windows terminal bug. -/// TODO: when https://github.com/microsoft/terminal/issues/16606 is resolved, revert this commit. pub var buffered_stdin = std.io.BufferedReader(4096, File.Reader){ - .unbuffered_reader = File.Reader{ .context = .{ .handle = if (Environment.isWindows) undefined else bun.toFD(0) } }, + .unbuffered_reader = .{ .context = .{ .handle = if (Environment.isWindows) undefined else .stdin() } }, }; diff --git a/src/patch.zig b/src/patch.zig index f00e929736..a25e1c01b7 100644 --- a/src/patch.zig +++ b/src/patch.zig @@ -120,7 +120,7 @@ pub const PatchFile = struct { .result => |fd| fd, .err => |e| return e.withoutPath(), }; - defer _ = bun.sys.close(newfile_fd); + defer newfile_fd.close(); const hunk = part.file_creation.hunk orelse { continue; @@ -192,7 +192,7 @@ pub const PatchFile = struct { .err => |e| return e.withoutPath(), .result => |f| f, }; - defer _ = bun.sys.close(fd); + defer fd.close(); if (bun.sys.fchmod(fd, newmode.toBunMode()).asErr()) |e| { return e.withoutPath(); } @@ -242,7 +242,7 @@ pub const PatchFile = struct { // to use the arena const use_arena: bool = stat.size <= PAGE_SIZE; const file_alloc = if (use_arena) arena.allocator() else bun.default_allocator; - const filebuf = patch_dir.asDir().readFileAlloc(file_alloc, file_path, 1024 * 1024 * 1024 * 4) catch return .{ .err = bun.sys.Error.fromCode(.INVAL, .read).withPath(file_path) }; + const filebuf = patch_dir.stdDir().readFileAlloc(file_alloc, file_path, 1024 * 1024 * 1024 * 4) catch return .{ .err = bun.sys.Error.fromCode(.INVAL, .read).withPath(file_path) }; defer file_alloc.free(filebuf); var file_line_count: usize = 0; @@ -323,9 +323,7 @@ pub const PatchFile = struct { .err => |e| return .{ .err = e.withPath(file_path) }, .result => |fd| fd, }; - defer { - _ = bun.sys.close(file_fd); - } + defer file_fd.close(); const contents = std.mem.join(bun.default_allocator, "\n", lines.items) catch bun.outOfMemory(); defer bun.default_allocator.free(contents); @@ -1121,8 +1119,9 @@ pub const TestingAPIs = struct { pub fn deinit(this: *ApplyArgs) void { this.patchfile_txt.deinit(); this.patchfile.deinit(bun.default_allocator); - if (bun.FileDescriptor.cwd().eq(this.dirfd)) { - _ = bun.sys.close(this.dirfd); + // TODO: HAVE @zackradisic REVIEW THIS DIFF + if (bun.FileDescriptor.cwd() != this.dirfd) { + this.dirfd.close(); } } }; @@ -1193,8 +1192,9 @@ pub const TestingAPIs = struct { const patchfile_src = patchfile_bunstr.toUTF8(bun.default_allocator); const patch_file = parsePatchFile(patchfile_src.slice()) catch |e| { - if (bun.FileDescriptor.cwd().eq(dir_fd)) { - _ = bun.sys.close(dir_fd); + // TODO: HAVE @zackradisic REVIEW THIS DIFF + if (bun.FileDescriptor.cwd() != dir_fd) { + dir_fd.close(); } patchfile_src.deinit(); diff --git a/src/resolver/dir_info.zig b/src/resolver/dir_info.zig index 5ff0767b54..999474408d 100644 --- a/src/resolver/dir_info.zig +++ b/src/resolver/dir_info.zig @@ -77,15 +77,10 @@ pub fn hasParentPackage(this: *const DirInfo) bool { } pub fn getFileDescriptor(dirinfo: *const DirInfo) StoredFileDescriptorType { - if (!FeatureFlags.store_file_descriptors) { - return .zero; - } - - if (dirinfo.getEntries(0)) |entries| { - return entries.fd; - } else { - return .zero; - } + if (FeatureFlags.store_file_descriptors) + if (dirinfo.getEntries(0)) |entries| + return entries.fd; + return .invalid; } pub fn getEntries(dirinfo: *const DirInfo, generation: bun.Generation) ?*Fs.FileSystem.DirEntry { diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index b8ca0c66aa..b39fd8d537 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -9,7 +9,7 @@ const FeatureFlags = bun.FeatureFlags; const PathString = bun.PathString; const stringZ = bun.stringZ; const default_allocator = bun.default_allocator; -const StoredFileDescriptorType = bun.StoredFileDescriptorType; +const FD = bun.FD; const C = bun.C; const ast = @import("../import_record.zig"); const logger = bun.logger; @@ -211,8 +211,8 @@ pub const Result = struct { debug_meta: ?DebugMeta = null, - dirname_fd: StoredFileDescriptorType = .zero, - file_fd: StoredFileDescriptorType = .zero, + dirname_fd: FD = .invalid, + file_fd: FD = .invalid, import_kind: ast.ImportKind = undefined, pub const Union = union(enum) { @@ -317,7 +317,7 @@ pub const DirEntryResolveQueueItem = struct { result: allocators.Result, unsafe_path: string, safe_path: string = "", - fd: StoredFileDescriptorType = .zero, + fd: FD = .invalid, }; pub const DebugLogs = struct { @@ -373,8 +373,8 @@ pub const DebugLogs = struct { pub const MatchResult = struct { path_pair: PathPair, - dirname_fd: StoredFileDescriptorType = .zero, - file_fd: StoredFileDescriptorType = .zero, + dirname_fd: FD = .invalid, + file_fd: FD = .invalid, is_node_module: bool = false, package_json: ?*PackageJSON = null, diff_case: ?Fs.FileSystem.Entry.Lookup.DifferentCase = null, @@ -442,8 +442,8 @@ pub const PendingResolution = struct { pub const LoadResult = struct { path: string, diff_case: ?Fs.FileSystem.Entry.Lookup.DifferentCase, - dirname_fd: StoredFileDescriptorType = .zero, - file_fd: StoredFileDescriptorType = .zero, + dirname_fd: FD = .invalid, + file_fd: FD = .invalid, dir_info: ?*DirInfo = null, }; @@ -460,9 +460,9 @@ const Timer = @import("../system_timer.zig").Timer; pub const AnyResolveWatcher = struct { context: *anyopaque, - callback: *const (fn (*anyopaque, dir_path: string, dir_fd: StoredFileDescriptorType) void) = undefined, + callback: *const (fn (*anyopaque, dir_path: string, dir_fd: FD) void) = undefined, - pub fn watch(this: @This(), dir_path: string, fd: StoredFileDescriptorType) void { + pub fn watch(this: @This(), dir_path: string, fd: FD) void { return this.callback(this.context, dir_path, fd); } }; @@ -475,7 +475,7 @@ pub fn ResolveWatcher(comptime Context: type, comptime onWatch: anytype) type { .callback = watch, }; } - pub fn watch(this: *anyopaque, dir_path: string, fd: StoredFileDescriptorType) void { + pub fn watch(this: *anyopaque, dir_path: string, fd: FD) void { onWatch(bun.cast(Context, this), dir_path, fd); } }; @@ -1024,7 +1024,7 @@ pub const Resolver = struct { const symlink_path = query.entry.symlink(&r.fs.fs, r.store_fd); if (symlink_path.len > 0) { path.setRealpath(symlink_path); - if (result.file_fd == .zero) result.file_fd = query.entry.cache.fd; + if (!result.file_fd.isValid()) result.file_fd = query.entry.cache.fd; if (r.debug_logs) |*debug| { debug.addNoteFmt("Resolved symlink \"{s}\" to \"{s}\"", .{ path.text, symlink_path }); @@ -1037,31 +1037,31 @@ pub const Resolver = struct { const store_fd = r.store_fd; - if (query.entry.cache.fd == .zero) { + if (!query.entry.cache.fd.isValid()) { buf[out.len] = 0; const span = buf[0..out.len :0]; - var file = try if (store_fd) - std.fs.openFileAbsoluteZ(span, .{ .mode = .read_only }) + var file: bun.FD = if (store_fd) + .fromStdFile(try std.fs.openFileAbsoluteZ(span, .{ .mode = .read_only })) else - bun.openFileForPath(span); + .fromStdFile(try bun.openFileForPath(span)); if (!store_fd) { - assert(bun.FDTag.get(file.handle) == .none); - out = try bun.getFdPath(file.handle, &buf); + assert(file.stdioTag() == null); + out = try file.getFdPath(&buf); file.close(); - query.entry.cache.fd = .zero; + query.entry.cache.fd = .invalid; } else { - query.entry.cache.fd = bun.toFD(file.handle); - Fs.FileSystem.setMaxFd(file.handle); + query.entry.cache.fd = file; + Fs.FileSystem.setMaxFd(file.native()); } } defer { if (r.fs.fs.needToCloseFiles()) { - if (query.entry.cache.fd != .zero) { - var file = query.entry.cache.fd.asFile(); + if (query.entry.cache.fd.isValid()) { + var file = query.entry.cache.fd.stdFile(); file.close(); - query.entry.cache.fd = .zero; + query.entry.cache.fd = .invalid; } } } @@ -1075,7 +1075,7 @@ pub const Resolver = struct { debug.addNoteFmt("Resolved symlink \"{s}\" to \"{s}\"", .{ symlink, path.text }); } query.entry.cache.symlink = PathString.init(symlink); - if (result.file_fd == .zero and store_fd) result.file_fd = query.entry.cache.fd; + if (!result.file_fd.isValid() and store_fd) result.file_fd = query.entry.cache.fd; path.setRealpath(symlink); } @@ -1888,7 +1888,7 @@ pub const Resolver = struct { .success = .{ .path_pair = .{ .primary = package_json.source.path }, .dirname_fd = pkg_dir_info.getFileDescriptor(), - .file_fd = .zero, + .file_fd = .invalid, .is_node_module = package_json.source.path.isNodeModule(), .package_json = package_json, .dir_info = dir_info, @@ -2169,7 +2169,7 @@ pub const Resolver = struct { .success = .{ .path_pair = .{ .primary = package_json.source.path }, .dirname_fd = pkg_dir_info.getFileDescriptor(), - .file_fd = .zero, + .file_fd = .invalid, .is_node_module = package_json.source.path.isNodeModule(), .package_json = package_json, .dir_info = dir_info, @@ -2270,10 +2270,10 @@ pub const Resolver = struct { dir_entries_ptr.* = new_entry; if (r.store_fd) { - dir_entries_ptr.fd = bun.toFD(open_dir.fd); + dir_entries_ptr.fd = .fromStdDir(open_dir); } - bun.fs.debug("readdir({}, {s}) = {d}", .{ bun.toFD(open_dir.fd), dir_path, dir_entries_ptr.data.count() }); + bun.fs.debug("readdir({}, {s}) = {d}", .{ bun.FD.fromStdDir(open_dir), dir_path, dir_entries_ptr.data.count() }); dir_entries_option = rfs.entries.put(&cached_dir_entry_result, .{ .entries = dir_entries_ptr, @@ -2294,7 +2294,7 @@ pub const Resolver = struct { // to check for a parent package.json null, allocators.NotFound, - bun.toFD(open_dir.fd), + .fromStdDir(open_dir), package_id, ); return dir_info_ptr; @@ -2561,7 +2561,7 @@ pub const Resolver = struct { pub fn parseTSConfig( r: *ThisResolver, file: string, - dirname_fd: StoredFileDescriptorType, + dirname_fd: FD, ) !?*TSConfigJSON { // Since tsconfig.json is cached permanently, in our DirEntries cache // we must use the global allocator @@ -2611,7 +2611,7 @@ pub const Resolver = struct { pub fn parsePackageJSON( r: *ThisResolver, file: string, - dirname_fd: StoredFileDescriptorType, + dirname_fd: FD, package_id: ?Install.PackageID, comptime allow_dependencies: bool, ) !?*PackageJSON { @@ -2737,7 +2737,7 @@ pub const Resolver = struct { bufs(.dir_entry_paths_to_resolve)[@as(usize, @intCast(i))] = DirEntryResolveQueueItem{ .unsafe_path = top, .result = result, - .fd = .zero, + .fd = .invalid, }; if (rfs.entries.get(top)) |top_entry| { @@ -2763,7 +2763,7 @@ pub const Resolver = struct { bufs(.dir_entry_paths_to_resolve)[@as(usize, @intCast(i))] = DirEntryResolveQueueItem{ .unsafe_path = root_path, .result = result, - .fd = .zero, + .fd = .invalid, }; if (rfs.entries.get(top)) |top_entry| { switch (top_entry.*) { @@ -2790,8 +2790,8 @@ pub const Resolver = struct { defer { if (open_dir_count > 0 and (!r.store_fd or r.fs.fs.needToCloseFiles())) { const open_dirs: []std.fs.Dir = bufs(.open_dirs)[0..open_dir_count]; - for (open_dirs) |*open_dir| { - _ = bun.sys.close(bun.toFD(open_dir.fd)); + for (open_dirs) |open_dir| { + bun.FD.fromStdDir(open_dir).close(); } } } @@ -2816,8 +2816,8 @@ pub const Resolver = struct { defer top_parent = queue_top.result; queue_slice.len -= 1; - const open_dir = if (queue_top.fd != .zero) - queue_top.fd.asDir() + const open_dir: std.fs.Dir = if (queue_top.fd.isValid()) + queue_top.fd.stdDir() else open_dir: { // This saves us N copies of .toPosixPath // which was likely the perf gain from resolving directories relative to the parent directory, anyway. @@ -2838,7 +2838,7 @@ pub const Resolver = struct { .read_only = true, }); if (dirfd_result.unwrap()) |result| { - break :open_req result.asDir(); + break :open_req result.stdDir(); } else |err| { break :open_req err; } @@ -2884,7 +2884,7 @@ pub const Resolver = struct { }; }; - if (queue_top.fd == .zero) { + if (!queue_top.fd.isValid()) { Fs.FileSystem.setMaxFd(open_dir.fd); // these objects mostly just wrap the file descriptor, so it's fine to keep it. bufs(.open_dirs)[open_dir_count] = open_dir; @@ -2951,13 +2951,13 @@ pub const Resolver = struct { if (in_place) |existing| { existing.data.clearAndFree(allocator); } - new_entry.fd = if (r.store_fd) bun.toFD(open_dir.fd) else .zero; + new_entry.fd = if (r.store_fd) .fromStdDir(open_dir) else .invalid; var dir_entries_ptr = in_place orelse allocator.create(Fs.FileSystem.DirEntry) catch unreachable; dir_entries_ptr.* = new_entry; dir_entries_option = try rfs.entries.put(&cached_dir_entry_result, .{ .entries = dir_entries_ptr, }); - bun.fs.debug("readdir({}, {s}) = {d}", .{ bun.toFD(open_dir.fd), dir_path, dir_entries_ptr.data.count() }); + bun.fs.debug("readdir({}, {s}) = {d}", .{ bun.FD.fromStdDir(open_dir), dir_path, dir_entries_ptr.data.count() }); } // We must initialize it as empty so that the result index is correct. @@ -2972,7 +2972,7 @@ pub const Resolver = struct { cached_dir_entry_result.index, r.dir_cache.atIndex(top_parent.index), top_parent.index, - bun.toFD(open_dir.fd), + .fromStdDir(open_dir), null, ); @@ -4038,10 +4038,11 @@ pub const Resolver = struct { bin_folders = BinFolderArray.init(0) catch unreachable; } - const this_dir = fd.asDir(); - var file = this_dir.openDirZ(bun.pathLiteral("node_modules/.bin"), .{ .iterate = true }) catch break :append_bin_dir; + const this_dir = fd.stdDir(); + var file = bun.FD.fromStdDir(this_dir.openDirZ(bun.pathLiteral("node_modules/.bin"), .{}) catch + break :append_bin_dir); defer file.close(); - const bin_path = bun.getFdPath(file.fd, bufs(.node_bin_path)) catch break :append_bin_dir; + const bin_path = file.getFdPath(bufs(.node_bin_path)) catch break :append_bin_dir; bin_folders_lock.lock(); defer bin_folders_lock.unlock(); @@ -4063,10 +4064,10 @@ pub const Resolver = struct { bin_folders = BinFolderArray.init(0) catch unreachable; } - const this_dir = fd.asDir(); + const this_dir = fd.stdDir(); var file = this_dir.openDirZ(".bin", .{}) catch break :append_bin_dir; defer file.close(); - const bin_path = bun.getFdPath(file.fd, bufs(.node_bin_path)) catch break :append_bin_dir; + const bin_path = bun.getFdPath(.fromStdDir(file), bufs(.node_bin_path)) catch break :append_bin_dir; bin_folders_lock.lock(); defer bin_folders_lock.unlock(); @@ -4108,7 +4109,7 @@ pub const Resolver = struct { if (!r.opts.preserve_symlinks) { if (parent_.getEntries(r.generation)) |parent_entries| { if (parent_entries.get(base)) |lookup| { - if (entries.fd != .zero and lookup.entry.cache.fd == .zero and r.store_fd) lookup.entry.cache.fd = entries.fd; + if (entries.fd.isValid() and !lookup.entry.cache.fd.isValid() and r.store_fd) lookup.entry.cache.fd = entries.fd; const entry = lookup.entry; var symlink = entry.symlink(rfs, r.store_fd); @@ -4142,9 +4143,9 @@ pub const Resolver = struct { const entry = lookup.entry; if (entry.kind(rfs, r.store_fd) == .file) { info.package_json = if (r.usePackageManager() and !info.hasNodeModules() and !info.isNodeModules()) - r.parsePackageJSON(path, if (FeatureFlags.store_file_descriptors) fd else .zero, package_id, true) catch null + r.parsePackageJSON(path, if (FeatureFlags.store_file_descriptors) fd else .invalid, package_id, true) catch null else - r.parsePackageJSON(path, if (FeatureFlags.store_file_descriptors) fd else .zero, null, false) catch null; + r.parsePackageJSON(path, if (FeatureFlags.store_file_descriptors) fd else .invalid, null, false) catch null; if (info.package_json) |pkg| { if (pkg.browser_map.count() > 0) { diff --git a/src/router.zig b/src/router.zig index 095478a033..32b71fabc7 100644 --- a/src/router.zig +++ b/src/router.zig @@ -38,7 +38,7 @@ pub const Param = struct { pub const List = std.MultiArrayList(Param); }; -dir: StoredFileDescriptorType = .zero, +dir: StoredFileDescriptorType = .invalid, routes: Routes, loaded_routes: bool = false, allocator: std.mem.Allocator, @@ -756,8 +756,8 @@ pub const Route = struct { var file: std.fs.File = undefined; var needs_close = false; defer if (needs_close) file.close(); - if (entry.cache.fd != .zero) { - file = entry.cache.fd.asFile(); + if (entry.cache.fd.unwrapValid()) |valid| { + file = valid.stdFile(); } else { var parts = [_]string{ entry.dir, entry.base() }; abs_path_str = FileSystem.instance.absBuf(&parts, &route_file_buf); @@ -770,10 +770,10 @@ pub const Route = struct { FileSystem.setMaxFd(file.handle); needs_close = FileSystem.instance.fs.needToCloseFiles(); - if (!needs_close) entry.cache.fd = bun.toFD(file.handle); + if (!needs_close) entry.cache.fd = .fromStdFile(file); } - const _abs = bun.getFdPath(file.handle, &route_file_buf) catch |err| { + const _abs = bun.getFdPath(.fromStdFile(file), &route_file_buf) catch |err| { log.addErrorFmt(null, Logger.Loc.Empty, allocator, "{s} resolving route: {s}", .{ @errorName(err), abs_path_str }) catch unreachable; return null; }; diff --git a/src/shell/Builtin.zig b/src/shell/Builtin.zig index 05335241bb..ee40ddc8ca 100644 --- a/src/shell/Builtin.zig +++ b/src/shell/Builtin.zig @@ -150,9 +150,7 @@ pub const BuiltinIO = struct { pub fn needsIO(this: *Output) ?OutputNeedsIOSafeGuard { return switch (this.*) { - .fd => OutputNeedsIOSafeGuard{ - .__i_know_what_i_am_doing_it_needs_io_yes = 0, - }, + .fd => .output_needs_io, else => null, }; } diff --git a/src/shell/IOWriter.zig b/src/shell/IOWriter.zig index 270759ecfd..49c8035657 100644 --- a/src/shell/IOWriter.zig +++ b/src/shell/IOWriter.zig @@ -461,7 +461,7 @@ pub fn deinitOnMainThread(this: *IOWriter) void { this.writer.handle.closeImpl(null, {}, false); } } else this.winbuf.deinit(bun.default_allocator); - if (this.fd != bun.invalid_fd) _ = bun.sys.close(this.fd); + if (this.fd.isValid()) this.fd.close(); this.writer.disableKeepingProcessAlive(this.evtloop); bun.destroy(this); } diff --git a/src/shell/builtin/ls.zig b/src/shell/builtin/ls.zig index 70a83f1b55..95069096a0 100644 --- a/src/shell/builtin/ls.zig +++ b/src/shell/builtin/ls.zig @@ -264,7 +264,7 @@ pub const ShellLsTask = struct { }; defer { - _ = Syscall.close(fd); + fd.close(); debug("run done", .{}); } @@ -274,7 +274,7 @@ pub const ShellLsTask = struct { std.fmt.format(writer, "{s}:\n", .{this.path}) catch bun.outOfMemory(); } - var iterator = DirIterator.iterate(fd.asDir(), .u8); + var iterator = DirIterator.iterate(fd.stdDir(), .u8); var entry = iterator.next(); while (switch (entry) { diff --git a/src/shell/builtin/mv.zig b/src/shell/builtin/mv.zig index 04916c748a..225aec79b1 100644 --- a/src/shell/builtin/mv.zig +++ b/src/shell/builtin/mv.zig @@ -377,9 +377,7 @@ pub fn batchedMoveTaskDone(this: *Mv, task: *ShellMvBatchedTask) void { } pub fn deinit(this: *Mv) void { - if (this.args.target_fd != null and this.args.target_fd.? != bun.invalid_fd) { - _ = Syscall.close(this.args.target_fd.?); - } + if (this.args.target_fd) |fd| fd.toOptional().close(); } const Opts = struct { diff --git a/src/shell/builtin/rm.zig b/src/shell/builtin/rm.zig index cd710d0c4c..02353d5b89 100644 --- a/src/shell/builtin/rm.zig +++ b/src/shell/builtin/rm.zig @@ -862,7 +862,7 @@ pub const ShellRmTask = struct { // On posix we can close the file descriptor whenever, but on Windows // we need to close it BEFORE we delete if (close_fd) { - _ = Syscall.close(fd); + fd.close(); } } @@ -870,7 +870,7 @@ pub const ShellRmTask = struct { return Maybe(void).success; } - var iterator = DirIterator.iterate(fd.asDir(), .u8); + var iterator = DirIterator.iterate(fd.stdDir(), .u8); var entry = iterator.next(); var remove_child_vtable = RemoveFileVTable{ @@ -927,7 +927,7 @@ pub const ShellRmTask = struct { if (bun.Environment.isWindows) { close_fd = false; - _ = Syscall.close(fd); + fd.close(); } debug("[removeEntryDir] remove after children {s}", .{path}); @@ -1031,7 +1031,7 @@ pub const ShellRmTask = struct { fn removeEntryDirAfterChildren(this: *ShellRmTask, dir_task: *DirTask) Maybe(bool) { debug("remove entry after children: {s}", .{dir_task.path}); - const dirfd = bun.toFD(this.cwd); + const dirfd = this.cwd; var state = RemoveFileParent{ .task = this, .treat_as_dir = true, @@ -1090,7 +1090,7 @@ pub const ShellRmTask = struct { return Maybe(void).success; } }; - const dirfd = bun.toFD(this.cwd); + const dirfd = this.cwd; switch (ShellSyscall.unlinkatWithFlags(dirfd, path, 0)) { .result => return this.verboseDeleted(parent_dir_task, path), .err => |e| { diff --git a/src/shell/builtin/touch.zig b/src/shell/builtin/touch.zig index d8cf4c4c80..596d9a3234 100644 --- a/src/shell/builtin/touch.zig +++ b/src/shell/builtin/touch.zig @@ -255,7 +255,7 @@ pub const ShellTouchTask = struct { const perm = 0o664; switch (Syscall.open(filepath, bun.O.CREAT | bun.O.WRONLY, perm)) { .result => |fd| { - _ = bun.sys.close(fd); + fd.close(); break :out; }, .err => |e| { diff --git a/src/shell/builtin/yes.zig b/src/shell/builtin/yes.zig index 2d01417501..ee65c7536a 100644 --- a/src/shell/builtin/yes.zig +++ b/src/shell/builtin/yes.zig @@ -72,8 +72,8 @@ pub const YesTask = struct { const yes: *Yes = @fieldParentPtr("task", this); // Manually make safeguard since this task should not be created if output does not need IO - yes.bltn().stdout.enqueue(yes, yes.expletive, OutputNeedsIOSafeGuard{ .__i_know_what_i_am_doing_it_needs_io_yes = 0 }); - yes.bltn().stdout.enqueue(yes, "\n", OutputNeedsIOSafeGuard{ .__i_know_what_i_am_doing_it_needs_io_yes = 0 }); + yes.bltn().stdout.enqueue(yes, yes.expletive, .output_needs_io); + yes.bltn().stdout.enqueue(yes, "\n", .output_needs_io); this.enqueue(); } diff --git a/src/shell/interpreter.zig b/src/shell/interpreter.zig index d4cc5b7ed5..d6ae4a69bd 100644 --- a/src/shell/interpreter.zig +++ b/src/shell/interpreter.zig @@ -89,10 +89,7 @@ const assert = bun.assert; /// /// Functions which accept a `_: OutputNeedsIOSafeGuard` parameter can /// safely assume the stdout/stderr they are working with require IO. -pub const OutputNeedsIOSafeGuard = struct { - /// Dummy zero sized field to prevent it from being trivial to create this type (by doing `.{}`) - __i_know_what_i_am_doing_it_needs_io_yes: u0, -}; +pub const OutputNeedsIOSafeGuard = enum(u0) { output_needs_io }; pub const ExitCode = u16; @@ -174,7 +171,7 @@ const CowFd = struct { pub fn deinit(this: *CowFd) void { assert(this.refcount == 0); - _ = bun.sys.close(this.__fd); + this.__fd.close(); bun.default_allocator.destroy(this); } }; @@ -614,7 +611,7 @@ pub const Interpreter = struct { return Maybe(void).initErr(err); }, }; - _ = Syscall.close2(this.cwd_fd); + _ = this.cwd_fd.closeAllowingBadFileDescriptor(null); this.__prev_cwd.clearRetainingCapacity(); this.__prev_cwd.appendSlice(this.__cwd.items[0..]) catch bun.outOfMemory(); @@ -1070,13 +1067,13 @@ pub const Interpreter = struct { const event_loop = this.event_loop; log("Duping stdout", .{}); - const stdout_fd = switch (if (bun.Output.Source.Stdio.isStdoutNull()) bun.sys.openNullDevice() else ShellSyscall.dup(bun.STDOUT_FD)) { + const stdout_fd = switch (if (bun.Output.Source.Stdio.isStdoutNull()) bun.sys.openNullDevice() else ShellSyscall.dup(.stdout())) { .result => |fd| fd, .err => |err| return .{ .err = err }, }; log("Duping stderr", .{}); - const stderr_fd = switch (if (bun.Output.Source.Stdio.isStderrNull()) bun.sys.openNullDevice() else ShellSyscall.dup(bun.STDERR_FD)) { + const stderr_fd = switch (if (bun.Output.Source.Stdio.isStderrNull()) bun.sys.openNullDevice() else ShellSyscall.dup(.stderr())) { .result => |fd| fd, .err => |err| return .{ .err = err }, }; @@ -3086,20 +3083,18 @@ pub const Interpreter = struct { if (uv.uv_pipe(&fds, 0, 0).errEnum()) |e| { return .{ .err = Syscall.Error.fromCode(e, .pipe) }; } - pipe[0] = bun.FDImpl.fromUV(fds[0]).encode(); - pipe[1] = bun.FDImpl.fromUV(fds[1]).encode(); + pipe[0] = .fromUV(fds[0]); + pipe[1] = .fromUV(fds[1]); } else { - const fds: [2]bun.FileDescriptor = switch (bun.sys.socketpair( + switch (bun.sys.socketpair( std.posix.AF.UNIX, std.posix.SOCK.STREAM, 0, .blocking, )) { - .result => |fds| .{ bun.toFD(fds[0]), bun.toFD(fds[1]) }, + .result => |fds| pipe.* = fds, .err => |err| return .{ .err = err }, - }; - - pipe.* = fds; + } } set_count.* += 1; } @@ -4880,7 +4875,7 @@ pub const Interpreter = struct { } } else { log("IOReader(0x{x}) __deinit fd={}", .{ @intFromPtr(this), this.fd }); - _ = bun.sys.close(this.fd); + this.fd.close(); } } this.buf.deinit(bun.default_allocator); @@ -5057,7 +5052,7 @@ pub fn MaybeChild(comptime T: type) type { } fn closefd(fd: bun.FileDescriptor) void { - if (Syscall.close2(fd)) |err| { + if (fd.closeAllowingBadFileDescriptor(null)) |err| { log("ERR closefd: {}\n", .{err}); } } @@ -5334,12 +5329,12 @@ pub const ShellSyscall = struct { .err => |e| return .{ .err = e }, }; return switch (Syscall.openDirAtWindowsA(dir, p, .{ .iterable = true, .no_follow = flags & bun.O.NOFOLLOW != 0 })) { - .result => |fd| bun.sys.toLibUVOwnedFD(fd, .open, .close_on_fail), + .result => |fd| fd.makeLibUVOwnedForSyscall(.open, .close_on_fail), .err => |e| .{ .err = e.withPath(path) }, }; } return switch (Syscall.openDirAtWindowsA(dir, path, .{ .iterable = true, .no_follow = flags & bun.O.NOFOLLOW != 0 })) { - .result => |fd| bun.sys.toLibUVOwnedFD(fd, .open, .close_on_fail), + .result => |fd| fd.makeLibUVOwnedForSyscall(.open, .close_on_fail), .err => |e| .{ .err = e.withPath(path) }, }; } @@ -5357,7 +5352,7 @@ pub const ShellSyscall = struct { .err => |e| return .{ .err = e.withPath(path) }, }; if (bun.Environment.isWindows) { - return bun.sys.toLibUVOwnedFD(fd, .open, .close_on_fail); + return fd.makeLibUVOwnedForSyscall(.open, .close_on_fail); } return .{ .result = fd }; } @@ -5368,7 +5363,7 @@ pub const ShellSyscall = struct { .err => |e| return .{ .err = e }, }; if (bun.Environment.isWindows) { - return bun.sys.toLibUVOwnedFD(fd, .open, .close_on_fail); + return fd.makeLibUVOwnedForSyscall(.open, .close_on_fail); } return .{ .result = fd }; } @@ -5376,7 +5371,7 @@ pub const ShellSyscall = struct { pub fn dup(fd: bun.FileDescriptor) Maybe(bun.FileDescriptor) { if (bun.Environment.isWindows) { return switch (Syscall.dup(fd)) { - .result => |duped_fd| bun.sys.toLibUVOwnedFD(duped_fd, .dup, .close_on_fail), + .result => |duped_fd| duped_fd.makeLibUVOwnedForSyscall(.dup, .close_on_fail), .err => |e| .{ .err = e }, }; } @@ -5572,8 +5567,10 @@ pub fn FlagParser(comptime Opts: type) type { } pub fn isPollable(fd: bun.FileDescriptor, mode: bun.Mode) bool { - if (bun.Environment.isWindows) return false; - if (bun.Environment.isLinux) return posix.S.ISFIFO(mode) or posix.S.ISSOCK(mode) or posix.isatty(fd.int()); - // macos allows regular files to be pollable: ISREG(mode) == true - return posix.S.ISFIFO(mode) or posix.S.ISSOCK(mode) or posix.isatty(fd.int()) or posix.S.ISREG(mode); + return switch (bun.Environment.os) { + .windows, .wasm => false, + .linux => posix.S.ISFIFO(mode) or posix.S.ISSOCK(mode) or posix.isatty(fd.native()), + // macos allows regular files to be pollable: ISREG(mode) == true + .mac => posix.S.ISFIFO(mode) or posix.S.ISSOCK(mode) or posix.S.ISREG(mode) or posix.isatty(fd.native()), + }; } diff --git a/src/shell/shell.zig b/src/shell/shell.zig index c92fea0de6..4785274f64 100644 --- a/src/shell/shell.zig +++ b/src/shell/shell.zig @@ -37,10 +37,10 @@ const GlobWalker = Glob.GlobWalker_(null, true); pub const SUBSHELL_TODO_ERROR = "Subshells are not implemented, please open GitHub issue!"; -/// Using these instead of `bun.STD{IN,OUT,ERR}_FD` to makesure we use uv fd -pub const STDIN_FD: bun.FileDescriptor = if (bun.Environment.isWindows) bun.FDImpl.fromUV(0).encode() else bun.STDIN_FD; -pub const STDOUT_FD: bun.FileDescriptor = if (bun.Environment.isWindows) bun.FDImpl.fromUV(1).encode() else bun.STDOUT_FD; -pub const STDERR_FD: bun.FileDescriptor = if (bun.Environment.isWindows) bun.FDImpl.fromUV(2).encode() else bun.STDERR_FD; +/// Using these instead of the file descriptor decl literals to make sure we use LivUV fds on Windows +pub const STDIN_FD: bun.FileDescriptor = .fromUV(0); +pub const STDOUT_FD: bun.FileDescriptor = .fromUV(1); +pub const STDERR_FD: bun.FileDescriptor = .fromUV(2); pub const POSIX_DEV_NULL: [:0]const u8 = "/dev/null"; pub const WINDOWS_DEV_NULL: [:0]const u8 = "NUL"; diff --git a/src/shell/subproc.zig b/src/shell/subproc.zig index 1f3f785f13..7afc54325d 100644 --- a/src/shell/subproc.zig +++ b/src/shell/subproc.zig @@ -287,7 +287,7 @@ pub const ShellSubprocess = struct { this.buffer.deref(); }, .memfd => |fd| { - _ = bun.sys.close(fd); + fd.close(); this.* = .{ .ignore = {} }; }, .ignore => {}, @@ -301,7 +301,7 @@ pub const ShellSubprocess = struct { _ = pipe.end(null); }, inline .memfd, .fd => |fd| { - _ = bun.sys.close(fd); + fd.close(); this.* = .{ .ignore = {} }; }, .buffer => { @@ -419,7 +419,7 @@ pub const ShellSubprocess = struct { switch (this.*) { inline .memfd, .fd => |fd| { this.* = .{ .closed = {} }; - _ = bun.sys.close(fd); + fd.close(); }, .pipe => { this.pipe.close(); @@ -432,7 +432,7 @@ pub const ShellSubprocess = struct { switch (this.*) { inline .memfd, .fd => |fd| { this.* = .{ .closed = {} }; - _ = bun.sys.close(fd); + fd.close(); }, .pipe => |pipe| { defer pipe.detach(); @@ -797,7 +797,7 @@ pub const ShellSubprocess = struct { if (should_close_memfd) { inline for (0..spawn_args.stdio.len) |fd_index| { if (spawn_args.stdio[fd_index] == .memfd) { - _ = bun.sys.close(spawn_args.stdio[fd_index].memfd); + spawn_args.stdio[fd_index].memfd.close(); spawn_args.stdio[fd_index] = .ignore; } } diff --git a/src/shell/util.zig b/src/shell/util.zig index 48c5b5577b..8d6c51fd1f 100644 --- a/src/shell/util.zig +++ b/src/shell/util.zig @@ -17,10 +17,11 @@ const posix = std.posix; pub const OutKind = enum { stdout, stderr, + pub fn toFd(this: OutKind) bun.FileDescriptor { return switch (this) { - .stdout => bun.STDOUT_FD, - .stderr => bun.STDERR_FD, + .stdout => .stdout(), + .stderr => .stderr(), }; } }; diff --git a/src/sys.zig b/src/sys.zig index 808a2af26e..08181c272a 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -520,8 +520,11 @@ pub const Error = struct { err.dest = bun.String.createUTF8(this.dest); } - if (this.fd != bun.invalid_fd) { - err.fd = this.fd; + if (this.fd.unwrapValid()) |valid| { + // When the FD is a windows handle, there is no sane way to report this. + if (!Environment.isWindows or valid.kind == .uv) { + err.fd = valid.uv(); + } } return err; @@ -582,8 +585,11 @@ pub const Error = struct { err.dest = bun.String.createUTF8(this.dest); } - if (this.fd != bun.invalid_fd) { - err.fd = this.fd; + if (this.fd.unwrapValid()) |valid| { + // When the FD is a windows handle, there is no sane way to report this. + if (!Environment.isWindows or valid.kind == .uv) { + err.fd = valid.uv(); + } } return err; @@ -838,11 +844,10 @@ pub fn lstat(path: [:0]const u8) Maybe(bun.Stat) { pub fn fstat(fd: bun.FileDescriptor) Maybe(bun.Stat) { if (Environment.isWindows) { - const dec = bun.FDImpl.decode(fd); - if (dec.kind == .system) { - const uvfd = bun.toLibUVOwnedFD(fd) catch return .{ .err = Error.fromCode(.MFILE, .uv_open_osfhandle) }; - return sys_uv.fstat(uvfd); - } else return sys_uv.fstat(fd); + // TODO: this is a bad usage of makeLibUVOwned + const uvfd = fd.makeLibUVOwned() catch + return .{ .err = Error.fromCode(.MFILE, .uv_open_osfhandle) }; + return sys_uv.fstat(uvfd); } var stat_ = mem.zeroes(bun.Stat); @@ -895,7 +900,7 @@ pub fn mkdiratW(dir_fd: bun.FileDescriptor, file_path: []const u16, _: i32) Mayb if (dir_to_make == .err) { return .{ .err = dir_to_make.err }; } - _ = close(dir_to_make.result); + dir_to_make.result.close(); return .{ .result = {} }; } @@ -903,15 +908,14 @@ pub fn fstatat(fd: bun.FileDescriptor, path: [:0]const u8) Maybe(bun.Stat) { if (Environment.isWindows) { return switch (openatWindowsA(fd, path, 0, 0)) { .result => |file| { - // :( - defer _ = close(file); + defer file.close(); return fstat(file); }, .err => |err| Maybe(bun.Stat){ .err = err }, }; } var stat_buf = mem.zeroes(bun.Stat); - const fd_valid = if (fd == bun.invalid_fd) std.posix.AT.FDCWD else fd.int(); + const fd_valid = if (fd == bun.invalid_fd) std.posix.AT.FDCWD else fd.native(); if (Maybe(bun.Stat).errnoSysFP(syscall.fstatat(fd_valid, path, &stat_buf, 0), .fstatat, fd, path)) |err| { log("fstatat({}, {s}) = {s}", .{ fd, path, @tagName(err.getErrno()) }); return err; @@ -1205,9 +1209,7 @@ fn openDirAtWindowsNtPath( switch (windows.Win32Error.fromNTStatus(rc)) { .SUCCESS => { - return .{ - .result = bun.toFD(fd), - }; + return .{ .result = .fromNative(fd) }; }, else => |code| { if (code.toSystemErrno()) |sys_err| { @@ -1253,7 +1255,7 @@ fn openWindowsDevicePath( .syscall = .open, } }; } - return .{ .result = bun.toFD(rc) }; + return .{ .result = .fromNative(rc) }; } pub const WindowsOpenDirOptions = packed struct { @@ -1440,9 +1442,7 @@ pub fn openFileAtWindowsNtPath( }; } } - return JSC.Maybe(bun.FileDescriptor){ - .result = bun.toFD(result), - }; + return .{ .result = .fromNative(result) }; }, else => |code| { if (code.toSystemErrno()) |sys_err| { @@ -1696,12 +1696,12 @@ fn openatWindowsTMaybeNormalize(comptime T: type, dir: bun.FileDescriptor, path: } pub fn openatWindows( - dir: anytype, + dir: bun.FileDescriptor, path: []const u16, flags: i32, perm: bun.Mode, ) Maybe(bun.FileDescriptor) { - return openatWindowsT(u16, bun.toFD(dir), path, flags, perm); + return openatWindowsT(u16, dir, path, flags, perm); } pub fn openatWindowsA( @@ -1720,7 +1720,7 @@ pub fn openatOSPath(dirfd: bun.FileDescriptor, file_path: bun.OSPathSliceZ, flag if (comptime Environment.allow_assert) log("openat({}, {s}, {d}) = {d}", .{ dirfd, bun.sliceTo(file_path, 0), flags, rc }); - return Maybe(bun.FileDescriptor).errnoSysFP(rc, .open, dirfd, file_path) orelse .{ .result = bun.toFD(rc) }; + return Maybe(bun.FileDescriptor).errnoSysFP(rc, .open, dirfd, file_path) orelse .{ .result = .fromNative(rc) }; } else if (comptime Environment.isWindows) { return openatWindowsT(bun.OSPathChar, dirfd, file_path, flags, perm); } @@ -1731,7 +1731,7 @@ pub fn openatOSPath(dirfd: bun.FileDescriptor, file_path: bun.OSPathSliceZ, flag log("openat({}, {s}, {d}) = {d}", .{ dirfd, bun.sliceTo(file_path, 0), flags, rc }); return switch (Syscall.getErrno(rc)) { - .SUCCESS => .{ .result = bun.toFD(@as(i32, @intCast(rc))) }, + .SUCCESS => .{ .result = .fromNative(@intCast(rc)) }, .INTR => continue, else => |err| { return .{ @@ -1813,7 +1813,7 @@ pub fn openatA(dirfd: bun.FileDescriptor, file_path: []const u8, flags: i32, per pub fn openA(file_path: []const u8, flags: i32, perm: bun.Mode) Maybe(bun.FileDescriptor) { // this is what open() does anyway. - return openatA(bun.FD.cwd(), file_path, flags, perm); + return openatA(.cwd(), file_path, flags, perm); } pub fn open(file_path: [:0]const u8, flags: i32, perm: bun.Mode) Maybe(bun.FileDescriptor) { @@ -1825,25 +1825,7 @@ pub fn open(file_path: [:0]const u8, flags: i32, perm: bun.Mode) Maybe(bun.FileD } // this is what open() does anyway. - return openat(bun.toFD(std.posix.AT.FDCWD), file_path, flags, perm); -} - -/// This function will prevent stdout and stderr from being closed. -pub fn close(fd: bun.FileDescriptor) ?Syscall.Error { - return bun.FDImpl.decode(fd).close(); -} - -pub fn close2(fd: bun.FileDescriptor) ?Syscall.Error { - if (fd == bun.STDOUT_FD or fd == bun.STDERR_FD or fd == bun.STDIN_FD) { - log("close({}) SKIPPED", .{fd}); - return null; - } - - return closeAllowingStdinStdoutAndStderr(fd); -} - -pub fn closeAllowingStdinStdoutAndStderr(fd: bun.FileDescriptor) ?Syscall.Error { - return bun.FDImpl.decode(fd).closeAllowingStdinStdoutAndStderr(); + return openat(.cwd(), file_path, flags, perm); } pub const max_count = switch (builtin.os.tag) { @@ -2175,11 +2157,11 @@ pub fn read(fd: bun.FileDescriptor, buf: []u8) Maybe(usize) { return Maybe(usize){ .result = @as(usize, @intCast(rc)) }; } }, - .windows => if (bun.FDImpl.decode(fd).kind == .uv) + .windows => if (fd.kind == .uv) sys_uv.read(fd, buf) else { var amount_read: u32 = 0; - const rc = kernel32.ReadFile(fd.cast(), buf.ptr, @as(u32, @intCast(adjusted_len)), &amount_read, null); + const rc = kernel32.ReadFile(fd.native(), buf.ptr, @as(u32, @intCast(adjusted_len)), &amount_read, null); if (rc == windows.FALSE) { const ret: Maybe(usize) = .{ .err = Syscall.Error{ @@ -2453,7 +2435,7 @@ pub fn renameatConcurrentlyWithoutFallback( // sad path: let's try to delete the folder and then rename it if (to_dir_fd.isValid()) { - var to_dir = to_dir_fd.asDir(); + var to_dir = to_dir_fd.stdDir(); to_dir.deleteTree(to) catch {}; } else { std.fs.deleteTreeAbsolute(to) catch {}; @@ -2822,11 +2804,11 @@ pub fn mmapFile(path: [:0]const u8, flags: std.c.MAP, wanted_size: ?usize, offse .result => |fd| fd, .err => |err| return .{ .err = err }, }; + defer fd.close(); var size = std.math.sub(usize, @as(usize, @intCast(switch (fstat(fd)) { .result => |result| result.size, .err => |err| { - _ = close(fd); return .{ .err = err }; }, })), offset) catch 0; @@ -2837,16 +2819,10 @@ pub fn mmapFile(path: [:0]const u8, flags: std.c.MAP, wanted_size: ?usize, offse .result => |map| map, .err => |err| { - _ = close(fd); return .{ .err = err }; }, }; - if (close(fd)) |err| { - _ = munmap(map); - return .{ .err = err }; - } - return .{ .result = map }; } @@ -2932,7 +2908,7 @@ pub fn socketpair(domain: socketpair_t, socktype: socketpair_t, protocol: socket // Set O_CLOEXEC first. inline for (0..2) |i| { - switch (setCloseOnExec(bun.toFD(fds_i[i]))) { + switch (setCloseOnExec(.fromNative(fds_i[i]))) { .err => |err| break :err err, .result => {}, } @@ -2940,7 +2916,7 @@ pub fn socketpair(domain: socketpair_t, socktype: socketpair_t, protocol: socket if (comptime Environment.isMac) { inline for (0..2) |i| { - switch (setNoSigpipe(bun.toFD(fds_i[i]))) { + switch (setNoSigpipe(.fromNative(fds_i[i]))) { .err => |err| break :err err, else => {}, } @@ -2949,7 +2925,7 @@ pub fn socketpair(domain: socketpair_t, socktype: socketpair_t, protocol: socket if (nonblocking_status == .nonblocking) { inline for (0..2) |i| { - switch (setNonblocking(bun.toFD(fds_i[i]))) { + switch (setNonblocking(.fromNative(fds_i[i]))) { .err => |err| break :err err, .result => {}, } @@ -2962,7 +2938,7 @@ pub fn socketpair(domain: socketpair_t, socktype: socketpair_t, protocol: socket // On any error after socketpair(), we need to close it. if (err) |errr| { inline for (0..2) |i| { - _ = close(bun.toFD(fds_i[i])); + bun.FD.fromNative(fds_i[i]).close(); } log("socketpair() = {d} {s}", .{ errr.errno, errr.name() }); @@ -2973,7 +2949,7 @@ pub fn socketpair(domain: socketpair_t, socktype: socketpair_t, protocol: socket log("socketpair() = [{d} {d}]", .{ fds_i[0], fds_i[1] }); - return Maybe([2]bun.FileDescriptor){ .result = .{ bun.toFD(fds_i[0]), bun.toFD(fds_i[1]) } }; + return Maybe([2]bun.FileDescriptor){ .result = .{ .fromNative(fds_i[0]), .fromNative(fds_i[1]) } }; } pub fn munmap(memory: []align(page_size_min) const u8) Maybe(void) { @@ -2990,7 +2966,7 @@ pub fn memfd_create(name: [:0]const u8, flags: u32) Maybe(bun.FileDescriptor) { log("memfd_create({s}, {d}) = {d}", .{ name, flags, rc }); return Maybe(bun.FileDescriptor).errnoSys(rc, .memfd_create) orelse - .{ .result = bun.toFD(@as(c_int, @intCast(rc))) }; + .{ .result = .fromNative(@intCast(rc)) }; } pub fn setPipeCapacityOnLinux(fd: bun.FileDescriptor, capacity: usize) Maybe(usize) { @@ -3040,7 +3016,7 @@ pub fn getMaxPipeSizeOnLinux() usize { return default_out_size; }, }; - defer _ = bun.sys.close(pipe_max_size_fd); + defer pipe_max_size_fd.close(); var max_pipe_size_buf: [128]u8 = undefined; const max_pipe_size = switch (bun.sys.read(pipe_max_size_fd, max_pipe_size_buf[0..])) { .result => |bytes_read| std.fmt.parseInt(i64, strings.trim(max_pipe_size_buf[0..bytes_read], "\n"), 10) catch |err| { @@ -3200,9 +3176,8 @@ pub fn existsZ(path: [:0]const u8) bool { } } -pub fn faccessat(dir_: anytype, subpath: anytype) JSC.Maybe(bool) { +pub fn faccessat(dir_fd: bun.FileDescriptor, subpath: anytype) JSC.Maybe(bool) { const has_sentinel = std.meta.sentinel(@TypeOf(subpath)) != null; - const dir_fd = bun.toFD(dir_); if (comptime !has_sentinel) { const path = std.os.toPosixPath(subpath) catch return JSC.Maybe(bool){ .err = Error.fromCode(.NAMETOOLONG, .access) }; @@ -3230,9 +3205,8 @@ pub fn faccessat(dir_: anytype, subpath: anytype) JSC.Maybe(bool) { return JSC.Maybe(bool){ .result = false }; } -pub fn directoryExistsAt(dir: anytype, subpath: anytype) JSC.Maybe(bool) { - const dir_fd = bun.toFD(dir); - return switch (existsAtType(dir_fd, subpath)) { +pub fn directoryExistsAt(dir: bun.FileDescriptor, subpath: anytype) JSC.Maybe(bool) { + return switch (existsAtType(dir, subpath)) { // .err => |err| if (err.getErrno() == .NOENT) .{ .result = false } @@ -3548,7 +3522,7 @@ pub fn pipe() Maybe([2]bun.FileDescriptor) { return err; } log("pipe() = [{d}, {d}]", .{ fds[0], fds[1] }); - return .{ .result = .{ bun.toFD(fds[0]), bun.toFD(fds[1]) } }; + return .{ .result = .{ .fromNative(fds[0]), .fromNative(fds[1]) } }; } pub fn openNullDevice() Maybe(bun.FileDescriptor) { @@ -3578,8 +3552,9 @@ pub fn dupWithFlags(fd: bun.FileDescriptor, _: i32) Maybe(bun.FileDescriptor) { return err; } } - log("dup({}) = {}", .{ fd, bun.toFD(target) }); - return Maybe(bun.FileDescriptor){ .result = bun.toFD(target) }; + const duplicated_fd = bun.FD.fromNative(target); + log("dup({}) = {}", .{ fd, duplicated_fd }); + return Maybe(bun.FileDescriptor){ .result = duplicated_fd }; } const ArgType = if (comptime Environment.isLinux) usize else c_int; @@ -3588,9 +3563,7 @@ pub fn dupWithFlags(fd: bun.FileDescriptor, _: i32) Maybe(bun.FileDescriptor) { .err => |err| return .{ .err = err }, }; - return Maybe(bun.FileDescriptor){ - .result = bun.toFD(@as(u32, @intCast(out))), - }; + return .initResult(.fromNative(@intCast(out))); } pub fn dup(fd: bun.FileDescriptor) Maybe(bun.FileDescriptor) { @@ -3695,7 +3668,7 @@ pub fn readNonblocking(fd: bun.FileDescriptor, buf: []u8) Maybe(usize) { var debug_timer = bun.Output.DebugTimer.start(); // Note that there is a bug on Linux Kernel 5 - const rc = C.sys_preadv2(@intCast(fd.int()), &iovec, 1, -1, std.os.linux.RWF.NOWAIT); + const rc = C.sys_preadv2(fd.native(), &iovec, 1, -1, std.os.linux.RWF.NOWAIT); if (comptime Environment.isDebug) { log("preadv2({}, {d}) = {d} ({})", .{ fd, buf.len, rc, debug_timer }); @@ -3739,7 +3712,7 @@ pub fn writeNonblocking(fd: bun.FileDescriptor, buf: []const u8) Maybe(usize) { var debug_timer = bun.Output.DebugTimer.start(); - const rc = C.sys_pwritev2(@intCast(fd.int()), &iovec, 1, -1, std.os.linux.RWF.NOWAIT); + const rc = C.sys_pwritev2(fd.native(), &iovec, 1, -1, std.os.linux.RWF.NOWAIT); if (comptime Environment.isDebug) { log("pwritev2({}, {d}) = {d} ({})", .{ fd, buf.len, rc, debug_timer }); @@ -3798,12 +3771,13 @@ pub fn isPollable(mode: mode_t) bool { const This = @This(); +/// TODO: make these all methods on `bun.FD`, and define them as methods `bun.FD` pub const File = struct { // "handle" matches std.fs.File handle: bun.FileDescriptor, - pub fn openat(other: anytype, path: [:0]const u8, flags: i32, mode: bun.Mode) Maybe(File) { - return switch (This.openat(bun.toFD(other), path, flags, mode)) { + pub fn openat(dir: bun.FileDescriptor, path: [:0]const u8, flags: i32, mode: bun.Mode) Maybe(File) { + return switch (This.openat(dir, path, flags, mode)) { .result => |fd| .{ .result = .{ .handle = fd } }, .err => |err| .{ .err = err }, }; @@ -3817,14 +3791,13 @@ pub const File = struct { return File.makeOpenat(bun.FD.cwd(), path, flags, mode); } - pub fn makeOpenat(other: anytype, path: [:0]const u8, flags: i32, mode: bun.Mode) Maybe(File) { - const dir = bun.toFD(other); - const fd = switch (This.openat(dir, path, flags, mode)) { + pub fn makeOpenat(other: bun.FD, path: [:0]const u8, flags: i32, mode: bun.Mode) Maybe(File) { + const fd = switch (This.openat(other, path, flags, mode)) { .result => |fd| fd, .err => |err| fd: { if (std.fs.path.dirname(path)) |dir_path| { - bun.makePath(dir.asDir(), dir_path) catch {}; - break :fd switch (This.openat(dir, path, flags, mode)) { + bun.makePath(other.stdDir(), dir_path) catch {}; + break :fd switch (This.openat(other, path, flags, mode)) { .result => |fd| fd, .err => |err2| return .{ .err = err2 }, }; @@ -3837,8 +3810,8 @@ pub const File = struct { return .{ .result = .{ .handle = fd } }; } - pub fn openatOSPath(other: anytype, path: bun.OSPathSliceZ, flags: i32, mode: bun.Mode) Maybe(File) { - return switch (This.openatOSPath(bun.toFD(other), path, flags, mode)) { + pub fn openatOSPath(other: bun.FD, path: bun.OSPathSliceZ, flags: i32, mode: bun.Mode) Maybe(File) { + return switch (This.openatOSPath(other, path, flags, mode)) { .result => |fd| .{ .result = .{ .handle = fd } }, .err => |err| .{ .err = err }, }; @@ -3852,30 +3825,24 @@ pub const File = struct { } if (T == std.posix.fd_t) { - return File{ .handle = bun.toFD(other) }; + return .{ .handle = .fromNative(other) }; } if (T == bun.FileDescriptor) { - return File{ .handle = other }; + return .{ .handle = other }; } if (T == std.fs.File) { - return File{ .handle = bun.toFD(other.handle) }; + return .{ .handle = .fromStdFile(other) }; } if (T == std.fs.Dir) { - return File{ .handle = bun.toFD(other.fd) }; - } - - if (comptime Environment.isWindows) { - if (T == bun.windows.HANDLE) { - return File{ .handle = bun.toFD(other) }; - } + return File{ .handle = .fromStdDir(other) }; } if (comptime Environment.isLinux) { if (T == u64) { - return File{ .handle = bun.toFD(other) }; + return File{ .handle = .fromNative(@intCast(other)) }; } } @@ -3936,7 +3903,8 @@ pub const File = struct { defer if (Environment.isPosix) this.close(); // On Windows, close the file before moving it. if (Environment.isWindows) this.close(); - try bun.C.moveFileZWithHandle(this.handle, bun.toFD(std.fs.cwd()), src, bun.toFD(std.fs.cwd()), dest); + const cwd = bun.FD.cwd(); + try bun.C.moveFileZWithHandle(this.handle, cwd, src, cwd, dest); } fn stdIoRead(this: File, buf: []u8) ReadError!usize { @@ -3979,9 +3947,9 @@ pub const File = struct { return std.posix.isatty(self.handle.cast()); } + /// Asserts in debug that this File object is valid pub fn close(self: File) void { - // TODO: probably return the error? we have a lot of code paths which do not so we are keeping for now - _ = This.close(self.handle); + self.handle.close(); } pub fn getEndPos(self: File) Maybe(usize) { @@ -4221,32 +4189,6 @@ pub const File = struct { } }; -pub inline fn toLibUVOwnedFD( - maybe_windows_fd: bun.FileDescriptor, - comptime syscall_tag: Syscall.Tag, - comptime error_case: enum { close_on_fail, leak_fd_on_fail }, -) Maybe(bun.FileDescriptor) { - if (!Environment.isWindows) { - return .{ .result = maybe_windows_fd }; - } - - return .{ - .result = bun.toLibUVOwnedFD(maybe_windows_fd) catch |err| switch (err) { - error.SystemFdQuotaExceeded => { - if (error_case == .close_on_fail) { - _ = close(maybe_windows_fd); - } - return .{ - .err = .{ - .errno = @intFromEnum(bun.C.E.MFILE), - .syscall = syscall_tag, - }, - }; - }, - }, - }; -} - pub const Dir = @import("./dir.zig"); const FILE_SHARE = w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE; diff --git a/src/sys_uv.zig b/src/sys_uv.zig index 9a574bd581..9163355579 100644 --- a/src/sys_uv.zig +++ b/src/sys_uv.zig @@ -14,7 +14,6 @@ const uv = bun.windows.libuv; const C = bun.C; const E = C.E; const Environment = bun.Environment; -const FDImpl = bun.FDImpl; const FileDescriptor = bun.FileDescriptor; const JSC = bun.JSC; const MAX_PATH_BYTES = bun.MAX_PATH_BYTES; @@ -28,7 +27,7 @@ comptime { pub const log = bun.sys.syslog; pub const Error = bun.sys.Error; -// libuv dont suppport openat (https://github.com/libuv/libuv/issues/4167) +// libuv dont support openat (https://github.com/libuv/libuv/issues/4167) pub const openat = bun.sys.openat; pub const getFdPath = bun.sys.getFdPath; pub const setFileOffset = bun.sys.setFileOffset; @@ -36,7 +35,7 @@ pub const openatOSPath = bun.sys.openatOSPath; pub const mkdirOSPath = bun.sys.mkdirOSPath; pub const access = bun.sys.access; -// Note: `req = undefined; req.deinit()` has a saftey-check in a debug build +// Note: `req = undefined; req.deinit()` has a safety-check in a debug build pub fn open(file_path: [:0]const u8, c_flags: i32, _perm: bun.Mode) Maybe(bun.FileDescriptor) { assertIsValidWindowsPath(u8, file_path); @@ -48,7 +47,7 @@ pub fn open(file_path: [:0]const u8, c_flags: i32, _perm: bun.Mode) Maybe(bun.Fi var perm = _perm; if (perm == 0) { - // Set a sensible default, otherwise on windows the file will be unuseable + // Set a sensible default, otherwise on windows the file will be unusable perm = 0o644; } @@ -57,7 +56,7 @@ pub fn open(file_path: [:0]const u8, c_flags: i32, _perm: bun.Mode) Maybe(bun.Fi return if (rc.errno()) |errno| .{ .err = .{ .errno = errno, .syscall = .open, .path = file_path } } else - .{ .result = bun.toFD(@as(i32, @intCast(req.result.int()))) }; + .{ .result = req.result.toFD() }; } pub fn mkdir(file_path: [:0]const u8, flags: bun.Mode) Maybe(void) { @@ -88,7 +87,7 @@ pub fn chmod(file_path: [:0]const u8, flags: bun.Mode) Maybe(void) { } pub fn fchmod(fd: FileDescriptor, flags: bun.Mode) Maybe(void) { - const uv_fd = bun.uvfdcast(fd); + const uv_fd = fd.uv(); var req: uv.fs_t = uv.fs_t.uninitialized; defer req.deinit(); const rc = uv.uv_fs_fchmod(uv.Loop.get(), &req, uv_fd, flags, null); @@ -127,7 +126,7 @@ pub fn chown(file_path: [:0]const u8, uid: uv.uv_uid_t, gid: uv.uv_uid_t) Maybe( } pub fn fchown(fd: FileDescriptor, uid: uv.uv_uid_t, gid: uv.uv_uid_t) Maybe(void) { - const uv_fd = bun.uvfdcast(fd); + const uv_fd = fd.uv(); var req: uv.fs_t = uv.fs_t.uninitialized; defer req.deinit(); @@ -235,7 +234,7 @@ pub fn symlinkUV(target: [:0]const u8, new_path: [:0]const u8, flags: c_int) May } pub fn ftruncate(fd: FileDescriptor, size: isize) Maybe(void) { - const uv_fd = bun.uvfdcast(fd); + const uv_fd = fd.uv(); var req: uv.fs_t = uv.fs_t.uninitialized; defer req.deinit(); const rc = uv.uv_fs_ftruncate(uv.Loop.get(), &req, uv_fd, size, null); @@ -248,7 +247,7 @@ pub fn ftruncate(fd: FileDescriptor, size: isize) Maybe(void) { } pub fn fstat(fd: FileDescriptor) Maybe(bun.Stat) { - const uv_fd = bun.uvfdcast(fd); + const uv_fd = fd.uv(); var req: uv.fs_t = uv.fs_t.uninitialized; defer req.deinit(); const rc = uv.uv_fs_fstat(uv.Loop.get(), &req, uv_fd, null); @@ -261,7 +260,7 @@ pub fn fstat(fd: FileDescriptor) Maybe(bun.Stat) { } pub fn fdatasync(fd: FileDescriptor) Maybe(void) { - const uv_fd = bun.uvfdcast(fd); + const uv_fd = fd.uv(); var req: uv.fs_t = uv.fs_t.uninitialized; defer req.deinit(); const rc = uv.uv_fs_fdatasync(uv.Loop.get(), &req, uv_fd, null); @@ -274,7 +273,7 @@ pub fn fdatasync(fd: FileDescriptor) Maybe(void) { } pub fn fsync(fd: FileDescriptor) Maybe(void) { - const uv_fd = bun.uvfdcast(fd); + const uv_fd = fd.uv(); var req: uv.fs_t = uv.fs_t.uninitialized; defer req.deinit(); const rc = uv.uv_fs_fsync(uv.Loop.get(), &req, uv_fd, null); @@ -313,15 +312,15 @@ pub fn lstat(path: [:0]const u8) Maybe(bun.Stat) { } pub fn close(fd: FileDescriptor) ?bun.sys.Error { - return FDImpl.decode(fd).close(); + return fd.closeAllowingBadFileDescriptor(@returnAddress()); } pub fn closeAllowingStdoutAndStderr(fd: FileDescriptor) ?bun.sys.Error { - return FDImpl.decode(fd).closeAllowingStdoutAndStderr(); + return fd.closeAllowingStandardIo(@returnAddress()); } pub fn preadv(fd: FileDescriptor, bufs: []const bun.PlatformIOVec, position: i64) Maybe(usize) { - const uv_fd = bun.uvfdcast(fd); + const uv_fd = fd.uv(); comptime bun.assert(bun.PlatformIOVec == uv.uv_buf_t); const debug_timer = bun.Output.DebugTimer.start(); @@ -355,7 +354,7 @@ pub fn preadv(fd: FileDescriptor, bufs: []const bun.PlatformIOVec, position: i64 } pub fn pwritev(fd: FileDescriptor, bufs: []const bun.PlatformIOVecConst, position: i64) Maybe(usize) { - const uv_fd = bun.uvfdcast(fd); + const uv_fd = fd.uv(); comptime bun.assert(bun.PlatformIOVec == uv.uv_buf_t); const debug_timer = bun.Output.DebugTimer.start(); diff --git a/src/tmp.zig b/src/tmp.zig index 9b1cd7a254..b8b12b3695 100644 --- a/src/tmp.zig +++ b/src/tmp.zig @@ -46,7 +46,7 @@ pub const Tmpfile = struct { } tmpfile.fd = switch (bun.sys.openat(destination_dir, tmpfilename, O.CREAT | O.CLOEXEC | O.WRONLY, perm)) { - .result => |fd| switch (bun.sys.toLibUVOwnedFD(fd, .open, .close_on_fail)) { + .result => |fd| switch (fd.makeLibUVOwnedForSyscall(.open, .close_on_fail)) { .result => |owned_fd| owned_fd, .err => |err| return .{ .err = err }, }, diff --git a/src/transpiler.zig b/src/transpiler.zig index da4eb523c5..7a140e14a2 100644 --- a/src/transpiler.zig +++ b/src/transpiler.zig @@ -796,12 +796,14 @@ pub const Transpiler = struct { var pathname = try transpiler.allocator.alloc(u8, hashed_name.len + file_path.name.ext.len); bun.copy(u8, pathname, hashed_name); bun.copy(u8, pathname[hashed_name.len..], file_path.name.ext); - const dir = if (transpiler.options.output_dir_handle) |output_handle| bun.toFD(output_handle.fd) else .zero; output_file.value = .{ .copy = options.OutputFile.FileOperation{ .pathname = pathname, - .dir = dir, + .dir = if (transpiler.options.output_dir_handle) |output_handle| + .fromStdDir(output_handle) + else + .invalid, .is_outdir = true, }, }; diff --git a/src/watcher/INotifyWatcher.zig b/src/watcher/INotifyWatcher.zig index 03c57615c2..9f962a4bec 100644 --- a/src/watcher/INotifyWatcher.zig +++ b/src/watcher/INotifyWatcher.zig @@ -98,7 +98,7 @@ pub fn init(this: *INotifyWatcher, _: []const u8) !void { } // TODO: convert to bun.sys.Error - this.fd = bun.toFD(try std.posix.inotify_init1(IN.CLOEXEC)); + this.fd = .fromNative(try std.posix.inotify_init1(IN.CLOEXEC)); this.eventlist_bytes = &(try bun.default_allocator.alignedAlloc(EventListBytes, @alignOf(Event), 1))[0]; log("{} init", .{this.fd}); } @@ -219,7 +219,7 @@ pub fn read(this: *INotifyWatcher) bun.JSC.Maybe([]const *align(1) Event) { pub fn stop(this: *INotifyWatcher) void { log("{} stop", .{this.fd}); if (this.fd != bun.invalid_fd) { - _ = bun.sys.close(this.fd); + this.fd.close(); this.fd = bun.invalid_fd; } } diff --git a/src/watcher/KEventWatcher.zig b/src/watcher/KEventWatcher.zig index cf823c59d2..ab3359459a 100644 --- a/src/watcher/KEventWatcher.zig +++ b/src/watcher/KEventWatcher.zig @@ -7,20 +7,19 @@ const KEvent = std.c.Kevent; // Everything being watched eventlist_index: EventListIndex = 0, -fd: bun.FileDescriptor = bun.invalid_fd, +fd: bun.FD.Optional = .none, const changelist_count = 128; pub fn init(this: *KEventWatcher, _: []const u8) !void { const fd = try std.posix.kqueue(); if (fd == 0) return error.KQueueError; - this.fd = bun.toFD(fd); + this.fd = .init(.fromNative(fd)); } pub fn stop(this: *KEventWatcher) void { - if (this.fd.isValid()) { - _ = bun.sys.close(this.fd); - this.fd = bun.invalid_fd; + if (this.fd.take()) |fd| { + fd.close(); } } @@ -37,7 +36,8 @@ pub fn watchEventFromKEvent(kevent: KEvent) Watcher.Event { } pub fn watchLoopCycle(this: *Watcher) bun.JSC.Maybe(void) { - bun.assert(this.platform.fd.isValid()); + const fd: bun.FD = this.platform.fd.unwrap() orelse + @panic("KEventWatcher has an invalid file descriptor"); // not initialized each time var changelist_array: [changelist_count]KEvent = undefined; @@ -47,7 +47,7 @@ pub fn watchLoopCycle(this: *Watcher) bun.JSC.Maybe(void) { defer Output.flush(); var count = std.posix.system.kevent( - this.platform.fd.cast(), + fd.native(), changelist, 0, changelist, @@ -59,7 +59,7 @@ pub fn watchLoopCycle(this: *Watcher) bun.JSC.Maybe(void) { if (count < 128 / 2) { const remain = 128 - count; const extra = std.posix.system.kevent( - this.platform.fd.cast(), + fd.native(), changelist[@intCast(count)..].ptr, 0, changelist[@intCast(count)..].ptr, diff --git a/src/windows.zig b/src/windows.zig index dca9a7d0c4..d83fa39597 100644 --- a/src/windows.zig +++ b/src/windows.zig @@ -107,7 +107,7 @@ pub fn GetFileType(hFile: win32.HANDLE) win32.DWORD { const rc = function(hFile); if (comptime Environment.enable_logs) - bun.sys.syslog("GetFileType({}) = {d}", .{ bun.toFD(hFile), rc }); + bun.sys.syslog("GetFileType({}) = {d}", .{ bun.FD.fromNative(hFile), rc }); return rc; } diff --git a/src/windows_c.zig b/src/windows_c.zig index 21a91e741f..4c629120ff 100644 --- a/src/windows_c.zig +++ b/src/windows_c.zig @@ -1260,7 +1260,7 @@ pub fn renameAtW( .result => |fd| break :brk fd, } }; - defer _ = bun.sys.close(src_fd); + defer src_fd.close(); return moveOpenedFileAt(src_fd, new_dir_fd, new_path_w, replace_if_exists); } @@ -1352,7 +1352,7 @@ pub fn moveOpenedFileAtLoose( .err => |e| return .{ .err = e }, .result => |fd| fd, }; - defer _ = bun.sys.close(fd); + defer fd.close(); const basename = new_path[last_slash + 1 ..]; return moveOpenedFileAt(src_fd, fd, basename, replace_if_exists); diff --git a/test/internal/ban-words.test.ts b/test/internal/ban-words.test.ts index 9c118175ec..13efd3e0c9 100644 --- a/test/internal/ban-words.test.ts +++ b/test/internal/ban-words.test.ts @@ -5,20 +5,21 @@ import path from "path"; const words: Record = { " != undefined": { reason: "This is by definition Undefined Behavior." }, " == undefined": { reason: "This is by definition Undefined Behavior." }, + "undefined != ": { reason: "This is by definition Undefined Behavior." }, + "undefined == ": { reason: "This is by definition Undefined Behavior." }, + '@import("root").bun.': { reason: "Only import 'bun' once" }, "std.debug.assert": { reason: "Use bun.assert instead", limit: 26 }, "std.debug.dumpStackTrace": { reason: "Use bun.handleErrorReturnTrace or bun.crash_handler.dumpStackTrace instead" }, "std.debug.print": { reason: "Don't let this be committed", limit: 0 }, "std.mem.indexOfAny(u8": { reason: "Use bun.strings.indexOfAny", limit: 3 }, - "undefined != ": { reason: "This is by definition Undefined Behavior." }, - "undefined == ": { reason: "This is by definition Undefined Behavior." }, - "bun.toFD(std.fs.cwd().fd)": { reason: "Use bun.FD.cwd()" }, "std.StringArrayHashMapUnmanaged(": { reason: "bun.StringArrayHashMapUnmanaged has a faster `eql`", limit: 12 }, "std.StringArrayHashMap(": { reason: "bun.StringArrayHashMap has a faster `eql`", limit: 1 }, "std.StringHashMapUnmanaged(": { reason: "bun.StringHashMapUnmanaged has a faster `eql`" }, "std.StringHashMap(": { reason: "bun.StringHashMap has a faster `eql`" }, "std.enums.tagName(": { reason: "Use bun.tagName instead", limit: 2 }, "std.unicode": { reason: "Use bun.strings instead", limit: 36 }, + "allocator.ptr ==": { reason: "The std.mem.Allocator context pointer can be undefined, which makes this comparison undefined behavior" }, "allocator.ptr !=": { reason: "The std.mem.Allocator context pointer can be undefined, which makes this comparison undefined behavior", limit: 1 }, "== allocator.ptr": { reason: "The std.mem.Allocator context pointer can be undefined, which makes this comparison undefined behavior" }, @@ -27,8 +28,16 @@ const words: Record "alloc.ptr !=": { reason: "The std.mem.Allocator context pointer can be undefined, which makes this comparison undefined behavior" }, "== alloc.ptr": { reason: "The std.mem.Allocator context pointer can be undefined, which makes this comparison undefined behavior" }, "!= alloc.ptr": { reason: "The std.mem.Allocator context pointer can be undefined, which makes this comparison undefined behavior" }, + [String.raw`: [a-zA-Z0-9_\.\*\?\[\]\(\)]+ = undefined,`]: { reason: "Do not default a struct field to undefined", limit: 242, regex: true }, + "usingnamespace": { reason: "Zig deprecates this, and will not support it in incremental compilation.", limit: 370 }, + + "std.fs.Dir": { reason: "Prefer bun.sys + bun.FD instead of std.fs", limit: 180 }, + "std.fs.cwd": { reason: "Prefer bun.FD.cwd()", limit: 103 }, + "std.fs.File": { reason: "Prefer bun.sys + bun.FD instead of std.fs", limit: 71 }, + ".stdFile()": { reason: "Prefer bun.sys + bun.FD instead of std.fs.File. Zig hides 'errno' when Bun wants to match libuv", limit: 18 }, + ".stdDir()": { reason: "Prefer bun.sys + bun.FD instead of std.fs.File. Zig hides 'errno' when Bun wants to match libuv", limit: 48 }, }; const words_keys = [...Object.keys(words)]; diff --git a/test/js/bun/test/snapshot-tests/snapshots/snapshot.test.ts b/test/js/bun/test/snapshot-tests/snapshots/snapshot.test.ts index 02c484dc7b..6aea9871fb 100644 --- a/test/js/bun/test/snapshot-tests/snapshots/snapshot.test.ts +++ b/test/js/bun/test/snapshot-tests/snapshots/snapshot.test.ts @@ -1,7 +1,7 @@ import { $, spawnSync } from "bun"; import { readFileSync, writeFileSync } from "fs"; import { describe, expect, it, test } from "bun:test"; -import { bunEnv, bunExe, DirectoryTree, tempDirWithFiles } from "harness"; +import { bunEnv, bunExe, DirectoryTree, isDebug, tempDirWithFiles } from "harness"; function test1000000(arg1: any, arg218718132: any) {} @@ -185,7 +185,7 @@ class SnapshotTester { contents: string, opts: { shouldNotError?: boolean; shouldGrow?: boolean; skipSnapshot?: boolean } = {}, ) { - test(label, async () => await this.update(contents, opts)); + test(label, async () => await this.update(contents, opts), isDebug ? 100_000 : 5_000); } async update( contents: string,