Commit 7b25d050e6

Andrew Kelley <andrew@ziglang.org>
2023-10-08 08:09:39
std.tar: fix creation of symlinks with omit_empty_directories
1 parent e5c2a7d
Changed files (2)
lib
src
Package
lib/std/tar.zig
@@ -31,6 +31,10 @@ pub const Options = struct {
                 file_name: []const u8,
                 link_name: []const u8,
             },
+            unable_to_create_file: struct {
+                code: anyerror,
+                file_name: []const u8,
+            },
             unsupported_file_type: struct {
                 file_name: []const u8,
                 file_type: Header.FileType,
@@ -44,6 +48,9 @@ pub const Options = struct {
                         d.allocator.free(info.file_name);
                         d.allocator.free(info.link_name);
                     },
+                    .unable_to_create_file => |info| {
+                        d.allocator.free(info.file_name);
+                    },
                     .unsupported_file_type => |info| {
                         d.allocator.free(info.file_name);
                     },
@@ -211,18 +218,34 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
                 if (file_size == 0 and unstripped_file_name.len == 0) return;
                 const file_name = try stripComponents(unstripped_file_name, options.strip_components);
 
-                if (std.fs.path.dirname(file_name)) |dir_name| {
-                    try dir.makePath(dir_name);
-                }
-                var file = try dir.createFile(file_name, .{});
-                defer file.close();
+                var file = dir.createFile(file_name, .{}) 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, .{}) 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 (file) |f| f.close();
 
                 var file_off: usize = 0;
                 while (true) {
                     const temp = try buffer.readChunk(reader, @intCast(rounded_file_size + 512 - file_off));
                     if (temp.len == 0) return error.UnexpectedEndOfStream;
                     const slice = temp[0..@intCast(@min(file_size - file_off, temp.len))];
-                    try file.writeAll(slice);
+                    if (file) |f| try f.writeAll(slice);
 
                     file_off += slice.len;
                     buffer.advance(slice.len);
@@ -275,13 +298,26 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
             },
             .hard_link => return error.TarUnsupportedFileType,
             .symbolic_link => {
+                // The file system path of the symbolic link.
                 const file_name = try stripComponents(unstripped_file_name, options.strip_components);
+                // The data inside the symbolic link.
                 const link_name = header.linkName();
 
-                dir.symLink(link_name, file_name, .{}) catch |err| {
+                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;
                     try d.errors.append(d.allocator, .{ .unable_to_create_sym_link = .{
-                        .code = err,
+                        .code = code,
                         .file_name = try d.allocator.dupe(u8, file_name),
                         .link_name = try d.allocator.dupe(u8, link_name),
                     } });
src/Package/Fetch.zig
@@ -1041,6 +1041,13 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {
                         }),
                     }));
                 },
+                .unable_to_create_file => |info| {
+                    eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{
+                        .msg = try eb.printString("unable to create file '{s}': {s}", .{
+                            info.file_name, @errorName(info.code),
+                        }),
+                    }));
+                },
                 .unsupported_file_type => |info| {
                     eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{
                         .msg = try eb.printString("file '{s}' has unsupported type '{c}'", .{