Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better way to check if a directory exists #10235

Merged
merged 4 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/bun.js/node/types.zig
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,9 @@ pub fn Maybe(comptime ReturnTypeT: type, comptime ErrorTypeT: type) type {

pub inline fn errnoSysFd(rc: anytype, syscall: Syscall.Tag, fd: bun.FileDescriptor) ?@This() {
if (comptime Environment.isWindows) {
if (rc != 0) return null;
if (comptime @TypeOf(rc) == std.os.windows.NTSTATUS) {} else {
if (rc != 0) return null;
}
}
return switch (Syscall.getErrno(rc)) {
.SUCCESS => null,
Expand All @@ -268,7 +270,9 @@ pub fn Maybe(comptime ReturnTypeT: type, comptime ErrorTypeT: type) type {
@compileError("Do not pass WString path to errnoSysP, it needs the path encoded as utf8");
}
if (comptime Environment.isWindows) {
if (rc != 0) return null;
if (comptime @TypeOf(rc) == std.os.windows.NTSTATUS) {} else {
if (rc != 0) return null;
}
}
return switch (Syscall.getErrno(rc)) {
.SUCCESS => null,
Expand Down
5 changes: 1 addition & 4 deletions src/install/install.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3338,10 +3338,7 @@ pub const PackageManager = struct {
}

pub fn isFolderInCache(this: *PackageManager, folder_path: stringZ) bool {
// TODO: is this slow?
var dir = this.getCacheDirectory().openDirZ(folder_path, .{}) catch return false;
dir.close();
return true;
return bun.sys.directoryExistsAt(this.getCacheDirectory(), folder_path).unwrap() catch false;
}

pub fn pathForCachedNPMPath(
Expand Down
67 changes: 67 additions & 0 deletions src/sys.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2118,6 +2118,73 @@ pub fn exists(path: []const u8) bool {
@compileError("TODO: existsOSPath");
}

pub fn directoryExistsAt(dir_: anytype, subpath: anytype) JSC.Maybe(bool) {
const has_sentinel = std.meta.sentinel(@TypeOf(subpath)) != null;
const dir_fd = bun.toFD(dir_);
if (comptime Environment.isWindows) {
var wbuf: bun.WPathBuffer = undefined;
const path = bun.strings.toNTPath(&wbuf, subpath);
const path_len_bytes: u16 = @truncate(path.len * 2);
var nt_name = w.UNICODE_STRING{
.Length = path_len_bytes,
.MaximumLength = path_len_bytes,
.Buffer = @constCast(path.ptr),
};
var attr = w.OBJECT_ATTRIBUTES{
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),
.RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(path))
null
else if (dir_fd == bun.invalid_fd)
std.fs.cwd().fd
else
dir_fd.cast(),
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
.ObjectName = &nt_name,
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
var basic_info: w.FILE_BASIC_INFORMATION = undefined;
const rc = kernel32.NtQueryAttributesFile(&attr, &basic_info);
if (JSC.Maybe(bool).errnoSysP(rc, .access, subpath)) |err| {
syslog("NtQueryAttributesFile({}, {}, O_DIRECTORY | O_RDONLY, 0) = {}", .{ dir_fd, bun.fmt.fmtOSPath(path, .{}), err });
return err;
}

const is_dir = basic_info.FileAttributes != kernel32.INVALID_FILE_ATTRIBUTES and
basic_info.FileAttributes & kernel32.FILE_ATTRIBUTE_DIRECTORY != 0;
syslog("NtQueryAttributesFile({}, {}, O_DIRECTORY | O_RDONLY, 0) = {d}", .{ dir_fd, bun.fmt.fmtOSPath(path, .{}), @intFromBool(is_dir) });

return .{
.result = is_dir,
};
}

if (comptime !has_sentinel) {
const path = std.os.toPosixPath(subpath) catch return JSC.Maybe(bool){ .err = Error.oom };
return directoryExistsAt(dir_fd, path);
}

if (comptime Environment.isLinux) {
// avoid loading the libc symbol for this to reduce chances of GLIBC minimum version requirements
const rc = linux.faccessat(dir_fd.cast(), subpath, linux.O.DIRECTORY | linux.O.RDONLY, 0);
syslog("faccessat({}, {}, O_DIRECTORY | O_RDONLY, 0) = {d}", .{ dir_fd, bun.fmt.fmtOSPath(subpath, .{}), rc });
if (rc == 0) {
return JSC.Maybe(bool){ .result = true };
}

return JSC.Maybe(bool){ .result = false };
}

// on toher platforms use faccessat from libc
const rc = std.c.faccessat(dir_fd.cast(), subpath, std.os.O.DIRECTORY | std.os.O.RDONLY, 0);
syslog("faccessat({}, {}, O_DIRECTORY | O_RDONLY, 0) = {d}", .{ dir_fd, bun.fmt.fmtOSPath(subpath, .{}), rc });
if (rc == 0) {
return JSC.Maybe(bool){ .result = true };
}

return JSC.Maybe(bool){ .result = false };
}

pub fn existsAt(fd: bun.FileDescriptor, subpath: []const u8) bool {
if (comptime Environment.isPosix) {
return system.faccessat(fd.cast(), &(std.os.toPosixPath(subpath) catch return false), 0, 0) == 0;
Expand Down
Loading