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