| ... | @@ -3,8 +3,13 @@ pub const Options = struct { | ... | @@ -3,8 +3,13 @@ pub const Options = struct { |
| 3 | strip_components: u32 = 0, | 3 | strip_components: u32 = 0, |
| 4 | /// How to handle the "mode" property of files from within the tar file. | 4 | /// How to handle the "mode" property of files from within the tar file. |
| 5 | mode_mode: ModeMode = .executable_bit_only, | 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 | /// The mode from the tar file is completely ignored. Files are created | 13 | /// The mode from the tar file is completely ignored. Files are created |
| 9 | /// with the default mode when creating files. | 14 | /// with the default mode when creating files. |
| 10 | ignore, | 15 | ignore, |
| ... | @@ -13,6 +18,32 @@ pub const Options = struct { | ... | @@ -13,6 +18,32 @@ pub const Options = struct { |
| 13 | /// Other bits of the mode are left as the default when creating files. | 18 | /// Other bits of the mode are left as the default when creating files. |
| 14 | executable_bit_only, | 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 | }; |
| | 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 | }; |
| 16 | }; | 47 | }; |
| 17 | | 48 | |
| 18 | pub const Header = struct { | 49 | pub const Header = struct { |
| ... | @@ -65,6 +96,10 @@ pub const Header = struct { | ... | @@ -65,6 +96,10 @@ pub const Header = struct { |
| 65 | return str(header, 0, 0 + 100); | 96 | return str(header, 0, 0 + 100); |
| 66 | } | 97 | } |
| 67 | | 98 | |
| | 99 | pub fn linkName(header: Header) []const u8 { |
| | 100 | return str(header, 157, 157 + 100); |
| | 101 | } |
| | 102 | |
| 68 | pub fn prefix(header: Header) []const u8 { | 103 | pub fn prefix(header: Header) []const u8 { |
| 69 | return str(header, 345, 345 + 155); | 104 | return str(header, 345, 345 + 155); |
| 70 | } | 105 | } |
| ... | @@ -148,7 +183,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi | ... | @@ -148,7 +183,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi |
| 148 | const header: Header = .{ .bytes = chunk[0..512] }; | 183 | const header: Header = .{ .bytes = chunk[0..512] }; |
| 149 | const file_size = try header.fileSize(); | 184 | const file_size = try header.fileSize(); |
| 150 | const rounded_file_size = std.mem.alignForward(u64, file_size, 512); | 185 | 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); |
| 152 | const unstripped_file_name = if (file_name_override_len > 0) | 187 | const unstripped_file_name = if (file_name_override_len > 0) |
| 153 | file_name_buffer[0..file_name_override_len] | 188 | file_name_buffer[0..file_name_override_len] |
| 154 | else | 189 | else |
| ... | @@ -228,7 +263,22 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi | ... | @@ -228,7 +263,22 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi |
| 228 | buffer.skip(reader, @intCast(rounded_file_size)) catch return error.TarHeadersTooBig; | 263 | buffer.skip(reader, @intCast(rounded_file_size)) catch return error.TarHeadersTooBig; |
| 229 | }, | 264 | }, |
| 230 | .hard_link => return error.TarUnsupportedFileType, | 265 | .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 | }, |
| 232 | else => return error.TarUnsupportedFileType, | 282 | else => return error.TarUnsupportedFileType, |
| 233 | } | 283 | } |
| 234 | } | 284 | } |