/**
 * 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);
}
Exemple #2
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);
}