/** * archive_multitape_copy(ina, read_cookie, a, write_cookie) * Copy the data for an entry from one archive to another. */ int archive_multitape_copy(struct archive * ina, void * read_cookie, struct archive * a, void * write_cookie) { char buff[64*1024]; struct chunkheader * ch; ssize_t lenread; ssize_t writelen; off_t entrylen; ssize_t backloglen; /* Compute the entry size. */ if ((entrylen = archive_read_get_entryleft(ina)) < 0) { archive_set_error(ina, ENOSYS, "read_get_entryleft not supported"); return (-2); } /* Copy data. */ while (entrylen > 0) { /* Is there data buffered by libarchive? */ if ((backloglen = archive_read_get_backlog(ina)) < 0) { warn0("Error reading libarchive data backlog"); return (-2); } if (backloglen > 0) { /* Drain some data from libarchive. */ if ((size_t)backloglen > sizeof(buff)) lenread = sizeof(buff); else lenread = backloglen; lenread = archive_read_data(ina, buff, lenread); if (lenread == 0) { warn0("libarchive claims data backlog," " but no data can be read?"); return (-2); } if (lenread < 0) return (-2); /* Write it out to the new archive. */ writelen = archive_write_data(a, buff, lenread); if (writelen < lenread) return (-1); /* Adjust the remaining entry length and continue. */ entrylen -= lenread; continue; } /* Attempt to read a chunk for fast-pathing. */ lenread = readtape_readchunk(read_cookie, &ch); if (lenread < 0) return (-2); if (lenread > entrylen) { warn0("readchunk returned chunk beyond end" " of archive entry?"); return (-2); } if (lenread == 0) goto nochunk; /* Attempt to write the chunk via the fast path. */ writelen = writetape_writechunk(write_cookie, ch); if (writelen < 0) return (-1); if (writelen == 0) goto nochunk; if (writelen != lenread) { warn0("chunk write size != chunk read size?"); return (-1); } /* * Advance libarchive pointers. Do the write pointer * first since a failure there is fatal. */ if (archive_write_skip(a, writelen)) return (-1); if (archive_read_advance(ina, lenread)) return (-2); /* We don't need to see this chunk again. */ if (readtape_skip(read_cookie, lenread) != lenread) { warn0("could not skip read data?"); return (-2); } /* We've done part of the entry. */ entrylen -= lenread; continue; nochunk: /* * We have no data buffered in libarchive, and we can't copy * an intact chunk. We need to read some data, but we have * no idea how much the multitape layer wants to provide to * libarchive next; and we don't want to read too much data * since we might waste time reading and writing chunked data * which could be fast-pathed. Simple solution: Read and * write one byte. Libarchive will almost certainly get more * than one byte from the multitape layer, but when we return * to the start of this loop and handle backlogged data we * will pick up the rest of the data. (Also, this is always * where we end up when we hit the end of an archive entry, * in which case archive_read_data returns 0 and we exit the * loop.) */ lenread = archive_read_data(ina, buff, 1); if (lenread == 0) break; if (lenread < 0) return (-2); writelen = archive_write_data(a, buff, 1); if (writelen < 1) return (-1); }; return (0); }
/* * Backend for write_entry. */ static void write_entry_backend(struct bsdtar *bsdtar, struct archive *a, struct archive_entry *entry, const struct stat *st, const char *rpath) { off_t skiplen; CCACHE_ENTRY *cce = NULL; int filecached = 0; int fd = -1; int e; /* * If this archive entry needs data, we have a canonical path to the * relevant file, and the chunkification cache isn't disabled, ask * the chunkification cache to find the entry for the file (if one * already exists) and tell us if it can provide the entire file. */ if ((st != NULL) && S_ISREG(st->st_mode) && (rpath != NULL) && (archive_entry_size(entry) > 0) && (bsdtar->cachecrunch < 2) && (bsdtar->chunk_cache != NULL)) { cce = ccache_entry_lookup(bsdtar->chunk_cache, rpath, st, bsdtar->write_cookie, &filecached); } /* * Open the file if we need to write archive entry data and the * chunkification cache can't provide all of it. */ if ((archive_entry_size(entry) > 0) && (filecached == 0)) { const char *pathname = archive_entry_sourcepath(entry); fd = open(pathname, O_RDONLY); if (fd == -1) { if (!bsdtar->verbose) bsdtar_warnc(bsdtar, errno, "%s: could not open file", pathname); else fprintf(stderr, ": %s", strerror(errno)); return; } } /* Write the archive header. */ if (MODE_HEADER(bsdtar, a)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); exit(1); } e = archive_write_header(a, entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) bsdtar_warnc(bsdtar, 0, "%s: %s", archive_entry_pathname(entry), archive_error_string(a)); else fprintf(stderr, ": %s", archive_error_string(a)); } if (e == ARCHIVE_FATAL) exit(1); /* * If we opened a file earlier, write it out now. Note that * the format handler might have reset the size field to zero * to inform us that the archive body won't get stored. In * that case, just skip the write. */ /* If the cache can provide the entire archive entry, do it. */ if (e >= ARCHIVE_WARN && filecached && archive_entry_size(entry) > 0) { if (MODE_DATA(bsdtar, a)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); exit(1); } skiplen = ccache_entry_write(cce, bsdtar->write_cookie); if (skiplen < st->st_size) { bsdtar_warnc(bsdtar, 0, "Error writing cached archive entry"); exit(1); } if (archive_write_skip(a, skiplen)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); exit(1); } } /* * We don't need to write anything now if the file was cached * and the cache wrote it out earlier. */ if (e >= ARCHIVE_WARN && fd >= 0 && archive_entry_size(entry) > 0 && filecached == 0) { /* Switch into data mode. */ if (MODE_DATA(bsdtar, a)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); exit(1); } if (cce != NULL) { /* Ask the cache to write as much as possible. */ skiplen = ccache_entry_writefile(cce, bsdtar->write_cookie, bsdtar->cachecrunch, fd); if (skiplen < 0) { bsdtar_warnc(bsdtar, 0, "Error writing archive"); exit(1); } /* Skip forward in the file. */ if (lseek(fd, skiplen, SEEK_SET) == -1) { bsdtar_warnc(bsdtar, errno, "lseek(%s)", archive_entry_pathname(entry)); exit(1); } /* Tell the archive layer that we've skipped. */ if (archive_write_skip(a, skiplen)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); exit(1); } } if (write_file_data(bsdtar, a, entry, fd)) exit(1); } /* This entry is done. */ if (!truncate_archive(bsdtar) && MODE_DONE(bsdtar, a)) { bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); exit(1); } /* Tell the cache that we're done. */ if (cce != NULL) { if (ccache_entry_end(bsdtar->chunk_cache, cce, bsdtar->write_cookie, rpath, bsdtar->snaptime)) exit(1); cce = NULL; } /* * If we opened a file, close it now even if there was an error * which made us decide not to write the archive body. */ if (fd >= 0) close(fd); }