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 39 struct 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 49 static ssize_t 50 short_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 67 static ssize_t 68 full_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 85 static struct archive * 86 create_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 117 static struct checker * 118 checker_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 130 static void 131 checker_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 153 static void 154 checker_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 162 static void 163 checker_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 169 static void 170 checker_free(struct checker *checker) 171 { 172 free(checker->shortbuf); 173 free(checker->fullbuf); 174 archive_read_free(checker->short_archive); 175 archive_read_free(checker->full_archive); 176 free(checker); 177 } 178 179 DEFINE_TEST(test_short_writes) 180 { 181 struct checker *checker; 182 uint16_t test_data[16384]; 183 int i; 184 185 for (i = 0; i < 16384; i++) 186 test_data[i] = i; 187 188 189 /* Write a file smaller than the default buffer size (10 * 1024); 190 * this will be written out at close. 191 */ 192 checker = checker_new(1); 193 checker_add_file(checker, "a", (char *)test_data, 1024); 194 checker_close(checker); 195 assert(checker->shortbuf_len > 1024); 196 checker_check(checker); 197 checker_free(checker); 198 199 /* Write a file larger larger than twice default buffer size (10 * 1024); 200 * this both fills the buffer and writes it out, and also exercises 201 * the "write out full blocks directly" code path. 202 */ 203 checker = checker_new(1); 204 checker_add_file(checker, "a", (char *)test_data, 21 * 1024); 205 checker_close(checker); 206 assert(checker->shortbuf_len > 21 * 1024); 207 checker_check(checker); 208 checker_free(checker); 209 210 /* Test unbuffered writes - a different code path. 211 */ 212 checker = checker_new(0); 213 checker_add_file(checker, "a", (char *)test_data, 1024); 214 checker_close(checker); 215 assert(checker->shortbuf_len > 1024); 216 checker_check(checker); 217 checker_free(checker); 218 } 219