Skip to content

Commit a5144d1

Browse files
committed
std.tar: support symlinks
closes #16678
1 parent 412d863 commit a5144d1

File tree

1 file changed

+53
-3
lines changed

1 file changed

+53
-3
lines changed

lib/std/tar.zig

+53-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ pub const Options = struct {
33
strip_components: u32 = 0,
44
/// How to handle the "mode" property of files from within the tar file.
55
mode_mode: ModeMode = .executable_bit_only,
6+
/// Provide this to receive detailed error messages.
7+
/// When this is provided, some errors which would otherwise be returned immediately
8+
/// will instead be added to this structure. The API user must check the errors
9+
/// in diagnostics to know whether the operation succeeded or failed.
10+
diagnostics: ?*Diagnostics = null,
611

7-
const ModeMode = enum {
12+
pub const ModeMode = enum {
813
/// The mode from the tar file is completely ignored. Files are created
914
/// with the default mode when creating files.
1015
ignore,
@@ -13,6 +18,32 @@ pub const Options = struct {
1318
/// Other bits of the mode are left as the default when creating files.
1419
executable_bit_only,
1520
};
21+
22+
pub const Diagnostics = struct {
23+
allocator: std.mem.Allocator,
24+
errors: std.ArrayListUnmanaged(Error) = .{},
25+
26+
pub const Error = union(enum) {
27+
unable_to_create_sym_link: struct {
28+
code: anyerror,
29+
file_name: []const u8,
30+
link_name: []const u8,
31+
},
32+
};
33+
34+
pub fn deinit(d: *Diagnostics) void {
35+
for (d.errors.items) |item| {
36+
switch (item) {
37+
.unable_to_create_sym_link => |info| {
38+
d.allocator.free(info.file_name);
39+
d.allocator.free(info.link_name);
40+
},
41+
}
42+
}
43+
d.errors.deinit(d.allocator);
44+
d.* = undefined;
45+
}
46+
};
1647
};
1748

1849
pub const Header = struct {
@@ -65,6 +96,10 @@ pub const Header = struct {
6596
return str(header, 0, 0 + 100);
6697
}
6798

99+
pub fn linkName(header: Header) []const u8 {
100+
return str(header, 157, 157 + 100);
101+
}
102+
68103
pub fn prefix(header: Header) []const u8 {
69104
return str(header, 345, 345 + 155);
70105
}
@@ -148,7 +183,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
148183
const header: Header = .{ .bytes = chunk[0..512] };
149184
const file_size = try header.fileSize();
150185
const rounded_file_size = std.mem.alignForward(u64, file_size, 512);
151-
const pad_len = @as(usize, @intCast(rounded_file_size - file_size));
186+
const pad_len: usize = @intCast(rounded_file_size - file_size);
152187
const unstripped_file_name = if (file_name_override_len > 0)
153188
file_name_buffer[0..file_name_override_len]
154189
else
@@ -228,7 +263,22 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
228263
buffer.skip(reader, @intCast(rounded_file_size)) catch return error.TarHeadersTooBig;
229264
},
230265
.hard_link => return error.TarUnsupportedFileType,
231-
.symbolic_link => return error.TarUnsupportedFileType,
266+
.symbolic_link => {
267+
const file_name = try stripComponents(unstripped_file_name, options.strip_components);
268+
const link_name = header.linkName();
269+
270+
dir.symLink(link_name, file_name, .{}) catch |err| {
271+
if (options.diagnostics) |d| {
272+
try d.errors.append(d.allocator, .{ .unable_to_create_sym_link = .{
273+
.code = err,
274+
.file_name = try d.allocator.dupe(u8, file_name),
275+
.link_name = try d.allocator.dupe(u8, link_name),
276+
} });
277+
} else {
278+
return error.UnableToCreateSymLink;
279+
}
280+
};
281+
},
232282
else => return error.TarUnsupportedFileType,
233283
}
234284
}

0 commit comments

Comments
 (0)