Compare commits

...

1 Commits

Author SHA1 Message Date
Jarred Sumner
8755c8aca9 wip junctions 2024-04-10 05:22:32 -07:00
9 changed files with 86 additions and 155 deletions

View File

@@ -282,15 +282,15 @@ pub fn NewIterator(comptime use_windows_ospath: bool) type {
if (mem.eql(u16, dir_info_name, &[_]u16{'.'}) or mem.eql(u16, dir_info_name, &[_]u16{ '.', '.' }))
continue;
const kind = blk: {
const kind: Entry.Kind = blk: {
const attrs = dir_info.FileAttributes;
if (attrs == w.INVALID_FILE_ATTRIBUTES) break :blk .unknown;
const isdir = attrs & w.FILE_ATTRIBUTE_DIRECTORY != 0;
const islink = attrs & w.FILE_ATTRIBUTE_REPARSE_POINT != 0;
// on windows symlinks can be directories, too. We prioritize the
// "sym_link" kind over the "directory" kind
if (islink) break :blk Entry.Kind.sym_link;
if (isdir) break :blk Entry.Kind.directory;
break :blk Entry.Kind.file;
if (islink and isdir) break :blk .sym_link_directory;
if (islink) break :blk .sym_link;
if (isdir) break :blk .directory;
break :blk .file;
};
if (use_windows_ospath) {

View File

@@ -1753,7 +1753,7 @@ pub const Dirent = struct {
// not publicly exposed
kind: Kind,
pub const Kind = std.fs.File.Kind;
pub const Kind = bun.sys.File.Kind;
pub usingnamespace JSC.Codegen.JSDirent;
pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*Dirent {
@@ -1775,49 +1775,49 @@ pub const Dirent = struct {
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) callconv(.C) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.block_device);
return JSC.JSValue.jsBoolean(this.kind == .block_device);
}
pub fn isCharacterDevice(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) callconv(.C) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.character_device);
return JSC.JSValue.jsBoolean(this.kind == .character_device);
}
pub fn isDirectory(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) callconv(.C) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.directory);
return JSC.JSValue.jsBoolean(this.kind == .directory);
}
pub fn isFIFO(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) callconv(.C) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.named_pipe or this.kind == std.fs.File.Kind.event_port);
return JSC.JSValue.jsBoolean(this.kind == .named_pipe or this.kind == .event_port);
}
pub fn isFile(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) callconv(.C) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.file);
return JSC.JSValue.jsBoolean(this.kind == .file);
}
pub fn isSocket(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) callconv(.C) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.unix_domain_socket);
return JSC.JSValue.jsBoolean(this.kind == .unix_domain_socket);
}
pub fn isSymbolicLink(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) callconv(.C) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.sym_link);
return JSC.JSValue.jsBoolean(this.kind == .sym_link or this.kind == .sym_link_directory);
}
pub fn finalize(this: *Dirent) callconv(.C) void {

View File

@@ -15,7 +15,8 @@ const builtin = @import("builtin");
const os = std.os;
const mem = std.mem;
const Stat = std.fs.File.Stat;
const Kind = std.fs.File.Kind;
const Kind = bun.sys.File.Kind;
const StatError = std.fs.File.StatError;
const errno = os.errno;
const mode_t = bun.Mode;
@@ -187,15 +188,15 @@ pub fn copyFileZSlowWithHandle(in_handle: bun.FileDescriptor, to_dir: bun.FileDe
}
}
pub fn kindFromMode(mode: mode_t) std.fs.File.Kind {
pub fn kindFromMode(mode: mode_t) bun.sys.File.Kind {
return switch (mode & bun.S.IFMT) {
bun.S.IFBLK => std.fs.File.Kind.block_device,
bun.S.IFCHR => std.fs.File.Kind.character_device,
bun.S.IFDIR => std.fs.File.Kind.directory,
bun.S.IFIFO => std.fs.File.Kind.named_pipe,
bun.S.IFLNK => std.fs.File.Kind.sym_link,
bun.S.IFREG => std.fs.File.Kind.file,
bun.S.IFSOCK => std.fs.File.Kind.unix_domain_socket,
bun.S.IFBLK => .block_device,
bun.S.IFCHR => .character_device,
bun.S.IFDIR => .directory,
bun.S.IFIFO => .named_pipe,
bun.S.IFLNK => .sym_link,
bun.S.IFREG => .file,
bun.S.IFSOCK => .unix_domain_socket,
else => .unknown,
};
}

View File

@@ -4,7 +4,7 @@ const builtin = @import("builtin");
const os = std.os;
const mem = std.mem;
const Stat = std.fs.File.Stat;
const Kind = std.fs.File.Kind;
const Kind = bun.sys.File.Kind;
const StatError = std.fs.File.StatError;
const off_t = std.c.off_t;
const errno = os.errno;

View File

@@ -158,7 +158,7 @@ pub const FileSystem = struct {
pub fn addEntry(dir: *DirEntry, prev_map: ?*EntryMap, entry: *const bun.DirIterator.IteratorResult, allocator: std.mem.Allocator, comptime Iterator: type, iterator: Iterator) !void {
const name_slice = entry.name.slice();
const _kind: Entry.Kind = switch (entry.kind) {
.directory => .dir,
.sym_link_directory, .directory => .dir,
// This might be wrong!
.sym_link => .file,
.file => .file,
@@ -208,7 +208,7 @@ pub const FileSystem = struct {
// Call "stat" lazily for performance. The "@material-ui/icons" package
// contains a directory with over 11,000 entries in it and running "stat"
// for each entry was a big performance issue for that package.
.need_stat = entry.kind == .sym_link,
.need_stat = entry.kind == .sym_link or entry.kind == .sym_link_directory,
.cache = .{
.symlink = PathString.empty,
.kind = _kind,
@@ -1136,19 +1136,21 @@ pub const FileSystem = struct {
allocator: std.mem.Allocator,
path: string,
_size: ?usize,
file: std.fs.File,
file_: std.fs.File,
comptime use_shared_buffer: bool,
shared_buffer: *MutableString,
comptime stream: bool,
) !PathContentsPair {
FileSystem.setMaxFd(file.handle);
_ = stream; // autofix
FileSystem.setMaxFd(file_.handle);
const file = bun.sys.File.from(file_);
// Skip the extra file.stat() call when possible
var size = _size orelse (file.getEndPos() catch |err| {
const size = _size orelse (file.getEndPos().unwrap() catch |err| {
fs.readFileError(path, err);
return err;
});
debug("stat({d}) = {d}", .{ file.handle, size });
debug("stat({}) = {d}", .{ file.handle, size });
// Skip the pread call for empty files
// Otherwise will get out of bounds errors
@@ -1170,52 +1172,19 @@ pub const FileSystem = struct {
// As a mitigation, we can just keep one buffer forever and re-use it for the parsed files
if (use_shared_buffer) {
shared_buffer.reset();
var offset: u64 = 0;
try shared_buffer.growBy(size + 1);
shared_buffer.list.expandToCapacity();
{
var list = shared_buffer.list.toManaged(bun.default_allocator);
defer shared_buffer.list = list.moveToUnmanaged();
const rc = file.readToEndWithArrayListAndSize(&list, size);
// if you press save on a large file we might not read all the
// bytes in the first few pread() calls. we only handle this on
// stream because we assume that this only realistically happens
// during HMR
while (true) {
// We use pread to ensure if the file handle was open, it doesn't seek from the last position
const prev_file_pos = if (comptime Environment.isWindows) try file.getPos() else 0;
const read_count = file.preadAll(shared_buffer.list.items[offset..], offset) catch |err| {
fs.readFileError(path, err);
return err;
};
if (comptime Environment.isWindows) try file.seekTo(prev_file_pos);
shared_buffer.list.items = shared_buffer.list.items[0 .. read_count + offset];
file_contents = shared_buffer.list.items;
debug("pread({d}, {d}) = {d}", .{ file.handle, size, read_count });
if (comptime stream) {
// check again that stat() didn't change the file size
// another reason to only do this when stream
const new_size = file.getEndPos() catch |err| {
fs.readFileError(path, err);
return err;
};
offset += read_count;
// don't infinite loop is we're still not reading more
if (read_count == 0) break;
if (offset < new_size) {
try shared_buffer.growBy(new_size - size);
shared_buffer.list.expandToCapacity();
size = new_size;
continue;
}
switch (rc) {
.result => {
file_contents = list.items;
},
.err => {
return bun.errnoToZigErr(rc.err.getErrno());
},
}
break;
}
if (shared_buffer.list.capacity > file_contents.len) {
file_contents.ptr[file_contents.len] = 0;
}
if (strings.BOM.detect(file_contents)) |bom| {
@@ -1223,20 +1192,14 @@ pub const FileSystem = struct {
file_contents = try bom.removeAndConvertToUTF8WithoutDealloc(allocator, &shared_buffer.list);
}
} else {
// We use pread to ensure if the file handle was open, it doesn't seek from the last position
var buf = try allocator.alloc(u8, size + 1);
const result = file.readToEnd(allocator);
if (comptime Environment.isWindows) _ = file.resetPosition();
file_contents = result.bytes.items;
// stick a zero at the end
buf[size] = 0;
const prev_file_pos = if (comptime Environment.isWindows) try file.getPos() else 0;
const read_count = file.preadAll(buf, 0) catch |err| {
fs.readFileError(path, err);
return err;
};
if (comptime Environment.isWindows) try file.seekTo(prev_file_pos);
file_contents = buf[0..read_count];
debug("pread({d}, {d}) = {d}", .{ file.handle, size, read_count });
if (result.err) |err| {
allocator.free(file_contents);
return bun.errnoToZigErr(err.getErrno());
}
if (strings.BOM.detect(file_contents)) |bom| {
debug("Convert {s} BOM", .{@tagName(bom)});
@@ -1247,60 +1210,6 @@ pub const FileSystem = struct {
return PathContentsPair{ .path = Path.init(path), .contents = file_contents };
}
pub fn kindFromAbsolute(
fs: *RealFS,
absolute_path: [:0]const u8,
existing_fd: StoredFileDescriptorType,
store_fd: bool,
) !Entry.Cache {
var outpath: [bun.MAX_PATH_BYTES]u8 = undefined;
const stat = try C.lstat_absolute(absolute_path);
const is_symlink = stat.kind == std.fs.File.Kind.SymLink;
var _kind = stat.kind;
var cache = Entry.Cache{
.kind = Entry.Kind.file,
.symlink = PathString.empty,
};
var symlink: []const u8 = "";
if (is_symlink) {
var file = try if (existing_fd != 0)
std.fs.File{ .handle = existing_fd }
else if (store_fd)
std.fs.openFileAbsoluteZ(absolute_path, .{ .mode = .read_only })
else
bun.openFileForPath(absolute_path);
setMaxFd(file.handle);
defer {
if ((!store_fd or fs.needToCloseFiles()) and existing_fd == 0) {
file.close();
} else if (comptime FeatureFlags.store_file_descriptors) {
cache.fd = file.handle;
}
}
const _stat = try file.stat();
symlink = try bun.getFdPath(file.handle, &outpath);
_kind = _stat.kind;
}
std.debug.assert(_kind != .SymLink);
if (_kind == .Directory) {
cache.kind = .dir;
} else {
cache.kind = .file;
}
if (symlink.len > 0) {
cache.symlink = PathString.init(try FilenameStore.instance.append([]const u8, symlink));
}
return cache;
}
pub fn kind(
fs: *RealFS,
_dir: string,
@@ -1324,19 +1233,39 @@ pub const FileSystem = struct {
const absolute_path_c: [:0]const u8 = outpath[0..entry_path.len :0];
if (comptime bun.Environment.isWindows) {
var file = try std.fs.openFileAbsoluteZ(absolute_path_c, .{ .mode = .read_only });
defer file.close();
const metadata = try file.metadata();
cache.kind = switch (metadata.kind()) {
.directory => .dir,
.sym_link => .file,
else => .file,
};
var wbuf: bun.WPathBuffer = undefined;
const wpath = bun.strings.toNTPath(&wbuf, absolute_path_c);
const attr = bun.windows.GetFileAttributesW(wpath);
if (attr == bun.windows.INVALID_FILE_ATTRIBUTES) {
return error.FileNotFound;
}
const is_dir = (attr & bun.windows.FILE_ATTRIBUTE_DIRECTORY) != 0;
const is_link = (attr & bun.windows.FILE_ATTRIBUTE_REPARSE_POINT) == 0;
if (is_dir) {
cache.kind = .dir;
} else {
cache.kind = .file;
}
if (is_link) {
const fd = switch (bun.sys.openatWindows(std.fs.cwd(), wpath, 0)) {
.err => {
return cache;
},
.result => |rc| rc,
};
defer _ = bun.sys.close(fd);
const symlink_path = try bun.getFdPath(fd, &outpath);
cache.symlink = PathString.init(try FilenameStore.instance.append([]const u8, symlink_path));
}
return cache;
}
const stat = try C.lstat_absolute(absolute_path_c);
const is_symlink = stat.kind == std.fs.File.Kind.sym_link;
const is_symlink = stat.kind == .sym_link;
var _kind = stat.kind;
var symlink: []const u8 = "";

View File

@@ -348,7 +348,7 @@ pub const BufferReadStream = struct {
// }
};
const Kind = std.fs.File.Kind;
const Kind = bun.sys.File.Kind;
pub const Archive = struct {
// impl: *lib.archive = undefined,

View File

@@ -600,7 +600,7 @@ pub const PackageJSON = struct {
false,
null,
) catch |err| {
if (err != error.IsDir) {
if (err != error.IsDir and err != error.EISDIR) {
r.log.addErrorFmt(null, logger.Loc.Empty, allocator, "Cannot read file \"{s}\": {s}", .{ r.prettyPath(fs.Path.init(input_path)), @errorName(err) }) catch unreachable;
}

View File

@@ -17,6 +17,7 @@ seed: u64 = 0,
const NameBufferList = std.ArrayList(bun.OSPathChar);
const Dir = std.fs.Dir;
const Kind = bun.sys.File.Kind;
const WrappedIterator = DirIterator.NewWrappedIterator(if (Environment.isWindows) .u16 else .u8);
pub const WalkerEntry = struct {
@@ -26,7 +27,7 @@ pub const WalkerEntry = struct {
dir: Dir,
basename: OSPathSlice,
path: OSPathSlice,
kind: Dir.Entry.Kind,
kind: Kind,
};
const StackItem = struct {
@@ -65,7 +66,7 @@ pub fn next(self: *Walker) !?WalkerEntry {
},
// we don't know what it is for a symlink
.sym_link => {
.sym_link_directory, .sym_link => {
if (std.mem.indexOfScalar(
u64,
self.skip_all,

View File

@@ -5,7 +5,7 @@ const win32 = std.os.windows;
const os = std.os;
const mem = std.mem;
const Stat = std.fs.File.Stat;
const Kind = std.fs.File.Kind;
const Kind = bun.sys.File.Kind;
const StatError = std.fs.File.StatError;
pub fn getTotalMemory() usize {