From: Apple Date: Mon, 2 May 2011 22:45:47 +0000 (+0000) Subject: copyfile-85.1.tar.gz X-Git-Tag: mac-os-x-107^0 X-Git-Url: https://git.saurik.com/apple/copyfile.git/commitdiff_plain/3f81d1c47321267dea83d8aeda8d3e9cbf343274 copyfile-85.1.tar.gz --- diff --git a/APPLE_LICENSE b/APPLE_LICENSE new file mode 100644 index 0000000..ab463a2 --- /dev/null +++ b/APPLE_LICENSE @@ -0,0 +1,335 @@ +APPLE PUBLIC SOURCE LICENSE +Version 2.0 - August 6, 2003 + +Please read this License carefully before downloading this software. By +downloading or using this software, you are agreeing to be bound by the terms +of this License. If you do not or cannot agree to the terms of this License, +please do not download or use the software. + +Apple Note: In January 2007, Apple changed its corporate name from "Apple +Computer, Inc." to "Apple Inc." This change has been reflected below and +copyright years updated, but no other changes have been made to the APSL 2.0. + +1. General; Definitions. This License applies to any program or other +work which Apple Inc. ("Apple") makes publicly available and which contains a +notice placed by Apple identifying such program or work as "Original Code" and +stating that it is subject to the terms of this Apple Public Source License +version 2.0 ("License"). As used in this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is the +grantor of rights, (i) claims of patents that are now or hereafter acquired, +owned by or assigned to Apple and (ii) that cover subject matter contained in +the Original Code, but only to the extent necessary to use, reproduce and/or +distribute the Original Code without infringement; and (b) in the case where +You are the grantor of rights, (i) claims of patents that are now or hereafter +acquired, owned by or assigned to You and (ii) that cover subject matter in +Your Modifications, taken alone or in combination with Original Code. + +1.2 "Contributor" means any person or entity that creates or contributes to +the creation of Modifications. + +1.3 "Covered Code" means the Original Code, Modifications, the combination +of Original Code and any Modifications, and/or any respective portions thereof. + +1.4 "Externally Deploy" means: (a) to sublicense, distribute or otherwise +make Covered Code available, directly or indirectly, to anyone other than You; +and/or (b) to use Covered Code, alone or as part of a Larger Work, in any way +to provide a service, including but not limited to delivery of content, through +electronic communication with a client other than You. + +1.5 "Larger Work" means a work which combines Covered Code or portions +thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change to, +the substance and/or structure of the Original Code, any previous +Modifications, the combination of Original Code and any previous Modifications, +and/or any respective portions thereof. When code is released as a series of +files, a Modification is: (a) any addition to or deletion from the contents of +a file containing Covered Code; and/or (b) any new file or other representation +of computer program statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other work as +originally made available by Apple under this License, including the Source +Code of any updates or upgrades to such programs or works made available by +Apple under this License, and that has been expressly identified by Apple as +such in the header file(s) of such work; and (b) the object code compiled from +such Source Code and originally made available by Apple under this License + +1.8 "Source Code" means the human readable form of a program or other work +that is suitable for making modifications to it, including all modules it +contains, plus any associated interface definition files, scripts used to +control compilation and installation of an executable (object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising rights +under this License. For legal entities, "You" or "Your" includes any entity +which controls, is controlled by, or is under common control with, You, where +"control" means (a) the power, direct or indirect, to cause the direction or +management of such entity, whether by contract or otherwise, or (b) ownership +of fifty percent (50%) or more of the outstanding shares or beneficial +ownership of such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms and +conditions of this License, Apple hereby grants You, effective on the date You +accept this License and download the Original Code, a world-wide, royalty-free, +non-exclusive license, to the extent of Apple's Applicable Patent Rights and +copyrights covering the Original Code, to do the following: + +2.1 Unmodified Code. You may use, reproduce, display, perform, internally +distribute within Your organization, and Externally Deploy verbatim, unmodified +copies of the Original Code, for commercial or non-commercial purposes, +provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the +copyright and other proprietary notices and disclaimers of Apple as they appear +in the Original Code, and keep intact all notices in the Original Code that +refer to this License; and + +(b) You must include a copy of this License with every copy of Source Code +of Covered Code and documentation You distribute or Externally Deploy, and You +may not offer or impose any terms on such Source Code that alter or restrict +this License or the recipients' rights hereunder, except as permitted under +Section 6. + +2.2 Modified Code. You may modify Covered Code and use, reproduce, +display, perform, internally distribute within Your organization, and +Externally Deploy Your Modifications and Covered Code, for commercial or +non-commercial purposes, provided that in each instance You also meet all of +these conditions: + +(a) You must satisfy all the conditions of Section 2.1 with respect to the +Source Code of the Covered Code; + +(b) You must duplicate, to the extent it does not already exist, the notice +in Exhibit A in each file of the Source Code of all Your Modifications, and +cause the modified files to carry prominent notices stating that You changed +the files and the date of any change; and + +(c) If You Externally Deploy Your Modifications, You must make Source Code +of all Your Externally Deployed Modifications either available to those to whom +You have Externally Deployed Your Modifications, or publicly available. Source +Code of Your Externally Deployed Modifications must be released under the terms +set forth in this License, including the license grants set forth in Section 3 +below, for as long as you Externally Deploy the Covered Code or twelve (12) +months from the date of initial External Deployment, whichever is longer. You +should preferably distribute the Source Code of Your Externally Deployed +Modifications electronically (e.g. download from a web site). + +2.3 Distribution of Executable Versions. In addition, if You Externally +Deploy Covered Code (Original Code and/or Modifications) in object code, +executable form only, You must include a prominent notice, in the code itself +as well as in related documentation, stating that Source Code of the Covered +Code is available under the terms of this License with information on how and +where to obtain such Source Code. + +2.4 Third Party Rights. You expressly acknowledge and agree that although +Apple and each Contributor grants the licenses to their respective portions of +the Covered Code set forth herein, no assurances are provided by Apple or any +Contributor that the Covered Code does not infringe the patent or other +intellectual property rights of any other entity. Apple and each Contributor +disclaim any liability to You for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a condition to +exercising the rights and licenses granted hereunder, You hereby assume sole +responsibility to secure any other intellectual property rights needed, if any. +For example, if a third party patent license is required to allow You to +distribute the Covered Code, it is Your responsibility to acquire that license +before distributing the Covered Code. + +3. Your Grants. In consideration of, and as a condition to, the licenses +granted to You under this License, You hereby grant to any person or entity +receiving or distributing Covered Code under this License a non-exclusive, +royalty-free, perpetual, irrevocable license, under Your Applicable Patent +Rights and other intellectual property rights (other than patent) owned or +controlled by You, to use, reproduce, display, perform, modify, sublicense, +distribute and Externally Deploy Your Modifications of the same scope and +extent as Apple's licenses under Sections 2.1 and 2.2 above. + +4. Larger Works. You may create a Larger Work by combining Covered Code +with other code not governed by the terms of this License and distribute the +Larger Work as a single product. In each such instance, You must make sure the +requirements of this License are fulfilled for the Covered Code or any portion +thereof. + +5. Limitations on Patent License. Except as expressly stated in Section +2, no other patent rights, express or implied, are granted by Apple herein. +Modifications and/or Larger Works may require additional patent licenses from +Apple which Apple may grant in its sole discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee for, +warranty, support, indemnity or liability obligations and/or other rights +consistent with the scope of the license granted herein ("Additional Terms") to +one or more recipients of Covered Code. However, You may do so only on Your own +behalf and as Your sole responsibility, and not on behalf of Apple or any +Contributor. You must obtain the recipient's agreement that any such Additional +Terms are offered by You alone, and You hereby agree to indemnify, defend and +hold Apple and every Contributor harmless for any liability incurred by or +claims asserted against Apple or such Contributor by reason of any such +Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new versions +of this License from time to time. Each version will be given a distinguishing +version number. Once Original Code has been published under a particular +version of this License, You may continue to use it under the terms of that +version. You may also choose to use such Original Code under the terms of any +subsequent version of this License published by Apple. No one other than Apple +has the right to modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in +part pre-release, untested, or not fully tested works. The Covered Code may +contain errors that could cause failures or loss of data, and may be incomplete +or contain inaccuracies. You expressly acknowledge and agree that use of the +Covered Code, or any portion thereof, is at Your sole and entire risk. THE +COVERED CODE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, UPGRADES OR SUPPORT OF +ANY KIND AND APPLE AND APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" +FOR THE PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM +ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF MERCHANTABILITY, OF +SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF ACCURACY, OF +QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. APPLE AND EACH +CONTRIBUTOR DOES NOT WARRANT AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE +COVERED CODE, THAT THE FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR +REQUIREMENTS, THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO ORAL OR +WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE AUTHORIZED +REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. You acknowledge +that the Covered Code is not intended for use in the operation of nuclear +facilities, aircraft navigation, communication systems, or air traffic control +machines in which case the failure of the Covered Code could lead to death, +personal injury, or severe physical or environmental damage. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO +EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL, +INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO THIS LICENSE OR +YOUR USE OR INABILITY TO USE THE COVERED CODE, OR ANY PORTION THEREOF, WHETHER +UNDER A THEORY OF CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCTS +LIABILITY OR OTHERWISE, EVEN IF APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL +PURPOSE OF ANY REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF +LIABILITY OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT +APPLY TO YOU. In no event shall Apple's total liability to You for all damages +(other than as may be required by applicable law) under this License exceed the +amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the +trademarks or trade names "Apple", "Mac", "Mac OS", "QuickTime", "QuickTime +Streaming Server" or any other trademarks, service marks, logos or trade names +belonging to Apple (collectively "Apple Marks") or to any trademark, service +mark, logo or trade name belonging to any Contributor. You agree not to use +any Apple Marks in or as part of the name of products derived from the Original +Code or to endorse or promote products derived from the Original Code other +than as expressly permitted by and in strict compliance at all times with +Apple's third party trademark usage guidelines which are posted at +http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Subject to the licenses granted under this License, each +Contributor retains all rights, title and interest in and to any Modifications +made by such Contributor. Apple retains all rights, title and interest in and +to the Original Code and any Modifications made by or on behalf of Apple +("Apple Modifications"), and such Apple Modifications will not be automatically +subject to this License. Apple may, at its sole discretion, choose to license +such Apple Modifications under this License, or on different terms from those +contained in this License or may choose not to license them at all. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Apple if You fail to comply with any +term(s) of this License and fail to cure such breach within 30 days of becoming +aware of such breach; +(b) immediately in the event of the circumstances described in Section +13.5(b); or +(c) automatically without notice from Apple if You, at any time during the +term of this License, commence an action for patent infringement against Apple; +provided that Apple did not first commence an action for patent infringement +against You in that instance. + +12.2 Effect of Termination. Upon termination, You agree to immediately stop +any further use, reproduction, modification, sublicensing and distribution of +the Covered Code. All sublicenses to the Covered Code which have been properly +granted prior to termination shall survive any termination of this License. +Provisions which, by their nature, should remain in effect beyond the +termination of this License shall survive, including but not limited to +Sections 3, 5, 8, 9, 10, 11, 12.2 and 13. No party will be liable to any other +for compensation, indemnity or damages of any sort solely as a result of +terminating this License in accordance with its terms, and termination of this +License will be without prejudice to any other right or remedy of any party. + +13. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as +defined in FAR 2.101. Government software and technical data rights in the +Covered Code include only those rights customarily provided to the public as +defined in this License. This customary commercial license in technical data +and software is provided in accordance with FAR 12.211 (Technical Data) and +12.212 (Computer Software) and, for Department of Defense purchases, DFAR +252.227-7015 (Technical Data -- Commercial Items) and 227.7202-3 (Rights in +Commercial Computer Software or Computer Software Documentation). Accordingly, +all U.S. Government End Users acquire Covered Code with only those rights set +forth herein. + +13.2 Relationship of Parties. This License will not be construed as +creating an agency, partnership, joint venture or any other form of legal +association between or among You, Apple or any Contributor, and You will not +represent to the contrary, whether expressly, by implication, appearance or +otherwise. + +13.3 Independent Development. Nothing in this License will impair Apple's +right to acquire, license, develop, have others develop for it, market and/or +distribute technology or products that perform the same or similar functions +as, or otherwise compete with, Modifications, Larger Works, technology or +products that You may develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple or any Contributor to enforce +any provision of this License will not be deemed a waiver of future enforcement +of that or any other provision. Any law or regulation which provides that the +language of a contract shall be construed against the drafter will not apply to +this License. + +13.5 Severability. (a) If for any reason a court of competent jurisdiction +finds any provision of this License, or portion thereof, to be unenforceable, +that provision of the License will be enforced to the maximum extent +permissible so as to effect the economic benefits and intent of the parties, +and the remainder of this License will continue in full force and effect. (b) +Notwithstanding the foregoing, if applicable law prohibits or restricts You +from fully and/or specifically complying with Sections 2 and/or 3 or prevents +the enforceability of either of those Sections, this License will immediately +terminate and You must immediately discontinue any use of the Covered Code and +destroy all copies of it that are in your possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution between +You and Apple relating to this License shall take place in the Northern +District of California, and You and Apple hereby consent to the personal +jurisdiction of, and venue in, the state and federal courts within that +District with respect to this License. The application of the United Nations +Convention on Contracts for the International Sale of Goods is expressly +excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the entire +agreement between the parties with respect to the subject matter hereof. This +License shall be governed by the laws of the United States and the State of +California, except that body of California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following clause +applies: The parties hereby confirm that they have requested that this License +and all related documents be drafted in English. Les parties ont exigé que le +présent contrat et tous les documents connexes soient rédigés en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999-2007 Apple Inc. All Rights Reserved. + +This file contains Original Code and/or Modifications of Original Code as +defined in and that are subject to the Apple Public Source License Version 2.0 +(the 'License'). You may not use this file except in compliance with the +License. Please obtain a copy of the License at +http://www.opensource.apple.com/apsl/ and read it before using this file. + +The Original Code and all software distributed under the License are +distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS +OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT +LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the +specific language governing rights and limitations under the License." + diff --git a/Makefile b/Makefile index 1d43cc1..b4cde73 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ +# buildit may need "-project copyfile-123" (any number should do) Project = copyfile -Install_Dir = /usr/local/lib/system -ProductType = staticlib +Install_Dir = /usr/lib/system +ProductType = dylib BuildProfile = YES BuildDebug = YES @@ -25,6 +26,8 @@ SDKROOT ?= / Extra_CC_Flags = ${WFLAGS} -fno-common \ -D__DARWIN_NOW_CANCELABLE=1 -I. +Extra_LD_Flags = -Wl,-umbrella -Wl,System + include $(MAKEFILEPATH)/CoreOS/ReleaseControl/BSDCommon.make $(OBJROOT)/$(Project)/_version.c: diff --git a/copyfile.3 b/copyfile.3 index c2a30dd..e7e94e3 100644 --- a/copyfile.3 +++ b/copyfile.3 @@ -106,7 +106,7 @@ functions can also have their behavior modified by the following flags: .It Dv COPYFILE_RECURSIVE Causes .Fn copyfile -to recursively copy a hierachy. +to recursively copy a hierarchy. This flag is not used by .Fn fcopyfile ; see below for more information. @@ -160,11 +160,18 @@ file, if it is a symbolic link. (This is only applicable for the .Fn copyfile function.) .It Dv COPYFILE_MOVE -Unlink (remove) the +Unlink (using +.Xr remove 3 ) +the .Fa from file. (This is only applicable for the .Fn copyfile -function.) +function.) No error is returned if +.Xr remove 3 +fails. Note that +.Xr remove 3 +removes a symbolic link itself, not the +target of the link. .It Dv COPYFILE_UNLINK Unlink the .Va to @@ -263,6 +270,13 @@ parameter is a pointer to .Vt off_t (type .Vt off_t\ * ). +.It Dv COPYFILE_STATE_XATTRNAME +Get the name of the extended attribute during a callback +for +.Dv COPYFILE_COPY_XATTR +(see below for details). This field cannot be set, +and may be +.Dv NULL . .El .Sh Recursive Copies When given the @@ -398,14 +412,14 @@ In addition to the recursive callbacks described above, .Fn copyfile and .Fn fcopyfile -will also use a callback to report data (i.e., +will also use a callback to report data (e.g., .Dv COPYFILE_DATA ) progress. If given, the callback will be invoked on each .Xr write 2 call. The first argument to the callback function will be .Dv COPYFILE_COPY_DATA . The second argument will either be -.Dv COPYFILE_COPY_PROGRESS +.Dv COPYFILE_PROGRESS (indicating that the write was successful), or .Dv COPYFILE_ERR (indicating that there was an error of some sort). @@ -417,6 +431,37 @@ with the requestor (the argument type is a pointer to .Vt off_t ). .Pp +When copying extended attributes, the first argument to the +callback function will be +.Dv COPYFILE_COPY_XATTR . +The other arguments will be as described for +.Dv COPYFILE_COPY_DATA ; +the name of the extended attribute being copied may be +retrieved using +.Fn copyfile_state_get +and the parameter +.Dv COPYFILE_STATE_XATTRNAME . +When using +.Dv COPYFILE_PACK , +the callback may be called with +.Dv COPYFILE_START +for each of the extended attributes first, followed by +.Dv COPYFILE_PROGRESS +before getting and packing the data for each +individual attribute, and then +.Dv COPYFILE_FINISH +when finished with each individual attribute. +(That is, +.Dv COPYFILE_START +may be called for all of the extended attributes, before +the first callback with +.Dv COPYFILE_PROGRESS +is invoked.) Any attribute skipped by returning +.Dv COPYFILE_SKIP +from the +.Dv COPYFILE_START +callback will not be placed into the packed output file. +.Pp The return value for the data callback must be one of .Bl -tag -width COPYFILE_CONTINUE .It Dv COPYFILE_CONTINUE @@ -426,7 +471,7 @@ The copy will continue as expected. The data copy will be aborted, but without error. .It Dv COPYFILE_QUIT The data copy will be aborted; in the case of -.Dv COPYFILE_COPY_PROGRESS , +.Dv COPYFILE_PROGRESS , .Dv errno will be set to .Dv ECANCELED . @@ -456,12 +501,17 @@ Both and .Fn fcopyfile can copy symbolic links; there is a gap between when the source -link is examnined and the actual copy is started, and this can +link is examined and the actual copy is started, and this can be a potential security risk, especially if the process has elevated privileges. .Pp When performing a recursive copy, if the source hierarchy changes while the copy is occurring, the results are undefined. +.Pp +.Fn fcopyfile +does not reset the seek position for either source or destination. +This can result in the destination file being a different size +than the source file. .Sh ERRORS .Fn copyfile and diff --git a/copyfile.c b/copyfile.c index 6834f59..6e8c0dd 100644 --- a/copyfile.c +++ b/copyfile.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2010 Apple, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,10 @@ #include #include +#ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION +# include +#endif + #include #if !TARGET_OS_EMBEDDED #include @@ -56,7 +61,7 @@ static void * qtn_file_alloc(void) { return NULL; } static int qtn_file_init_with_fd(void *x, int y) { return -1; } static void qtn_file_free(void *x) { return; } -static int qtn_file_apply_to_fd(void *x, int y) { return -1; } +static int qtn_file_apply_to_fd(void *x, int y) { return 0; } static char *qtn_error(int x) { return NULL; } static int qtn_file_to_data(void *x, char *y, size_t z) { return -1; } static void *qtn_file_clone(void *x) { return NULL; } @@ -66,7 +71,9 @@ static void *qtn_file_clone(void *x) { return NULL; } #include "copyfile.h" enum cfInternalFlags { - cfDelayAce = 1, + cfDelayAce = 1 << 0, + cfMakeFileInvisible = 1 << 1, + cfSawDecmpEA = 1 << 2, }; /* @@ -97,6 +104,7 @@ struct _copyfile_state filesec_t permissive_fsec; off_t totalCopied; int err; + char *xattr_name; }; struct acl_entry { @@ -129,6 +137,125 @@ acl_compare_permset_np(acl_permset_t p1, acl_permset_t p2) return ((ps1->ap_perms == ps2->ap_perms) ? 1 : 0); } + +static int +doesdecmpfs(int fd) { +#ifdef DECMPFS_XATTR_NAME + int rv; + struct attrlist attrs; + char volroot[MAXPATHLEN + 1]; + struct statfs sfs; + struct { + uint32_t length; + vol_capabilities_attr_t volAttrs; + } volattrs; + + (void)fstatfs(fd, &sfs); + strlcpy(volroot, sfs.f_mntonname, sizeof(volroot)); + + memset(&attrs, 0, sizeof(attrs)); + attrs.bitmapcount = ATTR_BIT_MAP_COUNT; + attrs.volattr = ATTR_VOL_CAPABILITIES; + + rv = getattrlist(volroot, &attrs, &volattrs, sizeof(volattrs), 0); + + if (rv != -1 && + (volattrs.volAttrs.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_DECMPFS_COMPRESSION) && + (volattrs.volAttrs.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_DECMPFS_COMPRESSION)) { + return 1; + } +#endif + return 0; +} + + +static void +sort_xattrname_list(void *start, size_t length) +{ + char **ptrs = NULL; + int nel; + char *tmp; + int indx = 0; + + /* If it's not a proper C string at the end, don't do anything */ + if (((char*)start)[length] != 0) + return; + /* + * In order to sort the list of names, we need to + * make a list of pointers to strings. To do that, + * we need to run through the buffer, and find the + * beginnings of strings. + */ + nel = 10; // Most files don't have many EAs + ptrs = (char**)calloc(nel, sizeof(char*)); + + if (ptrs == NULL) + goto done; + +#ifdef DEBUG +{ + char *curPtr = start; + while (curPtr < (char*)start + length) { + printf("%s\n", curPtr); + curPtr += strlen(curPtr) + 1; + } +} +#endif + + tmp = ptrs[indx++] = (char*)start; + + while (tmp = memchr(tmp, 0, ((char*)start + length) - tmp)) { + if (indx == nel) { + nel += 10; + ptrs = realloc(ptrs, sizeof(char**) * nel); + if (ptrs == NULL) + goto done; + } + ptrs[indx++] = ++tmp; + } +#ifdef DEBUG + printf("Unsorted:\n"); + for (nel = 0; nel < indx-1; nel++) { + printf("\tEA %d = `%s'\n", nel, ptrs[nel]); + } +#endif + qsort_b(ptrs, indx-1, sizeof(char*), ^(const void *left, const void *right) { + int rv; + char *lstr = *(char**)left, *rstr = *(char**)right; + rv = strcmp(lstr, rstr); + return rv; + }); +#ifdef DEBUG + printf("Sorted:\n"); + for (nel = 0; nel < indx-1; nel++) { + printf("\tEA %d = `%s'\n", nel, ptrs[nel]); + } +#endif + /* + * Now that it's sorted, we need to make a copy, so we can + * move the strings around into the new order. Then we + * copy that on top of the old buffer, and we're done. + */ + char *copy = malloc(length); + if (copy) { + int i; + char *curPtr = copy; + + for (i = 0; i < indx-1; i++) { + size_t len = strlen(ptrs[i]); + memcpy(curPtr, ptrs[i], len+1); + curPtr += len+1; + } + memcpy(start, copy, length); + free(copy); + } + +done: + if (ptrs) + free(ptrs); + return; +} + /* * Internally, the process is broken into a series of * private functions. @@ -215,27 +342,47 @@ add_uberace(acl_t *acl) */ if (acl_create_entry_np(acl, &entry, ACL_FIRST_ENTRY) == -1) goto error_exit; - if (acl_get_permset(entry, &permset) == -1) + if (acl_get_permset(entry, &permset) == -1) { + copyfile_warn("acl_get_permset"); goto error_exit; - if (acl_clear_perms(permset) == -1) + } + if (acl_clear_perms(permset) == -1) { + copyfile_warn("acl_clear_permset"); goto error_exit; - if (acl_add_perm(permset, ACL_WRITE_DATA) == -1) + } + if (acl_add_perm(permset, ACL_WRITE_DATA) == -1) { + copyfile_warn("add ACL_WRITE_DATA"); goto error_exit; - if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1) + } + if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1) { + copyfile_warn("add ACL_WRITE_ATTRIBUTES"); goto error_exit; - if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1) + } + if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1) { + copyfile_warn("add ACL_WRITE_EXTATTRIBUTES"); goto error_exit; - if (acl_add_perm(permset, ACL_APPEND_DATA) == -1) + } + if (acl_add_perm(permset, ACL_APPEND_DATA) == -1) { + copyfile_warn("add ACL_APPEND_DATA"); goto error_exit; - if (acl_add_perm(permset, ACL_WRITE_SECURITY) == -1) + } + if (acl_add_perm(permset, ACL_WRITE_SECURITY) == -1) { + copyfile_warn("add ACL_WRITE_SECURITY"); goto error_exit; - if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1) + } + if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1) { + copyfile_warn("set ACL_EXTENDED_ALLOW"); goto error_exit; + } - if(acl_set_permset(entry, permset) == -1) + if(acl_set_permset(entry, permset) == -1) { + copyfile_warn("acl_set_permset"); goto error_exit; - if(acl_set_qualifier(entry, qual) == -1) + } + if(acl_set_qualifier(entry, qual) == -1) { + copyfile_warn("acl_set_qualifier"); goto error_exit; + } return 0; error_exit: @@ -250,7 +397,7 @@ is_uberace(acl_entry_t ace) acl_t tacl; acl_entry_t tentry; acl_tag_t tag; - guid_t *qual; + guid_t *qual = NULL; uuid_t myuuid; // Who am I, and who is the ACE for? @@ -279,6 +426,9 @@ is_uberace(acl_entry_t ace) done: + if (qual) + acl_free(qual); + if (tacl) acl_free(tacl); @@ -465,7 +615,7 @@ copytree(copyfile_state_t s) retval = -1; goto done; } - if (sbuf.st_mode & S_IFDIR) { + if ((sbuf.st_mode & S_IFMT) == S_IFDIR) { srcisdir = 1; } @@ -626,7 +776,8 @@ copytree(copyfile_state_t s) goto stopit; } } - rv = copyfile(ftsent->fts_path, dstfile, tstate, flags); + int tmp_flags = (cmd == COPYFILE_RECURSE_DIR) ? (flags & ~COPYFILE_STAT) : flags; + rv = copyfile(ftsent->fts_path, dstfile, tstate, tmp_flags); if (rv < 0) { if (status) { rv = (*status)(cmd, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx); @@ -649,8 +800,6 @@ copytree(copyfile_state_t s) } } } else if (cmd == COPYFILE_RECURSE_DIR_CLEANUP) { - int tfd; - if (status) { rv = (*status)(cmd, COPYFILE_START, tstate, ftsent->fts_path, dstfile, s->ctx); if (rv == COPYFILE_QUIT) { @@ -661,24 +810,8 @@ copytree(copyfile_state_t s) goto skipit; } } - tfd = open(dstfile, O_RDONLY); - if (tfd != -1) { - struct stat sb; - if (s->flags & COPYFILE_STAT) { - (s->flags & COPYFILE_NOFOLLOW_SRC ? lstat : stat)(ftsent->fts_path, &sb); - } else { - (s->flags & COPYFILE_NOFOLLOW_DST ? lstat : stat)(dstfile, &sb); - } - remove_uberace(tfd, &sb); - close(tfd); - if (status) { - rv = (*status)(COPYFILE_RECURSE_DIR_CLEANUP, COPYFILE_FINISH, tstate, ftsent->fts_path, dstfile, s->ctx); - if (rv == COPYFILE_QUIT) { - rv = -1; errno = 0; - goto stopit; - } - } - } else { + rv = copyfile(ftsent->fts_path, dstfile, tstate, (flags & COPYFILE_NOFOLLOW) | COPYFILE_STAT); + if (rv < 0) { if (status) { rv = (*status)(COPYFILE_RECURSE_DIR_CLEANUP, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx); if (rv == COPYFILE_QUIT) { @@ -694,7 +827,16 @@ copytree(copyfile_state_t s) retval = -1; goto stopit; } + } else { + if (status) { + rv = (*status)(COPYFILE_RECURSE_DIR_CLEANUP, COPYFILE_FINISH, tstate, ftsent->fts_path, dstfile, s->ctx); + if (rv == COPYFILE_QUIT) { + retval = -1; errno = 0; + goto stopit; + } + } } + rv = 0; } skipit: @@ -860,7 +1002,7 @@ int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_ goto error_exit; if ((s->flags & COPYFILE_NOFOLLOW_DST) && lstat(s->dst, &dst_sb) == 0 && - (dst_sb.st_mode & S_IFLNK)) { + ((dst_sb.st_mode & S_IFMT) == S_IFLNK)) { if (s->permissive_fsec) free(s->permissive_fsec); s->permissive_fsec = NULL; @@ -902,6 +1044,9 @@ int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_ } else if ((ret = copyfile_open(s)) < 0) goto error_exit; + (void)fcntl(s->src_fd, F_NOCACHE, 1); + (void)fcntl(s->dst_fd, F_NOCACHE, 1); + ret = copyfile_internal(s, flags); if (ret == -1) goto error_exit; @@ -919,6 +1064,9 @@ int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_ reset_security(s); + if (s->src && (flags & COPYFILE_MOVE)) + (void)remove(s->src); + exit: if (state == NULL) { int t = errno; @@ -1032,8 +1180,27 @@ static int copyfile_internal(copyfile_state_t s, copyfile_flags_t flags) * to apply it to dst_fd. We don't care if * it fails, not yet anyway. */ - if (s->qinfo) - (void)qtn_file_apply_to_fd(s->qinfo, s->dst_fd); + if (s->qinfo) { + int qr = qtn_file_apply_to_fd(s->qinfo, s->dst_fd); + if (qr != 0) { + if (s->statuscb) { + int rv; + + s->xattr_name = (char*)XATTR_QUARANTINE_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + s->err = errno = (qr < 0 ? ENOTSUP : qr); + ret = -1; + goto exit; + } + } else { + s->err = errno = (qr < 0 ? ENOTSUP : qr); + ret = -1; + goto exit; + } + } + } /* * COPYFILE_XATTR tells us to copy the extended attributes; @@ -1142,6 +1309,8 @@ int copyfile_state_free(copyfile_state_t s) copyfile_warn("error closing files"); return -1; } + if (s->xattr_name) + free(s->xattr_name); if (s->dst) free(s->dst); if (s->src) @@ -1403,7 +1572,7 @@ static int copyfile_open(copyfile_state_t s) bp = calloc(1, sz); if (bp == NULL) { - copyfile_warn("cannot allocate %d bytes", sz); + copyfile_warn("cannot allocate %zd bytes", sz); return -1; } if (readlink(s->src, bp, sz-1) == -1) { @@ -1426,7 +1595,7 @@ static int copyfile_open(copyfile_state_t s) } } else if (isdir) { mode_t mode; - mode = s->sb.st_mode & ~S_IFMT; + mode = (s->sb.st_mode & ~S_IFMT) | S_IRWXU; if (mkdir(s->dst, mode) == -1) { if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) { @@ -1612,6 +1781,18 @@ static int copyfile_data(copyfile_state_t s) if ((s->sb.st_mode & S_IFMT) != S_IFREG) return 0; +#ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION + if (s->internal_flags & cfSawDecmpEA) { + if (s->sb.st_flags & UF_COMPRESSED) { + if ((s->flags & COPYFILE_STAT) == 0) { + if (fchflags(s->dst_fd, UF_COMPRESSED) == 0) { + goto exit; + } + } + } + } +#endif + if (fstatfs(s->src_fd, &sfs) == -1) { iBlocksize = s->sb.st_blksize; } else { @@ -1694,7 +1875,7 @@ static int copyfile_data(copyfile_state_t s) if (status) { int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); if (rv == COPYFILE_QUIT) { - ret = -1; s->err = ECANCELED; + ret = -1; s->err = errno = ECANCELED; goto exit; } } @@ -1707,7 +1888,7 @@ static int copyfile_data(copyfile_state_t s) goto exit; } - if (ftruncate(s->dst_fd, s->sb.st_size) < 0) + if (ftruncate(s->dst_fd, s->totalCopied) < 0) { ret = -1; goto exit; @@ -1731,11 +1912,8 @@ exit: static int copyfile_security(copyfile_state_t s) { int copied = 0; - int has_uberace = 0; - acl_flagset_t flags; struct stat sb; - acl_entry_t entry_src = NULL, entry_dst = NULL; - acl_t acl_src = NULL, acl_dst = NULL; + acl_t acl_src = NULL, acl_tmp = NULL, acl_dst = NULL; int ret = 0; filesec_t tmp_fsec = NULL; filesec_t fsec_dst = filesec_init(); @@ -1767,75 +1945,64 @@ static int copyfile_security(copyfile_state_t s) else goto error_exit; } - else - { - acl_t tmp = acl_init(4); - acl_entry_t ace = NULL; - int count = 0; - - if (tmp == NULL) - goto error_exit; - - - for (; acl_get_entry(acl_dst, - ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, - &ace) == 0;) - { - if (count++ == 0 && is_uberace(ace)) { - if ((ret = acl_create_entry(&tmp, &entry_dst)) == -1) - break; - if ((ret = acl_copy_entry(entry_dst, ace)) == -1) - break; - has_uberace = 1; - continue; - } - acl_get_flagset_np(ace, &flags); - if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) - { - if ((ret = acl_create_entry(&tmp, &entry_dst)) == -1) - break; - if ((ret = acl_copy_entry(entry_dst, ace)) == -1) - break; - } - } - acl_free(acl_dst); - acl_dst = tmp; - - if (ret == -1) - goto error_exit; - } if (acl_src == NULL && acl_dst == NULL) goto no_acl; + acl_tmp = acl_init(4); + if (acl_tmp == NULL) + goto error_exit; + if (acl_src) { - if (acl_dst == NULL) - acl_dst = acl_init(4); - for (copied = 0;acl_get_entry(acl_src, - entry_src == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, - &entry_src) == 0;) + acl_entry_t ace = NULL; + acl_entry_t tmp = NULL; + for (copied = 0; + acl_get_entry(acl_src, + ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, + &ace) == 0;) { - acl_get_flagset_np(entry_src, &flags); + acl_flagset_t flags = { 0 }; + acl_get_flagset_np(ace, &flags); if (!acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) { - if ((ret = acl_create_entry(&acl_dst, &entry_dst)) == -1) + if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1) goto error_exit; - if ((ret = acl_copy_entry(entry_dst, entry_src)) == -1) + if ((ret = acl_copy_entry(tmp, ace)) == -1) goto error_exit; copyfile_debug(2, "copied acl entry from %s to %s", s->src ? s->src : "(null src)", - s->dst ? s->dst : "(null dst)"); + s->dst ? s->dst : "(null tmp)"); copied++; } } } - if (!has_uberace && (s->internal_flags & cfDelayAce)) { - if (add_uberace(&acl_dst)) - goto error_exit; + if (acl_dst) { + acl_entry_t ace = NULL; + acl_entry_t tmp = NULL; + acl_flagset_t flags = { 0 }; + for (copied = 0;acl_get_entry(acl_dst, + ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, + &ace) == 0;) + { + acl_get_flagset_np(ace, &flags); + if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) + { + if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1) + goto error_exit; + + if ((ret = acl_copy_entry(tmp, ace)) == -1) + goto error_exit; + + copyfile_debug(2, "copied acl entry from %s to %s", + s->src ? s->src : "(null dst)", + s->dst ? s->dst : "(null tmp)"); + copied++; + } + } } - if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_dst)) + if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_tmp)) { copyfile_debug(3, "altered acl"); } @@ -1876,10 +2043,14 @@ no_acl: * discretely. We don't care if the fchown fails, but we do care * if the mode or ACL can't be set. For historical reasons, we * simply log those failures, however. + * + * Big warning here: we may NOT have COPYFILE_STAT set, since + * we fell-through from COPYFILE_ACL. So check for the fchmod. */ #define NS(x) ((x) ? (x) : "(null string)") - if (fchmod(s->dst_fd, s->sb.st_mode) == -1) { + if ((s->flags & COPYFILE_STAT) && + fchmod(s->dst_fd, s->sb.st_mode) == -1) { copyfile_warn("could not change mode of destination file %s to match source file %s", NS(s->dst), NS(s->src)); } (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid); @@ -1901,6 +2072,7 @@ exit: filesec_free(fsec_dst); if (acl_src) acl_free(acl_src); if (acl_dst) acl_free(acl_dst); + if (acl_tmp) acl_free(acl_tmp); return ret; @@ -1917,15 +2089,16 @@ goto exit; static int copyfile_stat(copyfile_state_t s) { struct timeval tval[2]; + unsigned int added_flags = 0; + /* - * NFS doesn't support chflags; ignore errors unless there's reason - * to believe we're losing bits. (Note, this still won't be right - * if the server supports flags and we were trying to *remove* flags - * on a file that we copied, i.e., that we didn't create.) + * NFS doesn't support chflags; ignore errors as a result, since + * we don't return failure for this. */ - if (fchflags(s->dst_fd, (u_int)s->sb.st_flags)) - if (errno != EOPNOTSUPP || s->sb.st_flags != 0) - copyfile_warn("%s: set flags (was: 0%07o)", s->dst ? s->dst : "(null dst)", s->sb.st_flags); + if (s->internal_flags & cfMakeFileInvisible) + added_flags |= UF_HIDDEN; + + (void)fchflags(s->dst_fd, (u_int)s->sb.st_flags | added_flags); /* If this fails, we don't care */ (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid); @@ -1936,8 +2109,8 @@ static int copyfile_stat(copyfile_state_t s) tval[0].tv_sec = s->sb.st_atime; tval[1].tv_sec = s->sb.st_mtime; tval[0].tv_usec = tval[1].tv_usec = 0; - if (futimes(s->dst_fd, tval)) - copyfile_warn("%s: set times", s->dst ? s->dst : "(null dst)"); + (void)futimes(s->dst_fd, tval); + return 0; } @@ -1961,6 +2134,7 @@ static int copyfile_xattr(copyfile_state_t s) ssize_t asize; ssize_t nsize; int ret = 0; + int look_for_decmpea = 0; /* delete EAs on destination */ if ((nsize = flistxattr(s->dst_fd, 0, 0, 0)) > 0) @@ -1998,8 +2172,17 @@ static int copyfile_xattr(copyfile_state_t s) return -1; } +#ifdef DECMPFS_XATTR_NAME + if ((s->flags & COPYFILE_DATA) && + (s->sb.st_flags & UF_COMPRESSED) && + doesdecmpfs(s->src_fd) && + doesdecmpfs(s->dst_fd)) { + look_for_decmpea = XATTR_SHOWCOMPRESSION; + } +#endif + /* get name list of EAs on source */ - if ((nsize = flistxattr(s->src_fd, 0, 0, 0)) < 0) + if ((nsize = flistxattr(s->src_fd, 0, 0, look_for_decmpea)) < 0) { if (errno == ENOTSUP || errno == EPERM) return 0; @@ -2012,7 +2195,7 @@ static int copyfile_xattr(copyfile_state_t s) if ((namebuf = (char *) malloc(nsize)) == NULL) return -1; else - nsize = flistxattr(s->src_fd, namebuf, nsize, 0); + nsize = flistxattr(s->src_fd, namebuf, nsize, look_for_decmpea); if (nsize <= 0) { free(namebuf); @@ -2040,9 +2223,8 @@ static int copyfile_xattr(copyfile_state_t s) if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) continue; - if ((xa_size = fgetxattr(s->src_fd, name, 0, 0, 0, 0)) < 0) + if ((xa_size = fgetxattr(s->src_fd, name, 0, 0, 0, look_for_decmpea)) < 0) { - ret = -1; continue; } @@ -2059,24 +2241,98 @@ static int copyfile_xattr(copyfile_state_t s) } } - if ((asize = fgetxattr(s->src_fd, name, xa_dataptr, xa_size, 0, 0)) < 0) + if ((asize = fgetxattr(s->src_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea)) < 0) { - ret = -1; continue; } if (xa_size != asize) xa_size = asize; - if (fsetxattr(s->dst_fd, name, xa_dataptr, xa_size, 0, 0) < 0) +#ifdef DECMPFS_XATTR_NAME + if (strncmp(name, DECMPFS_XATTR_NAME, end-name) == 0) { - ret = -1; - continue; + decmpfs_disk_header *hdr = xa_dataptr; + + /* + * If the EA has the decmpfs name, but is too + * small, or doesn't have the right magic number, + * or isn't the right type, we'll just skip it. + * This means it won't end up in the destination + * file, and data copy will happen normally. + */ + if ((size_t)xa_size < sizeof(decmpfs_disk_header)) { + continue; + } + if (OSSwapLittleToHostInt32(hdr->compression_magic) != DECMPFS_MAGIC) { + continue; + } + if (OSSwapLittleToHostInt32(hdr->compression_type) != 3 && + OSSwapLittleToHostInt32(hdr->compression_type) != 4) { + continue; + } + s->internal_flags |= cfSawDecmpEA; + } +#endif + + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } + s->xattr_name = strdup(name); + + if (s->statuscb) { + int rv; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_QUIT) { + s->err = ECANCELED; + goto out; + } else if (rv == COPYFILE_SKIP) { + continue; + } + } + if (fsetxattr(s->dst_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea) < 0) + { + if (s->statuscb) + { + int rv; + if (s->xattr_name == NULL) + s->xattr_name = strdup(name); + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_QUIT) + { + s->err = ECANCELED; + ret = -1; + goto out; + } + } + else + { + ret = -1; + copyfile_warn("could not set attributes %s on destination file descriptor: %s", name, strerror(errno)); + continue; + } + } + if (s->statuscb) { + int rv; + if (s->xattr_name == NULL) + s->xattr_name = strdup(name); + + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_QUIT) { + s->err = ECANCELED; + goto out; + } } } +out: if (namebuf) free(namebuf); free((void *) xa_dataptr); + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } return ret; } @@ -2126,6 +2382,11 @@ int copyfile_state_get(copyfile_state_t s, uint32_t flag, void *ret) case COPYFILE_STATE_COPIED: *(off_t*)ret = s->totalCopied; break; +#endif +#ifdef COPYFILE_STATE_XATTRNAME + case COPYFILE_STATE_XATTRNAME: + *(char**)ret = s->xattr_name; + break; #endif default: errno = EINVAL; @@ -2271,7 +2532,7 @@ int main(int c, char *v[]) #define offsetof(type, member) ((size_t)(&((type *)0)->member)) -#define XATTR_MAXATTRLEN (4*1024) +#define XATTR_MAXATTRLEN (32*1024) /* @@ -2564,7 +2825,7 @@ static int copyfile_unpack(copyfile_state_t s) buffer = calloc(1, hdrsize); if (buffer == NULL) { - copyfile_debug(1, "copyfile_unpack: calloc(1, %u) returned NULL", hdrsize); + copyfile_debug(1, "copyfile_unpack: calloc(1, %zu) returned NULL", hdrsize); error = -1; goto exit; } else @@ -2574,7 +2835,7 @@ static int copyfile_unpack(copyfile_state_t s) if (bytes < 0) { - copyfile_debug(1, "pread returned: %d", bytes); + copyfile_debug(1, "pread returned: %zd", bytes); error = -1; goto exit; } @@ -2608,30 +2869,27 @@ static int copyfile_unpack(copyfile_state_t s) * Remove any extended attributes on the target. */ - if (COPYFILE_XATTR & s->flags) + if ((bytes = flistxattr(s->dst_fd, 0, 0, 0)) > 0) { - if ((bytes = flistxattr(s->dst_fd, 0, 0, 0)) > 0) - { - char *namebuf, *name; + char *namebuf, *name; - if ((namebuf = (char*) malloc(bytes)) == NULL) - { - s->err = ENOMEM; - goto exit; - } - bytes = flistxattr(s->dst_fd, namebuf, bytes, 0); - - if (bytes > 0) - for (name = namebuf; name < namebuf + bytes; name += strlen(name) + 1) - (void)fremovexattr(s->dst_fd, name, 0); - - free(namebuf); - } - else if (bytes < 0) + if ((namebuf = (char*) malloc(bytes)) == NULL) { - if (errno != ENOTSUP && errno != EPERM) + s->err = ENOMEM; goto exit; } + bytes = flistxattr(s->dst_fd, namebuf, bytes, 0); + + if (bytes > 0) + for (name = namebuf; name < namebuf + bytes; name += strlen(name) + 1) + (void)fremovexattr(s->dst_fd, name, 0); + + free(namebuf); + } + else if (bytes < 0) + { + if (errno != ENOTSUP && errno != EPERM) + goto exit; } /* @@ -2650,7 +2908,7 @@ static int copyfile_unpack(copyfile_state_t s) int i; if ((size_t)hdrsize < sizeof(attr_header_t)) { - copyfile_warn("bad attribute header: %u < %u", hdrsize, sizeof(attr_header_t)); + copyfile_warn("bad attribute header: %zu < %zu", hdrsize, sizeof(attr_header_t)); error = -1; goto exit; } @@ -2756,7 +3014,7 @@ static int copyfile_unpack(copyfile_state_t s) dataptr = (char *)attrhdr + entry->offset; if (dataptr > endptr || dataptr < buffer) { - copyfile_debug(1, "Entry %d overflows: offset = %u", entry->offset); + copyfile_debug(1, "Entry %d overflows: offset = %u", i, entry->offset); error = -1; s->err = EINVAL; /* Invalid buffer */ goto exit; @@ -2767,7 +3025,7 @@ static int copyfile_unpack(copyfile_state_t s) if (COPYFILE_VERBOSE & s->flags) copyfile_warn("Incomplete or corrupt attribute entry"); copyfile_debug(1, "Entry %d length overflows: offset = %u, length = %u", - entry->offset, entry->length); + i, entry->offset, entry->length); error = -1; s->err = EINVAL; /* Invalid buffer */ goto exit; @@ -2799,8 +3057,22 @@ static int copyfile_unpack(copyfile_state_t s) { int x; x = qtn_file_apply_to_fd(tqinfo, s->dst_fd); - if (x != 0) + if (x != 0) { copyfile_warn("qtn_file_apply_to_fd failed: %s", qtn_error(x)); + if (s->statuscb) { + int rv; + s->xattr_name = (char*)XATTR_QUARANTINE_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + error = s->err = x < 0 ? ENOTSUP : errno; + goto exit; + } + } else { + error = s->err = x < 0 ? ENOTSUP : errno; + goto exit; + } + } } if (tqinfo && !s->qinfo) { @@ -2808,13 +3080,22 @@ static int copyfile_unpack(copyfile_state_t s) } } /* Look for ACL data */ - else if (COPYFILE_ACL & s->flags && strcmp((char*)entry->name, XATTR_SECURITY_NAME) == 0) + else if (strcmp((char*)entry->name, XATTR_SECURITY_NAME) == 0) { acl_t acl; struct stat sb; int retry = 1; char *tcp = dataptr; + if (entry->length == 0) { + /* Not sure how we got here, but we had one case + * where it was 0. In a normal EA, we can have a 0-byte + * payload. That means nothing in this case, so we'll + * simply skip the EA. + */ + error = 0; + goto acl_done; + } /* * acl_from_text() requires a NUL-terminated string. The ACL EA, * however, may not be NUL-terminated. So in that case, we need to @@ -2862,17 +3143,59 @@ static int copyfile_unpack(copyfile_state_t s) acl_free(acl); filesec_free(fsec_tmp); +acl_done: if (error == -1) goto exit; } } /* And, finally, everything else */ - else if (COPYFILE_XATTR & s->flags) { - if (fsetxattr(s->dst_fd, (char *)entry->name, dataptr, entry->length, 0, 0) == -1) { + else + { + if (s->statuscb) { + int rv; + s->xattr_name = strdup((char*)entry->name); + s->totalCopied = 0; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_QUIT) { + s->err = ECANCELED; + error = -1; + goto exit; + } + } + if (fsetxattr(s->dst_fd, (char *)entry->name, dataptr, entry->length, 0, 0) == -1) { if (COPYFILE_VERBOSE & s->flags) copyfile_warn("error %d setting attribute %s", error, entry->name); - error = -1; - goto exit; + if (s->statuscb) { + int rv; + + s->xattr_name = strdup((char*)entry->name); + rv = (s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } + if (rv == COPYFILE_QUIT) { + error = -1; + goto exit; + } + } else { + error = -1; + goto exit; + } + } else if (s->statuscb) { + int rv; + s->xattr_name = strdup((char*)entry->name); + s->totalCopied = entry->length; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } + if (rv == COPYFILE_QUIT) { + error = -1; + s->err = ECANCELED; + goto exit; + } } } entry = ATTR_NEXT(entry); @@ -2889,11 +3212,55 @@ static int copyfile_unpack(copyfile_state_t s) if (bcmp((u_int8_t*)buffer + adhdr->entries[0].offset, emptyfinfo, sizeof(emptyfinfo)) != 0) { + uint16_t *fFlags; + uint8_t *newFinfo; + enum { kFinderInvisibleMask = 1 << 14 }; + + newFinfo = (u_int8_t*)buffer + adhdr->entries[0].offset; + fFlags = (uint16_t*)&newFinfo[8]; copyfile_debug(3, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME); + if (s->statuscb) { + int rv; + s->xattr_name = (char*)XATTR_FINDERINFO_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + error = -1; + s->err = ECANCELED; + goto exit; + } else if (rv == COPYFILE_SKIP) { + goto skip_fi; + } + } error = fsetxattr(s->dst_fd, XATTR_FINDERINFO_NAME, (u_int8_t*)buffer + adhdr->entries[0].offset, sizeof(emptyfinfo), 0, 0); - if (error) + if (error) { + if (s->statuscb) { + int rv; + s->xattr_name = XATTR_FINDERINFO_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + error = -1; + s->err = ECANCELED; + goto exit; + } + } goto exit; + } else if (s->statuscb) { + int rv; + s->xattr_name = XATTR_FINDERINFO_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + error = -1; + s->err = ECANCELED; + goto exit; + } + } + if (SWAP16(*fFlags) & kFinderInvisibleMask) + s->internal_flags |= cfMakeFileInvisible; } +skip_fi: /* * Extract the Resource Fork. @@ -2912,7 +3279,7 @@ static int copyfile_unpack(copyfile_state_t s) rsrcforkdata = malloc(length); if (rsrcforkdata == NULL) { - copyfile_debug(1, "could not allocate %u bytes for rsrcforkdata", + copyfile_debug(1, "could not allocate %zu bytes for rsrcforkdata", length); error = -1; goto bad; @@ -2941,6 +3308,21 @@ static int copyfile_unpack(copyfile_state_t s) error = -1; goto bad; } + if (s->statuscb) { + int rv; + s->xattr_name = XATTR_RESOURCEFORK_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + error = -1; + s->err = ECANCELED; + if (rsrcforkdata) + free(rsrcforkdata); + goto exit; + } else if (rv == COPYFILE_SKIP) { + goto bad; + } + } error = fsetxattr(s->dst_fd, XATTR_RESOURCEFORK_NAME, rsrcforkdata, bytes, 0, 0); if (error) { @@ -2959,9 +3341,31 @@ static int copyfile_unpack(copyfile_state_t s) error = errno = 0; goto bad; } + if (s->statuscb) { + int rv; + s->xattr_name = XATTR_RESOURCEFORK_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_CONTINUE) { + error = errno = 0; + goto bad; + } + } copyfile_debug(1, "error %d setting resource fork attribute", error); error = -1; goto bad; + } else if (s->statuscb) { + int rv; + s->xattr_name = XATTR_RESOURCEFORK_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + error = -1; + s->err = ECANCELED; + if (rsrcforkdata) + free(rsrcforkdata); + goto exit; + } } copyfile_debug(3, "extracting \"%s\" (%d bytes)", XATTR_RESOURCEFORK_NAME, (int)length); @@ -3064,6 +3468,29 @@ static int copyfile_pack_rsrcfork(copyfile_state_t s, attr_header_t *filehdr) char *databuf = NULL; int ret = 0; +/* + * XXX + * do COPYFILE_COPY_XATTR here; no need to + * the work if we want to skip. + */ + + if (s->statuscb) + { + int rv; + + s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME; + + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_SKIP) { + ret = 0; + goto done; + } + if (rv == COPYFILE_QUIT) { + ret = -1; + s->err = ECANCELED; + goto done; + } + } /* Get the resource fork size */ if ((datasize = fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0)) < 0) { @@ -3078,6 +3505,19 @@ static int copyfile_pack_rsrcfork(copyfile_state_t s, attr_header_t *filehdr) goto done; } + if (s->statuscb) { + int rv; + s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME; + + s->totalCopied = 0; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + s->err = ECANCELED; + ret = -1; + goto done; + } + } if ((databuf = malloc(datasize)) == NULL) { copyfile_warn("malloc"); @@ -3099,14 +3539,38 @@ static int copyfile_pack_rsrcfork(copyfile_state_t s, attr_header_t *filehdr) if (COPYFILE_VERBOSE & s->flags) copyfile_warn("couldn't write resource fork"); } - copyfile_debug(3, "copied %d bytes of \"%s\" data @ offset 0x%08x", + if (s->statuscb) + { + int rv; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_QUIT) { + ret = -1; + goto done; + } + } + copyfile_debug(3, "copied %zd bytes of \"%s\" data @ offset 0x%08x", datasize, XATTR_RESOURCEFORK_NAME, filehdr->appledouble.entries[1].offset); filehdr->appledouble.entries[1].length = (u_int32_t)datasize; done: + if (ret == -1 && s->statuscb) + { + int rv; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_CONTINUE) + ret = 0; + } + if (s->xattr_name) { + s->xattr_name = NULL; + } if (databuf) free(databuf); +/* + * XXX + * Do status callback here + * If ret == -1, then error callback + */ return ret; } @@ -3163,7 +3627,7 @@ static int copyfile_pack(copyfile_state_t s) * Fill in the initial Attribute Header. */ filehdr->magic = ATTR_HDR_MAGIC; - filehdr->debug_tag = (u_int32_t)s->sb.st_ino; + filehdr->debug_tag = 0; filehdr->data_start = (u_int32_t)sizeof(attr_header_t); /* @@ -3184,6 +3648,7 @@ static int copyfile_pack(copyfile_state_t s) { offset = strlen(XATTR_SECURITY_NAME) + 1; strcpy(attrnamebuf, XATTR_SECURITY_NAME); + endnamebuf = attrnamebuf + offset; } if (temp_acl) acl_free(temp_acl); @@ -3202,13 +3667,15 @@ static int copyfile_pack(copyfile_state_t s) listsize = left; } - listsize += offset; - endnamebuf = attrnamebuf + listsize; + endnamebuf = attrnamebuf + offset + (listsize > 0 ? listsize : 0); if (endnamebuf > (attrnamebuf + ATTR_MAX_HDR_SIZE)) { error = -1; goto exit; } + if (listsize > 0) + sort_xattrname_list(attrnamebuf, endnamebuf - attrnamebuf); + for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen) { namelen = strlen(nameptr) + 1; @@ -3225,12 +3692,34 @@ static int copyfile_pack(copyfile_state_t s) if (namelen > XATTR_MAXNAMELEN + 1) { namelen = XATTR_MAXNAMELEN + 1; } + if (s->statuscb) { + int rv; + char eaname[namelen]; + bcopy(nameptr, eaname, namelen); + eaname[namelen] = 0; // Just to be sure! + s->xattr_name = eaname; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + error = -1; + s->err = ECANCELED; + goto exit; + } else if (rv == COPYFILE_SKIP) { + size_t amt = endnamebuf - (nameptr + namelen); + memmove(nameptr, nameptr + namelen, amt); + endnamebuf -= namelen; + /* Set namelen to 0 so continue doesn't miss names */ + namelen = 0; + continue; + } + } entry->namelen = namelen; entry->flags = 0; if (nameptr + namelen > endnamebuf) { error = -1; goto exit; } + bcopy(nameptr, &entry->name[0], namelen); copyfile_debug(2, "copied name [%s]", entry->name); @@ -3265,7 +3754,7 @@ static int copyfile_pack(copyfile_state_t s) */ entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t)); - for (nameptr = attrnamebuf; nameptr < attrnamebuf + listsize; nameptr += namelen + 1) + for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen + 1) { namelen = strlen(nameptr); @@ -3278,9 +3767,49 @@ static int copyfile_pack(copyfile_state_t s) /* Check for Finder Info. */ else if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0) { + if (s->statuscb) + { + int rv; + s->xattr_name = (char*)XATTR_FINDERINFO_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_QUIT) + { + s->xattr_name = NULL; + s->err = ECANCELED; + error = -1; + goto exit; + } + else if (rv == COPYFILE_SKIP) + { + s->xattr_name = NULL; + continue; + } + s->totalCopied = 0; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) + { + s->err = ECANCELED; + error = -1; + goto exit; + } + } datasize = fgetxattr(s->src_fd, nameptr, (u_int8_t*)filehdr + filehdr->appledouble.entries[0].offset, 32, 0, 0); if (datasize < 0) { + if (s->statuscb) { + int rv; + s->xattr_name = strdup(nameptr); + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } + if (rv == COPYFILE_QUIT) { + error = -1; + goto exit; + } + } if (COPYFILE_VERBOSE & s->flags) copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno); } else if (datasize != 32) @@ -3292,6 +3821,19 @@ static int copyfile_pack(copyfile_state_t s) if (COPYFILE_VERBOSE & s->flags) copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x", XATTR_FINDERINFO_NAME, filehdr->appledouble.entries[0].offset); + if (s->statuscb) { + int rv; + s->xattr_name = strdup(nameptr); + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } + if (rv == COPYFILE_QUIT) { + error = -1; + goto exit; + } + } } continue; /* finder info doesn't have an attribute entry */ } @@ -3303,6 +3845,26 @@ static int copyfile_pack(copyfile_state_t s) } else { /* Just a normal attribute. */ + if (s->statuscb) + { + int rv; + s->xattr_name = strdup(nameptr); + s->totalCopied = 0; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } + /* + * Due to the nature of the packed file, we can't skip at this point. + */ + if (rv == COPYFILE_QUIT) + { + s->err = ECANCELED; + error = -1; + goto exit; + } + } datasize = fgetxattr(s->src_fd, nameptr, NULL, 0, 0, 0); if (datasize == 0) goto next; @@ -3310,6 +3872,22 @@ static int copyfile_pack(copyfile_state_t s) { if (COPYFILE_VERBOSE & s->flags) copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno); + if (s->statuscb) + { + int rv; + s->xattr_name = strdup(nameptr); + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } + if (rv == COPYFILE_QUIT) + { + s->err = ECANCELED; + error = -1; + goto exit; + } + } goto next; } if (datasize > XATTR_MAXATTRLEN) @@ -3324,6 +3902,20 @@ static int copyfile_pack(copyfile_state_t s) continue; } datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0); + if (s->statuscb) { + int rv; + s->xattr_name = strdup(nameptr); + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } + if (rv == COPYFILE_QUIT) { + s->err = ECANCELED; + error = -1; + goto exit; + } + } } entry->length = (u_int32_t)datasize; diff --git a/copyfile.h b/copyfile.h index 0cb493b..4029d21 100644 --- a/copyfile.h +++ b/copyfile.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2010 Apple, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -75,6 +75,7 @@ typedef int (*copyfile_callback_t)(int, int, copyfile_state_t, const char *, con #define COPYFILE_STATE_STATUS_CB 6 #define COPYFILE_STATE_STATUS_CTX 7 #define COPYFILE_STATE_COPIED 8 +#define COPYFILE_STATE_XATTRNAME 9 #define COPYFILE_DISABLE_VAR "COPYFILE_DISABLE" @@ -108,6 +109,7 @@ typedef int (*copyfile_callback_t)(int, int, copyfile_state_t, const char *, con #define COPYFILE_RECURSE_DIR 2 #define COPYFILE_RECURSE_DIR_CLEANUP 3 #define COPYFILE_COPY_DATA 4 +#define COPYFILE_COPY_XATTR 5 #define COPYFILE_START 1 #define COPYFILE_FINISH 2