@@ -3,8 +3,13 @@ pub const Options = struct {
3
3
strip_components : u32 = 0 ,
4
4
/// How to handle the "mode" property of files from within the tar file.
5
5
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 ,
6
11
7
- const ModeMode = enum {
12
+ pub const ModeMode = enum {
8
13
/// The mode from the tar file is completely ignored. Files are created
9
14
/// with the default mode when creating files.
10
15
ignore ,
@@ -13,12 +18,46 @@ pub const Options = struct {
13
18
/// Other bits of the mode are left as the default when creating files.
14
19
executable_bit_only ,
15
20
};
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
+ unsupported_file_type : struct {
33
+ file_name : []const u8 ,
34
+ file_type : Header.FileType ,
35
+ },
36
+ };
37
+
38
+ pub fn deinit (d : * Diagnostics ) void {
39
+ for (d .errors .items ) | item | {
40
+ switch (item ) {
41
+ .unable_to_create_sym_link = > | info | {
42
+ d .allocator .free (info .file_name );
43
+ d .allocator .free (info .link_name );
44
+ },
45
+ .unsupported_file_type = > | info | {
46
+ d .allocator .free (info .file_name );
47
+ },
48
+ }
49
+ }
50
+ d .errors .deinit (d .allocator );
51
+ d .* = undefined ;
52
+ }
53
+ };
16
54
};
17
55
18
56
pub const Header = struct {
19
57
bytes : * const [512 ]u8 ,
20
58
21
59
pub const FileType = enum (u8 ) {
60
+ normal_alias = 0 ,
22
61
normal = '0' ,
23
62
hard_link = '1' ,
24
63
symbolic_link = '2' ,
@@ -65,13 +104,18 @@ pub const Header = struct {
65
104
return str (header , 0 , 0 + 100 );
66
105
}
67
106
107
+ pub fn linkName (header : Header ) []const u8 {
108
+ return str (header , 157 , 157 + 100 );
109
+ }
110
+
68
111
pub fn prefix (header : Header ) []const u8 {
69
112
return str (header , 345 , 345 + 155 );
70
113
}
71
114
72
115
pub fn fileType (header : Header ) FileType {
73
- const result = @as (FileType , @enumFromInt (header .bytes [156 ]));
74
- return if (result == @as (FileType , @enumFromInt (0 ))) .normal else result ;
116
+ const result : FileType = @enumFromInt (header .bytes [156 ]);
117
+ if (result == .normal_alias ) return .normal ;
118
+ return result ;
75
119
}
76
120
77
121
fn str (header : Header , start : usize , end : usize ) []const u8 {
@@ -148,7 +192,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
148
192
const header : Header = .{ .bytes = chunk [0.. 512] };
149
193
const file_size = try header .fileSize ();
150
194
const rounded_file_size = std .mem .alignForward (u64 , file_size , 512 );
151
- const pad_len = @as ( usize , @ intCast (rounded_file_size - file_size ) );
195
+ const pad_len : usize = @intCast (rounded_file_size - file_size );
152
196
const unstripped_file_name = if (file_name_override_len > 0 )
153
197
file_name_buffer [0.. file_name_override_len ]
154
198
else
@@ -175,7 +219,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
175
219
while (true ) {
176
220
const temp = try buffer .readChunk (reader , @intCast (rounded_file_size + 512 - file_off ));
177
221
if (temp .len == 0 ) return error .UnexpectedEndOfStream ;
178
- const slice = temp [0.. @as ( usize , @ intCast (@min (file_size - file_off , temp .len ) ))];
222
+ const slice = temp [0.. @intCast (@min (file_size - file_off , temp .len ))];
179
223
try file .writeAll (slice );
180
224
181
225
file_off += slice .len ;
@@ -228,8 +272,26 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
228
272
buffer .skip (reader , @intCast (rounded_file_size )) catch return error .TarHeadersTooBig ;
229
273
},
230
274
.hard_link = > return error .TarUnsupportedFileType ,
231
- .symbolic_link = > return error .TarUnsupportedFileType ,
232
- else = > return error .TarUnsupportedFileType ,
275
+ .symbolic_link = > {
276
+ const file_name = try stripComponents (unstripped_file_name , options .strip_components );
277
+ const link_name = header .linkName ();
278
+
279
+ dir .symLink (link_name , file_name , .{}) catch | err | {
280
+ const d = options .diagnostics orelse return error .UnableToCreateSymLink ;
281
+ try d .errors .append (d .allocator , .{ .unable_to_create_sym_link = .{
282
+ .code = err ,
283
+ .file_name = try d .allocator .dupe (u8 , file_name ),
284
+ .link_name = try d .allocator .dupe (u8 , link_name ),
285
+ } });
286
+ };
287
+ },
288
+ else = > | file_type | {
289
+ const d = options .diagnostics orelse return error .TarUnsupportedFileType ;
290
+ try d .errors .append (d .allocator , .{ .unsupported_file_type = .{
291
+ .file_name = try d .allocator .dupe (u8 , unstripped_file_name ),
292
+ .file_type = file_type ,
293
+ } });
294
+ },
233
295
}
234
296
}
235
297
}
0 commit comments