1132451Sroberto/*- 2182007Sroberto * Copyright (c) 2003-2007 Tim Kientzle 3182007Sroberto * All rights reserved. 4132451Sroberto * 5132451Sroberto * Redistribution and use in source and binary forms, with or without 6132451Sroberto * modification, are permitted provided that the following conditions 7132451Sroberto * are met: 8132451Sroberto * 1. Redistributions of source code must retain the above copyright 9182007Sroberto * notice, this list of conditions and the following disclaimer. 10182007Sroberto * 2. Redistributions in binary form must reproduce the above copyright 11182007Sroberto * notice, this list of conditions and the following disclaimer in the 12182007Sroberto * documentation and/or other materials provided with the distribution. 13182007Sroberto * 14182007Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15182007Sroberto * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16132451Sroberto * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17132451Sroberto * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18182007Sroberto * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19132451Sroberto * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20132451Sroberto * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21132451Sroberto * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22182007Sroberto * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23182007Sroberto * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24132451Sroberto */ 25132451Sroberto#include "test.h" 26132451Sroberto 27132451Sroberto#define UMASK 022 28132451Sroberto/* 29132451Sroberto * When comparing mode values, ignore high-order bits 30132451Sroberto * that are set on some OSes. This should cover the bits 31132451Sroberto * we're interested in (standard mode bits + file type bits) 32132451Sroberto * while ignoring extra markers such as Haiku/BeOS index 33132451Sroberto * flags. 34132451Sroberto */ 35132451Sroberto#define MODE_MASK 0777777 36132451Sroberto 37132451Srobertostatic void create(struct archive_entry *ae, const char *msg) 38132451Sroberto{ 39132451Sroberto struct archive *ad; 40182007Sroberto struct stat st; 41132451Sroberto 42132451Sroberto /* Write the entry to disk. */ 43132451Sroberto assert((ad = archive_write_disk_new()) != NULL); 44132451Sroberto failure("%s", msg); 45132451Sroberto assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 46132451Sroberto assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 47132451Sroberto assertEqualInt(0, archive_write_free(ad)); 48132451Sroberto 49132451Sroberto /* Test the entries on disk. */ 50132451Sroberto assert(0 == stat(archive_entry_pathname(ae), &st)); 51132451Sroberto failure("%s", msg); 52132451Sroberto 53132451Sroberto#if !defined(_WIN32) || defined(__CYGWIN__) 54132451Sroberto /* When verifying a dir, ignore the S_ISGID bit, as some systems set 55132451Sroberto * that automatically. */ 56132451Sroberto if (archive_entry_filetype(ae) == AE_IFDIR) 57182007Sroberto st.st_mode &= ~S_ISGID; 58132451Sroberto assertEqualInt(st.st_mode & MODE_MASK, 59132451Sroberto archive_entry_mode(ae) & ~UMASK & MODE_MASK); 60132451Sroberto#endif 61132451Sroberto} 62132451Sroberto 63132451Srobertostatic void create_reg_file(struct archive_entry *ae, const char *msg) 64182007Sroberto{ 65182007Sroberto static const char data[]="abcdefghijklmnopqrstuvwxyz"; 66182007Sroberto struct archive *ad; 67182007Sroberto 68182007Sroberto /* Write the entry to disk. */ 69182007Sroberto assert((ad = archive_write_disk_new()) != NULL); 70182007Sroberto archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME); 71182007Sroberto failure("%s", msg); 72182007Sroberto /* 73182007Sroberto * A touchy API design issue: archive_write_data() does (as of 74182007Sroberto * 2.4.12) enforce the entry size as a limit on the data 75182007Sroberto * written to the file. This was not enforced prior to 76182007Sroberto * 2.4.12. The change was prompted by the refined 77132451Sroberto * hardlink-restore semantics introduced at that time. In 78132451Sroberto * short, libarchive needs to know whether a "hardlink entry" 79132451Sroberto * is going to overwrite the contents so that it can know 80132451Sroberto * whether or not to open the file for writing. This implies 81132451Sroberto * that there is a fundamental semantic difference between an 82132451Sroberto * entry with a zero size and one with a non-zero size in the 83132451Sroberto * case of hardlinks and treating the hardlink case 84182007Sroberto * differently from the regular file case is just asking for 85182007Sroberto * trouble. So, a zero size must always mean that no data 86182007Sroberto * will be accepted, which is consistent with the file size in 87132451Sroberto * the entry being a maximum size. 88182007Sroberto */ 89182007Sroberto archive_entry_set_size(ae, sizeof(data)); 90182007Sroberto archive_entry_set_mtime(ae, 123456789, 0); 91182007Sroberto assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 92182007Sroberto assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); 93182007Sroberto assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 94182007Sroberto assertEqualInt(0, archive_write_free(ad)); 95182007Sroberto 96182007Sroberto /* Test the entries on disk. */ 97182007Sroberto assertIsReg(archive_entry_pathname(ae), archive_entry_mode(ae) & 0777); 98182007Sroberto assertFileSize(archive_entry_pathname(ae), sizeof(data)); 99182007Sroberto /* test_write_disk_times has more detailed tests of this area. */ 100182007Sroberto assertFileMtime(archive_entry_pathname(ae), 123456789, 0); 101182007Sroberto failure("No atime given, so atime should get set to current time"); 102182007Sroberto assertFileAtimeRecent(archive_entry_pathname(ae)); 103182007Sroberto} 104182007Sroberto 105182007Srobertostatic void create_reg_file2(struct archive_entry *ae, const char *msg) 106182007Sroberto{ 107182007Sroberto const int datasize = 100000; 108182007Sroberto char *data; 109182007Sroberto struct archive *ad; 110182007Sroberto int i; 111182007Sroberto 112182007Sroberto data = malloc(datasize); 113182007Sroberto for (i = 0; i < datasize; i++) 114182007Sroberto data[i] = (char)(i % 256); 115182007Sroberto 116182007Sroberto /* Write the entry to disk. */ 117182007Sroberto assert((ad = archive_write_disk_new()) != NULL); 118182007Sroberto failure("%s", msg); 119182007Sroberto /* 120182007Sroberto * See above for an explanation why this next call 121182007Sroberto * is necessary. 122182007Sroberto */ 123182007Sroberto archive_entry_set_size(ae, datasize); 124182007Sroberto assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 125182007Sroberto for (i = 0; i < datasize - 999; i += 1000) { 126182007Sroberto assertEqualIntA(ad, ARCHIVE_OK, 127182007Sroberto archive_write_data_block(ad, data + i, 1000, i)); 128182007Sroberto } 129182007Sroberto assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 130182007Sroberto assertEqualInt(0, archive_write_free(ad)); 131182007Sroberto 132182007Sroberto /* Test the entries on disk. */ 133182007Sroberto assertIsReg(archive_entry_pathname(ae), archive_entry_mode(ae) & 0777); 134182007Sroberto assertFileSize(archive_entry_pathname(ae), i); 135132451Sroberto assertFileContents(data, datasize, archive_entry_pathname(ae)); 136132451Sroberto free(data); 137132451Sroberto} 138132451Sroberto 139132451Srobertostatic void create_reg_file3(struct archive_entry *ae, const char *msg) 140132451Sroberto{ 141132451Sroberto static const char data[]="abcdefghijklmnopqrstuvwxyz"; 142132451Sroberto struct archive *ad; 143132451Sroberto struct stat st; 144132451Sroberto 145132451Sroberto /* Write the entry to disk. */ 146182007Sroberto assert((ad = archive_write_disk_new()) != NULL); 147182007Sroberto failure("%s", msg); 148182007Sroberto /* Set the size smaller than the data and verify the truncation. */ 149132451Sroberto archive_entry_set_size(ae, 5); 150132451Sroberto assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 151132451Sroberto assertEqualInt(5, archive_write_data(ad, data, sizeof(data))); 152132451Sroberto assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 153132451Sroberto assertEqualInt(0, archive_write_free(ad)); 154132451Sroberto 155132451Sroberto /* Test the entry on disk. */ 156132451Sroberto assert(0 == stat(archive_entry_pathname(ae), &st)); 157132451Sroberto failure("st.st_mode=%o archive_entry_mode(ae)=%o", 158132451Sroberto st.st_mode, archive_entry_mode(ae)); 159132451Sroberto#if !defined(_WIN32) || defined(__CYGWIN__) 160132451Sroberto assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK)); 161132451Sroberto#endif 162132451Sroberto assertEqualInt(st.st_size, 5); 163132451Sroberto} 164132451Sroberto 165132451Sroberto 166132451Srobertostatic void create_reg_file4(struct archive_entry *ae, const char *msg) 167132451Sroberto{ 168132451Sroberto static const char data[]="abcdefghijklmnopqrstuvwxyz"; 169132451Sroberto struct archive *ad; 170132451Sroberto struct stat st; 171132451Sroberto 172132451Sroberto /* Write the entry to disk. */ 173182007Sroberto assert((ad = archive_write_disk_new()) != NULL); 174132451Sroberto /* Leave the size unset. The data should not be truncated. */ 175132451Sroberto assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 176132451Sroberto assertEqualInt(ARCHIVE_OK, 177132451Sroberto archive_write_data_block(ad, data, sizeof(data), 0)); 178132451Sroberto assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 179132451Sroberto assertEqualInt(0, archive_write_free(ad)); 180132451Sroberto 181132451Sroberto /* Test the entry on disk. */ 182132451Sroberto assert(0 == stat(archive_entry_pathname(ae), &st)); 183132451Sroberto failure("st.st_mode=%o archive_entry_mode(ae)=%o", 184132451Sroberto st.st_mode, archive_entry_mode(ae)); 185132451Sroberto#if !defined(_WIN32) || defined(__CYGWIN__) 186132451Sroberto assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK)); 187132451Sroberto#endif 188132451Sroberto failure("%s", msg); 189132451Sroberto assertEqualInt(st.st_size, sizeof(data)); 190132451Sroberto} 191132451Sroberto 192132451Sroberto#if defined(_WIN32) && !defined(__CYGWIN__) 193132451Srobertostatic void create_reg_file_win(struct archive_entry *ae, const char *msg) 194132451Sroberto{ 195132451Sroberto static const char data[]="abcdefghijklmnopqrstuvwxyz"; 196132451Sroberto struct archive *ad; 197132451Sroberto struct _stat st; 198132451Sroberto wchar_t *p, *fname; 199132451Sroberto size_t l; 200132451Sroberto 201132451Sroberto /* Write the entry to disk. */ 202132451Sroberto assert((ad = archive_write_disk_new()) != NULL); 203132451Sroberto archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME); 204132451Sroberto failure("%s", msg); 205132451Sroberto archive_entry_set_size(ae, sizeof(data)); 206132451Sroberto archive_entry_set_mtime(ae, 123456789, 0); 207132451Sroberto assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 208132451Sroberto assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); 209132451Sroberto assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 210132451Sroberto assertEqualInt(0, archive_write_free(ad)); 211132451Sroberto 212132451Sroberto /* Test the entries on disk. */ 213132451Sroberto l = wcslen(archive_entry_pathname_w(ae)); 214182007Sroberto fname = malloc((l + 1) * sizeof(wchar_t)); 215182007Sroberto assert(NULL != fname); 216132451Sroberto wcscpy(fname, archive_entry_pathname_w(ae)); 217132451Sroberto p = fname; 218132451Sroberto /* Skip leading drive letter from archives created 219132451Sroberto * on Windows. */ 220132451Sroberto if (((p[0] >= L'a' && p[0] <= L'z') || 221 (p[0] >= L'A' && p[0] <= L'Z')) && 222 p[1] == L':' && p[2] == L'\\') { 223 p += 3; 224 } 225 /* Replace unusable characters in Windows to '_' */ 226 for (; *p != L'\0'; p++) 227 if (*p == L':' || *p == L'*' || *p == L'?' || 228 *p == L'"' || *p == L'<' || *p == L'>' || *p == L'|') 229 *p = '_'; 230 assert(0 == _wstat(fname, &st)); 231 failure("st.st_mode=%o archive_entry_mode(ae)=%o", 232 st.st_mode, archive_entry_mode(ae)); 233 assertEqualInt(st.st_size, sizeof(data)); 234 free(fname); 235} 236#endif /* _WIN32 && !__CYGWIN__ */ 237 238DEFINE_TEST(test_write_disk) 239{ 240 struct archive_entry *ae; 241#if defined(_WIN32) && !defined(__CYGWIN__) 242 wchar_t *fullpath; 243 DWORD l; 244#endif 245 246 /* Force the umask to something predictable. */ 247 assertUmask(UMASK); 248 249 /* A regular file. */ 250 assert((ae = archive_entry_new()) != NULL); 251 archive_entry_copy_pathname(ae, "file"); 252 archive_entry_set_mode(ae, S_IFREG | 0755); 253 create_reg_file(ae, "Test creating a regular file"); 254 archive_entry_free(ae); 255 256 /* Another regular file. */ 257 assert((ae = archive_entry_new()) != NULL); 258 archive_entry_copy_pathname(ae, "file2"); 259 archive_entry_set_mode(ae, S_IFREG | 0755); 260 create_reg_file2(ae, "Test creating another regular file"); 261 archive_entry_free(ae); 262 263 /* A regular file with a size restriction */ 264 assert((ae = archive_entry_new()) != NULL); 265 archive_entry_copy_pathname(ae, "file3"); 266 archive_entry_set_mode(ae, S_IFREG | 0755); 267 create_reg_file3(ae, "Regular file with size restriction"); 268 archive_entry_free(ae); 269 270 /* A regular file with an unspecified size */ 271 assert((ae = archive_entry_new()) != NULL); 272 archive_entry_copy_pathname(ae, "file3"); 273 archive_entry_set_mode(ae, S_IFREG | 0755); 274 create_reg_file4(ae, "Regular file with unspecified size"); 275 archive_entry_free(ae); 276 277 /* A regular file over an existing file */ 278 assert((ae = archive_entry_new()) != NULL); 279 archive_entry_copy_pathname(ae, "file"); 280 archive_entry_set_mode(ae, S_IFREG | 0724); 281 create(ae, "Test creating a file over an existing file."); 282 archive_entry_free(ae); 283 284 /* A directory. */ 285 assert((ae = archive_entry_new()) != NULL); 286 archive_entry_copy_pathname(ae, "dir"); 287 archive_entry_set_mode(ae, S_IFDIR | 0555); 288 create(ae, "Test creating a regular dir."); 289 archive_entry_free(ae); 290 291 /* A directory over an existing file. */ 292 assert((ae = archive_entry_new()) != NULL); 293 archive_entry_copy_pathname(ae, "file"); 294 archive_entry_set_mode(ae, S_IFDIR | 0742); 295 create(ae, "Test creating a dir over an existing file."); 296 archive_entry_free(ae); 297 298 /* A file over an existing dir. */ 299 assert((ae = archive_entry_new()) != NULL); 300 archive_entry_copy_pathname(ae, "file"); 301 archive_entry_set_mode(ae, S_IFREG | 0744); 302 create(ae, "Test creating a file over an existing dir."); 303 archive_entry_free(ae); 304 305#if defined(_WIN32) && !defined(__CYGWIN__) 306 /* A file with unusable characters in its file name. */ 307 assert((ae = archive_entry_new()) != NULL); 308 archive_entry_copy_pathname_w(ae, L"f:i*l?e\"f<i>l|e"); 309 archive_entry_set_mode(ae, S_IFREG | 0755); 310 create_reg_file_win(ae, "Test creating a regular file" 311 " with unusable characters in its file name"); 312 archive_entry_free(ae); 313 314 /* A file with unusable characters in its directory name. */ 315 assert((ae = archive_entry_new()) != NULL); 316 archive_entry_copy_pathname_w(ae, L"d:i*r?e\"c<t>o|ry/file1"); 317 archive_entry_set_mode(ae, S_IFREG | 0755); 318 create_reg_file_win(ae, "Test creating a regular file" 319 " with unusable characters in its file name"); 320 archive_entry_free(ae); 321 322 /* A full-path file with unusable characters in its file name. */ 323 assert((l = GetCurrentDirectoryW(0, NULL)) != 0); 324 assert((fullpath = malloc((l + 20) * sizeof(wchar_t))) != NULL); 325 assert((l = GetCurrentDirectoryW(l, fullpath)) != 0); 326 wcscat(fullpath, L"\\f:i*l?e\"f<i>l|e"); 327 assert((ae = archive_entry_new()) != NULL); 328 archive_entry_copy_pathname_w(ae, fullpath); 329 archive_entry_set_mode(ae, S_IFREG | 0755); 330 create_reg_file_win(ae, "Test creating a regular file" 331 " with unusable characters in its file name"); 332 archive_entry_free(ae); 333 free(fullpath); 334 335 /* A full-path file with unusable characters in its directory name. */ 336 assert((l = GetCurrentDirectoryW(0, NULL)) != 0); 337 assert((fullpath = malloc((l + 30) * sizeof(wchar_t))) != NULL); 338 assert((l = GetCurrentDirectoryW(l, fullpath)) != 0); 339 wcscat(fullpath, L"\\d:i*r?e\"c<t>o|ry/file1"); 340 assert((ae = archive_entry_new()) != NULL); 341 archive_entry_copy_pathname_w(ae, fullpath); 342 archive_entry_set_mode(ae, S_IFREG | 0755); 343 create_reg_file_win(ae, "Test creating a regular file" 344 " with unusable characters in its file name"); 345 archive_entry_free(ae); 346 free(fullpath); 347#endif /* _WIN32 && !__CYGWIN__ */ 348} 349