144301Swollman/*- 244301Swollman * Copyright (c) 2003-2007 Tim Kientzle 344301Swollman * All rights reserved. 444301Swollman * 544301Swollman * Redistribution and use in source and binary forms, with or without 644301Swollman * modification, are permitted provided that the following conditions 744301Swollman * are met: 844301Swollman * 1. Redistributions of source code must retain the above copyright 944301Swollman * notice, this list of conditions and the following disclaimer. 1044301Swollman * 2. Redistributions in binary form must reproduce the above copyright 1144301Swollman * notice, this list of conditions and the following disclaimer in the 1244301Swollman * documentation and/or other materials provided with the distribution. 1344301Swollman * 1444301Swollman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 1544301Swollman * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1644301Swollman * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1744301Swollman * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 1844301Swollman * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1944301Swollman * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2044301Swollman * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2144301Swollman * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2244301Swollman * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2344301Swollman * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2444301Swollman */ 2544301Swollman#include "test.h" 2644301Swollman__FBSDID("$FreeBSD$"); 2744301Swollman 2844301Swollman#include <errno.h> 2944301Swollman#include <stdlib.h> 3044301Swollman#include <string.h> 3144301Swollman 3244301Swollman/* 3344301Swollman * This is a somewhat tricky test that verifies the ability to 3444301Swollman * write and read very large entries to tar archives. It 3544301Swollman * writes entries from 2GB up to 1TB to an archive in memory. 3644301Swollman * The memory storage here carefully avoids actually storing 3744301Swollman * any part of the file bodies, so it runs very quickly and requires 3844301Swollman * very little memory. If you're willing to wait a few minutes, 3944301Swollman * you should be able to exercise petabyte entries with this code. 4044301Swollman */ 4144301Swollman 4244301Swollman/* 4344301Swollman * Each file is built up by duplicating the following block. 4444301Swollman */ 4544301Swollmanstatic size_t filedatasize; 4644301Swollmanstatic void *filedata; 4744301Swollman 4844301Swollman/* 4944301Swollman * We store the archive as blocks of data generated by libarchive, 5044301Swollman * each possibly followed by bytes of file data. 5144301Swollman */ 5244301Swollmanstruct memblock { 5344301Swollman struct memblock *next; 5444301Swollman size_t size; 5544301Swollman void *buff; 5644301Swollman int64_t filebytes; 5744301Swollman}; 5844301Swollman 5944301Swollman/* 6044301Swollman * The total memory store is just a list of memblocks plus 6144301Swollman * some accounting overhead. 6244301Swollman */ 6344301Swollmanstruct memdata { 6444301Swollman int64_t filebytes; 6544301Swollman void *buff; 6644301Swollman struct memblock *first; 6744301Swollman struct memblock *last; 6844301Swollman}; 6944301Swollman 7044301Swollman/* The following size definitions simplify things below. */ 7144301Swollman#define KB ((int64_t)1024) 7244301Swollman#define MB ((int64_t)1024 * KB) 7344301Swollman#define GB ((int64_t)1024 * MB) 7444301Swollman#define TB ((int64_t)1024 * GB) 7544301Swollman 7644301Swollmanstatic int64_t memory_read_skip(struct archive *, void *, int64_t request); 7744301Swollmanstatic ssize_t memory_read(struct archive *, void *, const void **buff); 7844301Swollmanstatic ssize_t memory_write(struct archive *, void *, const void *, size_t); 7944301Swollman 8044301Swollman 8144301Swollmanstatic ssize_t 8244301Swollmanmemory_write(struct archive *a, void *_private, const void *buff, size_t size) 8344301Swollman{ 8444301Swollman struct memdata *private = _private; 8544301Swollman struct memblock *block; 8644301Swollman 8744301Swollman (void)a; 8844301Swollman 8944301Swollman /* 9044301Swollman * Since libarchive tries to behave in a zero-copy manner, if 9144301Swollman * you give a pointer to filedata to the library, a pointer 9244301Swollman * into that data will (usually) pop out here. This way, we 9344301Swollman * can tell the difference between filedata and library header 9444301Swollman * and metadata. 9544301Swollman */ 9644301Swollman if ((const char *)filedata <= (const char *)buff 9744301Swollman && (const char *)buff < (const char *)filedata + filedatasize) { 9844301Swollman /* We don't need to store a block of file data. */ 9944301Swollman private->last->filebytes += (int64_t)size; 10044301Swollman } else { 10144301Swollman /* Yes, we're assuming the very first write is metadata. */ 10244301Swollman /* It's header or metadata, copy and save it. */ 10344301Swollman block = (struct memblock *)malloc(sizeof(*block)); 10444301Swollman memset(block, 0, sizeof(*block)); 10544301Swollman block->size = size; 10644301Swollman block->buff = malloc(size); 10744301Swollman memcpy(block->buff, buff, size); 10844301Swollman if (private->last == NULL) { 10944301Swollman private->first = private->last = block; 11044301Swollman } else { 11144301Swollman private->last->next = block; 11244301Swollman private->last = block; 11344301Swollman } 11444301Swollman block->next = NULL; 11544301Swollman } 11644301Swollman return ((long)size); 11744301Swollman} 11844301Swollman 11944301Swollmanstatic ssize_t 12044301Swollmanmemory_read(struct archive *a, void *_private, const void **buff) 12144301Swollman{ 12244301Swollman struct memdata *private = _private; 12344301Swollman struct memblock *block; 12444301Swollman ssize_t size; 12544301Swollman 12644301Swollman (void)a; 12744301Swollman 12844301Swollman free(private->buff); 12944301Swollman private->buff = NULL; 13044301Swollman if (private->first == NULL) { 13144301Swollman private->last = NULL; 13244301Swollman return (ARCHIVE_EOF); 13344301Swollman } 13444301Swollman if (private->filebytes > 0) { 13544301Swollman /* 13644301Swollman * We're returning file bytes, simulate it by 13744301Swollman * passing blocks from the template data. 13844301Swollman */ 13944301Swollman if (private->filebytes > (int64_t)filedatasize) 14044301Swollman size = (ssize_t)filedatasize; 14144301Swollman else 14244301Swollman size = (ssize_t)private->filebytes; 14344301Swollman private->filebytes -= size; 14444301Swollman *buff = filedata; 14544301Swollman } else { 14644301Swollman /* 14744301Swollman * We need to get some real data to return. 14844301Swollman */ 14944301Swollman block = private->first; 15044301Swollman private->first = block->next; 15144301Swollman size = (ssize_t)block->size; 15244301Swollman if (block->buff != NULL) { 15344301Swollman private->buff = block->buff; 15444301Swollman *buff = block->buff; 15544301Swollman } else { 15644301Swollman private->buff = NULL; 15744301Swollman *buff = filedata; 15844301Swollman } 15944301Swollman private->filebytes = block->filebytes; 16044301Swollman free(block); 16144301Swollman } 16244301Swollman return (size); 16344301Swollman} 16444301Swollman 16544301Swollman 16644301Swollmanstatic int64_t 16744301Swollmanmemory_read_skip(struct archive *a, void *_private, int64_t skip) 16844301Swollman{ 16944301Swollman struct memdata *private = _private; 17044301Swollman 17144301Swollman (void)a; 17244301Swollman 17344301Swollman if (private->first == NULL) { 17444301Swollman private->last = NULL; 17544301Swollman return (0); 17644301Swollman } 17744301Swollman if (private->filebytes > 0) { 17844301Swollman if (private->filebytes < skip) 17944301Swollman skip = (off_t)private->filebytes; 18044301Swollman private->filebytes -= skip; 18144301Swollman } else { 18244301Swollman skip = 0; 18344301Swollman } 18444301Swollman return (skip); 18544301Swollman} 18644301Swollman 18744301SwollmanDEFINE_TEST(test_tar_large) 18844301Swollman{ 18944301Swollman /* The sizes of the entries we're going to generate. */ 19044301Swollman static int64_t tests[] = { 19144301Swollman /* Test for 32-bit signed overflow. */ 19244301Swollman 2 * GB - 1, 2 * GB, 2 * GB + 1, 19344301Swollman /* Test for 32-bit unsigned overflow. */ 19444301Swollman 4 * GB - 1, 4 * GB, 4 * GB + 1, 19544301Swollman /* 8GB is the "official" max for ustar. */ 19644301Swollman 8 * GB - 1, 8 * GB, 8 * GB + 1, 19744301Swollman /* Bend ustar a tad and you can get 64GB (12 octal digits). */ 19844301Swollman 64 * GB - 1, 64 * GB, 19944301Swollman /* And larger entries that require non-ustar extensions. */ 20044301Swollman 256 * GB, 1 * TB, 0 }; 20144301Swollman int i; 20244301Swollman char namebuff[64]; 20344301Swollman struct memdata memdata; 20444301Swollman struct archive_entry *ae; 20544301Swollman struct archive *a; 20644301Swollman int64_t filesize; 20744301Swollman size_t writesize; 20844301Swollman 20944301Swollman filedatasize = (size_t)(1 * MB); 21044301Swollman filedata = malloc(filedatasize); 21144301Swollman memset(filedata, 0xAA, filedatasize); 21244301Swollman memset(&memdata, 0, sizeof(memdata)); 21344301Swollman 21444301Swollman /* 21544301Swollman * Open an archive for writing. 21644301Swollman */ 21744301Swollman a = archive_write_new(); 21844301Swollman archive_write_set_format_pax_restricted(a); 21944301Swollman archive_write_set_bytes_per_block(a, 0); /* No buffering. */ 22044301Swollman archive_write_open(a, &memdata, NULL, memory_write, NULL); 22144301Swollman 22244301Swollman /* 22344301Swollman * Write a series of large files to it. 22444301Swollman */ 22544301Swollman for (i = 0; tests[i] != 0; i++) { 22644301Swollman assert((ae = archive_entry_new()) != NULL); 22744301Swollman sprintf(namebuff, "file_%d", i); 22844301Swollman archive_entry_copy_pathname(ae, namebuff); 22944301Swollman archive_entry_set_mode(ae, S_IFREG | 0755); 23044301Swollman filesize = tests[i]; 23144301Swollman 23244301Swollman archive_entry_set_size(ae, filesize); 23344301Swollman 23444301Swollman assertA(0 == archive_write_header(a, ae)); 23544301Swollman archive_entry_free(ae); 23644301Swollman 23744301Swollman /* 23844301Swollman * Write the actual data to the archive. 23944301Swollman */ 24044301Swollman while (filesize > 0) { 24144301Swollman writesize = filedatasize; 24244301Swollman if ((int64_t)writesize > filesize) 24344301Swollman writesize = (size_t)filesize; 24444301Swollman assertA((int)writesize 24544301Swollman == archive_write_data(a, filedata, writesize)); 24644301Swollman filesize -= writesize; 24744301Swollman } 24844301Swollman } 24944301Swollman 25044301Swollman assert((ae = archive_entry_new()) != NULL); 25144301Swollman archive_entry_copy_pathname(ae, "lastfile"); 25244301Swollman archive_entry_set_mode(ae, S_IFREG | 0755); 25344301Swollman assertA(0 == archive_write_header(a, ae)); 25444301Swollman archive_entry_free(ae); 25544301Swollman 25644301Swollman 25744301Swollman /* Close out the archive. */ 25844301Swollman assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); 25944301Swollman assertEqualInt(ARCHIVE_OK, archive_write_free(a)); 26044301Swollman 26144301Swollman /* 26244301Swollman * Open the same archive for reading. 26344301Swollman */ 26444301Swollman a = archive_read_new(); 26544301Swollman archive_read_support_format_tar(a); 26644301Swollman archive_read_open2(a, &memdata, NULL, 26744301Swollman memory_read, memory_read_skip, NULL); 26844301Swollman 26944301Swollman /* 27044301Swollman * Read entries back. 27144301Swollman */ 27244301Swollman for (i = 0; tests[i] > 0; i++) { 27344301Swollman assertEqualIntA(a, 0, archive_read_next_header(a, &ae)); 27444301Swollman sprintf(namebuff, "file_%d", i); 27544301Swollman assertEqualString(namebuff, archive_entry_pathname(ae)); 27644301Swollman assert(tests[i] == archive_entry_size(ae)); 27744301Swollman } 27844301Swollman assertEqualIntA(a, 0, archive_read_next_header(a, &ae)); 27944301Swollman assertEqualString("lastfile", archive_entry_pathname(ae)); 28044301Swollman 28144301Swollman assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); 28244301Swollman 28344301Swollman /* Close out the archive. */ 28444301Swollman assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); 28544301Swollman assertEqualInt(ARCHIVE_OK, archive_read_free(a)); 28644301Swollman 28744301Swollman free(memdata.buff); 28844301Swollman free(filedata); 28944301Swollman} 29044301Swollman