DLSYM_PROTOTYPE(archive_entry_set_size) = NULL;
DLSYM_PROTOTYPE(archive_entry_set_symlink) = NULL;
DLSYM_PROTOTYPE(archive_entry_set_uid) = NULL;
+DLSYM_PROTOTYPE(archive_entry_sparse_add_entry) = NULL;
DLSYM_PROTOTYPE(archive_entry_symlink) = NULL;
DLSYM_PROTOTYPE(archive_entry_uid) = NULL;
#if HAVE_LIBARCHIVE_UID_IS_SET
DLSYM_PROTOTYPE(archive_write_open_FILE) = NULL;
DLSYM_PROTOTYPE(archive_write_open_fd) = NULL;
DLSYM_PROTOTYPE(archive_write_set_format_filter_by_ext) = NULL;
-DLSYM_PROTOTYPE(archive_write_set_format_gnutar) = NULL;
+DLSYM_PROTOTYPE(archive_write_set_format_pax) = NULL;
int dlopen_libarchive(void) {
ELF_NOTE_DLOPEN("archive",
DLSYM_ARG(archive_entry_set_size),
DLSYM_ARG(archive_entry_set_symlink),
DLSYM_ARG(archive_entry_set_uid),
+ DLSYM_ARG(archive_entry_sparse_add_entry),
DLSYM_ARG(archive_entry_symlink),
DLSYM_ARG(archive_entry_uid),
#if HAVE_LIBARCHIVE_UID_IS_SET
DLSYM_ARG(archive_write_open_FILE),
DLSYM_ARG(archive_write_open_fd),
DLSYM_ARG(archive_write_set_format_filter_by_ext),
- DLSYM_ARG(archive_write_set_format_gnutar)
- );
+ DLSYM_ARG(archive_write_set_format_pax));
}
/* libarchive uses its own file type macros. They happen to be defined the same way as the Linux ones, and
extern DLSYM_PROTOTYPE(archive_entry_set_size);
extern DLSYM_PROTOTYPE(archive_entry_set_symlink);
extern DLSYM_PROTOTYPE(archive_entry_set_uid);
+extern DLSYM_PROTOTYPE(archive_entry_sparse_add_entry);
extern DLSYM_PROTOTYPE(archive_entry_symlink);
extern DLSYM_PROTOTYPE(archive_entry_uid);
extern DLSYM_PROTOTYPE(archive_entry_xattr_add_entry);
extern DLSYM_PROTOTYPE(archive_write_open_FILE);
extern DLSYM_PROTOTYPE(archive_write_open_fd);
extern DLSYM_PROTOTYPE(archive_write_set_format_filter_by_ext);
-extern DLSYM_PROTOTYPE(archive_write_set_format_gnutar);
+extern DLSYM_PROTOTYPE(archive_write_set_format_pax);
#if HAVE_LIBARCHIVE_UID_IS_SET
extern DLSYM_PROTOTYPE(archive_entry_gid_is_set);
return 0;
}
+static int archive_generate_sparse(struct archive_entry *entry, int fd) {
+ assert(entry);
+ assert(fd);
+
+ off_t c = 0;
+ for (;;) {
+ /* Look for the next hole */
+ off_t h = lseek(fd, c, SEEK_HOLE);
+ if (h < 0) {
+ if (errno != ENXIO)
+ return log_error_errno(errno, "Failed to issue SEEK_HOLE: %m");
+
+ /* If errno == ENXIO, that means we've reached the final data of the file and
+ * that data isn't followed by anything more */
+
+ /* Figure out where the end of the file is */
+ off_t e = lseek(fd, 0, SEEK_END);
+ if (e < 0)
+ return log_error_errno(errno, "Failed to issue SEEK_END: %m");
+
+ /* Generate sparse entry for final block */
+ if (e > c && c != 0) {
+ log_debug("final sparse block %" PRIu64 "…%" PRIu64, (uint64_t) c, (uint64_t) e);
+ sym_archive_entry_sparse_add_entry(entry, c, e - c);
+ }
+
+ break;
+ }
+
+ if (h > c) {
+ log_debug("inner sparse block %" PRIu64 "…%" PRIu64 " (%" PRIu64 ")", (uint64_t) c, (uint64_t) h, (uint64_t) h - (uint64_t) c);
+ sym_archive_entry_sparse_add_entry(entry, c, h - c);
+ }
+
+ /* Now look for the next data after the hole */
+ c = lseek(fd, h, SEEK_DATA);
+ if (c < 0) {
+ if (errno != ENXIO)
+ return log_error_errno(errno, "Failed to issue SEEK_DATA: %m");
+
+ /* No data anymore */
+ break;
+ }
+ }
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return log_error_errno(errno, "Failed to reset seek offset: %m");
+
+ return 0;
+}
+
static int archive_item(
RecurseDirEvent event,
const char *path,
sym_archive_entry_xattr_add_entry(entry, xa, buf, size);
}
- if (sym_archive_write_header(d->archive, entry) != ARCHIVE_OK)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive entry header: %s", sym_archive_error_string(d->archive));
-
+ _cleanup_close_ int data_fd = -EBADF;
if (S_ISREG(sx->stx_mode)) {
- _cleanup_close_ int data_fd = -EBADF;
-
- /* Convert the O_PATH fd in a proper fd */
+ /* Convert the O_PATH fd into a proper fd */
data_fd = fd_reopen(inode_fd, O_RDONLY|O_CLOEXEC);
if (data_fd < 0)
return log_error_errno(data_fd, "Failed to open '%s': %m", path);
+ r = archive_generate_sparse(entry, data_fd);
+ if (r < 0)
+ return r;
+ }
+
+ if (sym_archive_write_header(d->archive, entry) != ARCHIVE_OK)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to write archive entry header: %s", sym_archive_error_string(d->archive));
+
+ if (S_ISREG(sx->stx_mode)) {
+ assert(data_fd >= 0);
+
for (;;) {
char buffer[64*1024];
ssize_t l;
if (filename)
r = sym_archive_write_set_format_filter_by_ext(a, filename);
else
- r = sym_archive_write_set_format_gnutar(a);
+ r = sym_archive_write_set_format_pax(a);
if (r != ARCHIVE_OK)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to set libarchive output format: %s", sym_archive_error_string(a));