| 198 | |
| 199 | private: |
| 200 | void BuildIndex() |
| 201 | { |
| 202 | struct archive *a = archive_read_new(); |
| 203 | archive_read_support_format_tar(a); |
| 204 | archive_read_support_format_gnutar(a); |
| 205 | |
| 206 | int ret = archive_read_open_filename(a, path.string().c_str(), 10240); |
| 207 | if (ret != ARCHIVE_OK) |
| 208 | { |
| 209 | const char *err = archive_error_string(a); |
| 210 | std::string errmsg = err ? err : "unknown error"; |
| 211 | archive_read_free(a); |
| 212 | throw util::RuntimeError( |
| 213 | path.string(), ErrorCode::FileOpenError, SOURCE_REF, errmsg.c_str()); |
| 214 | } |
| 215 | |
| 216 | struct archive_entry *ae; |
| 217 | |
| 218 | // Collect header positions for all entries in order. |
| 219 | // archive_read_header_position returns the position of the first header |
| 220 | // block for an entry. For pax entries with extended attributes (e.g. entries |
| 221 | // > 8GB), this points to the pax extension header, not the actual file header, |
| 222 | // so the naive "header_position + 512" calculation is wrong for such entries. |
| 223 | // |
| 224 | // Instead, we compute data offsets from consecutive header positions: |
| 225 | // each entry's padded data ends exactly where the next entry's header begins. |
| 226 | // data_offset[i] = header_position[i+1] - ceil_to_512(size[i]) |
| 227 | struct RawEntry |
| 228 | { |
| 229 | std::string name; |
| 230 | std::size_t size; |
| 231 | std::int64_t header_pos; |
| 232 | bool is_regular; |
| 233 | }; |
| 234 | std::vector<RawEntry> all_entries; |
| 235 | |
| 236 | while (archive_read_next_header(a, &ae) == ARCHIVE_OK) |
| 237 | { |
| 238 | std::int64_t pos = archive_read_header_position(a); |
| 239 | bool is_reg = (archive_entry_filetype(ae) == AE_IFREG); |
| 240 | std::string name; |
| 241 | std::size_t size = 0; |
| 242 | if (is_reg) |
| 243 | { |
| 244 | name = archive_entry_pathname(ae); |
| 245 | size = static_cast<std::size_t>(archive_entry_size(ae)); |
| 246 | } |
| 247 | all_entries.push_back({std::move(name), size, pos, is_reg}); |
| 248 | archive_read_data_skip(a); |
| 249 | } |
| 250 | |
| 251 | // After ARCHIVE_EOF, header_position points to the end-of-archive marker |
| 252 | auto end_of_archive_pos = archive_read_header_position(a); |
| 253 | |
| 254 | archive_read_free(a); |
| 255 | |
| 256 | for (std::size_t i = 0; i < all_entries.size(); ++i) |
| 257 | { |
nothing calls this directly
no test coverage detected