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: head/lib/libarchive/test/test_write_disk.c 201247 2009-12-30 05:59:21Z kientzle $"); 27 28 #if ARCHIVE_VERSION_NUMBER >= 1009000 29 30 #define UMASK 022 31 /* 32 * When comparing mode values, ignore high-order bits 33 * that are set on some OSes. This should cover the bits 34 * we're interested in (standard mode bits + file type bits) 35 * while ignoring extra markers such as Haiku/BeOS index 36 * flags. 37 */ 38 #define MODE_MASK 0777777 39 40 static void create(struct archive_entry *ae, const char *msg) 41 { 42 struct archive *ad; 43 struct stat st; 44 45 /* Write the entry to disk. */ 46 assert((ad = archive_write_disk_new()) != NULL); 47 failure("%s", msg); 48 assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 49 assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 50 #if ARCHIVE_VERSION_NUMBER < 2000000 51 archive_write_finish(ad); 52 #else 53 assertEqualInt(0, archive_write_finish(ad)); 54 #endif 55 /* Test the entries on disk. */ 56 assert(0 == stat(archive_entry_pathname(ae), &st)); 57 failure("%s", msg); 58 59 #if !defined(_WIN32) || defined(__CYGWIN__) 60 /* When verifying a dir, ignore the S_ISGID bit, as some systems set 61 * that automatically. */ 62 if (archive_entry_filetype(ae) == AE_IFDIR) 63 st.st_mode &= ~S_ISGID; 64 assertEqualInt(st.st_mode & MODE_MASK, 65 archive_entry_mode(ae) & ~UMASK & MODE_MASK); 66 #endif 67 } 68 69 static void create_reg_file(struct archive_entry *ae, const char *msg) 70 { 71 static const char data[]="abcdefghijklmnopqrstuvwxyz"; 72 struct archive *ad; 73 74 /* Write the entry to disk. */ 75 assert((ad = archive_write_disk_new()) != NULL); 76 archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME); 77 failure("%s", msg); 78 /* 79 * A touchy API design issue: archive_write_data() does (as of 80 * 2.4.12) enforce the entry size as a limit on the data 81 * written to the file. This was not enforced prior to 82 * 2.4.12. The change was prompted by the refined 83 * hardlink-restore semantics introduced at that time. In 84 * short, libarchive needs to know whether a "hardlink entry" 85 * is going to overwrite the contents so that it can know 86 * whether or not to open the file for writing. This implies 87 * that there is a fundamental semantic difference between an 88 * entry with a zero size and one with a non-zero size in the 89 * case of hardlinks and treating the hardlink case 90 * differently from the regular file case is just asking for 91 * trouble. So, a zero size must always mean that no data 92 * will be accepted, which is consistent with the file size in 93 * the entry being a maximum size. 94 */ 95 archive_entry_set_size(ae, sizeof(data)); 96 archive_entry_set_mtime(ae, 123456789, 0); 97 assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 98 assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); 99 assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 100 #if ARCHIVE_VERSION_NUMBER < 2000000 101 archive_write_finish(ad); 102 #else 103 assertEqualInt(0, archive_write_finish(ad)); 104 #endif 105 /* Test the entries on disk. */ 106 assertIsReg(archive_entry_pathname(ae), archive_entry_mode(ae) & 0777); 107 assertFileSize(archive_entry_pathname(ae), sizeof(data)); 108 /* test_write_disk_times has more detailed tests of this area. */ 109 assertFileMtime(archive_entry_pathname(ae), 123456789, 0); 110 failure("No atime given, so atime should get set to current time"); 111 assertFileAtimeRecent(archive_entry_pathname(ae)); 112 } 113 114 static void create_reg_file2(struct archive_entry *ae, const char *msg) 115 { 116 const int datasize = 100000; 117 char *data; 118 struct archive *ad; 119 int i; 120 121 data = malloc(datasize); 122 for (i = 0; i < datasize; i++) 123 data[i] = (char)(i % 256); 124 125 /* Write the entry to disk. */ 126 assert((ad = archive_write_disk_new()) != NULL); 127 failure("%s", msg); 128 /* 129 * See above for an explanation why this next call 130 * is necessary. 131 */ 132 archive_entry_set_size(ae, datasize); 133 assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 134 for (i = 0; i < datasize - 999; i += 1000) { 135 assertEqualIntA(ad, ARCHIVE_OK, 136 archive_write_data_block(ad, data + i, 1000, i)); 137 } 138 assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 139 assertEqualInt(0, archive_write_finish(ad)); 140 141 /* Test the entries on disk. */ 142 assertIsReg(archive_entry_pathname(ae), archive_entry_mode(ae) & 0777); 143 assertFileSize(archive_entry_pathname(ae), i); 144 assertFileContents(data, datasize, archive_entry_pathname(ae)); 145 free(data); 146 } 147 148 static void create_reg_file3(struct archive_entry *ae, const char *msg) 149 { 150 static const char data[]="abcdefghijklmnopqrstuvwxyz"; 151 struct archive *ad; 152 struct stat st; 153 154 /* Write the entry to disk. */ 155 assert((ad = archive_write_disk_new()) != NULL); 156 failure("%s", msg); 157 /* Set the size smaller than the data and verify the truncation. */ 158 archive_entry_set_size(ae, 5); 159 assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 160 assertEqualInt(5, archive_write_data(ad, data, sizeof(data))); 161 assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 162 #if ARCHIVE_VERSION_NUMBER < 2000000 163 archive_write_finish(ad); 164 #else 165 assertEqualInt(0, archive_write_finish(ad)); 166 #endif 167 /* Test the entry on disk. */ 168 assert(0 == stat(archive_entry_pathname(ae), &st)); 169 failure("st.st_mode=%o archive_entry_mode(ae)=%o", 170 st.st_mode, archive_entry_mode(ae)); 171 #if !defined(_WIN32) || defined(__CYGWIN__) 172 assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK)); 173 #endif 174 assertEqualInt(st.st_size, 5); 175 } 176 177 178 static void create_reg_file4(struct archive_entry *ae, const char *msg) 179 { 180 static const char data[]="abcdefghijklmnopqrstuvwxyz"; 181 struct archive *ad; 182 struct stat st; 183 184 /* Write the entry to disk. */ 185 assert((ad = archive_write_disk_new()) != NULL); 186 /* Leave the size unset. The data should not be truncated. */ 187 assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 188 assertEqualInt(ARCHIVE_OK, 189 archive_write_data_block(ad, data, sizeof(data), 0)); 190 assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 191 #if ARCHIVE_VERSION_NUMBER < 2000000 192 archive_write_finish(ad); 193 #else 194 assertEqualInt(0, archive_write_finish(ad)); 195 #endif 196 /* Test the entry on disk. */ 197 assert(0 == stat(archive_entry_pathname(ae), &st)); 198 failure("st.st_mode=%o archive_entry_mode(ae)=%o", 199 st.st_mode, archive_entry_mode(ae)); 200 #if !defined(_WIN32) || defined(__CYGWIN__) 201 assertEqualInt(st.st_mode, (archive_entry_mode(ae) & ~UMASK)); 202 #endif 203 failure(msg); 204 assertEqualInt(st.st_size, sizeof(data)); 205 } 206 207 #if defined(_WIN32) && !defined(__CYGWIN__) 208 static void create_reg_file_win(struct archive_entry *ae, const char *msg) 209 { 210 static const char data[]="abcdefghijklmnopqrstuvwxyz"; 211 struct archive *ad; 212 struct stat st; 213 char *p, *fname; 214 size_t l; 215 216 /* Write the entry to disk. */ 217 assert((ad = archive_write_disk_new()) != NULL); 218 archive_write_disk_set_options(ad, ARCHIVE_EXTRACT_TIME); 219 failure("%s", msg); 220 archive_entry_set_size(ae, sizeof(data)); 221 archive_entry_set_mtime(ae, 123456789, 0); 222 assertEqualIntA(ad, 0, archive_write_header(ad, ae)); 223 assertEqualInt(sizeof(data), archive_write_data(ad, data, sizeof(data))); 224 assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); 225 #if ARCHIVE_VERSION_NUMBER < 2000000 226 archive_write_finish(ad); 227 #else 228 assertEqualInt(0, archive_write_finish(ad)); 229 #endif 230 /* Test the entries on disk. */ 231 l = strlen(archive_entry_pathname(ae)); 232 fname = malloc(l + 1); 233 assert(NULL != fname); 234 strcpy(fname, archive_entry_pathname(ae)); 235 /* Replace unusable characters in Windows to '_' */ 236 for (p = fname; *p != '\0'; p++) 237 if (*p == ':' || *p == '*' || *p == '?' || 238 *p == '"' || *p == '<' || *p == '>' || *p == '|') 239 *p = '_'; 240 assert(0 == stat(fname, &st)); 241 failure("st.st_mode=%o archive_entry_mode(ae)=%o", 242 st.st_mode, archive_entry_mode(ae)); 243 assertEqualInt(st.st_size, sizeof(data)); 244 } 245 #endif /* _WIN32 && !__CYGWIN__ */ 246 #endif 247 248 DEFINE_TEST(test_write_disk) 249 { 250 #if ARCHIVE_VERSION_NUMBER < 1009000 251 skipping("archive_write_disk interface"); 252 #else 253 struct archive_entry *ae; 254 255 /* Force the umask to something predictable. */ 256 assertUmask(UMASK); 257 258 /* A regular file. */ 259 assert((ae = archive_entry_new()) != NULL); 260 archive_entry_copy_pathname(ae, "file"); 261 archive_entry_set_mode(ae, S_IFREG | 0755); 262 create_reg_file(ae, "Test creating a regular file"); 263 archive_entry_free(ae); 264 265 /* Another regular file. */ 266 assert((ae = archive_entry_new()) != NULL); 267 archive_entry_copy_pathname(ae, "file2"); 268 archive_entry_set_mode(ae, S_IFREG | 0755); 269 create_reg_file2(ae, "Test creating another regular file"); 270 archive_entry_free(ae); 271 272 /* A regular file with a size restriction */ 273 assert((ae = archive_entry_new()) != NULL); 274 archive_entry_copy_pathname(ae, "file3"); 275 archive_entry_set_mode(ae, S_IFREG | 0755); 276 create_reg_file3(ae, "Regular file with size restriction"); 277 archive_entry_free(ae); 278 279 /* A regular file with an unspecified size */ 280 assert((ae = archive_entry_new()) != NULL); 281 archive_entry_copy_pathname(ae, "file3"); 282 archive_entry_set_mode(ae, S_IFREG | 0755); 283 create_reg_file4(ae, "Regular file with unspecified size"); 284 archive_entry_free(ae); 285 286 /* A regular file over an existing file */ 287 assert((ae = archive_entry_new()) != NULL); 288 archive_entry_copy_pathname(ae, "file"); 289 archive_entry_set_mode(ae, S_IFREG | 0724); 290 create(ae, "Test creating a file over an existing file."); 291 archive_entry_free(ae); 292 293 /* A directory. */ 294 assert((ae = archive_entry_new()) != NULL); 295 archive_entry_copy_pathname(ae, "dir"); 296 archive_entry_set_mode(ae, S_IFDIR | 0555); 297 create(ae, "Test creating a regular dir."); 298 archive_entry_free(ae); 299 300 /* A directory over an existing file. */ 301 assert((ae = archive_entry_new()) != NULL); 302 archive_entry_copy_pathname(ae, "file"); 303 archive_entry_set_mode(ae, S_IFDIR | 0742); 304 create(ae, "Test creating a dir over an existing file."); 305 archive_entry_free(ae); 306 307 /* A file over an existing dir. */ 308 assert((ae = archive_entry_new()) != NULL); 309 archive_entry_copy_pathname(ae, "file"); 310 archive_entry_set_mode(ae, S_IFREG | 0744); 311 create(ae, "Test creating a file over an existing dir."); 312 archive_entry_free(ae); 313 314 #if defined(_WIN32) && !defined(__CYGWIN__) 315 /* A file with unusable characters in its file name. */ 316 assert((ae = archive_entry_new()) != NULL); 317 archive_entry_copy_pathname(ae, "f:i*l?e\"f<i>l|e"); 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 file with unusable characters in its directory name. */ 324 assert((ae = archive_entry_new()) != NULL); 325 archive_entry_copy_pathname(ae, "d:i*r?e\"c<t>o|ry/file1"); 326 archive_entry_set_mode(ae, S_IFREG | 0755); 327 create_reg_file_win(ae, "Test creating a regular file" 328 " with unusable characters in its file name"); 329 archive_entry_free(ae); 330 #endif /* _WIN32 && !__CYGWIN__ */ 331 #endif 332 } 333