this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

copyfile-166.40.1

+4929 -2631
-1
src/copyfile/Kernel
··· 1 - ../../kernel-include/
+142 -3
src/copyfile/copyfile.3
··· 1 1 .\" 2 2 .\" Copyright (c) 2002 Apple Computer, Inc. All rights reserved. 3 3 .\" 4 - .Dd April 27, 2006 4 + .Dd July 22, 2019 5 5 .Dt COPYFILE 3 6 6 .Os 7 7 .Sh NAME ··· 178 178 file before starting. (This is only applicable for the 179 179 .Fn copyfile 180 180 function.) 181 + .It Dv COPYFILE_CLONE_FORCE 182 + Clone the file instead. 183 + This is a force flag i.e. if cloning fails, an error is returned. 184 + This flag is equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA 185 + | COPYFILE_NOFOLLOW_SRC). 186 + Note that if cloning is successful, progress callbacks will not be invoked. 187 + Note also that there is no support for cloning directories: if a directory is provided as the source, 188 + an error will be returned. Since this flag implies COPYFILE_NOFOLLOW_SRC, symbolic links themselves will 189 + be cloned instead of their targets. 190 + (This is only applicable for the 191 + .Fn copyfile 192 + function.) 193 + .It Dv COPYFILE_CLONE 194 + Try to clone the file instead. 195 + This is a best try flag i.e. if cloning fails, fallback to copying the file. 196 + This flag is equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA 197 + | COPYFILE_NOFOLLOW_SRC). 198 + Note that if cloning is successful, progress callbacks will not be invoked. 199 + Note also that there is no support for cloning directories: if a directory is provided as the source and 200 + COPYFILE_CLONE_FORCE is not passed, this will instead copy the directory. Since this flag implies COPYFILE_NOFOLLOW_SRC, 201 + symbolic links themselves will be cloned instead of their targets. Recursive copying however is 202 + supported, see below for more information. 203 + (This is only applicable for the 204 + .Fn copyfile 205 + function.) 206 + .It Dv COPYFILE_DATA_SPARSE 207 + Copy a file sparsely. 208 + This requires that the source and destination file systems support sparse files with hole sizes 209 + at least as large as their block sizes. 210 + This also requires that the source file is sparse, and for 211 + .Fn fcopyfile 212 + the source file descriptor's offset be a multiple of the minimum hole size. 213 + If COPYFILE_DATA is also specified, this will fall back to a full copy 214 + if sparse copying cannot be performed for any reason; otherwise, an error is returned. 181 215 .It Dv COPYFILE_NOFOLLOW 182 216 This is a convenience macro, equivalent to 183 - .Dv (COPYFILE_NOFOLLOW_DST|COPYFILE_NOFOLLOW_SRC) . 217 + .Dv (COPYFILE_NOFOLLOW_DST | COPYFILE_NOFOLLOW_SRC) . 218 + .It Dv COPYFILE_RUN_IN_PLACE 219 + If the src file has quarantine information, add the QTN_FLAG_DO_NOT_TRANSLOCATE flag to the quarantine information of the dst file. This allows a bundle to run in place instead of being translocated. 220 + .It Dv COPYFILE_PRESERVE_DST_TRACKED 221 + Preserve the UF_TRACKED flag at 222 + .Va to 223 + when copying metadata, regardless of whether 224 + .Va from 225 + has it set. This flag is used in conjunction with COPYFILE_STAT, or COPYFILE_CLONE (for its fallback case). 184 226 .El 227 + .Pp 228 + Copying files into a directory is supported. If 229 + .Va to 230 + is a directory, 231 + .Va from 232 + will be copied into 233 + .Va to 234 + (if 235 + .Va from 236 + is a directory, 237 + copying its contents requires use of the COPYFILE_RECURSIVE parameter, 238 + which is documented below). 185 239 .Pp 186 240 The 187 241 .Fn copyfile_state_get ··· 264 318 (Only valid for 265 319 .Fn copyfile_state_get ; 266 320 see below for more details about callbacks.) 321 + If a 322 + .Dv COPYFILE_CLONE 323 + or 324 + .Dv COPYFILE_CLONE_FORCE 325 + operation successfully cloned the requested objects, then this value will be 0. 267 326 The 268 327 .Va dst 269 328 parameter is a pointer to ··· 277 336 (see below for details). This field cannot be set, 278 337 and may be 279 338 .Dv NULL . 339 + .It Dv COPYFILE_STATE_WAS_CLONED 340 + True if a 341 + .Dv COPYFILE_CLONE 342 + or 343 + .Dv COPYFILE_CLONE_FORCE 344 + operation successfully cloned the requested objects. 345 + The 346 + .Va dst 347 + parameter is a pointer to 348 + .Vt bool 349 + (type 350 + .Vt bool\ * ). 280 351 .El 281 352 .Sh Recursive Copies 282 353 When given the ··· 399 470 .Dv errno 400 471 value will be set appropriately. 401 472 .Pp 473 + Note that recursive cloning is also supported with the 474 + .Dv COPYFILE_CLONE 475 + flag (but not the 476 + .Dv COPYFILE_CLONE_FORCE 477 + flag). A recursive clone operation invokes 478 + .Fn copyfile 479 + with 480 + .Dv COPYFILE_CLONE 481 + on every entry found in the source file-system object. Because 482 + .Fn copyfile 483 + does not allow the cloning of directories, a recursive clone will 484 + instead copy any directory it finds (while cloning its contents). 485 + As symbolic links may point to directories, they are not followed 486 + during recursive clones even if the source is a symbolic link. 487 + Additionally, because the 488 + .Dv COPYFILE_CLONE 489 + flag implies the 490 + .Dv COPYFILE_EXCL 491 + flag, recursive clones require a nonexistent destination. 492 + .Pp 402 493 The 403 494 .Dv COPYFILE_PACK , 404 495 .Dv COPYFILE_UNPACK , ··· 407 498 .Dv COPYFILE_UNLINK 408 499 flags are not used during a recursive copy, and will result 409 500 in an error being returned. 501 + .Pp 502 + Note that if the source path ends in a 503 + .Va / 504 + its contents are copied rather than the directory itself (like cp(1)). 505 + The behavior of a recursive copy on a directory hierarchy also depends 506 + on the contents of the destination. If the destination is a directory, 507 + the source directory (or its contents, if the source path ends in a 508 + .Va / 509 + ) will be copied into it. If the destination exists but is not a 510 + directory, and the source is a non-empty directory, the copy will fail; 511 + the exact error set depends on the flags provided to 512 + .Fn copyfile 513 + initially. 410 514 .Sh Progress Callback 411 515 In addition to the recursive callbacks described above, 412 516 .Fn copyfile ··· 485 589 .Dv NULL 486 590 in the case of 487 591 .Fn fcopyfile . 592 + .Pp 593 + Note that progress callbacks are not invoked when a clone is requested 594 + (e.g. 595 + .Dv COPYFILE_CLONE ) 596 + unless the clone cannot be performed and a copy is performed instead. 488 597 .Sh RETURN VALUES 489 598 Except when given the 490 599 .Dv COPYFILE_CHECK ··· 537 646 or 538 647 .Va to 539 648 parameter to 540 - .Fn copyfile 649 + .Fn fcopyfile 541 650 was a negative number. 542 651 .It Bq Er ENOMEM 543 652 A memory allocation failed. 544 653 .It Bq Er ENOTSUP 545 654 The source file was not a directory, symbolic link, or regular file. 655 + .It Bq Er ENOTSUP 656 + COPYFILE_CLONE_FORCE was specified and file cloning is not supported. 657 + .It Bq Er ENOTSUP 658 + COPYFILE_DATA_SPARSE was specified, sparse copying is not supported, 659 + and COPYFILE_DATA was not specified. 546 660 .It Bq Er ECANCELED 547 661 The copy was cancelled by callback. 662 + .It Bq Er EEXIST 663 + The 664 + .Va to 665 + parameter to 666 + .Fn copyfile 667 + already existed and was passed in with 668 + .Dv COPYFILE_EXCL . 669 + .It Bq Er ENOENT 670 + The 671 + .Va from 672 + parameter to 673 + .Fn copyfile 674 + did not exist. 675 + .It Bq Er EACCES 676 + Search permission is denied for a component of the path prefix for 677 + the 678 + .Va from 679 + or 680 + .Va to 681 + parameters. 682 + .It Bq Er EACCES 683 + Write permission is denied for a component of the path prefix for the 684 + .Va to 685 + parameter. 548 686 .El 687 + .Pp 549 688 In addition, both functions may set 550 689 .Dv errno 551 690 via an underlying library or system call.
+3236 -2580
src/copyfile/copyfile.c
··· 1 - // Modified by Lubos Dolezel for Darling 2 1 /* 3 - * Copyright (c) 2004-2010 Apple, Inc. All rights reserved. 2 + * Copyright (c) 2004-2019 Apple, Inc. All rights reserved. 4 3 * 5 4 * @APPLE_LICENSE_HEADER_START@ 6 - * 5 + * 7 6 * This file contains Original Code and/or Modifications of Original Code 8 7 * as defined in and that are subject to the Apple Public Source License 9 8 * Version 2.0 (the 'License'). You may not use this file except in 10 9 * compliance with the License. Please obtain a copy of the License at 11 10 * http://www.opensource.apple.com/apsl/ and read it before using this 12 11 * file. 13 - * 12 + * 14 13 * The Original Code and all software distributed under the License are 15 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ··· 18 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 19 18 * Please see the License for the specific language governing rights and 20 19 * limitations under the License. 21 - * 20 + * 22 21 * @APPLE_LICENSE_HEADER_END@ 23 22 */ 24 23 ··· 46 45 #include <membership.h> 47 46 #include <fts.h> 48 47 #include <libgen.h> 48 + #include <sys/clonefile.h> 49 + #include <System/sys/content_protection.h> 49 50 50 51 #ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION 51 52 # include <Kernel/sys/decmpfs.h> 52 53 #endif 53 54 54 55 #include <TargetConditionals.h> 55 - #if !TARGET_OS_IPHONE && !defined(DARLING) 56 + #if !TARGET_OS_IPHONE 56 57 #include <quarantine.h> 57 58 58 59 #define XATTR_QUARANTINE_NAME qtn_xattr_name ··· 60 61 #define qtn_file_t void * 61 62 #define QTN_SERIALIZED_DATA_MAX 0 62 63 static void * qtn_file_alloc(void) { return NULL; } 63 - static int qtn_file_init_with_fd(void *x, int y) { return -1; } 64 - static int qtn_file_init_with_path(void *x, const char *path) { return -1; } 65 - static int qtn_file_init_with_data(void *x, const void *data, size_t len) { return -1; } 66 - static void qtn_file_free(void *x) { return; } 67 - static int qtn_file_apply_to_fd(void *x, int y) { return 0; } 68 - static char *qtn_error(int x) { return NULL; } 69 - static int qtn_file_to_data(void *x, char *y, size_t z) { return -1; } 70 - static void *qtn_file_clone(void *x) { return NULL; } 64 + static int qtn_file_init_with_fd(__unused void *x, __unused int y) { return -1; } 65 + static int qtn_file_init_with_path(__unused void *x, __unused const char *path) { return -1; } 66 + static int qtn_file_init_with_data(__unused void *x, __unused const void *data, __unused size_t len) { return -1; } 67 + static void qtn_file_free(__unused void *x) { return; } 68 + static int qtn_file_apply_to_fd(__unused void *x, __unused int y) { return 0; } 69 + static char *qtn_error(__unused int x) { return NULL; } 70 + static int qtn_file_to_data(__unused void *x, __unused char *y, __unused size_t *z) { return -1; } 71 + static void *qtn_file_clone(__unused void *x) { return NULL; } 72 + static uint32_t qtn_file_get_flags(__unused void *x) { return 0; } 73 + static int qtn_file_set_flags(__unused void *x, __unused uint32_t flags) { return 0; } 71 74 #define XATTR_QUARANTINE_NAME "figgledidiggledy" 75 + #define QTN_FLAG_DO_NOT_TRANSLOCATE 0 72 76 #endif /* TARGET_OS_IPHONE */ 73 77 74 78 #include "copyfile.h" 75 79 #include "copyfile_private.h" 76 80 #include "xattr_flags.h" 77 81 78 - #ifdef DARLING 79 - # define xattr_preserve_for_intent(x,y) (0) 80 - #endif 82 + #define XATTR_ROOT_INSTALLED_NAME "com.apple.root.installed" 81 83 82 84 enum cfInternalFlags { 83 - cfDelayAce = 1 << 0, 84 - cfMakeFileInvisible = 1 << 1, 85 - cfSawDecmpEA = 1 << 2, 85 + cfDelayAce = 1 << 0, /* set if ACE shouldn't be set until post-order traversal */ 86 + cfMakeFileInvisible = 1 << 1, /* set if kFinderInvisibleMask is on src */ 87 + cfSawDecmpEA = 1 << 2, /* set if we've seen a com.apple.decmpfs xattr */ 88 + cfSrcProtSupportValid = 1 << 3, /* set if cfSrcSupportsCProtect is valid */ 89 + cfSrcSupportsCProtect = 1 << 4, /* set if src supports MNT_CPROTECT */ 90 + cfDstProtSupportValid = 1 << 5, /* set if cfDstSupportsCProtect is valid */ 91 + cfDstSupportsCProtect = 1 << 6, /* set if dst supports MNT_CPROTECT */ 86 92 }; 87 93 94 + #define COPYFILE_MNT_CPROTECT_MASK (cfSrcProtSupportValid | cfSrcSupportsCProtect | cfDstProtSupportValid | cfDstSupportsCProtect) 95 + 88 96 /* 89 97 * The state structure keeps track of 90 98 * the source filename, the destination filename, their 91 - * associated file-descriptors, the stat infomration for the 99 + * associated file-descriptors, the stat information for the 92 100 * source file, the security information for the source file, 93 101 * the flags passed in for the copy, a pointer to place statistics 94 102 * (not currently implemented), debug flags, and a pointer to callbacks ··· 96 104 */ 97 105 struct _copyfile_state 98 106 { 99 - char *src; 100 - char *dst; 101 - int src_fd; 102 - int dst_fd; 103 - struct stat sb; 104 - filesec_t fsec; 105 - copyfile_flags_t flags; 106 - unsigned int internal_flags; 107 - void *stats; 108 - uint32_t debug; 109 - copyfile_callback_t statuscb; 110 - void *ctx; 111 - qtn_file_t qinfo; /* Quarantine information -- probably NULL */ 112 - filesec_t original_fsec; 113 - filesec_t permissive_fsec; 114 - off_t totalCopied; 115 - int err; 116 - char *xattr_name; 117 - xattr_operation_intent_t copyIntent; 107 + char *src; 108 + char *dst; 109 + int src_fd; 110 + int dst_fd; 111 + struct stat sb; 112 + filesec_t fsec; 113 + copyfile_flags_t flags; 114 + unsigned int internal_flags; 115 + void *stats; 116 + uint32_t debug; 117 + copyfile_callback_t statuscb; 118 + void *ctx; 119 + qtn_file_t qinfo; /* Quarantine information -- probably NULL */ 120 + filesec_t original_fsec; 121 + filesec_t permissive_fsec; 122 + off_t totalCopied; 123 + int err; 124 + char *xattr_name; 125 + xattr_operation_intent_t copyIntent; 126 + bool was_cloned; 118 127 }; 119 128 129 + #define GET_PROT_CLASS(fd) fcntl((fd), F_GETPROTECTIONCLASS) 130 + #define SET_PROT_CLASS(fd, prot_class) fcntl((fd), F_SETPROTECTIONCLASS, (prot_class)) 131 + 120 132 struct acl_entry { 121 - u_int32_t ae_magic; 133 + u_int32_t ae_magic; 122 134 #define _ACL_ENTRY_MAGIC 0xac1ac101 123 - u_int32_t ae_tag; 124 - guid_t ae_applicable; 125 - u_int32_t ae_flags; 126 - u_int32_t ae_perms; 135 + u_int32_t ae_tag; 136 + guid_t ae_applicable; 137 + u_int32_t ae_flags; 138 + u_int32_t ae_perms; 127 139 }; 128 140 129 - #define PACE(ace) do { \ 130 - struct acl_entry *__t = (struct acl_entry*)(ace); \ 131 - fprintf(stderr, "%s(%d): " #ace " = { flags = %#x, perms = %#x }\n", __FUNCTION__, __LINE__, __t->ae_flags, __t->ae_perms); \ 141 + #define PACE(ace) \ 142 + do { \ 143 + struct acl_entry *__t = (struct acl_entry*)(ace); \ 144 + fprintf(stderr, "%s(%d): " #ace " = { flags = %#x, perms = %#x }\n", __FUNCTION__, __LINE__, __t->ae_flags, __t->ae_perms); \ 132 145 } while (0) 133 146 134 147 #define PACL(ace) \ ··· 144 157 ps1 = (struct pm*) p1; 145 158 ps2 = (struct pm*) p2; 146 159 147 - return ((ps1->ap_perms == ps2->ap_perms) ? 1 : 0); 160 + return ((ps1->ap_perms == ps2->ap_perms) ? 1 : 0); 148 161 } 149 162 150 163 ··· 178 191 return 0; 179 192 } 180 193 194 + static int 195 + does_copy_protection(int fd) 196 + { 197 + struct statfs sfs; 198 + 199 + if (fstatfs(fd, &sfs) == -1) 200 + return -1; 201 + 202 + return ((sfs.f_flags & MNT_CPROTECT) == MNT_CPROTECT); 203 + } 181 204 182 205 static void 183 206 sort_xattrname_list(void *start, size_t length) ··· 203 226 goto done; 204 227 205 228 #ifdef DEBUG 206 - { 207 - char *curPtr = start; 208 - while (curPtr < (char*)start + length) { 209 - printf("%s\n", curPtr); 210 - curPtr += strlen(curPtr) + 1; 229 + { 230 + char *curPtr = start; 231 + while (curPtr < (char*)start + length) { 232 + printf("%s\n", curPtr); 233 + curPtr += strlen(curPtr) + 1; 234 + } 211 235 } 212 - } 213 236 #endif 214 237 215 238 tmp = ptrs[indx++] = (char*)start; 216 239 217 - while (tmp = memchr(tmp, 0, ((char*)start + length) - tmp)) { 240 + while ((tmp = memchr(tmp, 0, ((char*)start + length) - tmp))) { 218 241 if (indx == nel) { 219 242 nel += 10; 220 243 ptrs = realloc(ptrs, sizeof(char**) * nel); ··· 289 312 #define COPYFILE_DEBUG (1<<31) 290 313 #define COPYFILE_DEBUG_VAR "COPYFILE_DEBUG" 291 314 315 + // These macros preserve the value of errno. 292 316 #ifndef _COPYFILE_TEST 293 - # define copyfile_warn(str, ...) syslog(LOG_WARNING, str ": %m", ## __VA_ARGS__) 317 + # define copyfile_warn(str, ...) \ 318 + do { \ 319 + errno_t _errsv = errno; \ 320 + syslog(LOG_WARNING, str ": %m", ## __VA_ARGS__); \ 321 + errno = _errsv; \ 322 + } while (0) 294 323 # define copyfile_debug(d, str, ...) \ 295 - do { \ 296 - if (s && (d <= s->debug)) {\ 297 - syslog(LOG_DEBUG, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \ 298 - } \ 299 - } while (0) 324 + do { \ 325 + if (s && (d <= s->debug)) {\ 326 + errno_t _errsv = errno; \ 327 + syslog(LOG_DEBUG, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \ 328 + errno = _errsv; \ 329 + } \ 330 + } while (0) 300 331 #else 301 332 #define copyfile_warn(str, ...) \ 302 - fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : "") 333 + do { \ 334 + errno_t _errsv = errno; \ 335 + fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : ""); \ 336 + errno = _errsv; \ 337 + } while (0) 303 338 # define copyfile_debug(d, str, ...) \ 304 - do { \ 305 - if (s && (d <= s->debug)) {\ 306 - fprintf(stderr, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \ 307 - } \ 308 - } while(0) 339 + do { \ 340 + if (s && (d <= s->debug)) {\ 341 + errno_t _errsv = errno; \ 342 + fprintf(stderr, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \ 343 + errno = _errsv; \ 344 + } \ 345 + } while(0) 309 346 #endif 310 347 311 - #ifndef DARLING 312 348 static int copyfile_quarantine(copyfile_state_t s) 313 349 { 314 - int rv = 0; 315 - if (s->qinfo == NULL) 316 - { 317 - int error; 318 - s->qinfo = qtn_file_alloc(); 350 + int rv = 0; 319 351 if (s->qinfo == NULL) 320 352 { 321 - rv = -1; 322 - goto done; 353 + int error; 354 + s->qinfo = qtn_file_alloc(); 355 + if (s->qinfo == NULL) 356 + { 357 + rv = -1; 358 + goto done; 359 + } 360 + if ((error = qtn_file_init_with_fd(s->qinfo, s->src_fd)) != 0) 361 + { 362 + qtn_file_free(s->qinfo); 363 + s->qinfo = NULL; 364 + rv = -1; 365 + goto done; 366 + } 323 367 } 324 - if ((error = qtn_file_init_with_fd(s->qinfo, s->src_fd)) != 0) 325 - { 326 - qtn_file_free(s->qinfo); 327 - s->qinfo = NULL; 328 - rv = -1; 329 - goto done; 330 - } 331 - } 332 368 done: 333 - return rv; 369 + return rv; 334 370 } 335 - #else 336 - static int copyfile_quarantine(copyfile_state_t s) { return 0; } 337 - #endif 338 371 339 372 static int 340 373 add_uberace(acl_t *acl) ··· 500 533 static void 501 534 reset_security(copyfile_state_t s) 502 535 { 503 - /* If we haven't reset the file security information 504 - * (COPYFILE_SECURITY is not set in flags) 505 - * restore back the permissions the file had originally 506 - * 507 - * One of the reasons this seems so complicated is that 508 - * it is partially at odds with copyfile_security(). 509 - * 510 - * Simplisticly, we are simply trying to make sure we 511 - * only copy what was requested, and that we don't stomp 512 - * on what wasn't requested. 513 - */ 536 + /* If we haven't reset the file security information 537 + * (COPYFILE_SECURITY is not set in flags) 538 + * restore back the permissions the file had originally 539 + * 540 + * One of the reasons this seems so complicated is that 541 + * it is partially at odds with copyfile_security(). 542 + * 543 + * Simplisticly, we are simply trying to make sure we 544 + * only copy what was requested, and that we don't stomp 545 + * on what wasn't requested. 546 + */ 514 547 515 548 #ifdef COPYFILE_RECURSIVE 516 549 if (s->dst_fd > -1) { ··· 520 553 fstat(s->src_fd, &sbuf); 521 554 else 522 555 fstat(s->dst_fd, &sbuf); 523 - 556 + 524 557 if (!(s->internal_flags & cfDelayAce)) 525 558 remove_uberace(s->dst_fd, &sbuf); 526 559 } 527 560 #else 528 - if (s->permissive_fsec && (s->flags & COPYFILE_SECURITY) != COPYFILE_SECURITY) { 529 - if (s->flags & COPYFILE_ACL) { 530 - /* Just need to reset the BSD information -- mode, owner, group */ 531 - (void)fchown(s->dst_fd, s->dst_sb.st_uid, s->dst_sb.st_gid); 532 - (void)fchmod(s->dst_fd, s->dst_sb.st_mode); 533 - } else { 534 - /* 535 - * flags is either COPYFILE_STAT, or neither; if it's 536 - * neither, then we restore both ACL and POSIX permissions; 537 - * if it's STAT, however, then we only want to restore the 538 - * ACL (which may be empty). We do that by removing the 539 - * POSIX information from the filesec object. 540 - */ 541 - if (s->flags & COPYFILE_STAT) { 542 - copyfile_unset_posix_fsec(s->original_fsec); 561 + if (s->permissive_fsec && (s->flags & COPYFILE_SECURITY) != COPYFILE_SECURITY) { 562 + if (s->flags & COPYFILE_ACL) { 563 + /* Just need to reset the BSD information -- mode, owner, group */ 564 + (void)fchown(s->dst_fd, s->dst_sb.st_uid, s->dst_sb.st_gid); 565 + (void)fchmod(s->dst_fd, s->dst_sb.st_mode); 566 + } else { 567 + /* 568 + * flags is either COPYFILE_STAT, or neither; if it's 569 + * neither, then we restore both ACL and POSIX permissions; 570 + * if it's STAT, however, then we only want to restore the 571 + * ACL (which may be empty). We do that by removing the 572 + * POSIX information from the filesec object. 573 + */ 574 + if (s->flags & COPYFILE_STAT) { 575 + copyfile_unset_posix_fsec(s->original_fsec); 576 + } 577 + if (fchmodx_np(s->dst_fd, s->original_fsec) < 0 && errno != ENOTSUP) 578 + copyfile_warn("restoring security information"); 543 579 } 544 - if (fchmodx_np(s->dst_fd, s->original_fsec) < 0 && errno != ENOTSUP) 545 - copyfile_warn("restoring security information"); 546 580 } 547 - } 548 581 549 - if (s->permissive_fsec) { 550 - filesec_free(s->permissive_fsec); 551 - s->permissive_fsec = NULL; 552 - } 582 + if (s->permissive_fsec) { 583 + filesec_free(s->permissive_fsec); 584 + s->permissive_fsec = NULL; 585 + } 553 586 554 - if (s->original_fsec) { 555 - filesec_free(s->original_fsec); 556 - s->original_fsec = NULL; 557 - } 587 + if (s->original_fsec) { 588 + filesec_free(s->original_fsec); 589 + s->original_fsec = NULL; 590 + } 558 591 #endif 559 592 560 - return; 593 + return; 561 594 } 562 595 563 596 /* ··· 575 608 * 576 609 * copytree() is called from copyfile() -- but copytree() itself then calls 577 610 * copyfile() to copy each individual object. 611 + * 612 + * If COPYFILE_CLONE is passed, copytree() will clone (instead of copy) 613 + * regular files and symbolic links found in each directory. 614 + * Directories will still be copied normally. 578 615 * 579 616 * XXX - no effort is made to handle overlapping hierarchies at the moment. 580 617 * ··· 601 638 const char *paths[2] = { 0 }; 602 639 unsigned int flags = 0; 603 640 int fts_flags = FTS_NOCHDIR; 641 + dev_t last_dev = s->sb.st_dev; 604 642 605 643 if (s == NULL) { 606 644 errno = EINVAL; 607 645 retval = -1; 608 646 goto done; 609 647 } 610 - if (s->flags & (COPYFILE_MOVE | COPYFILE_UNLINK | COPYFILE_CHECK | COPYFILE_PACK | COPYFILE_UNPACK)) { 648 + if (s->flags & (COPYFILE_MOVE | COPYFILE_UNLINK | COPYFILE_CHECK | COPYFILE_PACK | COPYFILE_UNPACK | COPYFILE_CLONE_FORCE)) { 611 649 errno = EINVAL; 612 650 retval = -1; 613 651 goto done; 614 652 } 615 653 616 - flags = s->flags & (COPYFILE_ALL | COPYFILE_NOFOLLOW | COPYFILE_VERBOSE); 654 + flags = s->flags & (COPYFILE_ALL | COPYFILE_NOFOLLOW | COPYFILE_VERBOSE | COPYFILE_EXCL | COPYFILE_CLONE | COPYFILE_DATA_SPARSE); 617 655 618 656 paths[0] = src = s->src; 619 657 dst = s->dst; ··· 683 721 * 6) src is a directory, dst does not exist 684 722 * 685 723 * (1) copies src to dst/basename(src). 686 - * (2) fails if COPYFILE_EXCLUSIVE is set, otherwise copies src to dst. 724 + * (2) fails if COPYFILE_EXCL is set, otherwise copies src to dst. 687 725 * (3) and (6) copy src to the name dst. 688 726 * (4) copies the contents of src to the contents of dst. 689 727 * (5) is an error. ··· 705 743 offset = strlen(src); 706 744 } 707 745 708 - if (s->flags | COPYFILE_NOFOLLOW_SRC) 709 - fts_flags |= FTS_PHYSICAL; 710 - else 711 - fts_flags |= FTS_LOGICAL; 746 + // COPYFILE_RECURSIVE is always done physically: see 11717978. 747 + fts_flags |= FTS_PHYSICAL; 748 + if (!(s->flags & (COPYFILE_NOFOLLOW_SRC|COPYFILE_CLONE))) { 749 + // Follow 'src', even if it's a symlink, unless instructed not to 750 + // or we're cloning, where we never follow symlinks. 751 + fts_flags |= FTS_COMFOLLOW; 752 + } 712 753 713 754 fts = fts_open((char * const *)paths, fts_flags, NULL); 714 755 ··· 725 766 } 726 767 tstate->statuscb = s->statuscb; 727 768 tstate->ctx = s->ctx; 769 + if (last_dev == ftsent->fts_dev) { 770 + tstate->internal_flags |= (s->internal_flags & COPYFILE_MNT_CPROTECT_MASK); 771 + } else { 772 + last_dev = ftsent->fts_dev; 773 + } 728 774 asprintf(&dstfile, "%s%s%s", dst, dstpathsep, ftsent->fts_path + offset); 729 775 if (dstfile == NULL) { 730 776 copyfile_state_free(tstate); ··· 733 779 break; 734 780 } 735 781 switch (ftsent->fts_info) { 736 - case FTS_D: 737 - tstate->internal_flags |= cfDelayAce; 738 - cmd = COPYFILE_RECURSE_DIR; 739 - break; 740 - case FTS_SL: 741 - case FTS_SLNONE: 742 - case FTS_DEFAULT: 743 - case FTS_F: 744 - cmd = COPYFILE_RECURSE_FILE; 745 - break; 746 - case FTS_DP: 747 - cmd = COPYFILE_RECURSE_DIR_CLEANUP; 748 - break; 749 - case FTS_DNR: 750 - case FTS_ERR: 751 - case FTS_NS: 752 - case FTS_NSOK: 753 - default: 754 - errno = ftsent->fts_errno; 755 - if (status) { 756 - rv = (*status)(COPYFILE_RECURSE_ERROR, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx); 757 - if (rv == COPYFILE_SKIP || rv == COPYFILE_CONTINUE) { 758 - errno = 0; 759 - goto skipit; 760 - } 761 - if (rv == COPYFILE_QUIT) { 782 + case FTS_D: 783 + tstate->internal_flags |= cfDelayAce; 784 + cmd = COPYFILE_RECURSE_DIR; 785 + break; 786 + case FTS_SL: 787 + case FTS_SLNONE: 788 + case FTS_DEFAULT: 789 + case FTS_F: 790 + cmd = COPYFILE_RECURSE_FILE; 791 + break; 792 + case FTS_DP: 793 + cmd = COPYFILE_RECURSE_DIR_CLEANUP; 794 + break; 795 + case FTS_DNR: 796 + case FTS_ERR: 797 + case FTS_NS: 798 + case FTS_NSOK: 799 + default: 800 + errno = ftsent->fts_errno; 801 + if (status) { 802 + rv = (*status)(COPYFILE_RECURSE_ERROR, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx); 803 + if (rv == COPYFILE_SKIP || rv == COPYFILE_CONTINUE) { 804 + errno = 0; 805 + goto skipit; 806 + } 807 + if (rv == COPYFILE_QUIT) { 808 + retval = -1; 809 + goto stopit; 810 + } 811 + } else { 762 812 retval = -1; 763 813 goto stopit; 764 814 } 765 - } else { 766 - retval = -1; 767 - goto stopit; 768 - } 769 - case FTS_DOT: 770 - goto skipit; 815 + case FTS_DOT: 816 + goto skipit; 771 817 772 818 } 773 819 ··· 790 836 goto stopit; 791 837 } 792 838 } 839 + // Since we don't support cloning directories this code depends on copyfile() 840 + // falling back to a regular directory copy. 793 841 int tmp_flags = (cmd == COPYFILE_RECURSE_DIR) ? (flags & ~COPYFILE_STAT) : flags; 794 842 rv = copyfile(ftsent->fts_path, dstfile, tstate, tmp_flags); 795 843 if (rv < 0) { ··· 853 901 854 902 rv = 0; 855 903 } 856 - skipit: 857 - stopit: 904 + skipit: 905 + stopit: 906 + s->internal_flags &= ~COPYFILE_MNT_CPROTECT_MASK; 907 + s->internal_flags |= (tstate->internal_flags & COPYFILE_MNT_CPROTECT_MASK); 908 + 858 909 copyfile_state_free(tstate); 859 910 free(dstfile); 860 911 if (retval == -1) ··· 865 916 if (fts) 866 917 fts_close(fts); 867 918 919 + copyfile_debug(1, "returning: %d errno %d\n", retval, errno); 868 920 return retval; 869 921 } 870 922 ··· 876 928 */ 877 929 int fcopyfile(int src_fd, int dst_fd, copyfile_state_t state, copyfile_flags_t flags) 878 930 { 879 - int ret = 0; 880 - copyfile_state_t s = state; 881 - struct stat dst_sb; 931 + int ret = 0; 932 + copyfile_state_t s = state; 933 + struct stat dst_sb; 882 934 883 - if (src_fd < 0 || dst_fd < 0) 884 - { 885 - errno = EINVAL; 886 - return -1; 887 - } 935 + if (src_fd < 0 || dst_fd < 0) 936 + { 937 + errno = EINVAL; 938 + return -1; 939 + } 888 940 889 - if (copyfile_preamble(&s, flags) < 0) 890 - return -1; 941 + if (copyfile_preamble(&s, flags) < 0) 942 + return -1; 891 943 892 - copyfile_debug(2, "set src_fd <- %d", src_fd); 893 - if (s->src_fd == -2 && src_fd > -1) 894 - { 895 - s->src_fd = src_fd; 896 - if (fstatx_np(s->src_fd, &s->sb, s->fsec) != 0) 944 + copyfile_debug(2, "set src_fd <- %d", src_fd); 945 + if (s->src_fd == -2 && src_fd > -1) 897 946 { 898 - if (errno == ENOTSUP || errno == EPERM) 899 - fstat(s->src_fd, &s->sb); 900 - else 901 - { 902 - copyfile_warn("fstatx_np on src fd %d", s->src_fd); 903 - return -1; 904 - } 947 + s->src_fd = src_fd; 948 + if (fstatx_np(s->src_fd, &s->sb, s->fsec) != 0) 949 + { 950 + if (errno == ENOTSUP || errno == EPERM) 951 + fstat(s->src_fd, &s->sb); 952 + else 953 + { 954 + copyfile_warn("fstatx_np on src fd %d", s->src_fd); 955 + return -1; 956 + } 957 + } 905 958 } 906 - } 907 959 908 - /* prevent copying on unsupported types */ 909 - switch (s->sb.st_mode & S_IFMT) 910 - { 911 - case S_IFLNK: 912 - case S_IFDIR: 913 - case S_IFREG: 914 - break; 915 - default: 916 - errno = ENOTSUP; 917 - return -1; 918 - } 960 + /* prevent copying on unsupported types */ 961 + switch (s->sb.st_mode & S_IFMT) 962 + { 963 + case S_IFLNK: 964 + case S_IFDIR: 965 + case S_IFREG: 966 + break; 967 + default: 968 + errno = ENOTSUP; 969 + return -1; 970 + } 919 971 920 - copyfile_debug(2, "set dst_fd <- %d", dst_fd); 921 - if (s->dst_fd == -2 && dst_fd > -1) 922 - s->dst_fd = dst_fd; 972 + copyfile_debug(2, "set dst_fd <- %d", dst_fd); 973 + if (s->dst_fd == -2 && dst_fd > -1) 974 + s->dst_fd = dst_fd; 975 + 976 + (void)fstat(s->dst_fd, &dst_sb); 977 + (void)fchmod(s->dst_fd, (dst_sb.st_mode & ~S_IFMT) | (S_IRUSR | S_IWUSR)); 978 + 979 + (void)copyfile_quarantine(s); 923 980 924 - (void)fstat(s->dst_fd, &dst_sb); 925 - (void)fchmod(s->dst_fd, (dst_sb.st_mode & ~S_IFMT) | (S_IRUSR | S_IWUSR)); 981 + ret = copyfile_internal(s, flags); 926 982 927 - (void)copyfile_quarantine(s); 983 + if (ret >= 0 && !(s->flags & COPYFILE_STAT)) 984 + { 985 + (void)fchmod(s->dst_fd, dst_sb.st_mode & ~S_IFMT); 986 + } 928 987 929 - ret = copyfile_internal(s, flags); 988 + if (s->err) { 989 + errno = s->err; 990 + s->err = 0; 991 + } 992 + if (state == NULL) { 993 + int t = errno; 994 + copyfile_state_free(s); 995 + errno = t; 996 + } 997 + if (ret >= 0) { 998 + errno = 0; 999 + } 1000 + 1001 + return ret; 1002 + } 1003 + 1004 + /* 1005 + * This routine implements the clonefileat functionality 1006 + * for copyfile. There are 2 kinds of clone flags, namely 1007 + * 1. COPYFILE_CLONE_FORCE which is a 'force' clone flag. 1008 + * 2. COPYFILE_CLONE which is a 'best try' flag. 1009 + * In both cases, we inherit the flags provided 1010 + * to copyfile call and clone the file. 1011 + * Both these flags are equivalent to 1012 + * (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA) 1013 + * With force clone flag set, we return failure if cloning fails, 1014 + * however, in case of best try flag, we fallback to the copy method. 1015 + */ 930 1016 931 - if (ret >= 0 && !(s->flags & COPYFILE_STAT)) 932 - { 933 - (void)fchmod(s->dst_fd, dst_sb.st_mode & ~S_IFMT); 934 - } 1017 + static int copyfile_clone(copyfile_state_t state) 1018 + { 1019 + int ret = 0; 1020 + // Since we don't allow cloning of directories, we must also forbid 1021 + // cloning the target of symlinks (since that may be a directory). 1022 + int cloneFlags = CLONE_NOFOLLOW; 1023 + struct stat src_sb; 935 1024 936 - if (s->err) { 937 - errno = s->err; 938 - s->err = 0; 939 - } 940 - if (state == NULL) { 941 - int t = errno; 942 - copyfile_state_free(s); 943 - errno = t; 944 - } 1025 + if (lstat(state->src, &src_sb) != 0) 1026 + { 1027 + errno = EINVAL; 1028 + return -1; 1029 + } 945 1030 946 - return ret; 1031 + /* 1032 + * Support only for files and symbolic links. 1033 + * TODO:Remove this check when support for directories is added. 1034 + */ 1035 + if (S_ISREG(src_sb.st_mode) || S_ISLNK(src_sb.st_mode)) 1036 + { 1037 + /* 1038 + * COPYFILE_UNLINK tells us to try removing the destination 1039 + * before we create it. We don't care if the file doesn't 1040 + * exist, so we ignore ENOENT. 1041 + */ 1042 + if (state->flags & COPYFILE_UNLINK) 1043 + { 1044 + if (remove(state->dst) < 0 && errno != ENOENT) 1045 + { 1046 + return -1; 1047 + } 1048 + } 1049 + ret = clonefileat(AT_FDCWD, state->src, AT_FDCWD, state->dst, cloneFlags); 1050 + if (ret == 0) { 1051 + /* 1052 + * We could also report the size of the single 1053 + * object that was cloned. However, that's a lot 1054 + * more difficult when we eventually support 1055 + * cloning directories. It seems reasonable to NOT 1056 + * report any bytes being "copied" in this scenario, 1057 + * and let the caller figure out how they want to 1058 + * deal. 1059 + */ 1060 + state->was_cloned = true; 947 1061 1062 + /* 1063 + * COPYFILE_MOVE tells us to attempt removing 1064 + * the source file after the copy, and to 1065 + * ignore any errors returned by remove(3). 1066 + */ 1067 + if (state->flags & COPYFILE_MOVE) { 1068 + (void)remove(state->src); 1069 + } 1070 + } 1071 + } 1072 + else 1073 + { 1074 + errno = EINVAL; 1075 + ret = -1; 1076 + } 1077 + return ret; 948 1078 } 949 1079 950 1080 /* ··· 955 1085 */ 956 1086 int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_flags_t flags) 957 1087 { 958 - int ret = 0; 959 - int createdst = 0; 960 - copyfile_state_t s = state; 961 - struct stat dst_sb; 1088 + int ret = 0; 1089 + int createdst = 0; 1090 + copyfile_state_t s = state; 1091 + struct stat dst_sb; 962 1092 963 - if (src == NULL && dst == NULL) 964 - { 965 - errno = EINVAL; 966 - return -1; 967 - } 1093 + if (src == NULL && dst == NULL) 1094 + { 1095 + errno = EINVAL; 1096 + return -1; 1097 + } 968 1098 969 - if (copyfile_preamble(&s, flags) < 0) 970 - { 971 - return -1; 972 - } 1099 + if (copyfile_preamble(&s, flags) < 0) 1100 + { 1101 + return -1; 1102 + } 973 1103 974 - /* 975 - * This macro is... well, it's not the worst thing you can do with cpp, not 976 - * by a long shot. Essentially, we are setting the filename (src or dst) 977 - * in the state structure; since the structure may not have been cleared out 978 - * before being used again, we do some of the cleanup here: if the given 979 - * filename (e.g., src) is set, and state->src is not equal to that, then 980 - * we need to check to see if the file descriptor had been opened, and if so, 981 - * close it. After that, we set state->src to be a copy of the given filename, 982 - * releasing the old copy if necessary. 983 - */ 1104 + /* 1105 + * This macro is... well, it's not the worst thing you can do with cpp, not 1106 + * by a long shot. Essentially, we are setting the filename (src or dst) 1107 + * in the state structure; since the structure may not have been cleared out 1108 + * before being used again, we do some of the cleanup here: if the given 1109 + * filename (e.g., src) is set, and state->src is not equal to that, then 1110 + * we need to check to see if the file descriptor had been opened, and if so, 1111 + * close it. After that, we set state->src to be a copy of the given filename, 1112 + * releasing the old copy if necessary. 1113 + */ 984 1114 #define COPYFILE_SET_FNAME(NAME, S) \ 985 - do { \ 986 - if (NAME != NULL) { \ 987 - if (S->NAME != NULL && strncmp(NAME, S->NAME, MAXPATHLEN)) { \ 988 - copyfile_debug(2, "replacing string %s (%s) -> (%s)", #NAME, NAME, S->NAME);\ 989 - if (S->NAME##_fd != -2 && S->NAME##_fd > -1) { \ 990 - copyfile_debug(4, "closing %s fd: %d", #NAME, S->NAME##_fd); \ 991 - close(S->NAME##_fd); \ 992 - S->NAME##_fd = -2; \ 993 - } \ 994 - } \ 995 - if (S->NAME) { \ 996 - free(S->NAME); \ 997 - S->NAME = NULL; \ 998 - } \ 999 - if ((NAME) && (S->NAME = strdup(NAME)) == NULL) \ 1000 - return -1; \ 1001 - } \ 1002 - } while (0) 1115 + do { \ 1116 + if (NAME != NULL) { \ 1117 + if (S->NAME != NULL && strncmp(NAME, S->NAME, MAXPATHLEN)) { \ 1118 + copyfile_debug(2, "replacing string %s (%s) -> (%s)", #NAME, NAME, S->NAME);\ 1119 + if (S->NAME##_fd != -2 && S->NAME##_fd > -1) { \ 1120 + copyfile_debug(4, "closing %s fd: %d", #NAME, S->NAME##_fd); \ 1121 + close(S->NAME##_fd); \ 1122 + S->NAME##_fd = -2; \ 1123 + } \ 1124 + } \ 1125 + if (S->NAME) { \ 1126 + free(S->NAME); \ 1127 + S->NAME = NULL; \ 1128 + } \ 1129 + if ((NAME) && (S->NAME = strdup(NAME)) == NULL) \ 1130 + return -1; \ 1131 + } \ 1132 + } while (0) 1003 1133 1004 - COPYFILE_SET_FNAME(src, s); 1005 - COPYFILE_SET_FNAME(dst, s); 1134 + COPYFILE_SET_FNAME(src, s); 1135 + COPYFILE_SET_FNAME(dst, s); 1006 1136 1007 - if (s->flags & COPYFILE_RECURSIVE) { 1008 - ret = copytree(s); 1009 - goto exit; 1010 - } 1137 + if (s->flags & COPYFILE_RECURSIVE) { 1138 + ret = copytree(s); 1139 + goto exit; 1140 + } 1011 1141 1012 - /* 1013 - * Get a copy of the source file's security settings 1014 - */ 1015 - if (s->original_fsec) { 1016 - filesec_free(s->original_fsec); 1017 - s->original_fsec = NULL; 1018 - } 1019 - if ((s->original_fsec = filesec_init()) == NULL) 1020 - goto error_exit; 1142 + if (s->flags & (COPYFILE_CLONE_FORCE | COPYFILE_CLONE)) 1143 + { 1144 + ret = copyfile_clone(s); 1145 + if (ret == 0) { 1146 + goto exit; 1147 + } else if (s->flags & COPYFILE_CLONE_FORCE) { 1148 + goto error_exit; 1149 + } 1150 + // cloning failed. Inherit clonefile flags required for 1151 + // falling back to copyfile. 1152 + s->flags |= (COPYFILE_ACL | COPYFILE_EXCL | COPYFILE_NOFOLLOW_SRC | 1153 + COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA); 1021 1154 1022 - if ((s->flags & COPYFILE_NOFOLLOW_DST) && lstat(s->dst, &dst_sb) == 0 && 1023 - ((dst_sb.st_mode & S_IFMT) == S_IFLNK)) { 1024 - if (s->permissive_fsec) 1025 - free(s->permissive_fsec); 1026 - s->permissive_fsec = NULL; 1027 - } else if(statx_np(s->dst, &dst_sb, s->original_fsec) == 0) 1028 - { 1029 - /* 1030 - * copyfile_fix_perms() will make a copy of the permission set, 1031 - * and insert at the beginning an ACE that ensures we can write 1032 - * to the file and set attributes. 1033 - */ 1155 + s->flags &= ~COPYFILE_CLONE; 1156 + flags = s->flags; 1157 + ret = 0; 1158 + } 1034 1159 1035 - if((s->permissive_fsec = copyfile_fix_perms(s, &s->original_fsec)) != NULL) 1036 - { 1037 - /* 1038 - * Set the permissions for the destination to our copy. 1039 - * We should get ENOTSUP from any filesystem that simply 1040 - * doesn't support it. 1041 - */ 1042 - if (chmodx_np(s->dst, s->permissive_fsec) < 0 && errno != ENOTSUP) 1043 - { 1044 - copyfile_warn("setting security information"); 1045 - filesec_free(s->permissive_fsec); 1160 + /* 1161 + * Get a copy of the source file's security settings 1162 + */ 1163 + if (s->original_fsec) { 1164 + filesec_free(s->original_fsec); 1165 + s->original_fsec = NULL; 1166 + } 1167 + if ((s->original_fsec = filesec_init()) == NULL) 1168 + goto error_exit; 1169 + 1170 + if ((s->flags & COPYFILE_NOFOLLOW_DST) && lstat(s->dst, &dst_sb) == 0 && 1171 + ((dst_sb.st_mode & S_IFMT) == S_IFLNK)) { 1172 + if (s->permissive_fsec) 1173 + free(s->permissive_fsec); 1046 1174 s->permissive_fsec = NULL; 1047 - } 1175 + } else if(statx_np(s->dst, &dst_sb, s->original_fsec) == 0) 1176 + { 1177 + /* 1178 + * copyfile_fix_perms() will make a copy of the permission set, 1179 + * and insert at the beginning an ACE that ensures we can write 1180 + * to the file and set attributes. 1181 + */ 1182 + 1183 + if((s->permissive_fsec = copyfile_fix_perms(s, &s->original_fsec)) != NULL) 1184 + { 1185 + /* 1186 + * Set the permissions for the destination to our copy. 1187 + * We should get ENOTSUP from any filesystem that simply 1188 + * doesn't support it. 1189 + */ 1190 + if (chmodx_np(s->dst, s->permissive_fsec) < 0 && errno != ENOTSUP) 1191 + { 1192 + copyfile_warn("setting security information"); 1193 + filesec_free(s->permissive_fsec); 1194 + s->permissive_fsec = NULL; 1195 + } 1196 + } 1197 + } else if (errno == ENOENT) { 1198 + createdst = 1; 1048 1199 } 1049 - } else if (errno == ENOENT) { 1050 - createdst = 1; 1051 - } 1052 1200 1053 - /* 1054 - * If COPYFILE_CHECK is set in flags, then all we are going to do 1055 - * is see what kinds of things WOULD have been copied (see 1056 - * copyfile_check() below). We return that value. 1057 - */ 1058 - if (COPYFILE_CHECK & flags) 1059 - { 1060 - ret = copyfile_check(s); 1061 - goto exit; 1062 - } else if ((ret = copyfile_open(s)) < 0) 1063 - goto error_exit; 1201 + /* 1202 + * If COPYFILE_CHECK is set in flags, then all we are going to do 1203 + * is see what kinds of things WOULD have been copied (see 1204 + * copyfile_check() below). We return that value. 1205 + */ 1206 + if (COPYFILE_CHECK & flags) 1207 + { 1208 + ret = copyfile_check(s); 1209 + goto exit; 1210 + } else if ((ret = copyfile_open(s)) < 0) 1211 + goto error_exit; 1064 1212 1065 - (void)fcntl(s->src_fd, F_NOCACHE, 1); 1066 - (void)fcntl(s->dst_fd, F_NOCACHE, 1); 1213 + (void)fcntl(s->src_fd, F_NOCACHE, 1); 1214 + (void)fcntl(s->dst_fd, F_NOCACHE, 1); 1067 1215 #ifdef F_SINGLE_WRITER 1068 - (void)fcntl(s->dst_fd, F_SINGLE_WRITER, 1); 1216 + (void)fcntl(s->dst_fd, F_SINGLE_WRITER, 1); 1069 1217 #endif 1070 1218 1071 - ret = copyfile_internal(s, flags); 1072 - if (ret == -1) 1073 - goto error_exit; 1219 + ret = copyfile_internal(s, flags); 1220 + if (ret == -1) 1221 + goto error_exit; 1074 1222 1075 1223 #ifdef COPYFILE_RECURSIVE 1076 - if (!(flags & COPYFILE_STAT)) { 1077 - if (!createdst) 1078 - { 1079 - /* Just need to reset the BSD information -- mode, owner, group */ 1080 - (void)fchown(s->dst_fd, dst_sb.st_uid, dst_sb.st_gid); 1081 - (void)fchmod(s->dst_fd, dst_sb.st_mode); 1224 + if (!(flags & COPYFILE_STAT)) { 1225 + if (!createdst) 1226 + { 1227 + /* Just need to reset the BSD information -- mode, owner, group */ 1228 + (void)fchown(s->dst_fd, dst_sb.st_uid, dst_sb.st_gid); 1229 + (void)fchmod(s->dst_fd, dst_sb.st_mode); 1230 + } 1082 1231 } 1083 - } 1084 1232 #endif 1085 1233 1086 - reset_security(s); 1234 + reset_security(s); 1087 1235 1088 - if (s->src && (flags & COPYFILE_MOVE)) 1089 - (void)remove(s->src); 1236 + if (s->src && (flags & COPYFILE_MOVE)) 1237 + (void)remove(s->src); 1090 1238 1091 1239 exit: 1092 - if (state == NULL) { 1093 - int t = errno; 1094 - copyfile_state_free(s); 1095 - errno = t; 1096 - } 1240 + if (ret >= 0) { 1241 + errno = 0; 1242 + } 1243 + copyfile_debug(5, "returning %d errno %d\n", ret, errno); 1097 1244 1098 - return ret; 1245 + if (state == NULL) { 1246 + int t = errno; 1247 + copyfile_state_free(s); 1248 + errno = t; 1249 + } 1250 + return ret; 1099 1251 1100 1252 error_exit: 1101 - ret = -1; 1102 - if (s->err) { 1103 - errno = s->err; 1104 - s->err = 0; 1105 - } 1106 - goto exit; 1253 + ret = -1; 1254 + if (s && s->err) { 1255 + errno = s->err; 1256 + s->err = 0; 1257 + } 1258 + goto exit; 1107 1259 } 1108 1260 1109 1261 /* ··· 1113 1265 */ 1114 1266 static int copyfile_preamble(copyfile_state_t *state, copyfile_flags_t flags) 1115 1267 { 1116 - copyfile_state_t s; 1268 + copyfile_state_t s; 1117 1269 1118 - if (*state == NULL) 1119 - { 1120 - if ((*state = copyfile_state_alloc()) == NULL) 1121 - return -1; 1122 - } 1270 + if (*state == NULL) 1271 + { 1272 + if ((*state = copyfile_state_alloc()) == NULL) 1273 + return -1; 1274 + } 1123 1275 1124 - s = *state; 1276 + s = *state; 1125 1277 1126 - if (COPYFILE_DEBUG & flags) 1127 - { 1128 - char *e; 1129 - if ((e = getenv(COPYFILE_DEBUG_VAR))) 1278 + if (COPYFILE_DEBUG & flags) 1130 1279 { 1131 - errno = 0; 1132 - s->debug = (uint32_t)strtol(e, NULL, 0); 1280 + char *e; 1281 + if ((e = getenv(COPYFILE_DEBUG_VAR))) 1282 + { 1283 + errno = 0; 1284 + s->debug = (uint32_t)strtol(e, NULL, 0); 1133 1285 1134 - /* clamp s->debug to 1 if the environment variable is not parsable */ 1135 - if (s->debug == 0 && errno != 0) 1136 - s->debug = 1; 1286 + /* clamp s->debug to 1 if the environment variable is not parsable */ 1287 + if (s->debug == 0 && errno != 0) 1288 + s->debug = 1; 1289 + } 1290 + copyfile_debug(2, "debug value set to: %d", s->debug); 1137 1291 } 1138 - copyfile_debug(2, "debug value set to: %d", s->debug); 1139 - } 1140 1292 1141 1293 #if 0 1142 - /* Temporarily disabled */ 1143 - if (getenv(COPYFILE_DISABLE_VAR) != NULL) 1144 - { 1145 - copyfile_debug(1, "copyfile disabled"); 1146 - return 2; 1147 - } 1294 + /* Temporarily disabled */ 1295 + if (getenv(COPYFILE_DISABLE_VAR) != NULL) 1296 + { 1297 + copyfile_debug(1, "copyfile disabled"); 1298 + return 2; 1299 + } 1148 1300 #endif 1149 - copyfile_debug(2, "setting flags: %d", s->flags); 1150 - s->flags = flags; 1301 + copyfile_debug(2, "setting flags: %d", s->flags); 1302 + s->flags = flags; 1151 1303 1152 - return 0; 1304 + return 0; 1153 1305 } 1154 1306 1155 1307 /* ··· 1159 1311 */ 1160 1312 static int copyfile_internal(copyfile_state_t s, copyfile_flags_t flags) 1161 1313 { 1162 - int ret = 0; 1314 + int ret = 0; 1163 1315 1164 - if (s->dst_fd < 0 || s->src_fd < 0) 1165 - { 1166 - copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)", s->src_fd, s->dst_fd); 1167 - s->err = EINVAL; 1168 - return -1; 1169 - } 1316 + if (s->dst_fd < 0 || s->src_fd < 0) 1317 + { 1318 + copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)", s->src_fd, s->dst_fd); 1319 + s->err = EINVAL; 1320 + return -1; 1321 + } 1170 1322 1171 - /* 1172 - * COPYFILE_PACK causes us to create an Apple Double version of the 1173 - * source file, and puts it into the destination file. See 1174 - * copyfile_pack() below for all the gory details. 1175 - */ 1176 - if (COPYFILE_PACK & flags) 1177 - { 1178 - if ((ret = copyfile_pack(s)) < 0) 1323 + /* 1324 + * COPYFILE_PACK causes us to create an Apple Double version of the 1325 + * source file, and puts it into the destination file. See 1326 + * copyfile_pack() below for all the gory details. 1327 + */ 1328 + if (COPYFILE_PACK & flags) 1179 1329 { 1180 - if (s->dst) unlink(s->dst); 1181 - goto exit; 1330 + if ((ret = copyfile_pack(s)) < 0) 1331 + { 1332 + if (s->dst) unlink(s->dst); 1333 + goto exit; 1334 + } 1335 + goto exit; 1182 1336 } 1183 - goto exit; 1184 - } 1337 + 1338 + /* 1339 + * COPYFILE_UNPACK is the undoing of COPYFILE_PACK, obviously. 1340 + * The goal there is to take an Apple Double file, and turn it 1341 + * into a normal file (with data fork, resource fork, modes, 1342 + * extended attributes, ACLs, etc.). 1343 + */ 1344 + if (COPYFILE_UNPACK & flags) 1345 + { 1346 + if ((ret = copyfile_unpack(s)) < 0) 1347 + goto error_exit; 1348 + goto exit; 1349 + } 1350 + 1351 + 1352 + 1353 + /* 1354 + * If we have quarantine info set, we attempt 1355 + * to apply it to dst_fd. We don't care if 1356 + * it fails, not yet anyway. 1357 + */ 1358 + if (s->qinfo) 1359 + { 1360 + int qr; 1361 + uint32_t q_flags; 1185 1362 1186 - /* 1187 - * COPYFILE_UNPACK is the undoing of COPYFILE_PACK, obviously. 1188 - * The goal there is to take an Apple Double file, and turn it 1189 - * into a normal file (with data fork, resource fork, modes, 1190 - * extended attributes, ACLs, etc.). 1191 - */ 1192 - if (COPYFILE_UNPACK & flags) 1193 - { 1194 - if ((ret = copyfile_unpack(s)) < 0) 1195 - goto error_exit; 1196 - goto exit; 1197 - } 1363 + /* 1364 + * If COPYFILE_RUN_IN_PLACE is set, we need to add 1365 + * QTN_FLAG_DO_NOT_TRANSLOCATE to the qinfo flags. 1366 + * 1367 + * On iOS, qtn_file_get_flags & qtn_file_set_flags 1368 + * don't modify anything, always return 0, per static 1369 + * defines at top of this file, though we should never 1370 + * get here in that case as qinfo will always be NULL. 1371 + */ 1372 + if (COPYFILE_RUN_IN_PLACE & flags) 1373 + { 1374 + q_flags = 0; 1198 1375 1199 - /* 1200 - * If we have quarantine info set, we attempt 1201 - * to apply it to dst_fd. We don't care if 1202 - * it fails, not yet anyway. 1203 - */ 1204 - if (s->qinfo) { 1205 - int qr = qtn_file_apply_to_fd(s->qinfo, s->dst_fd); 1206 - if (qr != 0) { 1207 - if (s->statuscb) { 1208 - int rv; 1376 + q_flags = qtn_file_get_flags(s->qinfo); 1377 + q_flags |= QTN_FLAG_DO_NOT_TRANSLOCATE; 1209 1378 1210 - s->xattr_name = (char*)XATTR_QUARANTINE_NAME; 1211 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 1212 - s->xattr_name = NULL; 1213 - if (rv == COPYFILE_QUIT) { 1379 + if (qtn_file_set_flags(s->qinfo, q_flags) != 0) { 1380 + s->err = errno = EINVAL; 1381 + goto error_exit; 1382 + } 1383 + } 1384 + 1385 + qr = qtn_file_apply_to_fd(s->qinfo, s->dst_fd); 1386 + if (qr != 0) { 1387 + if (s->statuscb) { 1388 + int rv; 1389 + 1390 + s->xattr_name = (char*)XATTR_QUARANTINE_NAME; 1391 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 1392 + s->xattr_name = NULL; 1393 + if (rv == COPYFILE_QUIT) { 1394 + s->err = errno = (qr < 0 ? ENOTSUP : qr); 1395 + goto error_exit; 1396 + } 1397 + } else { 1214 1398 s->err = errno = (qr < 0 ? ENOTSUP : qr); 1215 - ret = -1; 1216 - goto exit; 1399 + goto error_exit; 1217 1400 } 1218 - } else { 1219 - s->err = errno = (qr < 0 ? ENOTSUP : qr); 1220 - ret = -1; 1221 - goto exit; 1222 1401 } 1223 1402 } 1224 - } 1225 1403 1226 - /* 1227 - * COPYFILE_XATTR tells us to copy the extended attributes; 1228 - * this is seperate from the extended security (aka ACLs), 1229 - * however. If we succeed in this, we continue to the next 1230 - * stage; if we fail, we return with an error value. Note 1231 - * that we fail if the errno is ENOTSUP, but we don't print 1232 - * a warning in that case. 1233 - */ 1234 - if (COPYFILE_XATTR & flags) 1235 - { 1236 - if ((ret = copyfile_xattr(s)) < 0) 1404 + /* 1405 + * COPYFILE_XATTR tells us to copy the extended attributes; 1406 + * this is seperate from the extended security (aka ACLs), 1407 + * however. If we succeed in this, we continue to the next 1408 + * stage; if we fail, we return with an error value. Note 1409 + * that we fail if the errno is ENOTSUP, but we don't print 1410 + * a warning in that case. 1411 + */ 1412 + if (COPYFILE_XATTR & flags) 1237 1413 { 1238 - if (errno != ENOTSUP && errno != EPERM) 1239 - copyfile_warn("error processing extended attributes"); 1240 - goto exit; 1414 + if ((ret = copyfile_xattr(s)) < 0) 1415 + { 1416 + if (errno != ENOTSUP && errno != EPERM) 1417 + copyfile_warn("error processing extended attributes"); 1418 + goto exit; 1419 + } 1241 1420 } 1242 - } 1243 1421 1244 - /* 1245 - * Simialr to above, this tells us whether or not to copy 1246 - * the non-meta data portion of the file. We attempt to 1247 - * remove (via unlink) the destination file if we fail. 1248 - */ 1249 - if (COPYFILE_DATA & flags) 1250 - { 1251 - if ((ret = copyfile_data(s)) < 0) 1422 + /* 1423 + * Similar to above, this tells us whether or not to copy 1424 + * the non-meta data portion of the file. We attempt to 1425 + * remove (via unlink) the destination file if we fail. 1426 + */ 1427 + if ((COPYFILE_DATA|COPYFILE_DATA_SPARSE) & flags) 1252 1428 { 1253 - copyfile_warn("error processing data"); 1254 - if (s->dst && unlink(s->dst)) 1255 - copyfile_warn("%s: remove", s->src ? s->src : "(null src)"); 1256 - goto exit; 1429 + if ((ret = copyfile_data(s)) < 0) 1430 + { 1431 + copyfile_warn("error processing data"); 1432 + if (s->dst && unlink(s->dst)) 1433 + copyfile_warn("%s: remove", s->src ? s->src : "(null src)"); 1434 + goto exit; 1435 + } 1257 1436 } 1258 - } 1259 1437 1260 - /* 1261 - * COPYFILE_SECURITY requests that we copy the security, both 1262 - * extended and mundane (that is, ACLs and POSIX). 1263 - */ 1264 - if (COPYFILE_SECURITY & flags) 1265 - { 1266 - if ((ret = copyfile_security(s)) < 0) 1438 + /* 1439 + * COPYFILE_SECURITY requests that we copy the security, both 1440 + * extended and mundane (that is, ACLs and POSIX). 1441 + */ 1442 + if (COPYFILE_SECURITY & flags) 1267 1443 { 1268 - copyfile_warn("error processing security information"); 1269 - goto exit; 1444 + if ((ret = copyfile_security(s)) < 0) 1445 + { 1446 + copyfile_warn("error processing security information"); 1447 + goto exit; 1448 + } 1270 1449 } 1271 - } 1272 1450 1273 - if (COPYFILE_STAT & flags) 1274 - { 1275 - if ((ret = copyfile_stat(s)) < 0) 1451 + if (COPYFILE_STAT & flags) 1276 1452 { 1277 - copyfile_warn("error processing POSIX information"); 1278 - goto exit; 1453 + if ((ret = copyfile_stat(s)) < 0) 1454 + { 1455 + copyfile_warn("error processing POSIX information"); 1456 + goto exit; 1457 + } 1279 1458 } 1280 - } 1281 1459 1282 1460 exit: 1283 - return ret; 1461 + return ret; 1284 1462 1285 1463 error_exit: 1286 - ret = -1; 1287 - goto exit; 1464 + ret = -1; 1465 + goto exit; 1288 1466 } 1289 1467 1290 1468 /* ··· 1292 1470 */ 1293 1471 copyfile_state_t copyfile_state_alloc(void) 1294 1472 { 1295 - copyfile_state_t s = (copyfile_state_t) calloc(1, sizeof(struct _copyfile_state)); 1473 + copyfile_state_t s = (copyfile_state_t) calloc(1, sizeof(struct _copyfile_state)); 1296 1474 1297 - if (s != NULL) 1298 - { 1299 - s->src_fd = -2; 1300 - s->dst_fd = -2; 1301 - if (s->fsec) { 1302 - filesec_free(s->fsec); 1303 - s->fsec = NULL; 1304 - } 1305 - s->fsec = filesec_init(); 1306 - } else 1307 - errno = ENOMEM; 1475 + if (s != NULL) 1476 + { 1477 + s->src_fd = -2; 1478 + s->dst_fd = -2; 1479 + if (s->fsec) { 1480 + filesec_free(s->fsec); 1481 + s->fsec = NULL; 1482 + } 1483 + s->fsec = filesec_init(); 1484 + } else 1485 + errno = ENOMEM; 1308 1486 1309 - return s; 1487 + return s; 1310 1488 } 1311 1489 1312 1490 /* ··· 1315 1493 */ 1316 1494 int copyfile_state_free(copyfile_state_t s) 1317 1495 { 1318 - if (s != NULL) 1319 - { 1320 - if (s->fsec) 1321 - filesec_free(s->fsec); 1496 + if (s != NULL) 1497 + { 1498 + if (s->fsec) 1499 + filesec_free(s->fsec); 1322 1500 1323 - if (s->original_fsec) 1324 - filesec_free(s->original_fsec); 1501 + if (s->original_fsec) 1502 + filesec_free(s->original_fsec); 1325 1503 1326 - if (s->permissive_fsec) 1327 - filesec_free(s->permissive_fsec); 1504 + if (s->permissive_fsec) 1505 + filesec_free(s->permissive_fsec); 1328 1506 1329 - if (s->qinfo) 1330 - qtn_file_free(s->qinfo); 1507 + if (s->qinfo) 1508 + qtn_file_free(s->qinfo); 1331 1509 1332 - if (copyfile_close(s) < 0) 1333 - { 1334 - copyfile_warn("error closing files"); 1335 - return -1; 1510 + if (copyfile_close(s) < 0) 1511 + { 1512 + copyfile_warn("error closing files"); 1513 + return -1; 1514 + } 1515 + if (s->xattr_name) 1516 + free(s->xattr_name); 1517 + if (s->dst) 1518 + free(s->dst); 1519 + if (s->src) 1520 + free(s->src); 1521 + free(s); 1336 1522 } 1337 - if (s->xattr_name) 1338 - free(s->xattr_name); 1339 - if (s->dst) 1340 - free(s->dst); 1341 - if (s->src) 1342 - free(s->src); 1343 - free(s); 1344 - } 1345 - return 0; 1523 + return 0; 1346 1524 } 1347 1525 1348 1526 /* ··· 1351 1529 */ 1352 1530 static int copyfile_close(copyfile_state_t s) 1353 1531 { 1354 - if (s->src && s->src_fd >= 0) 1355 - close(s->src_fd); 1532 + if (s->src && s->src_fd >= 0) 1533 + close(s->src_fd); 1356 1534 1357 - if (s->dst && s->dst_fd >= 0) { 1358 - if (close(s->dst_fd)) 1359 - return -1; 1360 - } 1535 + if (s->dst && s->dst_fd >= 0) { 1536 + if (close(s->dst_fd)) 1537 + return -1; 1538 + } 1361 1539 1362 - return 0; 1540 + return 0; 1363 1541 } 1364 1542 1365 1543 /* ··· 1372 1550 */ 1373 1551 static filesec_t copyfile_fix_perms(copyfile_state_t s __unused, filesec_t *fsec) 1374 1552 { 1375 - filesec_t ret_fsec = NULL; 1376 - mode_t mode; 1377 - acl_t acl = NULL; 1553 + filesec_t ret_fsec = NULL; 1554 + mode_t mode; 1555 + acl_t acl = NULL; 1378 1556 1379 - if ((ret_fsec = filesec_dup(*fsec)) == NULL) 1380 - goto error_exit; 1557 + if ((ret_fsec = filesec_dup(*fsec)) == NULL) 1558 + goto error_exit; 1381 1559 1382 - if (filesec_get_property(ret_fsec, FILESEC_ACL, &acl) == 0) 1383 - { 1560 + if (filesec_get_property(ret_fsec, FILESEC_ACL, &acl) == 0) 1561 + { 1384 1562 #ifdef COPYFILE_RECURSIVE 1385 - if (add_uberace(&acl)) 1386 - goto error_exit; 1563 + if (add_uberace(&acl)) 1564 + goto error_exit; 1387 1565 #else 1388 - acl_entry_t entry; 1389 - acl_permset_t permset; 1390 - uuid_t qual; 1566 + acl_entry_t entry; 1567 + acl_permset_t permset; 1568 + uuid_t qual; 1391 1569 1392 - if (mbr_uid_to_uuid(getuid(), qual) != 0) 1393 - goto error_exit; 1570 + if (mbr_uid_to_uuid(getuid(), qual) != 0) 1571 + goto error_exit; 1394 1572 1395 - /* 1396 - * First, we create an entry, and give it the special name 1397 - * of ACL_FIRST_ENTRY, thus guaranteeing it will be first. 1398 - * After that, we clear out all the permissions in it, and 1399 - * add three permissions: WRITE_DATA, WRITE_ATTRIBUTES, and 1400 - * WRITE_EXTATTRIBUTES. We put these into an ACE that allows 1401 - * the functionality, and put this into the ACL. 1402 - */ 1403 - if (acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY) == -1) 1404 - goto error_exit; 1405 - if (acl_get_permset(entry, &permset) == -1) 1406 - goto error_exit; 1407 - if (acl_clear_perms(permset) == -1) 1408 - goto error_exit; 1409 - if (acl_add_perm(permset, ACL_WRITE_DATA) == -1) 1410 - goto error_exit; 1411 - if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1) 1412 - goto error_exit; 1413 - if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1) 1414 - goto error_exit; 1415 - if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1) 1416 - goto error_exit; 1573 + /* 1574 + * First, we create an entry, and give it the special name 1575 + * of ACL_FIRST_ENTRY, thus guaranteeing it will be first. 1576 + * After that, we clear out all the permissions in it, and 1577 + * add three permissions: WRITE_DATA, WRITE_ATTRIBUTES, and 1578 + * WRITE_EXTATTRIBUTES. We put these into an ACE that allows 1579 + * the functionality, and put this into the ACL. 1580 + */ 1581 + if (acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY) == -1) 1582 + goto error_exit; 1583 + if (acl_get_permset(entry, &permset) == -1) 1584 + goto error_exit; 1585 + if (acl_clear_perms(permset) == -1) 1586 + goto error_exit; 1587 + if (acl_add_perm(permset, ACL_WRITE_DATA) == -1) 1588 + goto error_exit; 1589 + if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1) 1590 + goto error_exit; 1591 + if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1) 1592 + goto error_exit; 1593 + if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1) 1594 + goto error_exit; 1417 1595 1418 - if(acl_set_permset(entry, permset) == -1) 1419 - goto error_exit; 1420 - if(acl_set_qualifier(entry, qual) == -1) 1421 - goto error_exit; 1596 + if(acl_set_permset(entry, permset) == -1) 1597 + goto error_exit; 1598 + if(acl_set_qualifier(entry, qual) == -1) 1599 + goto error_exit; 1422 1600 #endif 1423 1601 1424 - if (filesec_set_property(ret_fsec, FILESEC_ACL, &acl) != 0) 1425 - goto error_exit; 1426 - } 1602 + if (filesec_set_property(ret_fsec, FILESEC_ACL, &acl) != 0) 1603 + goto error_exit; 1604 + } 1427 1605 1428 - /* 1429 - * This is for the normal, mundane, POSIX permission model. 1430 - * We make sure that we can write to the file. 1431 - */ 1432 - if (filesec_get_property(ret_fsec, FILESEC_MODE, &mode) == 0) 1433 - { 1434 - if ((mode & (S_IWUSR | S_IRUSR)) != (S_IWUSR | S_IRUSR)) 1606 + /* 1607 + * This is for the normal, mundane, POSIX permission model. 1608 + * We make sure that we can write to the file. 1609 + */ 1610 + if (filesec_get_property(ret_fsec, FILESEC_MODE, &mode) == 0) 1435 1611 { 1436 - mode |= S_IWUSR|S_IRUSR; 1437 - if (filesec_set_property(ret_fsec, FILESEC_MODE, &mode) != 0) 1438 - goto error_exit; 1612 + if ((mode & (S_IWUSR | S_IRUSR)) != (S_IWUSR | S_IRUSR)) 1613 + { 1614 + mode |= S_IWUSR|S_IRUSR; 1615 + if (filesec_set_property(ret_fsec, FILESEC_MODE, &mode) != 0) 1616 + goto error_exit; 1617 + } 1439 1618 } 1440 - } 1441 1619 1442 1620 exit: 1443 - if (acl) 1444 - acl_free(acl); 1621 + if (acl) 1622 + acl_free(acl); 1445 1623 1446 - return ret_fsec; 1624 + return ret_fsec; 1447 1625 1448 1626 error_exit: 1449 - if (ret_fsec) 1450 - { 1451 - filesec_free(ret_fsec); 1452 - ret_fsec = NULL; 1453 - } 1454 - goto exit; 1627 + if (ret_fsec) 1628 + { 1629 + filesec_free(ret_fsec); 1630 + ret_fsec = NULL; 1631 + } 1632 + goto exit; 1455 1633 } 1456 1634 1457 1635 /* ··· 1473 1651 */ 1474 1652 static int copyfile_unset_acl(copyfile_state_t s) 1475 1653 { 1476 - int ret = 0; 1477 - if (filesec_set_property(s->fsec, FILESEC_ACL, NULL) == -1) 1478 - { 1479 - copyfile_debug(5, "unsetting acl attribute on %s", s->dst ? s->dst : "(null dst)"); 1480 - ++ret; 1481 - } 1482 - if (filesec_set_property(s->fsec, FILESEC_UUID, NULL) == -1) 1483 - { 1484 - copyfile_debug(5, "unsetting uuid attribute on %s", s->dst ? s->dst : "(null dst)"); 1485 - ++ret; 1486 - } 1487 - if (filesec_set_property(s->fsec, FILESEC_GRPUUID, NULL) == -1) 1488 - { 1489 - copyfile_debug(5, "unsetting group uuid attribute on %s", s->dst ? s->dst : "(null dst)"); 1490 - ++ret; 1491 - } 1492 - return ret; 1654 + int ret = 0; 1655 + if (filesec_set_property(s->fsec, FILESEC_ACL, NULL) == -1) 1656 + { 1657 + copyfile_debug(5, "unsetting acl attribute on %s", s->dst ? s->dst : "(null dst)"); 1658 + ++ret; 1659 + } 1660 + if (filesec_set_property(s->fsec, FILESEC_UUID, NULL) == -1) 1661 + { 1662 + copyfile_debug(5, "unsetting uuid attribute on %s", s->dst ? s->dst : "(null dst)"); 1663 + ++ret; 1664 + } 1665 + if (filesec_set_property(s->fsec, FILESEC_GRPUUID, NULL) == -1) 1666 + { 1667 + copyfile_debug(5, "unsetting group uuid attribute on %s", s->dst ? s->dst : "(null dst)"); 1668 + ++ret; 1669 + } 1670 + return ret; 1493 1671 } 1494 1672 1495 1673 /* ··· 1500 1678 */ 1501 1679 static int copyfile_open(copyfile_state_t s) 1502 1680 { 1503 - int oflags = O_EXCL | O_CREAT | O_WRONLY; 1504 - int islnk = 0, isdir = 0; 1505 - int osrc = 0, dsrc = 0; 1681 + int oflags = O_EXCL | O_CREAT | O_WRONLY; 1682 + int islnk = 0, isdir = 0, isreg = 0; 1683 + int osrc = 0, dsrc = 0; 1684 + int prot_class = PROTECTION_CLASS_DEFAULT; 1685 + int set_cprot_explicit = 0; 1686 + int error = 0; 1506 1687 1507 - if (s->src && s->src_fd == -2) 1508 - { 1509 - if ((COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np) 1510 - (s->src, &s->sb, s->fsec)) 1688 + if (s->src && s->src_fd == -2) 1511 1689 { 1512 - copyfile_warn("stat on %s", s->src); 1513 - return -1; 1514 - } 1515 - 1516 - /* prevent copying on unsupported types */ 1517 - switch (s->sb.st_mode & S_IFMT) 1518 - { 1519 - case S_IFLNK: 1520 - islnk = 1; 1521 - if ((size_t)s->sb.st_size > SIZE_T_MAX) { 1522 - s->err = ENOMEM; /* too big for us to copy */ 1690 + if ((COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np) 1691 + (s->src, &s->sb, s->fsec)) 1692 + { 1693 + copyfile_warn("stat on %s", s->src); 1523 1694 return -1; 1524 1695 } 1525 - osrc = O_SYMLINK; 1526 - break; 1527 - case S_IFDIR: 1528 - isdir = 1; 1529 - break; 1530 - case S_IFREG: 1531 - break; 1532 - default: 1533 - if (!(strcmp(s->src, "/dev/null") == 0 && (s->flags & COPYFILE_METADATA))) { 1534 - s->err = ENOTSUP; 1535 - return -1; 1696 + 1697 + /* prevent copying on unsupported types */ 1698 + switch (s->sb.st_mode & S_IFMT) 1699 + { 1700 + case S_IFLNK: 1701 + islnk = 1; 1702 + if ((size_t)s->sb.st_size > SIZE_T_MAX) { 1703 + s->err = ENOMEM; /* too big for us to copy */ 1704 + return -1; 1705 + } 1706 + osrc = O_SYMLINK; 1707 + break; 1708 + case S_IFDIR: 1709 + isdir = 1; 1710 + break; 1711 + case S_IFREG: 1712 + isreg = 1; 1713 + break; 1714 + default: 1715 + if (!(strcmp(s->src, "/dev/null") == 0 && (s->flags & COPYFILE_METADATA))) { 1716 + s->err = ENOTSUP; 1717 + return -1; 1718 + } 1536 1719 } 1537 - } 1538 - /* 1539 - * If we're packing, then we are actually 1540 - * creating a file, no matter what the source 1541 - * was. 1542 - */ 1543 - if (s->flags & COPYFILE_PACK) { 1544 1720 /* 1545 - * O_SYMLINK and O_NOFOLLOW are not compatible options: 1546 - * if the file is a symlink, and O_NOFOLLOW is specified, 1547 - * open will return ELOOP, whether or not O_SYMLINK is set. 1548 - * However, we know whether or not it was a symlink from 1549 - * the stat above (although there is a potentiaal for a race 1550 - * condition here, but it will err on the side of returning 1551 - * ELOOP from open). 1721 + * If we're packing, then we are actually 1722 + * creating a file, no matter what the source 1723 + * was. 1552 1724 */ 1553 - if (!islnk) 1554 - osrc = (s->flags & COPYFILE_NOFOLLOW_SRC) ? O_NOFOLLOW : 0; 1555 - isdir = islnk = 0; 1556 - } 1725 + if (s->flags & COPYFILE_PACK) { 1726 + /* 1727 + * O_SYMLINK and O_NOFOLLOW are not compatible options: 1728 + * if the file is a symlink, and O_NOFOLLOW is specified, 1729 + * open will return ELOOP, whether or not O_SYMLINK is set. 1730 + * However, we know whether or not it was a symlink from 1731 + * the stat above (although there is a potentiaal for a race 1732 + * condition here, but it will err on the side of returning 1733 + * ELOOP from open). 1734 + */ 1735 + if (!islnk) 1736 + osrc = (s->flags & COPYFILE_NOFOLLOW_SRC) ? O_NOFOLLOW : 0; 1737 + isdir = islnk = 0; 1738 + } 1557 1739 1558 - if ((s->src_fd = open(s->src, O_RDONLY | osrc , 0)) < 0) 1559 - { 1560 - copyfile_warn("open on %s", s->src); 1561 - return -1; 1562 - } else 1563 - copyfile_debug(2, "open successful on source (%s)", s->src); 1740 + if ((s->src_fd = open(s->src, O_RDONLY | osrc , 0)) < 0) 1741 + { 1742 + copyfile_warn("open on %s", s->src); 1743 + return -1; 1744 + } 1745 + copyfile_debug(2, "open successful on source (%s)", s->src); 1564 1746 1565 - (void)copyfile_quarantine(s); 1566 - } 1747 + (void)copyfile_quarantine(s); 1748 + } 1567 1749 1568 - if (s->dst && s->dst_fd == -2) 1569 - { 1570 - /* 1571 - * COPYFILE_UNLINK tells us to try removing the destination 1572 - * before we create it. We don't care if the file doesn't 1573 - * exist, so we ignore ENOENT. 1574 - */ 1575 - if (COPYFILE_UNLINK & s->flags) 1750 + if (s->dst && s->dst_fd == -2) 1576 1751 { 1577 - if (remove(s->dst) < 0 && errno != ENOENT) 1578 - { 1579 - copyfile_warn("%s: remove", s->dst); 1580 - return -1; 1581 - } 1582 - } 1752 + /* 1753 + * COPYFILE_UNLINK tells us to try removing the destination 1754 + * before we create it. We don't care if the file doesn't 1755 + * exist, so we ignore ENOENT. 1756 + */ 1757 + if (COPYFILE_UNLINK & s->flags) 1758 + { 1759 + if (remove(s->dst) < 0 && errno != ENOENT) 1760 + { 1761 + copyfile_warn("%s: remove", s->dst); 1762 + return -1; 1763 + } 1764 + } 1583 1765 1584 - if (s->flags & COPYFILE_NOFOLLOW_DST) { 1585 - struct stat st; 1766 + if (s->flags & COPYFILE_NOFOLLOW_DST) { 1767 + struct stat st; 1586 1768 1587 - dsrc = O_NOFOLLOW; 1588 - if (lstat(s->dst, &st) != -1) { 1589 - if ((st.st_mode & S_IFMT) == S_IFLNK) 1590 - dsrc = O_SYMLINK; 1769 + dsrc = O_NOFOLLOW; 1770 + if (lstat(s->dst, &st) != -1) { 1771 + if ((st.st_mode & S_IFMT) == S_IFLNK) 1772 + dsrc = O_SYMLINK; 1773 + } 1591 1774 } 1592 - } 1593 1775 1594 - if (islnk) { 1595 - size_t sz = (size_t)s->sb.st_size + 1; 1596 - char *bp; 1597 - 1598 - bp = calloc(1, sz); 1599 - if (bp == NULL) { 1600 - copyfile_warn("cannot allocate %zd bytes", sz); 1601 - return -1; 1776 + if (!(s->internal_flags & cfSrcProtSupportValid)) 1777 + { 1778 + if ((error = does_copy_protection(s->src_fd)) > 0) 1779 + { 1780 + s->internal_flags |= cfSrcSupportsCProtect; 1781 + } 1782 + else if (error < 0) 1783 + { 1784 + copyfile_warn("does_copy_protection failed on (%s) with error <%d>", s->src, errno); 1785 + return -1; 1786 + } 1787 + s->internal_flags |= cfSrcProtSupportValid; 1602 1788 } 1603 - if (readlink(s->src, bp, sz-1) == -1) { 1604 - copyfile_warn("cannot readlink %s", s->src); 1605 - free(bp); 1606 - return -1; 1789 + 1790 + /* copy protection is only valid for regular files and directories. */ 1791 + if ((isreg || isdir) && (s->internal_flags & cfSrcSupportsCProtect)) 1792 + { 1793 + prot_class = GET_PROT_CLASS(s->src_fd); 1794 + if (prot_class < 0) 1795 + { 1796 + copyfile_warn("GET_PROT_CLASS failed on (%s) with error <%d>", s->src, errno); 1797 + return -1; 1798 + } 1607 1799 } 1608 - if (symlink(bp, s->dst) == -1) { 1609 - if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) { 1610 - copyfile_warn("Cannot make symlink %s", s->dst); 1800 + 1801 + if (islnk) { 1802 + size_t sz = (size_t)s->sb.st_size + 1; 1803 + char *bp; 1804 + 1805 + bp = calloc(1, sz); 1806 + if (bp == NULL) { 1807 + copyfile_warn("cannot allocate %zd bytes", sz); 1808 + return -1; 1809 + } 1810 + if (readlink(s->src, bp, sz-1) == -1) { 1811 + copyfile_warn("cannot readlink %s", s->src); 1611 1812 free(bp); 1612 1813 return -1; 1613 1814 } 1614 - } 1615 - free(bp); 1616 - s->dst_fd = open(s->dst, O_RDONLY | O_SYMLINK); 1617 - if (s->dst_fd == -1) { 1618 - copyfile_warn("Cannot open symlink %s for reading", s->dst); 1619 - return -1; 1620 - } 1621 - } else if (isdir) { 1622 - mode_t mode; 1623 - mode = (s->sb.st_mode & ~S_IFMT) | S_IRWXU; 1815 + if (symlink(bp, s->dst) == -1) { 1816 + if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) { 1817 + copyfile_warn("Cannot make symlink %s", s->dst); 1818 + free(bp); 1819 + return -1; 1820 + } 1821 + } 1822 + free(bp); 1823 + s->dst_fd = open(s->dst, O_RDONLY | O_SYMLINK); 1824 + if (s->dst_fd == -1) { 1825 + copyfile_warn("Cannot open symlink %s for reading", s->dst); 1826 + return -1; 1827 + } 1828 + } else if (isdir) { 1829 + mode_t mode; 1830 + mode = (s->sb.st_mode & ~S_IFMT) | S_IRWXU; 1624 1831 1625 - if (mkdir(s->dst, mode) == -1) { 1626 - if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) { 1627 - copyfile_warn("Cannot make directory %s", s->dst); 1832 + if (mkdir(s->dst, mode) == -1) { 1833 + if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) { 1834 + copyfile_warn("Cannot make directory %s", s->dst); 1835 + return -1; 1836 + } 1837 + } 1838 + s->dst_fd = open(s->dst, O_RDONLY | dsrc); 1839 + if (s->dst_fd == -1) { 1840 + copyfile_warn("Cannot open directory %s for reading", s->dst); 1628 1841 return -1; 1629 1842 } 1630 - } 1631 - s->dst_fd = open(s->dst, O_RDONLY | dsrc); 1632 - if (s->dst_fd == -1) { 1633 - copyfile_warn("Cannot open directory %s for reading", s->dst); 1634 - return -1; 1635 - } 1636 - } else while((s->dst_fd = open(s->dst, oflags | dsrc, s->sb.st_mode | S_IWUSR)) < 0) 1637 - { 1638 - /* 1639 - * We set S_IWUSR because fsetxattr does not -- at the time this comment 1640 - * was written -- allow one to set an extended attribute on a file descriptor 1641 - * for a read-only file, even if the file descriptor is opened for writing. 1642 - * This will only matter if the file does not already exist. 1643 - */ 1644 - switch(errno) 1645 - { 1646 - case EEXIST: 1647 - copyfile_debug(3, "open failed, retrying (%s)", s->dst); 1648 - if (s->flags & COPYFILE_EXCL) 1649 - break; 1650 - oflags = oflags & ~O_CREAT; 1651 - if (s->flags & (COPYFILE_PACK | COPYFILE_DATA)) 1652 - { 1653 - copyfile_debug(4, "truncating existing file (%s)", s->dst); 1654 - oflags |= O_TRUNC; 1655 - } 1656 - continue; 1657 - case EACCES: 1658 - if(chmod(s->dst, (s->sb.st_mode | S_IWUSR) & ~S_IFMT) == 0) 1659 - continue; 1660 - else { 1843 + set_cprot_explicit = 1; 1844 + } else while((s->dst_fd = open_dprotected_np(s->dst, oflags | dsrc, prot_class, 0, s->sb.st_mode | S_IWUSR)) < 0) 1845 + { 1661 1846 /* 1662 - * If we're trying to write to a directory to which we don't 1663 - * have access, the create above would have failed, but chmod 1664 - * here would have given us ENOENT. But the real error is 1665 - * still one of access, so we change the errno we're reporting. 1666 - * This could cause confusion with a race condition. 1847 + * We set S_IWUSR because fsetxattr does not -- at the time this comment 1848 + * was written -- allow one to set an extended attribute on a file descriptor 1849 + * for a read-only file, even if the file descriptor is opened for writing. 1850 + * This will only matter if the file does not already exist. 1667 1851 */ 1852 + switch(errno) 1853 + { 1854 + case EEXIST: 1855 + copyfile_debug(3, "open failed, retrying (%s)", s->dst); 1856 + if (s->flags & COPYFILE_EXCL) 1857 + break; 1858 + oflags = oflags & ~O_CREAT; 1859 + /* if O_CREAT isn't set in open_dprotected_np, it won't set protection class. 1860 + * Set the flag here so we know to do it later. 1861 + */ 1862 + set_cprot_explicit = 1; 1863 + if (s->flags & (COPYFILE_PACK | COPYFILE_DATA)) 1864 + { 1865 + copyfile_debug(4, "truncating existing file (%s)", s->dst); 1866 + oflags |= O_TRUNC; 1867 + } 1868 + continue; 1869 + case EACCES: 1870 + if(chmod(s->dst, (s->sb.st_mode | S_IWUSR) & ~S_IFMT) == 0) 1871 + continue; 1872 + else { 1873 + /* 1874 + * If we're trying to write to a directory to which we don't 1875 + * have access, the create above would have failed, but chmod 1876 + * here would have given us ENOENT. But the real error is 1877 + * still one of access, so we change the errno we're reporting. 1878 + * This could cause confusion with a race condition. 1879 + */ 1668 1880 1669 - if (errno == ENOENT) 1670 - errno = EACCES; 1671 - break; 1672 - } 1673 - case EISDIR: 1674 - copyfile_debug(3, "open failed because it is a directory (%s)", s->dst); 1675 - if (((s->flags & COPYFILE_EXCL) || 1676 - (!isdir && (s->flags & COPYFILE_DATA))) 1677 - && !(s->flags & COPYFILE_UNPACK)) 1678 - break; 1679 - oflags = (oflags & ~(O_WRONLY|O_CREAT|O_TRUNC)) | O_RDONLY; 1680 - continue; 1681 - } 1682 - copyfile_warn("open on %s", s->dst); 1683 - return -1; 1881 + if (errno == ENOENT) 1882 + errno = EACCES; 1883 + break; 1884 + } 1885 + case EISDIR: 1886 + copyfile_debug(3, "open failed because it is a directory (%s)", s->dst); 1887 + if (((s->flags & COPYFILE_EXCL) || 1888 + (!isdir && (s->flags & COPYFILE_DATA))) 1889 + && !(s->flags & COPYFILE_UNPACK)) 1890 + break; 1891 + oflags = (oflags & ~(O_WRONLY|O_CREAT|O_TRUNC)) | O_RDONLY; 1892 + continue; 1893 + } 1894 + copyfile_warn("open on %s", s->dst); 1895 + return -1; 1896 + } 1897 + copyfile_debug(2, "open successful on destination (%s)", s->dst); 1898 + 1899 + if (s->internal_flags & cfSrcSupportsCProtect) 1900 + { 1901 + if (!(s->internal_flags & cfDstProtSupportValid)) 1902 + { 1903 + if ((error = does_copy_protection(s->dst_fd)) > 0) 1904 + { 1905 + s->internal_flags |= cfDstSupportsCProtect; 1906 + } 1907 + else if (error < 0) 1908 + { 1909 + copyfile_warn("does_copy_protection failed on (%s) with error <%d>", s->dst, errno); 1910 + return -1; 1911 + } 1912 + s->internal_flags |= cfDstProtSupportValid; 1913 + } 1914 + 1915 + if ((isreg || isdir) 1916 + && set_cprot_explicit 1917 + && (s->internal_flags & cfDstSupportsCProtect)) 1918 + { 1919 + /* Protection class is set in open_dprotected_np for regular files that aren't truncated. 1920 + * We set the protection class here for truncated files and directories. 1921 + */ 1922 + if (SET_PROT_CLASS(s->dst_fd, prot_class) != 0) 1923 + { 1924 + copyfile_warn("SET_PROT_CLASS failed on (%s) with error <%d>", s->dst, errno); 1925 + return -1; 1926 + } 1927 + } 1928 + } 1684 1929 } 1685 - copyfile_debug(2, "open successful on destination (%s)", s->dst); 1686 - } 1687 1930 1688 - if (s->dst_fd < 0 || s->src_fd < 0) 1689 - { 1690 - copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)", 1691 - s->src_fd, s->dst_fd); 1692 - s->err = EINVAL; 1693 - return -1; 1694 - } 1695 - return 0; 1931 + if (s->dst_fd < 0 || s->src_fd < 0) 1932 + { 1933 + copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)", 1934 + s->src_fd, s->dst_fd); 1935 + s->err = EINVAL; 1936 + return -1; 1937 + } 1938 + return 0; 1696 1939 } 1697 1940 1698 1941 ··· 1706 1949 */ 1707 1950 static copyfile_flags_t copyfile_check(copyfile_state_t s) 1708 1951 { 1709 - acl_t acl = NULL; 1710 - copyfile_flags_t ret = 0; 1711 - int nofollow = (s->flags & COPYFILE_NOFOLLOW_SRC); 1712 - qtn_file_t qinfo; 1952 + acl_t acl = NULL; 1953 + copyfile_flags_t ret = 0; 1954 + int nofollow = (s->flags & COPYFILE_NOFOLLOW_SRC); 1955 + qtn_file_t qinfo; 1713 1956 1714 - if (!s->src) 1715 - { 1716 - s->err = EINVAL; 1717 - return -1; 1718 - } 1719 - 1720 - /* check EAs */ 1721 - if (COPYFILE_XATTR & s->flags) 1722 - if (listxattr(s->src, 0, 0, nofollow ? XATTR_NOFOLLOW : 0) > 0) 1957 + if (!s->src) 1723 1958 { 1724 - ret |= COPYFILE_XATTR; 1959 + s->err = EINVAL; 1960 + return -1; 1725 1961 } 1726 1962 1727 - if (COPYFILE_ACL & s->flags) 1728 - { 1729 - (COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np) 1963 + /* check EAs */ 1964 + if (COPYFILE_XATTR & s->flags) 1965 + if (listxattr(s->src, 0, 0, nofollow ? XATTR_NOFOLLOW : 0) > 0) 1966 + { 1967 + ret |= COPYFILE_XATTR; 1968 + } 1969 + 1970 + if (COPYFILE_ACL & s->flags) 1971 + { 1972 + (COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np) 1730 1973 (s->src, &s->sb, s->fsec); 1731 1974 1732 - if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) == 0) 1733 - ret |= COPYFILE_ACL; 1734 - } 1975 + if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) == 0) 1976 + ret |= COPYFILE_ACL; 1977 + } 1735 1978 1736 - copyfile_debug(2, "check result: %d (%s)", ret, s->src); 1979 + copyfile_debug(2, "check result: %d (%s)", ret, s->src); 1737 1980 1738 - if (acl) 1739 - acl_free(acl); 1981 + if (acl) 1982 + acl_free(acl); 1740 1983 1741 - if (s->qinfo) { 1742 - /* If the state has had quarantine info set already, we use that */ 1743 - ret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL); 1744 - } else { 1745 - qinfo = qtn_file_alloc(); 1746 - /* 1747 - * For quarantine information, we need to see if the source file 1748 - * has any. Since it may be a symlink, however, and we may, or 1749 - * not be following, *and* there's no qtn* routine which can optionally 1750 - * follow or not follow a symlink, we need to instead work around 1751 - * this limitation. 1752 - */ 1753 - if (qinfo) { 1754 - int fd; 1755 - int qret = 0; 1756 - struct stat sbuf; 1757 - 1984 + if (s->qinfo) { 1985 + /* If the state has had quarantine info set already, we use that */ 1986 + ret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL); 1987 + } else { 1988 + qinfo = qtn_file_alloc(); 1758 1989 /* 1759 - * If we care about not following symlinks, *and* the file exists 1760 - * (which is to say, lstat doesn't return an error), *and* the file 1761 - * is a symlink, then we open it up (with O_SYMLINK), and use 1762 - * qtn_file_init_with_fd(); if none of that is true, however, then 1763 - * we can simply use qtn_file_init_with_path(). 1990 + * For quarantine information, we need to see if the source file 1991 + * has any. Since it may be a symlink, however, and we may, or 1992 + * not be following, *and* there's no qtn* routine which can optionally 1993 + * follow or not follow a symlink, we need to instead work around 1994 + * this limitation. 1764 1995 */ 1765 - if (nofollow 1766 - && lstat(s->src, &sbuf) == 0 1996 + if (qinfo) { 1997 + int fd; 1998 + int qret = 0; 1999 + struct stat sbuf; 2000 + 2001 + /* 2002 + * If we care about not following symlinks, *and* the file exists 2003 + * (which is to say, lstat doesn't return an error), *and* the file 2004 + * is a symlink, then we open it up (with O_SYMLINK), and use 2005 + * qtn_file_init_with_fd(); if none of that is true, however, then 2006 + * we can simply use qtn_file_init_with_path(). 2007 + */ 2008 + if (nofollow 2009 + && lstat(s->src, &sbuf) == 0 1767 2010 && ((sbuf.st_mode & S_IFMT) == S_IFLNK)) { 1768 - fd = open(s->src, O_RDONLY | O_SYMLINK); 1769 - if (fd != -1) { 1770 - if (!qtn_file_init_with_fd(qinfo, fd)) { 2011 + fd = open(s->src, O_RDONLY | O_SYMLINK); 2012 + if (fd != -1) { 2013 + if (!qtn_file_init_with_fd(qinfo, fd)) { 2014 + qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL); 2015 + } 2016 + close(fd); 2017 + } 2018 + } else { 2019 + if (!qtn_file_init_with_path(qinfo, s->src)) { 1771 2020 qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL); 1772 2021 } 1773 - close(fd); 2022 + } 2023 + qtn_file_free(qinfo); 2024 + ret |= qret; 2025 + } 2026 + } 2027 + return ret; 2028 + } 2029 + 2030 + /* 2031 + * Attempt to copy the data section of a file sparsely. 2032 + * Requires that the source and destination file systems support sparse files. 2033 + * Also requires that the source file descriptor's offset is a multiple of the smaller of the 2034 + * source and destination file systems' block size. 2035 + * In practice, this means that we refuse to perform copies that are only partially sparse. 2036 + * Returns 0 if the source sparse file was copied, -1 on an unrecoverable error that 2037 + * callers should propagate, and ENOTSUP where this routine refuses to copy the source file. 2038 + * In this final case, callers are free to attempt a full copy. 2039 + */ 2040 + static int copyfile_data_sparse(copyfile_state_t s, size_t input_blk_size, size_t output_blk_size) 2041 + { 2042 + int src_fd = s->src_fd, dst_fd = s->dst_fd, rc = 0; 2043 + off_t src_start, dst_start, src_size = s->sb.st_size; 2044 + off_t first_hole_offset, next_hole_offset, current_src_offset, next_src_offset; 2045 + ssize_t nread; 2046 + size_t iosize = MIN(input_blk_size, output_blk_size); 2047 + copyfile_callback_t status = s->statuscb; 2048 + char *bp = NULL; 2049 + bool use_punchhole = true; 2050 + errno = 0; 2051 + 2052 + // Sanity checks. 2053 + if (!(s->flags & COPYFILE_DATA_SPARSE)) { 2054 + // Don't attempt this unless the right flags are passed. 2055 + return ENOTSUP; 2056 + } else if (src_size <= 0) { 2057 + // The file size of our source is invalid; there's nothing to copy. 2058 + errno = EINVAL; 2059 + goto error_exit; 2060 + } 2061 + 2062 + // Since a major underlying filesystem requires that holes are block-aligned, 2063 + // we only punch holes if we can guarantee that all holes from the source can 2064 + // be holes in the destination, which requires that the source filesystem's block size 2065 + // be an integral multiple of the destination filesystem's block size. 2066 + if (input_blk_size % output_blk_size != 0) { 2067 + use_punchhole = false; 2068 + } 2069 + 2070 + // Get the starting src/dest file descriptor offsets. 2071 + src_start = lseek(src_fd, 0, SEEK_CUR); 2072 + dst_start = lseek(dst_fd, 0, SEEK_CUR); 2073 + if (src_start < 0 || src_start >= src_size || dst_start < 0) { 2074 + /* 2075 + * Invalid starting source/destination offset: 2076 + * Either < 0 which is plainly invalid (lseek may have failed), 2077 + * or > EOF which means that the copy operation is undefined, 2078 + * as by definition there is no data past EOF. 2079 + */ 2080 + if (errno == 0) { 2081 + errno = EINVAL; 2082 + } 2083 + copyfile_warn("Invalid file descriptor offset, cannot perform a sparse copy"); 2084 + goto error_exit; 2085 + } else if (src_start != (off_t) roundup(src_start, iosize) || 2086 + dst_start != (off_t) roundup(dst_start, iosize)) { 2087 + // If the starting offset isn't a multiple of the iosize, we can't do an entire sparse copy. 2088 + // Fall back to copyfile_data(), which will perform a full copy from the starting position. 2089 + return ENOTSUP; 2090 + } 2091 + 2092 + // Make sure that there is at least one hole in this [part of the] file. 2093 + first_hole_offset = lseek(src_fd, src_start, SEEK_HOLE); 2094 + if (first_hole_offset == -1 || first_hole_offset == src_size) { 2095 + /* 2096 + * Either an error occurred, the src starting position is EOF, or there are no 2097 + * holes in this [portion of the] source file. Regardless, we rewind the source file 2098 + * and return ENOTSUP so copyfile_data() can attempt a full copy. 2099 + */ 2100 + if (lseek(src_fd, src_start, SEEK_SET) == -1) { 2101 + goto error_exit; 2102 + } 2103 + return ENOTSUP; 2104 + } 2105 + 2106 + // We are ready to begin copying. 2107 + // First, truncate the destination file to zero out any existing contents. 2108 + // Then, truncate it again to its eventual size. 2109 + if (ftruncate(dst_fd, dst_start) == -1) { 2110 + copyfile_warn("Could not zero destination file before copy"); 2111 + goto error_exit; 2112 + } else if (ftruncate(dst_fd, dst_start + src_size - src_start) == -1) { 2113 + copyfile_warn("Could not set destination file size before copy"); 2114 + goto error_exit; 2115 + } 2116 + 2117 + // Set the source's offset to the first data section. 2118 + current_src_offset = lseek(src_fd, src_start, SEEK_DATA); 2119 + if (current_src_offset == -1) { 2120 + if (errno == ENXIO) { 2121 + // There are no more data sections in the file, so there's nothing to copy. 2122 + goto set_total_copied; 2123 + } 2124 + goto error_exit; 2125 + } 2126 + 2127 + // Now, current_src_offset points at the start of src's first data region. 2128 + // Update dst_fd to point to the same offset (respecting its start). 2129 + if (lseek(dst_fd, dst_start + current_src_offset - src_start, SEEK_SET) == -1) { 2130 + copyfile_warn("failed to set dst to first data section"); 2131 + goto error_exit; 2132 + } 2133 + 2134 + // Allocate a temporary buffer to copy data sections into. 2135 + bp = malloc(iosize); 2136 + if (bp == NULL) { 2137 + copyfile_warn("No memory for copy buffer"); 2138 + goto error_exit; 2139 + } 2140 + 2141 + /* 2142 + * Performing a sparse copy: 2143 + * While our source fd points to a data section (and is < EOF), read iosize bytes in. 2144 + * Then, write those bytes to the dest fd, using the same iosize. 2145 + * Finally, update our source and dest fds to point to the next data section. 2146 + */ 2147 + while ((nread = read(src_fd, bp, iosize)) > 0) { 2148 + ssize_t nwritten; 2149 + size_t left = nread; 2150 + void *ptr = bp; 2151 + int loop = 0; 2152 + 2153 + while (left > 0) { 2154 + nwritten = write(dst_fd, ptr, left); 2155 + switch (nwritten) { 2156 + case 0: 2157 + if (++loop > 5) { 2158 + copyfile_warn("writing to output %d times resulted in 0 bytes written", loop); 2159 + errno = EAGAIN; 2160 + goto error_exit; 2161 + } 2162 + break; 2163 + case -1: 2164 + copyfile_warn("writing to output file failed"); 2165 + if (status) { 2166 + int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 2167 + if (rv == COPYFILE_SKIP) { // Skip the data copy 2168 + errno = 0; 2169 + goto exit; 2170 + } else if (rv == COPYFILE_CONTINUE) { // Retry the write 2171 + errno = 0; 2172 + continue; 2173 + } 2174 + } 2175 + // If we get here, we either have no callback or it didn't tell us to continue. 2176 + goto error_exit; 2177 + break; 2178 + default: 2179 + left -= nwritten; 2180 + ptr = ((char*)ptr) + nwritten; 2181 + loop = 0; 2182 + break; 2183 + } 2184 + s->totalCopied += nwritten; 2185 + if (status) { 2186 + int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); 2187 + if (rv == COPYFILE_QUIT) { 2188 + errno = ECANCELED; 2189 + goto error_exit; 2190 + } 2191 + } 2192 + } 2193 + current_src_offset += nread; 2194 + 2195 + // Find the next area of src_fd to copy. 2196 + // Since data sections can be any length, we need see if current_src_offset points 2197 + // at a hole. 2198 + // If we get ENXIO, we're done copying (the last part of the file was a data section). 2199 + // If this is not a hole, we do not need to alter current_src_offset yet. 2200 + // If this is a hole, then we need to look for the next data section. 2201 + next_hole_offset = lseek(src_fd, current_src_offset, SEEK_HOLE); 2202 + if (next_hole_offset == -1) { 2203 + if (errno == ENXIO) { 2204 + break; // We're done copying data sections. 2205 + } 2206 + copyfile_warn("unable to find next hole in file during copy"); 2207 + goto error_exit; 2208 + } else if (next_hole_offset != current_src_offset) { 2209 + // Keep copying this data section (we must rewind src_fd to current_src_offset). 2210 + if (lseek(src_fd, current_src_offset, SEEK_SET) == -1) { 2211 + goto error_exit; 2212 + } 2213 + continue; 2214 + } 2215 + 2216 + // If we get here, we need to find the next data section to copy. 2217 + next_src_offset = lseek(src_fd, current_src_offset, SEEK_DATA); 2218 + if (next_src_offset == -1) { 2219 + if (errno == ENXIO) { 2220 + // There are no more data sections in this file, so we're done with the copy. 2221 + break; 2222 + } 2223 + 2224 + copyfile_warn("unable to advance src to next data section"); 2225 + goto error_exit; 2226 + } 2227 + 2228 + // Advance the dst_fd to match (taking into account where it started). 2229 + if (lseek(dst_fd, dst_start + (next_src_offset - src_start), SEEK_SET) == -1) { 2230 + copyfile_warn("unable to advance dst to next data section"); 2231 + goto error_exit; 2232 + } 2233 + 2234 + current_src_offset = next_src_offset; 2235 + } 2236 + if (nread < 0) { 2237 + copyfile_warn("error %d reading from %s", errno, s->src ? s->src : "(null src)"); 2238 + goto error_exit; 2239 + } 2240 + 2241 + // Punch holes where possible if needed. 2242 + if (use_punchhole) { 2243 + struct fpunchhole punchhole_args; 2244 + off_t hole_start = first_hole_offset, hole_end; 2245 + bool trailing_hole = true; 2246 + 2247 + // First, reset the source and destination file descriptors. 2248 + if (lseek(src_fd, src_start, SEEK_SET) == -1 || lseek(dst_fd, dst_start, SEEK_SET) == -1) { 2249 + copyfile_warn("unable to reset file descriptors to punch holes"); 2250 + // We have still copied the data, so there's no need to return an error here. 2251 + goto set_total_copied; 2252 + } 2253 + 2254 + // Now, find holes in the source (first_hole_offset already points to a source hole), 2255 + // determining their length by the presence of a data section. 2256 + while ((hole_end = lseek(src_fd, hole_start + (off_t) iosize, SEEK_DATA)) != -1) { 2257 + memset(&punchhole_args, 0, sizeof(punchhole_args)); 2258 + 2259 + // Fix up the offset and length for the destination file. 2260 + punchhole_args.fp_offset = hole_start - src_start + dst_start; 2261 + punchhole_args.fp_length = hole_end - hole_start; 2262 + if (fcntl(dst_fd, F_PUNCHHOLE, &punchhole_args) == -1) { 2263 + copyfile_warn("unable to punch hole in destination file, offset %lld length %lld", 2264 + hole_start - src_start + dst_start, hole_end - hole_start); 2265 + goto set_total_copied; 1774 2266 } 1775 - } else { 1776 - if (!qtn_file_init_with_path(qinfo, s->src)) { 1777 - qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL); 2267 + 2268 + // Now, find the start of the next hole. 2269 + hole_start = lseek(src_fd, hole_end, SEEK_HOLE); 2270 + if (hole_start == -1 || hole_start == src_size) { 2271 + // No more holes (or lseek failed), so break. 2272 + trailing_hole = false; 2273 + break; 1778 2274 } 1779 2275 } 1780 - qtn_file_free(qinfo); 1781 - ret |= qret; 2276 + 2277 + if ((hole_end == -1 || hole_start == -1) && errno != ENXIO) { 2278 + // A call to lseek() failed. Hole punching is best effort, so exit. 2279 + copyfile_warn("lseek during hole punching failed"); 2280 + goto set_total_copied; 2281 + } 2282 + 2283 + // We will still have a trailing hole to punch if the last lseek(SEEK_HOLE) succeeded. 2284 + if (trailing_hole) { 2285 + // Since we can only punch iosize-aligned holes, we must make sure the last hole 2286 + // is iosize-aligned. Unfortunately, no good truncate macros are in scope here, 2287 + // so we must round down the end of the trailing hole to an iosize boundary ourselves. 2288 + hole_end = (src_size % iosize == 0) ? src_size : roundup(src_size, iosize) - iosize; 2289 + 2290 + memset(&punchhole_args, 0, sizeof(punchhole_args)); 2291 + punchhole_args.fp_offset = hole_start - src_start + dst_start; 2292 + punchhole_args.fp_length = hole_end - hole_start; 2293 + if (fcntl(dst_fd, F_PUNCHHOLE, &punchhole_args) == -1) { 2294 + copyfile_warn("unable to punch trailing hole in destination file, offset %lld", 2295 + hole_start - src_start + dst_start); 2296 + goto set_total_copied; 2297 + } 2298 + } 2299 + } 2300 + 2301 + set_total_copied: 2302 + // Since we don't know in advance how many bytes we're copying, we advance this number 2303 + // as we copy, but to match copyfile_data() we set it here to the amount of bytes that would 2304 + // have been transferred in a full copy. 2305 + s->totalCopied = src_size - src_start; 2306 + 2307 + exit: 2308 + if (bp) { 2309 + free(bp); 2310 + bp = NULL; 1782 2311 } 1783 - } 1784 - return ret; 2312 + 2313 + return rc; 2314 + 2315 + error_exit: 2316 + s->err = errno; 2317 + rc = -1; 2318 + goto exit; 1785 2319 } 1786 2320 1787 2321 /* ··· 1792 2326 */ 1793 2327 static int copyfile_data(copyfile_state_t s) 1794 2328 { 1795 - size_t blen; 1796 - char *bp = 0; 1797 - ssize_t nread; 1798 - int ret = 0; 1799 - size_t iBlocksize = 0; 1800 - size_t oBlocksize = 0; 1801 - const size_t onegig = 1 << 30; 1802 - struct statfs sfs; 1803 - copyfile_callback_t status = s->statuscb; 2329 + size_t blen; 2330 + char *bp = 0; 2331 + ssize_t nread; 2332 + int ret = 0; 2333 + size_t iBlocksize = 0, iMinblocksize = 0; 2334 + size_t oBlocksize = 0, oMinblocksize = 0; // If 0, we don't support sparse copying. 2335 + const size_t blocksize_limit = 1 << 30; // 1 GiB 2336 + struct statfs sfs; 2337 + copyfile_callback_t status = s->statuscb; 1804 2338 1805 - /* Unless it's a normal file, we don't copy. For now, anyway */ 1806 - if ((s->sb.st_mode & S_IFMT) != S_IFREG) 1807 - return 0; 2339 + /* Unless it's a normal file, we don't copy. For now, anyway */ 2340 + if ((s->sb.st_mode & S_IFMT) != S_IFREG) 2341 + return 0; 1808 2342 1809 2343 #ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION 1810 - if (s->internal_flags & cfSawDecmpEA) { 1811 - if (s->sb.st_flags & UF_COMPRESSED) { 1812 - if ((s->flags & COPYFILE_STAT) == 0) { 1813 - if (fchflags(s->dst_fd, UF_COMPRESSED) == 0) { 1814 - goto exit; 2344 + if (s->internal_flags & cfSawDecmpEA) { 2345 + if (s->sb.st_flags & UF_COMPRESSED) { 2346 + if ((s->flags & COPYFILE_STAT) == 0) { 2347 + if (fchflags(s->dst_fd, UF_COMPRESSED) == 0) { 2348 + goto exit; 2349 + } 2350 + } 1815 2351 } 1816 - } 1817 2352 } 1818 - } 1819 2353 #endif 1820 - 1821 - if (fstatfs(s->src_fd, &sfs) == -1) { 1822 - iBlocksize = s->sb.st_blksize; 1823 - } else { 1824 - iBlocksize = sfs.f_iosize; 1825 - } 2354 + 2355 + // Calculate the input and output block sizes. 2356 + // Our output block size can be no greater than our input block size. 2357 + if (fstatfs(s->src_fd, &sfs) == -1) { 2358 + iBlocksize = s->sb.st_blksize; 2359 + } else { 2360 + iBlocksize = sfs.f_iosize; 2361 + iMinblocksize = sfs.f_bsize; 2362 + } 2363 + 2364 + if (fstatfs(s->dst_fd, &sfs) == -1) { 2365 + oBlocksize = iBlocksize; 2366 + } else { 2367 + oBlocksize = (sfs.f_iosize == 0) ? iBlocksize : MIN((size_t) sfs.f_iosize, iBlocksize); 2368 + oMinblocksize = sfs.f_bsize; 2369 + } 2370 + 2371 + // 6453525 and 34848916 require us to limit our blocksize to resonable values. 2372 + if ((size_t) s->sb.st_size < iBlocksize && iMinblocksize > 0) { 2373 + copyfile_debug(3, "rounding up block size from fsize: %lld to multiple of %zu\n", s->sb.st_size, iMinblocksize); 2374 + iBlocksize = roundup((size_t) s->sb.st_size, iMinblocksize); 2375 + oBlocksize = MIN(oBlocksize, iBlocksize); 2376 + } 2377 + 2378 + if (iBlocksize > blocksize_limit) { 2379 + iBlocksize = blocksize_limit; 2380 + oBlocksize = MIN(oBlocksize, iBlocksize); 2381 + } 1826 2382 1827 - /* Work-around for 6453525, limit blocksize to 1G */ 1828 - if (iBlocksize > onegig) { 1829 - iBlocksize = onegig; 1830 - } 2383 + copyfile_debug(3, "input block size: %zu output block size: %zu\n", iBlocksize, oBlocksize); 1831 2384 1832 - if ((bp = malloc(iBlocksize)) == NULL) 1833 - return -1; 2385 + s->totalCopied = 0; 1834 2386 1835 - if (fstatfs(s->dst_fd, &sfs) == -1 || sfs.f_iosize == 0) { 1836 - oBlocksize = iBlocksize; 1837 - } else { 1838 - oBlocksize = sfs.f_iosize; 1839 - if (oBlocksize > onegig) 1840 - oBlocksize = onegig; 1841 - } 2387 + // If requested, attempt a sparse copy. 2388 + if (s->flags & COPYFILE_DATA_SPARSE) { 2389 + // Check if the source & destination volumes both support sparse files. 2390 + long min_hole_size = MIN(fpathconf(s->src_fd, _PC_MIN_HOLE_SIZE), 2391 + fpathconf(s->dst_fd, _PC_MIN_HOLE_SIZE)); 1842 2392 1843 - blen = iBlocksize; 2393 + // If holes are supported on both the source and dest volumes, make sure our min_hole_size 2394 + // is reasonable: if it's smaller than the source/dest block size, 2395 + // our copy performance will suffer (and we may not create sparse files). 2396 + if (iMinblocksize > 0 && oMinblocksize > 0 && (size_t) min_hole_size >= iMinblocksize 2397 + && (size_t) min_hole_size >= oMinblocksize) { 2398 + // Do the copy. 2399 + ret = copyfile_data_sparse(s, iMinblocksize, oMinblocksize); 1844 2400 1845 - s->totalCopied = 0; 1846 - /* If supported, do preallocation for Xsan / HFS volumes */ 2401 + // If we returned an error, exit gracefully. 2402 + // If sparse copying is not supported, we try full copying if allowed by our caller. 2403 + if (ret == 0) { 2404 + goto exit; 2405 + } else if (ret != ENOTSUP) { 2406 + goto exit; 2407 + } 2408 + ret = 0; 2409 + } 2410 + 2411 + // Make sure we're allowed to perform non-sparse copying. 2412 + if (!(s->flags & COPYFILE_DATA)) { 2413 + ret = -1; 2414 + errno = ENOTSUP; 2415 + goto exit; 2416 + } 2417 + } 2418 + 2419 + if ((bp = malloc(iBlocksize)) == NULL) 2420 + return -1; 2421 + 2422 + blen = iBlocksize; 2423 + 2424 + /* If supported, do preallocation for Xsan / HFS / apfs volumes */ 1847 2425 #ifdef F_PREALLOCATE 1848 - { 1849 - fstore_t fst; 2426 + { 2427 + off_t dst_bytes_allocated = 0; 2428 + struct stat dst_sb; 2429 + 2430 + if (fstat(s->dst_fd, &dst_sb) == 0) { 2431 + // The destination may already have 2432 + // preallocated space we can use. 2433 + dst_bytes_allocated = dst_sb.st_blocks * S_BLKSIZE; 2434 + } 2435 + 2436 + if (dst_bytes_allocated < s->sb.st_size) { 2437 + fstore_t fst; 2438 + 2439 + fst.fst_flags = 0; 2440 + fst.fst_posmode = F_PEOFPOSMODE; 2441 + fst.fst_offset = 0; 2442 + fst.fst_length = s->sb.st_size - dst_bytes_allocated; 1850 2443 1851 - fst.fst_flags = 0; 1852 - fst.fst_posmode = F_PEOFPOSMODE; 1853 - fst.fst_offset = 0; 1854 - fst.fst_length = s->sb.st_size; 1855 - /* Ignore errors; this is merely advisory. */ 1856 - (void)fcntl(s->dst_fd, F_PREALLOCATE, &fst); 1857 - } 2444 + copyfile_debug(3, "preallocating %lld bytes on destination", fst.fst_length); 2445 + /* Ignore errors; this is merely advisory. */ 2446 + (void)fcntl(s->dst_fd, F_PREALLOCATE, &fst); 2447 + } 2448 + } 1858 2449 #endif 1859 2450 1860 - while ((nread = read(s->src_fd, bp, blen)) > 0) 1861 - { 1862 - ssize_t nwritten; 1863 - size_t left = nread; 1864 - void *ptr = bp; 1865 - int loop = 0; 2451 + while ((nread = read(s->src_fd, bp, blen)) > 0) 2452 + { 2453 + ssize_t nwritten; 2454 + size_t left = nread; 2455 + void *ptr = bp; 2456 + int loop = 0; 1866 2457 1867 - while (left > 0) { 1868 - nwritten = write(s->dst_fd, ptr, MIN(left, oBlocksize)); 1869 - switch (nwritten) { 1870 - case 0: 1871 - if (++loop > 5) { 1872 - copyfile_warn("writing to output %d times resulted in 0 bytes written", loop); 1873 - ret = -1; 1874 - s->err = EAGAIN; 1875 - goto exit; 2458 + while (left > 0) { 2459 + nwritten = write(s->dst_fd, ptr, MIN(left, oBlocksize)); 2460 + switch (nwritten) { 2461 + case 0: 2462 + if (++loop > 5) { 2463 + copyfile_warn("writing to output %d times resulted in 0 bytes written", loop); 2464 + ret = -1; 2465 + s->err = EAGAIN; 2466 + goto exit; 2467 + } 2468 + break; 2469 + case -1: 2470 + copyfile_warn("writing to output file got error"); 2471 + if (status) { 2472 + int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 2473 + if (rv == COPYFILE_SKIP) { // Skip the data copy 2474 + ret = 0; 2475 + goto exit; 2476 + } 2477 + if (rv == COPYFILE_CONTINUE) { // Retry the write 2478 + errno = 0; 2479 + continue; 2480 + } 2481 + } 2482 + ret = -1; 2483 + goto exit; 2484 + default: 2485 + left -= nwritten; 2486 + ptr = ((char*)ptr) + nwritten; 2487 + loop = 0; 2488 + break; 1876 2489 } 1877 - break; 1878 - case -1: 1879 - copyfile_warn("writing to output file got error"); 2490 + s->totalCopied += nwritten; 1880 2491 if (status) { 1881 - int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 1882 - if (rv == COPYFILE_SKIP) { // Skip the data copy 1883 - ret = 0; 2492 + int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); 2493 + if (rv == COPYFILE_QUIT) { 2494 + ret = -1; s->err = errno = ECANCELED; 1884 2495 goto exit; 1885 2496 } 1886 - if (rv == COPYFILE_CONTINUE) { // Retry the write 1887 - errno = 0; 1888 - continue; 1889 - } 1890 2497 } 1891 - ret = -1; 1892 - goto exit; 1893 - default: 1894 - left -= nwritten; 1895 - ptr = ((char*)ptr) + nwritten; 1896 - loop = 0; 1897 - break; 1898 2498 } 1899 - s->totalCopied += nwritten; 1900 - if (status) { 1901 - int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); 1902 - if (rv == COPYFILE_QUIT) { 1903 - ret = -1; s->err = errno = ECANCELED; 1904 - goto exit; 1905 - } 1906 - } 2499 + } 2500 + if (nread < 0) 2501 + { 2502 + copyfile_warn("reading from %s", s->src ? s->src : "(null src)"); 2503 + ret = -1; 2504 + goto exit; 1907 2505 } 1908 - } 1909 - if (nread < 0) 1910 - { 1911 - copyfile_warn("reading from %s", s->src ? s->src : "(null src)"); 1912 - ret = -1; 1913 - goto exit; 1914 - } 1915 2506 1916 - if (ftruncate(s->dst_fd, s->totalCopied) < 0) 1917 - { 1918 - ret = -1; 1919 - goto exit; 1920 - } 2507 + if (ftruncate(s->dst_fd, s->totalCopied) < 0) 2508 + { 2509 + ret = -1; 2510 + goto exit; 2511 + } 1921 2512 1922 2513 exit: 1923 - if (ret == -1) 1924 - { 1925 - s->err = errno; 1926 - } 1927 - free(bp); 1928 - return ret; 2514 + if (ret == -1) 2515 + { 2516 + s->err = errno; 2517 + } 2518 + free(bp); 2519 + return ret; 1929 2520 } 1930 2521 1931 2522 /* ··· 1936 2527 */ 1937 2528 static int copyfile_security(copyfile_state_t s) 1938 2529 { 1939 - int copied = 0; 1940 - struct stat sb; 1941 - acl_t acl_src = NULL, acl_tmp = NULL, acl_dst = NULL; 1942 - int ret = 0; 1943 - filesec_t tmp_fsec = NULL; 1944 - filesec_t fsec_dst = filesec_init(); 2530 + int copied = 0; 2531 + struct stat sb; 2532 + acl_t acl_src = NULL, acl_tmp = NULL, acl_dst = NULL; 2533 + int ret = 0; 2534 + filesec_t tmp_fsec = NULL; 2535 + filesec_t fsec_dst = filesec_init(); 1945 2536 1946 - if (fsec_dst == NULL) 1947 - return -1; 2537 + if (fsec_dst == NULL) 2538 + return -1; 1948 2539 1949 2540 1950 - if (COPYFILE_ACL & s->flags) 1951 - { 1952 - if (filesec_get_property(s->fsec, FILESEC_ACL, &acl_src)) 2541 + if (COPYFILE_ACL & s->flags) 1953 2542 { 1954 - if (errno == ENOENT) 1955 - acl_src = NULL; 1956 - else 1957 - goto error_exit; 1958 - } 2543 + if (filesec_get_property(s->fsec, FILESEC_ACL, &acl_src)) 2544 + { 2545 + if (errno == ENOENT) 2546 + acl_src = NULL; 2547 + else 2548 + goto error_exit; 2549 + } 1959 2550 1960 - /* grab the destination acl 1961 - cannot assume it's empty due to inheritance 1962 - */ 1963 - if(fstatx_np(s->dst_fd, &sb, fsec_dst)) 1964 - goto error_exit; 2551 + /* grab the destination acl 2552 + cannot assume it's empty due to inheritance 2553 + */ 2554 + if(fstatx_np(s->dst_fd, &sb, fsec_dst)) 2555 + goto error_exit; 1965 2556 1966 - if (filesec_get_property(fsec_dst, FILESEC_ACL, &acl_dst)) 1967 - { 1968 - if (errno == ENOENT) 1969 - acl_dst = NULL; 1970 - else 1971 - goto error_exit; 1972 - } 2557 + if (filesec_get_property(fsec_dst, FILESEC_ACL, &acl_dst)) 2558 + { 2559 + if (errno == ENOENT) 2560 + acl_dst = NULL; 2561 + else 2562 + goto error_exit; 2563 + } 1973 2564 1974 - if (acl_src == NULL && acl_dst == NULL) 1975 - goto no_acl; 2565 + if (acl_src == NULL && acl_dst == NULL) 2566 + goto no_acl; 1976 2567 1977 - acl_tmp = acl_init(4); 1978 - if (acl_tmp == NULL) 1979 - goto error_exit; 2568 + acl_tmp = acl_init(4); 2569 + if (acl_tmp == NULL) 2570 + goto error_exit; 1980 2571 1981 - if (acl_src) { 1982 - acl_entry_t ace = NULL; 1983 - acl_entry_t tmp = NULL; 1984 - for (copied = 0; 1985 - acl_get_entry(acl_src, 1986 - ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, 1987 - &ace) == 0;) 1988 - { 1989 - acl_flagset_t flags = { 0 }; 1990 - acl_get_flagset_np(ace, &flags); 1991 - if (!acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) 1992 - { 1993 - if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1) 1994 - goto error_exit; 2572 + if (acl_src) { 2573 + acl_entry_t ace = NULL; 2574 + acl_entry_t tmp = NULL; 2575 + for (copied = 0; 2576 + acl_get_entry(acl_src, 2577 + ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, 2578 + &ace) == 0;) 2579 + { 2580 + acl_flagset_t flags = { 0 }; 2581 + acl_get_flagset_np(ace, &flags); 2582 + if (!acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) 2583 + { 2584 + if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1) 2585 + goto error_exit; 1995 2586 1996 - if ((ret = acl_copy_entry(tmp, ace)) == -1) 1997 - goto error_exit; 2587 + if ((ret = acl_copy_entry(tmp, ace)) == -1) 2588 + goto error_exit; 1998 2589 1999 - copyfile_debug(2, "copied acl entry from %s to %s", 2000 - s->src ? s->src : "(null src)", 2001 - s->dst ? s->dst : "(null tmp)"); 2002 - copied++; 2003 - } 2590 + copyfile_debug(2, "copied acl entry from %s to %s", 2591 + s->src ? s->src : "(null src)", 2592 + s->dst ? s->dst : "(null tmp)"); 2593 + copied++; 2594 + } 2595 + } 2004 2596 } 2005 - } 2006 - if (acl_dst) { 2007 - acl_entry_t ace = NULL; 2008 - acl_entry_t tmp = NULL; 2009 - acl_flagset_t flags = { 0 }; 2010 - for (copied = 0;acl_get_entry(acl_dst, 2011 - ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, 2012 - &ace) == 0;) 2013 - { 2014 - acl_get_flagset_np(ace, &flags); 2015 - if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) 2016 - { 2017 - if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1) 2018 - goto error_exit; 2597 + if (acl_dst) { 2598 + acl_entry_t ace = NULL; 2599 + acl_entry_t tmp = NULL; 2600 + acl_flagset_t flags = { 0 }; 2601 + for (copied = 0;acl_get_entry(acl_dst, 2602 + ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, 2603 + &ace) == 0;) 2604 + { 2605 + acl_get_flagset_np(ace, &flags); 2606 + if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) 2607 + { 2608 + if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1) 2609 + goto error_exit; 2019 2610 2020 - if ((ret = acl_copy_entry(tmp, ace)) == -1) 2021 - goto error_exit; 2611 + if ((ret = acl_copy_entry(tmp, ace)) == -1) 2612 + goto error_exit; 2022 2613 2023 - copyfile_debug(2, "copied acl entry from %s to %s", 2024 - s->src ? s->src : "(null dst)", 2025 - s->dst ? s->dst : "(null tmp)"); 2026 - copied++; 2027 - } 2614 + copyfile_debug(2, "copied acl entry from %s to %s", 2615 + s->src ? s->src : "(null dst)", 2616 + s->dst ? s->dst : "(null tmp)"); 2617 + copied++; 2618 + } 2619 + } 2620 + } 2621 + if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_tmp)) 2622 + { 2623 + copyfile_debug(3, "altered acl"); 2028 2624 } 2029 2625 } 2030 - if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_tmp)) 2031 - { 2032 - copyfile_debug(3, "altered acl"); 2626 + no_acl: 2627 + /* 2628 + * The following code is attempting to ensure that only the requested 2629 + * security information gets copied over to the destination file. 2630 + * We essentially have four cases: COPYFILE_ACL, COPYFILE_STAT, 2631 + * COPYFILE_(STAT|ACL), and none (in which case, we wouldn't be in 2632 + * this function). 2633 + * 2634 + * If we have both flags, we copy everything; if we have ACL but not STAT, 2635 + * we remove the POSIX information from the filesec object, and apply the 2636 + * ACL; if we have STAT but not ACL, then we just use fchmod(), and ignore 2637 + * the extended version. 2638 + */ 2639 + tmp_fsec = filesec_dup(s->fsec); 2640 + if (tmp_fsec == NULL) { 2641 + goto error_exit; 2033 2642 } 2034 - } 2035 - no_acl: 2036 - /* 2037 - * The following code is attempting to ensure that only the requested 2038 - * security information gets copied over to the destination file. 2039 - * We essentially have four cases: COPYFILE_ACL, COPYFILE_STAT, 2040 - * COPYFILE_(STAT|ACL), and none (in which case, we wouldn't be in 2041 - * this function). 2042 - * 2043 - * If we have both flags, we copy everything; if we have ACL but not STAT, 2044 - * we remove the POSIX information from the filesec object, and apply the 2045 - * ACL; if we have STAT but not ACL, then we just use fchmod(), and ignore 2046 - * the extended version. 2047 - */ 2048 - tmp_fsec = filesec_dup(s->fsec); 2049 - if (tmp_fsec == NULL) { 2050 - goto error_exit; 2051 - } 2052 2643 2053 - switch (COPYFILE_SECURITY & s->flags) { 2054 - case COPYFILE_ACL: 2055 - copyfile_unset_posix_fsec(tmp_fsec); 2056 - /* FALLTHROUGH */ 2057 - case COPYFILE_ACL | COPYFILE_STAT: 2058 - if (fchmodx_np(s->dst_fd, tmp_fsec) < 0) { 2059 - acl_t acl = NULL; 2060 - /* 2061 - * The call could have failed for a number of reasons, since 2062 - * it does a number of things: it changes the mode of the file, 2063 - * sets the owner and group, and applies an ACL (if one exists). 2064 - * The typical failure is going to be trying to set the group of 2065 - * the destination file to match the source file, when the process 2066 - * doesn't have permission to put files in that group. We try to 2067 - * work around this by breaking the steps out and doing them 2068 - * discretely. We don't care if the fchown fails, but we do care 2069 - * if the mode or ACL can't be set. For historical reasons, we 2070 - * simply log those failures, however. 2071 - * 2072 - * Big warning here: we may NOT have COPYFILE_STAT set, since 2073 - * we fell-through from COPYFILE_ACL. So check for the fchmod. 2074 - */ 2644 + switch (COPYFILE_SECURITY & s->flags) { 2645 + case COPYFILE_ACL: 2646 + copyfile_unset_posix_fsec(tmp_fsec); 2647 + /* FALLTHROUGH */ 2648 + case COPYFILE_ACL | COPYFILE_STAT: 2649 + if (fchmodx_np(s->dst_fd, tmp_fsec) < 0) { 2650 + acl_t acl = NULL; 2651 + /* 2652 + * The call could have failed for a number of reasons, since 2653 + * it does a number of things: it changes the mode of the file, 2654 + * sets the owner and group, and applies an ACL (if one exists). 2655 + * The typical failure is going to be trying to set the group of 2656 + * the destination file to match the source file, when the process 2657 + * doesn't have permission to put files in that group. We try to 2658 + * work around this by breaking the steps out and doing them 2659 + * discretely. We don't care if the fchown fails, but we do care 2660 + * if the mode or ACL can't be set. For historical reasons, we 2661 + * simply log those failures, however. 2662 + * 2663 + * Big warning here: we may NOT have COPYFILE_STAT set, since 2664 + * we fell-through from COPYFILE_ACL. So check for the fchmod. 2665 + */ 2075 2666 2076 2667 #define NS(x) ((x) ? (x) : "(null string)") 2077 - if ((s->flags & COPYFILE_STAT) && 2078 - fchmod(s->dst_fd, s->sb.st_mode) == -1) { 2079 - copyfile_warn("could not change mode of destination file %s to match source file %s", NS(s->dst), NS(s->src)); 2080 - } 2081 - (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid); 2082 - if (filesec_get_property(tmp_fsec, FILESEC_ACL, &acl) == 0) { 2083 - if (acl_set_fd(s->dst_fd, acl) == -1) { 2084 - copyfile_warn("could not apply acl to destination file %s from source file %s", NS(s->dst), NS(s->src)); 2668 + if ((s->flags & COPYFILE_STAT) && 2669 + fchmod(s->dst_fd, s->sb.st_mode) == -1) { 2670 + copyfile_warn("could not change mode of destination file %s to match source file %s", NS(s->dst), NS(s->src)); 2671 + } 2672 + (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid); 2673 + if (filesec_get_property(tmp_fsec, FILESEC_ACL, &acl) == 0) { 2674 + if (acl_set_fd(s->dst_fd, acl) == -1) { 2675 + copyfile_warn("could not apply acl to destination file %s from source file %s", NS(s->dst), NS(s->src)); 2676 + } 2677 + acl_free(acl); 2678 + } 2085 2679 } 2086 - acl_free(acl); 2087 - } 2088 - } 2089 2680 #undef NS 2090 - break; 2091 - case COPYFILE_STAT: 2092 - (void)fchmod(s->dst_fd, s->sb.st_mode); 2093 - break; 2094 - } 2095 - filesec_free(tmp_fsec); 2681 + break; 2682 + case COPYFILE_STAT: 2683 + (void)fchmod(s->dst_fd, s->sb.st_mode); 2684 + break; 2685 + } 2686 + filesec_free(tmp_fsec); 2096 2687 exit: 2097 - filesec_free(fsec_dst); 2098 - if (acl_src) acl_free(acl_src); 2099 - if (acl_dst) acl_free(acl_dst); 2100 - if (acl_tmp) acl_free(acl_tmp); 2688 + filesec_free(fsec_dst); 2689 + if (acl_src) acl_free(acl_src); 2690 + if (acl_dst) acl_free(acl_dst); 2691 + if (acl_tmp) acl_free(acl_tmp); 2101 2692 2102 - return ret; 2693 + return ret; 2103 2694 2104 2695 error_exit: 2105 - ret = -1; 2106 - goto exit; 2696 + ret = -1; 2697 + goto exit; 2107 2698 2108 2699 } 2109 2700 2110 2701 /* 2111 2702 * Attempt to set the destination file's stat information -- including 2112 2703 * flags and time-related fields -- to the source's. 2704 + * Note that we must set file flags *last*, as setting a flag like 2705 + * UF_IMMUTABLE can prevent us from setting other attributes. 2113 2706 */ 2114 2707 static int copyfile_stat(copyfile_state_t s) 2115 2708 { 2116 - struct timeval tval[2]; 2117 - unsigned int added_flags = 0, dst_flags = 0; 2118 - struct stat dst_sb; 2709 + unsigned int added_flags = 0, dst_flags = 0; 2710 + struct attrlist attrlist; 2711 + struct stat dst_sb; 2712 + struct { 2713 + /* Order of these structs matters for setattrlist. */ 2714 + struct timespec mod_time; 2715 + struct timespec acc_time; 2716 + } ma_times; 2119 2717 2120 - /* 2121 - * NFS doesn't support chflags; ignore errors as a result, since 2122 - * we don't return failure for this. 2123 - */ 2124 - if (s->internal_flags & cfMakeFileInvisible) 2125 - added_flags |= UF_HIDDEN; 2718 + /* Try to set m/atimes using setattrlist(), for nanosecond precision. */ 2719 + memset(&attrlist, 0, sizeof(attrlist)); 2720 + attrlist.bitmapcount = ATTR_BIT_MAP_COUNT; 2721 + attrlist.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME; 2722 + ma_times.mod_time = s->sb.st_mtimespec; 2723 + ma_times.acc_time = s->sb.st_atimespec; 2724 + (void)fsetattrlist(s->dst_fd, &attrlist, &ma_times, sizeof(ma_times), 0); 2126 2725 2127 - /* 2128 - * We need to check if SF_RESTRICTED was set on the destination 2129 - * by the kernel. If it was, don't drop it. 2130 - */ 2131 - if (fstat(s->dst_fd, &dst_sb)) 2132 - return -1; 2133 - if (dst_sb.st_flags & SF_RESTRICTED) 2134 - added_flags |= SF_RESTRICTED; 2726 + /* If this fails, we don't care */ 2727 + (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid); 2728 + 2729 + /* This may have already been done in copyfile_security() */ 2730 + (void)fchmod(s->dst_fd, s->sb.st_mode & ~S_IFMT); 2135 2731 2136 - /* Copy file flags, masking out any we don't want to preserve */ 2137 - dst_flags = (s->sb.st_flags & ~COPYFILE_OMIT_FLAGS) | added_flags; 2138 - (void)fchflags(s->dst_fd, dst_flags); 2732 + /* 2733 + * NFS doesn't support chflags; ignore errors as a result, since 2734 + * we don't return failure for this. 2735 + */ 2736 + if (s->internal_flags & cfMakeFileInvisible) 2737 + added_flags |= UF_HIDDEN; 2139 2738 2140 - /* If this fails, we don't care */ 2141 - (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid); 2739 + /* 2740 + * We need to check if certain flags were set on the destination 2741 + * by the kernel. If they were, don't drop them. 2742 + */ 2743 + if (fstat(s->dst_fd, &dst_sb)) 2744 + return -1; 2745 + added_flags |= (dst_sb.st_flags & COPYFILE_PRESERVE_FLAGS); 2142 2746 2143 - /* This may have already been done in copyfile_security() */ 2144 - (void)fchmod(s->dst_fd, s->sb.st_mode & ~S_IFMT); 2747 + /* 2748 + * The caller requested that copyfile attempts to preserve UF_TRACKED 2749 + * on the destination. This can be used to avoid dangling docIDs when 2750 + * the copy races against a process that sets the flag on newly created 2751 + * documents for instance. 2752 + */ 2753 + if (s->flags & COPYFILE_PRESERVE_DST_TRACKED) { 2754 + added_flags |= (dst_sb.st_flags & UF_TRACKED); 2755 + } 2145 2756 2146 - tval[0].tv_sec = s->sb.st_atime; 2147 - tval[1].tv_sec = s->sb.st_mtime; 2148 - tval[0].tv_usec = tval[1].tv_usec = 0; 2149 - (void)futimes(s->dst_fd, tval); 2757 + /* Copy file flags, masking out any we don't want to preserve */ 2758 + dst_flags = (s->sb.st_flags & ~COPYFILE_OMIT_FLAGS) | added_flags; 2759 + (void)fchflags(s->dst_fd, dst_flags); 2150 2760 2151 - return 0; 2761 + return 0; 2152 2762 } 2153 2763 2154 2764 /* ··· 2163 2773 */ 2164 2774 static int copyfile_xattr(copyfile_state_t s) 2165 2775 { 2166 - char *name; 2167 - char *namebuf, *end; 2168 - ssize_t xa_size; 2169 - void *xa_dataptr; 2170 - ssize_t bufsize = 4096; 2171 - ssize_t asize; 2172 - ssize_t nsize; 2173 - int ret = 0; 2174 - int look_for_decmpea = 0; 2776 + char *name; 2777 + char *namebuf, *end; 2778 + ssize_t xa_size; 2779 + void *xa_dataptr; 2780 + ssize_t bufsize = 4096; 2781 + ssize_t asize; 2782 + ssize_t nsize; 2783 + int ret = 0; 2784 + int look_for_decmpea = 0; 2175 2785 2176 - /* delete EAs on destination */ 2177 - if ((nsize = flistxattr(s->dst_fd, 0, 0, 0)) > 0) 2178 - { 2179 - if ((namebuf = (char *) malloc(nsize)) == NULL) 2180 - return -1; 2181 - else 2182 - nsize = flistxattr(s->dst_fd, namebuf, nsize, 0); 2786 + /* delete EAs on destination */ 2787 + if ((nsize = flistxattr(s->dst_fd, 0, 0, 0)) > 0) 2788 + { 2789 + if ((namebuf = (char *) malloc(nsize)) == NULL) 2790 + return -1; 2791 + else 2792 + nsize = flistxattr(s->dst_fd, namebuf, nsize, 0); 2183 2793 2184 - if (nsize > 0) { 2185 - /* 2186 - * With this, end points to the last byte of the allocated buffer 2187 - * This *should* be NUL, from flistxattr, but if it's not, we can 2188 - * set it anyway -- it'll result in a truncated name, which then 2189 - * shouldn't match when we get them later. 2190 - */ 2191 - end = namebuf + nsize - 1; 2192 - if (*end != 0) 2193 - *end = 0; 2194 - for (name = namebuf; name <= end; name += strlen(name) + 1) { 2195 - /* If the quarantine information shows up as an EA, we skip over it */ 2196 - if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) { 2197 - continue; 2794 + if (nsize > 0) { 2795 + /* 2796 + * With this, end points to the last byte of the allocated buffer 2797 + * This *should* be NUL, from flistxattr, but if it's not, we can 2798 + * set it anyway -- it'll result in a truncated name, which then 2799 + * shouldn't match when we get them later. 2800 + */ 2801 + end = namebuf + nsize - 1; 2802 + if (*end != 0) 2803 + *end = 0; 2804 + for (name = namebuf; name <= end; name += strlen(name) + 1) { 2805 + /* If the quarantine information shows up as an EA, we skip over it */ 2806 + if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) { 2807 + continue; 2808 + } 2809 + fremovexattr(s->dst_fd, name,0); 2810 + } 2198 2811 } 2199 - fremovexattr(s->dst_fd, name,0); 2200 - } 2201 - } 2202 - free(namebuf); 2203 - } else 2204 - if (nsize < 0) 2205 - { 2206 - if (errno == ENOTSUP || errno == EPERM) 2207 - return 0; 2208 - else 2209 - return -1; 2210 - } 2812 + free(namebuf); 2813 + } else 2814 + if (nsize < 0) 2815 + { 2816 + if (errno == ENOTSUP || errno == EPERM) 2817 + return 0; 2818 + else 2819 + return -1; 2820 + } 2211 2821 2212 2822 #ifdef DECMPFS_XATTR_NAME 2213 - if ((s->flags & COPYFILE_DATA) && 2214 - (s->sb.st_flags & UF_COMPRESSED) && 2215 - doesdecmpfs(s->src_fd) && 2216 - doesdecmpfs(s->dst_fd)) { 2217 - look_for_decmpea = XATTR_SHOWCOMPRESSION; 2218 - } 2823 + if ((s->flags & COPYFILE_DATA) && 2824 + (s->sb.st_flags & UF_COMPRESSED) && 2825 + doesdecmpfs(s->src_fd) && 2826 + doesdecmpfs(s->dst_fd)) { 2827 + look_for_decmpea = XATTR_SHOWCOMPRESSION; 2828 + } 2219 2829 #endif 2220 2830 2221 - /* get name list of EAs on source */ 2222 - if ((nsize = flistxattr(s->src_fd, 0, 0, look_for_decmpea)) < 0) 2223 - { 2224 - if (errno == ENOTSUP || errno == EPERM) 2225 - return 0; 2226 - else 2227 - return -1; 2228 - } else 2229 - if (nsize == 0) 2230 - return 0; 2231 - 2232 - if ((namebuf = (char *) malloc(nsize)) == NULL) 2233 - return -1; 2234 - else 2235 - nsize = flistxattr(s->src_fd, namebuf, nsize, look_for_decmpea); 2236 - 2237 - if (nsize <= 0) { 2238 - free(namebuf); 2239 - return (int)nsize; 2240 - } 2241 - 2242 - /* 2243 - * With this, end points to the last byte of the allocated buffer 2244 - * This *should* be NUL, from flistxattr, but if it's not, we can 2245 - * set it anyway -- it'll result in a truncated name, which then 2246 - * shouldn't match when we get them later. 2247 - */ 2248 - end = namebuf + nsize - 1; 2249 - if (*end != 0) 2250 - *end = 0; 2831 + /* get name list of EAs on source */ 2832 + if ((nsize = flistxattr(s->src_fd, 0, 0, look_for_decmpea)) < 0) 2833 + { 2834 + if (errno == ENOTSUP || errno == EPERM) 2835 + return 0; 2836 + else 2837 + return -1; 2838 + } else 2839 + if (nsize == 0) 2840 + return 0; 2251 2841 2252 - if ((xa_dataptr = (void *) malloc(bufsize)) == NULL) { 2253 - free(namebuf); 2254 - return -1; 2255 - } 2842 + if ((namebuf = (char *) malloc(nsize)) == NULL) 2843 + return -1; 2844 + else 2845 + nsize = flistxattr(s->src_fd, namebuf, nsize, look_for_decmpea); 2256 2846 2257 - for (name = namebuf; name <= end; name += strlen(name) + 1) 2258 - { 2259 - if (s->xattr_name) { 2260 - free(s->xattr_name); 2261 - s->xattr_name = NULL; 2847 + if (nsize <= 0) { 2848 + free(namebuf); 2849 + return (int)nsize; 2262 2850 } 2263 2851 2264 - /* If the quarantine information shows up as an EA, we skip over it */ 2265 - if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) 2266 - continue; 2852 + /* 2853 + * With this, end points to the last byte of the allocated buffer 2854 + * This *should* be NUL, from flistxattr, but if it's not, we can 2855 + * set it anyway -- it'll result in a truncated name, which then 2856 + * shouldn't match when we get them later. 2857 + */ 2858 + end = namebuf + nsize - 1; 2859 + if (*end != 0) 2860 + *end = 0; 2267 2861 2268 - if ((xa_size = fgetxattr(s->src_fd, name, 0, 0, 0, look_for_decmpea)) < 0) 2269 - { 2270 - continue; 2862 + if ((xa_dataptr = (void *) malloc(bufsize)) == NULL) { 2863 + free(namebuf); 2864 + return -1; 2271 2865 } 2272 2866 2273 - if (xa_size > bufsize) 2867 + for (name = namebuf; name <= end; name += strlen(name) + 1) 2274 2868 { 2275 - void *tdptr = xa_dataptr; 2276 - bufsize = xa_size; 2277 - if ((xa_dataptr = 2278 - (void *) realloc((void *) xa_dataptr, bufsize)) == NULL) 2279 - { 2280 - free(tdptr); 2281 - ret = -1; 2282 - continue; 2283 - } 2284 - } 2869 + if (s->xattr_name) { 2870 + free(s->xattr_name); 2871 + s->xattr_name = NULL; 2872 + } 2285 2873 2286 - if ((asize = fgetxattr(s->src_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea)) < 0) 2287 - { 2288 - continue; 2289 - } 2874 + /* If the quarantine information shows up as an EA, we skip over it */ 2875 + if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) 2876 + continue; 2290 2877 2291 - if (xa_size != asize) 2292 - xa_size = asize; 2878 + if ((xa_size = fgetxattr(s->src_fd, name, 0, 0, 0, look_for_decmpea)) < 0) 2879 + { 2880 + continue; 2881 + } 2882 + 2883 + if (xa_size > bufsize) 2884 + { 2885 + void *tdptr = xa_dataptr; 2886 + bufsize = xa_size; 2887 + if ((xa_dataptr = 2888 + (void *) realloc((void *) xa_dataptr, bufsize)) == NULL) 2889 + { 2890 + free(tdptr); 2891 + ret = -1; 2892 + continue; 2893 + } 2894 + } 2895 + 2896 + if ((asize = fgetxattr(s->src_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea)) < 0) 2897 + { 2898 + continue; 2899 + } 2900 + 2901 + if (xa_size != asize) 2902 + xa_size = asize; 2293 2903 2294 2904 #ifdef DECMPFS_XATTR_NAME 2295 - if (strncmp(name, DECMPFS_XATTR_NAME, end-name) == 0) 2296 - { 2297 - decmpfs_disk_header *hdr = xa_dataptr; 2905 + if (strncmp(name, DECMPFS_XATTR_NAME, end-name) == 0) 2906 + { 2907 + decmpfs_disk_header *hdr = xa_dataptr; 2298 2908 2299 - /* 2300 - * If the EA has the decmpfs name, but is too 2301 - * small, or doesn't have the right magic number, 2302 - * or isn't the right type, we'll just skip it. 2303 - * This means it won't end up in the destination 2304 - * file, and data copy will happen normally. 2305 - */ 2306 - if ((size_t)xa_size < sizeof(decmpfs_disk_header)) { 2307 - continue; 2308 - } 2309 - if (OSSwapLittleToHostInt32(hdr->compression_magic) != DECMPFS_MAGIC) { 2310 - continue; 2311 - } 2312 - /* 2313 - * From AppleFSCompression documentation: 2314 - * "It is incumbent on the aware copy engine to identify 2315 - * the type of compression being used, and to perform an 2316 - * unaware copy of any file it does not recognize." 2317 - * 2318 - * Compression Types are defined in: 2319 - * "AppleFSCompression/Common/compressorCommon.h" 2320 - * 2321 - * Unfortunately, they don't provide a way to dynamically 2322 - * determine what possible compression_type values exist, 2323 - * so we have to update this every time a new compression_type 2324 - * is added (Types 7->10 were added in Yosemite) 2325 - * 2326 - * Ubiquity faulting file compression type 0x80000001 are 2327 - * deprecated as of Yosemite, per rdar://17714998 don't copy the 2328 - * decmpfs xattr on these files, zero byte files are safer 2329 - * than a fault nobody knows how to handle. 2330 - */ 2331 - switch (OSSwapLittleToHostInt32(hdr->compression_type)) { 2332 - case 3: /* zlib-compressed data in xattr */ 2333 - case 4: /* 64k chunked zlib-compressed data in resource fork */ 2909 + /* 2910 + * If the EA has the decmpfs name, but is too 2911 + * small, or doesn't have the right magic number, 2912 + * or isn't the right type, we'll just skip it. 2913 + * This means it won't end up in the destination 2914 + * file, and data copy will happen normally. 2915 + */ 2916 + if ((size_t)xa_size < sizeof(decmpfs_disk_header)) { 2917 + continue; 2918 + } 2919 + if (OSSwapLittleToHostInt32(hdr->compression_magic) != DECMPFS_MAGIC) { 2920 + continue; 2921 + } 2922 + /* 2923 + * From AppleFSCompression documentation: 2924 + * "It is incumbent on the aware copy engine to identify 2925 + * the type of compression being used, and to perform an 2926 + * unaware copy of any file it does not recognize." 2927 + * 2928 + * Compression Types are defined in: 2929 + * "AppleFSCompression/Common/compressorCommon.h" 2930 + * 2931 + * Unfortunately, they don't provide a way to dynamically 2932 + * determine what possible compression_type values exist, 2933 + * so we have to update this every time a new compression_type 2934 + * is added. Types 7->10 were added in 10.10, Types 11 & 12 2935 + * were added in 10.11. 2936 + * 2937 + * Ubiquity faulting file compression type 0x80000001 are 2938 + * deprecated as of Yosemite, per rdar://17714998 don't copy the 2939 + * decmpfs xattr on these files, zero byte files are safer 2940 + * than a fault nobody knows how to handle. 2941 + */ 2942 + switch (OSSwapLittleToHostInt32(hdr->compression_type)) { 2943 + case 3: /* zlib-compressed data in xattr */ 2944 + case 4: /* 64k chunked zlib-compressed data in resource fork */ 2334 2945 2335 - case 7: /* LZVN-compressed data in xattr */ 2336 - case 8: /* 64k chunked LZVN-compressed data in resource fork */ 2946 + case 7: /* LZVN-compressed data in xattr */ 2947 + case 8: /* 64k chunked LZVN-compressed data in resource fork */ 2337 2948 2338 - case 9: /* uncompressed data in xattr (similar to but not identical to CMP_Type1) */ 2339 - case 10: /* 64k chunked uncompressed data in resource fork */ 2949 + case 9: /* uncompressed data in xattr (similar to but not identical to CMP_Type1) */ 2950 + case 10: /* 64k chunked uncompressed data in resource fork */ 2340 2951 2341 - /* valid compression type, we want to copy. */ 2342 - break; 2343 - 2344 - case 5: /* specifies de-dup within the generation store. Don't copy decmpfs xattr. */ 2345 - copyfile_debug(3, "compression_type <5> on attribute com.apple.decmpfs for src file %s is not copied.", 2346 - s->src ? s->src : "(null string)"); 2347 - continue; 2952 + case 11: /* LZFSE-compressed data in xattr */ 2953 + case 12: /* 64k chunked LZFSE-compressed data in resource fork */ 2954 + 2955 + /* valid compression type, we want to copy. */ 2956 + break; 2348 2957 2349 - case 6: /* unused */ 2350 - case 0x80000001: /* faulting files are deprecated, don't copy decmpfs xattr */ 2351 - default: 2352 - copyfile_warn("Invalid compression_type <%d> on attribute %s for src file %s", 2958 + case 5: /* specifies de-dup within the generation store. Don't copy decmpfs xattr. */ 2959 + copyfile_debug(3, "compression_type <5> on attribute com.apple.decmpfs for src file %s is not copied.", 2960 + s->src ? s->src : "(null string)"); 2961 + continue; 2962 + 2963 + case 6: /* unused */ 2964 + case 0x80000001: /* faulting files are deprecated, don't copy decmpfs xattr */ 2965 + default: 2966 + copyfile_warn("Invalid compression_type <%d> on attribute %s for src file %s", 2353 2967 OSSwapLittleToHostInt32(hdr->compression_type), name, s->src ? s->src : "(null string)"); 2354 - continue; 2355 - } 2356 - s->internal_flags |= cfSawDecmpEA; 2357 - } 2968 + continue; 2969 + } 2970 + s->internal_flags |= cfSawDecmpEA; 2971 + } 2358 2972 #endif 2359 2973 2360 - // If we have a copy intention stated, and the EA is to be ignored, we ignore it 2361 - if (s->copyIntent 2362 - && xattr_preserve_for_intent(name, s->copyIntent) == 0) 2363 - continue; 2974 + // If we have a copy intention stated, and the EA is to be ignored, we ignore it 2975 + if (s->copyIntent 2976 + && xattr_preserve_for_intent(name, s->copyIntent) == 0) 2977 + continue; 2978 + 2979 + s->xattr_name = strdup(name); 2364 2980 2365 - s->xattr_name = strdup(name); 2366 - 2367 - if (s->statuscb) { 2368 - int rv; 2369 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 2370 - if (rv == COPYFILE_QUIT) { 2371 - s->err = ECANCELED; 2372 - goto out; 2373 - } else if (rv == COPYFILE_SKIP) { 2374 - continue; 2981 + if (s->statuscb) { 2982 + int rv; 2983 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 2984 + if (rv == COPYFILE_QUIT) { 2985 + s->err = ECANCELED; 2986 + goto out; 2987 + } else if (rv == COPYFILE_SKIP) { 2988 + continue; 2989 + } 2375 2990 } 2376 - } 2377 - if (fsetxattr(s->dst_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea) < 0) 2378 - { 2379 - if (s->statuscb) 2380 - { 2381 - int rv; 2382 - if (s->xattr_name == NULL) 2383 - s->xattr_name = strdup(name); 2384 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 2385 - if (rv == COPYFILE_QUIT) 2991 + if (fsetxattr(s->dst_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea) < 0) 2386 2992 { 2387 - s->err = ECANCELED; 2388 - ret = -1; 2389 - goto out; 2993 + int error = errno; 2994 + if (error == EPERM && strcmp(name, XATTR_ROOT_INSTALLED_NAME) == 0) { 2995 + //Silently ignore if we fail to set XATTR_ROOT_INSTALLED_NAME 2996 + errno = error; 2997 + continue; 2998 + } 2999 + else if (s->statuscb) 3000 + { 3001 + int rv; 3002 + error = errno; 3003 + if (s->xattr_name == NULL) 3004 + s->xattr_name = strdup(name); 3005 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 3006 + if (rv == COPYFILE_QUIT) 3007 + { 3008 + s->err = ECANCELED; 3009 + ret = -1; 3010 + goto out; 3011 + } 3012 + } 3013 + else 3014 + { 3015 + errno = error; 3016 + ret = -1; 3017 + copyfile_warn("could not set attributes %s on destination file descriptor", name); 3018 + continue; 3019 + } 2390 3020 } 2391 - } 2392 - else 2393 - { 2394 - ret = -1; 2395 - copyfile_warn("could not set attributes %s on destination file descriptor: %s", name, strerror(errno)); 2396 - continue; 2397 - } 2398 - } 2399 - if (s->statuscb) { 2400 - int rv; 2401 - if (s->xattr_name == NULL) 2402 - s->xattr_name = strdup(name); 3021 + if (s->statuscb) { 3022 + int rv; 3023 + if (s->xattr_name == NULL) 3024 + s->xattr_name = strdup(name); 2403 3025 2404 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 2405 - if (rv == COPYFILE_QUIT) { 2406 - s->err = ECANCELED; 2407 - goto out; 3026 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 3027 + if (rv == COPYFILE_QUIT) { 3028 + s->err = ECANCELED; 3029 + goto out; 3030 + } 2408 3031 } 2409 3032 } 2410 - } 2411 3033 out: 2412 - if (namebuf) 2413 - free(namebuf); 2414 - free((void *) xa_dataptr); 2415 - if (s->xattr_name) { 2416 - free(s->xattr_name); 2417 - s->xattr_name = NULL; 2418 - } 2419 - return ret; 3034 + if (namebuf) 3035 + free(namebuf); 3036 + free((void *) xa_dataptr); 3037 + if (s->xattr_name) { 3038 + free(s->xattr_name); 3039 + s->xattr_name = NULL; 3040 + } 3041 + return ret; 2420 3042 } 2421 3043 2422 3044 /* ··· 2424 3046 */ 2425 3047 int copyfile_state_get(copyfile_state_t s, uint32_t flag, void *ret) 2426 3048 { 2427 - if (ret == NULL) 2428 - { 2429 - errno = EFAULT; 2430 - return -1; 2431 - } 3049 + if (ret == NULL) 3050 + { 3051 + errno = EFAULT; 3052 + return -1; 3053 + } 2432 3054 2433 - switch(flag) 2434 - { 2435 - case COPYFILE_STATE_SRC_FD: 2436 - *(int*)ret = s->src_fd; 2437 - break; 2438 - case COPYFILE_STATE_DST_FD: 2439 - *(int*)ret = s->dst_fd; 2440 - break; 2441 - case COPYFILE_STATE_SRC_FILENAME: 2442 - *(char**)ret = s->src; 2443 - break; 2444 - case COPYFILE_STATE_DST_FILENAME: 2445 - *(char**)ret = s->dst; 2446 - break; 2447 - case COPYFILE_STATE_QUARANTINE: 2448 - *(qtn_file_t*)ret = s->qinfo; 2449 - break; 3055 + switch(flag) 3056 + { 3057 + case COPYFILE_STATE_SRC_FD: 3058 + *(int*)ret = s->src_fd; 3059 + break; 3060 + case COPYFILE_STATE_DST_FD: 3061 + *(int*)ret = s->dst_fd; 3062 + break; 3063 + case COPYFILE_STATE_SRC_FILENAME: 3064 + *(char**)ret = s->src; 3065 + break; 3066 + case COPYFILE_STATE_DST_FILENAME: 3067 + *(char**)ret = s->dst; 3068 + break; 3069 + case COPYFILE_STATE_QUARANTINE: 3070 + *(qtn_file_t*)ret = s->qinfo; 3071 + break; 2450 3072 #if 0 2451 - case COPYFILE_STATE_STATS: 2452 - ret = s->stats.global; 2453 - break; 2454 - case COPYFILE_STATE_PROGRESS_CB: 2455 - ret = s->callbacks.progress; 2456 - break; 3073 + case COPYFILE_STATE_STATS: 3074 + ret = s->stats.global; 3075 + break; 3076 + case COPYFILE_STATE_PROGRESS_CB: 3077 + ret = s->callbacks.progress; 3078 + break; 2457 3079 #endif 2458 3080 #ifdef COPYFILE_STATE_STATUS_CB 2459 - case COPYFILE_STATE_STATUS_CB: 2460 - *(copyfile_callback_t*)ret = s->statuscb; 2461 - break; 2462 - case COPYFILE_STATE_STATUS_CTX: 2463 - *(void**)ret = s->ctx; 2464 - break; 2465 - case COPYFILE_STATE_COPIED: 2466 - *(off_t*)ret = s->totalCopied; 2467 - break; 3081 + case COPYFILE_STATE_STATUS_CB: 3082 + *(copyfile_callback_t*)ret = s->statuscb; 3083 + break; 3084 + case COPYFILE_STATE_STATUS_CTX: 3085 + *(void**)ret = s->ctx; 3086 + break; 3087 + case COPYFILE_STATE_COPIED: 3088 + *(off_t*)ret = s->totalCopied; 3089 + break; 2468 3090 #endif 2469 3091 #ifdef COPYFILE_STATE_XATTRNAME 2470 - case COPYFILE_STATE_XATTRNAME: 2471 - *(char**)ret = s->xattr_name; 2472 - break; 3092 + case COPYFILE_STATE_XATTRNAME: 3093 + *(char**)ret = s->xattr_name; 3094 + break; 2473 3095 #endif 2474 3096 #ifdef COPYFILE_STATE_INTENT 2475 - case COPYFILE_STATE_INTENT: 2476 - *(xattr_operation_intent_t*)ret = s->copyIntent; 2477 - break; 3097 + case COPYFILE_STATE_INTENT: 3098 + *(xattr_operation_intent_t*)ret = s->copyIntent; 3099 + break; 2478 3100 #endif 2479 - default: 2480 - errno = EINVAL; 2481 - ret = NULL; 2482 - return -1; 2483 - } 2484 - return 0; 3101 + case COPYFILE_STATE_WAS_CLONED: 3102 + *(bool *)ret = s->was_cloned; 3103 + break; 3104 + default: 3105 + errno = EINVAL; 3106 + ret = NULL; 3107 + return -1; 3108 + } 3109 + return 0; 2485 3110 } 2486 3111 2487 3112 /* ··· 2491 3116 int copyfile_state_set(copyfile_state_t s, uint32_t flag, const void * thing) 2492 3117 { 2493 3118 #define copyfile_set_string(DST, SRC) \ 2494 - do { \ 2495 - if (SRC != NULL) { \ 2496 - DST = strdup((char *)SRC); \ 2497 - } else { \ 2498 - if (DST != NULL) { \ 2499 - free(DST); \ 2500 - } \ 2501 - DST = NULL; \ 2502 - } \ 2503 - } while (0) 3119 + do { \ 3120 + if (SRC != NULL) { \ 3121 + DST = strdup((char *)SRC); \ 3122 + } else { \ 3123 + if (DST != NULL) { \ 3124 + free(DST); \ 3125 + } \ 3126 + DST = NULL; \ 3127 + } \ 3128 + } while (0) 2504 3129 2505 - if (thing == NULL) 2506 - { 2507 - errno = EFAULT; 2508 - return -1; 2509 - } 3130 + if (thing == NULL) 3131 + { 3132 + errno = EFAULT; 3133 + return -1; 3134 + } 2510 3135 2511 - switch(flag) 2512 - { 2513 - case COPYFILE_STATE_SRC_FD: 2514 - s->src_fd = *(int*)thing; 2515 - break; 2516 - case COPYFILE_STATE_DST_FD: 2517 - s->dst_fd = *(int*)thing; 2518 - break; 2519 - case COPYFILE_STATE_SRC_FILENAME: 2520 - copyfile_set_string(s->src, thing); 2521 - break; 2522 - case COPYFILE_STATE_DST_FILENAME: 2523 - copyfile_set_string(s->dst, thing); 2524 - break; 2525 - case COPYFILE_STATE_QUARANTINE: 2526 - if (s->qinfo) 2527 - { 2528 - qtn_file_free(s->qinfo); 2529 - s->qinfo = NULL; 2530 - } 2531 - if (*(qtn_file_t*)thing) 2532 - s->qinfo = qtn_file_clone(*(qtn_file_t*)thing); 2533 - break; 3136 + switch(flag) 3137 + { 3138 + case COPYFILE_STATE_SRC_FD: 3139 + s->src_fd = *(int*)thing; 3140 + break; 3141 + case COPYFILE_STATE_DST_FD: 3142 + s->dst_fd = *(int*)thing; 3143 + break; 3144 + case COPYFILE_STATE_SRC_FILENAME: 3145 + copyfile_set_string(s->src, thing); 3146 + break; 3147 + case COPYFILE_STATE_DST_FILENAME: 3148 + copyfile_set_string(s->dst, thing); 3149 + break; 3150 + case COPYFILE_STATE_QUARANTINE: 3151 + if (s->qinfo) 3152 + { 3153 + qtn_file_free(s->qinfo); 3154 + s->qinfo = NULL; 3155 + } 3156 + if (*(qtn_file_t*)thing) 3157 + s->qinfo = qtn_file_clone(*(qtn_file_t*)thing); 3158 + break; 2534 3159 #if 0 2535 - case COPYFILE_STATE_STATS: 2536 - s->stats.global = thing; 2537 - break; 2538 - case COPYFILE_STATE_PROGRESS_CB: 2539 - s->callbacks.progress = thing; 2540 - break; 3160 + case COPYFILE_STATE_STATS: 3161 + s->stats.global = thing; 3162 + break; 3163 + case COPYFILE_STATE_PROGRESS_CB: 3164 + s->callbacks.progress = thing; 3165 + break; 2541 3166 #endif 2542 3167 #ifdef COPYFILE_STATE_STATUS_CB 2543 - case COPYFILE_STATE_STATUS_CB: 2544 - s->statuscb = (copyfile_callback_t)thing; 2545 - break; 2546 - case COPYFILE_STATE_STATUS_CTX: 2547 - s->ctx = (void*)thing; 2548 - break; 3168 + case COPYFILE_STATE_STATUS_CB: 3169 + s->statuscb = (copyfile_callback_t)thing; 3170 + break; 3171 + case COPYFILE_STATE_STATUS_CTX: 3172 + s->ctx = (void*)thing; 3173 + break; 2549 3174 #endif 2550 3175 #ifdef COPYFILE_STATE_INTENT 2551 - case COPYFILE_STATE_INTENT: 2552 - s->copyIntent = *(xattr_operation_intent_t*)thing; 2553 - break; 3176 + case COPYFILE_STATE_INTENT: 3177 + s->copyIntent = *(xattr_operation_intent_t*)thing; 3178 + break; 2554 3179 #endif 2555 - default: 2556 - errno = EINVAL; 2557 - return -1; 2558 - } 2559 - return 0; 3180 + default: 3181 + errno = EINVAL; 3182 + return -1; 3183 + } 3184 + return 0; 2560 3185 #undef copyfile_set_string 2561 3186 } 2562 3187 ··· 2569 3194 #define COPYFILE_OPTION(x) { #x, COPYFILE_ ## x }, 2570 3195 2571 3196 struct {char *s; int v;} opts[] = { 2572 - COPYFILE_OPTION(ACL) 2573 - COPYFILE_OPTION(STAT) 2574 - COPYFILE_OPTION(XATTR) 2575 - COPYFILE_OPTION(DATA) 2576 - COPYFILE_OPTION(SECURITY) 2577 - COPYFILE_OPTION(METADATA) 2578 - COPYFILE_OPTION(ALL) 2579 - COPYFILE_OPTION(NOFOLLOW_SRC) 2580 - COPYFILE_OPTION(NOFOLLOW_DST) 2581 - COPYFILE_OPTION(NOFOLLOW) 2582 - COPYFILE_OPTION(EXCL) 2583 - COPYFILE_OPTION(MOVE) 2584 - COPYFILE_OPTION(UNLINK) 2585 - COPYFILE_OPTION(PACK) 2586 - COPYFILE_OPTION(UNPACK) 2587 - COPYFILE_OPTION(CHECK) 2588 - COPYFILE_OPTION(VERBOSE) 2589 - COPYFILE_OPTION(DEBUG) 2590 - {NULL, 0} 3197 + COPYFILE_OPTION(ACL) 3198 + COPYFILE_OPTION(STAT) 3199 + COPYFILE_OPTION(XATTR) 3200 + COPYFILE_OPTION(DATA) 3201 + COPYFILE_OPTION(SECURITY) 3202 + COPYFILE_OPTION(METADATA) 3203 + COPYFILE_OPTION(ALL) 3204 + COPYFILE_OPTION(NOFOLLOW_SRC) 3205 + COPYFILE_OPTION(NOFOLLOW_DST) 3206 + COPYFILE_OPTION(NOFOLLOW) 3207 + COPYFILE_OPTION(EXCL) 3208 + COPYFILE_OPTION(MOVE) 3209 + COPYFILE_OPTION(UNLINK) 3210 + COPYFILE_OPTION(PACK) 3211 + COPYFILE_OPTION(UNPACK) 3212 + COPYFILE_OPTION(CHECK) 3213 + COPYFILE_OPTION(CLONE) 3214 + COPYFILE_OPTION(CLONE_FORCE) 3215 + COPYFILE_OPTION(VERBOSE) 3216 + COPYFILE_OPTION(RECURSIVE) 3217 + COPYFILE_OPTION(DEBUG) 3218 + COPYFILE_OPTION(CLONE) 3219 + COPYFILE_OPTION(CLONE_FORCE) 3220 + COPYFILE_OPTION(DATA_SPARSE) 3221 + {NULL, 0} 2591 3222 }; 2592 3223 2593 3224 int main(int c, char *v[]) 2594 3225 { 2595 - int i; 2596 - int flags = 0; 3226 + int i, ret; 3227 + int flags = 0; 3228 + copyfile_state_t state = NULL; 2597 3229 2598 - if (c < 3) 2599 - errx(1, "insufficient arguments"); 3230 + if (c < 3) 3231 + errx(1, "insufficient arguments"); 2600 3232 2601 - while(c-- > 3) 2602 - { 2603 - for (i = 0; opts[i].s != NULL; ++i) 3233 + while(c-- > 3) 2604 3234 { 2605 - if (strcasecmp(opts[i].s, v[c]) == 0) 2606 - { 2607 - printf("option %d: %s <- %d\n", c, opts[i].s, opts[i].v); 2608 - flags |= opts[i].v; 2609 - break; 2610 - } 3235 + for (i = 0; opts[i].s != NULL; ++i) 3236 + { 3237 + if (strcasecmp(opts[i].s, v[c]) == 0) 3238 + { 3239 + printf("option %d: %s <- %d\n", c, opts[i].s, opts[i].v); 3240 + flags |= opts[i].v; 3241 + break; 3242 + } 3243 + } 3244 + } 3245 + 3246 + if (flags & COPYFILE_DEBUG) { 3247 + state = copyfile_state_alloc(); 3248 + state->debug = 10; // Turn on all debug statements 3249 + } 3250 + ret = copyfile(v[1], v[2], state, flags); 3251 + if (state) { 3252 + (void)copyfile_state_free(state); 2611 3253 } 2612 - } 2613 3254 2614 - return copyfile(v[1], v[2], NULL, flags); 3255 + return ret; 2615 3256 } 2616 3257 #endif 2617 3258 /* ··· 2622 3263 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. 2623 3264 */ 2624 3265 2625 - 2626 - #define offsetof(type, member) ((size_t)(&((type *)0)->member)) 2627 - 2628 3266 #define XATTR_MAXATTRLEN (16*1024*1024) 2629 3267 2630 3268 2631 3269 /* 2632 - Typical "._" AppleDouble Header File layout: 2633 - ------------------------------------------------------------ 2634 - MAGIC 0x00051607 2635 - VERSION 0x00020000 2636 - FILLER 0 2637 - COUNT 2 2638 - .-- AD ENTRY[0] Finder Info Entry (must be first) 2639 - .--+-- AD ENTRY[1] Resource Fork Entry (must be last) 2640 - | '-> FINDER INFO 2641 - | ///////////// Fixed Size Data (32 bytes) 2642 - | EXT ATTR HDR 2643 - | ///////////// 2644 - | ATTR ENTRY[0] --. 2645 - | ATTR ENTRY[1] --+--. 2646 - | ATTR ENTRY[2] --+--+--. 2647 - | ... | | | 2648 - | ATTR ENTRY[N] --+--+--+--. 2649 - | ATTR DATA 0 <-' | | | 2650 - | //////////// | | | 2651 - | ATTR DATA 1 <----' | | 2652 - | ///////////// | | 2653 - | ATTR DATA 2 <-------' | 2654 - | ///////////// | 2655 - | ... | 2656 - | ATTR DATA N <----------' 2657 - | ///////////// 2658 - | Attribute Free Space 2659 - | 2660 - '----> RESOURCE FORK 2661 - ///////////// Variable Sized Data 2662 - ///////////// 2663 - ///////////// 2664 - ///////////// 2665 - ///////////// 2666 - ///////////// 2667 - ... 2668 - ///////////// 2669 - 2670 - ------------------------------------------------------------ 3270 + Typical "._" AppleDouble Header File layout: 3271 + ------------------------------------------------------------ 3272 + MAGIC 0x00051607 3273 + VERSION 0x00020000 3274 + FILLER 0 3275 + COUNT 2 3276 + .-- AD ENTRY[0] Finder Info Entry (must be first) 3277 + .--+-- AD ENTRY[1] Resource Fork Entry (must be last) 3278 + | '-> FINDER INFO 3279 + | ///////////// Fixed Size Data (32 bytes) 3280 + | EXT ATTR HDR 3281 + | ///////////// 3282 + | ATTR ENTRY[0] --. 3283 + | ATTR ENTRY[1] --+--. 3284 + | ATTR ENTRY[2] --+--+--. 3285 + | ... | | | 3286 + | ATTR ENTRY[N] --+--+--+--. 3287 + | ATTR DATA 0 <-' | | | 3288 + | //////////// | | | 3289 + | ATTR DATA 1 <----' | | 3290 + | ///////////// | | 3291 + | ATTR DATA 2 <-------' | 3292 + | ///////////// | 3293 + | ... | 3294 + | ATTR DATA N <----------' 3295 + | ///////////// 3296 + | Attribute Free Space 3297 + | 3298 + '----> RESOURCE FORK 3299 + ///////////// Variable Sized Data 3300 + ///////////// 3301 + ///////////// 3302 + ///////////// 3303 + ///////////// 3304 + ///////////// 3305 + ... 3306 + ///////////// 2671 3307 2672 - NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are 2673 - stored as part of the Finder Info. The length in the Finder 2674 - Info AppleDouble entry includes the length of the extended 2675 - attribute header, attribute entries, and attribute data. 2676 - */ 3308 + ------------------------------------------------------------ 3309 + 3310 + NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are 3311 + stored as part of the Finder Info. The length in the Finder 3312 + Info AppleDouble entry includes the length of the extended 3313 + attribute header, attribute entries, and attribute data. 3314 + */ 2677 3315 2678 3316 2679 3317 /* ··· 2706 3344 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */ 2707 3345 #define AD_AFPNAME 13 /* Short name on AFP server */ 2708 3346 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */ 2709 - #define AD_AFPDIRID 15 /* AFP directory ID */ 3347 + #define AD_AFPDIRID 15 /* AFP directory ID */ 2710 3348 #define AD_ATTRIBUTES AD_FINDERINFO 2711 3349 2712 3350 ··· 2731 3369 2732 3370 typedef struct apple_double_entry 2733 3371 { 2734 - u_int32_t type; /* entry type: see list, 0 invalid */ 3372 + u_int32_t type; /* entry type: see list, 0 invalid */ 2735 3373 u_int32_t offset; /* entry data offset from the beginning of the file. */ 2736 3374 u_int32_t length; /* entry data length in bytes. */ 2737 3375 } __attribute__((aligned(2), packed)) apple_double_entry_t; ··· 2740 3378 typedef struct apple_double_header 2741 3379 { 2742 3380 u_int32_t magic; /* == ADH_MAGIC */ 2743 - u_int32_t version; /* format version: 2 = 0x00020000 */ 3381 + u_int32_t version; /* format version: 2 = 0x00020000 */ 2744 3382 u_int32_t filler[4]; 2745 - u_int16_t numEntries; /* number of entries which follow */ 3383 + u_int16_t numEntries; /* number of entries which follow */ 2746 3384 apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */ 2747 3385 u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */ 2748 3386 u_int8_t pad[2]; /* get better alignment inside attr_header */ ··· 2755 3393 u_int32_t offset; /* file offset to data */ 2756 3394 u_int32_t length; /* size of attribute data */ 2757 3395 u_int16_t flags; 2758 - u_int8_t namelen; /* length of name including NULL termination char */ 3396 + u_int8_t namelen; /* length of name including NULL termination char */ 2759 3397 u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */ 2760 3398 } __attribute__((aligned(2), packed)) attr_entry_t; 2761 3399 ··· 2767 3405 apple_double_header_t appledouble; 2768 3406 u_int32_t magic; /* == ATTR_HDR_MAGIC */ 2769 3407 u_int32_t debug_tag; /* for debugging == file id of owning file */ 2770 - u_int32_t total_size; /* total size of attribute header + entries + data */ 3408 + u_int32_t total_size; /* total size of attribute header + entries + data */ 2771 3409 u_int32_t data_start; /* file offset to attribute data area */ 2772 3410 u_int32_t data_length; /* length of attribute data area */ 2773 3411 u_int32_t reserved[3]; ··· 2784 3422 u_int32_t fh_MapLength; 2785 3423 u_int8_t systemData[112]; 2786 3424 u_int8_t appData[128]; 2787 - u_int32_t mh_DataOffset; 3425 + u_int32_t mh_DataOffset; 2788 3426 u_int32_t mh_MapOffset; 2789 - u_int32_t mh_DataLength; 3427 + u_int32_t mh_DataLength; 2790 3428 u_int32_t mh_MapLength; 2791 3429 u_int32_t mh_Next; 2792 3430 u_int16_t mh_RefNum; 2793 3431 u_int8_t mh_Attr; 2794 - u_int8_t mh_InMemoryAttr; 3432 + u_int8_t mh_InMemoryAttr; 2795 3433 u_int16_t mh_Types; 2796 3434 u_int16_t mh_Names; 2797 3435 u_int16_t typeCount; 2798 3436 } __attribute__((aligned(2), packed)) rsrcfork_header_t; 2799 3437 #define RF_FIRST_RESOURCE 256 2800 - #define RF_NULL_MAP_LENGTH 30 3438 + #define RF_NULL_MAP_LENGTH 30 2801 3439 #define RF_EMPTY_TAG "This resource fork intentionally left blank " 2802 3440 2803 3441 static const rsrcfork_header_t empty_rsrcfork_header = { ··· 2827 3465 #define ATTR_ALIGN 3L /* Use four-byte alignment */ 2828 3466 2829 3467 #define ATTR_ENTRY_LENGTH(namelen) \ 2830 - ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN)) 3468 + ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN)) 2831 3469 2832 3470 #define ATTR_NEXT(ae) \ 2833 - (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen)) 3471 + (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen)) 2834 3472 2835 3473 #define XATTR_SECURITY_NAME "com.apple.acl.text" 2836 3474 2837 3475 /* 2838 - * Endian swap Apple Double header 3476 + * Endian swap Apple Double header 2839 3477 */ 2840 3478 static void 2841 3479 swap_adhdr(apple_double_header_t *adh) ··· 2868 3506 swap_attrhdr_entry(attr_entry_t *ae) 2869 3507 { 2870 3508 #if BYTE_ORDER == LITTLE_ENDIAN 2871 - ae->offset = SWAP32 (ae->offset); 2872 - ae->length = SWAP32 (ae->length); 2873 - ae->flags = SWAP16 (ae->flags); 3509 + ae->offset = SWAP32 (ae->offset); 3510 + ae->length = SWAP32 (ae->length); 3511 + ae->flags = SWAP16 (ae->flags); 2874 3512 #else 2875 - (void)ae; 3513 + (void)ae; 2876 3514 #endif 2877 3515 } 2878 3516 ··· 2884 3522 swap_attrhdr_entries(attr_header_t *ah) 2885 3523 { 2886 3524 #if BYTE_ORDER == LITTLE_ENDIAN 2887 - int i; 2888 - int count; 2889 - attr_entry_t *entry; 2890 - attr_entry_t *next; 3525 + int i; 3526 + int count; 3527 + attr_entry_t *entry; 3528 + attr_entry_t *next; 2891 3529 2892 - /* If we're in copyfile_pack, num_args is native endian, 2893 - * if we're in _unpack, num_args is big endian. Use 2894 - * the magic number to test for endianess. 2895 - */ 2896 - count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs); 3530 + /* If we're in copyfile_pack, num_args is native endian, 3531 + * if we're in _unpack, num_args is big endian. Use 3532 + * the magic number to test for endianess. 3533 + */ 3534 + count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs); 2897 3535 2898 - entry = (attr_entry_t *)(&ah[1]); 2899 - for (i = 0; i < count; i++) { 2900 - next = ATTR_NEXT(entry); 2901 - swap_attrhdr_entry(entry); 2902 - entry = next; 2903 - } 3536 + entry = (attr_entry_t *)(&ah[1]); 3537 + for (i = 0; i < count; i++) { 3538 + next = ATTR_NEXT(entry); 3539 + swap_attrhdr_entry(entry); 3540 + entry = next; 3541 + } 2904 3542 #else 2905 - (void)ah; 3543 + (void)ah; 2906 3544 #endif 2907 3545 } 2908 3546 2909 3547 /* 2910 - * Endian swap extended attributes header 3548 + * Endian swap extended attributes header 2911 3549 */ 2912 3550 static void 2913 3551 swap_attrhdr(attr_header_t *ah) ··· 2934 3572 */ 2935 3573 static int copyfile_unpack(copyfile_state_t s) 2936 3574 { 2937 - ssize_t bytes; 2938 - void * buffer, * endptr, * dataptr = NULL; 2939 - apple_double_header_t *adhdr; 2940 - ssize_t hdrsize; 2941 - int error = 0; 2942 - 2943 - if (s->sb.st_size < ATTR_MAX_HDR_SIZE) 2944 - hdrsize = (ssize_t)s->sb.st_size; 2945 - else 2946 - hdrsize = ATTR_MAX_HDR_SIZE; 2947 - 2948 - buffer = calloc(1, hdrsize); 2949 - if (buffer == NULL) { 2950 - copyfile_debug(1, "copyfile_unpack: calloc(1, %zu) returned NULL", hdrsize); 2951 - error = -1; 2952 - goto exit; 2953 - } else 2954 - endptr = (char*)buffer + hdrsize; 3575 + ssize_t bytes; 3576 + void * buffer, * endptr, * dataptr = NULL; 3577 + apple_double_header_t *adhdr; 3578 + ssize_t hdrsize; 3579 + int error = 0; 2955 3580 2956 - bytes = pread(s->src_fd, buffer, hdrsize, 0); 3581 + if (s->sb.st_size < ATTR_MAX_HDR_SIZE) 3582 + hdrsize = (ssize_t)s->sb.st_size; 3583 + else 3584 + hdrsize = ATTR_MAX_HDR_SIZE; 2957 3585 2958 - if (bytes < 0) 2959 - { 2960 - copyfile_debug(1, "pread returned: %zd", bytes); 2961 - error = -1; 2962 - goto exit; 2963 - } 2964 - if (bytes < hdrsize) 2965 - { 2966 - copyfile_debug(1, 2967 - "pread couldn't read entire header: %d of %d", 2968 - (int)bytes, (int)s->sb.st_size); 2969 - error = -1; 2970 - goto exit; 2971 - } 2972 - adhdr = (apple_double_header_t *)buffer; 3586 + buffer = calloc(1, hdrsize); 3587 + if (buffer == NULL) { 3588 + copyfile_debug(1, "copyfile_unpack: calloc(1, %zu) returned NULL", hdrsize); 3589 + error = -1; 3590 + goto exit; 3591 + } else 3592 + endptr = (char*)buffer + hdrsize; 2973 3593 2974 - /* 2975 - * Check for Apple Double file. 2976 - */ 2977 - if ((size_t)bytes < sizeof(apple_double_header_t) - 2 || 2978 - SWAP32(adhdr->magic) != ADH_MAGIC || 2979 - SWAP32(adhdr->version) != ADH_VERSION || 2980 - SWAP16(adhdr->numEntries) != 2 || 2981 - SWAP32(adhdr->entries[0].type) != AD_FINDERINFO) 2982 - { 2983 - if (COPYFILE_VERBOSE & s->flags) 2984 - copyfile_warn("Not a valid Apple Double header"); 2985 - error = -1; 2986 - goto exit; 2987 - } 2988 - swap_adhdr(adhdr); 3594 + bytes = pread(s->src_fd, buffer, hdrsize, 0); 2989 3595 2990 - /* 2991 - * Remove any extended attributes on the target. 2992 - */ 3596 + if (bytes < 0) 3597 + { 3598 + copyfile_debug(1, "pread returned: %zd", bytes); 3599 + error = -1; 3600 + goto exit; 3601 + } 3602 + if (bytes < hdrsize) 3603 + { 3604 + copyfile_debug(1, 3605 + "pread couldn't read entire header: %d of %d", 3606 + (int)bytes, (int)s->sb.st_size); 3607 + error = -1; 3608 + goto exit; 3609 + } 3610 + adhdr = (apple_double_header_t *)buffer; 2993 3611 2994 - if ((bytes = flistxattr(s->dst_fd, 0, 0, 0)) > 0) 2995 - { 2996 - char *namebuf, *name; 2997 - 2998 - if ((namebuf = (char*) malloc(bytes)) == NULL) 3612 + /* 3613 + * Check for Apple Double file. 3614 + */ 3615 + if ((size_t)bytes < sizeof(apple_double_header_t) - 2 || 3616 + SWAP32(adhdr->magic) != ADH_MAGIC || 3617 + SWAP32(adhdr->version) != ADH_VERSION || 3618 + SWAP16(adhdr->numEntries) != 2 || 3619 + SWAP32(adhdr->entries[0].type) != AD_FINDERINFO) 2999 3620 { 3000 - s->err = ENOMEM; 3001 - goto exit; 3621 + if (COPYFILE_VERBOSE & s->flags) 3622 + copyfile_warn("Not a valid Apple Double header"); 3623 + error = -1; 3624 + goto exit; 3002 3625 } 3003 - bytes = flistxattr(s->dst_fd, namebuf, bytes, 0); 3626 + swap_adhdr(adhdr); 3004 3627 3005 - if (bytes > 0) 3006 - for (name = namebuf; name < namebuf + bytes; name += strlen(name) + 1) 3007 - (void)fremovexattr(s->dst_fd, name, 0); 3628 + /* 3629 + * Remove any extended attributes on the target. 3630 + */ 3008 3631 3009 - free(namebuf); 3010 - } 3011 - else if (bytes < 0) 3012 - { 3013 - if (errno != ENOTSUP && errno != EPERM) 3014 - goto exit; 3015 - } 3632 + if ((bytes = flistxattr(s->dst_fd, 0, 0, 0)) > 0) 3633 + { 3634 + char *namebuf, *name; 3016 3635 3017 - /* 3018 - * Extract the extended attributes. 3019 - * 3020 - * >>> WARNING <<< 3021 - * This assumes that the data is already in memory (not 3022 - * the case when there are lots of attributes or one of 3023 - * the attributes is very large. 3024 - */ 3025 - if (adhdr->entries[0].length > FINDERINFOSIZE) 3026 - { 3027 - attr_header_t *attrhdr; 3028 - attr_entry_t *entry; 3029 - int count; 3030 - int i; 3636 + if ((namebuf = (char*) malloc(bytes)) == NULL) 3637 + { 3638 + s->err = ENOMEM; 3639 + goto exit; 3640 + } 3641 + bytes = flistxattr(s->dst_fd, namebuf, bytes, 0); 3642 + 3643 + if (bytes > 0) 3644 + for (name = namebuf; name < namebuf + bytes; name += strlen(name) + 1) 3645 + (void)fremovexattr(s->dst_fd, name, 0); 3031 3646 3032 - if ((size_t)hdrsize < sizeof(attr_header_t)) { 3033 - copyfile_warn("bad attribute header: %zu < %zu", hdrsize, sizeof(attr_header_t)); 3034 - error = -1; 3035 - goto exit; 3647 + free(namebuf); 3036 3648 } 3037 - 3038 - attrhdr = (attr_header_t *)buffer; 3039 - swap_attrhdr(attrhdr); 3040 - if (attrhdr->magic != ATTR_HDR_MAGIC) 3649 + else if (bytes < 0) 3041 3650 { 3042 - if (COPYFILE_VERBOSE & s->flags) 3043 - copyfile_warn("bad attribute header"); 3044 - error = -1; 3045 - goto exit; 3651 + if (errno != ENOTSUP && errno != EPERM) 3652 + goto exit; 3046 3653 } 3047 - count = attrhdr->num_attrs; 3048 - entry = (attr_entry_t *)&attrhdr[1]; 3049 3654 3050 - for (i = 0; i < count; i++) 3655 + /* 3656 + * Extract the extended attributes. 3657 + * 3658 + * >>> WARNING <<< 3659 + * This assumes that the data is already in memory (not 3660 + * the case when there are lots of attributes or one of 3661 + * the attributes is very large. 3662 + */ 3663 + if (adhdr->entries[0].length > FINDERINFOSIZE) 3051 3664 { 3052 - /* 3053 - * First we do some simple sanity checking. 3054 - * +) See if entry is within the buffer's range; 3055 - * 3056 - * +) Check the attribute name length; if it's longer than the 3057 - * maximum, we truncate it down. (We could error out as well; 3058 - * I'm not sure which is the better way to go here.) 3059 - * 3060 - * +) If, given the name length, it goes beyond the end of 3061 - * the buffer, error out. 3062 - * 3063 - * +) If the last byte isn't a NUL, make it a NUL. (Since we 3064 - * truncated the name length above, we truncate the name here.) 3065 - * 3066 - * +) If entry->offset is so large that it causes dataptr to 3067 - * go beyond the end of the buffer -- or, worse, so large that 3068 - * it wraps around! -- we error out. 3069 - * 3070 - * +) If entry->length would cause the entry to go beyond the 3071 - * end of the buffer (or, worse, wrap around to before it), 3072 - * *or* if the length is larger than the hdrsize, we error out. 3073 - * (An explanation of that: what we're checking for there is 3074 - * the small range of values such that offset+length would cause 3075 - * it to go beyond endptr, and then wrap around past buffer. We 3076 - * care about this because we are passing entry->length down to 3077 - * fgetxattr() below, and an erroneously large value could cause 3078 - * problems there. By making sure that it's less than hdrsize, 3079 - * which has already been sanity-checked above, we're safe. 3080 - * That may mean that the check against < buffer is unnecessary.) 3081 - */ 3082 - if ((void*)entry >= endptr || (void*)entry < buffer) { 3083 - if (COPYFILE_VERBOSE & s->flags) 3084 - copyfile_warn("Incomplete or corrupt attribute entry"); 3085 - error = -1; 3086 - s->err = EINVAL; 3087 - goto exit; 3088 - } 3665 + attr_header_t *attrhdr; 3666 + attr_entry_t *entry; 3667 + int count; 3668 + int i; 3089 3669 3090 - if (((char*)entry + sizeof(*entry)) > (char*)endptr) { 3091 - if (COPYFILE_VERBOSE & s->flags) 3092 - copyfile_warn("Incomplete or corrupt attribute entry"); 3093 - error = -1; 3094 - s->err = EINVAL; 3095 - goto exit; 3096 - } 3097 - 3098 - /* 3099 - * Endian swap the entry we're looking at. Previously 3100 - * we did this swap as part of swap_attrhdr, but that 3101 - * allowed a maliciously constructed file to overrun 3102 - * our allocation. Instead do the swap after we've verified 3103 - * the entry struct is within the buffer's range. 3104 - */ 3105 - swap_attrhdr_entry(entry); 3670 + if ((size_t)hdrsize < sizeof(attr_header_t)) { 3671 + copyfile_warn("bad attribute header: %zu < %zu", hdrsize, sizeof(attr_header_t)); 3672 + error = -1; 3673 + goto exit; 3674 + } 3106 3675 3107 - if (entry->namelen < 2) { 3108 - if (COPYFILE_VERBOSE & s->flags) 3109 - copyfile_warn("Corrupt attribute entry (only %d bytes)", entry->namelen); 3110 - error = -1; 3111 - s->err = EINVAL; 3112 - goto exit; 3113 - } 3676 + attrhdr = (attr_header_t *)buffer; 3677 + swap_attrhdr(attrhdr); 3678 + if (attrhdr->magic != ATTR_HDR_MAGIC) 3679 + { 3680 + if (COPYFILE_VERBOSE & s->flags) 3681 + copyfile_warn("bad attribute header"); 3682 + error = -1; 3683 + goto exit; 3684 + } 3685 + count = attrhdr->num_attrs; 3686 + entry = (attr_entry_t *)&attrhdr[1]; 3114 3687 3115 - if (entry->namelen > XATTR_MAXNAMELEN + 1) { 3116 - if (COPYFILE_VERBOSE & s->flags) 3117 - copyfile_warn("Corrupt attribute entry (name length is %d bytes)", entry->namelen); 3118 - error = -1; 3119 - s->err = EINVAL; 3120 - goto exit; 3121 - } 3688 + for (i = 0; i < count; i++) 3689 + { 3690 + /* 3691 + * First we do some simple sanity checking. 3692 + * +) See if entry is within the buffer's range; 3693 + * 3694 + * +) Check the attribute name length; if it's longer than the 3695 + * maximum, we truncate it down. (We could error out as well; 3696 + * I'm not sure which is the better way to go here.) 3697 + * 3698 + * +) If, given the name length, it goes beyond the end of 3699 + * the buffer, error out. 3700 + * 3701 + * +) If the last byte isn't a NUL, make it a NUL. (Since we 3702 + * truncated the name length above, we truncate the name here.) 3703 + * 3704 + * +) If entry->offset is so large that it causes dataptr to 3705 + * go beyond the end of the buffer -- or, worse, so large that 3706 + * it wraps around! -- we error out. 3707 + * 3708 + * +) If entry->length would cause the entry to go beyond the 3709 + * end of the buffer (or, worse, wrap around to before it), 3710 + * *or* if the length is larger than the hdrsize, we error out. 3711 + * (An explanation of that: what we're checking for there is 3712 + * the small range of values such that offset+length would cause 3713 + * it to go beyond endptr, and then wrap around past buffer. We 3714 + * care about this because we are passing entry->length down to 3715 + * fgetxattr() below, and an erroneously large value could cause 3716 + * problems there. By making sure that it's less than hdrsize, 3717 + * which has already been sanity-checked above, we're safe. 3718 + * That may mean that the check against < buffer is unnecessary.) 3719 + */ 3720 + if ((void*)entry >= endptr || (void*)entry < buffer) { 3721 + if (COPYFILE_VERBOSE & s->flags) 3722 + copyfile_warn("Incomplete or corrupt attribute entry"); 3723 + error = -1; 3724 + s->err = EINVAL; 3725 + goto exit; 3726 + } 3122 3727 3123 - if ((void*)(entry->name + entry->namelen) > endptr) { 3124 - if (COPYFILE_VERBOSE & s->flags) 3125 - copyfile_warn("Incomplete or corrupt attribute entry"); 3126 - error = -1; 3127 - s->err = EINVAL; 3128 - goto exit; 3129 - } 3728 + if (((char*)entry + sizeof(*entry)) > (char*)endptr) { 3729 + if (COPYFILE_VERBOSE & s->flags) 3730 + copyfile_warn("Incomplete or corrupt attribute entry"); 3731 + error = -1; 3732 + s->err = EINVAL; 3733 + goto exit; 3734 + } 3130 3735 3131 - /* Because namelen includes the NUL, we check one byte back */ 3132 - if (entry->name[entry->namelen-1] != 0) { 3133 - if (COPYFILE_VERBOSE & s->flags) 3134 - copyfile_warn("Corrupt attribute entry (name is not NUL-terminated)"); 3135 - error = -1; 3136 - s->err = EINVAL; 3137 - goto exit; 3138 - } 3736 + /* 3737 + * Endian swap the entry we're looking at. Previously 3738 + * we did this swap as part of swap_attrhdr, but that 3739 + * allowed a maliciously constructed file to overrun 3740 + * our allocation. Instead do the swap after we've verified 3741 + * the entry struct is within the buffer's range. 3742 + */ 3743 + swap_attrhdr_entry(entry); 3139 3744 3140 - copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u", 3141 - entry->name, entry->length, entry->offset); 3745 + if (entry->namelen < 2) { 3746 + if (COPYFILE_VERBOSE & s->flags) 3747 + copyfile_warn("Corrupt attribute entry (only %d bytes)", entry->namelen); 3748 + error = -1; 3749 + s->err = EINVAL; 3750 + goto exit; 3751 + } 3142 3752 3143 - #if 0 3144 - dataptr = (char *)attrhdr + entry->offset; 3753 + if (entry->namelen > XATTR_MAXNAMELEN + 1) { 3754 + if (COPYFILE_VERBOSE & s->flags) 3755 + copyfile_warn("Corrupt attribute entry (name length is %d bytes)", entry->namelen); 3756 + error = -1; 3757 + s->err = EINVAL; 3758 + goto exit; 3759 + } 3145 3760 3146 - if (dataptr > endptr || dataptr < buffer) { 3147 - copyfile_debug(1, "Entry %d overflows: offset = %u", i, entry->offset); 3148 - error = -1; 3149 - s->err = EINVAL; /* Invalid buffer */ 3150 - goto exit; 3151 - } 3761 + if ((void*)(entry->name + entry->namelen) > endptr) { 3762 + if (COPYFILE_VERBOSE & s->flags) 3763 + copyfile_warn("Incomplete or corrupt attribute entry"); 3764 + error = -1; 3765 + s->err = EINVAL; 3766 + goto exit; 3767 + } 3152 3768 3153 - if (((char*)dataptr + entry->length) > (char*)endptr || 3154 - (((char*)dataptr + entry->length) < (char*)buffer) || 3155 - (entry->length > (size_t)hdrsize)) { 3156 - if (COPYFILE_VERBOSE & s->flags) 3157 - copyfile_warn("Incomplete or corrupt attribute entry"); 3158 - copyfile_debug(1, "Entry %d length overflows: offset = %u, length = %u", 3159 - i, entry->offset, entry->length); 3160 - error = -1; 3161 - s->err = EINVAL; /* Invalid buffer */ 3162 - goto exit; 3163 - } 3769 + /* Because namelen includes the NUL, we check one byte back */ 3770 + if (entry->name[entry->namelen-1] != 0) { 3771 + if (COPYFILE_VERBOSE & s->flags) 3772 + copyfile_warn("Corrupt attribute entry (name is not NUL-terminated)"); 3773 + error = -1; 3774 + s->err = EINVAL; 3775 + goto exit; 3776 + } 3164 3777 3165 - #else 3166 - dataptr = malloc(entry->length); 3167 - if (dataptr == NULL) { 3168 - copyfile_debug(1, "no memory for %u bytes\n", entry->length); 3169 - error = -1; 3170 - s->err = ENOMEM; 3171 - goto exit; 3172 - } 3173 - if (pread(s->src_fd, dataptr, entry->length, entry->offset) != (ssize_t)entry->length) { 3174 - copyfile_debug(1, "failed to read %u bytes at offset %u\n", entry->length, entry->offset); 3175 - error = -1; 3176 - s->err = EINVAL; 3177 - goto exit; 3178 - } 3179 - #endif 3778 + copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u", 3779 + entry->name, entry->length, entry->offset); 3180 3780 3181 - if (strcmp((char*)entry->name, XATTR_QUARANTINE_NAME) == 0) 3182 - { 3183 - qtn_file_t tqinfo = NULL; 3781 + #if 0 3782 + dataptr = (char *)attrhdr + entry->offset; 3184 3783 3185 - if (s->qinfo == NULL) 3186 - { 3187 - tqinfo = qtn_file_alloc(); 3188 - if (tqinfo) 3189 - { 3190 - int x; 3191 - if ((x = qtn_file_init_with_data(tqinfo, dataptr, entry->length)) != 0) 3192 - { 3193 - copyfile_warn("qtn_file_init_with_data failed: %s", qtn_error(x)); 3194 - qtn_file_free(tqinfo); 3195 - tqinfo = NULL; 3784 + if (dataptr > endptr || dataptr < buffer) { 3785 + copyfile_debug(1, "Entry %d overflows: offset = %u", i, entry->offset); 3786 + error = -1; 3787 + s->err = EINVAL; /* Invalid buffer */ 3788 + goto exit; 3196 3789 } 3197 - } 3198 - } 3199 - else 3200 - { 3201 - tqinfo = s->qinfo; 3202 - } 3203 - if (tqinfo) 3204 - { 3205 - int x; 3206 - x = qtn_file_apply_to_fd(tqinfo, s->dst_fd); 3207 - if (x != 0) { 3208 - copyfile_warn("qtn_file_apply_to_fd failed: %s", qtn_error(x)); 3209 - if (s->statuscb) { 3210 - int rv; 3211 - s->xattr_name = (char*)XATTR_QUARANTINE_NAME; 3212 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 3213 - s->xattr_name = NULL; 3214 - if (rv == COPYFILE_QUIT) { 3215 - error = s->err = x < 0 ? ENOTSUP : errno; 3216 - goto exit; 3217 - } 3218 - } else { 3219 - error = s->err = x < 0 ? ENOTSUP : errno; 3220 - goto exit; 3221 - } 3790 + 3791 + if (((char*)dataptr + entry->length) > (char*)endptr || 3792 + (((char*)dataptr + entry->length) < (char*)buffer) || 3793 + (entry->length > (size_t)hdrsize)) { 3794 + if (COPYFILE_VERBOSE & s->flags) 3795 + copyfile_warn("Incomplete or corrupt attribute entry"); 3796 + copyfile_debug(1, "Entry %d length overflows: offset = %u, length = %u", 3797 + i, entry->offset, entry->length); 3798 + error = -1; 3799 + s->err = EINVAL; /* Invalid buffer */ 3800 + goto exit; 3222 3801 } 3223 - } 3224 - if (tqinfo && !s->qinfo) 3225 - { 3226 - qtn_file_free(tqinfo); 3227 - } 3228 - } 3229 - /* Look for ACL data */ 3230 - else if (strcmp((char*)entry->name, XATTR_SECURITY_NAME) == 0) 3231 - { 3232 - acl_t acl; 3233 - struct stat sb; 3234 - int retry = 1; 3235 - char *tcp = dataptr; 3236 3802 3237 - if (entry->length == 0) { 3238 - /* Not sure how we got here, but we had one case 3239 - * where it was 0. In a normal EA, we can have a 0-byte 3240 - * payload. That means nothing in this case, so we'll 3241 - * simply skip the EA. 3242 - */ 3243 - error = 0; 3244 - goto acl_done; 3245 - } 3246 - /* 3247 - * acl_from_text() requires a NUL-terminated string. The ACL EA, 3248 - * however, may not be NUL-terminated. So in that case, we need to 3249 - * copy it to a +1 sized buffer, to ensure it's got a terminated string. 3250 - */ 3251 - if (tcp[entry->length - 1] != 0) { 3252 - char *tmpstr = malloc(entry->length + 1); 3253 - if (tmpstr == NULL) { 3803 + #else 3804 + dataptr = malloc(entry->length); 3805 + if (dataptr == NULL) { 3806 + copyfile_debug(1, "no memory for %u bytes\n", entry->length); 3254 3807 error = -1; 3808 + s->err = ENOMEM; 3255 3809 goto exit; 3256 3810 } 3257 - strlcpy(tmpstr, tcp, entry->length + 1); 3258 - acl = acl_from_text(tmpstr); 3259 - free(tmpstr); 3260 - } else { 3261 - acl = acl_from_text(tcp); 3262 - } 3811 + if (pread(s->src_fd, dataptr, entry->length, entry->offset) != (ssize_t)entry->length) { 3812 + copyfile_debug(1, "failed to read %u bytes at offset %u\n", entry->length, entry->offset); 3813 + error = -1; 3814 + s->err = EINVAL; 3815 + goto exit; 3816 + } 3817 + #endif 3263 3818 3264 - if (acl != NULL) 3265 - { 3266 - filesec_t fsec_tmp; 3267 - 3268 - if ((fsec_tmp = filesec_init()) == NULL) 3269 - error = -1; 3270 - else if((error = fstatx_np(s->dst_fd, &sb, fsec_tmp)) < 0) 3271 - error = -1; 3272 - else if (filesec_set_property(fsec_tmp, FILESEC_ACL, &acl) < 0) 3273 - error = -1; 3274 - else { 3275 - while (fchmodx_np(s->dst_fd, fsec_tmp) < 0) 3819 + if (strcmp((char*)entry->name, XATTR_QUARANTINE_NAME) == 0) 3276 3820 { 3277 - if (errno == ENOTSUP) 3278 - { 3279 - if (retry && !copyfile_unset_acl(s)) 3280 - { 3281 - retry = 0; 3282 - continue; 3283 - } 3284 - } 3285 - copyfile_warn("setting security information"); 3286 - error = -1; 3287 - break; 3288 - } 3289 - } 3290 - acl_free(acl); 3291 - filesec_free(fsec_tmp); 3821 + qtn_file_t tqinfo = NULL; 3292 3822 3293 - acl_done: 3294 - if (error == -1) 3295 - goto exit; 3296 - } 3297 - } 3298 - /* And, finally, everything else */ 3299 - else 3300 - { 3301 - if (s->copyIntent || 3302 - xattr_preserve_for_intent((char*)entry->name, s->copyIntent) == 1) { 3303 - if (s->statuscb) { 3304 - int rv; 3305 - s->xattr_name = strdup((char*)entry->name); 3306 - s->totalCopied = 0; 3307 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 3308 - if (s->xattr_name) { 3309 - free(s->xattr_name); 3310 - s->xattr_name = NULL; 3823 + if (s->qinfo == NULL) 3824 + { 3825 + tqinfo = qtn_file_alloc(); 3826 + if (tqinfo) 3827 + { 3828 + int x; 3829 + if ((x = qtn_file_init_with_data(tqinfo, dataptr, entry->length)) != 0) 3830 + { 3831 + copyfile_warn("qtn_file_init_with_data failed: %s", qtn_error(x)); 3832 + qtn_file_free(tqinfo); 3833 + tqinfo = NULL; 3834 + } 3835 + } 3836 + } 3837 + else 3838 + { 3839 + tqinfo = s->qinfo; 3840 + } 3841 + if (tqinfo) 3842 + { 3843 + int x; 3844 + x = qtn_file_apply_to_fd(tqinfo, s->dst_fd); 3845 + if (x != 0) { 3846 + copyfile_warn("qtn_file_apply_to_fd failed: %s", qtn_error(x)); 3847 + if (s->statuscb) { 3848 + int rv; 3849 + s->xattr_name = (char*)XATTR_QUARANTINE_NAME; 3850 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 3851 + s->xattr_name = NULL; 3852 + if (rv == COPYFILE_QUIT) { 3853 + error = s->err = x < 0 ? ENOTSUP : errno; 3854 + goto exit; 3855 + } 3856 + } else { 3857 + error = s->err = x < 0 ? ENOTSUP : errno; 3858 + goto exit; 3859 + } 3860 + } 3311 3861 } 3312 - if (rv == COPYFILE_QUIT) { 3313 - s->err = ECANCELED; 3314 - error = -1; 3315 - goto exit; 3862 + if (tqinfo && !s->qinfo) 3863 + { 3864 + qtn_file_free(tqinfo); 3316 3865 } 3317 3866 } 3318 - if (fsetxattr(s->dst_fd, (char *)entry->name, dataptr, entry->length, 0, 0) == -1) { 3319 - if (COPYFILE_VERBOSE & s->flags) 3320 - copyfile_warn("error %d setting attribute %s", errno, entry->name); 3321 - if (s->statuscb) { 3322 - int rv; 3323 - 3324 - s->xattr_name = strdup((char*)entry->name); 3325 - rv = (s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 3326 - if (s->xattr_name) { 3327 - free(s->xattr_name); 3328 - s->xattr_name = NULL; 3329 - } 3330 - if (rv == COPYFILE_QUIT) { 3867 + /* Look for ACL data */ 3868 + else if (strcmp((char*)entry->name, XATTR_SECURITY_NAME) == 0) 3869 + { 3870 + acl_t acl; 3871 + struct stat sb; 3872 + int retry = 1; 3873 + char *tcp = dataptr; 3874 + 3875 + if (entry->length == 0) { 3876 + /* Not sure how we got here, but we had one case 3877 + * where it was 0. In a normal EA, we can have a 0-byte 3878 + * payload. That means nothing in this case, so we'll 3879 + * simply skip the EA. 3880 + */ 3881 + error = 0; 3882 + goto acl_done; 3883 + } 3884 + /* 3885 + * acl_from_text() requires a NUL-terminated string. The ACL EA, 3886 + * however, may not be NUL-terminated. So in that case, we need to 3887 + * copy it to a +1 sized buffer, to ensure it's got a terminated string. 3888 + */ 3889 + if (tcp[entry->length - 1] != 0) { 3890 + char *tmpstr = malloc(entry->length + 1); 3891 + if (tmpstr == NULL) { 3331 3892 error = -1; 3332 3893 goto exit; 3333 3894 } 3895 + // Can't use strlcpy here: tcp is not NUL-terminated! 3896 + memcpy(tmpstr, tcp, entry->length); 3897 + tmpstr[entry->length] = 0; 3898 + acl = acl_from_text(tmpstr); 3899 + free(tmpstr); 3334 3900 } else { 3335 - error = -1; 3336 - goto exit; 3901 + acl = acl_from_text(tcp); 3337 3902 } 3338 - } else if (s->statuscb) { 3339 - int rv; 3340 - s->xattr_name = strdup((char*)entry->name); 3341 - s->totalCopied = entry->length; 3342 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 3343 - if (s->xattr_name) { 3344 - free(s->xattr_name); 3345 - s->xattr_name = NULL; 3903 + 3904 + if (acl != NULL) 3905 + { 3906 + filesec_t fsec_tmp; 3907 + 3908 + if ((fsec_tmp = filesec_init()) == NULL) 3909 + error = -1; 3910 + else if((error = fstatx_np(s->dst_fd, &sb, fsec_tmp)) < 0) 3911 + error = -1; 3912 + else if (filesec_set_property(fsec_tmp, FILESEC_ACL, &acl) < 0) 3913 + error = -1; 3914 + else { 3915 + while (fchmodx_np(s->dst_fd, fsec_tmp) < 0) 3916 + { 3917 + if (errno == ENOTSUP) 3918 + { 3919 + if (retry && !copyfile_unset_acl(s)) 3920 + { 3921 + retry = 0; 3922 + continue; 3923 + } 3346 3924 } 3347 - if (rv == COPYFILE_QUIT) { 3348 - error = -1; 3349 - s->err = ECANCELED; 3350 - goto exit; 3925 + copyfile_warn("setting security information"); 3926 + error = -1; 3927 + break; 3928 + } 3929 + } 3930 + acl_free(acl); 3931 + filesec_free(fsec_tmp); 3932 + 3933 + acl_done: 3934 + if (error == -1) 3935 + goto exit; 3351 3936 } 3352 3937 } 3938 + /* And, finally, everything else */ 3939 + else 3940 + { 3941 + if (s->copyIntent || 3942 + xattr_preserve_for_intent((char*)entry->name, s->copyIntent) == 1) { 3943 + if (s->statuscb) { 3944 + int rv; 3945 + s->xattr_name = strdup((char*)entry->name); 3946 + s->totalCopied = 0; 3947 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 3948 + if (s->xattr_name) { 3949 + free(s->xattr_name); 3950 + s->xattr_name = NULL; 3951 + } 3952 + if (rv == COPYFILE_QUIT) { 3953 + s->err = ECANCELED; 3954 + error = -1; 3955 + goto exit; 3956 + } 3957 + } 3958 + //Silently ignore failure to set XATTR_ROOT_INSTALLED_NAME 3959 + int result = fsetxattr(s->dst_fd, (char *)entry->name, dataptr, entry->length, 0, 0); 3960 + int errorcode = errno; 3961 + if (result == -1 && !(errorcode == EPERM && 3962 + strcmp((char*)entry->name, XATTR_ROOT_INSTALLED_NAME) == 0)) { 3963 + errno = errorcode; 3964 + if (COPYFILE_VERBOSE & s->flags) 3965 + copyfile_warn("error %d setting attribute %s", errorcode, entry->name); 3966 + if (s->statuscb) { 3967 + int rv; 3968 + 3969 + s->xattr_name = strdup((char*)entry->name); 3970 + rv = (s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 3971 + if (s->xattr_name) { 3972 + free(s->xattr_name); 3973 + s->xattr_name = NULL; 3974 + } 3975 + if (rv == COPYFILE_QUIT) { 3976 + error = -1; 3977 + goto exit; 3978 + } 3979 + } else { 3980 + error = -1; 3981 + goto exit; 3982 + } 3983 + } else if (s->statuscb) { 3984 + int rv; 3985 + errno = errorcode; 3986 + s->xattr_name = strdup((char*)entry->name); 3987 + s->totalCopied = entry->length; 3988 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 3989 + if (s->xattr_name) { 3990 + free(s->xattr_name); 3991 + s->xattr_name = NULL; 3992 + } 3993 + if (rv == COPYFILE_QUIT) { 3994 + error = -1; 3995 + s->err = ECANCELED; 3996 + goto exit; 3997 + } 3998 + } else { 3999 + errno = errorcode; 4000 + } 4001 + } 4002 + } 4003 + if (dataptr) { 4004 + free(dataptr); 4005 + dataptr = NULL; 4006 + } 4007 + entry = ATTR_NEXT(entry); 3353 4008 } 3354 - } 3355 - if (dataptr) { 3356 - free(dataptr); 3357 - dataptr = NULL; 3358 - } 3359 - entry = ATTR_NEXT(entry); 3360 4009 } 3361 - } 3362 4010 3363 - /* 3364 - * Extract the Finder Info. 3365 - */ 3366 - if (adhdr->entries[0].offset > (hdrsize - sizeof(emptyfinfo))) { 3367 - error = -1; 3368 - goto exit; 3369 - } 4011 + /* 4012 + * Extract the Finder Info. 4013 + */ 4014 + if (adhdr->entries[0].offset > (hdrsize - sizeof(emptyfinfo))) { 4015 + error = -1; 4016 + goto exit; 4017 + } 3370 4018 3371 - if (bcmp((u_int8_t*)buffer + adhdr->entries[0].offset, emptyfinfo, sizeof(emptyfinfo)) != 0) 3372 - { 3373 - uint16_t *fFlags; 3374 - uint8_t *newFinfo; 3375 - enum { kFinderInvisibleMask = 1 << 14 }; 4019 + if (bcmp((u_int8_t*)buffer + adhdr->entries[0].offset, emptyfinfo, sizeof(emptyfinfo)) != 0) 4020 + { 4021 + uint16_t *fFlags; 4022 + uint8_t *newFinfo; 4023 + enum { kFinderInvisibleMask = 1 << 14 }; 3376 4024 3377 - newFinfo = (u_int8_t*)buffer + adhdr->entries[0].offset; 3378 - fFlags = (uint16_t*)&newFinfo[8]; 3379 - copyfile_debug(3, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME); 3380 - if (s->statuscb) { 3381 - int rv; 3382 - s->xattr_name = (char*)XATTR_FINDERINFO_NAME; 3383 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 3384 - s->xattr_name = NULL; 3385 - if (rv == COPYFILE_QUIT) { 3386 - error = -1; 3387 - s->err = ECANCELED; 3388 - goto exit; 3389 - } else if (rv == COPYFILE_SKIP) { 3390 - goto skip_fi; 3391 - } 3392 - } 3393 - error = fsetxattr(s->dst_fd, XATTR_FINDERINFO_NAME, (u_int8_t*)buffer + adhdr->entries[0].offset, sizeof(emptyfinfo), 0, 0); 3394 - if (error) { 4025 + newFinfo = (u_int8_t*)buffer + adhdr->entries[0].offset; 4026 + fFlags = (uint16_t*)&newFinfo[8]; 4027 + copyfile_debug(3, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME); 3395 4028 if (s->statuscb) { 3396 4029 int rv; 3397 - s->xattr_name = (char *)XATTR_FINDERINFO_NAME; 3398 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 4030 + s->xattr_name = (char*)XATTR_FINDERINFO_NAME; 4031 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 3399 4032 s->xattr_name = NULL; 3400 4033 if (rv == COPYFILE_QUIT) { 3401 4034 error = -1; 3402 4035 s->err = ECANCELED; 3403 4036 goto exit; 4037 + } else if (rv == COPYFILE_SKIP) { 4038 + goto skip_fi; 3404 4039 } 3405 4040 } 3406 - goto exit; 3407 - } else if (s->statuscb) { 3408 - int rv; 3409 - s->xattr_name = (char *)XATTR_FINDERINFO_NAME; 3410 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 3411 - s->xattr_name = NULL; 3412 - if (rv == COPYFILE_QUIT) { 3413 - error = -1; 3414 - s->err = ECANCELED; 4041 + error = fsetxattr(s->dst_fd, XATTR_FINDERINFO_NAME, (u_int8_t*)buffer + adhdr->entries[0].offset, sizeof(emptyfinfo), 0, 0); 4042 + if (error) { 4043 + if (s->statuscb) { 4044 + int rv; 4045 + s->xattr_name = (char *)XATTR_FINDERINFO_NAME; 4046 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 4047 + s->xattr_name = NULL; 4048 + if (rv == COPYFILE_QUIT) { 4049 + error = -1; 4050 + s->err = ECANCELED; 4051 + goto exit; 4052 + } 4053 + } 3415 4054 goto exit; 4055 + } else if (s->statuscb) { 4056 + int rv; 4057 + s->xattr_name = (char *)XATTR_FINDERINFO_NAME; 4058 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 4059 + s->xattr_name = NULL; 4060 + if (rv == COPYFILE_QUIT) { 4061 + error = -1; 4062 + s->err = ECANCELED; 4063 + goto exit; 4064 + } 3416 4065 } 4066 + if (SWAP16(*fFlags) & kFinderInvisibleMask) 4067 + s->internal_flags |= cfMakeFileInvisible; 3417 4068 } 3418 - if (SWAP16(*fFlags) & kFinderInvisibleMask) 3419 - s->internal_flags |= cfMakeFileInvisible; 3420 - } 3421 4069 skip_fi: 3422 4070 3423 - /* 3424 - * Extract the Resource Fork. 3425 - */ 3426 - if (adhdr->entries[1].type == AD_RESOURCE && 3427 - adhdr->entries[1].length > 0) 3428 - { 3429 - void * rsrcforkdata = NULL; 3430 - size_t length; 3431 - off_t offset; 3432 - struct stat sb; 3433 - struct timeval tval[2]; 4071 + /* 4072 + * Extract the Resource Fork. 4073 + */ 4074 + if (adhdr->entries[1].type == AD_RESOURCE && 4075 + adhdr->entries[1].length > 0) 4076 + { 4077 + void * rsrcforkdata = NULL; 4078 + size_t length; 4079 + off_t offset; 4080 + struct stat sb; 4081 + struct attrlist attrlist; 4082 + struct { 4083 + /* Order of these structs matters for setattrlist. */ 4084 + struct timespec mod_time; 4085 + struct timespec acc_time; 4086 + } ma_times; 3434 4087 3435 - length = adhdr->entries[1].length; 3436 - offset = adhdr->entries[1].offset; 3437 - rsrcforkdata = malloc(length); 4088 + length = adhdr->entries[1].length; 4089 + offset = adhdr->entries[1].offset; 4090 + rsrcforkdata = malloc(length); 3438 4091 3439 - if (rsrcforkdata == NULL) { 3440 - copyfile_debug(1, "could not allocate %zu bytes for rsrcforkdata", 3441 - length); 3442 - error = -1; 3443 - goto bad; 3444 - } 4092 + if (rsrcforkdata == NULL) { 4093 + copyfile_debug(1, "could not allocate %zu bytes for rsrcforkdata", 4094 + length); 4095 + error = -1; 4096 + goto bad; 4097 + } 3445 4098 3446 - if (fstat(s->dst_fd, &sb) < 0) 3447 - { 3448 - copyfile_debug(1, "couldn't stat destination file"); 3449 - error = -1; 3450 - goto bad; 3451 - } 4099 + if (fstat(s->dst_fd, &sb) < 0) 4100 + { 4101 + copyfile_debug(1, "couldn't stat destination file"); 4102 + error = -1; 4103 + goto bad; 4104 + } 3452 4105 3453 - bytes = pread(s->src_fd, rsrcforkdata, length, offset); 3454 - if (bytes < (ssize_t)length) 3455 - { 3456 - if (bytes == -1) 3457 - { 3458 - copyfile_debug(1, "couldn't read resource fork"); 3459 - } 3460 - else 3461 - { 3462 - copyfile_debug(1, 3463 - "couldn't read resource fork (only read %d bytes of %d)", 3464 - (int)bytes, (int)length); 3465 - } 3466 - error = -1; 3467 - goto bad; 3468 - } 3469 - if (s->statuscb) { 3470 - int rv; 3471 - s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME; 3472 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 3473 - s->xattr_name = NULL; 3474 - if (rv == COPYFILE_QUIT) { 4106 + bytes = pread(s->src_fd, rsrcforkdata, length, offset); 4107 + if (bytes < (ssize_t)length) 4108 + { 4109 + if (bytes == -1) 4110 + { 4111 + copyfile_debug(1, "couldn't read resource fork"); 4112 + } 4113 + else 4114 + { 4115 + copyfile_debug(1, 4116 + "couldn't read resource fork (only read %d bytes of %d)", 4117 + (int)bytes, (int)length); 4118 + } 3475 4119 error = -1; 3476 - s->err = ECANCELED; 3477 - if (rsrcforkdata) 3478 - free(rsrcforkdata); 3479 - goto exit; 3480 - } else if (rv == COPYFILE_SKIP) { 3481 4120 goto bad; 3482 4121 } 3483 - } 3484 - error = fsetxattr(s->dst_fd, XATTR_RESOURCEFORK_NAME, rsrcforkdata, bytes, 0, 0); 3485 - if (error) 3486 - { 3487 - /* 3488 - * For filesystems that do not natively support named attributes, 3489 - * the kernel creates an AppleDouble file that -- for compatabilty 3490 - * reasons -- has a resource fork containing nothing but a rsrcfork_header_t 3491 - * structure that says there are no resources. So, if fsetxattr has 3492 - * failed, and the resource fork is that empty structure, *and* the 3493 - * target file is a directory, then we do nothing with it. 3494 - */ 3495 - if ((bytes == sizeof(rsrcfork_header_t)) && 3496 - ((sb.st_mode & S_IFMT) == S_IFDIR) && 3497 - (memcmp(rsrcforkdata, &empty_rsrcfork_header, bytes) == 0)) { 3498 - copyfile_debug(2, "not setting empty resource fork on directory"); 3499 - error = errno = 0; 3500 - goto bad; 3501 - } 3502 - if (s->statuscb) { 3503 - int rv; 3504 - s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME; 3505 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 3506 - s->xattr_name = NULL; 3507 - if (rv == COPYFILE_CONTINUE) { 3508 - error = errno = 0; 4122 + if (s->statuscb) { 4123 + int rv; 4124 + s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME; 4125 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 4126 + s->xattr_name = NULL; 4127 + if (rv == COPYFILE_QUIT) { 4128 + error = -1; 4129 + s->err = ECANCELED; 4130 + if (rsrcforkdata) 4131 + free(rsrcforkdata); 4132 + goto exit; 4133 + } else if (rv == COPYFILE_SKIP) { 4134 + goto bad; 4135 + } 4136 + } 4137 + error = fsetxattr(s->dst_fd, XATTR_RESOURCEFORK_NAME, rsrcforkdata, bytes, 0, 0); 4138 + if (error) 4139 + { 4140 + /* 4141 + * For filesystems that do not natively support named attributes, 4142 + * the kernel creates an AppleDouble file that -- for compatabilty 4143 + * reasons -- has a resource fork containing nothing but a rsrcfork_header_t 4144 + * structure that says there are no resources. So, if fsetxattr has 4145 + * failed, and the resource fork is that empty structure, *and* the 4146 + * target file is a directory, then we do nothing with it. 4147 + */ 4148 + if ((bytes == sizeof(rsrcfork_header_t)) && 4149 + ((sb.st_mode & S_IFMT) == S_IFDIR) && 4150 + (memcmp(rsrcforkdata, &empty_rsrcfork_header, bytes) == 0)) { 4151 + copyfile_debug(2, "not setting empty resource fork on directory"); 4152 + error = errno = 0; 4153 + goto bad; 4154 + } 4155 + if (s->statuscb) { 4156 + int rv; 4157 + s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME; 4158 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 4159 + s->xattr_name = NULL; 4160 + if (rv == COPYFILE_CONTINUE) { 4161 + error = errno = 0; 4162 + goto bad; 4163 + } 4164 + } 4165 + copyfile_debug(1, "error %d setting resource fork attribute", error); 4166 + error = -1; 3509 4167 goto bad; 4168 + } else if (s->statuscb) { 4169 + int rv; 4170 + s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME; 4171 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 4172 + s->xattr_name = NULL; 4173 + if (rv == COPYFILE_QUIT) { 4174 + error = -1; 4175 + s->err = ECANCELED; 4176 + if (rsrcforkdata) 4177 + free(rsrcforkdata); 4178 + goto exit; 4179 + } 3510 4180 } 3511 - } 3512 - copyfile_debug(1, "error %d setting resource fork attribute", error); 3513 - error = -1; 3514 - goto bad; 3515 - } else if (s->statuscb) { 3516 - int rv; 3517 - s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME; 3518 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 3519 - s->xattr_name = NULL; 3520 - if (rv == COPYFILE_QUIT) { 3521 - error = -1; 3522 - s->err = ECANCELED; 3523 - if (rsrcforkdata) 3524 - free(rsrcforkdata); 3525 - goto exit; 4181 + copyfile_debug(3, "extracting \"%s\" (%d bytes)", 4182 + XATTR_RESOURCEFORK_NAME, (int)length); 4183 + 4184 + if (!(s->flags & COPYFILE_STAT)) 4185 + { 4186 + /* Try to set m/atimes using setattrlist(), for nanosecond precision. */ 4187 + memset(&attrlist, 0, sizeof(attrlist)); 4188 + attrlist.bitmapcount = ATTR_BIT_MAP_COUNT; 4189 + attrlist.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME; 4190 + ma_times.mod_time = sb.st_mtimespec; 4191 + ma_times.acc_time = sb.st_atimespec; 4192 + if (fsetattrlist(s->dst_fd, &attrlist, &ma_times, sizeof(ma_times), 0) != 0) { 4193 + copyfile_warn("%s: set times", s->dst ? s->dst : "(null dst)"); 4194 + } 3526 4195 } 4196 + bad: 4197 + if (rsrcforkdata) 4198 + free(rsrcforkdata); 3527 4199 } 3528 - copyfile_debug(3, "extracting \"%s\" (%d bytes)", 3529 - XATTR_RESOURCEFORK_NAME, (int)length); 3530 4200 3531 - if (!(s->flags & COPYFILE_STAT)) 4201 + if (COPYFILE_STAT & s->flags) 3532 4202 { 3533 - tval[0].tv_sec = sb.st_atime; 3534 - tval[1].tv_sec = sb.st_mtime; 3535 - tval[0].tv_usec = tval[1].tv_usec = 0; 3536 - 3537 - if (futimes(s->dst_fd, tval)) 3538 - copyfile_warn("%s: set times", s->dst ? s->dst : "(null dst)"); 4203 + error = copyfile_stat(s); 3539 4204 } 3540 - bad: 3541 - if (rsrcforkdata) 3542 - free(rsrcforkdata); 3543 - } 3544 - 3545 - if (COPYFILE_STAT & s->flags) 3546 - { 3547 - error = copyfile_stat(s); 3548 - } 3549 4205 exit: 3550 - if (buffer) free(buffer); 3551 - if (dataptr) free(dataptr); 3552 - return error; 4206 + if (buffer) free(buffer); 4207 + if (dataptr) free(dataptr); 4208 + return error; 3553 4209 } 3554 4210 3555 4211 static int copyfile_pack_quarantine(copyfile_state_t s, void **buf, ssize_t *len) 3556 4212 { 3557 - int ret = 0; 3558 - char qbuf[QTN_SERIALIZED_DATA_MAX]; 3559 - size_t qlen = sizeof(qbuf); 4213 + int ret = 0; 4214 + char qbuf[QTN_SERIALIZED_DATA_MAX]; 4215 + size_t qlen = sizeof(qbuf); 3560 4216 3561 - if (s->qinfo == NULL) 3562 - { 3563 - ret = -1; 3564 - goto done; 3565 - } 4217 + if (s->qinfo == NULL) 4218 + { 4219 + ret = -1; 4220 + goto done; 4221 + } 3566 4222 3567 - if (qtn_file_to_data(s->qinfo, qbuf, &qlen) != 0) 3568 - { 3569 - ret = -1; 3570 - goto done; 3571 - } 4223 + if (qtn_file_to_data(s->qinfo, qbuf, &qlen) != 0) 4224 + { 4225 + ret = -1; 4226 + goto done; 4227 + } 3572 4228 3573 - *buf = malloc(qlen); 3574 - if (*buf) 3575 - { 3576 - memcpy(*buf, qbuf, qlen); 3577 - *len = qlen; 3578 - } 4229 + *buf = malloc(qlen); 4230 + if (*buf) 4231 + { 4232 + memcpy(*buf, qbuf, qlen); 4233 + *len = qlen; 4234 + } 3579 4235 done: 3580 - return ret; 4236 + return ret; 3581 4237 } 3582 4238 3583 4239 static int copyfile_pack_acl(copyfile_state_t s, void **buf, ssize_t *len) 3584 4240 { 3585 - int ret = 0; 3586 - acl_t acl = NULL; 3587 - char *acl_text; 4241 + int ret = 0; 4242 + acl_t acl = NULL; 4243 + char *acl_text; 3588 4244 3589 - if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) < 0) 3590 - { 3591 - if (errno != ENOENT) 4245 + if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) < 0) 3592 4246 { 3593 - ret = -1; 3594 - if (COPYFILE_VERBOSE & s->flags) 3595 - copyfile_warn("getting acl"); 4247 + if (errno != ENOENT) 4248 + { 4249 + ret = -1; 4250 + if (COPYFILE_VERBOSE & s->flags) 4251 + copyfile_warn("getting acl"); 4252 + } 4253 + *len = 0; 4254 + goto exit; 3596 4255 } 3597 - *len = 0; 3598 - goto exit; 3599 - } 3600 4256 3601 - if ((acl_text = acl_to_text(acl, len)) != NULL) 3602 - { 3603 - /* 3604 - * acl_to_text() doesn't include the NUL at the endo 3605 - * in it's count (*len). It does, however, promise to 3606 - * return a valid C string, so we need to up the count 3607 - * by 1. 3608 - */ 3609 - *len = *len + 1; 3610 - *buf = malloc(*len); 3611 - if (*buf) 3612 - memcpy(*buf, acl_text, *len); 3613 - else 3614 - *len = 0; 3615 - acl_free(acl_text); 3616 - } 3617 - copyfile_debug(2, "copied acl (%ld) %p", *len, *buf); 4257 + if ((acl_text = acl_to_text(acl, len)) != NULL) 4258 + { 4259 + /* 4260 + * acl_to_text() doesn't include the NUL at the endo 4261 + * in it's count (*len). It does, however, promise to 4262 + * return a valid C string, so we need to up the count 4263 + * by 1. 4264 + */ 4265 + *len = *len + 1; 4266 + *buf = malloc(*len); 4267 + if (*buf) 4268 + memcpy(*buf, acl_text, *len); 4269 + else 4270 + *len = 0; 4271 + acl_free(acl_text); 4272 + } 4273 + copyfile_debug(2, "copied acl (%ld) %p", *len, *buf); 3618 4274 exit: 3619 - if (acl) 3620 - acl_free(acl); 3621 - return ret; 4275 + if (acl) 4276 + acl_free(acl); 4277 + return ret; 3622 4278 } 3623 4279 3624 4280 static int copyfile_pack_rsrcfork(copyfile_state_t s, attr_header_t *filehdr) 3625 4281 { 3626 - ssize_t datasize; 3627 - char *databuf = NULL; 3628 - int ret = 0; 4282 + ssize_t datasize; 4283 + char *databuf = NULL; 4284 + int ret = 0; 3629 4285 3630 - /* 3631 - * XXX 3632 - * do COPYFILE_COPY_XATTR here; no need to 3633 - * the work if we want to skip. 3634 - */ 4286 + /* 4287 + * XXX 4288 + * do COPYFILE_COPY_XATTR here; no need to 4289 + * the work if we want to skip. 4290 + */ 3635 4291 3636 - if (s->statuscb) 3637 - { 3638 - int rv; 4292 + if (s->statuscb) 4293 + { 4294 + int rv; 3639 4295 3640 - s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME; 4296 + s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME; 3641 4297 3642 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 3643 - s->xattr_name = NULL; 3644 - if (rv == COPYFILE_SKIP) { 3645 - ret = 0; 3646 - goto done; 4298 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 4299 + s->xattr_name = NULL; 4300 + if (rv == COPYFILE_SKIP) { 4301 + ret = 0; 4302 + goto done; 4303 + } 4304 + if (rv == COPYFILE_QUIT) { 4305 + ret = -1; 4306 + s->err = ECANCELED; 4307 + goto done; 4308 + } 3647 4309 } 3648 - if (rv == COPYFILE_QUIT) { 4310 + /* Get the resource fork size */ 4311 + if ((datasize = fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0)) < 0) 4312 + { 4313 + if (COPYFILE_VERBOSE & s->flags) 4314 + copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME, errno); 4315 + return -1; 4316 + } 4317 + 4318 + if (datasize > INT_MAX) { 4319 + s->err = EINVAL; 3649 4320 ret = -1; 3650 - s->err = ECANCELED; 3651 4321 goto done; 3652 4322 } 3653 - } 3654 - /* Get the resource fork size */ 3655 - if ((datasize = fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0)) < 0) 3656 - { 3657 - if (COPYFILE_VERBOSE & s->flags) 3658 - copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME, errno); 3659 - return -1; 3660 - } 3661 4323 3662 - if (datasize > INT_MAX) { 3663 - s->err = EINVAL; 3664 - ret = -1; 3665 - goto done; 3666 - } 4324 + if (s->statuscb) { 4325 + int rv; 4326 + s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME; 3667 4327 3668 - if (s->statuscb) { 3669 - int rv; 3670 - s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME; 4328 + s->totalCopied = 0; 4329 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); 4330 + s->xattr_name = NULL; 4331 + if (rv == COPYFILE_QUIT) { 4332 + s->err = ECANCELED; 4333 + ret = -1; 4334 + goto done; 4335 + } 4336 + } 4337 + if ((databuf = malloc(datasize)) == NULL) 4338 + { 4339 + copyfile_warn("malloc"); 4340 + ret = -1; 4341 + goto done; 4342 + } 3671 4343 3672 - s->totalCopied = 0; 3673 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); 3674 - s->xattr_name = NULL; 3675 - if (rv == COPYFILE_QUIT) { 3676 - s->err = ECANCELED; 4344 + if (fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, databuf, datasize, 0, 0) != datasize) 4345 + { 4346 + if (COPYFILE_VERBOSE & s->flags) 4347 + copyfile_warn("couldn't read entire resource fork"); 3677 4348 ret = -1; 3678 4349 goto done; 3679 4350 } 3680 - } 3681 - if ((databuf = malloc(datasize)) == NULL) 3682 - { 3683 - copyfile_warn("malloc"); 3684 - ret = -1; 3685 - goto done; 3686 - } 3687 4351 3688 - if (fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, databuf, datasize, 0, 0) != datasize) 3689 - { 3690 - if (COPYFILE_VERBOSE & s->flags) 3691 - copyfile_warn("couldn't read entire resource fork"); 3692 - ret = -1; 3693 - goto done; 3694 - } 3695 - 3696 - /* Write the resource fork to disk. */ 3697 - if (pwrite(s->dst_fd, databuf, datasize, filehdr->appledouble.entries[1].offset) != datasize) 3698 - { 3699 - if (COPYFILE_VERBOSE & s->flags) 3700 - copyfile_warn("couldn't write resource fork"); 3701 - } 3702 - if (s->statuscb) 3703 - { 3704 - int rv; 3705 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 3706 - if (rv == COPYFILE_QUIT) { 3707 - ret = -1; 3708 - goto done; 4352 + /* Write the resource fork to disk. */ 4353 + if (pwrite(s->dst_fd, databuf, datasize, filehdr->appledouble.entries[1].offset) != datasize) 4354 + { 4355 + if (COPYFILE_VERBOSE & s->flags) 4356 + copyfile_warn("couldn't write resource fork"); 4357 + } 4358 + if (s->statuscb) 4359 + { 4360 + int rv; 4361 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 4362 + if (rv == COPYFILE_QUIT) { 4363 + ret = -1; 4364 + goto done; 4365 + } 3709 4366 } 3710 - } 3711 - copyfile_debug(3, "copied %zd bytes of \"%s\" data @ offset 0x%08x", 3712 - datasize, XATTR_RESOURCEFORK_NAME, filehdr->appledouble.entries[1].offset); 3713 - filehdr->appledouble.entries[1].length = (u_int32_t)datasize; 4367 + copyfile_debug(3, "copied %zd bytes of \"%s\" data @ offset 0x%08x", 4368 + datasize, XATTR_RESOURCEFORK_NAME, filehdr->appledouble.entries[1].offset); 4369 + filehdr->appledouble.entries[1].length = (u_int32_t)datasize; 3714 4370 3715 4371 done: 3716 - if (ret == -1 && s->statuscb) 3717 - { 3718 - int rv; 3719 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 3720 - if (rv == COPYFILE_CONTINUE) 3721 - ret = 0; 3722 - } 3723 - if (s->xattr_name) { 3724 - s->xattr_name = NULL; 3725 - } 3726 - if (databuf) 3727 - free(databuf); 4372 + if (ret == -1 && s->statuscb) 4373 + { 4374 + int rv; 4375 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 4376 + if (rv == COPYFILE_CONTINUE) 4377 + ret = 0; 4378 + } 4379 + if (s->xattr_name) { 4380 + s->xattr_name = NULL; 4381 + } 4382 + if (databuf) 4383 + free(databuf); 3728 4384 3729 - /* 3730 - * XXX 3731 - * Do status callback here 3732 - * If ret == -1, then error callback 3733 - */ 3734 - return ret; 4385 + /* 4386 + * XXX 4387 + * Do status callback here 4388 + * If ret == -1, then error callback 4389 + */ 4390 + return ret; 3735 4391 } 3736 4392 3737 4393 /* ··· 3739 4395 */ 3740 4396 static int copyfile_pack(copyfile_state_t s) 3741 4397 { 3742 - char *attrnamebuf = NULL, *endnamebuf; 3743 - void *databuf = NULL; 3744 - attr_header_t *filehdr, *endfilehdr; 3745 - attr_entry_t *entry; 3746 - ssize_t listsize = 0; 3747 - char *nameptr; 3748 - size_t namelen; 3749 - size_t entrylen; 3750 - ssize_t datasize; 3751 - size_t offset = 0; 3752 - int hasrsrcfork = 0; 3753 - int error = 0; 3754 - int seenq = 0; // Have we seen any quarantine info already? 4398 + char *attrnamebuf = NULL, *endnamebuf; 4399 + void *databuf = NULL; 4400 + attr_header_t *filehdr, *endfilehdr; 4401 + attr_entry_t *entry; 4402 + ssize_t listsize = 0; 4403 + char *nameptr; 4404 + size_t namelen; 4405 + size_t entrylen; 4406 + ssize_t datasize; 4407 + size_t offset = 0; 4408 + int hasrsrcfork = 0; 4409 + int error = 0; 4410 + int seenq = 0; // Have we seen any quarantine info already? 3755 4411 3756 - filehdr = (attr_header_t *) calloc(1, ATTR_MAX_HDR_SIZE); 4412 + filehdr = (attr_header_t *) calloc(1, ATTR_MAX_HDR_SIZE); 3757 4413 3758 - if (filehdr == NULL) { 3759 - error = -1; 3760 - goto exit; 3761 - } else { 3762 - endfilehdr = (attr_header_t*)(((char*)filehdr) + ATTR_MAX_HDR_SIZE); 3763 - } 4414 + if (filehdr == NULL) { 4415 + error = -1; 4416 + goto exit; 4417 + } else { 4418 + endfilehdr = (attr_header_t*)(((char*)filehdr) + ATTR_MAX_HDR_SIZE); 4419 + } 3764 4420 3765 - attrnamebuf = calloc(1, ATTR_MAX_HDR_SIZE); 3766 - if (attrnamebuf == NULL) { 3767 - error = -1; 3768 - goto exit; 3769 - } else { 3770 - endnamebuf = ((char*)attrnamebuf) + ATTR_MAX_HDR_SIZE; 3771 - } 4421 + attrnamebuf = calloc(1, ATTR_MAX_HDR_SIZE); 4422 + if (attrnamebuf == NULL) { 4423 + error = -1; 4424 + goto exit; 4425 + } else { 4426 + endnamebuf = ((char*)attrnamebuf) + ATTR_MAX_HDR_SIZE; 4427 + } 3772 4428 3773 - /* 3774 - * Fill in the Apple Double Header defaults. 3775 - */ 3776 - filehdr->appledouble.magic = ADH_MAGIC; 3777 - filehdr->appledouble.version = ADH_VERSION; 3778 - filehdr->appledouble.numEntries = 2; 3779 - filehdr->appledouble.entries[0].type = AD_FINDERINFO; 3780 - filehdr->appledouble.entries[0].offset = (u_int32_t)offsetof(apple_double_header_t, finfo); 3781 - filehdr->appledouble.entries[0].length = FINDERINFOSIZE; 3782 - filehdr->appledouble.entries[1].type = AD_RESOURCE; 3783 - filehdr->appledouble.entries[1].offset = (u_int32_t)offsetof(apple_double_header_t, pad); 3784 - filehdr->appledouble.entries[1].length = 0; 3785 - bcopy(ADH_MACOSX, filehdr->appledouble.filler, sizeof(filehdr->appledouble.filler)); 3786 - 3787 - /* 3788 - * Fill in the initial Attribute Header. 3789 - */ 3790 - filehdr->magic = ATTR_HDR_MAGIC; 3791 - filehdr->debug_tag = 0; 3792 - filehdr->data_start = (u_int32_t)sizeof(attr_header_t); 4429 + /* 4430 + * Fill in the Apple Double Header defaults. 4431 + */ 4432 + filehdr->appledouble.magic = ADH_MAGIC; 4433 + filehdr->appledouble.version = ADH_VERSION; 4434 + filehdr->appledouble.numEntries = 2; 4435 + filehdr->appledouble.entries[0].type = AD_FINDERINFO; 4436 + filehdr->appledouble.entries[0].offset = (u_int32_t)__builtin_offsetof(apple_double_header_t, finfo); 4437 + filehdr->appledouble.entries[0].length = FINDERINFOSIZE; 4438 + filehdr->appledouble.entries[1].type = AD_RESOURCE; 4439 + filehdr->appledouble.entries[1].offset = (u_int32_t)__builtin_offsetof(apple_double_header_t, pad); 4440 + filehdr->appledouble.entries[1].length = 0; 4441 + bcopy(ADH_MACOSX, filehdr->appledouble.filler, sizeof(filehdr->appledouble.filler)); 3793 4442 3794 - /* 3795 - * Collect the attribute names. 3796 - */ 3797 - entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t)); 4443 + /* 4444 + * Fill in the initial Attribute Header. 4445 + */ 4446 + filehdr->magic = ATTR_HDR_MAGIC; 4447 + filehdr->debug_tag = 0; 4448 + filehdr->data_start = (u_int32_t)sizeof(attr_header_t); 3798 4449 3799 - /* 3800 - * Test if there are acls to copy 3801 - */ 3802 - if (COPYFILE_ACL & s->flags) 3803 - { 3804 - acl_t temp_acl = NULL; 3805 - if (filesec_get_property(s->fsec, FILESEC_ACL, &temp_acl) < 0) 3806 - { 3807 - copyfile_debug(2, "no acl entries found (errno = %d)", errno); 3808 - } else 3809 - { 3810 - offset = strlen(XATTR_SECURITY_NAME) + 1; 3811 - strcpy(attrnamebuf, XATTR_SECURITY_NAME); 3812 - endnamebuf = attrnamebuf + offset; 3813 - } 3814 - if (temp_acl) 3815 - acl_free(temp_acl); 3816 - } 4450 + /* 4451 + * Collect the attribute names. 4452 + */ 4453 + entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t)); 3817 4454 3818 - if (COPYFILE_XATTR & s->flags) 3819 - { 3820 - ssize_t left = ATTR_MAX_HDR_SIZE - offset; 3821 - if ((listsize = flistxattr(s->src_fd, attrnamebuf + offset, left, 0)) <= 0) 4455 + /* 4456 + * Test if there are acls to copy 4457 + */ 4458 + if (COPYFILE_ACL & s->flags) 3822 4459 { 3823 - copyfile_debug(2, "no extended attributes found (%d)", errno); 3824 - } 3825 - if (listsize > left) 3826 - { 3827 - copyfile_debug(1, "extended attribute list too long"); 3828 - listsize = left; 4460 + acl_t temp_acl = NULL; 4461 + if (filesec_get_property(s->fsec, FILESEC_ACL, &temp_acl) < 0) 4462 + { 4463 + copyfile_debug(2, "no acl entries found (errno = %d)", errno); 4464 + } else 4465 + { 4466 + offset = strlen(XATTR_SECURITY_NAME) + 1; 4467 + strcpy(attrnamebuf, XATTR_SECURITY_NAME); 4468 + endnamebuf = attrnamebuf + offset; 4469 + } 4470 + if (temp_acl) 4471 + acl_free(temp_acl); 3829 4472 } 3830 4473 3831 - endnamebuf = attrnamebuf + offset + (listsize > 0 ? listsize : 0); 3832 - if (endnamebuf > (attrnamebuf + ATTR_MAX_HDR_SIZE)) { 3833 - error = -1; 3834 - goto exit; 3835 - } 3836 - 3837 - if (listsize > 0) 3838 - sort_xattrname_list(attrnamebuf, endnamebuf - attrnamebuf); 3839 - 3840 - for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen) 4474 + if (COPYFILE_XATTR & s->flags) 3841 4475 { 3842 - namelen = strlen(nameptr) + 1; 3843 - /* Skip over FinderInfo or Resource Fork names */ 3844 - if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0 || 3845 - strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0) { 3846 - continue; 3847 - } 3848 - if (strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0) { 3849 - seenq = 1; 3850 - } 4476 + ssize_t left = ATTR_MAX_HDR_SIZE - offset; 4477 + if ((listsize = flistxattr(s->src_fd, attrnamebuf + offset, left, 0)) <= 0) 4478 + { 4479 + copyfile_debug(2, "no extended attributes found (%d)", errno); 4480 + } 4481 + if (listsize > left) 4482 + { 4483 + copyfile_debug(1, "extended attribute list too long"); 4484 + listsize = left; 4485 + } 3851 4486 3852 - /* The system should prevent this from happening, but... */ 3853 - if (namelen > XATTR_MAXNAMELEN + 1) { 3854 - namelen = XATTR_MAXNAMELEN + 1; 3855 - } 3856 - if (s->copyIntent && 3857 - xattr_preserve_for_intent(nameptr, s->copyIntent) == 0) { 3858 - // Skip it 3859 - size_t amt = endnamebuf - (nameptr + namelen); 3860 - memmove(nameptr, nameptr + namelen, amt); 3861 - endnamebuf -= namelen; 3862 - /* Set namelen to 0 so continue doesn't miss names */ 3863 - namelen = 0; 3864 - continue; 3865 - } 3866 - 3867 - if (s->statuscb) { 3868 - int rv; 3869 - char eaname[namelen]; 3870 - bcopy(nameptr, eaname, namelen); 3871 - eaname[namelen - 1] = 0; // Just to be sure! 3872 - s->xattr_name = eaname; 3873 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 3874 - s->xattr_name = NULL; 3875 - if (rv == COPYFILE_QUIT) { 4487 + endnamebuf = attrnamebuf + offset + (listsize > 0 ? listsize : 0); 4488 + if (endnamebuf > (attrnamebuf + ATTR_MAX_HDR_SIZE)) { 3876 4489 error = -1; 3877 - s->err = ECANCELED; 3878 4490 goto exit; 3879 - } else if (rv == COPYFILE_SKIP) { 3880 - size_t amt = endnamebuf - (nameptr + namelen); 3881 - memmove(nameptr, nameptr + namelen, amt); 3882 - endnamebuf -= namelen; 3883 - /* Set namelen to 0 so continue doesn't miss names */ 3884 - namelen = 0; 3885 - continue; 3886 4491 } 3887 - } 3888 - entry->namelen = namelen; 3889 - entry->flags = 0; 3890 - if (nameptr + namelen > endnamebuf) { 3891 - error = -1; 3892 - goto exit; 3893 - } 3894 4492 3895 - bcopy(nameptr, &entry->name[0], namelen); 3896 - copyfile_debug(2, "copied name [%s]", entry->name); 4493 + if (listsize > 0) 4494 + sort_xattrname_list(attrnamebuf, endnamebuf - attrnamebuf); 3897 4495 3898 - entrylen = ATTR_ENTRY_LENGTH(namelen); 3899 - entry = (attr_entry_t *)(((char *)entry) + entrylen); 4496 + for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen) 4497 + { 4498 + namelen = strlen(nameptr) + 1; 4499 + /* Skip over FinderInfo or Resource Fork names */ 4500 + if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0 || 4501 + strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0) { 4502 + continue; 4503 + } 4504 + if (strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0) { 4505 + seenq = 1; 4506 + } 3900 4507 3901 - if ((void*)entry >= (void*)endfilehdr) { 3902 - error = -1; 3903 - goto exit; 3904 - } 4508 + /* The system should prevent this from happening, but... */ 4509 + if (namelen > XATTR_MAXNAMELEN + 1) { 4510 + namelen = XATTR_MAXNAMELEN + 1; 4511 + } 4512 + if (s->copyIntent && 4513 + xattr_preserve_for_intent(nameptr, s->copyIntent) == 0) { 4514 + // Skip it 4515 + size_t amt = endnamebuf - (nameptr + namelen); 4516 + memmove(nameptr, nameptr + namelen, amt); 4517 + endnamebuf -= namelen; 4518 + /* Set namelen to 0 so continue doesn't miss names */ 4519 + namelen = 0; 4520 + continue; 4521 + } 3905 4522 3906 - /* Update the attributes header. */ 3907 - filehdr->num_attrs++; 3908 - filehdr->data_start += (u_int32_t)entrylen; 3909 - } 3910 - } 4523 + if (s->statuscb) { 4524 + int rv; 4525 + char eaname[namelen]; 4526 + bcopy(nameptr, eaname, namelen); 4527 + eaname[namelen - 1] = 0; // Just to be sure! 4528 + s->xattr_name = eaname; 4529 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 4530 + s->xattr_name = NULL; 4531 + if (rv == COPYFILE_QUIT) { 4532 + error = -1; 4533 + s->err = ECANCELED; 4534 + goto exit; 4535 + } else if (rv == COPYFILE_SKIP) { 4536 + size_t amt = endnamebuf - (nameptr + namelen); 4537 + memmove(nameptr, nameptr + namelen, amt); 4538 + endnamebuf -= namelen; 4539 + /* Set namelen to 0 so continue doesn't miss names */ 4540 + namelen = 0; 4541 + continue; 4542 + } 4543 + } 4544 + entry->namelen = namelen; 4545 + entry->flags = 0; 4546 + if (nameptr + namelen > endnamebuf) { 4547 + error = -1; 4548 + goto exit; 4549 + } 3911 4550 3912 - /* 3913 - * If we have any quarantine data, we always pack it. 3914 - * But if we've already got it in the EA list, don't put it in again. 3915 - */ 3916 - if (s->qinfo && !seenq) 3917 - { 3918 - ssize_t left = ATTR_MAX_HDR_SIZE - offset; 3919 - /* strlcpy returns number of bytes copied, but we need offset to point to the next byte */ 3920 - offset += strlcpy(attrnamebuf + offset, XATTR_QUARANTINE_NAME, left) + 1; 3921 - } 4551 + bcopy(nameptr, &entry->name[0], namelen); 4552 + copyfile_debug(2, "copied name [%s]", entry->name); 3922 4553 3923 - seenq = 0; 3924 - /* 3925 - * Collect the attribute data. 3926 - */ 3927 - entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t)); 4554 + entrylen = ATTR_ENTRY_LENGTH(namelen); 4555 + entry = (attr_entry_t *)(((char *)entry) + entrylen); 3928 4556 3929 - for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen + 1) 3930 - { 3931 - namelen = strlen(nameptr); 4557 + if ((void*)entry >= (void*)endfilehdr) { 4558 + error = -1; 4559 + goto exit; 4560 + } 4561 + 4562 + /* Update the attributes header. */ 4563 + filehdr->num_attrs++; 4564 + filehdr->data_start += (u_int32_t)entrylen; 4565 + } 4566 + } 3932 4567 3933 - if (strcmp(nameptr, XATTR_SECURITY_NAME) == 0) 3934 - copyfile_pack_acl(s, &databuf, &datasize); 3935 - else if (s->qinfo && strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0) 4568 + /* 4569 + * If we have any quarantine data, we always pack it. 4570 + * But if we've already got it in the EA list, don't put it in again. 4571 + */ 4572 + if (s->qinfo && !seenq) 3936 4573 { 3937 - copyfile_pack_quarantine(s, &databuf, &datasize); 4574 + ssize_t left = ATTR_MAX_HDR_SIZE - offset; 4575 + /* strlcpy returns number of bytes copied, but we need offset to point to the next byte */ 4576 + offset += strlcpy(attrnamebuf + offset, XATTR_QUARANTINE_NAME, left) + 1; 3938 4577 } 3939 - /* Check for Finder Info. */ 3940 - else if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0) 4578 + 4579 + seenq = 0; 4580 + /* 4581 + * Collect the attribute data. 4582 + */ 4583 + entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t)); 4584 + 4585 + for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen + 1) 3941 4586 { 3942 - if (s->statuscb) 3943 - { 3944 - int rv; 3945 - s->xattr_name = (char*)XATTR_FINDERINFO_NAME; 3946 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 3947 - s->xattr_name = NULL; 3948 - if (rv == COPYFILE_QUIT) 4587 + namelen = strlen(nameptr); 4588 + 4589 + if (strcmp(nameptr, XATTR_SECURITY_NAME) == 0) 4590 + copyfile_pack_acl(s, &databuf, &datasize); 4591 + else if (s->qinfo && strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0) 3949 4592 { 3950 - s->xattr_name = NULL; 3951 - s->err = ECANCELED; 3952 - error = -1; 3953 - goto exit; 4593 + copyfile_pack_quarantine(s, &databuf, &datasize); 3954 4594 } 3955 - else if (rv == COPYFILE_SKIP) 4595 + /* Check for Finder Info. */ 4596 + else if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0) 3956 4597 { 3957 - s->xattr_name = NULL; 3958 - continue; 4598 + if (s->statuscb) 4599 + { 4600 + int rv; 4601 + s->xattr_name = (char*)XATTR_FINDERINFO_NAME; 4602 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); 4603 + s->xattr_name = NULL; 4604 + if (rv == COPYFILE_QUIT) 4605 + { 4606 + s->xattr_name = NULL; 4607 + s->err = ECANCELED; 4608 + error = -1; 4609 + goto exit; 4610 + } 4611 + else if (rv == COPYFILE_SKIP) 4612 + { 4613 + s->xattr_name = NULL; 4614 + continue; 4615 + } 4616 + s->totalCopied = 0; 4617 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); 4618 + s->xattr_name = NULL; 4619 + if (rv == COPYFILE_QUIT) 4620 + { 4621 + s->err = ECANCELED; 4622 + error = -1; 4623 + goto exit; 4624 + } 4625 + } 4626 + datasize = fgetxattr(s->src_fd, nameptr, (u_int8_t*)filehdr + filehdr->appledouble.entries[0].offset, 32, 0, 0); 4627 + if (datasize < 0) 4628 + { 4629 + if (s->statuscb) { 4630 + int rv; 4631 + s->xattr_name = strdup(nameptr); 4632 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 4633 + if (s->xattr_name) { 4634 + free(s->xattr_name); 4635 + s->xattr_name = NULL; 4636 + } 4637 + if (rv == COPYFILE_QUIT) { 4638 + error = -1; 4639 + goto exit; 4640 + } 4641 + } 4642 + if (COPYFILE_VERBOSE & s->flags) 4643 + copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno); 4644 + } else if (datasize != 32) 4645 + { 4646 + if (COPYFILE_VERBOSE & s->flags) 4647 + copyfile_warn("unexpected size (%ld) for \"%s\"", datasize, nameptr); 4648 + } else 4649 + { 4650 + if (COPYFILE_VERBOSE & s->flags) 4651 + copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x", 4652 + XATTR_FINDERINFO_NAME, filehdr->appledouble.entries[0].offset); 4653 + if (s->statuscb) { 4654 + int rv; 4655 + s->xattr_name = strdup(nameptr); 4656 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 4657 + if (s->xattr_name) { 4658 + free(s->xattr_name); 4659 + s->xattr_name = NULL; 4660 + } 4661 + if (rv == COPYFILE_QUIT) { 4662 + error = -1; 4663 + goto exit; 4664 + } 4665 + } 4666 + } 4667 + continue; /* finder info doesn't have an attribute entry */ 3959 4668 } 3960 - s->totalCopied = 0; 3961 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); 3962 - s->xattr_name = NULL; 3963 - if (rv == COPYFILE_QUIT) 4669 + /* Check for Resource Fork. */ 4670 + else if (strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0) 4671 + { 4672 + hasrsrcfork = 1; 4673 + continue; 4674 + } else 3964 4675 { 3965 - s->err = ECANCELED; 3966 - error = -1; 3967 - goto exit; 3968 - } 3969 - } 3970 - datasize = fgetxattr(s->src_fd, nameptr, (u_int8_t*)filehdr + filehdr->appledouble.entries[0].offset, 32, 0, 0); 3971 - if (datasize < 0) 3972 - { 3973 - if (s->statuscb) { 3974 - int rv; 3975 - s->xattr_name = strdup(nameptr); 3976 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 3977 - if (s->xattr_name) { 3978 - free(s->xattr_name); 3979 - s->xattr_name = NULL; 4676 + /* Just a normal attribute. */ 4677 + if (s->statuscb) 4678 + { 4679 + int rv; 4680 + s->xattr_name = strdup(nameptr); 4681 + s->totalCopied = 0; 4682 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); 4683 + if (s->xattr_name) { 4684 + free(s->xattr_name); 4685 + s->xattr_name = NULL; 4686 + } 4687 + /* 4688 + * Due to the nature of the packed file, we can't skip at this point. 4689 + */ 4690 + if (rv == COPYFILE_QUIT) 4691 + { 4692 + s->err = ECANCELED; 4693 + error = -1; 4694 + goto exit; 4695 + } 3980 4696 } 3981 - if (rv == COPYFILE_QUIT) { 3982 - error = -1; 3983 - goto exit; 4697 + datasize = fgetxattr(s->src_fd, nameptr, NULL, 0, 0, 0); 4698 + if (datasize == 0) 4699 + goto next; 4700 + if (datasize < 0) 4701 + { 4702 + if (COPYFILE_VERBOSE & s->flags) 4703 + copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno); 4704 + if (s->statuscb) 4705 + { 4706 + int rv; 4707 + s->xattr_name = strdup(nameptr); 4708 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 4709 + if (s->xattr_name) { 4710 + free(s->xattr_name); 4711 + s->xattr_name = NULL; 4712 + } 4713 + if (rv == COPYFILE_QUIT) 4714 + { 4715 + s->err = ECANCELED; 4716 + error = -1; 4717 + goto exit; 4718 + } 4719 + } 4720 + goto next; 3984 4721 } 3985 - } 3986 - if (COPYFILE_VERBOSE & s->flags) 3987 - copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno); 3988 - } else if (datasize != 32) 3989 - { 3990 - if (COPYFILE_VERBOSE & s->flags) 3991 - copyfile_warn("unexpected size (%ld) for \"%s\"", datasize, nameptr); 3992 - } else 3993 - { 3994 - if (COPYFILE_VERBOSE & s->flags) 3995 - copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x", 3996 - XATTR_FINDERINFO_NAME, filehdr->appledouble.entries[0].offset); 3997 - if (s->statuscb) { 3998 - int rv; 3999 - s->xattr_name = strdup(nameptr); 4000 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 4001 - if (s->xattr_name) { 4002 - free(s->xattr_name); 4003 - s->xattr_name = NULL; 4722 + if (datasize > XATTR_MAXATTRLEN) 4723 + { 4724 + if (COPYFILE_VERBOSE & s->flags) 4725 + copyfile_warn("skipping attr \"%s\" (too big)", nameptr); 4726 + goto next; 4004 4727 } 4005 - if (rv == COPYFILE_QUIT) { 4728 + databuf = malloc(datasize); 4729 + if (databuf == NULL) { 4006 4730 error = -1; 4007 - goto exit; 4731 + continue; 4732 + } 4733 + datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0); 4734 + if (s->statuscb) { 4735 + int rv; 4736 + s->xattr_name = strdup(nameptr); 4737 + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 4738 + if (s->xattr_name) { 4739 + free(s->xattr_name); 4740 + s->xattr_name = NULL; 4741 + } 4742 + if (rv == COPYFILE_QUIT) { 4743 + s->err = ECANCELED; 4744 + error = -1; 4745 + goto exit; 4746 + } 4008 4747 } 4009 - } 4010 - } 4011 - continue; /* finder info doesn't have an attribute entry */ 4012 - } 4013 - /* Check for Resource Fork. */ 4014 - else if (strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0) 4015 - { 4016 - hasrsrcfork = 1; 4017 - continue; 4018 - } else 4019 - { 4020 - /* Just a normal attribute. */ 4021 - if (s->statuscb) 4022 - { 4023 - int rv; 4024 - s->xattr_name = strdup(nameptr); 4025 - s->totalCopied = 0; 4026 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); 4027 - if (s->xattr_name) { 4028 - free(s->xattr_name); 4029 - s->xattr_name = NULL; 4030 4748 } 4749 + 4750 + entry->length = (u_int32_t)datasize; 4751 + entry->offset = filehdr->data_start + filehdr->data_length; 4752 + 4753 + filehdr->data_length += (u_int32_t)datasize; 4754 + #if 0 4031 4755 /* 4032 - * Due to the nature of the packed file, we can't skip at this point. 4756 + * >>> WARNING <<< 4757 + * This assumes that the data is fits in memory (not 4758 + * the case when there are lots of attributes or one of 4759 + * the attributes is very large. 4033 4760 */ 4034 - if (rv == COPYFILE_QUIT) 4035 - { 4036 - s->err = ECANCELED; 4037 - error = -1; 4038 - goto exit; 4761 + if (entry->offset > ATTR_MAX_SIZE || 4762 + (entry->offset + datasize > ATTR_MAX_SIZE)) { 4763 + error = 1; 4764 + } else { 4765 + bcopy(databuf, (char*)filehdr + entry->offset, datasize); 4039 4766 } 4040 - } 4041 - datasize = fgetxattr(s->src_fd, nameptr, NULL, 0, 0, 0); 4042 - if (datasize == 0) 4043 - goto next; 4044 - if (datasize < 0) 4045 - { 4046 - if (COPYFILE_VERBOSE & s->flags) 4047 - copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno); 4048 - if (s->statuscb) 4049 - { 4050 - int rv; 4051 - s->xattr_name = strdup(nameptr); 4052 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); 4053 - if (s->xattr_name) { 4054 - free(s->xattr_name); 4055 - s->xattr_name = NULL; 4056 - } 4057 - if (rv == COPYFILE_QUIT) 4058 - { 4059 - s->err = ECANCELED; 4060 - error = -1; 4061 - goto exit; 4062 - } 4767 + #else 4768 + if (pwrite(s->dst_fd, databuf, datasize, entry->offset) != datasize) { 4769 + error = 1; 4063 4770 } 4064 - goto next; 4065 - } 4066 - if (datasize > XATTR_MAXATTRLEN) 4067 - { 4068 - if (COPYFILE_VERBOSE & s->flags) 4069 - copyfile_warn("skipping attr \"%s\" (too big)", nameptr); 4070 - goto next; 4071 - } 4072 - databuf = malloc(datasize); 4073 - if (databuf == NULL) { 4074 - error = -1; 4075 - continue; 4076 - } 4077 - datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0); 4078 - if (s->statuscb) { 4079 - int rv; 4080 - s->xattr_name = strdup(nameptr); 4081 - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); 4082 - if (s->xattr_name) { 4083 - free(s->xattr_name); 4084 - s->xattr_name = NULL; 4085 - } 4086 - if (rv == COPYFILE_QUIT) { 4087 - s->err = ECANCELED; 4088 - error = -1; 4089 - goto exit; 4090 - } 4091 - } 4771 + #endif 4772 + free(databuf); 4773 + 4774 + copyfile_debug(3, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize, nameptr, entry->offset); 4775 + next: 4776 + /* bump to next entry */ 4777 + entrylen = ATTR_ENTRY_LENGTH(entry->namelen); 4778 + entry = (attr_entry_t *)((char *)entry + entrylen); 4092 4779 } 4093 4780 4094 - entry->length = (u_int32_t)datasize; 4095 - entry->offset = filehdr->data_start + filehdr->data_length; 4781 + /* Now we know where the resource fork data starts. */ 4782 + filehdr->appledouble.entries[1].offset = (filehdr->data_start + filehdr->data_length); 4096 4783 4097 - filehdr->data_length += (u_int32_t)datasize; 4098 - #if 0 4099 - /* 4100 - * >>> WARNING <<< 4101 - * This assumes that the data is fits in memory (not 4102 - * the case when there are lots of attributes or one of 4103 - * the attributes is very large. 4104 - */ 4105 - if (entry->offset > ATTR_MAX_SIZE || 4106 - (entry->offset + datasize > ATTR_MAX_SIZE)) { 4107 - error = 1; 4108 - } else { 4109 - bcopy(databuf, (char*)filehdr + entry->offset, datasize); 4110 - } 4111 - #else 4112 - if (pwrite(s->dst_fd, databuf, datasize, entry->offset) != datasize) { 4113 - error = 1; 4114 - } 4115 - #endif 4116 - free(databuf); 4784 + /* We also know the size of the "Finder Info entry. */ 4785 + filehdr->appledouble.entries[0].length = 4786 + filehdr->appledouble.entries[1].offset - filehdr->appledouble.entries[0].offset; 4117 4787 4118 - copyfile_debug(3, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize, nameptr, entry->offset); 4119 - next: 4120 - /* bump to next entry */ 4121 - entrylen = ATTR_ENTRY_LENGTH(entry->namelen); 4122 - entry = (attr_entry_t *)((char *)entry + entrylen); 4123 - } 4788 + filehdr->total_size = filehdr->appledouble.entries[1].offset; 4124 4789 4125 - /* Now we know where the resource fork data starts. */ 4126 - filehdr->appledouble.entries[1].offset = (filehdr->data_start + filehdr->data_length); 4127 - 4128 - /* We also know the size of the "Finder Info entry. */ 4129 - filehdr->appledouble.entries[0].length = 4130 - filehdr->appledouble.entries[1].offset - filehdr->appledouble.entries[0].offset; 4131 - 4132 - filehdr->total_size = filehdr->appledouble.entries[1].offset; 4133 - 4134 - /* Copy Resource Fork. */ 4135 - if (hasrsrcfork && (error = copyfile_pack_rsrcfork(s, filehdr))) 4136 - goto exit; 4790 + /* Copy Resource Fork. */ 4791 + if (hasrsrcfork && (error = copyfile_pack_rsrcfork(s, filehdr))) 4792 + goto exit; 4137 4793 4138 - /* Write the header to disk. */ 4139 - datasize = filehdr->data_start; 4794 + /* Write the header to disk. */ 4795 + datasize = filehdr->data_start; 4140 4796 4141 - swap_adhdr(&filehdr->appledouble); 4142 - swap_attrhdr(filehdr); 4143 - swap_attrhdr_entries(filehdr); 4797 + swap_adhdr(&filehdr->appledouble); 4798 + swap_attrhdr(filehdr); 4799 + swap_attrhdr_entries(filehdr); 4144 4800 4145 - if (pwrite(s->dst_fd, filehdr, datasize, 0) != datasize) 4146 - { 4147 - if (COPYFILE_VERBOSE & s->flags) 4148 - copyfile_warn("couldn't write file header"); 4149 - error = -1; 4150 - goto exit; 4151 - } 4801 + if (pwrite(s->dst_fd, filehdr, datasize, 0) != datasize) 4802 + { 4803 + if (COPYFILE_VERBOSE & s->flags) 4804 + copyfile_warn("couldn't write file header"); 4805 + error = -1; 4806 + goto exit; 4807 + } 4152 4808 exit: 4153 - if (filehdr) free(filehdr); 4154 - if (attrnamebuf) free(attrnamebuf); 4809 + if (filehdr) free(filehdr); 4810 + if (attrnamebuf) free(attrnamebuf); 4155 4811 4156 - if (error) 4157 - return error; 4158 - else 4159 - return copyfile_stat(s); 4812 + if (error) 4813 + return error; 4814 + else 4815 + return copyfile_stat(s); 4160 4816 }
+417
src/copyfile/copyfile.xcodeproj/project.pbxproj
··· 1 + // !$*UTF8*$! 2 + { 3 + archiveVersion = 1; 4 + classes = { 5 + }; 6 + objectVersion = 46; 7 + objects = { 8 + 9 + /* Begin PBXBuildFile section */ 10 + 098AF3B622692BF300F9BA42 /* stat_test.c in Sources */ = {isa = PBXBuildFile; fileRef = 098AF3B522692BF300F9BA42 /* stat_test.c */; }; 11 + 721D4F071EA95283000F0555 /* copyfile.c in Sources */ = {isa = PBXBuildFile; fileRef = FCCE17C1135A658F002CEE6D /* copyfile.c */; }; 12 + 721D4F081EA95290000F0555 /* xattr_flags.c in Sources */ = {isa = PBXBuildFile; fileRef = 72406E621676C3C80099568B /* xattr_flags.c */; }; 13 + 72406E631676C3C80099568B /* xattr_flags.c in Sources */ = {isa = PBXBuildFile; fileRef = 72406E621676C3C80099568B /* xattr_flags.c */; }; 14 + 726EE9DB1E9423E50017A5B9 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 726EE9DA1E9423E50017A5B9 /* main.c */; }; 15 + 726EE9E01E9425160017A5B9 /* sparse_test.c in Sources */ = {isa = PBXBuildFile; fileRef = 726EE9DE1E9425160017A5B9 /* sparse_test.c */; }; 16 + 726EE9E41E946B320017A5B9 /* systemx.c in Sources */ = {isa = PBXBuildFile; fileRef = 726EE9E21E946B320017A5B9 /* systemx.c */; }; 17 + 726EE9E61E946D590017A5B9 /* test_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 726EE9E51E946D590017A5B9 /* test_utils.c */; }; 18 + 72B4C0F41676C47D00C13E05 /* copyfile_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 72B4C0F31676C47D00C13E05 /* copyfile_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 19 + 72EAA3B016A72F4500833E98 /* xattr_flags.h in Headers */ = {isa = PBXBuildFile; fileRef = 72EAA3AF16A72F4500833E98 /* xattr_flags.h */; settings = {ATTRIBUTES = (Public, ); }; }; 20 + 86EF9F0A1834018C00AAB3F3 /* xattr_properties.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EF9F091834018C00AAB3F3 /* xattr_properties.h */; settings = {ATTRIBUTES = (Private, ); }; }; 21 + FCCE17C3135A658F002CEE6D /* copyfile.c in Sources */ = {isa = PBXBuildFile; fileRef = FCCE17C1135A658F002CEE6D /* copyfile.c */; }; 22 + FCCE17C4135A658F002CEE6D /* copyfile.h in Headers */ = {isa = PBXBuildFile; fileRef = FCCE17C2135A658F002CEE6D /* copyfile.h */; settings = {ATTRIBUTES = (Public, ); }; }; 23 + /* End PBXBuildFile section */ 24 + 25 + /* Begin PBXCopyFilesBuildPhase section */ 26 + 726EE9D61E9423E50017A5B9 /* CopyFiles */ = { 27 + isa = PBXCopyFilesBuildPhase; 28 + buildActionMask = 2147483647; 29 + dstPath = /usr/share/man/man1/; 30 + dstSubfolderSpec = 0; 31 + files = ( 32 + ); 33 + runOnlyForDeploymentPostprocessing = 1; 34 + }; 35 + /* End PBXCopyFilesBuildPhase section */ 36 + 37 + /* Begin PBXFileReference section */ 38 + 098AF3B422692BF300F9BA42 /* stat_test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stat_test.h; sourceTree = "<group>"; }; 39 + 098AF3B522692BF300F9BA42 /* stat_test.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = stat_test.c; sourceTree = "<group>"; }; 40 + 098AF3B7226A510E00F9BA42 /* copyfile_test.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = copyfile_test.entitlements; sourceTree = "<group>"; }; 41 + 3F1EFD4C185C4EB400D1C970 /* copyfile.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = copyfile.xcconfig; path = xcodescripts/copyfile.xcconfig; sourceTree = "<group>"; }; 42 + 721D4F051EA95008000F0555 /* libcopyfile.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcopyfile.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/lib/system/libcopyfile.tbd; sourceTree = DEVELOPER_DIR; }; 43 + 72406E621676C3C80099568B /* xattr_flags.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xattr_flags.c; sourceTree = "<group>"; }; 44 + 726EE9D81E9423E50017A5B9 /* copyfile_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = copyfile_test; sourceTree = BUILT_PRODUCTS_DIR; }; 45 + 726EE9DA1E9423E50017A5B9 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; }; 46 + 726EE9DE1E9425160017A5B9 /* sparse_test.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sparse_test.c; sourceTree = "<group>"; }; 47 + 726EE9DF1E9425160017A5B9 /* sparse_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sparse_test.h; sourceTree = "<group>"; }; 48 + 726EE9E11E9427B40017A5B9 /* test_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = test_utils.h; sourceTree = "<group>"; }; 49 + 726EE9E21E946B320017A5B9 /* systemx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = systemx.c; sourceTree = "<group>"; }; 50 + 726EE9E31E946B320017A5B9 /* systemx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = systemx.h; sourceTree = "<group>"; }; 51 + 726EE9E51E946D590017A5B9 /* test_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test_utils.c; sourceTree = "<group>"; }; 52 + 72B4C0F31676C47D00C13E05 /* copyfile_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = copyfile_private.h; sourceTree = "<group>"; }; 53 + 72EAA3AF16A72F4500833E98 /* xattr_flags.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xattr_flags.h; sourceTree = "<group>"; }; 54 + 861E1C14180F0AF900E65B9A /* xattr_name_with_flags.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = xattr_name_with_flags.3; sourceTree = "<group>"; }; 55 + 86EF9F091834018C00AAB3F3 /* xattr_properties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xattr_properties.h; sourceTree = "<group>"; }; 56 + FCCE17BB135A6444002CEE6D /* libcopyfile.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libcopyfile.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 57 + FCCE17C0135A658F002CEE6D /* copyfile.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = copyfile.3; sourceTree = "<group>"; }; 58 + FCCE17C1135A658F002CEE6D /* copyfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = copyfile.c; sourceTree = "<group>"; }; 59 + FCCE17C2135A658F002CEE6D /* copyfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = copyfile.h; sourceTree = "<group>"; }; 60 + /* End PBXFileReference section */ 61 + 62 + /* Begin PBXFrameworksBuildPhase section */ 63 + 726EE9D51E9423E50017A5B9 /* Frameworks */ = { 64 + isa = PBXFrameworksBuildPhase; 65 + buildActionMask = 2147483647; 66 + files = ( 67 + ); 68 + runOnlyForDeploymentPostprocessing = 0; 69 + }; 70 + FCCE17B8135A6444002CEE6D /* Frameworks */ = { 71 + isa = PBXFrameworksBuildPhase; 72 + buildActionMask = 2147483647; 73 + files = ( 74 + ); 75 + runOnlyForDeploymentPostprocessing = 0; 76 + }; 77 + /* End PBXFrameworksBuildPhase section */ 78 + 79 + /* Begin PBXGroup section */ 80 + 721D4F041EA95007000F0555 /* Frameworks */ = { 81 + isa = PBXGroup; 82 + children = ( 83 + 721D4F051EA95008000F0555 /* libcopyfile.tbd */, 84 + ); 85 + name = Frameworks; 86 + sourceTree = "<group>"; 87 + }; 88 + 726EE9D91E9423E50017A5B9 /* copyfile_test */ = { 89 + isa = PBXGroup; 90 + children = ( 91 + 726EE9DA1E9423E50017A5B9 /* main.c */, 92 + 726EE9DE1E9425160017A5B9 /* sparse_test.c */, 93 + 726EE9DF1E9425160017A5B9 /* sparse_test.h */, 94 + 098AF3B522692BF300F9BA42 /* stat_test.c */, 95 + 098AF3B422692BF300F9BA42 /* stat_test.h */, 96 + 726EE9E51E946D590017A5B9 /* test_utils.c */, 97 + 726EE9E11E9427B40017A5B9 /* test_utils.h */, 98 + 726EE9E21E946B320017A5B9 /* systemx.c */, 99 + 726EE9E31E946B320017A5B9 /* systemx.h */, 100 + 098AF3B7226A510E00F9BA42 /* copyfile_test.entitlements */, 101 + ); 102 + path = copyfile_test; 103 + sourceTree = "<group>"; 104 + }; 105 + FCCE17AB135A5FFB002CEE6D = { 106 + isa = PBXGroup; 107 + children = ( 108 + 3F1EFD4C185C4EB400D1C970 /* copyfile.xcconfig */, 109 + 861E1C14180F0AF900E65B9A /* xattr_name_with_flags.3 */, 110 + 72B4C0F31676C47D00C13E05 /* copyfile_private.h */, 111 + 72EAA3AF16A72F4500833E98 /* xattr_flags.h */, 112 + 72406E621676C3C80099568B /* xattr_flags.c */, 113 + FCCE17C0135A658F002CEE6D /* copyfile.3 */, 114 + FCCE17C1135A658F002CEE6D /* copyfile.c */, 115 + 86EF9F091834018C00AAB3F3 /* xattr_properties.h */, 116 + FCCE17C2135A658F002CEE6D /* copyfile.h */, 117 + 726EE9D91E9423E50017A5B9 /* copyfile_test */, 118 + FCCE17BC135A6444002CEE6D /* Products */, 119 + 721D4F041EA95007000F0555 /* Frameworks */, 120 + ); 121 + sourceTree = "<group>"; 122 + usesTabs = 1; 123 + }; 124 + FCCE17BC135A6444002CEE6D /* Products */ = { 125 + isa = PBXGroup; 126 + children = ( 127 + FCCE17BB135A6444002CEE6D /* libcopyfile.dylib */, 128 + 726EE9D81E9423E50017A5B9 /* copyfile_test */, 129 + ); 130 + name = Products; 131 + sourceTree = "<group>"; 132 + }; 133 + /* End PBXGroup section */ 134 + 135 + /* Begin PBXHeadersBuildPhase section */ 136 + FCCE17B9135A6444002CEE6D /* Headers */ = { 137 + isa = PBXHeadersBuildPhase; 138 + buildActionMask = 2147483647; 139 + files = ( 140 + FCCE17C4135A658F002CEE6D /* copyfile.h in Headers */, 141 + 72EAA3B016A72F4500833E98 /* xattr_flags.h in Headers */, 142 + 72B4C0F41676C47D00C13E05 /* copyfile_private.h in Headers */, 143 + 86EF9F0A1834018C00AAB3F3 /* xattr_properties.h in Headers */, 144 + ); 145 + runOnlyForDeploymentPostprocessing = 0; 146 + }; 147 + /* End PBXHeadersBuildPhase section */ 148 + 149 + /* Begin PBXNativeTarget section */ 150 + 726EE9D71E9423E50017A5B9 /* copyfile_test */ = { 151 + isa = PBXNativeTarget; 152 + buildConfigurationList = 726EE9DD1E9423E50017A5B9 /* Build configuration list for PBXNativeTarget "copyfile_test" */; 153 + buildPhases = ( 154 + 726EE9D41E9423E50017A5B9 /* Sources */, 155 + 726EE9D51E9423E50017A5B9 /* Frameworks */, 156 + 726EE9D61E9423E50017A5B9 /* CopyFiles */, 157 + ); 158 + buildRules = ( 159 + ); 160 + dependencies = ( 161 + ); 162 + name = copyfile_test; 163 + productName = copyfile_test; 164 + productReference = 726EE9D81E9423E50017A5B9 /* copyfile_test */; 165 + productType = "com.apple.product-type.tool"; 166 + }; 167 + FCCE17BA135A6444002CEE6D /* copyfile */ = { 168 + isa = PBXNativeTarget; 169 + buildConfigurationList = FCCE17BE135A6444002CEE6D /* Build configuration list for PBXNativeTarget "copyfile" */; 170 + buildPhases = ( 171 + FCCE17B7135A6444002CEE6D /* Sources */, 172 + FCCE17B8135A6444002CEE6D /* Frameworks */, 173 + FCCE17B9135A6444002CEE6D /* Headers */, 174 + FCCE17DF135A70A0002CEE6D /* Run Script */, 175 + ); 176 + buildRules = ( 177 + ); 178 + dependencies = ( 179 + ); 180 + name = copyfile; 181 + productName = copyfile; 182 + productReference = FCCE17BB135A6444002CEE6D /* libcopyfile.dylib */; 183 + productType = "com.apple.product-type.library.dynamic"; 184 + }; 185 + /* End PBXNativeTarget section */ 186 + 187 + /* Begin PBXProject section */ 188 + FCCE17AD135A5FFB002CEE6D /* Project object */ = { 189 + isa = PBXProject; 190 + attributes = { 191 + ORGANIZATIONNAME = "Apple Inc."; 192 + TargetAttributes = { 193 + 726EE9D71E9423E50017A5B9 = { 194 + CreatedOnToolsVersion = 9.0; 195 + ProvisioningStyle = Automatic; 196 + }; 197 + }; 198 + }; 199 + buildConfigurationList = FCCE17B0135A5FFB002CEE6D /* Build configuration list for PBXProject "copyfile" */; 200 + compatibilityVersion = "Xcode 3.2"; 201 + developmentRegion = English; 202 + hasScannedForEncodings = 0; 203 + knownRegions = ( 204 + en, 205 + ); 206 + mainGroup = FCCE17AB135A5FFB002CEE6D; 207 + productRefGroup = FCCE17BC135A6444002CEE6D /* Products */; 208 + projectDirPath = ""; 209 + projectRoot = ""; 210 + targets = ( 211 + FCCE17BA135A6444002CEE6D /* copyfile */, 212 + 726EE9D71E9423E50017A5B9 /* copyfile_test */, 213 + ); 214 + }; 215 + /* End PBXProject section */ 216 + 217 + /* Begin PBXShellScriptBuildPhase section */ 218 + FCCE17DF135A70A0002CEE6D /* Run Script */ = { 219 + isa = PBXShellScriptBuildPhase; 220 + buildActionMask = 8; 221 + files = ( 222 + ); 223 + inputPaths = ( 224 + ); 225 + name = "Run Script"; 226 + outputPaths = ( 227 + ); 228 + runOnlyForDeploymentPostprocessing = 1; 229 + shellPath = /bin/sh; 230 + shellScript = ". \"$PROJECT_DIR\"/xcodescripts/install_files.sh"; 231 + }; 232 + /* End PBXShellScriptBuildPhase section */ 233 + 234 + /* Begin PBXSourcesBuildPhase section */ 235 + 726EE9D41E9423E50017A5B9 /* Sources */ = { 236 + isa = PBXSourcesBuildPhase; 237 + buildActionMask = 2147483647; 238 + files = ( 239 + 098AF3B622692BF300F9BA42 /* stat_test.c in Sources */, 240 + 721D4F081EA95290000F0555 /* xattr_flags.c in Sources */, 241 + 721D4F071EA95283000F0555 /* copyfile.c in Sources */, 242 + 726EE9DB1E9423E50017A5B9 /* main.c in Sources */, 243 + 726EE9E61E946D590017A5B9 /* test_utils.c in Sources */, 244 + 726EE9E41E946B320017A5B9 /* systemx.c in Sources */, 245 + 726EE9E01E9425160017A5B9 /* sparse_test.c in Sources */, 246 + ); 247 + runOnlyForDeploymentPostprocessing = 0; 248 + }; 249 + FCCE17B7135A6444002CEE6D /* Sources */ = { 250 + isa = PBXSourcesBuildPhase; 251 + buildActionMask = 2147483647; 252 + files = ( 253 + FCCE17C3135A658F002CEE6D /* copyfile.c in Sources */, 254 + 72406E631676C3C80099568B /* xattr_flags.c in Sources */, 255 + ); 256 + runOnlyForDeploymentPostprocessing = 0; 257 + }; 258 + /* End PBXSourcesBuildPhase section */ 259 + 260 + /* Begin XCBuildConfiguration section */ 261 + 726EE9DC1E9423E50017A5B9 /* Release */ = { 262 + isa = XCBuildConfiguration; 263 + buildSettings = { 264 + ALWAYS_SEARCH_USER_PATHS = NO; 265 + CLANG_ANALYZER_NONNULL = YES; 266 + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 267 + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 268 + CLANG_CXX_LIBRARY = "libc++"; 269 + CLANG_ENABLE_MODULES = YES; 270 + CLANG_ENABLE_OBJC_ARC = YES; 271 + CLANG_WARN_BOOL_CONVERSION = YES; 272 + CLANG_WARN_CONSTANT_CONVERSION = YES; 273 + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 274 + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 275 + CLANG_WARN_EMPTY_BODY = YES; 276 + CLANG_WARN_ENUM_CONVERSION = YES; 277 + CLANG_WARN_INFINITE_RECURSION = YES; 278 + CLANG_WARN_INT_CONVERSION = YES; 279 + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 280 + CLANG_WARN_SUSPICIOUS_MOVE = YES; 281 + CLANG_WARN_UNREACHABLE_CODE = YES; 282 + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 283 + CODE_SIGN_ENTITLEMENTS = copyfile_test/copyfile_test.entitlements; 284 + CODE_SIGN_IDENTITY = "-"; 285 + COPY_PHASE_STRIP = NO; 286 + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 287 + ENABLE_NS_ASSERTIONS = NO; 288 + ENABLE_STRICT_OBJC_MSGSEND = YES; 289 + GCC_C_LANGUAGE_STANDARD = gnu99; 290 + GCC_NO_COMMON_BLOCKS = YES; 291 + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 292 + GCC_WARN_UNDECLARED_SELECTOR = YES; 293 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 294 + LIBRARY_SEARCH_PATHS = ( 295 + "$(inherited)", 296 + "$(SDKROOT)/usr/lib/system", 297 + ); 298 + MTL_ENABLE_DEBUG_INFO = NO; 299 + PRODUCT_NAME = "$(TARGET_NAME)"; 300 + SDKROOT = macosx; 301 + SUPPORTS_TEXT_BASED_API = YES; 302 + TAPI_VERIFY_MODE = Pedantic; 303 + }; 304 + name = Release; 305 + }; 306 + FCCE17B3135A5FFB002CEE6D /* Release */ = { 307 + isa = XCBuildConfiguration; 308 + baseConfigurationReference = 3F1EFD4C185C4EB400D1C970 /* copyfile.xcconfig */; 309 + buildSettings = { 310 + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 311 + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 312 + GCC_WARN_ABOUT_RETURN_TYPE = YES; 313 + GCC_WARN_SHADOW = YES; 314 + GCC_WARN_SIGN_COMPARE = YES; 315 + GCC_WARN_UNUSED_FUNCTION = YES; 316 + GCC_WARN_UNUSED_LABEL = YES; 317 + GCC_WARN_UNUSED_PARAMETER = YES; 318 + GCC_WARN_UNUSED_VARIABLE = YES; 319 + SDKROOT = macosx.internal; 320 + WARNING_CFLAGS = ( 321 + "-Wall", 322 + "-Wextra", 323 + "-Wformat=2", 324 + "-Wformat-security", 325 + "-Wnested-externs", 326 + "-Wno-trigraphs", 327 + "-Wredundant-decls", 328 + "-Wwrite-strings", 329 + ); 330 + }; 331 + name = Release; 332 + }; 333 + FCCE17BD135A6444002CEE6D /* Release */ = { 334 + isa = XCBuildConfiguration; 335 + buildSettings = { 336 + EXECUTABLE_PREFIX = lib; 337 + GCC_NO_COMMON_BLOCKS = YES; 338 + GCC_PREPROCESSOR_DEFINITIONS = "__DARWIN_NOW_CANCELABLE=1"; 339 + LINK_WITH_STANDARD_LIBRARIES = NO; 340 + OTHER_LDFLAGS = ( 341 + "-Wl,-umbrella,System", 342 + "-L/usr/lib/system", 343 + "-ldyld", 344 + "-lcompiler_rt", 345 + "-lsystem_kernel", 346 + "-lsystem_malloc", 347 + "-lsystem_c", 348 + "-lsystem_blocks", 349 + "-lquarantine", 350 + "-lsystem_asl", 351 + "-lsystem_info", 352 + "-lxpc", 353 + "-ldispatch", 354 + ); 355 + "OTHER_LDFLAGS[sdk=iphone*]" = ( 356 + "-Wl,-umbrella,System", 357 + "-L/usr/lib/system", 358 + "-ldyld", 359 + "-lcompiler_rt", 360 + "-lsystem$(SIM_SUFFIX)_kernel", 361 + "-lsystem_malloc", 362 + "-lsystem_c", 363 + "-lsystem_blocks", 364 + "-lsystem_asl", 365 + "-lsystem_info", 366 + "-ldispatch", 367 + "-lxpc", 368 + ); 369 + SDKROOT = macosx.internal; 370 + "SIM_SUFFIX[sdk=iphonesimulator*]" = _sim; 371 + SUPPORTS_TEXT_BASED_API = YES; 372 + TAPI_VERIFY_MODE = Pedantic; 373 + WARNING_CFLAGS = ( 374 + "-Wall", 375 + "-Wextra", 376 + "-Wformat=2", 377 + "-Wformat-security", 378 + "-Wnested-externs", 379 + "-Wno-parentheses", 380 + "-Wno-trigraphs", 381 + "-Wredundant-decls", 382 + "-Wwrite-strings", 383 + ); 384 + }; 385 + name = Release; 386 + }; 387 + /* End XCBuildConfiguration section */ 388 + 389 + /* Begin XCConfigurationList section */ 390 + 726EE9DD1E9423E50017A5B9 /* Build configuration list for PBXNativeTarget "copyfile_test" */ = { 391 + isa = XCConfigurationList; 392 + buildConfigurations = ( 393 + 726EE9DC1E9423E50017A5B9 /* Release */, 394 + ); 395 + defaultConfigurationIsVisible = 0; 396 + defaultConfigurationName = Release; 397 + }; 398 + FCCE17B0135A5FFB002CEE6D /* Build configuration list for PBXProject "copyfile" */ = { 399 + isa = XCConfigurationList; 400 + buildConfigurations = ( 401 + FCCE17B3135A5FFB002CEE6D /* Release */, 402 + ); 403 + defaultConfigurationIsVisible = 0; 404 + defaultConfigurationName = Release; 405 + }; 406 + FCCE17BE135A6444002CEE6D /* Build configuration list for PBXNativeTarget "copyfile" */ = { 407 + isa = XCConfigurationList; 408 + buildConfigurations = ( 409 + FCCE17BD135A6444002CEE6D /* Release */, 410 + ); 411 + defaultConfigurationIsVisible = 0; 412 + defaultConfigurationName = Release; 413 + }; 414 + /* End XCConfigurationList section */ 415 + }; 416 + rootObject = FCCE17AD135A5FFB002CEE6D /* Project object */; 417 + }
src/copyfile/copyfile_test/copyfile_test.entitlements

This is a binary file and will not be displayed.

+68
src/copyfile/copyfile_test/main.c
··· 1 + // 2 + // main.c 3 + // copyfile_test 4 + // 5 + 6 + #include <stdbool.h> 7 + #include <stdio.h> 8 + #include <stdlib.h> 9 + #include <string.h> 10 + #include <sys/mount.h> 11 + #include <sys/stat.h> 12 + #include <removefile.h> 13 + 14 + #include "sparse_test.h" 15 + #include "stat_test.h" 16 + #include "test_utils.h" 17 + 18 + #define DISK_IMAGE_SIZE_MB 512 19 + 20 + #if TARGET_OS_OSX 21 + #define TEST_DIR MOUNT_PATH 22 + #define USING_DISK_IMAGE 1 23 + #else 24 + #define TEST_DIR "/tmp/copyfile_test" 25 + #define USING_DISK_IMAGE 0 26 + #endif // TARGET_OS_OSX 27 + 28 + #define MIN_BLOCKSIZE_B 512 29 + #define DEFAULT_BLOCKSIZE_B 4096 30 + #define MAX_BLOCKSIZE_B 16384 31 + 32 + int main(__unused int argc, __unused const char * argv[]) { 33 + bool failed = false; 34 + struct statfs stb; 35 + 36 + // Create a disk image to run our tests in. 37 + if (USING_DISK_IMAGE) { 38 + disk_image_create(APFS_FSTYPE, DISK_IMAGE_SIZE_MB); 39 + } else { 40 + (void)removefile(TEST_DIR, NULL, REMOVEFILE_RECURSIVE); 41 + assert_no_err(mkdir(TEST_DIR, 0777)); 42 + } 43 + 44 + // Make sure the test directory exists, is apfs formatted, 45 + // and that we have a sane block size. 46 + assert_no_err(statfs(TEST_DIR, &stb)); 47 + assert_no_err(memcmp(stb.f_fstypename, APFS_FSTYPE, sizeof(APFS_FSTYPE))); 48 + if (stb.f_bsize < MIN_BLOCKSIZE_B || stb.f_bsize > MAX_BLOCKSIZE_B) { 49 + stb.f_bsize = DEFAULT_BLOCKSIZE_B; 50 + } 51 + 52 + // Run our tests. 53 + sranddev(); 54 + failed |= do_sparse_test(TEST_DIR, stb.f_bsize); 55 + failed |= do_sparse_recursive_test(TEST_DIR, stb.f_bsize); 56 + failed |= do_fcopyfile_offset_test(TEST_DIR, stb.f_bsize); 57 + failed |= do_preserve_dst_flags_test(TEST_DIR, stb.f_bsize); 58 + failed |= do_preserve_dst_tracked_test(TEST_DIR, stb.f_bsize); 59 + 60 + // Cleanup the disk image we ran our tests on. 61 + if (USING_DISK_IMAGE) { 62 + disk_image_destroy(false); 63 + } else { 64 + (void)removefile(TEST_DIR, NULL, REMOVEFILE_RECURSIVE); 65 + } 66 + 67 + return failed ? EXIT_FAILURE : EXIT_SUCCESS; 68 + }
+397
src/copyfile/copyfile_test/sparse_test.c
··· 1 + // 2 + // sparse_test.c 3 + // copyfile_test 4 + // 5 + 6 + #include <stdbool.h> 7 + #include <stdio.h> 8 + #include <stdlib.h> 9 + #include <string.h> 10 + #include <unistd.h> 11 + #include <removefile.h> 12 + #include <sys/fcntl.h> 13 + #include <sys/stat.h> 14 + 15 + #include "../copyfile.h" 16 + #include "sparse_test.h" 17 + #include "test_utils.h" 18 + #include "systemx.h" 19 + 20 + /* 21 + * Copy the file pointed to by src_fd (and orig_name) to copy_name, 22 + * using copyfile()/fcopyfile() and COPYFILE_DATA. If do_sparse, also pass COPYFILE_DATA_SPARSE. 23 + * Before copying, rewind src_fd to start_off. 24 + */ 25 + static bool test_copy(int src_fd, char* orig_name, char* copy_name, bool do_sparse, off_t start_off) { 26 + struct stat orig_sb, copy_sb; 27 + int copy_fd; 28 + bool result = true; 29 + copyfile_state_t cpf_state; 30 + 31 + // Get ready for the test. 32 + memset(&orig_sb, 0, sizeof(orig_sb)); 33 + memset(&copy_sb, 0, sizeof(copy_sb)); 34 + assert_with_errno((cpf_state = copyfile_state_alloc()) != NULL); 35 + assert_with_errno(lseek(src_fd, start_off, SEEK_SET) == start_off); 36 + 37 + // First, verify copyfile(). 38 + copyfile_flags_t flags = COPYFILE_ALL; 39 + if (do_sparse) { 40 + flags |= COPYFILE_DATA_SPARSE; 41 + } 42 + assert_no_err(copyfile(orig_name, copy_name, cpf_state, flags)); 43 + 44 + // The file was (hopefully) copied. Now, we must verify three things: 45 + // 1. If (do_sparse), verify that the copy is a sparse file. 46 + // For now, let's approximate this by testing that the sizes of the two files are equal. 47 + // 2. The copyfile_state_t for the copy returns that all bytes were copied. 48 + // 3. The copy and the source have identical contents. 49 + 50 + // 1. The copy is a sparse file. 51 + // 2. The copyfile_state_t for the copy returns that all bytes were copied. 52 + assert_no_err(stat(orig_name, &orig_sb)); 53 + assert_no_err(stat(copy_name, &copy_sb)); 54 + result &= verify_copy_sizes(&orig_sb, &copy_sb, cpf_state, do_sparse, 0); 55 + 56 + // 3. The copy and the source have identical contents. 57 + result &= verify_copy_contents(orig_name, copy_name); 58 + 59 + // Post-test cleanup. 60 + assert_no_err(copyfile_state_free(cpf_state)); 61 + assert_no_err(removefile(copy_name, NULL, REMOVEFILE_RECURSIVE)); 62 + memset(&orig_sb, 0, sizeof(struct stat)); 63 + memset(&copy_sb, 0, sizeof(struct stat)); 64 + 65 + // Next, verify fcopyfile(). 66 + // Make an fd for the destination. 67 + assert_with_errno((copy_fd = open(copy_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM)) > 0); 68 + 69 + // Call fcopyfile(). 70 + assert_with_errno((cpf_state = copyfile_state_alloc()) != NULL); 71 + assert_no_err(fcopyfile(src_fd, copy_fd, cpf_state, flags)); 72 + 73 + // 1. The copy is a sparse file (if start_off is 0). 74 + // 2. The copyfile_state_t for the copy returns that all bytes were copied. 75 + assert_no_err(fstat(src_fd, &orig_sb)); 76 + assert_no_err(fstat(copy_fd, &copy_sb)); 77 + result &= verify_copy_sizes(&orig_sb, &copy_sb, cpf_state, 78 + start_off > 0 ? false : do_sparse, start_off); 79 + 80 + // 3. The copy and the source have identical contents. 81 + if (start_off == 0) { 82 + result &= verify_copy_contents(orig_name, copy_name); 83 + } 84 + 85 + // Post-test cleanup. 86 + assert_no_err(copyfile_state_free(cpf_state)); 87 + assert_no_err(removefile(copy_name, NULL, REMOVEFILE_RECURSIVE)); 88 + assert_no_err(close(copy_fd)); 89 + 90 + return result; 91 + } 92 + 93 + 94 + // Sparse file creation functions. 95 + // Each take the source file descriptor pointing at the beginning of the file and the block size. 96 + // Each return the offset we should return the fd to before any copying should be performed. 97 + typedef off_t (*creator_func)(int, off_t); 98 + 99 + static off_t write_start_and_end_holes(int fd, off_t block_size) { 100 + assert_with_errno(pwrite(fd, "j", 1, block_size) == 1); 101 + assert_no_err(ftruncate(fd, 3 * block_size)); 102 + 103 + assert_no_err(create_hole_in_fd(fd, 0, block_size)); 104 + assert_no_err(create_hole_in_fd(fd, 2 * block_size, block_size)); 105 + return 0; 106 + } 107 + 108 + static off_t write_end_hole(int fd, off_t block_size) { 109 + assert_with_errno(pwrite(fd, "n", 1, 0) == 1); 110 + assert_no_err(ftruncate(fd, 16 * block_size)); 111 + 112 + assert_no_err(create_hole_in_fd(fd, block_size, 15 * block_size)); 113 + return 0; 114 + } 115 + 116 + static off_t write_start_hole(int fd, off_t block_size) { 117 + assert_with_errno(pwrite(fd, "p", 1, 16 * block_size) == 1); 118 + 119 + assert_no_err(create_hole_in_fd(fd, 0, 16 * block_size)); 120 + return 0; 121 + } 122 + 123 + static off_t write_middle_hole(int fd, off_t block_size) { 124 + assert_with_errno(pwrite(fd, "k", 1, 0) == 1); 125 + assert_with_errno(pwrite(fd, "k", 1, 4 * block_size) == 1); 126 + assert_no_err(ftruncate(fd, 5 * block_size)); 127 + 128 + assert_no_err(create_hole_in_fd(fd, block_size, 3 * block_size)); 129 + return 0; 130 + } 131 + 132 + static off_t write_start_and_middle_holes(int fd, off_t block_size) { 133 + assert_with_errno(pwrite(fd, "l", 1, 16 * block_size) == 1); 134 + assert_with_errno(pwrite(fd, "l", 1, 32 * block_size) == 1); 135 + 136 + assert_no_err(create_hole_in_fd(fd, 0, 16 * block_size)); 137 + assert_no_err(create_hole_in_fd(fd, 17 * block_size, 15 * block_size)); 138 + return 0; 139 + } 140 + 141 + static off_t write_middle_and_end_holes(int fd, off_t block_size) { 142 + assert_with_errno(pwrite(fd, "m", 1, 0) == 1); 143 + assert_with_errno(pwrite(fd, "m", 1, 16 * block_size) == 1); 144 + assert_no_err(ftruncate(fd, 32 * block_size)); 145 + 146 + assert_no_err(create_hole_in_fd(fd, block_size, 15 * block_size)); 147 + assert_no_err(create_hole_in_fd(fd, 17 * block_size, 15 * block_size)); 148 + return 0; 149 + } 150 + 151 + static off_t write_no_sparse(int fd, __unused off_t block_size) { 152 + assert_with_errno(pwrite(fd, "z", 1, 0) == 1); 153 + return 0; 154 + } 155 + 156 + static off_t write_sparse_odd_offset(int fd, off_t block_size) { 157 + assert_with_errno(pwrite(fd, "q", 1, block_size) == 1); 158 + 159 + assert_no_err(create_hole_in_fd(fd, 0, block_size)); 160 + // Return with the fd pointing at offset 1. 161 + assert_with_errno(lseek(fd, 1, SEEK_SET) == 1); 162 + return 1; 163 + } 164 + 165 + static off_t write_sparse_bs_offset(int fd, off_t block_size) { 166 + assert_with_errno(pwrite(fd, "a", 1, block_size) == 1); 167 + assert_with_errno(pwrite(fd, "b", 1, 2 * block_size) == 1); 168 + 169 + assert_no_err(create_hole_in_fd(fd, 0, block_size)); 170 + // Return with the fd pointing at block_size. 171 + assert_with_errno(lseek(fd, block_size, SEEK_SET) == block_size); 172 + return block_size; 173 + } 174 + 175 + static off_t write_diff_adj_holes(int fd, off_t block_size) { 176 + assert_with_errno(pwrite(fd, "w", 1, 0)); 177 + assert_with_errno(pwrite(fd, "w", 1, 3 * block_size)); 178 + assert_no_err(ftruncate(fd, 4 * block_size)); 179 + 180 + assert_no_err(create_hole_in_fd(fd, block_size, block_size)); 181 + assert_no_err(create_hole_in_fd(fd, 2 * block_size, block_size)); 182 + return 0; 183 + } 184 + 185 + typedef struct { 186 + creator_func func; // pointer to function to create a sparse file 187 + const char * name; // null terminated string 188 + } sparse_test_func; 189 + 190 + #define NUM_TEST_FUNCTIONS 10 191 + sparse_test_func test_functions[NUM_TEST_FUNCTIONS] = { 192 + {write_start_and_end_holes, "start_and_end_holes"}, 193 + {write_middle_hole, "middle_hole"}, 194 + {write_start_and_middle_holes, "start_and_middle_holes"}, 195 + {write_middle_and_end_holes, "middle_and_end_holes"}, 196 + {write_end_hole, "end_hole"}, 197 + {write_start_hole, "start_hole"}, 198 + {write_no_sparse, "no_sparse"}, 199 + {write_sparse_odd_offset, "write_sparse_odd_offset"}, 200 + {write_sparse_bs_offset, "write_sparse_bs_offset"}, 201 + {write_diff_adj_holes, "write_diff_adj_holes"} 202 + }; 203 + 204 + bool do_sparse_test(const char* apfs_test_directory, size_t block_size) { 205 + int fd, test_file_id; 206 + char out_name[BSIZE_B], sparse_copy_name[BSIZE_B], full_copy_name[BSIZE_B]; 207 + bool success = true, sub_test_success; 208 + off_t start_off; 209 + 210 + for (size_t sub_test = 0; sub_test < NUM_TEST_FUNCTIONS; sub_test++) { 211 + printf("START [%s]\n", test_functions[sub_test].name); 212 + sub_test_success = false; 213 + 214 + // Make new names for this file and its copies. 215 + test_file_id = rand() % DEFAULT_NAME_MOD; 216 + create_test_file_name(apfs_test_directory, "sparse", test_file_id, out_name); 217 + create_test_file_name(apfs_test_directory, "copy_sparse", test_file_id, sparse_copy_name); 218 + create_test_file_name(apfs_test_directory, "copy_full", test_file_id, full_copy_name); 219 + 220 + // Create the test file. 221 + fd = open(out_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM); 222 + assert_with_errno(fd >= 0); 223 + 224 + // Write to the test file, making it sparse. 225 + start_off = test_functions[sub_test].func(fd, (off_t) block_size); 226 + assert_no_err(fsync(fd)); 227 + 228 + // Make sure that a sparse copy is successful. 229 + sub_test_success = test_copy(fd, out_name, sparse_copy_name, true, start_off); 230 + if (sub_test_success) { 231 + // Make sure that a full copy is successful. 232 + sub_test_success = test_copy(fd, out_name, full_copy_name, false, start_off); 233 + } 234 + 235 + // Report the result on stdout. 236 + if (!sub_test_success) { 237 + printf("FAIL [%s]\n", test_functions[sub_test].name); 238 + success = false; 239 + } else { 240 + printf("PASS [%s]\n", test_functions[sub_test].name); 241 + } 242 + 243 + // Cleanup for the next test. 244 + assert_no_err(close(fd)); 245 + (void)removefile(out_name, NULL, 0); 246 + (void)removefile(sparse_copy_name, NULL, 0); 247 + (void)removefile(full_copy_name, NULL, 0); 248 + memset(out_name, 0, BSIZE_B); 249 + memset(sparse_copy_name, 0, BSIZE_B); 250 + memset(full_copy_name, 0, BSIZE_B); 251 + } 252 + 253 + return success ? EXIT_SUCCESS : EXIT_FAILURE; 254 + } 255 + 256 + bool do_sparse_recursive_test(const char *apfs_test_directory, size_t block_size) { 257 + int exterior_file_src_fd, interior_file_src_fd, test_file_id; 258 + char exterior_dir_src[BSIZE_B] = {0}, interior_dir_src[BSIZE_B] = {0}, exterior_dir_dst[BSIZE_B] = {0}, interior_dir_dst[BSIZE_B] = {0}; 259 + char exterior_file_src[BSIZE_B] = {0}, interior_file_src[BSIZE_B] = {0}, exterior_file_dst[BSIZE_B] = {0}, interior_file_dst[BSIZE_B] = {0}; 260 + struct stat exterior_file_src_sb, interior_file_src_sb, exterior_file_dst_sb, interior_file_dst_sb; 261 + bool success = true; 262 + 263 + printf("START [sparse_recursive]\n"); 264 + 265 + // Get ready for the test. 266 + memset(&exterior_file_src_sb, 0, sizeof(exterior_file_src_sb)); 267 + memset(&interior_file_src_sb, 0, sizeof(interior_file_src_sb)); 268 + memset(&exterior_file_dst_sb, 0, sizeof(exterior_file_dst_sb)); 269 + memset(&interior_file_dst_sb, 0, sizeof(interior_file_dst_sb)); 270 + 271 + // Construct our source layout. 272 + assert_with_errno(snprintf(exterior_dir_src, BSIZE_B, "%s/recursive_src", apfs_test_directory) > 0); 273 + assert_with_errno(snprintf(interior_dir_src, BSIZE_B, "%s/interior", exterior_dir_src) > 0); 274 + 275 + assert_no_err(mkdir(exterior_dir_src, DEFAULT_MKDIR_PERM)); 276 + assert_no_err(mkdir(interior_dir_src, DEFAULT_MKDIR_PERM)); 277 + 278 + test_file_id = rand() % DEFAULT_NAME_MOD; 279 + create_test_file_name(exterior_dir_src, "exterior_sparse_file", test_file_id, exterior_file_src); 280 + create_test_file_name(interior_dir_src, "interior_sparse_file", test_file_id, interior_file_src); 281 + 282 + // Create the actual test files. 283 + exterior_file_src_fd = open(exterior_file_src, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM); 284 + assert_with_errno(exterior_file_src_fd >= 0); 285 + write_start_and_end_holes(exterior_file_src_fd, block_size); 286 + 287 + interior_file_src_fd = open(interior_file_src, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM); 288 + assert_with_errno(interior_file_src_fd >= 0); 289 + write_middle_hole(interior_file_src_fd, block_size); 290 + 291 + // Now, recursively copy our folder using sparse data copying. 292 + assert_with_errno(snprintf(exterior_dir_dst, BSIZE_B, "%s/recursive_dst", apfs_test_directory) > 0); 293 + assert_no_err(copyfile(exterior_dir_src, exterior_dir_dst, NULL, COPYFILE_ALL|COPYFILE_RECURSIVE|COPYFILE_DATA_SPARSE)); 294 + 295 + // The files were (hopefully) copied. Now, we must verify three things: 296 + // 1. Verify that the copy is a sparse file. 297 + // For now, let's approximate this by testing that the sizes of the two files are equal. 298 + // 2. The copy and the source have identical contents. 299 + 300 + // First, construct our destination layout. 301 + assert_with_errno(snprintf(exterior_dir_dst, BSIZE_B, "%s/recursive_dst", apfs_test_directory) > 0); 302 + create_test_file_name(exterior_dir_dst, "exterior_sparse_file", test_file_id, exterior_file_dst); 303 + assert_with_errno(snprintf(interior_dir_dst, BSIZE_B, "%s/interior", exterior_dir_dst) > 0); 304 + create_test_file_name(interior_dir_dst, "interior_sparse_file", test_file_id, interior_file_dst); 305 + 306 + // 1. The copy is a sparse file. 307 + assert_no_err(fstat(exterior_file_src_fd, &exterior_file_src_sb)); 308 + assert_no_err(stat(exterior_file_dst, &exterior_file_dst_sb)); 309 + 310 + assert_no_err(fstat(interior_file_src_fd, &interior_file_src_sb)); 311 + assert_no_err(stat(interior_file_dst, &interior_file_dst_sb)); 312 + 313 + success &= verify_copy_sizes(&exterior_file_src_sb, &exterior_file_dst_sb, NULL, true, 0); 314 + success &= verify_copy_sizes(&interior_file_src_sb, &interior_file_dst_sb, NULL, true, 0); 315 + 316 + // 2. The copy and the source have identical contents. 317 + success &= verify_copy_contents(exterior_file_src, exterior_file_dst); 318 + success &= verify_copy_contents(interior_file_src, interior_file_dst); 319 + 320 + if (success) { 321 + printf("PASS [sparse_recursive]\n"); 322 + } else { 323 + printf("FAIL [sparse_recursive]\n"); 324 + } 325 + 326 + assert_no_err(close(interior_file_src_fd)); 327 + assert_no_err(close(exterior_file_src_fd)); 328 + (void)removefile(exterior_dir_src, NULL, REMOVEFILE_RECURSIVE); 329 + (void)removefile(exterior_dir_dst, NULL, REMOVEFILE_RECURSIVE); 330 + 331 + return success ? EXIT_SUCCESS : EXIT_FAILURE; 332 + } 333 + 334 + bool do_fcopyfile_offset_test(const char *apfs_test_directory, size_t block_size) { 335 + int src_fd, sparse_copy_fd, full_copy_fd, test_file_id; 336 + char out_name[BSIZE_B], sparse_copy_name[BSIZE_B], full_copy_name[BSIZE_B]; 337 + bool success = true; 338 + 339 + printf("START [fcopyfile_offset]\n"); 340 + 341 + // Make new names for this file and its copies. 342 + test_file_id = rand() % DEFAULT_NAME_MOD; 343 + 344 + create_test_file_name(apfs_test_directory, "foff_sparse", test_file_id, out_name); 345 + create_test_file_name(apfs_test_directory, "foff_copy_sparse", test_file_id, sparse_copy_name); 346 + create_test_file_name(apfs_test_directory, "foff_copy_full", test_file_id, full_copy_name); 347 + 348 + // Create the test file. 349 + src_fd = open(out_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM); 350 + assert_with_errno(src_fd >= 0); 351 + // This writes 5 * block_size bytes. 352 + assert_with_errno(lseek(src_fd, write_middle_hole(src_fd, block_size), SEEK_SET) == 0); 353 + 354 + // Create a sparse copy using fcopyfile(). 355 + sparse_copy_fd = open(sparse_copy_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM); 356 + assert_with_errno(sparse_copy_fd >= 0); 357 + 358 + // Seek the sparse copy to a non-zero offset. 359 + assert_with_errno(lseek(sparse_copy_fd, block_size, SEEK_SET) == (off_t) block_size); 360 + // Write into the sparse copy a different byte. 361 + assert_with_errno(pwrite(sparse_copy_fd, "z", 1, block_size) == 1); 362 + 363 + // Run fcopyfile(). 364 + assert_no_err(fcopyfile(src_fd, sparse_copy_fd, NULL, COPYFILE_ALL|COPYFILE_DATA_SPARSE)); 365 + 366 + // Check that the source matches the copy at the appropriate region. 367 + success &= verify_fd_contents(src_fd, 0, sparse_copy_fd, block_size, 4 * block_size); 368 + 369 + // Now, repeat the same procedure with a full copy. 370 + assert_with_errno(lseek(src_fd, 0, SEEK_SET) == 0); 371 + full_copy_fd = open(full_copy_name, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM); 372 + assert_with_errno(full_copy_name >= 0); 373 + 374 + assert_with_errno(lseek(full_copy_fd, block_size, SEEK_SET) == (off_t) block_size); 375 + assert_with_errno(pwrite(full_copy_fd, "r", 1, block_size) == 1); 376 + 377 + // Run fcopyfile(). 378 + assert_no_err(fcopyfile(src_fd, full_copy_fd, NULL, COPYFILE_ALL)); 379 + 380 + // Check that the source matches the copy at the appropriate region. 381 + success &= verify_fd_contents(src_fd, 0, full_copy_fd, block_size, 4 * block_size); 382 + 383 + if (success) { 384 + printf("PASS [fcopyfile_offset]\n"); 385 + } else { 386 + printf("FAIL [fcopyfile_offset]\n"); 387 + } 388 + 389 + assert_no_err(close(full_copy_fd)); 390 + assert_no_err(close(sparse_copy_fd)); 391 + assert_no_err(close(src_fd)); 392 + (void)removefile(full_copy_name, NULL, 0); 393 + (void)removefile(sparse_copy_name, NULL, 0); 394 + (void)removefile(out_name, NULL, 0); 395 + 396 + return success ? EXIT_SUCCESS : EXIT_FAILURE; 397 + }
+16
src/copyfile/copyfile_test/sparse_test.h
··· 1 + // 2 + // sparse_test.h 3 + // copyfile_test 4 + // 5 + 6 + #ifndef sparse_test_h 7 + #define sparse_test_h 8 + 9 + #include <stdbool.h> 10 + #include <stdlib.h> 11 + 12 + bool do_sparse_test(const char *apfs_test_directory, size_t block_size); 13 + bool do_sparse_recursive_test(const char *apfs_test_directory, size_t block_size); 14 + bool do_fcopyfile_offset_test(const char *apfs_test_directory, size_t block_size); 15 + 16 + #endif /* sparse_test_h */
+189
src/copyfile/copyfile_test/stat_test.c
··· 1 + // 2 + // stat_test.c 3 + // copyfile_test 4 + // 5 + 6 + #include <stdbool.h> 7 + #include <stdio.h> 8 + #include <stdlib.h> 9 + #include <string.h> 10 + #include <unistd.h> 11 + #include <removefile.h> 12 + #include <sandbox/rootless.h> 13 + #include <sys/fcntl.h> 14 + #include <sys/stat.h> 15 + #include <sys/types.h> 16 + 17 + #include "../copyfile.h" 18 + #include "stat_test.h" 19 + #include "test_utils.h" 20 + 21 + #define STORAGE_CLASS "copyfile_test" 22 + #define SPECIAL_DIR_NAME "special_dir/" 23 + #define REGULAR_DIR_NAME "regular_dir" 24 + #define TEST_FILE_NAME "almighty_tallest" 25 + 26 + typedef int (*special_mkdir_func)(const char *, mode_t, const char *); 27 + 28 + static bool test_special_dir_with_flag(const char *source_directory, const char *test_directory, 29 + special_mkdir_func create_func, uint32_t flag_to_test) { 30 + char special_dir[BSIZE_B] = {0}, regular_dir[BSIZE_B] = {0}, test_file[BSIZE_B] = {0}; 31 + struct stat sb = {0}; 32 + bool success = true; 33 + 34 + // The plan here is as follows: 35 + // 36 + // (1) Create a special directory using 'create_func', and verify that its bsdflags 37 + // have `flag_to_test` set. 38 + // 39 + // (2) Copy the contents of `source_directory` into that special directory, 40 + // and verify that the directory and, optionally, a well-known file inside it 41 + // have `flag_to_test` set. 42 + // 43 + // (3) Copy the contents of the special directory into a directory that is not a child 44 + // of our special directory (a 'regular' directory), and verify that the directory and 45 + // a well-known file inside it do *NOT* have `flag_to_test` set. 46 + 47 + // Create path names. 48 + assert_with_errno(snprintf(special_dir, BSIZE_B, "%s/" SPECIAL_DIR_NAME, test_directory) > 0); 49 + assert_with_errno(snprintf(regular_dir, BSIZE_B, "%s/" REGULAR_DIR_NAME, test_directory) > 0); 50 + assert_with_errno(snprintf(test_file, BSIZE_B, "%s/" TEST_FILE_NAME, special_dir) > 0); 51 + 52 + // Create our regular directory. 53 + assert_no_err(mkdir(regular_dir, DEFAULT_MKDIR_PERM)); 54 + 55 + // Create our special directory. 56 + assert_no_err(create_func(special_dir, DEFAULT_MKDIR_PERM, STORAGE_CLASS)); 57 + 58 + // (1) Make sure the special directory has the specified bit set. 59 + assert_no_err(stat(special_dir, &sb)); 60 + assert(sb.st_flags & flag_to_test); 61 + 62 + // Now, copy the source directory's into the special directory. 63 + assert_no_err(copyfile(source_directory, special_dir, NULL, COPYFILE_ALL|COPYFILE_RECURSIVE)); 64 + 65 + // (2) Make sure that the resulting folder (and optionally, its well-known subfile) 66 + // have the specified bit set. 67 + assert_no_err(stat(special_dir, &sb)); 68 + success &= verify_st_flags(&sb, flag_to_test); 69 + 70 + if (flag_to_test != SF_NOUNLINK) { 71 + assert_no_err(stat(test_file, &sb)); 72 + success &= verify_st_flags(&sb, flag_to_test); 73 + } 74 + 75 + // Finally, copy the contents of the special directory into our regular directory. 76 + // Since at least one of the files in this directory will have a rootless xattr, 77 + // which cannot be copied here, we do not attempt to copy extended attributes here. 78 + assert_no_err(copyfile(special_dir, regular_dir, NULL, 79 + COPYFILE_DATA|COPYFILE_SECURITY|COPYFILE_RECURSIVE)); 80 + 81 + // (3) Make sure that the regular directory (and optionally, its well-known subfile) 82 + // do *NOT* have the specified bit set. 83 + assert_no_err(stat(regular_dir, &sb)); 84 + success &= ((sb.st_flags & flag_to_test) == 0); 85 + 86 + if (flag_to_test != SF_NOUNLINK) { 87 + // Rebuild the path to the subfile, as our original path is relative 88 + // to the special directory. 89 + memset(test_file, 0, BSIZE_B); 90 + assert_with_errno(snprintf(test_file, BSIZE_B, "%s/" TEST_FILE_NAME, regular_dir) > 0); 91 + 92 + assert_no_err(stat(test_file, &sb)); 93 + success &= verify_st_flags(&sb, 0); 94 + } 95 + 96 + // Clean up after the test. 97 + assert_no_err(removefile(special_dir, NULL, REMOVEFILE_RECURSIVE)); 98 + assert_no_err(removefile(regular_dir, NULL, REMOVEFILE_RECURSIVE)); 99 + 100 + return success; 101 + } 102 + 103 + bool do_preserve_dst_flags_test(const char *test_directory, __unused size_t block_size) { 104 + int interior_file_src_fd, test_file_id; 105 + char exterior_dir_src[BSIZE_B] = {0}, interior_file_src[BSIZE_B] = {0}; 106 + uid_t euid = geteuid(); 107 + bool success = true; 108 + 109 + printf("START [preserve_dst_flags]\n"); 110 + 111 + // Construct our source layout. 112 + assert_with_errno(snprintf(exterior_dir_src, BSIZE_B, "%s/" TEST_FILE_NAME, test_directory) > 0); 113 + 114 + assert_no_err(mkdir(exterior_dir_src, DEFAULT_MKDIR_PERM)); 115 + 116 + test_file_id = rand() % DEFAULT_NAME_MOD; 117 + create_test_file_name(exterior_dir_src, TEST_FILE_NAME, test_file_id, interior_file_src); 118 + 119 + // Create our interior test file. 120 + interior_file_src_fd = open(interior_file_src, DEFAULT_OPEN_FLAGS, DEFAULT_OPEN_PERM); 121 + assert_with_errno(interior_file_src_fd >= 0); 122 + assert_no_err(close(interior_file_src_fd)); // file only needs to exist 123 + 124 + if (euid == 0) { 125 + // Try to copy our directory into a restricted environment. 126 + success &= test_special_dir_with_flag(exterior_dir_src, test_directory, 127 + rootless_mkdir_restricted, SF_RESTRICTED); 128 + 129 + // Make sure SF_NOUNLINK works as well. 130 + success &= test_special_dir_with_flag(exterior_dir_src, test_directory, 131 + rootless_mkdir_nounlink, SF_NOUNLINK); 132 + } else { 133 + printf("Skipping SF_RESTRICTED and SF_NOUNLINK tests, because we are not root.\n"); 134 + } 135 + 136 + // Try to copy our directory into a datavault. 137 + success &= test_special_dir_with_flag(exterior_dir_src, test_directory, 138 + rootless_mkdir_datavault, UF_DATAVAULT); 139 + 140 + if (success) { 141 + printf("PASS [preserve_dst_flags]\n"); 142 + } else { 143 + printf("FAIL [preserve_dst_flags]\n"); 144 + } 145 + 146 + (void)removefile(exterior_dir_src, NULL, REMOVEFILE_RECURSIVE); 147 + 148 + return success ? EXIT_SUCCESS : EXIT_FAILURE; 149 + } 150 + 151 + bool do_preserve_dst_tracked_test(const char *test_directory, __unused size_t block_size) { 152 + char file_src[BSIZE_B] = {0}, file_dst[BSIZE_B] = {0}; 153 + off_t src_fsize = 0x1000; 154 + int test_file_id; 155 + struct stat dst_stb; 156 + bool success = true; 157 + 158 + printf("START [preserve_dst_tracked]\n"); 159 + 160 + // Create source file 161 + assert_with_errno(snprintf(file_src, BSIZE_B, "%s/" TEST_FILE_NAME, test_directory) > 0); 162 + assert_no_err(close(open(file_src, O_CREAT|O_EXCL, 0644))); 163 + assert_no_err(truncate(file_src, src_fsize)); 164 + 165 + // Create destination file 166 + test_file_id = rand() % DEFAULT_NAME_MOD; 167 + assert_with_errno(snprintf(file_dst, BSIZE_B, "%s/%s.%d", test_directory, TEST_FILE_NAME, test_file_id) > 0); 168 + assert_no_err(close(open(file_dst, O_CREAT|O_EXCL, 0644))); 169 + 170 + // Track destination file 171 + assert_no_err(chflags(file_dst, UF_TRACKED)); 172 + 173 + // Try to copy src onto destination 174 + assert_no_err(copyfile(file_src, file_dst, NULL, COPYFILE_DATA|COPYFILE_STAT|COPYFILE_PRESERVE_DST_TRACKED)); 175 + 176 + assert_no_err(stat(file_dst, &dst_stb)); 177 + success &= (dst_stb.st_size == src_fsize); 178 + success &= (dst_stb.st_flags & UF_TRACKED); 179 + if (success) { 180 + printf("PASS [preserve_dst_tracked]\n"); 181 + } else { 182 + printf("FAIL [preserve_dst_tracked]\n"); 183 + } 184 + 185 + (void)unlink(file_src); 186 + (void)unlink(file_dst); 187 + 188 + return success ? EXIT_SUCCESS : EXIT_FAILURE; 189 + }
+15
src/copyfile/copyfile_test/stat_test.h
··· 1 + // 2 + // stat_test.h 3 + // copyfile_test 4 + // 5 + 6 + #ifndef stat_test_h 7 + #define stat_test_h 8 + 9 + #include <stdbool.h> 10 + #include <stdlib.h> 11 + 12 + bool do_preserve_dst_flags_test(const char *test_directory, size_t block_size); 13 + bool do_preserve_dst_tracked_test(const char *test_directory, size_t block_size); 14 + 15 + #endif /* stat_test_h */
+68
src/copyfile/copyfile_test/systemx.c
··· 1 + // 2 + // systemx.c 3 + // copyfile_test 4 + // Stolen from the test routines from the apfs project. 5 + // 6 + #include <fcntl.h> 7 + #include <libgen.h> 8 + #include <spawn.h> 9 + #include <stdarg.h> 10 + #include <stdbool.h> 11 + #include <unistd.h> 12 + #include <sys/stat.h> 13 + 14 + #include "systemx.h" 15 + #include "test_utils.h" 16 + 17 + 18 + int __attribute__((sentinel)) systemx(const char *prog, ...) 19 + { 20 + const char *args[64]; 21 + 22 + va_list ap; 23 + 24 + const char **parg = args; 25 + 26 + *parg++ = basename((char *)prog); 27 + 28 + va_start(ap, prog); 29 + 30 + bool quiet = false, quiet_stderr = false; 31 + 32 + while ((*parg = va_arg(ap, char *))) { 33 + if (*parg == SYSTEMX_QUIET) { 34 + quiet = true; 35 + } else if (*parg == SYSTEMX_QUIET_STDERR) 36 + quiet_stderr = true; 37 + else 38 + ++parg; 39 + } 40 + 41 + va_end(ap); 42 + 43 + posix_spawn_file_actions_t facts, *pfacts = NULL; 44 + 45 + if (quiet || quiet_stderr) { 46 + posix_spawn_file_actions_init(&facts); 47 + if (quiet) 48 + posix_spawn_file_actions_addopen(&facts, STDOUT_FILENO, "/dev/null", O_APPEND, 0); 49 + if (quiet_stderr) 50 + posix_spawn_file_actions_addopen(&facts, STDERR_FILENO, "/dev/null", O_APPEND, 0); 51 + pfacts = &facts; 52 + } 53 + 54 + pid_t pid; 55 + assert_no_err(errno = posix_spawn(&pid, prog, pfacts, NULL, 56 + (char * const *)args, NULL)); 57 + 58 + if (pfacts) 59 + posix_spawn_file_actions_destroy(pfacts); 60 + 61 + int status; 62 + assert(ignore_eintr(waitpid(pid, &status, 0), -1) == pid); 63 + 64 + if (WIFEXITED(status)) 65 + return WEXITSTATUS(status); 66 + else 67 + return -1; 68 + }
+15
src/copyfile/copyfile_test/systemx.h
··· 1 + // 2 + // systemx.h 3 + // copyfile_test 4 + // Stolen from the test routines from the apfs project. 5 + // 6 + 7 + #ifndef systemx_h_ 8 + #define systemx_h_ 9 + 10 + #define SYSTEMX_QUIET ((void *)1) 11 + #define SYSTEMX_QUIET_STDERR ((void *)2) 12 + 13 + int __attribute__((sentinel)) systemx(const char *prog, ...); 14 + 15 + #endif /* systemx_h_ */
+160
src/copyfile/copyfile_test/test_utils.c
··· 1 + // 2 + // test_utils.c 3 + // copyfile_test 4 + // 5 + 6 + #include <errno.h> 7 + #include <stdbool.h> 8 + #include <stdlib.h> 9 + #include <stdio.h> 10 + #include <removefile.h> 11 + #include <unistd.h> 12 + #include <sys/fcntl.h> 13 + #include <sys/stat.h> 14 + 15 + #include "test_utils.h" 16 + #include "systemx.h" 17 + 18 + bool verify_st_flags(struct stat *sb, uint32_t flags_to_expect) { 19 + // Verify that sb's flags include flags_to_expect. 20 + if (((sb->st_flags & flags_to_expect)) != flags_to_expect) { 21 + printf("st_flags (%u) do not include expected flags (%u)\n", 22 + sb->st_flags, flags_to_expect); 23 + return false; 24 + } 25 + 26 + return true; 27 + } 28 + 29 + bool verify_fd_contents(int orig_fd, off_t orig_pos, int copy_fd, off_t copy_pos, size_t length) { 30 + // Read *length* contents of the two fds and make sure they compare as equal. 31 + // Don't alter the position of either fd. 32 + char orig_contents[length], copy_contents[length]; 33 + bool equal; 34 + 35 + assert(orig_fd > 0 && copy_fd > 0); 36 + memset(orig_contents, 0, length); 37 + memset(copy_contents, 0, length); 38 + 39 + // Read the contents into our temporary buffers, and call memcmp(). 40 + errno = 0; 41 + ssize_t pread_res = pread(orig_fd, orig_contents, length, 0); 42 + assert_with_errno(pread_res == (off_t) length); 43 + assert_with_errno(pread(copy_fd, copy_contents, length, copy_pos) >= 0); 44 + equal = (memcmp(orig_contents, copy_contents, length) == 0); 45 + if (!equal) { 46 + printf("original fd (%lld - %lld) did not match copy (%lld - %lld)\n", 47 + orig_pos, orig_pos + length, copy_pos, copy_pos + length); 48 + 49 + // Find the first non-matching byte and print it out. 50 + for (size_t bad_off = 0; bad_off < length; bad_off++) { 51 + if (orig_contents[bad_off] != copy_contents[bad_off]) { 52 + printf("first mismatch is at offset %zu, original 0x%llx COPY 0x%llx\n", 53 + bad_off, (unsigned long long)orig_contents[bad_off], 54 + (unsigned long long)copy_contents[bad_off]); 55 + break; 56 + } 57 + } 58 + } 59 + 60 + return equal; 61 + } 62 + 63 + bool verify_copy_contents(const char *orig_name, const char *copy_name) { 64 + // Verify that the copy and the source have identical contents. 65 + // Here, we just call out to 'diff' to do the work for us. 66 + int rc = systemx(DIFF_PATH, orig_name, copy_name, SYSTEMX_QUIET, SYSTEMX_QUIET_STDERR, NULL); 67 + if (rc != 0) { 68 + printf("%s and %s are not identical: diff returned %d\n", orig_name, copy_name, rc); 69 + exit(1); 70 + } 71 + 72 + return !rc; 73 + } 74 + 75 + bool verify_copy_sizes(struct stat *orig_sb, struct stat *copy_sb, copyfile_state_t cpf_state, 76 + bool do_sparse, off_t src_start) { 77 + off_t cpf_bytes_copied, blocks_offset; 78 + bool result = true; 79 + 80 + // If requested, verify that the copy is a sparse file. 81 + if (do_sparse) { 82 + if (orig_sb->st_size - src_start != copy_sb->st_size) { 83 + printf("original size - offset (%lld) != copy size (%lld)\n", 84 + orig_sb->st_size - src_start, copy_sb->st_size); 85 + result = false; 86 + } 87 + 88 + blocks_offset = src_start / S_BLKSIZE; 89 + if (orig_sb->st_blocks - blocks_offset < copy_sb->st_blocks) { 90 + printf("original blocks - offset (%lld) < copy blocks (%lld)\n", 91 + orig_sb->st_blocks - blocks_offset, copy_sb->st_blocks); 92 + result = false; 93 + } 94 + } 95 + 96 + // Verify that the copyfile_state_t for the copy returns that all bytes were copied. 97 + if (cpf_state) { 98 + assert_no_err(copyfile_state_get(cpf_state, COPYFILE_STATE_COPIED, &cpf_bytes_copied)); 99 + if (orig_sb->st_size - src_start != cpf_bytes_copied) { 100 + printf("original size - start (%lld) != copied bytes (%lld)\n", 101 + orig_sb->st_size - src_start, cpf_bytes_copied); 102 + result = false; 103 + } 104 + } 105 + 106 + return result; 107 + } 108 + 109 + int create_hole_in_fd(int fd, off_t offset, off_t length) { 110 + struct fpunchhole punchhole_args = { 111 + .fp_flags = 0, 112 + .reserved = 0, 113 + .fp_offset = offset, 114 + .fp_length = length 115 + }; 116 + 117 + return fcntl(fd, F_PUNCHHOLE, &punchhole_args); 118 + } 119 + 120 + 121 + void create_test_file_name(const char *dir, const char *postfix, int id, char *string_out) { 122 + // Make a name for this new file and put it in out_name, which should be BSIZE_B bytes. 123 + assert_with_errno(snprintf(string_out, BSIZE_B, "%s/testfile-%d.%s", dir, id, postfix) > 0); 124 + } 125 + 126 + void disk_image_create(const char *fstype, size_t size_in_mb) { 127 + char size[BSIZE_B]; 128 + 129 + // Set up good default values. 130 + if (!fstype) { 131 + fstype = DEFAULT_FSTYPE; 132 + } 133 + if (size_in_mb > MAX_DISK_IMAGE_SIZE_MB) { 134 + size_in_mb = MAX_DISK_IMAGE_SIZE_MB; 135 + } 136 + assert_with_errno(snprintf(size, BSIZE_B, "%zum", size_in_mb) >= 3); 137 + 138 + // Unmount and remove the sparseimage if it already exists. 139 + disk_image_destroy(true); 140 + if (removefile(DISK_IMAGE_PATH, NULL, REMOVEFILE_RECURSIVE) < 0) { 141 + assert_with_errno(errno == ENOENT); 142 + } 143 + 144 + // Make the disk image. 145 + assert_no_err(systemx(HDIUTIL_PATH, SYSTEMX_QUIET, "create", "-fs", fstype, 146 + "-size", size, "-type", "SPARSE", "-volname", "apfs_sparse", 147 + DISK_IMAGE_PATH, NULL)); 148 + 149 + // Attach the disk image. 150 + assert_no_err(systemx(HDIUTIL_PATH, SYSTEMX_QUIET, "attach", DISK_IMAGE_PATH, NULL)); 151 + } 152 + 153 + void disk_image_destroy(bool allow_failure) { 154 + // If the caller allows, ignore any failures (also silence stderr). 155 + if (allow_failure) { 156 + systemx(HDIUTIL_PATH, "eject", MOUNT_PATH, SYSTEMX_QUIET, SYSTEMX_QUIET_STDERR, NULL); 157 + } else { 158 + assert_no_err(systemx(HDIUTIL_PATH, "eject", MOUNT_PATH, SYSTEMX_QUIET, NULL)); 159 + } 160 + }
+125
src/copyfile/copyfile_test/test_utils.h
··· 1 + // 2 + // test_utils.h 3 + // copyfile_test 4 + // Based on the test routines from the apfs project. 5 + // 6 + 7 + #ifndef test_utils_h 8 + #define test_utils_h 9 + 10 + #include <stdarg.h> 11 + #include <stdio.h> 12 + #include <stdlib.h> 13 + #include <string.h> 14 + #include <sys/errno.h> 15 + 16 + #include "../copyfile.h" 17 + 18 + #define BSIZE_B 128 19 + #define MAX_DISK_IMAGE_SIZE_MB 1024 20 + 21 + #define DEFAULT_NAME_MOD 999 22 + #define DEFAULT_OPEN_FLAGS O_CREAT|O_TRUNC|O_RDWR 23 + #define DEFAULT_OPEN_PERM 0666 24 + #define DEFAULT_MKDIR_PERM 0777 25 + 26 + #define DISK_IMAGE_PATH "/tmp/copyfile_sparse.sparseimage" 27 + #define VOLUME_NAME "apfs_sparse" 28 + #define DEFAULT_FSTYPE "JHFS+" 29 + #define APFS_FSTYPE "apfs" 30 + 31 + // We assume that we're mounted on /Volumes. 32 + #define MOUNT_PATH "/Volumes/" VOLUME_NAME 33 + 34 + #define HDIUTIL_PATH "/usr/bin/hdiutil" 35 + #define DIFF_PATH "/usr/bin/diff" 36 + 37 + // Test routine helpers. 38 + bool verify_st_flags(struct stat *sb, uint32_t flags_to_expect); 39 + bool verify_fd_contents(int orig_fd, off_t orig_pos, int copy_fd, off_t copy_pos, size_t length); 40 + bool verify_copy_contents(const char *orig_name, const char *copy_name); 41 + bool verify_copy_sizes(struct stat *orig_sb, struct stat *copy_sb, copyfile_state_t cpf_state, 42 + bool do_sparse, off_t src_start); 43 + int create_hole_in_fd(int fd, off_t offset, off_t length); 44 + void create_test_file_name(const char *dir, const char *postfix, int id, char *string_out); 45 + 46 + // Our disk image test functions. 47 + void disk_image_create(const char *fstype, size_t size_in_mb); 48 + void disk_image_destroy(bool allow_failure); 49 + 50 + // Assertion functions/macros for tests. 51 + static inline void 52 + __attribute__((format (printf, 3, 4))) 53 + __attribute__((noreturn)) 54 + assert_fail_(const char *file, int line, const char *assertion, ...) 55 + { 56 + va_list args; 57 + va_start(args, assertion); 58 + char *msg; 59 + vasprintf(&msg, assertion, args); 60 + va_end(args); 61 + printf("\n%s:%u: error: %s\n", file, line, msg); 62 + exit(1); 63 + } 64 + 65 + #define assert_fail(str, ...) \ 66 + assert_fail_(__FILE__, __LINE__, str, ## __VA_ARGS__) 67 + 68 + #undef assert 69 + #define assert(condition) \ 70 + do { \ 71 + if (!(condition)) \ 72 + assert_fail_(__FILE__, __LINE__, \ 73 + "assertion failed: %s", #condition); \ 74 + } while (0) 75 + 76 + #define assert_with_errno_(condition, condition_str) \ 77 + do { \ 78 + if (!(condition)) \ 79 + assert_fail_(__FILE__, __LINE__, "%s failed: %s", \ 80 + condition_str, strerror(errno)); \ 81 + } while (0) 82 + 83 + #define assert_with_errno(condition) \ 84 + assert_with_errno_((condition), #condition) 85 + 86 + #define assert_no_err(condition) \ 87 + assert_with_errno_(!(condition), #condition) 88 + 89 + #define assert_equal(lhs, rhs, fmt) \ 90 + do { \ 91 + typeof (lhs) lhs_ = (lhs); \ 92 + typeof (lhs) rhs_ = (rhs); \ 93 + if (lhs_ != rhs_) \ 94 + assert_fail(#lhs " (" fmt ") != " #rhs " (" fmt ")", \ 95 + lhs_, rhs_); \ 96 + } while (0) 97 + 98 + #define assert_equal_(lhs, rhs, lhs_str, rhs_str, fmt) \ 99 + do { \ 100 + typeof (lhs) lhs_ = (lhs); \ 101 + typeof (lhs) rhs_ = (rhs); \ 102 + if (lhs_ != rhs_) \ 103 + assert_fail(lhs_str " (" fmt ") != " rhs_str " (" fmt ")", \ 104 + lhs_, rhs_); \ 105 + } while (0) 106 + 107 + #define assert_equal_int(lhs, rhs) assert_equal_(lhs, rhs, #lhs, #rhs, "%d") 108 + #define assert_equal_ll(lhs, rhs) assert_equal_(lhs, rhs, #lhs, #rhs, "%lld") 109 + #define assert_equal_str(lhs, rhs) \ 110 + do { \ 111 + const char *lhs_ = (lhs), *rhs_ = (rhs); \ 112 + if (strcmp(lhs_, rhs_)) \ 113 + assert_fail("\"%s\" != \"%s\"", lhs_, rhs_); \ 114 + } while (0) 115 + 116 + #define ignore_eintr(x, error_val) \ 117 + ({ \ 118 + typeof(x) eintr_ret_; \ 119 + do { \ 120 + eintr_ret_ = (x); \ 121 + } while (eintr_ret_ == (error_val) && errno == EINTR); \ 122 + eintr_ret_; \ 123 + }) 124 + 125 + #endif /* test_utils_h */
+11 -1
src/copyfile/include/copyfile.h src/copyfile/copyfile.h
··· 1 1 /* 2 - * Copyright (c) 2004-2010 Apple, Inc. All rights reserved. 2 + * Copyright (c) 2004-2019 Apple, Inc. All rights reserved. 3 3 * 4 4 * @APPLE_LICENSE_HEADER_START@ 5 5 * ··· 73 73 #define COPYFILE_STATE_STATUS_CTX 7 74 74 #define COPYFILE_STATE_COPIED 8 75 75 #define COPYFILE_STATE_XATTRNAME 9 76 + #define COPYFILE_STATE_WAS_CLONED 10 76 77 77 78 78 79 #define COPYFILE_DISABLE_VAR "COPYFILE_DISABLE" ··· 99 100 100 101 #define COPYFILE_PACK (1<<22) 101 102 #define COPYFILE_UNPACK (1<<23) 103 + 104 + #define COPYFILE_CLONE (1<<24) 105 + #define COPYFILE_CLONE_FORCE (1<<25) 106 + 107 + #define COPYFILE_RUN_IN_PLACE (1<<26) 108 + 109 + #define COPYFILE_DATA_SPARSE (1<<27) 110 + 111 + #define COPYFILE_PRESERVE_DST_TRACKED (1<<28) 102 112 103 113 #define COPYFILE_VERBOSE (1<<30) 104 114
+7 -2
src/copyfile/include/copyfile_private.h src/copyfile/copyfile_private.h
··· 1 1 /* 2 - * Copyright (c) 2013 Apple, Inc. All rights reserved. 2 + * Copyright (c) 2013-19 Apple, Inc. All rights reserved. 3 3 * 4 4 * @APPLE_LICENSE_HEADER_START@ 5 5 * ··· 33 33 /* 34 34 * File flags that are not preserved when copying stat information. 35 35 */ 36 - #define COPYFILE_OMIT_FLAGS (UF_TRACKED | SF_RESTRICTED) 36 + #define COPYFILE_OMIT_FLAGS (UF_TRACKED | SF_RESTRICTED | SF_NOUNLINK | UF_DATAVAULT) 37 + 38 + /* 39 + * File flags that are not removed when replacing an existing file. 40 + */ 41 + #define COPYFILE_PRESERVE_FLAGS (SF_RESTRICTED | SF_NOUNLINK | UF_DATAVAULT) 37 42 38 43 #endif /* _COPYFILE_PRIVATE_H */
+1 -1
src/copyfile/include/xattr_flags.h src/copyfile/xattr_flags.h
··· 1 1 /* 2 - * Copyright (c) 2013 Apple, Inc. All rights reserved. 2 + * Copyright (c) 2013-19 Apple, Inc. All rights reserved. 3 3 * 4 4 * @APPLE_LICENSE_HEADER_START@ 5 5 *
+1 -35
src/copyfile/include/xattr_properties.h src/copyfile/xattr_properties.h
··· 1 1 /* 2 - * Copyright (c) 2013 Apple, Inc. All rights reserved. 2 + * Copyright (c) 2013-19 Apple, Inc. All rights reserved. 3 3 * 4 4 * @APPLE_LICENSE_HEADER_START@ 5 5 * ··· 88 88 * intention type. 89 89 */ 90 90 #define kCopyOperationPropertyNeverPreserve ((CopyOperationProperties_t)0x0004) 91 - 92 - #if 0 93 - /* 94 - * These are all going to be removed, and I don't believe anyone used them. 95 - */ 96 - /* 97 - * Given an extended attribute name, and a set of properties, return an 98 - * allocated C string with the name. This will return NULL on error; 99 - * errno may be set to ENOMEM if the new name cannot be allocated, or 100 - * ENAMETOOLONG if the new name is longer than the maximum for EAs (127 UTF8 101 - * characters). The caller must deallocate the return value otherwise. 102 - * 103 - * If no properties are set, it returns a copy of the EA name. 104 - * 105 - * If the EA name is in the internal list, and the properties are the same as 106 - * defined there, then it will also return an unmodified copy of the EA name. 107 - */ 108 - extern char *_xattrNameWithProperties(const char *, CopyOperationProperties_t) DEPRECATED_IN_MAC_OS_X_VERSION_10_10_AND_LATER; 109 - 110 - /* 111 - * Given an extended attribute name, which may or may not have properties encoded 112 - * as a suffix, return just the name of the attribute. E.g., com.example.mine#P 113 - * would return "com.example.mine". The return value will be NULL on error; 114 - * errno will be set to ENOMEM if it cannot be allocated. The caller must deallocate 115 - * the return value. 116 - */ 117 - extern char *_xattrNameWithoutProperties(const char *) DEPRECATED_IN_MAC_OS_X_VERSION_10_10_AND_LATER; 118 - 119 - /* 120 - * Given an EA name, return the properties. If the name is in the internal list, 121 - * those properties will be returned. Unknown property encodings are ignored. 122 - */ 123 - extern CopyOperationProperties_t _xattrPropertiesFromName(const char *) DEPRECATED_IN_MAC_OS_X_VERSION_10_10_AND_LATER; 124 - #endif /* 0 */ 125 91 126 92 __END_DECLS 127 93
+2
src/copyfile/xattr_flags.c
··· 58 58 { "com.apple.security.", "S", propFlagsPrefix }, 59 59 { XATTR_RESOURCEFORK_NAME, "PCS", 0 }, // Don't keep for safe save 60 60 { XATTR_FINDERINFO_NAME, "PCS", 0 }, // Same as ResourceFork 61 + { "com.apple.root.installed", "PC", 0}, // Don't share or sync. Copyable by entitled callers 61 62 { 0, 0, 0 }, 62 63 }; 63 64 ··· 69 70 { "com.apple.security.", "N", propFlagsPrefix }, 70 71 { XATTR_RESOURCEFORK_NAME, "PCS", 0 }, // Don't keep for safe save 71 72 { XATTR_FINDERINFO_NAME, "PCS", 0 }, // Same as ResourceFork 73 + { "com.apple.root.installed", "PC", 0}, // Don't share or sync. Copyable by entitled callers 72 74 { 0, 0, 0 }, 73 75 }; 74 76
+7 -8
src/copyfile/xattr_name_with_flags.3
··· 1 1 .\" 2 2 .\" Copyright (c) 2013 Apple Computer, Inc. All rights reserved. 3 3 .\" 4 - .Dd October 7, 2013 4 + .Dd October 9, 2018 5 5 .Dt XATTR_NAME_WITH_FLAGS 3 6 6 .Os 7 7 .Sh NAME ··· 10 10 .Sh LIBRARY 11 11 .Lb libc 12 12 .Sh SYNOPSIS 13 - .In xattr_properties.h 13 + .In xattr_flags.h 14 14 .Ft int 15 15 .Fn xattr_preserve_for_intent "const char *" "xattr_operation_intent_t" 16 16 .Ft char * ··· 56 56 intent, or 1 if it should. 57 57 .Pp 58 58 The function 59 - .Fn xattr_presere_for_intent 59 + .Fn xattr_preserve_for_intent 60 60 combines the functions above, and will return zero if the 61 61 named extended attribute should be preserved during a copy for 62 62 the given intent. ··· 73 73 .It Dv XATTR_OPERATION_INTENT_SAVE 74 74 Indicates that intent is to perform a save (perhaps as in a "safe save"). 75 75 This differs from a copy in that the content may be changing; the destination 76 - may be over-writing or replacing the source, and som extended attributes should 76 + may be over-writing or replacing the source, and some extended attributes should 77 77 not be preserved during this process. 78 78 .It Dv XATTR_OPERATION_INTENT_SHARE 79 79 Indicates that the intent is to share, or export, the object. For example, 80 80 saving as an attachment in an email message, or placing in a public folder. 81 81 Sensitive information should probably not be preserved in this case. 82 82 .It Dv XATTR_OPERATION_INTENT_SYNC 83 - Indicates that the intent is to sync the object to a service like iCloud. 83 + Indicates that the intent is to sync the object to a service like iCloud Drive. 84 84 .El 85 85 .Sh FLAGS 86 86 Various flags are defined by the type ··· 101 101 source object to a destination, no matter what the given intent is. 102 102 .It Dv XATTR_FLAG_SYNCABLE 103 103 This indicates that the extended attribute should be copied when the file 104 - is synced on services like iCloud. Sync services tends to want the metadata 105 - synced to be kept to a bare minimum, and may enforce additional restrictions 106 - on the acceptable size and number of extended attributes. 104 + is synced on services like iCloud Drive. Sync services may enforce additional 105 + restrictions on the acceptable size and number of extended attributes. 107 106 .El 108 107 .Sh EXAMPLE 109 108 The following example is a simple function that, given an extended attribute
+12
src/copyfile/xcodescripts/copyfile.xcconfig
··· 1 + #include "<DEVELOPER_DIR>/Makefiles/CoreOS/Xcode/BSD.xcconfig" 2 + 3 + INSTALL_PATH = /usr/lib/system 4 + 5 + PUBLIC_HEADERS_FOLDER_PATH = /usr/include 6 + PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include 7 + 8 + BUILD_VARIANTS = normal debug 9 + VERSION_INFO_PREFIX = __attribute__((visibility("hidden"))) 10 + IS_ZIPPERED = YES 11 + 12 + PRODUCT_NAME = copyfile
+40
src/copyfile/xcodescripts/install_files.sh
··· 1 + #!/bin/sh 2 + set -e -x 3 + 4 + # don't install man pages for installhdrs or iOS builds 5 + if [ "$ACTION" = installhdrs ]; then exit 0; fi 6 + if [ "${PLATFORM_NAME/iphone/}" != "${PLATFORM_NAME}" ]; then exit 0; fi 7 + 8 + function InstallManPages() { 9 + for MANPAGE in "$@"; do 10 + SECTION=`basename "${MANPAGE/*./}"` 11 + MANDIR="$DSTROOT"/usr/share/man/man"$SECTION" 12 + install -d -o "$INSTALL_OWNER" -g "$INSTALL_GROUP" -m 0755 "$MANDIR" 13 + install -o "$INSTALL_OWNER" -g "$INSTALL_GROUP" -m 0444 "$MANPAGE" "$MANDIR" 14 + done 15 + } 16 + 17 + function LinkManPages() { 18 + MANPAGE=`basename "$1"` 19 + SECTION=`basename "${MANPAGE/*./}"` 20 + MANDIR="$DSTROOT"/usr/share/man/man"$SECTION" 21 + shift 22 + for LINK in "$@"; do 23 + ln -hf "$MANDIR/$MANPAGE" "$MANDIR/$LINK" 24 + done 25 + } 26 + 27 + InstallManPages copyfile.3 28 + LinkManPages copyfile.3 \ 29 + fcopyfile.3 \ 30 + copyfile_state_alloc.3 \ 31 + copyfile_state_free.3 \ 32 + copyfile_state_get.3 \ 33 + copyfile_state_set.3 34 + 35 + InstallManPages xattr_name_with_flags.3 36 + LinkManPages xattr_name_with_flags.3 \ 37 + xattr_name_without_flags.3 \ 38 + xattr_flags_from_name.3 \ 39 + xattr_intent_with_flags.3 40 +