diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2023-09-30 23:00:39 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2023-10-02 17:02:24 -0700 |
| commit | a5144d19b7a3585122dafbe05f7a1ce21f61a992 (patch) | |
| tree | dd50ab6152444bfa2de23509a2753ae0c0eee550 /lib | |
| parent | 412d863ba5801c1376af7ab8f04a71b839a820a6 (diff) | |
| download | zig-a5144d19b7a3585122dafbe05f7a1ce21f61a992.tar.gz zig-a5144d19b7a3585122dafbe05f7a1ce21f61a992.zip | |
std.tar: support symlinks
closes #16678
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/std/tar.zig | 56 |
1 files changed, 53 insertions, 3 deletions
diff --git a/lib/std/tar.zig b/lib/std/tar.zig index fc0d71ce02..d927cce303 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -3,8 +3,13 @@ pub const Options = struct { strip_components: u32 = 0, /// How to handle the "mode" property of files from within the tar file. mode_mode: ModeMode = .executable_bit_only, + /// Provide this to receive detailed error messages. + /// When this is provided, some errors which would otherwise be returned immediately + /// will instead be added to this structure. The API user must check the errors + /// in diagnostics to know whether the operation succeeded or failed. + diagnostics: ?*Diagnostics = null, - const ModeMode = enum { + pub const ModeMode = enum { /// The mode from the tar file is completely ignored. Files are created /// with the default mode when creating files. ignore, @@ -13,6 +18,32 @@ pub const Options = struct { /// Other bits of the mode are left as the default when creating files. executable_bit_only, }; + + pub const Diagnostics = struct { + allocator: std.mem.Allocator, + errors: std.ArrayListUnmanaged(Error) = .{}, + + pub const Error = union(enum) { + unable_to_create_sym_link: struct { + code: anyerror, + file_name: []const u8, + link_name: []const u8, + }, + }; + + pub fn deinit(d: *Diagnostics) void { + for (d.errors.items) |item| { + switch (item) { + .unable_to_create_sym_link => |info| { + d.allocator.free(info.file_name); + d.allocator.free(info.link_name); + }, + } + } + d.errors.deinit(d.allocator); + d.* = undefined; + } + }; }; pub const Header = struct { @@ -65,6 +96,10 @@ pub const Header = struct { return str(header, 0, 0 + 100); } + pub fn linkName(header: Header) []const u8 { + return str(header, 157, 157 + 100); + } + pub fn prefix(header: Header) []const u8 { return str(header, 345, 345 + 155); } @@ -148,7 +183,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi const header: Header = .{ .bytes = chunk[0..512] }; const file_size = try header.fileSize(); const rounded_file_size = std.mem.alignForward(u64, file_size, 512); - const pad_len = @as(usize, @intCast(rounded_file_size - file_size)); + const pad_len: usize = @intCast(rounded_file_size - file_size); const unstripped_file_name = if (file_name_override_len > 0) file_name_buffer[0..file_name_override_len] else @@ -228,7 +263,22 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi buffer.skip(reader, @intCast(rounded_file_size)) catch return error.TarHeadersTooBig; }, .hard_link => return error.TarUnsupportedFileType, - .symbolic_link => return error.TarUnsupportedFileType, + .symbolic_link => { + const file_name = try stripComponents(unstripped_file_name, options.strip_components); + const link_name = header.linkName(); + + dir.symLink(link_name, file_name, .{}) catch |err| { + if (options.diagnostics) |d| { + try d.errors.append(d.allocator, .{ .unable_to_create_sym_link = .{ + .code = err, + .file_name = try d.allocator.dupe(u8, file_name), + .link_name = try d.allocator.dupe(u8, link_name), + } }); + } else { + return error.UnableToCreateSymLink; + } + }; + }, else => return error.TarUnsupportedFileType, } } |
