| ... | ... | @@ -136,51 +136,90 @@ pub const Header = struct { |
| 136 | 136 | } |
| 137 | 137 | }; |
| 138 | 138 | |
| 139 | | const Buffer = struct { |
| 140 | | buffer: [512 * 8]u8 = undefined, |
| 141 | | start: usize = 0, |
| 142 | | end: usize = 0, |
| 139 | fn BufferedReader(comptime ReaderType: type) type { |
| 140 | return struct { |
| 141 | unbuffered_reader: ReaderType, |
| 142 | buffer: [512 * 8]u8 = undefined, |
| 143 | start: usize = 0, |
| 144 | end: usize = 0, |
| 143 | 145 | |
| 144 | | pub fn readChunk(b: *Buffer, reader: anytype, count: usize) ![]const u8 { |
| 145 | | b.ensureCapacity(1024); |
| 146 | const Self = @This(); |
| 146 | 147 | |
| 147 | | const ask = @min(b.buffer.len - b.end, count -| (b.end - b.start)); |
| 148 | | b.end += try reader.readAtLeast(b.buffer[b.end..], ask); |
| 148 | pub fn readChunk(self: *Self, count: usize) ![]const u8 { |
| 149 | self.ensureCapacity(1024); |
| 149 | 150 | |
| 150 | | return b.buffer[b.start..b.end]; |
| 151 | | } |
| 151 | const ask = @min(self.buffer.len - self.end, count -| (self.end - self.start)); |
| 152 | self.end += try self.unbuffered_reader.readAtLeast(self.buffer[self.end..], ask); |
| 152 | 153 | |
| 153 | | pub fn advance(b: *Buffer, count: usize) void { |
| 154 | | b.start += count; |
| 155 | | assert(b.start <= b.end); |
| 156 | | } |
| 154 | return self.buffer[self.start..self.end]; |
| 155 | } |
| 157 | 156 | |
| 158 | | pub fn skip(b: *Buffer, reader: anytype, count: usize) !void { |
| 159 | | if (b.start + count > b.end) { |
| 160 | | try reader.skipBytes(b.start + count - b.end, .{}); |
| 161 | | b.start = b.end; |
| 162 | | } else { |
| 163 | | b.advance(count); |
| 157 | pub fn advance(self: *Self, count: usize) void { |
| 158 | self.start += count; |
| 159 | assert(self.start <= self.end); |
| 160 | } |
| 161 | |
| 162 | pub fn skip(self: *Self, count: usize) !void { |
| 163 | if (self.start + count > self.end) { |
| 164 | try self.unbuffered_reader.skipBytes(self.start + count - self.end, .{}); |
| 165 | self.start = self.end; |
| 166 | } else { |
| 167 | self.advance(count); |
| 168 | } |
| 164 | 169 | } |
| 165 | | } |
| 166 | 170 | |
| 167 | | inline fn ensureCapacity(b: *Buffer, count: usize) void { |
| 168 | | if (b.buffer.len - b.start < count) { |
| 169 | | const dest_end = b.end - b.start; |
| 170 | | @memcpy(b.buffer[0..dest_end], b.buffer[b.start..b.end]); |
| 171 | | b.end = dest_end; |
| 172 | | b.start = 0; |
| 171 | inline fn ensureCapacity(self: *Self, count: usize) void { |
| 172 | if (self.buffer.len - self.start < count) { |
| 173 | const dest_end = self.end - self.start; |
| 174 | @memcpy(self.buffer[0..dest_end], self.buffer[self.start..self.end]); |
| 175 | self.end = dest_end; |
| 176 | self.start = 0; |
| 177 | } |
| 173 | 178 | } |
| 174 | | } |
| 175 | | }; |
| 179 | |
| 180 | pub fn write(self: *Self, writer: anytype, size: usize) !void { |
| 181 | const rounded_file_size = std.mem.alignForward(usize, size, 512); |
| 182 | const chunk_size = rounded_file_size + 512; |
| 183 | const pad_len: usize = rounded_file_size - size; |
| 184 | |
| 185 | var file_off: usize = 0; |
| 186 | while (true) { |
| 187 | const temp = try self.readChunk(chunk_size - file_off); |
| 188 | if (temp.len == 0) return error.UnexpectedEndOfStream; |
| 189 | const slice = temp[0..@min(size - file_off, temp.len)]; |
| 190 | try writer.writeAll(slice); |
| 191 | |
| 192 | file_off += slice.len; |
| 193 | self.advance(slice.len); |
| 194 | if (file_off >= size) { |
| 195 | self.advance(pad_len); |
| 196 | return; |
| 197 | } |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | pub fn copy(self: *Self, dst_buffer: []u8, size: usize) !void { |
| 202 | const rounded_file_size = std.mem.alignForward(usize, size, 512); |
| 203 | const chunk_size = rounded_file_size + 512; |
| 204 | |
| 205 | var i: usize = 0; |
| 206 | while (i < size) { |
| 207 | const slice = try self.readChunk(chunk_size - i); |
| 208 | if (slice.len == 0) return error.UnexpectedEndOfStream; |
| 209 | const copy_size: usize = @min(size - i, slice.len); |
| 210 | @memcpy(dst_buffer[i .. i + copy_size], slice[0..copy_size]); |
| 211 | self.advance(copy_size); |
| 212 | i += copy_size; |
| 213 | } |
| 214 | } |
| 215 | }; |
| 216 | } |
| 176 | 217 | |
| 177 | 218 | fn Iterator(comptime ReaderType: type) type { |
| 178 | 219 | return struct { |
| 179 | 220 | file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined, |
| 180 | 221 | file_name_len: usize = 0, |
| 181 | | buffer: Buffer = .{}, |
| 182 | | reader: ReaderType, |
| 183 | | pad_len: usize = 0, |
| 222 | reader: BufferedReader(ReaderType), |
| 184 | 223 | diagnostics: ?*Options.Diagnostics, |
| 185 | 224 | |
| 186 | 225 | const Self = @This(); |
| ... | ... | @@ -193,50 +232,32 @@ fn Iterator(comptime ReaderType: type) type { |
| 193 | 232 | iter: *Self, |
| 194 | 233 | |
| 195 | 234 | pub fn write(self: File, writer: anytype) !void { |
| 196 | | const rounded_file_size = std.mem.alignForward(u64, self.size, 512); |
| 197 | | var file_off: usize = 0; |
| 198 | | while (true) { |
| 199 | | const temp = try self.iter.buffer.readChunk(self.iter.reader, @intCast(rounded_file_size + 512 - file_off)); |
| 200 | | if (temp.len == 0) return error.UnexpectedEndOfStream; |
| 201 | | const slice = temp[0..@intCast(@min(self.size - file_off, temp.len))]; |
| 202 | | try writer.writeAll(slice); |
| 203 | | |
| 204 | | file_off += slice.len; |
| 205 | | self.iter.buffer.advance(slice.len); |
| 206 | | if (file_off >= self.size) { |
| 207 | | return; |
| 208 | | // self.iter.buffer.advance(pad_len); |
| 209 | | // continue :header; |
| 210 | | } |
| 211 | | } |
| 235 | try self.iter.reader.write(writer, self.size); |
| 212 | 236 | } |
| 213 | 237 | |
| 214 | | pub fn skip(self: File) void { |
| 215 | | _ = self; |
| 216 | | unreachable; |
| 238 | pub fn skip(self: File) !void { |
| 239 | const rounded_file_size = std.mem.alignForward(usize, self.size, 512); |
| 240 | try self.iter.reader.skip(rounded_file_size); |
| 217 | 241 | } |
| 218 | 242 | }; |
| 219 | 243 | |
| 220 | 244 | pub fn next(self: *Self) !?File { |
| 221 | | self.buffer.advance(self.pad_len); |
| 222 | | self.pad_len = 0; |
| 223 | 245 | self.file_name_len = 0; |
| 224 | | |
| 225 | 246 | while (true) { |
| 226 | | const chunk = try self.buffer.readChunk(self.reader, 1024); |
| 247 | const chunk = try self.reader.readChunk(1024); |
| 227 | 248 | switch (chunk.len) { |
| 228 | 249 | 0 => return null, |
| 229 | 250 | 1...511 => return error.UnexpectedEndOfStream, |
| 230 | 251 | else => {}, |
| 231 | 252 | } |
| 232 | | self.buffer.advance(512); |
| 253 | self.reader.advance(512); |
| 233 | 254 | |
| 234 | 255 | const header: Header = .{ .bytes = chunk[0..512] }; |
| 235 | 256 | const file_size = try header.fileSize(); |
| 236 | 257 | const file_type = header.fileType(); |
| 237 | 258 | const link_name = header.linkName(); |
| 238 | | const rounded_file_size = std.mem.alignForward(u64, file_size, 512); |
| 239 | | self.pad_len = @intCast(rounded_file_size - file_size); |
| 259 | const rounded_file_size: usize = std.mem.alignForward(usize, file_size, 512); |
| 260 | |
| 240 | 261 | const file_name = if (self.file_name_len == 0) |
| 241 | 262 | try header.fullFileName(&self.file_name_buffer) |
| 242 | 263 | else |
| ... | ... | @@ -253,44 +274,33 @@ fn Iterator(comptime ReaderType: type) type { |
| 253 | 274 | }; |
| 254 | 275 | }, |
| 255 | 276 | .global_extended_header => { |
| 256 | | self.buffer.skip(self.reader, @intCast(rounded_file_size)) catch return error.TarHeadersTooBig; |
| 277 | self.reader.skip(rounded_file_size) catch return error.TarHeadersTooBig; |
| 257 | 278 | }, |
| 258 | 279 | .extended_header => { |
| 259 | | if (file_size == 0) { |
| 260 | | self.buffer.advance(@intCast(rounded_file_size)); |
| 261 | | continue; |
| 262 | | } |
| 280 | if (file_size == 0) continue; |
| 263 | 281 | |
| 264 | | const chunk_size: usize = @intCast(rounded_file_size + 512); |
| 282 | const chunk_size: usize = rounded_file_size + 512; |
| 265 | 283 | var data_off: usize = 0; |
| 266 | 284 | const file_name_override_len = while (data_off < file_size) { |
| 267 | | const slice = try self.buffer.readChunk(self.reader, chunk_size - data_off); |
| 285 | const slice = try self.reader.readChunk(chunk_size - data_off); |
| 268 | 286 | if (slice.len == 0) return error.UnexpectedEndOfStream; |
| 269 | | const remaining_size: usize = @intCast(file_size - data_off); |
| 287 | const remaining_size: usize = file_size - data_off; |
| 270 | 288 | const attr_info = try parsePaxAttribute(slice[0..@min(remaining_size, slice.len)], remaining_size); |
| 271 | 289 | |
| 272 | 290 | if (std.mem.eql(u8, attr_info.key, "path")) { |
| 273 | 291 | if (attr_info.value_len > self.file_name_buffer.len) return error.NameTooLong; |
| 274 | | self.buffer.advance(attr_info.value_off); |
| 292 | self.reader.advance(attr_info.value_off); |
| 275 | 293 | data_off += attr_info.value_off; |
| 276 | 294 | break attr_info.value_len; |
| 277 | 295 | } |
| 278 | 296 | |
| 279 | | try self.buffer.skip(self.reader, attr_info.size); |
| 297 | try self.reader.skip(attr_info.size); |
| 280 | 298 | data_off += attr_info.size; |
| 281 | 299 | } else 0; |
| 282 | 300 | |
| 283 | | var i: usize = 0; |
| 284 | | while (i < file_name_override_len) { |
| 285 | | const slice = try self.buffer.readChunk(self.reader, chunk_size - data_off - i); |
| 286 | | if (slice.len == 0) return error.UnexpectedEndOfStream; |
| 287 | | const copy_size: usize = @intCast(@min(file_name_override_len - i, slice.len)); |
| 288 | | @memcpy(self.file_name_buffer[i .. i + copy_size], slice[0..copy_size]); |
| 289 | | self.buffer.advance(copy_size); |
| 290 | | i += copy_size; |
| 291 | | } |
| 301 | try self.reader.copy(&self.file_name_buffer, file_name_override_len); |
| 292 | 302 | |
| 293 | | try self.buffer.skip(self.reader, @intCast(rounded_file_size - data_off - file_name_override_len)); |
| 303 | try self.reader.skip(rounded_file_size - data_off - file_name_override_len); |
| 294 | 304 | self.file_name_len = file_name_override_len; |
| 295 | 305 | continue; |
| 296 | 306 | }, |
| ... | ... | @@ -309,7 +319,11 @@ fn Iterator(comptime ReaderType: type) type { |
| 309 | 319 | } |
| 310 | 320 | |
| 311 | 321 | pub fn iterator(reader: anytype, diagnostics: ?*Options.Diagnostics) Iterator(@TypeOf(reader)) { |
| 312 | | return .{ .reader = reader, .diagnostics = diagnostics }; |
| 322 | const ReaderType = @TypeOf(reader); |
| 323 | return .{ |
| 324 | .reader = BufferedReader(ReaderType){ .unbuffered_reader = reader }, |
| 325 | .diagnostics = diagnostics, |
| 326 | }; |
| 313 | 327 | } |
| 314 | 328 | |
| 315 | 329 | pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void { |
| ... | ... | @@ -364,7 +378,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi |
| 364 | 378 | if (file) |f| { |
| 365 | 379 | try iter_file.write(f); |
| 366 | 380 | } else { |
| 367 | | iter_file.skip(); |
| 381 | try iter_file.skip(); |
| 368 | 382 | } |
| 369 | 383 | }, |
| 370 | 384 | .symbolic_link => { |