2 * Copyright (c) 2003-2007 Tim Kientzle
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
26 __FBSDID("$FreeBSD$");
30 * When comparing mode values, ignore high-order bits
31 * that are set on some OSes. This should cover the bits
32 * we're interested in (standard mode bits + file type bits)
33 * while ignoring extra markers such as Haiku/BeOS index
36 #define MODE_MASK 0777777
38 static void create(struct archive_entry *ae, const char *msg)
43 /* Write the entry to disk. */
44 assert((ad = archive_write_disk_new()) != NULL);
46 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
47 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
48 assertEqualInt(0, archive_write_free(ad));
50 /* Test the entries on disk. */
51 assert(0 == stat(archive_entry_pathname(ae), &st));
54 #if !defined(_WIN32) || defined(__CYGWIN__)
55 /* When verifying a dir, ignore the S_ISGID bit, as some systems set
56 * that automatically. */
57 if (archive_entry_filetype(ae) == AE_IFDIR)
58 st.st_mode &= ~S_ISGID;
59 assertEqualInt(st.st_mode & MODE_MASK,
60 archive_entry_mode(ae) & ~UMASK & MODE_MASK);
64 static void create_reg_file(struct archive_entry *ae, const char *msg)
66 static const char data[]="abcdefghijklmnopqrstuvwxyz";
69 /* Write the entry to disk. */
70 assert((ad = archive_write_disk_new()) != NULL);
71 archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME);
74 * A touchy API design issue: archive_write_data() does (as of
75 * 2.4.12) enforce the entry size as a limit on the data
76 * written to the file. This was not enforced prior to
77 * 2.4.12. The change was prompted by the refined
78 * hardlink-restore semantics introduced at that time. In
79 * short, libarchive needs to know whether a "hardlink entry"
80 * is going to overwrite the contents so that it can know
81 * whether or not to open the file for writing. This implies
82 * that there is a fundamental semantic difference between an
83 * entry with a zero size and one with a non-zero size in the
84 * case of hardlinks and treating the hardlink case
85 * differently from the regular file case is just asking for
86 * trouble. So, a zero size must always mean that no data
87 * will be accepted, which is consistent with the file size in
88 * the entry being a maximum size.
90 archive_entry_set_size(ae, sizeof(data));
91 archive_entry_set_mtime(ae, 123456789, 0);
92 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
93 assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
94 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
95 assertEqualInt(0, archive_write_free(ad));
97 /* Test the entries on disk. */
98 assertIsReg(archive_entry_pathname(ae), archive_entry_mode(ae) & 0777);
99 assertFileSize(archive_entry_pathname(ae), sizeof(data));
100 /* test_write_disk_times has more detailed tests of this area. */
101 assertFileMtime(archive_entry_pathname(ae), 123456789, 0);
102 failure("No atime given, so atime should get set to current time");
103 assertFileAtimeRecent(archive_entry_pathname(ae));
106 static void create_reg_file2(struct archive_entry *ae, const char *msg)
108 const int datasize = 100000;
113 data = malloc(datasize);
114 for (i = 0; i < datasize; i++)
115 data[i] = (char)(i % 256);
117 /* Write the entry to disk. */
118 assert((ad = archive_write_disk_new()) != NULL);
121 * See above for an explanation why this next call
124 archive_entry_set_size(ae, datasize);
125 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
126 for (i = 0; i < datasize - 999; i += 1000) {
127 assertEqualIntA(ad, ARCHIVE_OK,
128 archive_write_data_block(ad, data + i, 1000, i));
130 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
131 assertEqualInt(0, archive_write_free(ad));
133 /* Test the entries on disk. */
134 assertIsReg(archive_entry_pathname(ae), archive_entry_mode(ae) & 0777);
135 assertFileSize(archive_entry_pathname(ae), i);
136 assertFileContents(data, datasize, archive_entry_pathname(ae));
140 static void create_reg_file3(struct archive_entry *ae, const char *msg)
142 static const char data[]="abcdefghijklmnopqrstuvwxyz";
146 /* Write the entry to disk. */
147 assert((ad = archive_write_disk_new()) != NULL);
149 /* Set the size smaller than the data and verify the truncation. */
150 archive_entry_set_size(ae, 5);
151 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
152 assertEqualInt(5, archive_write_data(ad, data, sizeof(data)));
153 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
154 assertEqualInt(0, archive_write_free(ad));
156 /* Test the entry on disk. */
157 assert(0 == stat(archive_entry_pathname(ae), &st));
158 failure("st.st_mode=%o archive_entry_mode(ae)=%o",
159 st.st_mode, archive_entry_mode(ae));
160 #if !defined(_WIN32) || defined(__CYGWIN__)
161 assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK));
163 assertEqualInt(st.st_size, 5);
167 static void create_reg_file4(struct archive_entry *ae, const char *msg)
169 static const char data[]="abcdefghijklmnopqrstuvwxyz";
173 /* Write the entry to disk. */
174 assert((ad = archive_write_disk_new()) != NULL);
175 /* Leave the size unset. The data should not be truncated. */
176 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
177 assertEqualInt(ARCHIVE_OK,
178 archive_write_data_block(ad, data, sizeof(data), 0));
179 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
180 assertEqualInt(0, archive_write_free(ad));
182 /* Test the entry on disk. */
183 assert(0 == stat(archive_entry_pathname(ae), &st));
184 failure("st.st_mode=%o archive_entry_mode(ae)=%o",
185 st.st_mode, archive_entry_mode(ae));
186 #if !defined(_WIN32) || defined(__CYGWIN__)
187 assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK));
190 assertEqualInt(st.st_size, sizeof(data));
193 #if defined(_WIN32) && !defined(__CYGWIN__)
194 static void create_reg_file_win(struct archive_entry *ae, const char *msg)
196 static const char data[]="abcdefghijklmnopqrstuvwxyz";
202 /* Write the entry to disk. */
203 assert((ad = archive_write_disk_new()) != NULL);
204 archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME);
206 archive_entry_set_size(ae, sizeof(data));
207 archive_entry_set_mtime(ae, 123456789, 0);
208 assertEqualIntA(ad, 0, archive_write_header(ad, ae));
209 assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data)));
210 assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
211 assertEqualInt(0, archive_write_free(ad));
213 /* Test the entries on disk. */
214 l = wcslen(archive_entry_pathname_w(ae));
215 fname = malloc((l + 1) * sizeof(wchar_t));
216 assert(NULL != fname);
217 wcscpy(fname, archive_entry_pathname_w(ae));
219 /* Skip leading drive letter from archives created
221 if (((p[0] >= L'a' && p[0] <= L'z') ||
222 (p[0] >= L'A' && p[0] <= L'Z')) &&
223 p[1] == L':' && p[2] == L'\\') {
226 /* Replace unusable characters in Windows to '_' */
227 for (; *p != L'\0'; p++)
228 if (*p == L':' || *p == L'*' || *p == L'?' ||
229 *p == L'"' || *p == L'<' || *p == L'>' || *p == L'|')
231 assert(0 == _wstat(fname, &st));
232 failure("st.st_mode=%o archive_entry_mode(ae)=%o",
233 st.st_mode, archive_entry_mode(ae));
234 assertEqualInt(st.st_size, sizeof(data));
237 #endif /* _WIN32 && !__CYGWIN__ */
239 DEFINE_TEST(test_write_disk)
241 struct archive_entry *ae;
242 #if defined(_WIN32) && !defined(__CYGWIN__)
247 /* Force the umask to something predictable. */
250 /* A regular file. */
251 assert((ae = archive_entry_new()) != NULL);
252 archive_entry_copy_pathname(ae, "file");
253 archive_entry_set_mode(ae, S_IFREG | 0755);
254 create_reg_file(ae, "Test creating a regular file");
255 archive_entry_free(ae);
257 /* Another regular file. */
258 assert((ae = archive_entry_new()) != NULL);
259 archive_entry_copy_pathname(ae, "file2");
260 archive_entry_set_mode(ae, S_IFREG | 0755);
261 create_reg_file2(ae, "Test creating another regular file");
262 archive_entry_free(ae);
264 /* A regular file with a size restriction */
265 assert((ae = archive_entry_new()) != NULL);
266 archive_entry_copy_pathname(ae, "file3");
267 archive_entry_set_mode(ae, S_IFREG | 0755);
268 create_reg_file3(ae, "Regular file with size restriction");
269 archive_entry_free(ae);
271 /* A regular file with an unspecified size */
272 assert((ae = archive_entry_new()) != NULL);
273 archive_entry_copy_pathname(ae, "file3");
274 archive_entry_set_mode(ae, S_IFREG | 0755);
275 create_reg_file4(ae, "Regular file with unspecified size");
276 archive_entry_free(ae);
278 /* A regular file over an existing file */
279 assert((ae = archive_entry_new()) != NULL);
280 archive_entry_copy_pathname(ae, "file");
281 archive_entry_set_mode(ae, S_IFREG | 0724);
282 create(ae, "Test creating a file over an existing file.");
283 archive_entry_free(ae);
286 assert((ae = archive_entry_new()) != NULL);
287 archive_entry_copy_pathname(ae, "dir");
288 archive_entry_set_mode(ae, S_IFDIR | 0555);
289 create(ae, "Test creating a regular dir.");
290 archive_entry_free(ae);
292 /* A directory over an existing file. */
293 assert((ae = archive_entry_new()) != NULL);
294 archive_entry_copy_pathname(ae, "file");
295 archive_entry_set_mode(ae, S_IFDIR | 0742);
296 create(ae, "Test creating a dir over an existing file.");
297 archive_entry_free(ae);
299 /* A file over an existing dir. */
300 assert((ae = archive_entry_new()) != NULL);
301 archive_entry_copy_pathname(ae, "file");
302 archive_entry_set_mode(ae, S_IFREG | 0744);
303 create(ae, "Test creating a file over an existing dir.");
304 archive_entry_free(ae);
306 #if defined(_WIN32) && !defined(__CYGWIN__)
307 /* A file with unusable characters in its file name. */
308 assert((ae = archive_entry_new()) != NULL);
309 archive_entry_copy_pathname_w(ae, L"f:i*l?e\"f<i>l|e");
310 archive_entry_set_mode(ae, S_IFREG | 0755);
311 create_reg_file_win(ae, "Test creating a regular file"
312 " with unusable characters in its file name");
313 archive_entry_free(ae);
315 /* A file with unusable characters in its directory name. */
316 assert((ae = archive_entry_new()) != NULL);
317 archive_entry_copy_pathname_w(ae, L"d:i*r?e\"c<t>o|ry/file1");
318 archive_entry_set_mode(ae, S_IFREG | 0755);
319 create_reg_file_win(ae, "Test creating a regular file"
320 " with unusable characters in its file name");
321 archive_entry_free(ae);
323 /* A full-path file with unusable characters in its file name. */
324 assert((l = GetCurrentDirectoryW(0, NULL)) != 0);
325 assert((fullpath = malloc((l + 20) * sizeof(wchar_t))) != NULL);
326 assert((l = GetCurrentDirectoryW(l, fullpath)) != 0);
327 wcscat(fullpath, L"\\f:i*l?e\"f<i>l|e");
328 assert((ae = archive_entry_new()) != NULL);
329 archive_entry_copy_pathname_w(ae, fullpath);
330 archive_entry_set_mode(ae, S_IFREG | 0755);
331 create_reg_file_win(ae, "Test creating a regular file"
332 " with unusable characters in its file name");
333 archive_entry_free(ae);
336 /* A full-path file with unusable characters in its directory name. */
337 assert((l = GetCurrentDirectoryW(0, NULL)) != 0);
338 assert((fullpath = malloc((l + 30) * sizeof(wchar_t))) != NULL);
339 assert((l = GetCurrentDirectoryW(l, fullpath)) != 0);
340 wcscat(fullpath, L"\\d:i*r?e\"c<t>o|ry/file1");
341 assert((ae = archive_entry_new()) != NULL);
342 archive_entry_copy_pathname_w(ae, fullpath);
343 archive_entry_set_mode(ae, S_IFREG | 0755);
344 create_reg_file_win(ae, "Test creating a regular file"
345 " with unusable characters in its file name");
346 archive_entry_free(ae);
348 #endif /* _WIN32 && !__CYGWIN__ */