test_short_writes.c revision 370535
1/*- 2 * Copyright (c) 2021 Red Hat, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25#include "test.h" 26 27#include <errno.h> 28#include <stdlib.h> 29#include <string.h> 30 31/* 32 * This test checks whether things work correctly when the archive_write_callback 33 * passed to archive_write_open() does a short write and only writes some of the 34 * data passed in. The way the test works is that two archives are constructed 35 * in parallel - one with short writes, one with full writes - and the results 36 * are compared to see if they are identical. 37 */ 38 39struct checker { 40 struct archive *short_archive; 41 char *shortbuf; 42 size_t shortbuf_len; 43 44 struct archive *full_archive; 45 char *fullbuf; 46 size_t fullbuf_len; 47}; 48 49static ssize_t 50short_write_callback(struct archive *a, void *client_data, const void *buffer, size_t length) 51{ 52 (void)a; 53 54 struct checker *checker = client_data; 55 size_t to_write = length < 100 ? length : 100; 56 size_t new_len = checker->shortbuf_len + to_write; 57 char *new_buf = realloc(checker->shortbuf, new_len); 58 assert(new_buf != NULL); 59 60 checker->shortbuf = new_buf; 61 memcpy(checker->shortbuf + checker->shortbuf_len, buffer, to_write); 62 checker->shortbuf_len = new_len; 63 64 return to_write; 65} 66 67static ssize_t 68full_write_callback(struct archive *a, void *client_data, const void *buffer, size_t length) 69{ 70 (void)a; 71 72 struct checker *checker = client_data; 73 size_t to_write = length; 74 size_t new_len = checker->fullbuf_len + to_write; 75 char *new_buf = realloc(checker->fullbuf, new_len); 76 assert(new_buf != NULL); 77 78 checker->fullbuf = new_buf; 79 memcpy(checker->fullbuf + checker->fullbuf_len, buffer, to_write); 80 checker->fullbuf_len = new_len; 81 82 return to_write; 83} 84 85static struct archive * 86create_archive(struct checker *checker, archive_write_callback write_cb, int buffered) 87{ 88 struct archive *a; 89 90 assert((a = archive_write_new()) != NULL); 91 92 if (!buffered) 93 assertEqualIntA(a, ARCHIVE_OK, 94 archive_write_set_bytes_per_block(a, 0)); 95 96 /* With the default value of bytes_in_last_block, the writing code will 97 * pad out the final write to make it a full block. This causes problems 98 * for us because the size of the final write can be different depending 99 * on the size of previous writes, causing the "short" and "full" paths 100 * to get different amounts of padding. Setting it to 1 results in no 101 * padding other than that defined by the archive format. */ 102 assertEqualIntA(a, ARCHIVE_OK, 103 archive_write_set_bytes_in_last_block(a, 1)); 104 105 /* We write a pax archive, but other formats would work fine too. */ 106 assertEqualIntA(a, ARCHIVE_OK, 107 archive_write_set_format_pax(a)); 108 assertEqualIntA(a, ARCHIVE_OK, 109 archive_write_add_filter_none(a)); 110 111 assertEqualIntA(a, ARCHIVE_OK, 112 archive_write_open(a, checker, NULL, write_cb, NULL)); 113 114 return a; 115} 116 117static struct checker * 118checker_new(int buffered) 119{ 120 struct checker *checker; 121 122 assert ((checker = calloc(1, sizeof *checker)) != NULL); 123 124 checker->short_archive = create_archive(checker, short_write_callback, buffered); 125 checker->full_archive = create_archive(checker, full_write_callback, buffered); 126 127 return checker; 128} 129 130static void 131checker_add_file(struct checker *checker, const char *name, char *buffer, size_t len) 132{ 133 struct archive_entry *entry; 134 assert((entry = archive_entry_new()) != NULL); 135 136 archive_entry_set_pathname(entry, name); 137 archive_entry_set_mode(entry, AE_IFREG | 0755); 138 archive_entry_set_size(entry, len); 139 140 assertEqualIntA(checker->short_archive, ARCHIVE_OK, 141 archive_write_header(checker->short_archive, entry)); 142 assertEqualIntA(checker->short_archive, len, 143 archive_write_data(checker->short_archive, buffer, len)); 144 145 assertEqualIntA(checker->full_archive, ARCHIVE_OK, 146 archive_write_header(checker->full_archive, entry)); 147 assertEqualIntA(checker->full_archive, len, 148 archive_write_data(checker->full_archive, buffer, len)); 149 150 archive_entry_free(entry); 151} 152 153static void 154checker_close(struct checker *checker) 155{ 156 assertEqualIntA(checker->short_archive, ARCHIVE_OK, 157 archive_write_close(checker->short_archive)); 158 assertEqualIntA(checker->short_archive, ARCHIVE_OK, 159 archive_write_close(checker->full_archive)); 160} 161 162static void 163checker_check(struct checker *checker) 164{ 165 assertEqualInt(checker->shortbuf_len, checker->fullbuf_len); 166 assert(memcmp(checker->shortbuf, checker->fullbuf, checker->fullbuf_len) == 0); 167} 168 169static void 170checker_free(struct checker *checker) 171{ 172 free(checker->shortbuf); 173 free(checker->fullbuf); 174 free(checker); 175} 176 177DEFINE_TEST(test_short_writes) 178{ 179 struct checker *checker; 180 uint16_t test_data[16384]; 181 int i; 182 183 for (i = 0; i < 16384; i++) 184 test_data[i] = i; 185 186 187 /* Write a file smaller than the default buffer size (10 * 1024); 188 * this will be written out at close. 189 */ 190 checker = checker_new(1); 191 checker_add_file(checker, "a", (char *)test_data, 1024); 192 checker_close(checker); 193 assert(checker->shortbuf_len > 1024); 194 checker_check(checker); 195 checker_free(checker); 196 197 /* Write a file larger larger than twice default buffer size (10 * 1024); 198 * this both fills the buffer and writes it out, and also exercises 199 * the "write out full blocks directly" code path. 200 */ 201 checker = checker_new(1); 202 checker_add_file(checker, "a", (char *)test_data, 21 * 1024); 203 checker_close(checker); 204 assert(checker->shortbuf_len > 21 * 1024); 205 checker_check(checker); 206 checker_free(checker); 207 208 /* Test unbuffered writes - a different code path. 209 */ 210 checker = checker_new(0); 211 checker_add_file(checker, "a", (char *)test_data, 1024); 212 checker_close(checker); 213 assert(checker->shortbuf_len > 1024); 214 checker_check(checker); 215 checker_free(checker); 216} 217