Commit 30a319be6d

Igor Anić <igor.anic@gmail.com>
2024-02-25 12:03:23
std.tar improve error reporting
Report file name which failed to create in all cases.
1 parent 96e4d56
Changed files (3)
lib/std/tar/testdata/pipe_to_file_system_test.tar
Binary file
lib/std/tar/test.zig
@@ -452,3 +452,31 @@ test "tar case sensitivity" {
     try testing.expect((try root.dir.statFile("alacritty/darkermatrix.yml")).kind == .file);
     try testing.expect((try root.dir.statFile("alacritty/Darkermatrix.yml")).kind == .file);
 }
+
+test "tar pipeToFileSystem" {
+    // $ tar tvf
+    //    pipe_to_file_system_test/
+    //    pipe_to_file_system_test/b/
+    //    pipe_to_file_system_test/b/symlink -> ../a/file
+    //    pipe_to_file_system_test/a/
+    //    pipe_to_file_system_test/a/file
+    //    pipe_to_file_system_test/empty/
+    const data = @embedFile("testdata/pipe_to_file_system_test.tar");
+    var fsb = std.io.fixedBufferStream(data);
+
+    var root = std.testing.tmpDir(.{ .no_follow = true });
+    defer root.cleanup();
+
+    try tar.pipeToFileSystem(root.dir, fsb.reader(), .{
+        .mode_mode = .ignore,
+        .strip_components = 1,
+        .exclude_empty_directories = true,
+    });
+
+    try testing.expectError(error.FileNotFound, root.dir.statFile("empty"));
+    try testing.expect((try root.dir.statFile("a/file")).kind == .file);
+    // TODO is there better way to test symlink
+    try testing.expect((try root.dir.statFile("b/symlink")).kind == .file); // statFile follows symlink
+    var buf: [8]u8 = undefined;
+    _ = try root.dir.readLink("b/symlink", &buf);
+}
lib/std/tar.zig
@@ -544,31 +544,15 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
                 const file_name = stripComponents(file.name, options.strip_components);
                 if (file_name.len == 0) return error.BadFileName;
 
-                const fs_file = dir.createFile(file_name, .{ .exclusive = true }) catch |err| switch (err) {
-                    error.FileNotFound => again: {
-                        const code = code: {
-                            if (std.fs.path.dirname(file_name)) |dir_name| {
-                                dir.makePath(dir_name) catch |code| break :code code;
-                                break :again dir.createFile(file_name, .{ .exclusive = true }) catch |code| {
-                                    break :code code;
-                                };
-                            }
-                            break :code err;
-                        };
-                        const d = options.diagnostics orelse return error.UnableToCreateFile;
-                        try d.errors.append(d.allocator, .{ .unable_to_create_file = .{
-                            .code = code,
-                            .file_name = try d.allocator.dupe(u8, file_name),
-                        } });
-                        break :again null;
-                    },
-                    else => |e| return e,
-                };
-                defer if (fs_file) |f| f.close();
-
-                if (fs_file) |f| {
-                    try file.write(f);
-                } else {
+                if (createDirAndFile(dir, file_name)) |fs_file| {
+                    defer fs_file.close();
+                    try file.write(fs_file);
+                } else |err| {
+                    const d = options.diagnostics orelse return err;
+                    try d.errors.append(d.allocator, .{ .unable_to_create_file = .{
+                        .code = err,
+                        .file_name = try d.allocator.dupe(u8, file_name),
+                    } });
                     try file.skip();
                 }
             },
@@ -579,21 +563,10 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
                 // The data inside the symbolic link.
                 const link_name = file.link_name;
 
-                dir.symLink(link_name, file_name, .{}) catch |err| again: {
-                    const code = code: {
-                        if (err == error.FileNotFound) {
-                            if (std.fs.path.dirname(file_name)) |dir_name| {
-                                dir.makePath(dir_name) catch |code| break :code code;
-                                break :again dir.symLink(link_name, file_name, .{}) catch |code| {
-                                    break :code code;
-                                };
-                            }
-                        }
-                        break :code err;
-                    };
-                    const d = options.diagnostics orelse return error.UnableToCreateSymLink;
+                createDirAndSymlink(dir, link_name, file_name) catch |err| {
+                    const d = options.diagnostics orelse return err;
                     try d.errors.append(d.allocator, .{ .unable_to_create_sym_link = .{
-                        .code = code,
+                        .code = err,
                         .file_name = try d.allocator.dupe(u8, file_name),
                         .link_name = try d.allocator.dupe(u8, link_name),
                     } });
@@ -604,6 +577,30 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
     }
 }
 
+fn createDirAndFile(dir: std.fs.Dir, file_name: []const u8) !std.fs.File {
+    const fs_file = dir.createFile(file_name, .{ .exclusive = true }) catch |err| {
+        if (err == error.FileNotFound) {
+            if (std.fs.path.dirname(file_name)) |dir_name| {
+                try dir.makePath(dir_name);
+                return try dir.createFile(file_name, .{ .exclusive = true });
+            }
+        }
+        return err;
+    };
+    return fs_file;
+}
+
+fn createDirAndSymlink(dir: std.fs.Dir, link_name: []const u8, file_name: []const u8) !void {
+    dir.symLink(link_name, file_name, .{}) catch |err| {
+        if (err == error.FileNotFound) {
+            if (std.fs.path.dirname(file_name)) |dir_name| {
+                try dir.makePath(dir_name);
+                try dir.symLink(link_name, file_name, .{});
+            }
+        }
+    };
+}
+
 fn stripComponents(path: []const u8, count: u32) []const u8 {
     var i: usize = 0;
     var c = count;