| ... | ... | @@ -1870,10 +1870,14 @@ fn dirStatPathPosix( |
| 1870 | 1870 | |
| 1871 | 1871 | const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0; |
| 1872 | 1872 | |
| 1873 | return posixStatPath(current_thread, dir.handle, sub_path_posix, flags); |
| 1874 | } |
| 1875 | |
| 1876 | fn posixStatPath(current_thread: *Thread, dir_fd: posix.fd_t, sub_path: [:0]const u8, flags: u32) Dir.StatPathError!File.Stat { |
| 1873 | 1877 | try current_thread.beginSyscall(); |
| 1874 | 1878 | while (true) { |
| 1875 | 1879 | var stat = std.mem.zeroes(posix.Stat); |
| 1876 | | switch (posix.errno(fstatat_sym(dir.handle, sub_path_posix, &stat, flags))) { |
| 1880 | switch (posix.errno(fstatat_sym(dir_fd, sub_path, &stat, flags))) { |
| 1877 | 1881 | .SUCCESS => { |
| 1878 | 1882 | current_thread.endSyscall(); |
| 1879 | 1883 | return statFromPosix(&stat); |
| ... | ... | @@ -3273,6 +3277,12 @@ fn dirClose(userdata: ?*anyopaque, dirs: []const Dir) void { |
| 3273 | 3277 | |
| 3274 | 3278 | const dirRead = switch (native_os) { |
| 3275 | 3279 | .linux => dirReadLinux, |
| 3280 | .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => dirReadDarwin, |
| 3281 | .freebsd, .netbsd, .dragonfly, .openbsd => dirReadBsd, |
| 3282 | .illumos => dirReadIllumos, |
| 3283 | .haiku => dirReadHaiku, |
| 3284 | .windows => dirReadWindows, |
| 3285 | .wasi => dirReadWasi, |
| 3276 | 3286 | else => dirReadUnimplemented, |
| 3277 | 3287 | }; |
| 3278 | 3288 | |
| ... | ... | @@ -3339,7 +3349,6 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir |
| 3339 | 3349 | dr.index = next_index; |
| 3340 | 3350 | |
| 3341 | 3351 | const name = std.mem.sliceTo(@as([*:0]u8, @ptrCast(&linux_entry.name)), 0); |
| 3342 | | // Skip "." and ".." entries. |
| 3343 | 3352 | if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue; |
| 3344 | 3353 | |
| 3345 | 3354 | const entry_kind: File.Kind = switch (linux_entry.type) { |
| ... | ... | @@ -3362,6 +3371,278 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir |
| 3362 | 3371 | return buffer_index; |
| 3363 | 3372 | } |
| 3364 | 3373 | |
| 3374 | fn dirReadDarwin(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { |
| 3375 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 3376 | const current_thread = Thread.getCurrent(t); |
| 3377 | const Header = extern struct { |
| 3378 | seek: i64, |
| 3379 | }; |
| 3380 | const header: *Header = @ptrCast(&dr.buffer); |
| 3381 | const header_end: usize = @sizeOf(Header); |
| 3382 | if (dr.index < header_end) { |
| 3383 | // Initialize header. |
| 3384 | dr.index = header_end; |
| 3385 | header.* = .{ .seek = 0 }; |
| 3386 | } |
| 3387 | var buffer_index: usize = 0; |
| 3388 | while (buffer.len - buffer_index != 0) { |
| 3389 | if (dr.end - dr.index == 0) { |
| 3390 | // Refill the buffer, unless we've already created references to |
| 3391 | // buffered data. |
| 3392 | if (buffer_index != 0) break; |
| 3393 | if (dr.state == .reset) { |
| 3394 | posixSeekTo(current_thread, dr.dir.handle, 0) catch |err| switch (err) { |
| 3395 | error.Unseekable => return error.Unexpected, |
| 3396 | else => |e| return e, |
| 3397 | }; |
| 3398 | dr.state = .reading; |
| 3399 | } |
| 3400 | const dents_buffer = dr.buffer[header_end..]; |
| 3401 | try current_thread.beginSyscall(); |
| 3402 | const n: usize = while (true) { |
| 3403 | const rc = posix.system.getdirentries(dr.dir.fd, dents_buffer.ptr, dents_buffer.len, &header.seek); |
| 3404 | switch (posix.errno(rc)) { |
| 3405 | .SUCCESS => { |
| 3406 | current_thread.endSyscall(); |
| 3407 | break @intCast(rc); |
| 3408 | }, |
| 3409 | .INTR => { |
| 3410 | try current_thread.checkCancel(); |
| 3411 | continue; |
| 3412 | }, |
| 3413 | .CANCELED => return current_thread.endSyscallCanceled(), |
| 3414 | else => |e| { |
| 3415 | current_thread.endSyscall(); |
| 3416 | switch (e) { |
| 3417 | .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability |
| 3418 | .FAULT => |err| return errnoBug(err), |
| 3419 | .NOTDIR => |err| return errnoBug(err), |
| 3420 | .INVAL => |err| return errnoBug(err), |
| 3421 | else => |err| return posix.unexpectedErrno(err), |
| 3422 | } |
| 3423 | }, |
| 3424 | } |
| 3425 | }; |
| 3426 | if (n == 0) { |
| 3427 | dr.state = .finished; |
| 3428 | return 0; |
| 3429 | } |
| 3430 | dr.index = header_end; |
| 3431 | dr.end = header_end + n; |
| 3432 | } |
| 3433 | const darwin_entry = @as(*align(1) posix.system.dirent, @ptrCast(&dr.buffer[dr.index])); |
| 3434 | const next_index = dr.index + darwin_entry.reclen; |
| 3435 | dr.index = next_index; |
| 3436 | |
| 3437 | const name = @as([*]u8, @ptrCast(&darwin_entry.name))[0..darwin_entry.namlen]; |
| 3438 | if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..") or (darwin_entry.ino == 0)) |
| 3439 | continue; |
| 3440 | |
| 3441 | const entry_kind: File.Kind = switch (darwin_entry.type) { |
| 3442 | posix.DT.BLK => .block_device, |
| 3443 | posix.DT.CHR => .character_device, |
| 3444 | posix.DT.DIR => .directory, |
| 3445 | posix.DT.FIFO => .named_pipe, |
| 3446 | posix.DT.LNK => .sym_link, |
| 3447 | posix.DT.REG => .file, |
| 3448 | posix.DT.SOCK => .unix_domain_socket, |
| 3449 | posix.DT.WHT => .whiteout, |
| 3450 | else => .unknown, |
| 3451 | }; |
| 3452 | buffer[buffer_index] = .{ |
| 3453 | .name = name, |
| 3454 | .kind = entry_kind, |
| 3455 | .inode = darwin_entry.ino, |
| 3456 | }; |
| 3457 | buffer_index += 1; |
| 3458 | } |
| 3459 | return buffer_index; |
| 3460 | } |
| 3461 | |
| 3462 | fn dirReadBsd(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { |
| 3463 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 3464 | const current_thread = Thread.getCurrent(t); |
| 3465 | var buffer_index: usize = 0; |
| 3466 | while (buffer.len - buffer_index != 0) { |
| 3467 | if (dr.end - dr.index == 0) { |
| 3468 | // Refill the buffer, unless we've already created references to |
| 3469 | // buffered data. |
| 3470 | if (buffer_index != 0) break; |
| 3471 | if (dr.state == .reset) { |
| 3472 | posixSeekTo(current_thread, dr.dir.handle, 0) catch |err| switch (err) { |
| 3473 | error.Unseekable => return error.Unexpected, |
| 3474 | else => |e| return e, |
| 3475 | }; |
| 3476 | dr.state = .reading; |
| 3477 | } |
| 3478 | try current_thread.beginSyscall(); |
| 3479 | const n: usize = while (true) { |
| 3480 | const rc = posix.system.getdents(dr.dir.handle, dr.buffer.ptr, dr.buffer.len); |
| 3481 | switch (posix.errno(rc)) { |
| 3482 | .SUCCESS => { |
| 3483 | current_thread.endSyscall(); |
| 3484 | break @intCast(rc); |
| 3485 | }, |
| 3486 | .INTR => { |
| 3487 | try current_thread.checkCancel(); |
| 3488 | continue; |
| 3489 | }, |
| 3490 | .CANCELED => return current_thread.endSyscallCanceled(), |
| 3491 | else => |e| { |
| 3492 | current_thread.endSyscall(); |
| 3493 | switch (e) { |
| 3494 | .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability |
| 3495 | .FAULT => |err| return errnoBug(err), |
| 3496 | .NOTDIR => |err| return errnoBug(err), |
| 3497 | .INVAL => |err| return errnoBug(err), |
| 3498 | // Introduced in freebsd 13.2: directory unlinked |
| 3499 | // but still open. To be consistent, iteration ends |
| 3500 | // if the directory being iterated is deleted |
| 3501 | // during iteration. |
| 3502 | .NOENT => { |
| 3503 | dr.state = .finished; |
| 3504 | return 0; |
| 3505 | }, |
| 3506 | else => |err| return posix.unexpectedErrno(err), |
| 3507 | } |
| 3508 | }, |
| 3509 | } |
| 3510 | }; |
| 3511 | if (n == 0) { |
| 3512 | dr.state = .finished; |
| 3513 | return 0; |
| 3514 | } |
| 3515 | dr.index = 0; |
| 3516 | dr.end = n; |
| 3517 | } |
| 3518 | const bsd_entry = @as(*align(1) posix.system.dirent, @ptrCast(&dr.buffer[dr.index])); |
| 3519 | const next_index = dr.index + |
| 3520 | if (@hasField(posix.system.dirent, "reclen")) bsd_entry.reclen else bsd_entry.reclen(); |
| 3521 | dr.index = next_index; |
| 3522 | |
| 3523 | const name = @as([*]u8, @ptrCast(&bsd_entry.name))[0..bsd_entry.namlen]; |
| 3524 | |
| 3525 | const skip_zero_fileno = switch (native_os) { |
| 3526 | // fileno=0 is used to mark invalid entries or deleted files. |
| 3527 | .openbsd, .netbsd => true, |
| 3528 | else => false, |
| 3529 | }; |
| 3530 | if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..") or |
| 3531 | (skip_zero_fileno and bsd_entry.fileno == 0)) |
| 3532 | { |
| 3533 | continue; |
| 3534 | } |
| 3535 | |
| 3536 | const entry_kind: File.Kind = switch (bsd_entry.type) { |
| 3537 | posix.DT.BLK => .block_device, |
| 3538 | posix.DT.CHR => .character_device, |
| 3539 | posix.DT.DIR => .directory, |
| 3540 | posix.DT.FIFO => .named_pipe, |
| 3541 | posix.DT.LNK => .sym_link, |
| 3542 | posix.DT.REG => .file, |
| 3543 | posix.DT.SOCK => .unix_domain_socket, |
| 3544 | posix.DT.WHT => .whiteout, |
| 3545 | else => .unknown, |
| 3546 | }; |
| 3547 | buffer[buffer_index] = .{ |
| 3548 | .name = name, |
| 3549 | .kind = entry_kind, |
| 3550 | .inode = bsd_entry.fileno, |
| 3551 | }; |
| 3552 | buffer_index += 1; |
| 3553 | } |
| 3554 | return buffer_index; |
| 3555 | } |
| 3556 | |
| 3557 | fn dirReadIllumos(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { |
| 3558 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 3559 | const current_thread = Thread.getCurrent(t); |
| 3560 | var buffer_index: usize = 0; |
| 3561 | while (buffer.len - buffer_index != 0) { |
| 3562 | if (dr.end - dr.index == 0) { |
| 3563 | // Refill the buffer, unless we've already created references to |
| 3564 | // buffered data. |
| 3565 | if (buffer_index != 0) break; |
| 3566 | if (dr.state == .reset) { |
| 3567 | posixSeekTo(current_thread, dr.dir.handle, 0) catch |err| switch (err) { |
| 3568 | error.Unseekable => return error.Unexpected, |
| 3569 | else => |e| return e, |
| 3570 | }; |
| 3571 | dr.state = .reading; |
| 3572 | } |
| 3573 | try current_thread.beginSyscall(); |
| 3574 | const n: usize = while (true) { |
| 3575 | const rc = posix.system.getdents(dr.dir.handle, dr.buffer.ptr, dr.buffer.len); |
| 3576 | switch (posix.errno(rc)) { |
| 3577 | .SUCCESS => { |
| 3578 | current_thread.endSyscall(); |
| 3579 | break rc; |
| 3580 | }, |
| 3581 | .INTR => { |
| 3582 | try current_thread.checkCancel(); |
| 3583 | continue; |
| 3584 | }, |
| 3585 | .CANCELED => return current_thread.endSyscallCanceled(), |
| 3586 | else => |e| { |
| 3587 | current_thread.endSyscall(); |
| 3588 | switch (e) { |
| 3589 | .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability |
| 3590 | .FAULT => |err| return errnoBug(err), |
| 3591 | .NOTDIR => |err| return errnoBug(err), |
| 3592 | .INVAL => |err| return errnoBug(err), |
| 3593 | else => |err| return posix.unexpectedErrno(err), |
| 3594 | } |
| 3595 | }, |
| 3596 | } |
| 3597 | }; |
| 3598 | if (n == 0) { |
| 3599 | dr.state = .finished; |
| 3600 | return 0; |
| 3601 | } |
| 3602 | dr.index = 0; |
| 3603 | dr.end = n; |
| 3604 | } |
| 3605 | const entry = @as(*align(1) posix.system.dirent, @ptrCast(&dr.buffer[dr.index])); |
| 3606 | const next_index = dr.index + entry.reclen; |
| 3607 | dr.index = next_index; |
| 3608 | |
| 3609 | const name = std.mem.sliceTo(@as([*:0]u8, @ptrCast(&entry.name)), 0); |
| 3610 | if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue; |
| 3611 | |
| 3612 | // illumos dirent doesn't expose type, so we have to call stat to get it. |
| 3613 | const stat = try posixStatPath(current_thread, dr.dir.handle, name, posix.AT.SYMLINK_NOFOLLOW); |
| 3614 | |
| 3615 | buffer[buffer_index] = .{ |
| 3616 | .name = name, |
| 3617 | .kind = stat.kind, |
| 3618 | .inode = entry.ino, |
| 3619 | }; |
| 3620 | buffer_index += 1; |
| 3621 | } |
| 3622 | return buffer_index; |
| 3623 | } |
| 3624 | |
| 3625 | fn dirReadHaiku(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { |
| 3626 | _ = userdata; |
| 3627 | _ = dr; |
| 3628 | _ = buffer; |
| 3629 | @panic("TODO"); |
| 3630 | } |
| 3631 | |
| 3632 | fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { |
| 3633 | _ = userdata; |
| 3634 | _ = dr; |
| 3635 | _ = buffer; |
| 3636 | @panic("TODO"); |
| 3637 | } |
| 3638 | |
| 3639 | fn dirReadWasi(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { |
| 3640 | _ = userdata; |
| 3641 | _ = dr; |
| 3642 | _ = buffer; |
| 3643 | @panic("TODO"); |
| 3644 | } |
| 3645 | |
| 3365 | 3646 | fn dirReadUnimplemented(userdata: ?*anyopaque, dir_reader: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { |
| 3366 | 3647 | _ = userdata; |
| 3367 | 3648 | _ = dir_reader; |
| ... | ... | @@ -9628,8 +9909,10 @@ fn lockStderrWriter(userdata: ?*anyopaque, buffer: []u8) Io.Cancelable!*File.Wri |
| 9628 | 9909 | // Only global mutex since this is Threaded. |
| 9629 | 9910 | Io.stderr_thread_mutex.lock(); |
| 9630 | 9911 | if (!t.stderr_writer_initialized) { |
| 9912 | const io_t = ioBasic(t); |
| 9631 | 9913 | if (is_windows) t.stderr_writer.file = .stderr(); |
| 9632 | | t.stderr_writer.mode = try .detect(ioBasic(t), t.stderr_writer.file, true, .streaming_simple); |
| 9914 | t.stderr_writer.io = io_t; |
| 9915 | t.stderr_writer.mode = try .detect(io_t, t.stderr_writer.file, true, .streaming_simple); |
| 9633 | 9916 | t.stderr_writer_initialized = true; |
| 9634 | 9917 | } |
| 9635 | 9918 | std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch {}; |
| ... | ... | @@ -9643,8 +9926,10 @@ fn tryLockStderrWriter(userdata: ?*anyopaque, buffer: []u8) ?*File.Writer { |
| 9643 | 9926 | // Only global mutex since this is Threaded. |
| 9644 | 9927 | if (!Io.stderr_thread_mutex.tryLock()) return null; |
| 9645 | 9928 | if (!t.stderr_writer_initialized) { |
| 9929 | const io_t = ioBasic(t); |
| 9646 | 9930 | if (is_windows) t.stderr_writer.file = .stderr(); |
| 9647 | | t.stderr_writer.mode = File.Writer.Mode.detect(ioBasic(t), t.stderr_writer.file, true, .streaming_simple) catch |
| 9931 | t.stderr_writer.io = io_t; |
| 9932 | t.stderr_writer.mode = File.Writer.Mode.detect(io_t, t.stderr_writer.file, true, .streaming_simple) catch |
| 9648 | 9933 | return null; |
| 9649 | 9934 | t.stderr_writer_initialized = true; |
| 9650 | 9935 | } |