Skip to content

Commit

Permalink
Better way to check if a directory exists (#10235)
Browse files Browse the repository at this point in the history
* Better way to check if a directory exists

* Update sys.zig

* Fix windows build

* Add missing file
  • Loading branch information
Jarred-Sumner authored Apr 13, 2024
1 parent f6b9c0c commit 8d49a3e
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 6 deletions.
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(bun.toFD(fd), &(std.os.toPosixPath(subpath) catch return false), 0, 0) == 0;
Expand Down

0 comments on commit 8d49a3e

Please sign in to comment.