1228753Smm/*- 2228753Smm * Copyright (c) 2003-2007 Tim Kientzle 3228753Smm * All rights reserved. 4228753Smm * 5228753Smm * Redistribution and use in source and binary forms, with or without 6228753Smm * modification, are permitted provided that the following conditions 7228753Smm * are met: 8228753Smm * 1. Redistributions of source code must retain the above copyright 9228753Smm * notice, this list of conditions and the following disclaimer. 10228753Smm * 2. Redistributions in binary form must reproduce the above copyright 11228753Smm * notice, this list of conditions and the following disclaimer in the 12228753Smm * documentation and/or other materials provided with the distribution. 13228753Smm * 14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24228753Smm */ 25228753Smm#include "test.h" 26228763Smm__FBSDID("$FreeBSD$"); 27228753Smm 28228753Smm#define UMASK 022 29228753Smm/* 30228753Smm * When comparing mode values, ignore high-order bits 31228753Smm * that are set on some OSes. This should cover the bits 32228753Smm * we're interested in (standard mode bits + file type bits) 33228753Smm * while ignoring extra markers such as Haiku/BeOS index 34228753Smm * flags. 35228753Smm */ 36228753Smm#define MODE_MASK 0777777 37228753Smm 38228753Smmstatic void create(struct archive_entry *ae, const char *msg) 39228753Smm{ 40228753Smm struct archive *ad; 41228753Smm struct stat st; 42228753Smm 43228753Smm /* Write the entry to disk. */ 44228753Smm assert((ad = archive_write_disk_new()) != NULL); 45228753Smm failure("%s", msg); 46228753Smm assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 47228753Smm assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 48232153Smm assertEqualInt(0, archive_write_free(ad)); 49232153Smm 50228753Smm /* Test the entries on disk. */ 51228753Smm assert(0 == stat(archive_entry_pathname(ae), &st)); 52228753Smm failure("%s", msg); 53228753Smm 54228753Smm#if !defined(_WIN32) || defined(__CYGWIN__) 55228753Smm /* When verifying a dir, ignore the S_ISGID bit, as some systems set 56228753Smm * that automatically. */ 57228753Smm if (archive_entry_filetype(ae) == AE_IFDIR) 58228753Smm st.st_mode &= ~S_ISGID; 59228753Smm assertEqualInt(st.st_mode & MODE_MASK, 60228753Smm archive_entry_mode(ae) & ~UMASK & MODE_MASK); 61228753Smm#endif 62228753Smm} 63228753Smm 64228753Smmstatic void create_reg_file(struct archive_entry *ae, const char *msg) 65228753Smm{ 66228753Smm static const char data[]="abcdefghijklmnopqrstuvwxyz"; 67228753Smm struct archive *ad; 68228753Smm 69228753Smm /* Write the entry to disk. */ 70228753Smm assert((ad = archive_write_disk_new()) != NULL); 71228753Smm archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME); 72228753Smm failure("%s", msg); 73228753Smm /* 74228753Smm * A touchy API design issue: archive_write_data() does (as of 75228753Smm * 2.4.12) enforce the entry size as a limit on the data 76228753Smm * written to the file. This was not enforced prior to 77228753Smm * 2.4.12. The change was prompted by the refined 78228753Smm * hardlink-restore semantics introduced at that time. In 79228753Smm * short, libarchive needs to know whether a "hardlink entry" 80228753Smm * is going to overwrite the contents so that it can know 81228753Smm * whether or not to open the file for writing. This implies 82228753Smm * that there is a fundamental semantic difference between an 83228753Smm * entry with a zero size and one with a non-zero size in the 84228753Smm * case of hardlinks and treating the hardlink case 85228753Smm * differently from the regular file case is just asking for 86228753Smm * trouble. So, a zero size must always mean that no data 87228753Smm * will be accepted, which is consistent with the file size in 88228753Smm * the entry being a maximum size. 89228753Smm */ 90228753Smm archive_entry_set_size(ae, sizeof(data)); 91228753Smm archive_entry_set_mtime(ae, 123456789, 0); 92228753Smm assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 93228753Smm assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); 94228753Smm assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 95232153Smm assertEqualInt(0, archive_write_free(ad)); 96232153Smm 97228753Smm /* Test the entries on disk. */ 98228753Smm assertIsReg(archive_entry_pathname(ae), archive_entry_mode(ae) & 0777); 99228753Smm assertFileSize(archive_entry_pathname(ae), sizeof(data)); 100228753Smm /* test_write_disk_times has more detailed tests of this area. */ 101228753Smm assertFileMtime(archive_entry_pathname(ae), 123456789, 0); 102228753Smm failure("No atime given, so atime should get set to current time"); 103228753Smm assertFileAtimeRecent(archive_entry_pathname(ae)); 104228753Smm} 105228753Smm 106228753Smmstatic void create_reg_file2(struct archive_entry *ae, const char *msg) 107228753Smm{ 108228753Smm const int datasize = 100000; 109228753Smm char *data; 110228753Smm struct archive *ad; 111228753Smm int i; 112228753Smm 113228753Smm data = malloc(datasize); 114228753Smm for (i = 0; i < datasize; i++) 115228753Smm data[i] = (char)(i % 256); 116228753Smm 117228753Smm /* Write the entry to disk. */ 118228753Smm assert((ad = archive_write_disk_new()) != NULL); 119228753Smm failure("%s", msg); 120228753Smm /* 121228753Smm * See above for an explanation why this next call 122228753Smm * is necessary. 123228753Smm */ 124228753Smm archive_entry_set_size(ae, datasize); 125228753Smm assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 126228753Smm for (i = 0; i < datasize - 999; i += 1000) { 127228753Smm assertEqualIntA(ad, ARCHIVE_OK, 128228753Smm archive_write_data_block(ad, data + i, 1000, i)); 129228753Smm } 130228753Smm assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 131232153Smm assertEqualInt(0, archive_write_free(ad)); 132228753Smm 133228753Smm /* Test the entries on disk. */ 134228753Smm assertIsReg(archive_entry_pathname(ae), archive_entry_mode(ae) & 0777); 135228753Smm assertFileSize(archive_entry_pathname(ae), i); 136228753Smm assertFileContents(data, datasize, archive_entry_pathname(ae)); 137228753Smm free(data); 138228753Smm} 139228753Smm 140228753Smmstatic void create_reg_file3(struct archive_entry *ae, const char *msg) 141228753Smm{ 142228753Smm static const char data[]="abcdefghijklmnopqrstuvwxyz"; 143228753Smm struct archive *ad; 144228753Smm struct stat st; 145228753Smm 146228753Smm /* Write the entry to disk. */ 147228753Smm assert((ad = archive_write_disk_new()) != NULL); 148228753Smm failure("%s", msg); 149228753Smm /* Set the size smaller than the data and verify the truncation. */ 150228753Smm archive_entry_set_size(ae, 5); 151228753Smm assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 152228753Smm assertEqualInt(5, archive_write_data(ad, data, sizeof(data))); 153228753Smm assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 154232153Smm assertEqualInt(0, archive_write_free(ad)); 155232153Smm 156228753Smm /* Test the entry on disk. */ 157228753Smm assert(0 == stat(archive_entry_pathname(ae), &st)); 158228753Smm failure("st.st_mode=%o archive_entry_mode(ae)=%o", 159228753Smm st.st_mode, archive_entry_mode(ae)); 160228753Smm#if !defined(_WIN32) || defined(__CYGWIN__) 161228753Smm assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK)); 162228753Smm#endif 163228753Smm assertEqualInt(st.st_size, 5); 164228753Smm} 165228753Smm 166228753Smm 167228753Smmstatic void create_reg_file4(struct archive_entry *ae, const char *msg) 168228753Smm{ 169228753Smm static const char data[]="abcdefghijklmnopqrstuvwxyz"; 170228753Smm struct archive *ad; 171228753Smm struct stat st; 172228753Smm 173228753Smm /* Write the entry to disk. */ 174228753Smm assert((ad = archive_write_disk_new()) != NULL); 175228753Smm /* Leave the size unset. The data should not be truncated. */ 176228753Smm assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 177228753Smm assertEqualInt(ARCHIVE_OK, 178228753Smm archive_write_data_block(ad, data, sizeof(data), 0)); 179228753Smm assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 180232153Smm assertEqualInt(0, archive_write_free(ad)); 181232153Smm 182228753Smm /* Test the entry on disk. */ 183228753Smm assert(0 == stat(archive_entry_pathname(ae), &st)); 184228753Smm failure("st.st_mode=%o archive_entry_mode(ae)=%o", 185228753Smm st.st_mode, archive_entry_mode(ae)); 186228753Smm#if !defined(_WIN32) || defined(__CYGWIN__) 187228753Smm assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK)); 188228753Smm#endif 189228753Smm failure(msg); 190228753Smm assertEqualInt(st.st_size, sizeof(data)); 191228753Smm} 192228753Smm 193228753Smm#if defined(_WIN32) && !defined(__CYGWIN__) 194228753Smmstatic void create_reg_file_win(struct archive_entry *ae, const char *msg) 195228753Smm{ 196228753Smm static const char data[]="abcdefghijklmnopqrstuvwxyz"; 197228753Smm struct archive *ad; 198232153Smm struct _stat st; 199232153Smm wchar_t *p, *fname; 200228753Smm size_t l; 201228753Smm 202228753Smm /* Write the entry to disk. */ 203228753Smm assert((ad = archive_write_disk_new()) != NULL); 204232153Smm archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME); 205228753Smm failure("%s", msg); 206228753Smm archive_entry_set_size(ae, sizeof(data)); 207228753Smm archive_entry_set_mtime(ae, 123456789, 0); 208228753Smm assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 209228753Smm assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); 210228753Smm assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 211232153Smm assertEqualInt(0, archive_write_free(ad)); 212232153Smm 213228753Smm /* Test the entries on disk. */ 214232153Smm l = wcslen(archive_entry_pathname_w(ae)); 215232153Smm fname = malloc((l + 1) * sizeof(wchar_t)); 216228753Smm assert(NULL != fname); 217232153Smm wcscpy(fname, archive_entry_pathname_w(ae)); 218232153Smm p = fname; 219232153Smm /* Skip leading drive letter from archives created 220232153Smm * on Windows. */ 221232153Smm if (((p[0] >= L'a' && p[0] <= L'z') || 222232153Smm (p[0] >= L'A' && p[0] <= L'Z')) && 223232153Smm p[1] == L':' && p[2] == L'\\') { 224232153Smm p += 3; 225232153Smm } 226228753Smm /* Replace unusable characters in Windows to '_' */ 227232153Smm for (; *p != L'\0'; p++) 228232153Smm if (*p == L':' || *p == L'*' || *p == L'?' || 229232153Smm *p == L'"' || *p == L'<' || *p == L'>' || *p == L'|') 230228753Smm *p = '_'; 231232153Smm assert(0 == _wstat(fname, &st)); 232228753Smm failure("st.st_mode=%o archive_entry_mode(ae)=%o", 233228753Smm st.st_mode, archive_entry_mode(ae)); 234228753Smm assertEqualInt(st.st_size, sizeof(data)); 235232153Smm free(fname); 236228753Smm} 237228753Smm#endif /* _WIN32 && !__CYGWIN__ */ 238228753Smm 239228753SmmDEFINE_TEST(test_write_disk) 240228753Smm{ 241228753Smm struct archive_entry *ae; 242232153Smm#if defined(_WIN32) && !defined(__CYGWIN__) 243232153Smm wchar_t *fullpath; 244232153Smm DWORD l; 245232153Smm#endif 246228753Smm 247228753Smm /* Force the umask to something predictable. */ 248228753Smm assertUmask(UMASK); 249228753Smm 250228753Smm /* A regular file. */ 251228753Smm assert((ae = archive_entry_new()) != NULL); 252228753Smm archive_entry_copy_pathname(ae, "file"); 253228753Smm archive_entry_set_mode(ae, S_IFREG | 0755); 254228753Smm create_reg_file(ae, "Test creating a regular file"); 255228753Smm archive_entry_free(ae); 256228753Smm 257228753Smm /* Another regular file. */ 258228753Smm assert((ae = archive_entry_new()) != NULL); 259228753Smm archive_entry_copy_pathname(ae, "file2"); 260228753Smm archive_entry_set_mode(ae, S_IFREG | 0755); 261228753Smm create_reg_file2(ae, "Test creating another regular file"); 262228753Smm archive_entry_free(ae); 263228753Smm 264228753Smm /* A regular file with a size restriction */ 265228753Smm assert((ae = archive_entry_new()) != NULL); 266228753Smm archive_entry_copy_pathname(ae, "file3"); 267228753Smm archive_entry_set_mode(ae, S_IFREG | 0755); 268228753Smm create_reg_file3(ae, "Regular file with size restriction"); 269228753Smm archive_entry_free(ae); 270228753Smm 271228753Smm /* A regular file with an unspecified size */ 272228753Smm assert((ae = archive_entry_new()) != NULL); 273228753Smm archive_entry_copy_pathname(ae, "file3"); 274228753Smm archive_entry_set_mode(ae, S_IFREG | 0755); 275228753Smm create_reg_file4(ae, "Regular file with unspecified size"); 276228753Smm archive_entry_free(ae); 277228753Smm 278228753Smm /* A regular file over an existing file */ 279228753Smm assert((ae = archive_entry_new()) != NULL); 280228753Smm archive_entry_copy_pathname(ae, "file"); 281228753Smm archive_entry_set_mode(ae, S_IFREG | 0724); 282228753Smm create(ae, "Test creating a file over an existing file."); 283228753Smm archive_entry_free(ae); 284228753Smm 285228753Smm /* A directory. */ 286228753Smm assert((ae = archive_entry_new()) != NULL); 287228753Smm archive_entry_copy_pathname(ae, "dir"); 288228753Smm archive_entry_set_mode(ae, S_IFDIR | 0555); 289228753Smm create(ae, "Test creating a regular dir."); 290228753Smm archive_entry_free(ae); 291228753Smm 292228753Smm /* A directory over an existing file. */ 293228753Smm assert((ae = archive_entry_new()) != NULL); 294228753Smm archive_entry_copy_pathname(ae, "file"); 295228753Smm archive_entry_set_mode(ae, S_IFDIR | 0742); 296228753Smm create(ae, "Test creating a dir over an existing file."); 297228753Smm archive_entry_free(ae); 298228753Smm 299228753Smm /* A file over an existing dir. */ 300228753Smm assert((ae = archive_entry_new()) != NULL); 301228753Smm archive_entry_copy_pathname(ae, "file"); 302228753Smm archive_entry_set_mode(ae, S_IFREG | 0744); 303228753Smm create(ae, "Test creating a file over an existing dir."); 304228753Smm archive_entry_free(ae); 305228753Smm 306228753Smm#if defined(_WIN32) && !defined(__CYGWIN__) 307228753Smm /* A file with unusable characters in its file name. */ 308228753Smm assert((ae = archive_entry_new()) != NULL); 309232153Smm archive_entry_copy_pathname_w(ae, L"f:i*l?e\"f<i>l|e"); 310228753Smm archive_entry_set_mode(ae, S_IFREG | 0755); 311228753Smm create_reg_file_win(ae, "Test creating a regular file" 312228753Smm " with unusable characters in its file name"); 313228753Smm archive_entry_free(ae); 314228753Smm 315228753Smm /* A file with unusable characters in its directory name. */ 316228753Smm assert((ae = archive_entry_new()) != NULL); 317232153Smm archive_entry_copy_pathname_w(ae, L"d:i*r?e\"c<t>o|ry/file1"); 318228753Smm archive_entry_set_mode(ae, S_IFREG | 0755); 319228753Smm create_reg_file_win(ae, "Test creating a regular file" 320228753Smm " with unusable characters in its file name"); 321228753Smm archive_entry_free(ae); 322232153Smm 323232153Smm /* A full-path file with unusable characters in its file name. */ 324232153Smm assert((l = GetCurrentDirectoryW(0, NULL)) != 0); 325232153Smm assert((fullpath = malloc((l + 20) * sizeof(wchar_t))) != NULL); 326232153Smm assert((l = GetCurrentDirectoryW(l, fullpath)) != 0); 327232153Smm wcscat(fullpath, L"\\f:i*l?e\"f<i>l|e"); 328232153Smm assert((ae = archive_entry_new()) != NULL); 329232153Smm archive_entry_copy_pathname_w(ae, fullpath); 330232153Smm archive_entry_set_mode(ae, S_IFREG | 0755); 331232153Smm create_reg_file_win(ae, "Test creating a regular file" 332232153Smm " with unusable characters in its file name"); 333232153Smm archive_entry_free(ae); 334232153Smm free(fullpath); 335232153Smm 336232153Smm /* A full-path file with unusable characters in its directory name. */ 337232153Smm assert((l = GetCurrentDirectoryW(0, NULL)) != 0); 338232153Smm assert((fullpath = malloc((l + 30) * sizeof(wchar_t))) != NULL); 339232153Smm assert((l = GetCurrentDirectoryW(l, fullpath)) != 0); 340232153Smm wcscat(fullpath, L"\\d:i*r?e\"c<t>o|ry/file1"); 341232153Smm assert((ae = archive_entry_new()) != NULL); 342232153Smm archive_entry_copy_pathname_w(ae, fullpath); 343232153Smm archive_entry_set_mode(ae, S_IFREG | 0755); 344232153Smm create_reg_file_win(ae, "Test creating a regular file" 345232153Smm " with unusable characters in its file name"); 346232153Smm archive_entry_free(ae); 347232153Smm free(fullpath); 348228753Smm#endif /* _WIN32 && !__CYGWIN__ */ 349228753Smm} 350