test_short_writes.c revision 370535
1370535Sgit2svn/*- 2370535Sgit2svn * Copyright (c) 2021 Red Hat, Inc. 3370535Sgit2svn * All rights reserved. 4370535Sgit2svn * 5370535Sgit2svn * Redistribution and use in source and binary forms, with or without 6370535Sgit2svn * modification, are permitted provided that the following conditions 7370535Sgit2svn * are met: 8370535Sgit2svn * 1. Redistributions of source code must retain the above copyright 9370535Sgit2svn * notice, this list of conditions and the following disclaimer. 10370535Sgit2svn * 2. Redistributions in binary form must reproduce the above copyright 11370535Sgit2svn * notice, this list of conditions and the following disclaimer in the 12370535Sgit2svn * documentation and/or other materials provided with the distribution. 13370535Sgit2svn * 14370535Sgit2svn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15370535Sgit2svn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16370535Sgit2svn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17370535Sgit2svn * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18370535Sgit2svn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19370535Sgit2svn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20370535Sgit2svn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21370535Sgit2svn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22370535Sgit2svn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23370535Sgit2svn * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24370535Sgit2svn */ 25370535Sgit2svn#include "test.h" 26370535Sgit2svn 27370535Sgit2svn#include <errno.h> 28370535Sgit2svn#include <stdlib.h> 29370535Sgit2svn#include <string.h> 30370535Sgit2svn 31370535Sgit2svn/* 32370535Sgit2svn * This test checks whether things work correctly when the archive_write_callback 33370535Sgit2svn * passed to archive_write_open() does a short write and only writes some of the 34370535Sgit2svn * data passed in. The way the test works is that two archives are constructed 35370535Sgit2svn * in parallel - one with short writes, one with full writes - and the results 36370535Sgit2svn * are compared to see if they are identical. 37370535Sgit2svn */ 38370535Sgit2svn 39370535Sgit2svnstruct checker { 40370535Sgit2svn struct archive *short_archive; 41370535Sgit2svn char *shortbuf; 42370535Sgit2svn size_t shortbuf_len; 43370535Sgit2svn 44370535Sgit2svn struct archive *full_archive; 45370535Sgit2svn char *fullbuf; 46370535Sgit2svn size_t fullbuf_len; 47370535Sgit2svn}; 48370535Sgit2svn 49370535Sgit2svnstatic ssize_t 50370535Sgit2svnshort_write_callback(struct archive *a, void *client_data, const void *buffer, size_t length) 51370535Sgit2svn{ 52370535Sgit2svn (void)a; 53370535Sgit2svn 54370535Sgit2svn struct checker *checker = client_data; 55370535Sgit2svn size_t to_write = length < 100 ? length : 100; 56370535Sgit2svn size_t new_len = checker->shortbuf_len + to_write; 57370535Sgit2svn char *new_buf = realloc(checker->shortbuf, new_len); 58370535Sgit2svn assert(new_buf != NULL); 59370535Sgit2svn 60370535Sgit2svn checker->shortbuf = new_buf; 61370535Sgit2svn memcpy(checker->shortbuf + checker->shortbuf_len, buffer, to_write); 62370535Sgit2svn checker->shortbuf_len = new_len; 63370535Sgit2svn 64370535Sgit2svn return to_write; 65370535Sgit2svn} 66370535Sgit2svn 67370535Sgit2svnstatic ssize_t 68370535Sgit2svnfull_write_callback(struct archive *a, void *client_data, const void *buffer, size_t length) 69370535Sgit2svn{ 70370535Sgit2svn (void)a; 71370535Sgit2svn 72370535Sgit2svn struct checker *checker = client_data; 73370535Sgit2svn size_t to_write = length; 74370535Sgit2svn size_t new_len = checker->fullbuf_len + to_write; 75370535Sgit2svn char *new_buf = realloc(checker->fullbuf, new_len); 76370535Sgit2svn assert(new_buf != NULL); 77370535Sgit2svn 78370535Sgit2svn checker->fullbuf = new_buf; 79370535Sgit2svn memcpy(checker->fullbuf + checker->fullbuf_len, buffer, to_write); 80370535Sgit2svn checker->fullbuf_len = new_len; 81370535Sgit2svn 82370535Sgit2svn return to_write; 83370535Sgit2svn} 84370535Sgit2svn 85370535Sgit2svnstatic struct archive * 86370535Sgit2svncreate_archive(struct checker *checker, archive_write_callback write_cb, int buffered) 87370535Sgit2svn{ 88370535Sgit2svn struct archive *a; 89370535Sgit2svn 90370535Sgit2svn assert((a = archive_write_new()) != NULL); 91370535Sgit2svn 92370535Sgit2svn if (!buffered) 93370535Sgit2svn assertEqualIntA(a, ARCHIVE_OK, 94370535Sgit2svn archive_write_set_bytes_per_block(a, 0)); 95370535Sgit2svn 96370535Sgit2svn /* With the default value of bytes_in_last_block, the writing code will 97370535Sgit2svn * pad out the final write to make it a full block. This causes problems 98370535Sgit2svn * for us because the size of the final write can be different depending 99370535Sgit2svn * on the size of previous writes, causing the "short" and "full" paths 100370535Sgit2svn * to get different amounts of padding. Setting it to 1 results in no 101370535Sgit2svn * padding other than that defined by the archive format. */ 102370535Sgit2svn assertEqualIntA(a, ARCHIVE_OK, 103370535Sgit2svn archive_write_set_bytes_in_last_block(a, 1)); 104370535Sgit2svn 105370535Sgit2svn /* We write a pax archive, but other formats would work fine too. */ 106370535Sgit2svn assertEqualIntA(a, ARCHIVE_OK, 107370535Sgit2svn archive_write_set_format_pax(a)); 108370535Sgit2svn assertEqualIntA(a, ARCHIVE_OK, 109370535Sgit2svn archive_write_add_filter_none(a)); 110370535Sgit2svn 111370535Sgit2svn assertEqualIntA(a, ARCHIVE_OK, 112370535Sgit2svn archive_write_open(a, checker, NULL, write_cb, NULL)); 113370535Sgit2svn 114370535Sgit2svn return a; 115370535Sgit2svn} 116370535Sgit2svn 117370535Sgit2svnstatic struct checker * 118370535Sgit2svnchecker_new(int buffered) 119370535Sgit2svn{ 120370535Sgit2svn struct checker *checker; 121370535Sgit2svn 122370535Sgit2svn assert ((checker = calloc(1, sizeof *checker)) != NULL); 123370535Sgit2svn 124370535Sgit2svn checker->short_archive = create_archive(checker, short_write_callback, buffered); 125370535Sgit2svn checker->full_archive = create_archive(checker, full_write_callback, buffered); 126370535Sgit2svn 127370535Sgit2svn return checker; 128370535Sgit2svn} 129370535Sgit2svn 130370535Sgit2svnstatic void 131370535Sgit2svnchecker_add_file(struct checker *checker, const char *name, char *buffer, size_t len) 132370535Sgit2svn{ 133370535Sgit2svn struct archive_entry *entry; 134370535Sgit2svn assert((entry = archive_entry_new()) != NULL); 135370535Sgit2svn 136370535Sgit2svn archive_entry_set_pathname(entry, name); 137370535Sgit2svn archive_entry_set_mode(entry, AE_IFREG | 0755); 138370535Sgit2svn archive_entry_set_size(entry, len); 139370535Sgit2svn 140370535Sgit2svn assertEqualIntA(checker->short_archive, ARCHIVE_OK, 141370535Sgit2svn archive_write_header(checker->short_archive, entry)); 142370535Sgit2svn assertEqualIntA(checker->short_archive, len, 143370535Sgit2svn archive_write_data(checker->short_archive, buffer, len)); 144370535Sgit2svn 145370535Sgit2svn assertEqualIntA(checker->full_archive, ARCHIVE_OK, 146370535Sgit2svn archive_write_header(checker->full_archive, entry)); 147370535Sgit2svn assertEqualIntA(checker->full_archive, len, 148370535Sgit2svn archive_write_data(checker->full_archive, buffer, len)); 149370535Sgit2svn 150370535Sgit2svn archive_entry_free(entry); 151370535Sgit2svn} 152370535Sgit2svn 153370535Sgit2svnstatic void 154370535Sgit2svnchecker_close(struct checker *checker) 155370535Sgit2svn{ 156370535Sgit2svn assertEqualIntA(checker->short_archive, ARCHIVE_OK, 157370535Sgit2svn archive_write_close(checker->short_archive)); 158370535Sgit2svn assertEqualIntA(checker->short_archive, ARCHIVE_OK, 159370535Sgit2svn archive_write_close(checker->full_archive)); 160370535Sgit2svn} 161370535Sgit2svn 162370535Sgit2svnstatic void 163370535Sgit2svnchecker_check(struct checker *checker) 164370535Sgit2svn{ 165370535Sgit2svn assertEqualInt(checker->shortbuf_len, checker->fullbuf_len); 166370535Sgit2svn assert(memcmp(checker->shortbuf, checker->fullbuf, checker->fullbuf_len) == 0); 167370535Sgit2svn} 168370535Sgit2svn 169370535Sgit2svnstatic void 170370535Sgit2svnchecker_free(struct checker *checker) 171370535Sgit2svn{ 172370535Sgit2svn free(checker->shortbuf); 173370535Sgit2svn free(checker->fullbuf); 174370535Sgit2svn free(checker); 175370535Sgit2svn} 176370535Sgit2svn 177370535Sgit2svnDEFINE_TEST(test_short_writes) 178370535Sgit2svn{ 179370535Sgit2svn struct checker *checker; 180370535Sgit2svn uint16_t test_data[16384]; 181370535Sgit2svn int i; 182370535Sgit2svn 183370535Sgit2svn for (i = 0; i < 16384; i++) 184370535Sgit2svn test_data[i] = i; 185370535Sgit2svn 186370535Sgit2svn 187370535Sgit2svn /* Write a file smaller than the default buffer size (10 * 1024); 188370535Sgit2svn * this will be written out at close. 189370535Sgit2svn */ 190370535Sgit2svn checker = checker_new(1); 191370535Sgit2svn checker_add_file(checker, "a", (char *)test_data, 1024); 192370535Sgit2svn checker_close(checker); 193370535Sgit2svn assert(checker->shortbuf_len > 1024); 194370535Sgit2svn checker_check(checker); 195370535Sgit2svn checker_free(checker); 196370535Sgit2svn 197370535Sgit2svn /* Write a file larger larger than twice default buffer size (10 * 1024); 198370535Sgit2svn * this both fills the buffer and writes it out, and also exercises 199370535Sgit2svn * the "write out full blocks directly" code path. 200370535Sgit2svn */ 201370535Sgit2svn checker = checker_new(1); 202370535Sgit2svn checker_add_file(checker, "a", (char *)test_data, 21 * 1024); 203370535Sgit2svn checker_close(checker); 204370535Sgit2svn assert(checker->shortbuf_len > 21 * 1024); 205370535Sgit2svn checker_check(checker); 206370535Sgit2svn checker_free(checker); 207370535Sgit2svn 208370535Sgit2svn /* Test unbuffered writes - a different code path. 209370535Sgit2svn */ 210370535Sgit2svn checker = checker_new(0); 211370535Sgit2svn checker_add_file(checker, "a", (char *)test_data, 1024); 212370535Sgit2svn checker_close(checker); 213370535Sgit2svn assert(checker->shortbuf_len > 1024); 214370535Sgit2svn checker_check(checker); 215370535Sgit2svn checker_free(checker); 216370535Sgit2svn} 217