authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-21 23:54:29-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-21 23:54:29-04:00
loge839250c5156d438f76e7b08e7053e9087fae77c
tree53550b0fc05c78c8594ed4c11c0f606cfc9b2437
parenta5cc758036720babeb13ec8cdec68b720c6af1eb
parent064377be9aaf409bf483f512354ef22f656bf213
signaturelock-open Commit is signed but in an unrecognized format.

Merge branch 'stratact-no-dir-allocators'

closes #2885 closes #2886 closes #2888 closes #3249

25 files changed, 1089 insertions(+), 411 deletions(-)

CMakeLists.txt+3
......@@ -630,5 +630,8 @@ set_target_properties(zig PROPERTIES
630630 LINK_FLAGS ${EXE_LDFLAGS}
631631)
632632target_link_libraries(zig compiler "${LIBUSERLAND}")
633if(MSVC)
634 target_link_libraries(zig ntdll.lib)
635endif()
633636add_dependencies(zig zig_build_libuserland)
634637install(TARGETS zig DESTINATION bin)
doc/docgen.zig+1-1
......@@ -51,7 +51,7 @@ pub fn main() !void {
5151 var toc = try genToc(allocator, &tokenizer);
5252
5353 try fs.makePath(allocator, tmp_dir_name);
54 defer fs.deleteTree(allocator, tmp_dir_name) catch {};
54 defer fs.deleteTree(tmp_dir_name) catch {};
5555
5656 try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);
5757 try buffered_out_stream.flush();
lib/std/build.zig+2-2
......@@ -331,7 +331,7 @@ pub const Builder = struct {
331331 if (self.verbose) {
332332 warn("rm {}\n", full_path);
333333 }
334 fs.deleteTree(self.allocator, full_path) catch {};
334 fs.deleteTree(full_path) catch {};
335335 }
336336
337337 // TODO remove empty directories
......@@ -2687,7 +2687,7 @@ pub const RemoveDirStep = struct {
26872687 const self = @fieldParentPtr(RemoveDirStep, "step", step);
26882688
26892689 const full_path = self.builder.pathFromRoot(self.dir_path);
2690 fs.deleteTree(self.builder.allocator, full_path) catch |err| {
2690 fs.deleteTree(full_path) catch |err| {
26912691 warn("Unable to remove {}: {}\n", full_path, @errorName(err));
26922692 return err;
26932693 };
lib/std/c.zig+1
......@@ -80,6 +80,7 @@ pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint
8080pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int;
8181pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int;
8282pub extern "c" fn unlink(path: [*]const u8) c_int;
83pub extern "c" fn unlinkat(dirfd: fd_t, path: [*]const u8, flags: c_uint) c_int;
8384pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;
8485pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_uint, options: c_uint) c_int;
8586pub extern "c" fn fork() c_int;
lib/std/event/fs.zig+1-1
......@@ -1312,7 +1312,7 @@ const test_tmp_dir = "std_event_fs_test";
13121312//
13131313// // TODO move this into event loop too
13141314// try os.makePath(allocator, test_tmp_dir);
1315// defer os.deleteTree(allocator, test_tmp_dir) catch {};
1315// defer os.deleteTree(test_tmp_dir) catch {};
13161316//
13171317// var loop: Loop = undefined;
13181318// try loop.initMultiThreaded(allocator);
lib/std/fs.zig+653-383
......@@ -335,444 +335,708 @@ pub fn deleteDirW(dir_path: [*]const u16) !void {
335335 return os.rmdirW(dir_path);
336336}
337337
338const DeleteTreeError = error{
339 OutOfMemory,
340 AccessDenied,
341 FileTooBig,
342 IsDir,
343 SymLinkLoop,
344 ProcessFdQuotaExceeded,
345 NameTooLong,
346 SystemFdQuotaExceeded,
347 NoDevice,
348 SystemResources,
349 NoSpaceLeft,
350 PathAlreadyExists,
351 ReadOnlyFileSystem,
352 NotDir,
353 FileNotFound,
354 FileSystem,
355 FileBusy,
356 DirNotEmpty,
357 DeviceBusy,
358
359 /// On Windows, file paths must be valid Unicode.
360 InvalidUtf8,
361
362 /// On Windows, file paths cannot contain these characters:
363 /// '/', '*', '?', '"', '<', '>', '|'
364 BadPathName,
365
366 Unexpected,
367};
368
369/// Whether `full_path` describes a symlink, file, or directory, this function
370/// removes it. If it cannot be removed because it is a non-empty directory,
371/// this function recursively removes its entries and then tries again.
372/// TODO determine if we can remove the allocator requirement
373/// https://github.com/ziglang/zig/issues/2886
374pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void {
375 start_over: while (true) {
376 var got_access_denied = false;
377 // First, try deleting the item as a file. This way we don't follow sym links.
378 if (deleteFile(full_path)) {
379 return;
380 } else |err| switch (err) {
381 error.FileNotFound => return,
382 error.IsDir => {},
383 error.AccessDenied => got_access_denied = true,
384
385 error.InvalidUtf8,
386 error.SymLinkLoop,
387 error.NameTooLong,
388 error.SystemResources,
389 error.ReadOnlyFileSystem,
390 error.NotDir,
391 error.FileSystem,
392 error.FileBusy,
393 error.BadPathName,
394 error.Unexpected,
395 => return err,
396 }
397 {
398 var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
399 error.NotDir => {
400 if (got_access_denied) {
401 return error.AccessDenied;
402 }
403 continue :start_over;
404 },
405
406 error.OutOfMemory,
407 error.AccessDenied,
408 error.FileTooBig,
409 error.IsDir,
410 error.SymLinkLoop,
411 error.ProcessFdQuotaExceeded,
412 error.NameTooLong,
413 error.SystemFdQuotaExceeded,
414 error.NoDevice,
415 error.FileNotFound,
416 error.SystemResources,
417 error.NoSpaceLeft,
418 error.PathAlreadyExists,
419 error.Unexpected,
420 error.InvalidUtf8,
421 error.BadPathName,
422 error.DeviceBusy,
423 => return err,
424 };
425 defer dir.close();
426
427 var full_entry_buf = std.ArrayList(u8).init(allocator);
428 defer full_entry_buf.deinit();
429
430 while (try dir.next()) |entry| {
431 try full_entry_buf.resize(full_path.len + entry.name.len + 1);
432 const full_entry_path = full_entry_buf.toSlice();
433 mem.copy(u8, full_entry_path, full_path);
434 full_entry_path[full_path.len] = path.sep;
435 mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name);
436
437 try deleteTree(allocator, full_entry_path);
438 }
439 }
440 return deleteDir(full_path);
338/// Removes a symlink, file, or directory.
339/// If `full_path` is relative, this is equivalent to `Dir.deleteTree` with the
340/// current working directory as the open directory handle.
341/// If `full_path` is absolute, this is equivalent to `Dir.deleteTree` with the
342/// base directory.
343pub fn deleteTree(full_path: []const u8) !void {
344 if (path.isAbsolute(full_path)) {
345 const dirname = path.dirname(full_path) orelse return error{
346 /// Attempt to remove the root file system path.
347 /// This error is unreachable if `full_path` is relative.
348 CannotDeleteRootDirectory,
349 }.CannotDeleteRootDirectory;
350
351 var dir = try Dir.open(dirname);
352 defer dir.close();
353
354 return dir.deleteTree(path.basename(full_path));
355 } else {
356 return Dir.cwd().deleteTree(full_path);
441357 }
442358}
443359
444/// TODO: separate this API into the one that opens directory handles to then subsequently open
445/// files, and into the one that reads files from an open directory handle.
446360pub const Dir = struct {
447 handle: Handle,
448 allocator: *Allocator,
361 fd: os.fd_t,
362
363 pub const Entry = struct {
364 name: []const u8,
365 kind: Kind,
449366
450 pub const Handle = switch (builtin.os) {
367 pub const Kind = enum {
368 BlockDevice,
369 CharacterDevice,
370 Directory,
371 NamedPipe,
372 SymLink,
373 File,
374 UnixDomainSocket,
375 Whiteout,
376 Unknown,
377 };
378 };
379
380 const IteratorError = error{AccessDenied} || os.UnexpectedError;
381
382 pub const Iterator = switch (builtin.os) {
451383 .macosx, .ios, .freebsd, .netbsd => struct {
452 fd: i32,
384 dir: Dir,
453385 seek: i64,
454 buf: []u8,
386 buf: [8192]u8, // TODO align(@alignOf(os.dirent)),
455387 index: usize,
456388 end_index: usize,
389
390 const Self = @This();
391
392 pub const Error = IteratorError;
393
394 /// Memory such as file names referenced in this returned entry becomes invalid
395 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
396 pub fn next(self: *Self) Error!?Entry {
397 switch (builtin.os) {
398 .macosx, .ios => return self.nextDarwin(),
399 .freebsd, .netbsd => return self.nextBsd(),
400 else => @compileError("unimplemented"),
401 }
402 }
403
404 fn nextDarwin(self: *Self) !?Entry {
405 start_over: while (true) {
406 if (self.index >= self.end_index) {
407 const rc = os.system.__getdirentries64(
408 self.dir.fd,
409 &self.buf,
410 self.buf.len,
411 &self.seek,
412 );
413 if (rc == 0) return null;
414 if (rc < 0) {
415 switch (os.errno(rc)) {
416 os.EBADF => unreachable,
417 os.EFAULT => unreachable,
418 os.ENOTDIR => unreachable,
419 os.EINVAL => unreachable,
420 else => |err| return os.unexpectedErrno(err),
421 }
422 }
423 self.index = 0;
424 self.end_index = @intCast(usize, rc);
425 }
426 const darwin_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]);
427 const next_index = self.index + darwin_entry.d_reclen;
428 self.index = next_index;
429
430 const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen];
431
432 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
433 continue :start_over;
434 }
435
436 const entry_kind = switch (darwin_entry.d_type) {
437 os.DT_BLK => Entry.Kind.BlockDevice,
438 os.DT_CHR => Entry.Kind.CharacterDevice,
439 os.DT_DIR => Entry.Kind.Directory,
440 os.DT_FIFO => Entry.Kind.NamedPipe,
441 os.DT_LNK => Entry.Kind.SymLink,
442 os.DT_REG => Entry.Kind.File,
443 os.DT_SOCK => Entry.Kind.UnixDomainSocket,
444 os.DT_WHT => Entry.Kind.Whiteout,
445 else => Entry.Kind.Unknown,
446 };
447 return Entry{
448 .name = name,
449 .kind = entry_kind,
450 };
451 }
452 }
453
454 fn nextBsd(self: *Self) !?Entry {
455 start_over: while (true) {
456 if (self.index >= self.end_index) {
457 const rc = os.system.getdirentries(
458 self.dir.fd,
459 self.buf[0..].ptr,
460 self.buf.len,
461 &self.seek,
462 );
463 switch (os.errno(rc)) {
464 0 => {},
465 os.EBADF => unreachable,
466 os.EFAULT => unreachable,
467 os.ENOTDIR => unreachable,
468 os.EINVAL => unreachable,
469 else => |err| return os.unexpectedErrno(err),
470 }
471 if (rc == 0) return null;
472 self.index = 0;
473 self.end_index = @intCast(usize, rc);
474 }
475 const freebsd_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]);
476 const next_index = self.index + freebsd_entry.d_reclen;
477 self.index = next_index;
478
479 const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen];
480
481 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
482 continue :start_over;
483 }
484
485 const entry_kind = switch (freebsd_entry.d_type) {
486 os.DT_BLK => Entry.Kind.BlockDevice,
487 os.DT_CHR => Entry.Kind.CharacterDevice,
488 os.DT_DIR => Entry.Kind.Directory,
489 os.DT_FIFO => Entry.Kind.NamedPipe,
490 os.DT_LNK => Entry.Kind.SymLink,
491 os.DT_REG => Entry.Kind.File,
492 os.DT_SOCK => Entry.Kind.UnixDomainSocket,
493 os.DT_WHT => Entry.Kind.Whiteout,
494 else => Entry.Kind.Unknown,
495 };
496 return Entry{
497 .name = name,
498 .kind = entry_kind,
499 };
500 }
501 }
457502 },
458503 .linux => struct {
459 fd: i32,
460 buf: []u8,
504 dir: Dir,
505 buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
461506 index: usize,
462507 end_index: usize,
508
509 const Self = @This();
510
511 pub const Error = IteratorError;
512
513 /// Memory such as file names referenced in this returned entry becomes invalid
514 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
515 pub fn next(self: *Self) Error!?Entry {
516 start_over: while (true) {
517 if (self.index >= self.end_index) {
518 const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
519 switch (os.linux.getErrno(rc)) {
520 0 => {},
521 os.EBADF => unreachable,
522 os.EFAULT => unreachable,
523 os.ENOTDIR => unreachable,
524 os.EINVAL => unreachable,
525 else => |err| return os.unexpectedErrno(err),
526 }
527 if (rc == 0) return null;
528 self.index = 0;
529 self.end_index = rc;
530 }
531 const linux_entry = @ptrCast(*align(1) os.dirent64, &self.buf[self.index]);
532 const next_index = self.index + linux_entry.d_reclen;
533 self.index = next_index;
534
535 const name = mem.toSlice(u8, @ptrCast([*]u8, &linux_entry.d_name));
536
537 // skip . and .. entries
538 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
539 continue :start_over;
540 }
541
542 const entry_kind = switch (linux_entry.d_type) {
543 os.DT_BLK => Entry.Kind.BlockDevice,
544 os.DT_CHR => Entry.Kind.CharacterDevice,
545 os.DT_DIR => Entry.Kind.Directory,
546 os.DT_FIFO => Entry.Kind.NamedPipe,
547 os.DT_LNK => Entry.Kind.SymLink,
548 os.DT_REG => Entry.Kind.File,
549 os.DT_SOCK => Entry.Kind.UnixDomainSocket,
550 else => Entry.Kind.Unknown,
551 };
552 return Entry{
553 .name = name,
554 .kind = entry_kind,
555 };
556 }
557 }
463558 },
464559 .windows => struct {
465 handle: os.windows.HANDLE,
466 find_file_data: os.windows.WIN32_FIND_DATAW,
560 dir: Dir,
561 buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)),
562 index: usize,
563 end_index: usize,
467564 first: bool,
468565 name_data: [256]u8,
566
567 const Self = @This();
568
569 pub const Error = IteratorError;
570
571 pub fn next(self: *Self) Error!?Entry {
572 start_over: while (true) {
573 const w = os.windows;
574 if (self.index >= self.end_index) {
575 var io: w.IO_STATUS_BLOCK = undefined;
576 const rc = w.ntdll.NtQueryDirectoryFile(
577 self.dir.fd,
578 null,
579 null,
580 null,
581 &io,
582 &self.buf,
583 self.buf.len,
584 .FileBothDirectoryInformation,
585 w.FALSE,
586 null,
587 if (self.first) w.BOOLEAN(w.TRUE) else w.BOOLEAN(w.FALSE),
588 );
589 self.first = false;
590 if (io.Information == 0) return null;
591 self.index = 0;
592 self.end_index = io.Information;
593 switch (rc) {
594 w.STATUS.SUCCESS => {},
595 w.STATUS.ACCESS_DENIED => return error.AccessDenied,
596 else => return w.unexpectedStatus(rc),
597 }
598 }
599
600 const aligned_ptr = @alignCast(@alignOf(w.FILE_BOTH_DIR_INFORMATION), &self.buf[self.index]);
601 const dir_info = @ptrCast(*w.FILE_BOTH_DIR_INFORMATION, aligned_ptr);
602 if (dir_info.NextEntryOffset != 0) {
603 self.index += dir_info.NextEntryOffset;
604 } else {
605 self.index = self.buf.len;
606 }
607
608 const name_utf16le = @ptrCast([*]u16, &dir_info.FileName)[0 .. dir_info.FileNameLength / 2];
609
610 if (mem.eql(u16, name_utf16le, [_]u16{'.'}) or mem.eql(u16, name_utf16le, [_]u16{ '.', '.' }))
611 continue;
612 // Trust that Windows gives us valid UTF-16LE
613 const name_utf8_len = std.unicode.utf16leToUtf8(self.name_data[0..], name_utf16le) catch unreachable;
614 const name_utf8 = self.name_data[0..name_utf8_len];
615 const kind = blk: {
616 const attrs = dir_info.FileAttributes;
617 if (attrs & w.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
618 if (attrs & w.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;
619 break :blk Entry.Kind.File;
620 };
621 return Entry{
622 .name = name_utf8,
623 .kind = kind,
624 };
625 }
626 }
469627 },
470628 else => @compileError("unimplemented"),
471629 };
472630
473 pub const Entry = struct {
474 name: []const u8,
475 kind: Kind,
631 pub fn iterate(self: Dir) Iterator {
632 switch (builtin.os) {
633 .macosx, .ios, .freebsd, .netbsd => return Iterator{
634 .dir = self,
635 .seek = 0,
636 .index = 0,
637 .end_index = 0,
638 .buf = undefined,
639 },
640 .linux => return Iterator{
641 .dir = self,
642 .index = 0,
643 .end_index = 0,
644 .buf = undefined,
645 },
646 .windows => return Iterator{
647 .dir = self,
648 .index = 0,
649 .end_index = 0,
650 .first = true,
651 .buf = undefined,
652 .name_data = undefined,
653 },
654 else => @compileError("unimplemented"),
655 }
656 }
476657
477 pub const Kind = enum {
478 BlockDevice,
479 CharacterDevice,
480 Directory,
481 NamedPipe,
482 SymLink,
483 File,
484 UnixDomainSocket,
485 Whiteout,
486 Unknown,
487 };
488 };
658 /// Returns an open handle to the current working directory.
659 /// Closing the returned `Dir` is checked illegal behavior.
660 /// On POSIX targets, this function is comptime-callable.
661 pub fn cwd() Dir {
662 if (os.windows.is_the_target) {
663 return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
664 } else {
665 return Dir{ .fd = os.AT_FDCWD };
666 }
667 }
489668
490669 pub const OpenError = error{
491670 FileNotFound,
492671 NotDir,
493672 AccessDenied,
494 FileTooBig,
495 IsDir,
496673 SymLinkLoop,
497674 ProcessFdQuotaExceeded,
498675 NameTooLong,
499676 SystemFdQuotaExceeded,
500677 NoDevice,
501678 SystemResources,
502 NoSpaceLeft,
503 PathAlreadyExists,
504 OutOfMemory,
505679 InvalidUtf8,
506680 BadPathName,
507681 DeviceBusy,
682 } || os.UnexpectedError;
508683
509 Unexpected,
510 };
511
512 /// Call close when done.
513 /// TODO remove the allocator requirement from this API
514 /// https://github.com/ziglang/zig/issues/2885
515 pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
516 return Dir{
517 .allocator = allocator,
518 .handle = switch (builtin.os) {
519 .windows => blk: {
520 var find_file_data: os.windows.WIN32_FIND_DATAW = undefined;
521 const handle = try os.windows.FindFirstFile(dir_path, &find_file_data);
522 break :blk Handle{
523 .handle = handle,
524 .find_file_data = find_file_data, // TODO guaranteed copy elision
525 .first = true,
526 .name_data = undefined,
527 };
528 },
529 .macosx, .ios, .freebsd, .netbsd => Handle{
530 .fd = try os.open(dir_path, os.O_RDONLY | os.O_NONBLOCK | os.O_DIRECTORY | os.O_CLOEXEC, 0),
531 .seek = 0,
532 .index = 0,
533 .end_index = 0,
534 .buf = [_]u8{},
535 },
536 .linux => Handle{
537 .fd = try os.open(dir_path, os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC, 0),
538 .index = 0,
539 .end_index = 0,
540 .buf = [_]u8{},
541 },
542 else => @compileError("unimplemented"),
543 },
544 };
684 /// Call `close` to free the directory handle.
685 pub fn open(dir_path: []const u8) OpenError!Dir {
686 return cwd().openDir(dir_path);
545687 }
546688
547 pub fn close(self: *Dir) void {
548 if (os.windows.is_the_target) {
549 return os.windows.FindClose(self.handle.handle);
550 }
551 self.allocator.free(self.handle.buf);
552 os.close(self.handle.fd);
689 /// Same as `open` except the parameter is null-terminated.
690 pub fn openC(dir_path_c: [*]const u8) OpenError!Dir {
691 return cwd().openDirC(dir_path_c);
553692 }
554693
555 /// Memory such as file names referenced in this returned entry becomes invalid
556 /// with subsequent calls to next, as well as when this `Dir` is deinitialized.
557 pub fn next(self: *Dir) !?Entry {
558 switch (builtin.os) {
559 .linux => return self.nextLinux(),
560 .macosx, .ios => return self.nextDarwin(),
561 .windows => return self.nextWindows(),
562 .freebsd => return self.nextBsd(),
563 .netbsd => return self.nextBsd(),
564 else => @compileError("unimplemented"),
565 }
694 pub fn close(self: *Dir) void {
695 os.close(self.fd);
696 self.* = undefined;
566697 }
567698
568 pub fn openRead(self: Dir, file_path: []const u8) os.OpenError!File {
569 const path_c = try os.toPosixPath(file_path);
699 /// Call `File.close` on the result when done.
700 pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File {
701 const path_c = try os.toPosixPath(sub_path);
570702 return self.openReadC(&path_c);
571703 }
572704
573 pub fn openReadC(self: Dir, file_path: [*]const u8) OpenError!File {
705 /// Call `File.close` on the result when done.
706 pub fn openReadC(self: Dir, sub_path: [*]const u8) File.OpenError!File {
574707 const flags = os.O_LARGEFILE | os.O_RDONLY;
575 const fd = try os.openatC(self.handle.fd, file_path, flags, 0);
708 const fd = try os.openatC(self.fd, sub_path, flags, 0);
576709 return File.openHandle(fd);
577710 }
578711
579 fn nextDarwin(self: *Dir) !?Entry {
580 start_over: while (true) {
581 if (self.handle.index >= self.handle.end_index) {
582 if (self.handle.buf.len == 0) {
583 self.handle.buf = try self.allocator.alloc(u8, mem.page_size);
584 }
585
586 while (true) {
587 const rc = os.system.__getdirentries64(
588 self.handle.fd,
589 self.handle.buf.ptr,
590 self.handle.buf.len,
591 &self.handle.seek,
592 );
593 if (rc == 0) return null;
594 if (rc < 0) {
595 switch (os.errno(rc)) {
596 os.EBADF => unreachable,
597 os.EFAULT => unreachable,
598 os.ENOTDIR => unreachable,
599 os.EINVAL => {
600 self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2);
601 continue;
602 },
603 else => |err| return os.unexpectedErrno(err),
604 }
605 }
606 self.handle.index = 0;
607 self.handle.end_index = @intCast(usize, rc);
608 break;
609 }
610 }
611 const darwin_entry = @ptrCast(*align(1) os.dirent, &self.handle.buf[self.handle.index]);
612 const next_index = self.handle.index + darwin_entry.d_reclen;
613 self.handle.index = next_index;
614
615 const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen];
712 /// Call `close` on the result when done.
713 pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir {
714 if (os.windows.is_the_target) {
715 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
716 return self.openDirW(&sub_path_w);
717 }
616718
617 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
618 continue :start_over;
619 }
719 const sub_path_c = try os.toPosixPath(sub_path);
720 return self.openDirC(&sub_path_c);
721 }
620722
621 const entry_kind = switch (darwin_entry.d_type) {
622 os.DT_BLK => Entry.Kind.BlockDevice,
623 os.DT_CHR => Entry.Kind.CharacterDevice,
624 os.DT_DIR => Entry.Kind.Directory,
625 os.DT_FIFO => Entry.Kind.NamedPipe,
626 os.DT_LNK => Entry.Kind.SymLink,
627 os.DT_REG => Entry.Kind.File,
628 os.DT_SOCK => Entry.Kind.UnixDomainSocket,
629 os.DT_WHT => Entry.Kind.Whiteout,
630 else => Entry.Kind.Unknown,
631 };
632 return Entry{
633 .name = name,
634 .kind = entry_kind,
635 };
723 /// Same as `openDir` except the parameter is null-terminated.
724 pub fn openDirC(self: Dir, sub_path_c: [*]const u8) OpenError!Dir {
725 if (os.windows.is_the_target) {
726 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
727 return self.openDirW(&sub_path_w);
636728 }
729
730 const flags = os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC;
731 const fd = os.openatC(self.fd, sub_path_c, flags, 0) catch |err| switch (err) {
732 error.FileTooBig => unreachable, // can't happen for directories
733 error.IsDir => unreachable, // we're providing O_DIRECTORY
734 error.NoSpaceLeft => unreachable, // not providing O_CREAT
735 error.PathAlreadyExists => unreachable, // not providing O_CREAT
736 else => |e| return e,
737 };
738 return Dir{ .fd = fd };
637739 }
638740
639 fn nextWindows(self: *Dir) !?Entry {
640 while (true) {
641 if (self.handle.first) {
642 self.handle.first = false;
643 } else {
644 if (!try os.windows.FindNextFile(self.handle.handle, &self.handle.find_file_data))
645 return null;
646 }
647 const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
648 if (mem.eql(u16, name_utf16le, [_]u16{'.'}) or mem.eql(u16, name_utf16le, [_]u16{ '.', '.' }))
649 continue;
650 // Trust that Windows gives us valid UTF-16LE
651 const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable;
652 const name_utf8 = self.handle.name_data[0..name_utf8_len];
653 const kind = blk: {
654 const attrs = self.handle.find_file_data.dwFileAttributes;
655 if (attrs & os.windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
656 if (attrs & os.windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;
657 break :blk Entry.Kind.File;
658 };
659 return Entry{
660 .name = name_utf8,
661 .kind = kind,
662 };
741 /// Same as `openDir` except the path parameter is UTF16LE, NT-prefixed.
742 /// This function is Windows-only.
743 pub fn openDirW(self: Dir, sub_path_w: [*]const u16) OpenError!Dir {
744 const w = os.windows;
745
746 var result = Dir{
747 .fd = undefined,
748 };
749
750 const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2);
751 var nt_name = w.UNICODE_STRING{
752 .Length = path_len_bytes,
753 .MaximumLength = path_len_bytes,
754 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
755 };
756 var attr = w.OBJECT_ATTRIBUTES{
757 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
758 .RootDirectory = if (path.isAbsoluteW(sub_path_w)) null else self.fd,
759 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
760 .ObjectName = &nt_name,
761 .SecurityDescriptor = null,
762 .SecurityQualityOfService = null,
763 };
764 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
765 // Windows does not recognize this, but it does work with empty string.
766 nt_name.Length = 0;
767 }
768 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
769 // If you're looking to contribute to zig and fix this, see here for an example of how to
770 // implement this: https://git.midipix.org/ntapi/tree/src/fs/ntapi_tt_open_physical_parent_directory.c
771 @panic("TODO opening '..' with a relative directory handle is not yet implemented on Windows");
772 }
773 var io: w.IO_STATUS_BLOCK = undefined;
774 const rc = w.ntdll.NtCreateFile(
775 &result.fd,
776 w.GENERIC_READ | w.SYNCHRONIZE,
777 &attr,
778 &io,
779 null,
780 0,
781 w.FILE_SHARE_READ | w.FILE_SHARE_WRITE,
782 w.FILE_OPEN,
783 w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT,
784 null,
785 0,
786 );
787 switch (rc) {
788 w.STATUS.SUCCESS => return result,
789 w.STATUS.OBJECT_NAME_INVALID => unreachable,
790 w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
791 w.STATUS.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
792 w.STATUS.INVALID_PARAMETER => unreachable,
793 else => return w.unexpectedStatus(rc),
663794 }
664795 }
665796
666 fn nextLinux(self: *Dir) !?Entry {
667 start_over: while (true) {
668 if (self.handle.index >= self.handle.end_index) {
669 if (self.handle.buf.len == 0) {
670 self.handle.buf = try self.allocator.alloc(u8, mem.page_size);
671 }
797 pub const DeleteFileError = os.UnlinkError;
672798
673 while (true) {
674 const rc = os.linux.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len);
675 switch (os.linux.getErrno(rc)) {
676 0 => {},
677 os.EBADF => unreachable,
678 os.EFAULT => unreachable,
679 os.ENOTDIR => unreachable,
680 os.EINVAL => {
681 self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2);
682 continue;
683 },
684 else => |err| return os.unexpectedErrno(err),
685 }
686 if (rc == 0) return null;
687 self.handle.index = 0;
688 self.handle.end_index = rc;
689 break;
690 }
691 }
692 const linux_entry = @ptrCast(*align(1) os.dirent64, &self.handle.buf[self.handle.index]);
693 const next_index = self.handle.index + linux_entry.d_reclen;
694 self.handle.index = next_index;
799 /// Delete a file name and possibly the file it refers to, based on an open directory handle.
800 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
801 const sub_path_c = try os.toPosixPath(sub_path);
802 return self.deleteFileC(&sub_path_c);
803 }
695804
696 const name = mem.toSlice(u8, @ptrCast([*]u8, &linux_entry.d_name));
805 /// Same as `deleteFile` except the parameter is null-terminated.
806 pub fn deleteFileC(self: Dir, sub_path_c: [*]const u8) DeleteFileError!void {
807 os.unlinkatC(self.fd, sub_path_c, 0) catch |err| switch (err) {
808 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
809 else => |e| return e,
810 };
811 }
697812
698 // skip . and .. entries
699 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
700 continue :start_over;
701 }
813 pub const DeleteDirError = error{
814 DirNotEmpty,
815 FileNotFound,
816 AccessDenied,
817 FileBusy,
818 FileSystem,
819 SymLinkLoop,
820 NameTooLong,
821 NotDir,
822 SystemResources,
823 ReadOnlyFileSystem,
824 InvalidUtf8,
825 BadPathName,
826 Unexpected,
827 };
702828
703 const entry_kind = switch (linux_entry.d_type) {
704 os.DT_BLK => Entry.Kind.BlockDevice,
705 os.DT_CHR => Entry.Kind.CharacterDevice,
706 os.DT_DIR => Entry.Kind.Directory,
707 os.DT_FIFO => Entry.Kind.NamedPipe,
708 os.DT_LNK => Entry.Kind.SymLink,
709 os.DT_REG => Entry.Kind.File,
710 os.DT_SOCK => Entry.Kind.UnixDomainSocket,
711 else => Entry.Kind.Unknown,
712 };
713 return Entry{
714 .name = name,
715 .kind = entry_kind,
716 };
829 /// Returns `error.DirNotEmpty` if the directory is not empty.
830 /// To delete a directory recursively, see `deleteTree`.
831 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
832 if (os.windows.is_the_target) {
833 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
834 return self.deleteDirW(&sub_path_w);
717835 }
836 const sub_path_c = try os.toPosixPath(sub_path);
837 return self.deleteDirC(&sub_path_c);
718838 }
719839
720 fn nextBsd(self: *Dir) !?Entry {
721 start_over: while (true) {
722 if (self.handle.index >= self.handle.end_index) {
723 if (self.handle.buf.len == 0) {
724 self.handle.buf = try self.allocator.alloc(u8, mem.page_size);
725 }
840 /// Same as `deleteDir` except the parameter is null-terminated.
841 pub fn deleteDirC(self: Dir, sub_path_c: [*]const u8) DeleteDirError!void {
842 os.unlinkatC(self.fd, sub_path_c, os.AT_REMOVEDIR) catch |err| switch (err) {
843 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
844 else => |e| return e,
845 };
846 }
726847
727 while (true) {
728 const rc = os.system.getdirentries(
729 self.handle.fd,
730 self.handle.buf.ptr,
731 self.handle.buf.len,
732 &self.handle.seek,
733 );
734 switch (os.errno(rc)) {
735 0 => {},
736 os.EBADF => unreachable,
737 os.EFAULT => unreachable,
738 os.ENOTDIR => unreachable,
739 os.EINVAL => {
740 self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2);
741 continue;
742 },
743 else => |err| return os.unexpectedErrno(err),
744 }
745 if (rc == 0) return null;
746 self.handle.index = 0;
747 self.handle.end_index = @intCast(usize, rc);
748 break;
749 }
750 }
751 const freebsd_entry = @ptrCast(*align(1) os.dirent, &self.handle.buf[self.handle.index]);
752 const next_index = self.handle.index + freebsd_entry.d_reclen;
753 self.handle.index = next_index;
848 /// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed.
849 /// This function is Windows-only.
850 pub fn deleteDirW(self: Dir, sub_path_w: [*]const u16) DeleteDirError!void {
851 os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) {
852 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
853 else => |e| return e,
854 };
855 }
754856
755 const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen];
857 /// Read value of a symbolic link.
858 /// The return value is a slice of `buffer`, from index `0`.
859 pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
860 const sub_path_c = try os.toPosixPath(sub_path);
861 return self.readLinkC(&sub_path_c, buffer);
862 }
756863
757 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
758 continue :start_over;
864 /// Same as `readLink`, except the `pathname` parameter is null-terminated.
865 pub fn readLinkC(self: Dir, sub_path_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
866 return os.readlinkatC(self.fd, sub_path_c, buffer);
867 }
868
869 pub const DeleteTreeError = error{
870 AccessDenied,
871 FileTooBig,
872 SymLinkLoop,
873 ProcessFdQuotaExceeded,
874 NameTooLong,
875 SystemFdQuotaExceeded,
876 NoDevice,
877 SystemResources,
878 ReadOnlyFileSystem,
879 FileSystem,
880 FileBusy,
881 DeviceBusy,
882
883 /// One of the path components was not a directory.
884 /// This error is unreachable if `sub_path` does not contain a path separator.
885 NotDir,
886
887 /// On Windows, file paths must be valid Unicode.
888 InvalidUtf8,
889
890 /// On Windows, file paths cannot contain these characters:
891 /// '/', '*', '?', '"', '<', '>', '|'
892 BadPathName,
893 } || os.UnexpectedError;
894
895 /// Whether `full_path` describes a symlink, file, or directory, this function
896 /// removes it. If it cannot be removed because it is a non-empty directory,
897 /// this function recursively removes its entries and then tries again.
898 /// This operation is not atomic on most file systems.
899 pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void {
900 start_over: while (true) {
901 var got_access_denied = false;
902 // First, try deleting the item as a file. This way we don't follow sym links.
903 if (self.deleteFile(sub_path)) {
904 return;
905 } else |err| switch (err) {
906 error.FileNotFound => return,
907 error.IsDir => {},
908 error.AccessDenied => got_access_denied = true,
909
910 error.InvalidUtf8,
911 error.SymLinkLoop,
912 error.NameTooLong,
913 error.SystemResources,
914 error.ReadOnlyFileSystem,
915 error.NotDir,
916 error.FileSystem,
917 error.FileBusy,
918 error.BadPathName,
919 error.Unexpected,
920 => |e| return e,
759921 }
922 var dir = self.openDir(sub_path) catch |err| switch (err) {
923 error.NotDir => {
924 if (got_access_denied) {
925 return error.AccessDenied;
926 }
927 continue :start_over;
928 },
929 error.FileNotFound => {
930 // That's fine, we were trying to remove this directory anyway.
931 continue :start_over;
932 },
760933
761 const entry_kind = switch (freebsd_entry.d_type) {
762 os.DT_BLK => Entry.Kind.BlockDevice,
763 os.DT_CHR => Entry.Kind.CharacterDevice,
764 os.DT_DIR => Entry.Kind.Directory,
765 os.DT_FIFO => Entry.Kind.NamedPipe,
766 os.DT_LNK => Entry.Kind.SymLink,
767 os.DT_REG => Entry.Kind.File,
768 os.DT_SOCK => Entry.Kind.UnixDomainSocket,
769 os.DT_WHT => Entry.Kind.Whiteout,
770 else => Entry.Kind.Unknown,
771 };
772 return Entry{
773 .name = name,
774 .kind = entry_kind,
934 error.AccessDenied,
935 error.SymLinkLoop,
936 error.ProcessFdQuotaExceeded,
937 error.NameTooLong,
938 error.SystemFdQuotaExceeded,
939 error.NoDevice,
940 error.SystemResources,
941 error.Unexpected,
942 error.InvalidUtf8,
943 error.BadPathName,
944 error.DeviceBusy,
945 => |e| return e,
775946 };
947 var cleanup_dir_parent: ?Dir = null;
948 defer if (cleanup_dir_parent) |*d| d.close();
949
950 var cleanup_dir = true;
951 defer if (cleanup_dir) dir.close();
952
953 var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined;
954 var dir_name: []const u8 = sub_path;
955 var parent_dir = self;
956
957 // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function.
958 // Go through each entry and if it is not a directory, delete it. If it is a directory,
959 // open it, and close the original directory. Repeat. Then start the entire operation over.
960
961 scan_dir: while (true) {
962 var dir_it = dir.iterate();
963 while (try dir_it.next()) |entry| {
964 if (dir.deleteFile(entry.name)) {
965 continue;
966 } else |err| switch (err) {
967 error.FileNotFound => continue,
968
969 // Impossible because we do not pass any path separators.
970 error.NotDir => unreachable,
971
972 error.IsDir => {},
973 error.AccessDenied => got_access_denied = true,
974
975 error.InvalidUtf8,
976 error.SymLinkLoop,
977 error.NameTooLong,
978 error.SystemResources,
979 error.ReadOnlyFileSystem,
980 error.FileSystem,
981 error.FileBusy,
982 error.BadPathName,
983 error.Unexpected,
984 => |e| return e,
985 }
986
987 const new_dir = dir.openDir(entry.name) catch |err| switch (err) {
988 error.NotDir => {
989 if (got_access_denied) {
990 return error.AccessDenied;
991 }
992 continue :scan_dir;
993 },
994 error.FileNotFound => {
995 // That's fine, we were trying to remove this directory anyway.
996 continue :scan_dir;
997 },
998
999 error.AccessDenied,
1000 error.SymLinkLoop,
1001 error.ProcessFdQuotaExceeded,
1002 error.NameTooLong,
1003 error.SystemFdQuotaExceeded,
1004 error.NoDevice,
1005 error.SystemResources,
1006 error.Unexpected,
1007 error.InvalidUtf8,
1008 error.BadPathName,
1009 error.DeviceBusy,
1010 => |e| return e,
1011 };
1012 if (cleanup_dir_parent) |*d| d.close();
1013 cleanup_dir_parent = dir;
1014 dir = new_dir;
1015 mem.copy(u8, &dir_name_buf, entry.name);
1016 dir_name = dir_name_buf[0..entry.name.len];
1017 continue :scan_dir;
1018 }
1019 // Reached the end of the directory entries, which means we successfully deleted all of them.
1020 // Now to remove the directory itself.
1021 dir.close();
1022 cleanup_dir = false;
1023
1024 if (cleanup_dir_parent) |d| {
1025 d.deleteDir(dir_name) catch |err| switch (err) {
1026 // These two things can happen due to file system race conditions.
1027 error.FileNotFound, error.DirNotEmpty => continue :start_over,
1028 else => |e| return e,
1029 };
1030 continue :start_over;
1031 } else {
1032 self.deleteDir(sub_path) catch |err| switch (err) {
1033 error.FileNotFound => return,
1034 error.DirNotEmpty => continue :start_over,
1035 else => |e| return e,
1036 };
1037 return;
1038 }
1039 }
7761040 }
7771041 }
7781042};
......@@ -782,13 +1046,18 @@ pub const Walker = struct {
7821046 name_buffer: std.Buffer,
7831047
7841048 pub const Entry = struct {
785 path: []const u8,
1049 /// The containing directory. This can be used to operate directly on `basename`
1050 /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths.
1051 /// The directory remains open until `next` or `deinit` is called.
1052 dir: Dir,
7861053 basename: []const u8,
1054
1055 path: []const u8,
7871056 kind: Dir.Entry.Kind,
7881057 };
7891058
7901059 const StackItem = struct {
791 dir_it: Dir,
1060 dir_it: Dir.Iterator,
7921061 dirname_len: usize,
7931062 };
7941063
......@@ -806,23 +1075,26 @@ pub const Walker = struct {
8061075 try self.name_buffer.appendByte(path.sep);
8071076 try self.name_buffer.append(base.name);
8081077 if (base.kind == .Directory) {
809 // TODO https://github.com/ziglang/zig/issues/2888
810 var new_dir = try Dir.open(self.stack.allocator, self.name_buffer.toSliceConst());
1078 var new_dir = top.dir_it.dir.openDir(base.name) catch |err| switch (err) {
1079 error.NameTooLong => unreachable, // no path sep in base.name
1080 else => |e| return e,
1081 };
8111082 {
8121083 errdefer new_dir.close();
8131084 try self.stack.append(StackItem{
814 .dir_it = new_dir,
1085 .dir_it = new_dir.iterate(),
8151086 .dirname_len = self.name_buffer.len(),
8161087 });
8171088 }
8181089 }
8191090 return Entry{
1091 .dir = top.dir_it.dir,
8201092 .basename = self.name_buffer.toSliceConst()[dirname_len + 1 ..],
8211093 .path = self.name_buffer.toSliceConst(),
8221094 .kind = base.kind,
8231095 };
8241096 } else {
825 self.stack.pop().dir_it.close();
1097 self.stack.pop().dir_it.dir.close();
8261098 }
8271099 }
8281100 }
......@@ -837,12 +1109,12 @@ pub const Walker = struct {
8371109/// Recursively iterates over a directory.
8381110/// Must call `Walker.deinit` when done.
8391111/// `dir_path` must not end in a path separator.
840/// TODO: https://github.com/ziglang/zig/issues/2888
1112/// The order of returned file system entries is undefined.
8411113pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
8421114 assert(!mem.endsWith(u8, dir_path, path.sep_str));
8431115
844 var dir_it = try Dir.open(allocator, dir_path);
845 errdefer dir_it.close();
1116 var dir = try Dir.open(dir_path);
1117 errdefer dir.close();
8461118
8471119 var name_buffer = try std.Buffer.init(allocator, dir_path);
8481120 errdefer name_buffer.deinit();
......@@ -853,7 +1125,7 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
8531125 };
8541126
8551127 try walker.stack.append(Walker.StackItem{
856 .dir_it = dir_it,
1128 .dir_it = dir.iterate(),
8571129 .dirname_len = dir_path.len,
8581130 });
8591131
......@@ -862,15 +1134,13 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
8621134
8631135/// Read value of a symbolic link.
8641136/// The return value is a slice of buffer, from index `0`.
865/// TODO https://github.com/ziglang/zig/issues/2888
866pub fn readLink(pathname: []const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {
1137pub fn readLink(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
8671138 return os.readlink(pathname, buffer);
8681139}
8691140
870/// Same as `readLink`, except the `pathname` parameter is null-terminated.
871/// TODO https://github.com/ziglang/zig/issues/2888
872pub fn readLinkC(pathname: [*]const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {
873 return os.readlinkC(pathname, buffer);
1141/// Same as `readLink`, except the parameter is null-terminated.
1142pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
1143 return os.readlinkC(pathname_c, buffer);
8741144}
8751145
8761146pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError;
lib/std/fs/file.zig+1
......@@ -243,6 +243,7 @@ pub const File = struct {
243243 switch (rc) {
244244 windows.STATUS.SUCCESS => {},
245245 windows.STATUS.BUFFER_OVERFLOW => {},
246 windows.STATUS.INVALID_PARAMETER => unreachable,
246247 else => return windows.unexpectedStatus(rc),
247248 }
248249 return Stat{
lib/std/fs/path.zig+19
......@@ -136,6 +136,25 @@ pub fn isAbsolute(path: []const u8) bool {
136136 }
137137}
138138
139pub fn isAbsoluteW(path_w: [*]const u16) bool {
140 if (path_w[0] == '/')
141 return true;
142
143 if (path_w[0] == '\\') {
144 return true;
145 }
146 if (path_w[0] == 0 or path_w[1] == 0 or path_w[2] == 0) {
147 return false;
148 }
149 if (path_w[1] == ':') {
150 if (path_w[2] == '/')
151 return true;
152 if (path_w[2] == '\\')
153 return true;
154 }
155 return false;
156}
157
139158pub fn isAbsoluteWindows(path: []const u8) bool {
140159 if (path[0] == '/')
141160 return true;
lib/std/io.zig+3
......@@ -127,6 +127,7 @@ pub fn OutStream(comptime WriteError: type) type {
127127 };
128128}
129129
130/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
130131pub fn writeFile(path: []const u8, data: []const u8) !void {
131132 var file = try File.openWrite(path);
132133 defer file.close();
......@@ -134,11 +135,13 @@ pub fn writeFile(path: []const u8, data: []const u8) !void {
134135}
135136
136137/// On success, caller owns returned buffer.
138/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
137139pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
138140 return readFileAllocAligned(allocator, path, @alignOf(u8));
139141}
140142
141143/// On success, caller owns returned buffer.
144/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
142145pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 {
143146 var file = try File.openRead(path);
144147 defer file.close();
lib/std/os.zig+146-3
......@@ -529,22 +529,36 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void
529529
530530pub const OpenError = error{
531531 AccessDenied,
532 FileTooBig,
533 IsDir,
534532 SymLinkLoop,
535533 ProcessFdQuotaExceeded,
536 NameTooLong,
537534 SystemFdQuotaExceeded,
538535 NoDevice,
539536 FileNotFound,
540537
538 /// The path exceeded `MAX_PATH_BYTES` bytes.
539 NameTooLong,
540
541541 /// Insufficient kernel memory was available, or
542542 /// the named file is a FIFO and per-user hard limit on
543543 /// memory allocation for pipes has been reached.
544544 SystemResources,
545545
546 /// The file is too large to be opened. This error is unreachable
547 /// for 64-bit targets, as well as when opening directories.
548 FileTooBig,
549
550 /// The path refers to directory but the `O_DIRECTORY` flag was not provided.
551 IsDir,
552
553 /// A new path cannot be created because the device has no room for the new file.
554 /// This error is only reachable when the `O_CREAT` flag is provided.
546555 NoSpaceLeft,
556
557 /// A component used as a directory in the path was not, in fact, a directory, or
558 /// `O_DIRECTORY` was specified and the path was not a directory.
547559 NotDir,
560
561 /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided.
548562 PathAlreadyExists,
549563 DeviceBusy,
550564} || UnexpectedError;
......@@ -978,6 +992,114 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void {
978992 }
979993}
980994
995pub const UnlinkatError = UnlinkError || error{
996 /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty.
997 DirNotEmpty,
998};
999
1000/// Delete a file name and possibly the file it refers to, based on an open directory handle.
1001pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
1002 if (windows.is_the_target) {
1003 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
1004 return unlinkatW(dirfd, &file_path_w, flags);
1005 }
1006 const file_path_c = try toPosixPath(file_path);
1007 return unlinkatC(dirfd, &file_path_c, flags);
1008}
1009
1010/// Same as `unlinkat` but `file_path` is a null-terminated string.
1011pub fn unlinkatC(dirfd: fd_t, file_path_c: [*]const u8, flags: u32) UnlinkatError!void {
1012 if (windows.is_the_target) {
1013 const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
1014 return unlinkatW(dirfd, &file_path_w, flags);
1015 }
1016 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
1017 0 => return,
1018 EACCES => return error.AccessDenied,
1019 EPERM => return error.AccessDenied,
1020 EBUSY => return error.FileBusy,
1021 EFAULT => unreachable,
1022 EIO => return error.FileSystem,
1023 EISDIR => return error.IsDir,
1024 ELOOP => return error.SymLinkLoop,
1025 ENAMETOOLONG => return error.NameTooLong,
1026 ENOENT => return error.FileNotFound,
1027 ENOTDIR => return error.NotDir,
1028 ENOMEM => return error.SystemResources,
1029 EROFS => return error.ReadOnlyFileSystem,
1030 ENOTEMPTY => return error.DirNotEmpty,
1031
1032 EINVAL => unreachable, // invalid flags, or pathname has . as last component
1033 EBADF => unreachable, // always a race condition
1034
1035 else => |err| return unexpectedErrno(err),
1036 }
1037}
1038
1039/// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only.
1040pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*]const u16, flags: u32) UnlinkatError!void {
1041 const w = windows;
1042
1043 const want_rmdir_behavior = (flags & AT_REMOVEDIR) != 0;
1044 const create_options_flags = if (want_rmdir_behavior)
1045 w.ULONG(w.FILE_DELETE_ON_CLOSE)
1046 else
1047 w.ULONG(w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE);
1048
1049 const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2);
1050 var nt_name = w.UNICODE_STRING{
1051 .Length = path_len_bytes,
1052 .MaximumLength = path_len_bytes,
1053 // The Windows API makes this mutable, but it will not mutate here.
1054 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
1055 };
1056
1057 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
1058 // Windows does not recognize this, but it does work with empty string.
1059 nt_name.Length = 0;
1060 }
1061 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
1062 // Can't remove the parent directory with an open handle.
1063 return error.FileBusy;
1064 }
1065
1066
1067 var attr = w.OBJECT_ATTRIBUTES{
1068 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
1069 .RootDirectory = dirfd,
1070 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
1071 .ObjectName = &nt_name,
1072 .SecurityDescriptor = null,
1073 .SecurityQualityOfService = null,
1074 };
1075 var io: w.IO_STATUS_BLOCK = undefined;
1076 var tmp_handle: w.HANDLE = undefined;
1077 var rc = w.ntdll.NtCreateFile(
1078 &tmp_handle,
1079 w.SYNCHRONIZE | w.DELETE,
1080 &attr,
1081 &io,
1082 null,
1083 0,
1084 w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE,
1085 w.FILE_OPEN,
1086 create_options_flags,
1087 null,
1088 0,
1089 );
1090 if (rc == w.STATUS.SUCCESS) {
1091 rc = w.ntdll.NtClose(tmp_handle);
1092 }
1093 switch (rc) {
1094 w.STATUS.SUCCESS => return,
1095 w.STATUS.OBJECT_NAME_INVALID => unreachable,
1096 w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
1097 w.STATUS.INVALID_PARAMETER => unreachable,
1098 w.STATUS.FILE_IS_A_DIRECTORY => return error.IsDir,
1099 else => return w.unexpectedStatus(rc),
1100 }
1101}
1102
9811103const RenameError = error{
9821104 AccessDenied,
9831105 FileBusy,
......@@ -1237,6 +1359,27 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
12371359 }
12381360}
12391361
1362pub fn readlinkatC(dirfd: fd_t, file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
1363 if (windows.is_the_target) {
1364 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
1365 @compileError("TODO implement readlink for Windows");
1366 }
1367 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
1368 switch (errno(rc)) {
1369 0 => return out_buffer[0..@bitCast(usize, rc)],
1370 EACCES => return error.AccessDenied,
1371 EFAULT => unreachable,
1372 EINVAL => unreachable,
1373 EIO => return error.FileSystem,
1374 ELOOP => return error.SymLinkLoop,
1375 ENAMETOOLONG => return error.NameTooLong,
1376 ENOENT => return error.FileNotFound,
1377 ENOMEM => return error.SystemResources,
1378 ENOTDIR => return error.NotDir,
1379 else => |err| return unexpectedErrno(err),
1380 }
1381}
1382
12401383pub const SetIdError = error{
12411384 ResourceLimitReached,
12421385 InvalidUserId,
lib/std/os/bits/darwin.zig+14
......@@ -1178,3 +1178,17 @@ pub fn S_IWHT(m: u32) bool {
11781178 return m & S_IFMT == S_IFWHT;
11791179}
11801180pub const HOST_NAME_MAX = 72;
1181
1182pub const AT_FDCWD = -2;
1183
1184/// Use effective ids in access check
1185pub const AT_EACCESS = 0x0010;
1186
1187/// Act on the symlink itself not the target
1188pub const AT_SYMLINK_NOFOLLOW = 0x0020;
1189
1190/// Act on target of symlink
1191pub const AT_SYMLINK_FOLLOW = 0x0040;
1192
1193/// Path refers to directory
1194pub const AT_REMOVEDIR = 0x0080;
lib/std/os/bits/freebsd.zig+20
......@@ -939,3 +939,23 @@ pub fn S_IWHT(m: u32) bool {
939939}
940940
941941pub const HOST_NAME_MAX = 255;
942
943/// Magic value that specify the use of the current working directory
944/// to determine the target of relative file paths in the openat() and
945/// similar syscalls.
946pub const AT_FDCWD = -100;
947
948/// Check access using effective user and group ID
949pub const AT_EACCESS = 0x0100;
950
951/// Do not follow symbolic links
952pub const AT_SYMLINK_NOFOLLOW = 0x0200;
953
954/// Follow symbolic link
955pub const AT_SYMLINK_FOLLOW = 0x0400;
956
957/// Remove directory instead of file
958pub const AT_REMOVEDIR = 0x0800;
959
960/// Fail if not under dirfd
961pub const AT_BENEATH = 0x1000;
lib/std/os/bits/windows.zig+3
......@@ -158,3 +158,6 @@ pub const EWOULDBLOCK = 140;
158158pub const EDQUOT = 10069;
159159
160160pub const F_OK = 0;
161
162/// Remove directory instead of unlinking file
163pub const AT_REMOVEDIR = 0x200;
lib/std/os/test.zig+3-3
......@@ -19,8 +19,8 @@ test "makePath, put some files in it, deleteTree" {
1919 try fs.makePath(a, "os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
2020 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
2121 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
22 try fs.deleteTree(a, "os_test_tmp");
23 if (fs.Dir.open(a, "os_test_tmp")) |dir| {
22 try fs.deleteTree("os_test_tmp");
23 if (fs.Dir.open("os_test_tmp")) |dir| {
2424 @panic("expected error");
2525 } else |err| {
2626 expect(err == error.FileNotFound);
......@@ -37,7 +37,7 @@ test "access file" {
3737
3838 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");
3939 try os.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", os.F_OK);
40 try fs.deleteTree(a, "os_test_tmp");
40 try fs.deleteTree("os_test_tmp");
4141}
4242
4343fn testThreadIdFn(thread_id: *Thread.Id) void {
lib/std/os/windows.zig+24-3
......@@ -20,6 +20,8 @@ pub const shell32 = @import("windows/shell32.zig");
2020
2121pub usingnamespace @import("windows/bits.zig");
2222
23pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
24
2325/// `builtin` is missing `subsystem` when the subsystem is automatically detected,
2426/// so Zig standard library has the subsystem detection logic here. This should generally be
2527/// used rather than `builtin.subsystem`.
......@@ -791,6 +793,25 @@ pub fn SetFileTime(
791793 }
792794}
793795
796pub fn peb() *PEB {
797 switch (builtin.arch) {
798 .i386 => {
799 return asm (
800 \\ mov %%fs:0x18, %[ptr]
801 \\ mov %%ds:0x30(%[ptr]), %[ptr]
802 : [ptr] "=r" (-> *PEB)
803 );
804 },
805 .x86_64 => {
806 return asm (
807 \\ mov %%gs:0x60, %[ptr]
808 : [ptr] "=r" (-> *PEB)
809 );
810 },
811 else => @compileError("unsupported architecture"),
812 }
813}
814
794815/// A file time is a 64-bit value that represents the number of 100-nanosecond
795816/// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated
796817/// Universal Time (UTC).
......@@ -843,8 +864,8 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16)
843864 else => {},
844865 }
845866 }
846 const start_index = if (mem.startsWith(u8, s, "\\\\") or !std.fs.path.isAbsolute(s)) 0 else blk: {
847 const prefix = [_]u16{ '\\', '\\', '?', '\\' };
867 const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: {
868 const prefix = [_]u16{ '\\', '?', '?', '\\' };
848869 mem.copy(u16, result[0..], prefix);
849870 break :blk prefix.len;
850871 };
......@@ -878,7 +899,7 @@ pub fn unexpectedError(err: DWORD) std.os.UnexpectedError {
878899/// and you get an unexpected status.
879900pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {
880901 if (std.os.unexpected_error_tracing) {
881 std.debug.warn("error.Unexpected NTSTATUS={}\n", status);
902 std.debug.warn("error.Unexpected NTSTATUS=0x{x}\n", status);
882903 std.debug.dumpCurrentStackTrace(null);
883904 }
884905 return error.Unexpected;
lib/std/os/windows/bits.zig+145-3
......@@ -300,6 +300,44 @@ pub const FILE_SHARE_DELETE = 0x00000004;
300300pub const FILE_SHARE_READ = 0x00000001;
301301pub const FILE_SHARE_WRITE = 0x00000002;
302302
303pub const DELETE = 0x00010000;
304pub const READ_CONTROL = 0x00020000;
305pub const WRITE_DAC = 0x00040000;
306pub const WRITE_OWNER = 0x00080000;
307pub const SYNCHRONIZE = 0x00100000;
308pub const STANDARD_RIGHTS_REQUIRED = 0x000f0000;
309
310// disposition for NtCreateFile
311pub const FILE_SUPERSEDE = 0;
312pub const FILE_OPEN = 1;
313pub const FILE_CREATE = 2;
314pub const FILE_OPEN_IF = 3;
315pub const FILE_OVERWRITE = 4;
316pub const FILE_OVERWRITE_IF = 5;
317pub const FILE_MAXIMUM_DISPOSITION = 5;
318
319// flags for NtCreateFile and NtOpenFile
320pub const FILE_DIRECTORY_FILE = 0x00000001;
321pub const FILE_WRITE_THROUGH = 0x00000002;
322pub const FILE_SEQUENTIAL_ONLY = 0x00000004;
323pub const FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008;
324pub const FILE_SYNCHRONOUS_IO_ALERT = 0x00000010;
325pub const FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020;
326pub const FILE_NON_DIRECTORY_FILE = 0x00000040;
327pub const FILE_CREATE_TREE_CONNECTION = 0x00000080;
328pub const FILE_COMPLETE_IF_OPLOCKED = 0x00000100;
329pub const FILE_NO_EA_KNOWLEDGE = 0x00000200;
330pub const FILE_OPEN_FOR_RECOVERY = 0x00000400;
331pub const FILE_RANDOM_ACCESS = 0x00000800;
332pub const FILE_DELETE_ON_CLOSE = 0x00001000;
333pub const FILE_OPEN_BY_FILE_ID = 0x00002000;
334pub const FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000;
335pub const FILE_NO_COMPRESSION = 0x00008000;
336pub const FILE_RESERVE_OPFILTER = 0x00100000;
337pub const FILE_TRANSACTED_MODE = 0x00200000;
338pub const FILE_OPEN_OFFLINE_FILE = 0x00400000;
339pub const FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000;
340
303341pub const CREATE_ALWAYS = 2;
304342pub const CREATE_NEW = 1;
305343pub const OPEN_ALWAYS = 4;
......@@ -720,15 +758,119 @@ pub const VECTORED_EXCEPTION_HANDLER = stdcallcc fn (ExceptionInfo: *EXCEPTION_P
720758
721759pub const OBJECT_ATTRIBUTES = extern struct {
722760 Length: ULONG,
723 RootDirectory: HANDLE,
761 RootDirectory: ?HANDLE,
724762 ObjectName: *UNICODE_STRING,
725763 Attributes: ULONG,
726764 SecurityDescriptor: ?*c_void,
727765 SecurityQualityOfService: ?*c_void,
728766};
729767
768pub const OBJ_INHERIT = 0x00000002;
769pub const OBJ_PERMANENT = 0x00000010;
770pub const OBJ_EXCLUSIVE = 0x00000020;
771pub const OBJ_CASE_INSENSITIVE = 0x00000040;
772pub const OBJ_OPENIF = 0x00000080;
773pub const OBJ_OPENLINK = 0x00000100;
774pub const OBJ_KERNEL_HANDLE = 0x00000200;
775pub const OBJ_VALID_ATTRIBUTES = 0x000003F2;
776
730777pub const UNICODE_STRING = extern struct {
731 Length: USHORT,
732 MaximumLength: USHORT,
778 Length: c_ushort,
779 MaximumLength: c_ushort,
733780 Buffer: [*]WCHAR,
734781};
782
783pub const PEB = extern struct {
784 Reserved1: [2]BYTE,
785 BeingDebugged: BYTE,
786 Reserved2: [1]BYTE,
787 Reserved3: [2]PVOID,
788 Ldr: *PEB_LDR_DATA,
789 ProcessParameters: *RTL_USER_PROCESS_PARAMETERS,
790 Reserved4: [3]PVOID,
791 AtlThunkSListPtr: PVOID,
792 Reserved5: PVOID,
793 Reserved6: ULONG,
794 Reserved7: PVOID,
795 Reserved8: ULONG,
796 AtlThunkSListPtr32: ULONG,
797 Reserved9: [45]PVOID,
798 Reserved10: [96]BYTE,
799 PostProcessInitRoutine: PPS_POST_PROCESS_INIT_ROUTINE,
800 Reserved11: [128]BYTE,
801 Reserved12: [1]PVOID,
802 SessionId: ULONG,
803};
804
805pub const PEB_LDR_DATA = extern struct {
806 Reserved1: [8]BYTE,
807 Reserved2: [3]PVOID,
808 InMemoryOrderModuleList: LIST_ENTRY,
809};
810
811pub const RTL_USER_PROCESS_PARAMETERS = extern struct {
812 AllocationSize: ULONG,
813 Size: ULONG,
814 Flags: ULONG,
815 DebugFlags: ULONG,
816 ConsoleHandle: HANDLE,
817 ConsoleFlags: ULONG,
818 hStdInput: HANDLE,
819 hStdOutput: HANDLE,
820 hStdError: HANDLE,
821 CurrentDirectory: CURDIR,
822 DllPath: UNICODE_STRING,
823 ImagePathName: UNICODE_STRING,
824 CommandLine: UNICODE_STRING,
825 Environment: [*]WCHAR,
826 dwX: ULONG,
827 dwY: ULONG,
828 dwXSize: ULONG,
829 dwYSize: ULONG,
830 dwXCountChars: ULONG,
831 dwYCountChars: ULONG,
832 dwFillAttribute: ULONG,
833 dwFlags: ULONG,
834 dwShowWindow: ULONG,
835 WindowTitle: UNICODE_STRING,
836 Desktop: UNICODE_STRING,
837 ShellInfo: UNICODE_STRING,
838 RuntimeInfo: UNICODE_STRING,
839 DLCurrentDirectory: [0x20]RTL_DRIVE_LETTER_CURDIR,
840};
841
842pub const RTL_DRIVE_LETTER_CURDIR = extern struct {
843 Flags: c_ushort,
844 Length: c_ushort,
845 TimeStamp: ULONG,
846 DosPath: UNICODE_STRING,
847};
848
849pub const PPS_POST_PROCESS_INIT_ROUTINE = ?extern fn () void;
850
851pub const FILE_BOTH_DIR_INFORMATION = extern struct {
852 NextEntryOffset: ULONG,
853 FileIndex: ULONG,
854 CreationTime: LARGE_INTEGER,
855 LastAccessTime: LARGE_INTEGER,
856 LastWriteTime: LARGE_INTEGER,
857 ChangeTime: LARGE_INTEGER,
858 EndOfFile: LARGE_INTEGER,
859 AllocationSize: LARGE_INTEGER,
860 FileAttributes: ULONG,
861 FileNameLength: ULONG,
862 EaSize: ULONG,
863 ShortNameLength: CHAR,
864 ShortName: [12]WCHAR,
865 FileName: [1]WCHAR,
866};
867pub const FILE_BOTH_DIRECTORY_INFORMATION = FILE_BOTH_DIR_INFORMATION;
868
869pub const IO_APC_ROUTINE = extern fn (PVOID, *IO_STATUS_BLOCK, ULONG) void;
870
871pub const CURDIR = extern struct {
872 DosPath: UNICODE_STRING,
873 Handle: HANDLE,
874};
875
876pub const DUPLICATE_SAME_ACCESS = 2;
\ No newline at end of file
lib/std/os/windows/kernel32.zig+2
......@@ -47,6 +47,8 @@ pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_
4747
4848pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
4949
50pub extern "kernel32" stdcallcc fn DuplicateHandle(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE, hTargetProcessHandle: HANDLE, lpTargetHandle: *HANDLE, dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwOptions: DWORD) BOOL;
51
5052pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
5153
5254pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE;
lib/std/os/windows/ntdll.zig+23-2
......@@ -13,12 +13,33 @@ pub extern "NtDll" stdcallcc fn NtCreateFile(
1313 DesiredAccess: ACCESS_MASK,
1414 ObjectAttributes: *OBJECT_ATTRIBUTES,
1515 IoStatusBlock: *IO_STATUS_BLOCK,
16 AllocationSize: *LARGE_INTEGER,
16 AllocationSize: ?*LARGE_INTEGER,
1717 FileAttributes: ULONG,
1818 ShareAccess: ULONG,
1919 CreateDisposition: ULONG,
2020 CreateOptions: ULONG,
21 EaBuffer: *c_void,
21 EaBuffer: ?*c_void,
2222 EaLength: ULONG,
2323) NTSTATUS;
2424pub extern "NtDll" stdcallcc fn NtClose(Handle: HANDLE) NTSTATUS;
25pub extern "NtDll" stdcallcc fn RtlDosPathNameToNtPathName_U(
26 DosPathName: [*]const u16,
27 NtPathName: *UNICODE_STRING,
28 NtFileNamePart: ?*?[*]const u16,
29 DirectoryInfo: ?*CURDIR,
30) BOOL;
31pub extern "NtDll" stdcallcc fn RtlFreeUnicodeString(UnicodeString: *UNICODE_STRING) void;
32
33pub extern "NtDll" stdcallcc fn NtQueryDirectoryFile(
34 FileHandle: HANDLE,
35 Event: ?HANDLE,
36 ApcRoutine: ?IO_APC_ROUTINE,
37 ApcContext: ?*c_void,
38 IoStatusBlock: *IO_STATUS_BLOCK,
39 FileInformation: *c_void,
40 Length: ULONG,
41 FileInformationClass: FILE_INFORMATION_CLASS,
42 ReturnSingleEntry: BOOLEAN,
43 FileName: ?*UNICODE_STRING,
44 RestartScan: BOOLEAN,
45) NTSTATUS;
lib/std/os/windows/status.zig+8
......@@ -3,3 +3,11 @@ pub const SUCCESS = 0x00000000;
33
44/// The data was too large to fit into the specified buffer.
55pub const BUFFER_OVERFLOW = 0x80000005;
6
7pub const INVALID_PARAMETER = 0xC000000D;
8pub const ACCESS_DENIED = 0xC0000022;
9pub const OBJECT_NAME_INVALID = 0xC0000033;
10pub const OBJECT_NAME_NOT_FOUND = 0xC0000034;
11pub const OBJECT_PATH_NOT_FOUND = 0xC000003A;
12pub const OBJECT_PATH_SYNTAX_BAD = 0xC000003B;
13pub const FILE_IS_A_DIRECTORY = 0xC00000BA;
lib/std/special/test_runner.zig+6-1
......@@ -16,14 +16,17 @@ pub fn main() anyerror!void {
1616 var test_node = root_node.start(test_fn.name, null);
1717 test_node.activate();
1818 progress.refresh();
19 if (progress.terminal == null) std.debug.warn("{}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
1920 if (test_fn.func()) |_| {
2021 ok_count += 1;
2122 test_node.end();
23 if (progress.terminal == null) std.debug.warn("OK\n");
2224 } else |err| switch (err) {
2325 error.SkipZigTest => {
2426 skip_count += 1;
2527 test_node.end();
2628 progress.log("{}...SKIP\n", test_fn.name);
29 if (progress.terminal == null) std.debug.warn("SKIP\n");
2730 },
2831 else => {
2932 progress.log("");
......@@ -32,7 +35,9 @@ pub fn main() anyerror!void {
3235 }
3336 }
3437 root_node.end();
35 if (ok_count != test_fn_list.len) {
38 if (ok_count == test_fn_list.len) {
39 std.debug.warn("All tests passed.\n");
40 } else {
3641 std.debug.warn("{} passed; {} skipped.\n", ok_count, skip_count);
3742 }
3843}
src-self-hosted/main.zig+1-1
......@@ -747,7 +747,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
747747 )) catch |err| switch (err) {
748748 error.IsDir, error.AccessDenied => {
749749 // TODO make event based (and dir.next())
750 var dir = try fs.Dir.open(fmt.loop.allocator, file_path);
750 var dir = try fs.Dir.open(file_path);
751751 defer dir.close();
752752
753753 var group = event.Group(FmtError!void).init(fmt.loop);
src-self-hosted/stage1.zig+5-3
......@@ -283,11 +283,13 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void
283283 const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {
284284 error.IsDir, error.AccessDenied => {
285285 // TODO make event based (and dir.next())
286 var dir = try fs.Dir.open(fmt.allocator, file_path);
286 var dir = try fs.Dir.open(file_path);
287287 defer dir.close();
288288
289 while (try dir.next()) |entry| {
290 if (entry.kind == fs.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
289 var dir_it = dir.iterate();
290
291 while (try dir_it.next()) |entry| {
292 if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
291293 const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name });
292294 try fmtPath(fmt, full_path, check_mode);
293295 }
src-self-hosted/test.zig+2-2
......@@ -56,11 +56,11 @@ pub const TestContext = struct {
5656 errdefer allocator.free(self.zig_lib_dir);
5757
5858 try std.fs.makePath(allocator, tmp_dir_name);
59 errdefer std.fs.deleteTree(allocator, tmp_dir_name) catch {};
59 errdefer std.fs.deleteTree(tmp_dir_name) catch {};
6060 }
6161
6262 fn deinit(self: *TestContext) void {
63 std.fs.deleteTree(allocator, tmp_dir_name) catch {};
63 std.fs.deleteTree(tmp_dir_name) catch {};
6464 allocator.free(self.zig_lib_dir);
6565 self.zig_compiler.deinit();
6666 self.loop.deinit();
test/cli.zig+2-2
......@@ -37,7 +37,7 @@ pub fn main() !void {
3737 testMissingOutputPath,
3838 };
3939 for (test_fns) |testFn| {
40 try fs.deleteTree(a, dir_path);
40 try fs.deleteTree(dir_path);
4141 try fs.makeDir(dir_path);
4242 try testFn(zig_exe, dir_path);
4343 }
......@@ -87,7 +87,7 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {
8787fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {
8888 _ = try exec(dir_path, [_][]const u8{ zig_exe, "init-lib" });
8989 const test_result = try exec(dir_path, [_][]const u8{ zig_exe, "build", "test" });
90 testing.expect(std.mem.eql(u8, test_result.stderr, ""));
90 testing.expect(std.mem.endsWith(u8, test_result.stderr, "All tests passed.\n"));
9191}
9292
9393fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
tools/process_headers.zig+1-1
......@@ -340,7 +340,7 @@ pub fn main() !void {
340340 try dir_stack.append(target_include_dir);
341341
342342 while (dir_stack.popOrNull()) |full_dir_name| {
343 var dir = std.fs.Dir.open(allocator, full_dir_name) catch |err| switch (err) {
343 var dir = std.fs.Dir.open(full_dir_name) catch |err| switch (err) {
344344 error.FileNotFound => continue :search,
345345 error.AccessDenied => continue :search,
346346 else => return err,