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