authorgravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2024-02-25 12:03:23+01:00
committergravatar for igor.anic@gmail.comIgor Anić <igor.anic@gmail.com> 2024-02-25 12:03:23+01:00
log30a319be6dd14c773094e784c27b89b4f10622b2
tree5350121395fed4b4dcf9e543c36402deb0ee57ee
parent96e4d568195602afde3486c2d9de890a383a1c4b

std.tar improve error reporting

Report file name which failed to create in all cases.

3 files changed, 64 insertions(+), 39 deletions(-)

lib/std/tar.zig+36-39
......@@ -544,31 +544,15 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
544544 const file_name = stripComponents(file.name, options.strip_components);
545545 if (file_name.len == 0) return error.BadFileName;
546546
547 const fs_file = dir.createFile(file_name, .{ .exclusive = true }) catch |err| switch (err) {
548 error.FileNotFound => again: {
549 const code = code: {
550 if (std.fs.path.dirname(file_name)) |dir_name| {
551 dir.makePath(dir_name) catch |code| break :code code;
552 break :again dir.createFile(file_name, .{ .exclusive = true }) catch |code| {
553 break :code code;
554 };
555 }
556 break :code err;
557 };
558 const d = options.diagnostics orelse return error.UnableToCreateFile;
559 try d.errors.append(d.allocator, .{ .unable_to_create_file = .{
560 .code = code,
561 .file_name = try d.allocator.dupe(u8, file_name),
562 } });
563 break :again null;
564 },
565 else => |e| return e,
566 };
567 defer if (fs_file) |f| f.close();
568
569 if (fs_file) |f| {
570 try file.write(f);
571 } else {
547 if (createDirAndFile(dir, file_name)) |fs_file| {
548 defer fs_file.close();
549 try file.write(fs_file);
550 } else |err| {
551 const d = options.diagnostics orelse return err;
552 try d.errors.append(d.allocator, .{ .unable_to_create_file = .{
553 .code = err,
554 .file_name = try d.allocator.dupe(u8, file_name),
555 } });
572556 try file.skip();
573557 }
574558 },
......@@ -579,21 +563,10 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
579563 // The data inside the symbolic link.
580564 const link_name = file.link_name;
581565
582 dir.symLink(link_name, file_name, .{}) catch |err| again: {
583 const code = code: {
584 if (err == error.FileNotFound) {
585 if (std.fs.path.dirname(file_name)) |dir_name| {
586 dir.makePath(dir_name) catch |code| break :code code;
587 break :again dir.symLink(link_name, file_name, .{}) catch |code| {
588 break :code code;
589 };
590 }
591 }
592 break :code err;
593 };
594 const d = options.diagnostics orelse return error.UnableToCreateSymLink;
566 createDirAndSymlink(dir, link_name, file_name) catch |err| {
567 const d = options.diagnostics orelse return err;
595568 try d.errors.append(d.allocator, .{ .unable_to_create_sym_link = .{
596 .code = code,
569 .code = err,
597570 .file_name = try d.allocator.dupe(u8, file_name),
598571 .link_name = try d.allocator.dupe(u8, link_name),
599572 } });
......@@ -604,6 +577,30 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
604577 }
605578}
606579
580fn createDirAndFile(dir: std.fs.Dir, file_name: []const u8) !std.fs.File {
581 const fs_file = dir.createFile(file_name, .{ .exclusive = true }) catch |err| {
582 if (err == error.FileNotFound) {
583 if (std.fs.path.dirname(file_name)) |dir_name| {
584 try dir.makePath(dir_name);
585 return try dir.createFile(file_name, .{ .exclusive = true });
586 }
587 }
588 return err;
589 };
590 return fs_file;
591}
592
593fn createDirAndSymlink(dir: std.fs.Dir, link_name: []const u8, file_name: []const u8) !void {
594 dir.symLink(link_name, file_name, .{}) catch |err| {
595 if (err == error.FileNotFound) {
596 if (std.fs.path.dirname(file_name)) |dir_name| {
597 try dir.makePath(dir_name);
598 try dir.symLink(link_name, file_name, .{});
599 }
600 }
601 };
602}
603
607604fn stripComponents(path: []const u8, count: u32) []const u8 {
608605 var i: usize = 0;
609606 var c = count;
lib/std/tar/test.zig+28
......@@ -452,3 +452,31 @@ test "tar case sensitivity" {
452452 try testing.expect((try root.dir.statFile("alacritty/darkermatrix.yml")).kind == .file);
453453 try testing.expect((try root.dir.statFile("alacritty/Darkermatrix.yml")).kind == .file);
454454}
455
456test "tar pipeToFileSystem" {
457 // $ tar tvf
458 // pipe_to_file_system_test/
459 // pipe_to_file_system_test/b/
460 // pipe_to_file_system_test/b/symlink -> ../a/file
461 // pipe_to_file_system_test/a/
462 // pipe_to_file_system_test/a/file
463 // pipe_to_file_system_test/empty/
464 const data = @embedFile("testdata/pipe_to_file_system_test.tar");
465 var fsb = std.io.fixedBufferStream(data);
466
467 var root = std.testing.tmpDir(.{ .no_follow = true });
468 defer root.cleanup();
469
470 try tar.pipeToFileSystem(root.dir, fsb.reader(), .{
471 .mode_mode = .ignore,
472 .strip_components = 1,
473 .exclude_empty_directories = true,
474 });
475
476 try testing.expectError(error.FileNotFound, root.dir.statFile("empty"));
477 try testing.expect((try root.dir.statFile("a/file")).kind == .file);
478 // TODO is there better way to test symlink
479 try testing.expect((try root.dir.statFile("b/symlink")).kind == .file); // statFile follows symlink
480 var buf: [8]u8 = undefined;
481 _ = try root.dir.readLink("b/symlink", &buf);
482}
lib/std/tar/testdata/pipe_to_file_system_test.tar created
Binary files /dev/null and b/lib/std/tar/testdata/pipe_to_file_system_test.tar differ