1/*- 2 * Copyright (c) 2003-2007 Tim Kientzle 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__FBSDID("$FreeBSD$"); 27 28#define UMASK 022 29/* 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 34 * flags. 35 */ 36#define MODE_MASK 0777777 37 38static void create(struct archive_entry *ae, const char *msg) 39{ 40 struct archive *ad; 41 struct stat st; 42 43 /* Write the entry to disk. */ 44 assert((ad = archive_write_disk_new()) != NULL); 45 failure("%s", msg); 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)); 49 50 /* Test the entries on disk. */ 51 assert(0 == stat(archive_entry_pathname(ae), &st)); 52 failure("%s", msg); 53 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); 61#endif 62} 63 64static void create_reg_file(struct archive_entry *ae, const char *msg) 65{ 66 static const char data[]="abcdefghijklmnopqrstuvwxyz"; 67 struct archive *ad; 68 69 /* Write the entry to disk. */ 70 assert((ad = archive_write_disk_new()) != NULL); 71 archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME); 72 failure("%s", msg); 73 /* 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. 89 */ 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)); 96 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)); 104} 105 106static void create_reg_file2(struct archive_entry *ae, const char *msg) 107{ 108 const int datasize = 100000; 109 char *data; 110 struct archive *ad; 111 int i; 112 113 data = malloc(datasize); 114 for (i = 0; i < datasize; i++) 115 data[i] = (char)(i % 256); 116 117 /* Write the entry to disk. */ 118 assert((ad = archive_write_disk_new()) != NULL); 119 failure("%s", msg); 120 /* 121 * See above for an explanation why this next call 122 * is necessary. 123 */ 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)); 129 } 130 assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 131 assertEqualInt(0, archive_write_free(ad)); 132 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)); 137 free(data); 138} 139 140static void create_reg_file3(struct archive_entry *ae, const char *msg) 141{ 142 static const char data[]="abcdefghijklmnopqrstuvwxyz"; 143 struct archive *ad; 144 struct stat st; 145 146 /* Write the entry to disk. */ 147 assert((ad = archive_write_disk_new()) != NULL); 148 failure("%s", msg); 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)); 155 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)); 162#endif 163 assertEqualInt(st.st_size, 5); 164} 165 166 167static void create_reg_file4(struct archive_entry *ae, const char *msg) 168{ 169 static const char data[]="abcdefghijklmnopqrstuvwxyz"; 170 struct archive *ad; 171 struct stat st; 172 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)); 181 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)); 188#endif 189 failure(msg); 190 assertEqualInt(st.st_size, sizeof(data)); 191} 192 193#if defined(_WIN32) && !defined(__CYGWIN__) 194static void create_reg_file_win(struct archive_entry *ae, const char *msg) 195{ 196 static const char data[]="abcdefghijklmnopqrstuvwxyz"; 197 struct archive *ad; 198 struct _stat st; 199 wchar_t *p, *fname; 200 size_t l; 201 202 /* Write the entry to disk. */ 203 assert((ad = archive_write_disk_new()) != NULL); 204 archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME); 205 failure("%s", msg); 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)); 212 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)); 218 p = fname; 219 /* Skip leading drive letter from archives created 220 * on Windows. */ 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'\\') { 224 p += 3; 225 } 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'|') 230 *p = '_'; 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)); 235 free(fname); 236} 237#endif /* _WIN32 && !__CYGWIN__ */ 238 239DEFINE_TEST(test_write_disk) 240{ 241 struct archive_entry *ae; 242#if defined(_WIN32) && !defined(__CYGWIN__) 243 wchar_t *fullpath; 244 DWORD l; 245#endif 246 247 /* Force the umask to something predictable. */ 248 assertUmask(UMASK); 249 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); 256 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); 263 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); 270 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); 277 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); 284 285 /* A directory. */ 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); 291 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); 298 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); 305 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); 314 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); 322 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); 334 free(fullpath); 335 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); 347 free(fullpath); 348#endif /* _WIN32 && !__CYGWIN__ */ 349} 350