test_acl_platform_posix1e.c revision 315433
1/*- 2 * Copyright (c) 2003-2008 Tim Kientzle 3 * Copyright (c) 2017 Martin Matuska 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26#include "test.h" 27__FBSDID("$FreeBSD: head/lib/libarchive/test/test_acl_freebsd.c 189427 2009-03-06 04:21:23Z kientzle $"); 28 29#if HAVE_POSIX_ACL || HAVE_SUN_ACL 30#include <sys/acl.h> 31#if HAVE_ACL_GET_PERM 32#include <acl/libacl.h> 33#define ACL_GET_PERM acl_get_perm 34#elif HAVE_ACL_GET_PERM_NP 35#define ACL_GET_PERM acl_get_perm_np 36#endif 37 38static struct archive_test_acl_t acls2[] = { 39 { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_EXECUTE | ARCHIVE_ENTRY_ACL_READ, 40 ARCHIVE_ENTRY_ACL_USER_OBJ, -1, "" }, 41 { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, 42 ARCHIVE_ENTRY_ACL_USER, 77, "user77" }, 43 { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0, 44 ARCHIVE_ENTRY_ACL_USER, 78, "user78" }, 45 { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_READ, 46 ARCHIVE_ENTRY_ACL_GROUP_OBJ, -1, "" }, 47 { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 0007, 48 ARCHIVE_ENTRY_ACL_GROUP, 78, "group78" }, 49 { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 50 ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_EXECUTE, 51 ARCHIVE_ENTRY_ACL_OTHER, -1, "" }, 52 { ARCHIVE_ENTRY_ACL_TYPE_ACCESS, 53 ARCHIVE_ENTRY_ACL_WRITE | ARCHIVE_ENTRY_ACL_READ | ARCHIVE_ENTRY_ACL_EXECUTE, 54 ARCHIVE_ENTRY_ACL_MASK, -1, "" }, 55}; 56 57static int 58#if HAVE_SUN_ACL 59acl_entry_get_perm(aclent_t *aclent) 60#else 61acl_entry_get_perm(acl_entry_t aclent) 62#endif 63{ 64 int permset = 0; 65#if HAVE_POSIX_ACL 66 acl_permset_t opaque_ps; 67#endif 68 69#if HAVE_SUN_ACL 70 if (aclent->a_perm & 1) 71 permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 72 if (aclent->a_perm & 2) 73 permset |= ARCHIVE_ENTRY_ACL_WRITE; 74 if (aclent->a_perm & 4) 75 permset |= ARCHIVE_ENTRY_ACL_READ; 76#else 77 /* translate the silly opaque permset to a bitmap */ 78 acl_get_permset(aclent, &opaque_ps); 79 if (ACL_GET_PERM(opaque_ps, ACL_EXECUTE)) 80 permset |= ARCHIVE_ENTRY_ACL_EXECUTE; 81 if (ACL_GET_PERM(opaque_ps, ACL_WRITE)) 82 permset |= ARCHIVE_ENTRY_ACL_WRITE; 83 if (ACL_GET_PERM(opaque_ps, ACL_READ)) 84 permset |= ARCHIVE_ENTRY_ACL_READ; 85#endif 86 return permset; 87} 88 89#if 0 90static int 91acl_get_specific_entry(acl_t acl, acl_tag_t requested_tag_type, int requested_tag) { 92 int entry_id = ACL_FIRST_ENTRY; 93 acl_entry_t acl_entry; 94 acl_tag_t acl_tag_type; 95 96 while (1 == acl_get_entry(acl, entry_id, &acl_entry)) { 97 /* After the first time... */ 98 entry_id = ACL_NEXT_ENTRY; 99 100 /* If this matches, return perm mask */ 101 acl_get_tag_type(acl_entry, &acl_tag_type); 102 if (acl_tag_type == requested_tag_type) { 103 switch (acl_tag_type) { 104 case ACL_USER_OBJ: 105 if ((uid_t)requested_tag == *(uid_t *)(acl_get_qualifier(acl_entry))) { 106 return acl_entry_get_perm(acl_entry); 107 } 108 break; 109 case ACL_GROUP_OBJ: 110 if ((gid_t)requested_tag == *(gid_t *)(acl_get_qualifier(acl_entry))) { 111 return acl_entry_get_perm(acl_entry); 112 } 113 break; 114 case ACL_USER: 115 case ACL_GROUP: 116 case ACL_OTHER: 117 return acl_entry_get_perm(acl_entry); 118 default: 119 failure("Unexpected ACL tag type"); 120 assert(0); 121 } 122 } 123 124 125 } 126 return -1; 127} 128#endif 129 130static int 131#if HAVE_SUN_ACL 132acl_match(aclent_t *aclent, struct archive_test_acl_t *myacl) 133#else 134acl_match(acl_entry_t aclent, struct archive_test_acl_t *myacl) 135#endif 136{ 137#if HAVE_POSIX_ACL 138 gid_t g, *gp; 139 uid_t u, *up; 140 acl_tag_t tag_type; 141#endif 142 143 if (myacl->permset != acl_entry_get_perm(aclent)) 144 return (0); 145 146#if HAVE_SUN_ACL 147 switch (aclent->a_type) 148#else 149 acl_get_tag_type(aclent, &tag_type); 150 switch (tag_type) 151#endif 152 { 153#if HAVE_SUN_ACL 154 case DEF_USER_OBJ: 155 case USER_OBJ: 156#else 157 case ACL_USER_OBJ: 158#endif 159 if (myacl->tag != ARCHIVE_ENTRY_ACL_USER_OBJ) return (0); 160 break; 161#if HAVE_SUN_ACL 162 case DEF_USER: 163 case USER: 164#else 165 case ACL_USER: 166#endif 167 if (myacl->tag != ARCHIVE_ENTRY_ACL_USER) 168 return (0); 169#if HAVE_SUN_ACL 170 if ((uid_t)myacl->qual != aclent->a_id) 171 return (0); 172#else 173 up = acl_get_qualifier(aclent); 174 u = *up; 175 acl_free(up); 176 if ((uid_t)myacl->qual != u) 177 return (0); 178#endif 179 break; 180#if HAVE_SUN_ACL 181 case DEF_GROUP_OBJ: 182 case GROUP_OBJ: 183#else 184 case ACL_GROUP_OBJ: 185#endif 186 if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP_OBJ) return (0); 187 break; 188#if HAVE_SUN_ACL 189 case DEF_GROUP: 190 case GROUP: 191#else 192 case ACL_GROUP: 193#endif 194 if (myacl->tag != ARCHIVE_ENTRY_ACL_GROUP) 195 return (0); 196#if HAVE_SUN_ACL 197 if ((gid_t)myacl->qual != aclent->a_id) 198 return (0); 199#else 200 gp = acl_get_qualifier(aclent); 201 g = *gp; 202 acl_free(gp); 203 if ((gid_t)myacl->qual != g) 204 return (0); 205#endif 206 break; 207#if HAVE_SUN_ACL 208 case DEF_CLASS_OBJ: 209 case CLASS_OBJ: 210#else 211 case ACL_MASK: 212#endif 213 if (myacl->tag != ARCHIVE_ENTRY_ACL_MASK) return (0); 214 break; 215#if HAVE_SUN_ACL 216 case DEF_OTHER_OBJ: 217 case OTHER_OBJ: 218#else 219 case ACL_OTHER: 220#endif 221 if (myacl->tag != ARCHIVE_ENTRY_ACL_OTHER) return (0); 222 break; 223 } 224 return (1); 225} 226 227static void 228#if HAVE_SUN_ACL 229compare_acls(void *aclp, int aclcnt, struct archive_test_acl_t *myacls, int n) 230#else 231compare_acls(acl_t acl, struct archive_test_acl_t *myacls, int n) 232#endif 233{ 234 int *marker; 235 int matched; 236 int i; 237#if HAVE_SUN_ACL 238 int e; 239 aclent_t *acl_entry; 240#else 241 int entry_id = ACL_FIRST_ENTRY; 242 acl_entry_t acl_entry; 243#endif 244 245 /* Count ACL entries in myacls array and allocate an indirect array. */ 246 marker = malloc(sizeof(marker[0]) * n); 247 if (marker == NULL) 248 return; 249 for (i = 0; i < n; i++) 250 marker[i] = i; 251 252 /* 253 * Iterate over acls in system acl object, try to match each 254 * one with an item in the myacls array. 255 */ 256#if HAVE_SUN_ACL 257 for(e = 0; e < aclcnt; e++) { 258 acl_entry = &((aclent_t *)aclp)[e]; 259#else 260 while (1 == acl_get_entry(acl, entry_id, &acl_entry)) { 261 /* After the first time... */ 262 entry_id = ACL_NEXT_ENTRY; 263#endif 264 265 /* Search for a matching entry (tag and qualifier) */ 266 for (i = 0, matched = 0; i < n && !matched; i++) { 267 if (acl_match(acl_entry, &myacls[marker[i]])) { 268 /* We found a match; remove it. */ 269 marker[i] = marker[n - 1]; 270 n--; 271 matched = 1; 272 } 273 } 274 275 /* TODO: Print out more details in this case. */ 276 failure("ACL entry on file that shouldn't be there"); 277 assert(matched == 1); 278 } 279 280 /* Dump entries in the myacls array that weren't in the system acl. */ 281 for (i = 0; i < n; ++i) { 282 failure(" ACL entry missing from file: " 283 "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n", 284 myacls[marker[i]].type, myacls[marker[i]].permset, 285 myacls[marker[i]].tag, myacls[marker[i]].qual, 286 myacls[marker[i]].name); 287 assert(0); /* Record this as a failure. */ 288 } 289 free(marker); 290} 291 292#endif 293 294 295/* 296 * Verify ACL restore-to-disk. This test is Platform-specific. 297 */ 298 299DEFINE_TEST(test_acl_platform_posix1e_restore) 300{ 301#if !HAVE_SUN_ACL && !HAVE_POSIX_ACL 302 skipping("POSIX.1e ACLs are not supported on this platform"); 303#else /* HAVE_SUN_ACL || HAVE_POSIX_ACL */ 304 struct stat st; 305 struct archive *a; 306 struct archive_entry *ae; 307#if HAVE_SUN_ACL 308 void *aclp; 309 int aclcnt; 310#else 311 acl_t acl; 312#endif 313 314 assertMakeFile("pretest", 0644, "a"); 315 316 if (setTestAcl("pretest") != ARCHIVE_TEST_ACL_TYPE_POSIX1E) { 317 skipping("POSIX.1e ACLs are not writable on this filesystem"); 318 return; 319 } 320 321 /* Create a write-to-disk object. */ 322 assert(NULL != (a = archive_write_disk_new())); 323 archive_write_disk_set_options(a, 324 ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL); 325 326 /* Populate an archive entry with some metadata, including ACL info */ 327 ae = archive_entry_new(); 328 assert(ae != NULL); 329 archive_entry_set_pathname(ae, "test0"); 330 archive_entry_set_mtime(ae, 123456, 7890); 331 archive_entry_set_size(ae, 0); 332 assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); 333 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 334 archive_entry_free(ae); 335 336 /* Close the archive. */ 337 assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); 338 assertEqualInt(ARCHIVE_OK, archive_write_free(a)); 339 340 /* Verify the data on disk. */ 341 assertEqualInt(0, stat("test0", &st)); 342 assertEqualInt(st.st_mtime, 123456); 343#if HAVE_SUN_ACL 344 aclp = sunacl_get(GETACL, &aclcnt, 0, "test0"); 345 failure("acl(): errno = %d (%s)", errno, strerror(errno)); 346 assert(aclp != NULL); 347#else 348 acl = acl_get_file("test0", ACL_TYPE_ACCESS); 349 failure("acl_get_file(): errno = %d (%s)", errno, strerror(errno)); 350 assert(acl != (acl_t)NULL); 351#endif 352#if HAVE_SUN_ACL 353 compare_acls(aclp, aclcnt, acls2, sizeof(acls2)/sizeof(acls2[0])); 354 free(aclp); 355 aclp = NULL; 356#else 357 compare_acls(acl, acls2, sizeof(acls2)/sizeof(acls2[0])); 358 acl_free(acl); 359#endif 360 361#endif /* HAVE_SUN_ACL || HAVE_POSIX_ACL */ 362} 363 364/* 365 * Verify ACL read-from-disk. This test is Platform-specific. 366 */ 367DEFINE_TEST(test_acl_platform_posix1e_read) 368{ 369#if !HAVE_SUN_ACL && !HAVE_POSIX_ACL 370 skipping("POSIX.1e ACLs are not supported on this platform"); 371#else 372 struct archive *a; 373 struct archive_entry *ae; 374 int n, fd, flags, dflags; 375 char *func, *acl_text; 376 const char *acl1_text, *acl2_text, *acl3_text; 377#if HAVE_SUN_ACL 378 void *aclp; 379 int aclcnt; 380#else 381 acl_t acl1, acl2, acl3; 382#endif 383 384 /* 385 * Manually construct a directory and two files with 386 * different ACLs. This also serves to verify that ACLs 387 * are supported on the local filesystem. 388 */ 389 390 /* Create a test file f1 with acl1 */ 391#if HAVE_SUN_ACL 392 acl1_text = "user::rwx," 393 "group::rwx," 394 "other:rwx," 395 "user:1:rw-," 396 "group:15:r-x," 397 "mask:rwx"; 398 aclent_t aclp1[] = { 399 { USER_OBJ, -1, 4 | 2 | 1 }, 400 { USER, 1, 4 | 2 }, 401 { GROUP_OBJ, -1, 4 | 2 | 1 }, 402 { GROUP, 15, 4 | 1 }, 403 { CLASS_OBJ, -1, 4 | 2 | 1 }, 404 { OTHER_OBJ, -1, 4 | 2 | 1 } 405 }; 406#else 407 acl1_text = "user::rwx\n" 408 "group::rwx\n" 409 "other::rwx\n" 410 "user:1:rw-\n" 411 "group:15:r-x\n" 412 "mask::rwx"; 413 acl1 = acl_from_text(acl1_text); 414 failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); 415 assert((void *)acl1 != NULL); 416#endif 417 fd = open("f1", O_WRONLY | O_CREAT | O_EXCL, 0777); 418 failure("Could not create test file?!"); 419 if (!assert(fd >= 0)) { 420#if !HAVE_SUN_ACL 421 acl_free(acl1); 422#endif 423 return; 424 } 425#if HAVE_SUN_ACL 426 /* Check if Solaris filesystem supports POSIX.1e ACLs */ 427 aclp = sunacl_get(GETACL, &aclcnt, fd, NULL); 428 if (aclp == 0) 429 close(fd); 430 if (errno == ENOSYS || errno == ENOTSUP) { 431 skipping("POSIX.1e ACLs are not supported on this filesystem"); 432 return; 433 } 434 failure("facl(): errno = %d (%s)", errno, strerror(errno)); 435 assert(aclp != NULL); 436 437 func = "facl()"; 438 n = facl(fd, SETACL, (int)(sizeof(aclp1)/sizeof(aclp1[0])), aclp1); 439#else 440 func = "acl_set_fd()"; 441 n = acl_set_fd(fd, acl1); 442#endif 443#if !HAVE_SUN_ACL 444 acl_free(acl1); 445#endif 446 447 if (n != 0) { 448#if HAVE_SUN_ACL 449 if (errno == ENOSYS || errno == ENOTSUP) 450#else 451 if (errno == EOPNOTSUPP || errno == EINVAL) 452#endif 453 { 454 close(fd); 455 skipping("POSIX.1e ACLs are not supported on this filesystem"); 456 return; 457 } 458 } 459 failure("%s: errno = %d (%s)", func, errno, strerror(errno)); 460 assertEqualInt(0, n); 461 462 close(fd); 463 464 assertMakeDir("d", 0700); 465 466 /* 467 * Create file d/f1 with acl2 468 * 469 * This differs from acl1 in the u:1: and g:15: permissions. 470 * 471 * This file deliberately has the same name but a different ACL. 472 * Github Issue #777 explains how libarchive's directory traversal 473 * did not always correctly enter directories before attempting 474 * to read ACLs, resulting in reading the ACL from a like-named 475 * file in the wrong directory. 476 */ 477#if HAVE_SUN_ACL 478 acl2_text = "user::rwx," 479 "group::rwx," 480 "other:---," 481 "user:1:r--," 482 "group:15:r--," 483 "mask:rwx"; 484 aclent_t aclp2[] = { 485 { USER_OBJ, -1, 4 | 2 | 1 }, 486 { USER, 1, 4 }, 487 { GROUP_OBJ, -1, 4 | 2 | 1}, 488 { GROUP, 15, 4 }, 489 { CLASS_OBJ, -1, 4 | 2 | 1}, 490 { OTHER_OBJ, -1, 0 } 491 }; 492#else 493 acl2_text = "user::rwx\n" 494 "group::rwx\n" 495 "other::---\n" 496 "user:1:r--\n" 497 "group:15:r--\n" 498 "mask::rwx"; 499 acl2 = acl_from_text(acl2_text); 500 failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); 501 assert((void *)acl2 != NULL); 502#endif 503 fd = open("d/f1", O_WRONLY | O_CREAT | O_EXCL, 0777); 504 failure("Could not create test file?!"); 505 if (!assert(fd >= 0)) { 506#if !HAVE_SUN_ACL 507 acl_free(acl2); 508#endif 509 return; 510 } 511#if HAVE_SUN_ACL 512 func = "facl()"; 513 n = facl(fd, SETACL, (int)(sizeof(aclp2) / sizeof(aclp2[0])), aclp2); 514#else 515 func = "acl_set_fd()"; 516 n = acl_set_fd(fd, acl2); 517 acl_free(acl2); 518#endif 519 if (n != 0) 520 close(fd); 521 failure("%s: errno = %d (%s)", func, errno, strerror(errno)); 522 assertEqualInt(0, n); 523 close(fd); 524 525 /* Create nested directory d2 with default ACLs */ 526 assertMakeDir("d/d2", 0755); 527 528#if HAVE_SUN_ACL 529 acl3_text = "user::rwx," 530 "group::r-x," 531 "other:r-x," 532 "user:2:r--," 533 "group:16:-w-," 534 "mask:rwx," 535 "default:user::rwx," 536 "default:user:1:r--," 537 "default:group::r-x," 538 "default:group:15:r--," 539 "default:mask:rwx," 540 "default:other:r-x"; 541 aclent_t aclp3[] = { 542 { USER_OBJ, -1, 4 | 2 | 1 }, 543 { USER, 2, 4 }, 544 { GROUP_OBJ, -1, 4 | 1 }, 545 { GROUP, 16, 2 }, 546 { CLASS_OBJ, -1, 4 | 2 | 1 }, 547 { OTHER_OBJ, -1, 4 | 1 }, 548 { USER_OBJ | ACL_DEFAULT, -1, 4 | 2 | 1 }, 549 { USER | ACL_DEFAULT, 1, 4 }, 550 { GROUP_OBJ | ACL_DEFAULT, -1, 4 | 1 }, 551 { GROUP | ACL_DEFAULT, 15, 4 }, 552 { CLASS_OBJ | ACL_DEFAULT, -1, 4 | 2 | 1}, 553 { OTHER_OBJ | ACL_DEFAULT, -1, 4 | 1 } 554 }; 555#else 556 acl3_text = "user::rwx\n" 557 "user:1:r--\n" 558 "group::r-x\n" 559 "group:15:r--\n" 560 "mask::rwx\n" 561 "other::r-x"; 562 acl3 = acl_from_text(acl3_text); 563 failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); 564 assert((void *)acl3 != NULL); 565#endif 566 567#if HAVE_SUN_ACL 568 func = "acl()"; 569 n = acl("d/d2", SETACL, (int)(sizeof(aclp3) / sizeof(aclp3[0])), aclp3); 570#else 571 func = "acl_set_file()"; 572 n = acl_set_file("d/d2", ACL_TYPE_DEFAULT, acl3); 573 acl_free(acl3); 574#endif 575 failure("%s: errno = %d (%s)", func, errno, strerror(errno)); 576 assertEqualInt(0, n); 577 578 /* Create a read-from-disk object. */ 579 assert(NULL != (a = archive_read_disk_new())); 580 assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, ".")); 581 assert(NULL != (ae = archive_entry_new())); 582 583#if HAVE_SUN_ACL 584 flags = ARCHIVE_ENTRY_ACL_TYPE_POSIX1E 585 | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA 586 | ARCHIVE_ENTRY_ACL_STYLE_SOLARIS; 587 dflags = flags; 588#else 589 flags = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; 590 dflags = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; 591#endif 592 593 /* Walk the dir until we see both of the files */ 594 while (ARCHIVE_OK == archive_read_next_header2(a, ae)) { 595 archive_read_disk_descend(a); 596 if (strcmp(archive_entry_pathname(ae), "./f1") == 0) { 597 acl_text = archive_entry_acl_to_text(ae, NULL, flags); 598 assertEqualString(acl_text, acl1_text); 599 free(acl_text); 600 } else if (strcmp(archive_entry_pathname(ae), "./d/f1") == 0) { 601 acl_text = archive_entry_acl_to_text(ae, NULL, flags); 602 assertEqualString(acl_text, acl2_text); 603 free(acl_text); 604 } else if (strcmp(archive_entry_pathname(ae), "./d/d2") == 0) { 605 acl_text = archive_entry_acl_to_text(ae, NULL, dflags); 606 assertEqualString(acl_text, acl3_text); 607 free(acl_text); 608 } 609 } 610 611 archive_entry_free(ae); 612 assertEqualInt(ARCHIVE_OK, archive_free(a)); 613#endif 614} 615