diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/javascript/jsc/api/bun.zig | 51 | ||||
-rw-r--r-- | src/javascript/jsc/node/syscall.zig | 70 |
2 files changed, 121 insertions, 0 deletions
diff --git a/src/javascript/jsc/api/bun.zig b/src/javascript/jsc/api/bun.zig index 0eaab1dbb..b43771f07 100644 --- a/src/javascript/jsc/api/bun.zig +++ b/src/javascript/jsc/api/bun.zig @@ -1019,6 +1019,10 @@ pub const Class = NewClass( .rfn = Bun.allocUnsafe, .ts = .{}, }, + .mmap = .{ + .rfn = Bun.mmapFile, + .ts = .{}, + }, .generateHeapSnapshot = .{ .rfn = Bun.generateHeapSnapshot, .ts = d.ts{}, @@ -1158,6 +1162,53 @@ pub fn allocUnsafe( ).toJSObjectRef(ctx, null); } +pub fn mmapFile( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, +) js.JSValueRef { + var args = JSC.Node.ArgumentsSlice.from(arguments); + + var buf: [bun.MAX_PATH_BYTES]u8 = undefined; + const path = getFilePath(ctx, arguments[0..@minimum(1, arguments.len)], &buf, exception) orelse return null; + + buf[path.len] = 0; + + const buf_z: [:0]const u8 = buf[0..path.len :0]; + + const flags = v: { + _ = args.nextEat(); + const opts = args.nextEat() orelse break :v std.os.MAP.SHARED; + const sync = opts.get(ctx.ptr(), "sync") orelse JSC.JSValue.jsBoolean(false); + const shared = opts.get(ctx.ptr(), "shared") orelse JSC.JSValue.jsBoolean(true); + const sync_flags = if (@hasDecl(std.os, "SYNC")) std.os.MAP.SYNC | std.os.MAP.SHARED_VALIDATE else 0; + + var flags: u32 = 0; + if (sync.toBoolean()) flags |= sync_flags; + if (shared.toBoolean()) flags |= std.os.MAP.SHARED else flags |= std.os.MAP.PRIVATE; + + break :v flags; + }; + + const map = switch (JSC.Node.Syscall.mmapFile(buf_z, flags)) { + .result => |map| map, + + .err => |err| { + exception.* = err.toJS(ctx); + return null; + }, + }; + + return JSC.C.JSObjectMakeTypedArrayWithBytesNoCopy(ctx, JSC.C.JSTypedArrayType.kJSTypedArrayTypeUint8Array, @ptrCast(?*anyopaque, map.ptr), map.len, struct { + pub fn x(ptr: ?*anyopaque, size: ?*anyopaque) callconv(.C) void { + _ = JSC.Node.Syscall.munmap(@ptrCast([*]align(std.mem.page_size) u8, @alignCast(std.mem.page_size, ptr))[0..@ptrToInt(size)]); + } + }.x, @intToPtr(?*anyopaque, map.len), exception); +} + pub fn getTranspilerConstructor( _: void, ctx: js.JSContextRef, diff --git a/src/javascript/jsc/node/syscall.zig b/src/javascript/jsc/node/syscall.zig index 75ea5be89..bf4d6189e 100644 --- a/src/javascript/jsc/node/syscall.zig +++ b/src/javascript/jsc/node/syscall.zig @@ -51,6 +51,8 @@ pub const Tag = enum(u8) { lutimes, mkdir, mkdtemp, + mmap, + munmap, open, pread, pwrite, @@ -399,6 +401,74 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) Maybe([]u8) { } } +/// Use of a mapped region can result in these signals: +/// * SIGSEGV - Attempted write into a region mapped as read-only. +/// * SIGBUS - Attempted access to a portion of the buffer that does not correspond to the file +fn sysmmap( + ptr: ?[*]align(mem.page_size) u8, + length: usize, + prot: u32, + flags: u32, + fd: os.fd_t, + offset: u64, +) Maybe([]align(mem.page_size) u8) { + const mmap_sym = if (builtin.os.tag == .linux and builtin.link_libc) + system.mmap64 + else + system.mmap; + + const ioffset = @bitCast(i64, offset); // the OS treats this as unsigned + const rc = mmap_sym(ptr, length, prot, flags, fd, ioffset); + + _ = if (builtin.link_libc) blk: { + if (rc != std.c.MAP.FAILED) return .{ .result = @ptrCast([*]align(mem.page_size) u8, @alignCast(mem.page_size, rc))[0..length] }; + break :blk rc; + } else blk: { + const err = os.errno(rc); + if (err == .SUCCESS) return .{ .result = @intToPtr([*]align(mem.page_size) u8, rc)[0..length] }; + break :blk rc; + }; + + return Maybe([]align(mem.page_size) u8).errnoSys(@ptrToInt(rc), .mmap).?; +} + +pub fn mmapFile(path: [:0]const u8, flags: u32) Maybe([]align(mem.page_size) u8) { + const fd = switch (open(path, os.O.RDWR, 0)) { + .result => |fd| fd, + .err => |err| return .{ .err = err }, + }; + + const size = switch (stat(path)) { + .result => |stat| stat.size, + .err => |err| { + _ = close(fd); + return .{ .err = err }; + }, + }; + + const map = switch (sysmmap(null, @intCast(usize, size), os.PROT.READ | os.PROT.WRITE, flags, fd, 0)) { + .result => |map| map, + + .err => |err| { + _ = close(fd); + return .{ .err = err }; + }, + }; + + if (close(fd)) |err| { + _ = munmap(map); + return .{ .err = err }; + } + + return .{ .result = map }; +} + +pub fn munmap(memory: []align(mem.page_size) const u8) Maybe(void) { + if (Maybe(void).errnoSys(system.munmap(memory.ptr, memory.len), .munmap)) |err| { + return err; + } else return Maybe(void).success; +} + pub const Error = struct { const max_errno_value = brk: { const errno_values = std.enums.values(os.E); |