···11+.\"
22+.\" Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
33+.\"
44+.Dd April 27, 2006
55+.Dt COPYFILE 3
66+.Os
77+.Sh NAME
88+.Nm copyfile , fcopyfile ,
99+.Nm copyfile_state_alloc , copyfile_state_free ,
1010+.Nm copyfile_state_get , copyfile_state_set
1111+.Nd copy a file
1212+.Sh LIBRARY
1313+.Lb libc
1414+.Sh SYNOPSIS
1515+.In copyfile.h
1616+.Ft int
1717+.Fn copyfile "const char *from" "const char *to" "copyfile_state_t state" "copyfile_flags_t flags"
1818+.Ft int
1919+.Fn fcopyfile "int from" "int to" "copyfile_state_t state" "copyfile_flags_t flags"
2020+.Ft copyfile_state_t
2121+.Fn copyfile_state_alloc "void"
2222+.Ft int
2323+.Fn copyfile_state_free "copyfile_state_t state"
2424+.Ft int
2525+.Fn copyfile_state_get "copyfile_state_t state" "uint32_t flag" "void * dst"
2626+.Ft int
2727+.Fn copyfile_state_set "copyfile_state_t state" "uint32_t flag" "const void * src"
2828+.Ft typedef int
2929+.Fn (*copyfile_callback_t) "int what" "int stage" "copyfile_state_t state" "const char * src" "const char * dst" "void * ctx"
3030+.Sh DESCRIPTION
3131+These functions are used to copy a file's data and/or metadata. (Metadata
3232+consists of permissions, extended attributes, access control lists, and so
3333+forth.)
3434+.Pp
3535+The
3636+.Fn copyfile_state_alloc
3737+function initializes a
3838+.Vt copyfile_state_t
3939+object (which is an opaque data type).
4040+This object can be passed to
4141+.Fn copyfile
4242+and
4343+.Fn fcopyfile ;
4444+.Fn copyfile_state_get
4545+and
4646+.Fn copyfile_state_set
4747+can be used to manipulate the state (see below).
4848+The
4949+.Fn copyfile_state_free
5050+function is used to deallocate the object and its contents.
5151+.Pp
5252+The
5353+.Fn copyfile
5454+function can copy the named
5555+.Va from
5656+file to the named
5757+.Va to
5858+file; the
5959+.Fn fcopyfile
6060+function does the same, but using the file descriptors of already-opened
6161+files.
6262+If the
6363+.Va state
6464+parameter is the return value from
6565+.Fn copyfile_state_alloc ,
6666+then
6767+.Fn copyfile
6868+and
6969+.Fn fcopyfile
7070+will use the information from the state object; if it is
7171+.Dv NULL ,
7272+then both functions will work normally, but less control will be available to the caller.
7373+The
7474+.Va flags
7575+parameter controls which contents are copied:
7676+.Bl -tag -width COPYFILE_XATTR
7777+.It Dv COPYFILE_ACL
7878+Copy the source file's access control lists.
7979+.It Dv COPYFILE_STAT
8080+Copy the source file's POSIX information (mode, modification time, etc.).
8181+.It Dv COPYFILE_XATTR
8282+Copy the source file's extended attributes.
8383+.It Dv COPYFILE_DATA
8484+Copy the source file's data.
8585+.El
8686+.Pp
8787+These values may be or'd together; several convenience macros are provided:
8888+.Bl -tag -width COPYFILE_SECURITY
8989+.It Dv COPYFILE_SECURITY
9090+Copy the source file's POSIX and ACL information; equivalent to
9191+.Dv (COPYFILE_STAT|COPYFILE_ACL) .
9292+.It Dv COPYFILE_METADATA
9393+Copy the metadata; equivalent to
9494+.Dv (COPYFILE_SECURITY|COPYFILE_XATTR) .
9595+.It Dv COPYFILE_ALL
9696+Copy the entire file; equivalent to
9797+.Dv (COPYFILE_METADATA|COPYFILE_DATA) .
9898+.El
9999+.Pp
100100+The
101101+.Fn copyfile
102102+and
103103+.Fn fcopyfile
104104+functions can also have their behavior modified by the following flags:
105105+.Bl -tag -width COPYFILE_NOFOLLOW_SRC
106106+.It Dv COPYFILE_RECURSIVE
107107+Causes
108108+.Fn copyfile
109109+to recursively copy a hierarchy.
110110+This flag is not used by
111111+.Fn fcopyfile ;
112112+see below for more information.
113113+.It Dv COPYFILE_CHECK
114114+Return a bitmask (corresponding to the
115115+.Va flags
116116+argument) indicating which contents would be copied; no data are actually
117117+copied. (E.g., if
118118+.Va flags
119119+was set to
120120+.Dv COPYFILE_CHECK|COPYFILE_METADATA ,
121121+and the
122122+.Va from
123123+file had extended attributes but no ACLs, the return value would be
124124+.Dv COPYFILE_XATTR .)
125125+.It Dv COPYFILE_PACK
126126+Serialize the
127127+.Va from
128128+file. The
129129+.Va to
130130+file is an AppleDouble-format file.
131131+.It Dv COPYFILE_UNPACK
132132+Unserialize the
133133+.Va from
134134+file. The
135135+.Va from
136136+file is an AppleDouble-format file; the
137137+.Va to
138138+file will have the extended attributes, ACLs, resource fork, and
139139+FinderInfo data from the
140140+.Va to
141141+file, regardless of the
142142+.Va flags
143143+argument passed in.
144144+.It Dv COPYFILE_EXCL
145145+Fail if the
146146+.Va to
147147+file already exists. (This is only applicable for the
148148+.Fn copyfile
149149+function.)
150150+.It Dv COPYFILE_NOFOLLOW_SRC
151151+Do not follow the
152152+.Va from
153153+file, if it is a symbolic link. (This is only applicable for the
154154+.Fn copyfile
155155+function.)
156156+.It Dv COPYFILE_NOFOLLOW_DST
157157+Do not follow the
158158+.Va to
159159+file, if it is a symbolic link. (This is only applicable for the
160160+.Fn copyfile
161161+function.)
162162+.It Dv COPYFILE_MOVE
163163+Unlink (using
164164+.Xr remove 3 )
165165+the
166166+.Fa from
167167+file. (This is only applicable for the
168168+.Fn copyfile
169169+function.) No error is returned if
170170+.Xr remove 3
171171+fails. Note that
172172+.Xr remove 3
173173+removes a symbolic link itself, not the
174174+target of the link.
175175+.It Dv COPYFILE_UNLINK
176176+Unlink the
177177+.Va to
178178+file before starting. (This is only applicable for the
179179+.Fn copyfile
180180+function.)
181181+.It Dv COPYFILE_NOFOLLOW
182182+This is a convenience macro, equivalent to
183183+.Dv (COPYFILE_NOFOLLOW_DST|COPYFILE_NOFOLLOW_SRC) .
184184+.El
185185+.Pp
186186+The
187187+.Fn copyfile_state_get
188188+and
189189+.Fn copyfile_state_set
190190+functions can be used to manipulate the
191191+.Ft copyfile_state_t
192192+object returned by
193193+.Fn copyfile_state_alloc .
194194+In both functions, the
195195+.Va dst
196196+parameter's type depends on the
197197+.Va flag
198198+parameter that is passed in.
199199+.Bl -tag -width COPYFILE_STATE_DST_FILENAME
200200+.It Dv COPYFILE_STATE_SRC_FD
201201+.It Dv COPYFILE_STATE_DST_FD
202202+Get or set the file descriptor associated with the source (or destination)
203203+file.
204204+If this has not been initialized yet, the value will be -2.
205205+The
206206+.Va dst
207207+(for
208208+.Fn copyfile_state_get )
209209+and
210210+.Va src
211211+(for
212212+.Fn copyfile_state_set )
213213+parameters are pointers to
214214+.Vt int .
215215+.It Dv COPYFILE_STATE_SRC_FILENAME
216216+.It Dv COPYFILE_STATE_DST_FILENAME
217217+Get or set the filename associated with the source (or destination)
218218+file. If it has not been initialized yet, the value will be
219219+.Dv NULL .
220220+For
221221+.Fn copyfile_state_set ,
222222+the
223223+.Va src
224224+parameter is a pointer to a C string
225225+(i.e.,
226226+.Vt char* );
227227+.Fn copyfile_state_set
228228+makes a private copy of this string.
229229+For
230230+.Fn copyfile_state_get
231231+function, the
232232+.Va dst
233233+parameter is a pointer to a pointer to a C string
234234+(i.e.,
235235+.Vt char** );
236236+the returned value is a pointer to the
237237+.Va state 's
238238+copy, and must not be modified or released.
239239+.It Dv COPYFILE_STATE_STATUS_CB
240240+Get or set the callback status function (currently
241241+only used for recursive copies; see below for details).
242242+The
243243+.Va src
244244+parameter is a pointer to a function of type
245245+.Vt copyfile_callback_t
246246+(see above).
247247+.It Dv COPYFILE_STATE_STATUS_CTX
248248+Get or set the context parameter for the status
249249+call-back function (see below for details).
250250+The
251251+.Va src
252252+parameter is a
253253+.Vt void\ * .
254254+.It Dv COPYFILE_STATE_QUARANTINE
255255+Get or set the quarantine information with the source file.
256256+The
257257+.Va src
258258+parameter is a pointer to an opaque
259259+object (type
260260+.Vt void\ *
261261+).
262262+.It Dv COPYFILE_STATE_COPIED
263263+Get the number of data bytes copied so far.
264264+(Only valid for
265265+.Fn copyfile_state_get ;
266266+see below for more details about callbacks.)
267267+The
268268+.Va dst
269269+parameter is a pointer to
270270+.Vt off_t
271271+(type
272272+.Vt off_t\ * ).
273273+.It Dv COPYFILE_STATE_XATTRNAME
274274+Get the name of the extended attribute during a callback
275275+for
276276+.Dv COPYFILE_COPY_XATTR
277277+(see below for details). This field cannot be set,
278278+and may be
279279+.Dv NULL .
280280+.El
281281+.Sh Recursive Copies
282282+When given the
283283+.Dv COPYFILE_RECURSIVE
284284+flag,
285285+.Fn copyfile
286286+(but not
287287+.Fn fcopyfile )
288288+will use the
289289+.Xr fts 3
290290+functions to recursively descend into the source file-system object.
291291+It then calls
292292+.Fn copyfile
293293+on each of the entries it finds that way.
294294+If a call-back function is given (using
295295+.Fn copyfile_state_set
296296+and
297297+.Dv COPYFILE_STATE_STATUS_CB ),
298298+the call-back function will be called four times for each directory
299299+object, and twice for all other objects. (Each directory will
300300+be examined twice, once on entry -- before copying each of the
301301+objects contained in the directory -- and once on exit -- after
302302+copying each object contained in the directory, in order to perform
303303+some final cleanup.)
304304+.Pp
305305+The call-back function will have one of the following values
306306+as the first argument, indicating what is being copied:
307307+.Bl -tag -width COPYFILE_RECURSE_DIR_CLEANUP
308308+.It Dv COPYFILE_RECURSE_FILE
309309+The object being copied is a file (or, rather,
310310+something other than a directory).
311311+.It Dv COPYFILE_RECURSE_DIR
312312+The object being copied is a directory, and is being
313313+entered. (That is, none of the filesystem objects contained
314314+within the directory have been copied yet.)
315315+.It Dv COPYFILE_RECURSE_DIR_CLEANUP
316316+The object being copied is a directory, and all of the
317317+objects contained have been copied. At this stage, the destination directory
318318+being copied will have any extra permissions that were added to
319319+allow the copying will be removed.
320320+.It Dv COPYFILE_RECURSE_ERROR
321321+There was an error in processing an element of the source hierarchy;
322322+this happens when
323323+.Xr fts 3
324324+returns an error or unknown file type.
325325+(Currently, the second argument to the call-back function will always
326326+be
327327+.Dv COPYFILE_ERR
328328+in this case.)
329329+.El
330330+.Pp
331331+The second argument to the call-back function will indicate
332332+the stage of the copy, and will be one of the following values:
333333+.Bl -tag -width COPYFILE_FINISH
334334+.It Dv COPYFILE_START
335335+Before copying has begun. The third
336336+parameter will be a newly-created
337337+.Vt copyfile_state_t
338338+object with the call-back function and context pre-loaded.
339339+.It Dv COPYFILE_FINISH
340340+After copying has successfully finished.
341341+.It Dv COPYFILE_ERR
342342+Indicates an error has happened at some stage. If the
343343+first argument to the call-back function is
344344+.Dv COPYFILE_RECURSE_ERROR ,
345345+then an error occurred while processing the source hierarchy;
346346+otherwise, it will indicate what type of object was being copied,
347347+and
348348+.Dv errno
349349+will be set to indicate the error.
350350+.El
351351+.Pp
352352+The fourth and fifth
353353+parameters are the source and destination paths that
354354+are to be copied (or have been copied, or failed to copy, depending on
355355+the second argument).
356356+.Pp
357357+The last argument to the call-back function will be the value
358358+set by
359359+.Dv COPYFILE_STATE_STATUS_CTX ,
360360+if any.
361361+.Pp
362362+The call-back function is required to return one of the following
363363+values:
364364+.Bl -tag -width COPYFILE_CONTINUE
365365+.It Dv COPYFILE_CONTINUE
366366+The copy will continue as expected.
367367+.It Dv COPYFILE_SKIP
368368+This object will be skipped, and the next object will
369369+be processed. (Note that, when entering a directory.
370370+returning
371371+.Dv COPYFILE_SKIP
372372+from the call-back function will prevent the contents
373373+of the directory from being copied.)
374374+.It Dv COPYFILE_QUIT
375375+The entire copy is aborted at this stage. Any filesystem
376376+objects created up to this point will remain.
377377+.Fn copyfile
378378+will return -1, but
379379+.Dv errno
380380+will be unmodified.
381381+.El
382382+.Pp
383383+The call-back function must always return one of the values listed
384384+above; if not, the results are undefined.
385385+.Pp
386386+The call-back function will be called twice for each object
387387+(and an additional two times for directory cleanup); the first
388388+call will have a
389389+.Ar stage
390390+parameter of
391391+.Dv COPYFILE_START ;
392392+the second time, that value will be either
393393+.Dv COPYFILE_FINISH
394394+or
395395+.Dv COPYFILE_ERR
396396+to indicate a successful completion, or an error during
397397+processing.
398398+In the event of an error, the
399399+.Dv errno
400400+value will be set appropriately.
401401+.Pp
402402+The
403403+.Dv COPYFILE_PACK ,
404404+.Dv COPYFILE_UNPACK ,
405405+.Dv COPYFILE_MOVE ,
406406+and
407407+.Dv COPYFILE_UNLINK
408408+flags are not used during a recursive copy, and will result
409409+in an error being returned.
410410+.Sh Progress Callback
411411+In addition to the recursive callbacks described above,
412412+.Fn copyfile
413413+and
414414+.Fn fcopyfile
415415+will also use a callback to report data (e.g.,
416416+.Dv COPYFILE_DATA )
417417+progress. If given, the callback will be invoked on each
418418+.Xr write 2
419419+call. The first argument to the callback function will be
420420+.Dv COPYFILE_COPY_DATA .
421421+The second argument will either be
422422+.Dv COPYFILE_PROGRESS
423423+(indicating that the write was successful), or
424424+.Dv COPYFILE_ERR
425425+(indicating that there was an error of some sort).
426426+.Pp
427427+The amount of data bytes copied so far can be retrieved using
428428+.Fn copyfile_state_get ,
429429+with the
430430+.Dv COPYFILE_STATE_COPIED
431431+requestor (the argument type is a pointer to
432432+.Vt off_t ).
433433+.Pp
434434+When copying extended attributes, the first argument to the
435435+callback function will be
436436+.Dv COPYFILE_COPY_XATTR .
437437+The other arguments will be as described for
438438+.Dv COPYFILE_COPY_DATA ;
439439+the name of the extended attribute being copied may be
440440+retrieved using
441441+.Fn copyfile_state_get
442442+and the parameter
443443+.Dv COPYFILE_STATE_XATTRNAME .
444444+When using
445445+.Dv COPYFILE_PACK ,
446446+the callback may be called with
447447+.Dv COPYFILE_START
448448+for each of the extended attributes first, followed by
449449+.Dv COPYFILE_PROGRESS
450450+before getting and packing the data for each
451451+individual attribute, and then
452452+.Dv COPYFILE_FINISH
453453+when finished with each individual attribute.
454454+(That is,
455455+.Dv COPYFILE_START
456456+may be called for all of the extended attributes, before
457457+the first callback with
458458+.Dv COPYFILE_PROGRESS
459459+is invoked.) Any attribute skipped by returning
460460+.Dv COPYFILE_SKIP
461461+from the
462462+.Dv COPYFILE_START
463463+callback will not be placed into the packed output file.
464464+.Pp
465465+The return value for the data callback must be one of
466466+.Bl -tag -width COPYFILE_CONTINUE
467467+.It Dv COPYFILE_CONTINUE
468468+The copy will continue as expected.
469469+(In the case of error, it will attempt to write the data again.)
470470+.It Dv COPYFILE_SKIP
471471+The data copy will be aborted, but without error.
472472+.It Dv COPYFILE_QUIT
473473+The data copy will be aborted; in the case of
474474+.Dv COPYFILE_PROGRESS ,
475475+.Dv errno
476476+will be set to
477477+.Dv ECANCELED .
478478+.El
479479+.Pp
480480+While the
481481+.Va src
482482+and
483483+.Va dst
484484+parameters will be passed in, they may be
485485+.Dv NULL
486486+in the case of
487487+.Fn fcopyfile .
488488+.Sh RETURN VALUES
489489+Except when given the
490490+.Dv COPYFILE_CHECK
491491+flag,
492492+.Fn copyfile
493493+and
494494+.Fn fcopyfile
495495+return less than 0 on error, and 0 on success.
496496+All of the other functions return 0 on success, and less than 0
497497+on error.
498498+.Sh WARNING
499499+Both
500500+.Fn copyfile
501501+and
502502+.Fn fcopyfile
503503+can copy symbolic links; there is a gap between when the source
504504+link is examined and the actual copy is started, and this can
505505+be a potential security risk, especially if the process has
506506+elevated privileges.
507507+.Pp
508508+When performing a recursive copy, if the source hierarchy
509509+changes while the copy is occurring, the results are undefined.
510510+.Pp
511511+.Fn fcopyfile
512512+does not reset the seek position for either source or destination.
513513+This can result in the destination file being a different size
514514+than the source file.
515515+.Sh ERRORS
516516+.Fn copyfile
517517+and
518518+.Fn fcopyfile
519519+will fail if:
520520+.Bl -tag -width Er
521521+.It Bq Er EINVAL
522522+An invalid flag was passed in with
523523+.Dv COPYFILE_RECURSIVE .
524524+.It Bq Er EINVAL
525525+The
526526+.Va from
527527+or
528528+.Va to
529529+parameter to
530530+.Fn copyfile
531531+was a
532532+.Dv NULL
533533+pointer.
534534+.It Bq Er EINVAL
535535+The
536536+.Va from
537537+or
538538+.Va to
539539+parameter to
540540+.Fn copyfile
541541+was a negative number.
542542+.It Bq Er ENOMEM
543543+A memory allocation failed.
544544+.It Bq Er ENOTSUP
545545+The source file was not a directory, symbolic link, or regular file.
546546+.It Bq Er ECANCELED
547547+The copy was cancelled by callback.
548548+.El
549549+In addition, both functions may set
550550+.Dv errno
551551+via an underlying library or system call.
552552+.Sh EXAMPLES
553553+.Bd -literal -offset indent
554554+/* Initialize a state variable */
555555+copyfile_state_t s;
556556+s = copyfile_state_alloc();
557557+/* Copy the data and extended attributes of one file to another */
558558+copyfile("/tmp/f1", "/tmp/f2", s, COPYFILE_DATA | COPYFILE_XATTR);
559559+/* Convert a file to an AppleDouble file for serialization */
560560+copyfile("/tmp/f2", "/tmp/tmpfile", NULL, COPYFILE_ALL | COPYFILE_PACK);
561561+/* Release the state variable */
562562+copyfile_state_free(s);
563563+/* A more complex way to call copyfile() */
564564+s = copyfile_state_alloc();
565565+copyfile_state_set(s, COPYFILE_STATE_SRC_FILENAME, "/tmp/foo");
566566+/* One of src or dst must be set... rest can come from the state */
567567+copyfile(NULL, "/tmp/bar", s, COPYFILE_ALL);
568568+/* Now copy the same source file to another destination file */
569569+copyfile(NULL, "/tmp/car", s, COPYFILE_ALL);
570570+copyfile_state_free(s);
571571+/* Remove extended attributes from a file */
572572+copyfile("/dev/null", "/tmp/bar", NULL, COPYFILE_XATTR);
573573+.Ed
574574+.Sh SEE ALSO
575575+.Xr listxattr 2 ,
576576+.Xr getxattr 2 ,
577577+.Xr setxattr 2 ,
578578+.Xr acl 3
579579+.Sh BUGS
580580+Both
581581+.Fn copyfile
582582+functions lack a way to set the input or output block size.
583583+.Pp
584584+Recursive copies do not honor hard links.
585585+.Sh HISTORY
586586+The
587587+.Fn copyfile
588588+API was introduced in Mac OS X 10.5.
+4156
src/copyfile/copyfile.c
···11+// Modified by Lubos Dolezel for Darling
22+/*
33+ * Copyright (c) 2004-2010 Apple, Inc. All rights reserved.
44+ *
55+ * @APPLE_LICENSE_HEADER_START@
66+ *
77+ * This file contains Original Code and/or Modifications of Original Code
88+ * as defined in and that are subject to the Apple Public Source License
99+ * Version 2.0 (the 'License'). You may not use this file except in
1010+ * compliance with the License. Please obtain a copy of the License at
1111+ * http://www.opensource.apple.com/apsl/ and read it before using this
1212+ * file.
1313+ *
1414+ * The Original Code and all software distributed under the License are
1515+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1616+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1717+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1818+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1919+ * Please see the License for the specific language governing rights and
2020+ * limitations under the License.
2121+ *
2222+ * @APPLE_LICENSE_HEADER_END@
2323+ */
2424+2525+#include <err.h>
2626+#include <errno.h>
2727+#include <sys/types.h>
2828+#include <sys/acl.h>
2929+#include <stdio.h>
3030+#include <stdlib.h>
3131+#include <string.h>
3232+#include <stdint.h>
3333+#include <syslog.h>
3434+#include <unistd.h>
3535+#include <fcntl.h>
3636+#include <sys/errno.h>
3737+#include <sys/stat.h>
3838+#include <sys/time.h>
3939+#include <sys/xattr.h>
4040+#include <sys/attr.h>
4141+#include <sys/syscall.h>
4242+#include <sys/param.h>
4343+#include <sys/mount.h>
4444+#include <sys/acl.h>
4545+#include <libkern/OSByteOrder.h>
4646+#include <membership.h>
4747+#include <fts.h>
4848+#include <libgen.h>
4949+5050+#ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION
5151+# include <Kernel/sys/decmpfs.h>
5252+#endif
5353+5454+#include <TargetConditionals.h>
5555+#if !TARGET_OS_IPHONE && !defined(DARLING)
5656+#include <quarantine.h>
5757+5858+#define XATTR_QUARANTINE_NAME qtn_xattr_name
5959+#else /* TARGET_OS_IPHONE */
6060+#define qtn_file_t void *
6161+#define QTN_SERIALIZED_DATA_MAX 0
6262+static void * qtn_file_alloc(void) { return NULL; }
6363+static int qtn_file_init_with_fd(void *x, int y) { return -1; }
6464+static int qtn_file_init_with_path(void *x, const char *path) { return -1; }
6565+static int qtn_file_init_with_data(void *x, const void *data, size_t len) { return -1; }
6666+static void qtn_file_free(void *x) { return; }
6767+static int qtn_file_apply_to_fd(void *x, int y) { return 0; }
6868+static char *qtn_error(int x) { return NULL; }
6969+static int qtn_file_to_data(void *x, char *y, size_t z) { return -1; }
7070+static void *qtn_file_clone(void *x) { return NULL; }
7171+#define XATTR_QUARANTINE_NAME "figgledidiggledy"
7272+#endif /* TARGET_OS_IPHONE */
7373+7474+#include "copyfile.h"
7575+#include "copyfile_private.h"
7676+#include "xattr_flags.h"
7777+7878+enum cfInternalFlags {
7979+ cfDelayAce = 1 << 0,
8080+ cfMakeFileInvisible = 1 << 1,
8181+ cfSawDecmpEA = 1 << 2,
8282+};
8383+8484+/*
8585+ * The state structure keeps track of
8686+ * the source filename, the destination filename, their
8787+ * associated file-descriptors, the stat infomration for the
8888+ * source file, the security information for the source file,
8989+ * the flags passed in for the copy, a pointer to place statistics
9090+ * (not currently implemented), debug flags, and a pointer to callbacks
9191+ * (not currently implemented).
9292+ */
9393+struct _copyfile_state
9494+{
9595+ char *src;
9696+ char *dst;
9797+ int src_fd;
9898+ int dst_fd;
9999+ struct stat sb;
100100+ filesec_t fsec;
101101+ copyfile_flags_t flags;
102102+ unsigned int internal_flags;
103103+ void *stats;
104104+ uint32_t debug;
105105+ copyfile_callback_t statuscb;
106106+ void *ctx;
107107+ qtn_file_t qinfo; /* Quarantine information -- probably NULL */
108108+ filesec_t original_fsec;
109109+ filesec_t permissive_fsec;
110110+ off_t totalCopied;
111111+ int err;
112112+ char *xattr_name;
113113+ xattr_operation_intent_t copyIntent;
114114+};
115115+116116+struct acl_entry {
117117+ u_int32_t ae_magic;
118118+#define _ACL_ENTRY_MAGIC 0xac1ac101
119119+ u_int32_t ae_tag;
120120+ guid_t ae_applicable;
121121+ u_int32_t ae_flags;
122122+ u_int32_t ae_perms;
123123+};
124124+125125+#define PACE(ace) do { \
126126+ struct acl_entry *__t = (struct acl_entry*)(ace); \
127127+ fprintf(stderr, "%s(%d): " #ace " = { flags = %#x, perms = %#x }\n", __FUNCTION__, __LINE__, __t->ae_flags, __t->ae_perms); \
128128+ } while (0)
129129+130130+#define PACL(ace) \
131131+ do { \
132132+ ssize_t __l; char *__cp = acl_to_text(ace, &__l); \
133133+ fprintf(stderr, "%s(%d): " #ace " = %s\n", __FUNCTION__, __LINE__, __cp ? __cp : "(null)"); \
134134+ } while (0)
135135+136136+static int
137137+acl_compare_permset_np(acl_permset_t p1, acl_permset_t p2)
138138+{
139139+ struct pm { u_int32_t ap_perms; } *ps1, *ps2;
140140+ ps1 = (struct pm*) p1;
141141+ ps2 = (struct pm*) p2;
142142+143143+ return ((ps1->ap_perms == ps2->ap_perms) ? 1 : 0);
144144+}
145145+146146+147147+static int
148148+doesdecmpfs(int fd) {
149149+#ifdef DECMPFS_XATTR_NAME
150150+ int rv;
151151+ struct attrlist attrs;
152152+ char volroot[MAXPATHLEN + 1];
153153+ struct statfs sfs;
154154+ struct {
155155+ uint32_t length;
156156+ vol_capabilities_attr_t volAttrs;
157157+ } volattrs;
158158+159159+ (void)fstatfs(fd, &sfs);
160160+ strlcpy(volroot, sfs.f_mntonname, sizeof(volroot));
161161+162162+ memset(&attrs, 0, sizeof(attrs));
163163+ attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
164164+ attrs.volattr = ATTR_VOL_CAPABILITIES;
165165+166166+ rv = getattrlist(volroot, &attrs, &volattrs, sizeof(volattrs), 0);
167167+168168+ if (rv != -1 &&
169169+ (volattrs.volAttrs.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_DECMPFS_COMPRESSION) &&
170170+ (volattrs.volAttrs.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_DECMPFS_COMPRESSION)) {
171171+ return 1;
172172+ }
173173+#endif
174174+ return 0;
175175+}
176176+177177+178178+static void
179179+sort_xattrname_list(void *start, size_t length)
180180+{
181181+ char **ptrs = NULL;
182182+ int nel;
183183+ char *tmp;
184184+ int indx = 0;
185185+186186+ /* If it's not a proper C string at the end, don't do anything */
187187+ if (((char*)start)[length] != 0)
188188+ return;
189189+ /*
190190+ * In order to sort the list of names, we need to
191191+ * make a list of pointers to strings. To do that,
192192+ * we need to run through the buffer, and find the
193193+ * beginnings of strings.
194194+ */
195195+ nel = 10; // Most files don't have many EAs
196196+ ptrs = (char**)calloc(nel, sizeof(char*));
197197+198198+ if (ptrs == NULL)
199199+ goto done;
200200+201201+#ifdef DEBUG
202202+{
203203+ char *curPtr = start;
204204+ while (curPtr < (char*)start + length) {
205205+ printf("%s\n", curPtr);
206206+ curPtr += strlen(curPtr) + 1;
207207+ }
208208+}
209209+#endif
210210+211211+ tmp = ptrs[indx++] = (char*)start;
212212+213213+ while (tmp = memchr(tmp, 0, ((char*)start + length) - tmp)) {
214214+ if (indx == nel) {
215215+ nel += 10;
216216+ ptrs = realloc(ptrs, sizeof(char**) * nel);
217217+ if (ptrs == NULL)
218218+ goto done;
219219+ }
220220+ ptrs[indx++] = ++tmp;
221221+ }
222222+#ifdef DEBUG
223223+ printf("Unsorted:\n");
224224+ for (nel = 0; nel < indx-1; nel++) {
225225+ printf("\tEA %d = `%s'\n", nel, ptrs[nel]);
226226+ }
227227+#endif
228228+ qsort_b(ptrs, indx-1, sizeof(char*), ^(const void *left, const void *right) {
229229+ int rv;
230230+ char *lstr = *(char**)left, *rstr = *(char**)right;
231231+ rv = strcmp(lstr, rstr);
232232+ return rv;
233233+ });
234234+#ifdef DEBUG
235235+ printf("Sorted:\n");
236236+ for (nel = 0; nel < indx-1; nel++) {
237237+ printf("\tEA %d = `%s'\n", nel, ptrs[nel]);
238238+ }
239239+#endif
240240+ /*
241241+ * Now that it's sorted, we need to make a copy, so we can
242242+ * move the strings around into the new order. Then we
243243+ * copy that on top of the old buffer, and we're done.
244244+ */
245245+ char *copy = malloc(length);
246246+ if (copy) {
247247+ int i;
248248+ char *curPtr = copy;
249249+250250+ for (i = 0; i < indx-1; i++) {
251251+ size_t len = strlen(ptrs[i]);
252252+ memcpy(curPtr, ptrs[i], len+1);
253253+ curPtr += len+1;
254254+ }
255255+ memcpy(start, copy, length);
256256+ free(copy);
257257+ }
258258+259259+done:
260260+ if (ptrs)
261261+ free(ptrs);
262262+ return;
263263+}
264264+265265+/*
266266+ * Internally, the process is broken into a series of
267267+ * private functions.
268268+ */
269269+static int copyfile_open (copyfile_state_t);
270270+static int copyfile_close (copyfile_state_t);
271271+static int copyfile_data (copyfile_state_t);
272272+static int copyfile_stat (copyfile_state_t);
273273+static int copyfile_security (copyfile_state_t);
274274+static int copyfile_xattr (copyfile_state_t);
275275+static int copyfile_pack (copyfile_state_t);
276276+static int copyfile_unpack (copyfile_state_t);
277277+278278+static copyfile_flags_t copyfile_check (copyfile_state_t);
279279+static filesec_t copyfile_fix_perms(copyfile_state_t, filesec_t *);
280280+static int copyfile_preamble(copyfile_state_t *s, copyfile_flags_t flags);
281281+static int copyfile_internal(copyfile_state_t state, copyfile_flags_t flags);
282282+static int copyfile_unset_posix_fsec(filesec_t);
283283+static int copyfile_quarantine(copyfile_state_t);
284284+285285+#define COPYFILE_DEBUG (1<<31)
286286+#define COPYFILE_DEBUG_VAR "COPYFILE_DEBUG"
287287+288288+#ifndef _COPYFILE_TEST
289289+# define copyfile_warn(str, ...) syslog(LOG_WARNING, str ": %m", ## __VA_ARGS__)
290290+# define copyfile_debug(d, str, ...) \
291291+ do { \
292292+ if (s && (d <= s->debug)) {\
293293+ syslog(LOG_DEBUG, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \
294294+ } \
295295+ } while (0)
296296+#else
297297+#define copyfile_warn(str, ...) \
298298+ fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : "")
299299+# define copyfile_debug(d, str, ...) \
300300+ do { \
301301+ if (s && (d <= s->debug)) {\
302302+ fprintf(stderr, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \
303303+ } \
304304+ } while(0)
305305+#endif
306306+307307+#ifndef DARLING
308308+static int copyfile_quarantine(copyfile_state_t s)
309309+{
310310+ int rv = 0;
311311+ if (s->qinfo == NULL)
312312+ {
313313+ int error;
314314+ s->qinfo = qtn_file_alloc();
315315+ if (s->qinfo == NULL)
316316+ {
317317+ rv = -1;
318318+ goto done;
319319+ }
320320+ if ((error = qtn_file_init_with_fd(s->qinfo, s->src_fd)) != 0)
321321+ {
322322+ qtn_file_free(s->qinfo);
323323+ s->qinfo = NULL;
324324+ rv = -1;
325325+ goto done;
326326+ }
327327+ }
328328+done:
329329+ return rv;
330330+}
331331+#else
332332+static int copyfile_quarantine(copyfile_state_t s) { return 0; }
333333+#endif
334334+335335+static int
336336+add_uberace(acl_t *acl)
337337+{
338338+ acl_entry_t entry;
339339+ acl_permset_t permset;
340340+ uuid_t qual;
341341+342342+ if (mbr_uid_to_uuid(getuid(), qual) != 0)
343343+ goto error_exit;
344344+345345+ /*
346346+ * First, we create an entry, and give it the special name
347347+ * of ACL_FIRST_ENTRY, thus guaranteeing it will be first.
348348+ * After that, we clear out all the permissions in it, and
349349+ * add three permissions: WRITE_DATA, WRITE_ATTRIBUTES, and
350350+ * WRITE_EXTATTRIBUTES. We put these into an ACE that allows
351351+ * the functionality, and put this into the ACL.
352352+ */
353353+ if (acl_create_entry_np(acl, &entry, ACL_FIRST_ENTRY) == -1)
354354+ goto error_exit;
355355+ if (acl_get_permset(entry, &permset) == -1) {
356356+ copyfile_warn("acl_get_permset");
357357+ goto error_exit;
358358+ }
359359+ if (acl_clear_perms(permset) == -1) {
360360+ copyfile_warn("acl_clear_permset");
361361+ goto error_exit;
362362+ }
363363+ if (acl_add_perm(permset, ACL_WRITE_DATA) == -1) {
364364+ copyfile_warn("add ACL_WRITE_DATA");
365365+ goto error_exit;
366366+ }
367367+ if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1) {
368368+ copyfile_warn("add ACL_WRITE_ATTRIBUTES");
369369+ goto error_exit;
370370+ }
371371+ if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1) {
372372+ copyfile_warn("add ACL_WRITE_EXTATTRIBUTES");
373373+ goto error_exit;
374374+ }
375375+ if (acl_add_perm(permset, ACL_APPEND_DATA) == -1) {
376376+ copyfile_warn("add ACL_APPEND_DATA");
377377+ goto error_exit;
378378+ }
379379+ if (acl_add_perm(permset, ACL_WRITE_SECURITY) == -1) {
380380+ copyfile_warn("add ACL_WRITE_SECURITY");
381381+ goto error_exit;
382382+ }
383383+ if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1) {
384384+ copyfile_warn("set ACL_EXTENDED_ALLOW");
385385+ goto error_exit;
386386+ }
387387+388388+ if(acl_set_permset(entry, permset) == -1) {
389389+ copyfile_warn("acl_set_permset");
390390+ goto error_exit;
391391+ }
392392+ if(acl_set_qualifier(entry, qual) == -1) {
393393+ copyfile_warn("acl_set_qualifier");
394394+ goto error_exit;
395395+ }
396396+397397+ return 0;
398398+error_exit:
399399+ return -1;
400400+}
401401+402402+static int
403403+is_uberace(acl_entry_t ace)
404404+{
405405+ int retval = 0;
406406+ acl_permset_t perms, tperms;
407407+ acl_t tacl;
408408+ acl_entry_t tentry;
409409+ acl_tag_t tag;
410410+ guid_t *qual = NULL;
411411+ uuid_t myuuid;
412412+413413+ // Who am I, and who is the ACE for?
414414+ mbr_uid_to_uuid(geteuid(), myuuid);
415415+ qual = (guid_t*)acl_get_qualifier(ace);
416416+417417+ // Need to create a temporary acl, so I can get the uberace template.
418418+ tacl = acl_init(1);
419419+ if (tacl == NULL) {
420420+ goto done;
421421+ }
422422+ add_uberace(&tacl);
423423+ if (acl_get_entry(tacl, ACL_FIRST_ENTRY, &tentry) != 0) {
424424+ goto done;
425425+ }
426426+ acl_get_permset(tentry, &tperms);
427427+428428+ // Now I need to get
429429+ acl_get_tag_type(ace, &tag);
430430+ acl_get_permset(ace, &perms);
431431+432432+ if (tag == ACL_EXTENDED_ALLOW &&
433433+ (memcmp(qual, myuuid, sizeof(myuuid)) == 0) &&
434434+ acl_compare_permset_np(tperms, perms))
435435+ retval = 1;
436436+437437+done:
438438+439439+ if (qual)
440440+ acl_free(qual);
441441+442442+ if (tacl)
443443+ acl_free(tacl);
444444+445445+ return retval;
446446+}
447447+448448+static void
449449+remove_uberace(int fd, struct stat *sbuf)
450450+{
451451+ filesec_t fsec = NULL;
452452+ acl_t acl = NULL;
453453+ acl_entry_t entry;
454454+ struct stat sb;
455455+456456+ fsec = filesec_init();
457457+ if (fsec == NULL) {
458458+ goto noacl;
459459+ }
460460+461461+ if (fstatx_np(fd, &sb, fsec) != 0) {
462462+ if (errno == ENOTSUP)
463463+ goto noacl;
464464+ goto done;
465465+ }
466466+467467+ if (filesec_get_property(fsec, FILESEC_ACL, &acl) != 0) {
468468+ goto done;
469469+ }
470470+471471+ if (acl_get_entry(acl, ACL_FIRST_ENTRY, &entry) == 0) {
472472+ if (is_uberace(entry))
473473+ {
474474+ mode_t m = sbuf->st_mode & ~S_IFMT;
475475+476476+ if (acl_delete_entry(acl, entry) != 0 ||
477477+ filesec_set_property(fsec, FILESEC_ACL, &acl) != 0 ||
478478+ filesec_set_property(fsec, FILESEC_MODE, &m) != 0 ||
479479+ fchmodx_np(fd, fsec) != 0)
480480+ goto noacl;
481481+ }
482482+ }
483483+484484+done:
485485+ if (acl)
486486+ acl_free(acl);
487487+ if (fsec)
488488+ filesec_free(fsec);
489489+ return;
490490+491491+noacl:
492492+ fchmod(fd, sbuf->st_mode & ~S_IFMT);
493493+ goto done;
494494+}
495495+496496+static void
497497+reset_security(copyfile_state_t s)
498498+{
499499+ /* If we haven't reset the file security information
500500+ * (COPYFILE_SECURITY is not set in flags)
501501+ * restore back the permissions the file had originally
502502+ *
503503+ * One of the reasons this seems so complicated is that
504504+ * it is partially at odds with copyfile_security().
505505+ *
506506+ * Simplisticly, we are simply trying to make sure we
507507+ * only copy what was requested, and that we don't stomp
508508+ * on what wasn't requested.
509509+ */
510510+511511+#ifdef COPYFILE_RECURSIVE
512512+ if (s->dst_fd > -1) {
513513+ struct stat sbuf;
514514+515515+ if (s->src_fd > -1 && (s->flags & COPYFILE_STAT))
516516+ fstat(s->src_fd, &sbuf);
517517+ else
518518+ fstat(s->dst_fd, &sbuf);
519519+520520+ if (!(s->internal_flags & cfDelayAce))
521521+ remove_uberace(s->dst_fd, &sbuf);
522522+ }
523523+#else
524524+ if (s->permissive_fsec && (s->flags & COPYFILE_SECURITY) != COPYFILE_SECURITY) {
525525+ if (s->flags & COPYFILE_ACL) {
526526+ /* Just need to reset the BSD information -- mode, owner, group */
527527+ (void)fchown(s->dst_fd, s->dst_sb.st_uid, s->dst_sb.st_gid);
528528+ (void)fchmod(s->dst_fd, s->dst_sb.st_mode);
529529+ } else {
530530+ /*
531531+ * flags is either COPYFILE_STAT, or neither; if it's
532532+ * neither, then we restore both ACL and POSIX permissions;
533533+ * if it's STAT, however, then we only want to restore the
534534+ * ACL (which may be empty). We do that by removing the
535535+ * POSIX information from the filesec object.
536536+ */
537537+ if (s->flags & COPYFILE_STAT) {
538538+ copyfile_unset_posix_fsec(s->original_fsec);
539539+ }
540540+ if (fchmodx_np(s->dst_fd, s->original_fsec) < 0 && errno != ENOTSUP)
541541+ copyfile_warn("restoring security information");
542542+ }
543543+ }
544544+545545+ if (s->permissive_fsec) {
546546+ filesec_free(s->permissive_fsec);
547547+ s->permissive_fsec = NULL;
548548+ }
549549+550550+ if (s->original_fsec) {
551551+ filesec_free(s->original_fsec);
552552+ s->original_fsec = NULL;
553553+ }
554554+#endif
555555+556556+ return;
557557+}
558558+559559+/*
560560+ * copytree -- recursively copy a hierarchy.
561561+ *
562562+ * Unlike normal copyfile(), copytree() can copy an entire hierarchy.
563563+ * Care is taken to keep the ACLs set up correctly, in addition to the
564564+ * normal copying that is done. (When copying a hierarchy, we can't
565565+ * get rid of the "allow-all-writes" ACE on a directory until we're done
566566+ * copying the *contents* of the directory.)
567567+ *
568568+ * The other big difference from copyfile (for the moment) is that copytree()
569569+ * will use a call-back function to pass along information about what is
570570+ * about to be copied, and whether or not it succeeded.
571571+ *
572572+ * copytree() is called from copyfile() -- but copytree() itself then calls
573573+ * copyfile() to copy each individual object.
574574+ *
575575+ * XXX - no effort is made to handle overlapping hierarchies at the moment.
576576+ *
577577+ */
578578+579579+static int
580580+copytree(copyfile_state_t s)
581581+{
582582+ char *slash;
583583+ int retval = 0;
584584+ int (*sfunc)(const char *, struct stat *);
585585+ copyfile_callback_t status = NULL;
586586+ char srcisdir = 0, dstisdir = 0, dstexists = 0;
587587+ struct stat sbuf;
588588+ char *src, *dst;
589589+ const char *dstpathsep = "";
590590+#ifdef NOTYET
591591+ char srcpath[PATH_MAX * 2 + 1], dstpath[PATH_MAX * 2 + 1];
592592+#endif
593593+ char *srcroot;
594594+ FTS *fts = NULL;
595595+ FTSENT *ftsent;
596596+ ssize_t offset = 0;
597597+ const char *paths[2] = { 0 };
598598+ unsigned int flags = 0;
599599+ int fts_flags = FTS_NOCHDIR;
600600+601601+ if (s == NULL) {
602602+ errno = EINVAL;
603603+ retval = -1;
604604+ goto done;
605605+ }
606606+ if (s->flags & (COPYFILE_MOVE | COPYFILE_UNLINK | COPYFILE_CHECK | COPYFILE_PACK | COPYFILE_UNPACK)) {
607607+ errno = EINVAL;
608608+ retval = -1;
609609+ goto done;
610610+ }
611611+612612+ flags = s->flags & (COPYFILE_ALL | COPYFILE_NOFOLLOW | COPYFILE_VERBOSE);
613613+614614+ paths[0] = src = s->src;
615615+ dst = s->dst;
616616+617617+ if (src == NULL || dst == NULL) {
618618+ errno = EINVAL;
619619+ retval = -1;
620620+ goto done;
621621+ }
622622+623623+ sfunc = (flags & COPYFILE_NOFOLLOW_SRC) ? lstat : stat;
624624+ if ((sfunc)(src, &sbuf) == -1) {
625625+ retval = -1;
626626+ goto done;
627627+ }
628628+ if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
629629+ srcisdir = 1;
630630+ }
631631+632632+ sfunc = (flags & COPYFILE_NOFOLLOW_DST) ? lstat : stat;
633633+ if ((sfunc)(dst, &sbuf) == -1) {
634634+ if (errno != ENOENT) {
635635+ retval = -1;
636636+ goto done;
637637+ }
638638+ } else {
639639+ dstexists = 1;
640640+ if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
641641+ dstisdir = 1;
642642+ }
643643+ }
644644+645645+#ifdef NOTYET
646646+ // This doesn't handle filesystem crossing and case sensitivity
647647+ // So there's got to be a better way
648648+649649+ if (realpath(src, srcpath) == NULL) {
650650+ retval = -1;
651651+ goto done;
652652+ }
653653+654654+ if (realpath(dst, dstpath) == NULL &&
655655+ (errno == ENOENT && realpath(dirname(dst), dstpath) == NULL)) {
656656+ retval = -1;
657657+ goto done;
658658+ }
659659+ if (strstr(srcpath, dstpath) != NULL) {
660660+ errno = EINVAL;
661661+ retval = -1;
662662+ goto done;
663663+ }
664664+#endif
665665+ srcroot = basename((char*)src);
666666+ if (srcroot == NULL) {
667667+ retval = -1;
668668+ goto done;
669669+ }
670670+671671+ /*
672672+ * To work on as well:
673673+ * We have a few cases when copying a hierarchy:
674674+ * 1) src is a non-directory, dst is a directory;
675675+ * 2) src is a non-directory, dst is a non-directory;
676676+ * 3) src is a non-directory, dst does not exist;
677677+ * 4) src is a directory, dst is a directory;
678678+ * 5) src is a directory, dst is a non-directory;
679679+ * 6) src is a directory, dst does not exist
680680+ *
681681+ * (1) copies src to dst/basename(src).
682682+ * (2) fails if COPYFILE_EXCLUSIVE is set, otherwise copies src to dst.
683683+ * (3) and (6) copy src to the name dst.
684684+ * (4) copies the contents of src to the contents of dst.
685685+ * (5) is an error.
686686+ */
687687+688688+ if (dstisdir) {
689689+ // copy /path/to/src to /path/to/dst/src
690690+ // Append "/" and (fts_path - strlen(basename(src))) to dst?
691691+ dstpathsep = "/";
692692+ slash = strrchr(src, '/');
693693+ if (slash == NULL)
694694+ offset = 0;
695695+ else
696696+ offset = slash - src + 1;
697697+ } else {
698698+ // copy /path/to/src to /path/to/dst
699699+ // append (fts_path + strlen(src)) to dst?
700700+ dstpathsep = "";
701701+ offset = strlen(src);
702702+ }
703703+704704+ if (s->flags | COPYFILE_NOFOLLOW_SRC)
705705+ fts_flags |= FTS_PHYSICAL;
706706+ else
707707+ fts_flags |= FTS_LOGICAL;
708708+709709+ fts = fts_open((char * const *)paths, fts_flags, NULL);
710710+711711+ status = s->statuscb;
712712+ while ((ftsent = fts_read(fts)) != NULL) {
713713+ int rv = 0;
714714+ char *dstfile = NULL;
715715+ int cmd = 0;
716716+ copyfile_state_t tstate = copyfile_state_alloc();
717717+ if (tstate == NULL) {
718718+ errno = ENOMEM;
719719+ retval = -1;
720720+ break;
721721+ }
722722+ tstate->statuscb = s->statuscb;
723723+ tstate->ctx = s->ctx;
724724+ asprintf(&dstfile, "%s%s%s", dst, dstpathsep, ftsent->fts_path + offset);
725725+ if (dstfile == NULL) {
726726+ copyfile_state_free(tstate);
727727+ errno = ENOMEM;
728728+ retval = -1;
729729+ break;
730730+ }
731731+ switch (ftsent->fts_info) {
732732+ case FTS_D:
733733+ tstate->internal_flags |= cfDelayAce;
734734+ cmd = COPYFILE_RECURSE_DIR;
735735+ break;
736736+ case FTS_SL:
737737+ case FTS_SLNONE:
738738+ case FTS_DEFAULT:
739739+ case FTS_F:
740740+ cmd = COPYFILE_RECURSE_FILE;
741741+ break;
742742+ case FTS_DP:
743743+ cmd = COPYFILE_RECURSE_DIR_CLEANUP;
744744+ break;
745745+ case FTS_DNR:
746746+ case FTS_ERR:
747747+ case FTS_NS:
748748+ case FTS_NSOK:
749749+ default:
750750+ errno = ftsent->fts_errno;
751751+ if (status) {
752752+ rv = (*status)(COPYFILE_RECURSE_ERROR, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx);
753753+ if (rv == COPYFILE_SKIP || rv == COPYFILE_CONTINUE) {
754754+ errno = 0;
755755+ goto skipit;
756756+ }
757757+ if (rv == COPYFILE_QUIT) {
758758+ retval = -1;
759759+ goto stopit;
760760+ }
761761+ } else {
762762+ retval = -1;
763763+ goto stopit;
764764+ }
765765+ case FTS_DOT:
766766+ goto skipit;
767767+768768+ }
769769+770770+ if (cmd == COPYFILE_RECURSE_DIR || cmd == COPYFILE_RECURSE_FILE) {
771771+ if (status) {
772772+ rv = (*status)(cmd, COPYFILE_START, tstate, ftsent->fts_path, dstfile, s->ctx);
773773+ if (rv == COPYFILE_SKIP) {
774774+ if (cmd == COPYFILE_RECURSE_DIR) {
775775+ rv = fts_set(fts, ftsent, FTS_SKIP);
776776+ if (rv == -1) {
777777+ rv = (*status)(0, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx);
778778+ if (rv == COPYFILE_QUIT)
779779+ retval = -1;
780780+ }
781781+ }
782782+ goto skipit;
783783+ }
784784+ if (rv == COPYFILE_QUIT) {
785785+ retval = -1; errno = 0;
786786+ goto stopit;
787787+ }
788788+ }
789789+ int tmp_flags = (cmd == COPYFILE_RECURSE_DIR) ? (flags & ~COPYFILE_STAT) : flags;
790790+ rv = copyfile(ftsent->fts_path, dstfile, tstate, tmp_flags);
791791+ if (rv < 0) {
792792+ if (status) {
793793+ rv = (*status)(cmd, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx);
794794+ if (rv == COPYFILE_QUIT) {
795795+ retval = -1;
796796+ goto stopit;
797797+ } else
798798+ rv = 0;
799799+ goto skipit;
800800+ } else {
801801+ retval = -1;
802802+ goto stopit;
803803+ }
804804+ }
805805+ if (status) {
806806+ rv = (*status)(cmd, COPYFILE_FINISH, tstate, ftsent->fts_path, dstfile, s->ctx);
807807+ if (rv == COPYFILE_QUIT) {
808808+ retval = -1; errno = 0;
809809+ goto stopit;
810810+ }
811811+ }
812812+ } else if (cmd == COPYFILE_RECURSE_DIR_CLEANUP) {
813813+ if (status) {
814814+ rv = (*status)(cmd, COPYFILE_START, tstate, ftsent->fts_path, dstfile, s->ctx);
815815+ if (rv == COPYFILE_QUIT) {
816816+ retval = -1; errno = 0;
817817+ goto stopit;
818818+ } else if (rv == COPYFILE_SKIP) {
819819+ rv = 0;
820820+ goto skipit;
821821+ }
822822+ }
823823+ rv = copyfile(ftsent->fts_path, dstfile, tstate, (flags & COPYFILE_NOFOLLOW) | COPYFILE_STAT);
824824+ if (rv < 0) {
825825+ if (status) {
826826+ rv = (*status)(COPYFILE_RECURSE_DIR_CLEANUP, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx);
827827+ if (rv == COPYFILE_QUIT) {
828828+ retval = -1;
829829+ goto stopit;
830830+ } else if (rv == COPYFILE_SKIP || rv == COPYFILE_CONTINUE) {
831831+ if (rv == COPYFILE_CONTINUE)
832832+ errno = 0;
833833+ retval = 0;
834834+ goto skipit;
835835+ }
836836+ } else {
837837+ retval = -1;
838838+ goto stopit;
839839+ }
840840+ } else {
841841+ if (status) {
842842+ rv = (*status)(COPYFILE_RECURSE_DIR_CLEANUP, COPYFILE_FINISH, tstate, ftsent->fts_path, dstfile, s->ctx);
843843+ if (rv == COPYFILE_QUIT) {
844844+ retval = -1; errno = 0;
845845+ goto stopit;
846846+ }
847847+ }
848848+ }
849849+850850+ rv = 0;
851851+ }
852852+skipit:
853853+stopit:
854854+ copyfile_state_free(tstate);
855855+ free(dstfile);
856856+ if (retval == -1)
857857+ break;
858858+ }
859859+860860+done:
861861+ if (fts)
862862+ fts_close(fts);
863863+864864+ return retval;
865865+}
866866+867867+/*
868868+ * fcopyfile() is used to copy a source file descriptor to a destination file
869869+ * descriptor. This allows an application to figure out how it wants to open
870870+ * the files (doing various security checks, perhaps), and then just pass in
871871+ * the file descriptors.
872872+ */
873873+int fcopyfile(int src_fd, int dst_fd, copyfile_state_t state, copyfile_flags_t flags)
874874+{
875875+ int ret = 0;
876876+ copyfile_state_t s = state;
877877+ struct stat dst_sb;
878878+879879+ if (src_fd < 0 || dst_fd < 0)
880880+ {
881881+ errno = EINVAL;
882882+ return -1;
883883+ }
884884+885885+ if (copyfile_preamble(&s, flags) < 0)
886886+ return -1;
887887+888888+ copyfile_debug(2, "set src_fd <- %d", src_fd);
889889+ if (s->src_fd == -2 && src_fd > -1)
890890+ {
891891+ s->src_fd = src_fd;
892892+ if (fstatx_np(s->src_fd, &s->sb, s->fsec) != 0)
893893+ {
894894+ if (errno == ENOTSUP || errno == EPERM)
895895+ fstat(s->src_fd, &s->sb);
896896+ else
897897+ {
898898+ copyfile_warn("fstatx_np on src fd %d", s->src_fd);
899899+ return -1;
900900+ }
901901+ }
902902+ }
903903+904904+ /* prevent copying on unsupported types */
905905+ switch (s->sb.st_mode & S_IFMT)
906906+ {
907907+ case S_IFLNK:
908908+ case S_IFDIR:
909909+ case S_IFREG:
910910+ break;
911911+ default:
912912+ errno = ENOTSUP;
913913+ return -1;
914914+ }
915915+916916+ copyfile_debug(2, "set dst_fd <- %d", dst_fd);
917917+ if (s->dst_fd == -2 && dst_fd > -1)
918918+ s->dst_fd = dst_fd;
919919+920920+ (void)fstat(s->dst_fd, &dst_sb);
921921+ (void)fchmod(s->dst_fd, (dst_sb.st_mode & ~S_IFMT) | (S_IRUSR | S_IWUSR));
922922+923923+ (void)copyfile_quarantine(s);
924924+925925+ ret = copyfile_internal(s, flags);
926926+927927+ if (ret >= 0 && !(s->flags & COPYFILE_STAT))
928928+ {
929929+ (void)fchmod(s->dst_fd, dst_sb.st_mode & ~S_IFMT);
930930+ }
931931+932932+ if (s->err) {
933933+ errno = s->err;
934934+ s->err = 0;
935935+ }
936936+ if (state == NULL) {
937937+ int t = errno;
938938+ copyfile_state_free(s);
939939+ errno = t;
940940+ }
941941+942942+ return ret;
943943+944944+}
945945+946946+/*
947947+ * the original copyfile() routine; this copies a source file to a destination
948948+ * file. Note that because we need to set the names in the state variable, this
949949+ * is not just the same as opening the two files, and then calling fcopyfile().
950950+ * Oh, if only life were that simple!
951951+ */
952952+int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_flags_t flags)
953953+{
954954+ int ret = 0;
955955+ int createdst = 0;
956956+ copyfile_state_t s = state;
957957+ struct stat dst_sb;
958958+959959+ if (src == NULL && dst == NULL)
960960+ {
961961+ errno = EINVAL;
962962+ return -1;
963963+ }
964964+965965+ if (copyfile_preamble(&s, flags) < 0)
966966+ {
967967+ return -1;
968968+ }
969969+970970+/*
971971+ * This macro is... well, it's not the worst thing you can do with cpp, not
972972+ * by a long shot. Essentially, we are setting the filename (src or dst)
973973+ * in the state structure; since the structure may not have been cleared out
974974+ * before being used again, we do some of the cleanup here: if the given
975975+ * filename (e.g., src) is set, and state->src is not equal to that, then
976976+ * we need to check to see if the file descriptor had been opened, and if so,
977977+ * close it. After that, we set state->src to be a copy of the given filename,
978978+ * releasing the old copy if necessary.
979979+ */
980980+#define COPYFILE_SET_FNAME(NAME, S) \
981981+ do { \
982982+ if (NAME != NULL) { \
983983+ if (S->NAME != NULL && strncmp(NAME, S->NAME, MAXPATHLEN)) { \
984984+ copyfile_debug(2, "replacing string %s (%s) -> (%s)", #NAME, NAME, S->NAME);\
985985+ if (S->NAME##_fd != -2 && S->NAME##_fd > -1) { \
986986+ copyfile_debug(4, "closing %s fd: %d", #NAME, S->NAME##_fd); \
987987+ close(S->NAME##_fd); \
988988+ S->NAME##_fd = -2; \
989989+ } \
990990+ } \
991991+ if (S->NAME) { \
992992+ free(S->NAME); \
993993+ S->NAME = NULL; \
994994+ } \
995995+ if ((NAME) && (S->NAME = strdup(NAME)) == NULL) \
996996+ return -1; \
997997+ } \
998998+ } while (0)
999999+10001000+ COPYFILE_SET_FNAME(src, s);
10011001+ COPYFILE_SET_FNAME(dst, s);
10021002+10031003+ if (s->flags & COPYFILE_RECURSIVE) {
10041004+ ret = copytree(s);
10051005+ goto exit;
10061006+ }
10071007+10081008+ /*
10091009+ * Get a copy of the source file's security settings
10101010+ */
10111011+ if (s->original_fsec) {
10121012+ filesec_free(s->original_fsec);
10131013+ s->original_fsec = NULL;
10141014+ }
10151015+ if ((s->original_fsec = filesec_init()) == NULL)
10161016+ goto error_exit;
10171017+10181018+ if ((s->flags & COPYFILE_NOFOLLOW_DST) && lstat(s->dst, &dst_sb) == 0 &&
10191019+ ((dst_sb.st_mode & S_IFMT) == S_IFLNK)) {
10201020+ if (s->permissive_fsec)
10211021+ free(s->permissive_fsec);
10221022+ s->permissive_fsec = NULL;
10231023+ } else if(statx_np(s->dst, &dst_sb, s->original_fsec) == 0)
10241024+ {
10251025+ /*
10261026+ * copyfile_fix_perms() will make a copy of the permission set,
10271027+ * and insert at the beginning an ACE that ensures we can write
10281028+ * to the file and set attributes.
10291029+ */
10301030+10311031+ if((s->permissive_fsec = copyfile_fix_perms(s, &s->original_fsec)) != NULL)
10321032+ {
10331033+ /*
10341034+ * Set the permissions for the destination to our copy.
10351035+ * We should get ENOTSUP from any filesystem that simply
10361036+ * doesn't support it.
10371037+ */
10381038+ if (chmodx_np(s->dst, s->permissive_fsec) < 0 && errno != ENOTSUP)
10391039+ {
10401040+ copyfile_warn("setting security information");
10411041+ filesec_free(s->permissive_fsec);
10421042+ s->permissive_fsec = NULL;
10431043+ }
10441044+ }
10451045+ } else if (errno == ENOENT) {
10461046+ createdst = 1;
10471047+ }
10481048+10491049+ /*
10501050+ * If COPYFILE_CHECK is set in flags, then all we are going to do
10511051+ * is see what kinds of things WOULD have been copied (see
10521052+ * copyfile_check() below). We return that value.
10531053+ */
10541054+ if (COPYFILE_CHECK & flags)
10551055+ {
10561056+ ret = copyfile_check(s);
10571057+ goto exit;
10581058+ } else if ((ret = copyfile_open(s)) < 0)
10591059+ goto error_exit;
10601060+10611061+ (void)fcntl(s->src_fd, F_NOCACHE, 1);
10621062+ (void)fcntl(s->dst_fd, F_NOCACHE, 1);
10631063+#ifdef F_SINGLE_WRITER
10641064+ (void)fcntl(s->dst_fd, F_SINGLE_WRITER, 1);
10651065+#endif
10661066+10671067+ ret = copyfile_internal(s, flags);
10681068+ if (ret == -1)
10691069+ goto error_exit;
10701070+10711071+#ifdef COPYFILE_RECURSIVE
10721072+ if (!(flags & COPYFILE_STAT)) {
10731073+ if (!createdst)
10741074+ {
10751075+ /* Just need to reset the BSD information -- mode, owner, group */
10761076+ (void)fchown(s->dst_fd, dst_sb.st_uid, dst_sb.st_gid);
10771077+ (void)fchmod(s->dst_fd, dst_sb.st_mode);
10781078+ }
10791079+ }
10801080+#endif
10811081+10821082+ reset_security(s);
10831083+10841084+ if (s->src && (flags & COPYFILE_MOVE))
10851085+ (void)remove(s->src);
10861086+10871087+exit:
10881088+ if (state == NULL) {
10891089+ int t = errno;
10901090+ copyfile_state_free(s);
10911091+ errno = t;
10921092+ }
10931093+10941094+ return ret;
10951095+10961096+error_exit:
10971097+ ret = -1;
10981098+ if (s->err) {
10991099+ errno = s->err;
11001100+ s->err = 0;
11011101+ }
11021102+ goto exit;
11031103+}
11041104+11051105+/*
11061106+ * Shared prelude to the {f,}copyfile(). This initializes the
11071107+ * state variable, if necessary, and also checks for both debugging
11081108+ * and disabling environment variables.
11091109+ */
11101110+static int copyfile_preamble(copyfile_state_t *state, copyfile_flags_t flags)
11111111+{
11121112+ copyfile_state_t s;
11131113+11141114+ if (*state == NULL)
11151115+ {
11161116+ if ((*state = copyfile_state_alloc()) == NULL)
11171117+ return -1;
11181118+ }
11191119+11201120+ s = *state;
11211121+11221122+ if (COPYFILE_DEBUG & flags)
11231123+ {
11241124+ char *e;
11251125+ if ((e = getenv(COPYFILE_DEBUG_VAR)))
11261126+ {
11271127+ errno = 0;
11281128+ s->debug = (uint32_t)strtol(e, NULL, 0);
11291129+11301130+ /* clamp s->debug to 1 if the environment variable is not parsable */
11311131+ if (s->debug == 0 && errno != 0)
11321132+ s->debug = 1;
11331133+ }
11341134+ copyfile_debug(2, "debug value set to: %d", s->debug);
11351135+ }
11361136+11371137+#if 0
11381138+ /* Temporarily disabled */
11391139+ if (getenv(COPYFILE_DISABLE_VAR) != NULL)
11401140+ {
11411141+ copyfile_debug(1, "copyfile disabled");
11421142+ return 2;
11431143+ }
11441144+#endif
11451145+ copyfile_debug(2, "setting flags: %d", s->flags);
11461146+ s->flags = flags;
11471147+11481148+ return 0;
11491149+}
11501150+11511151+/*
11521152+ * The guts of {f,}copyfile().
11531153+ * This looks through the flags in a particular order, and calls the
11541154+ * associated functions.
11551155+ */
11561156+static int copyfile_internal(copyfile_state_t s, copyfile_flags_t flags)
11571157+{
11581158+ int ret = 0;
11591159+11601160+ if (s->dst_fd < 0 || s->src_fd < 0)
11611161+ {
11621162+ copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)", s->src_fd, s->dst_fd);
11631163+ s->err = EINVAL;
11641164+ return -1;
11651165+ }
11661166+11671167+ /*
11681168+ * COPYFILE_PACK causes us to create an Apple Double version of the
11691169+ * source file, and puts it into the destination file. See
11701170+ * copyfile_pack() below for all the gory details.
11711171+ */
11721172+ if (COPYFILE_PACK & flags)
11731173+ {
11741174+ if ((ret = copyfile_pack(s)) < 0)
11751175+ {
11761176+ if (s->dst) unlink(s->dst);
11771177+ goto exit;
11781178+ }
11791179+ goto exit;
11801180+ }
11811181+11821182+ /*
11831183+ * COPYFILE_UNPACK is the undoing of COPYFILE_PACK, obviously.
11841184+ * The goal there is to take an Apple Double file, and turn it
11851185+ * into a normal file (with data fork, resource fork, modes,
11861186+ * extended attributes, ACLs, etc.).
11871187+ */
11881188+ if (COPYFILE_UNPACK & flags)
11891189+ {
11901190+ if ((ret = copyfile_unpack(s)) < 0)
11911191+ goto error_exit;
11921192+ goto exit;
11931193+ }
11941194+11951195+ /*
11961196+ * If we have quarantine info set, we attempt
11971197+ * to apply it to dst_fd. We don't care if
11981198+ * it fails, not yet anyway.
11991199+ */
12001200+ if (s->qinfo) {
12011201+ int qr = qtn_file_apply_to_fd(s->qinfo, s->dst_fd);
12021202+ if (qr != 0) {
12031203+ if (s->statuscb) {
12041204+ int rv;
12051205+12061206+ s->xattr_name = (char*)XATTR_QUARANTINE_NAME;
12071207+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
12081208+ s->xattr_name = NULL;
12091209+ if (rv == COPYFILE_QUIT) {
12101210+ s->err = errno = (qr < 0 ? ENOTSUP : qr);
12111211+ ret = -1;
12121212+ goto exit;
12131213+ }
12141214+ } else {
12151215+ s->err = errno = (qr < 0 ? ENOTSUP : qr);
12161216+ ret = -1;
12171217+ goto exit;
12181218+ }
12191219+ }
12201220+ }
12211221+12221222+ /*
12231223+ * COPYFILE_XATTR tells us to copy the extended attributes;
12241224+ * this is seperate from the extended security (aka ACLs),
12251225+ * however. If we succeed in this, we continue to the next
12261226+ * stage; if we fail, we return with an error value. Note
12271227+ * that we fail if the errno is ENOTSUP, but we don't print
12281228+ * a warning in that case.
12291229+ */
12301230+ if (COPYFILE_XATTR & flags)
12311231+ {
12321232+ if ((ret = copyfile_xattr(s)) < 0)
12331233+ {
12341234+ if (errno != ENOTSUP && errno != EPERM)
12351235+ copyfile_warn("error processing extended attributes");
12361236+ goto exit;
12371237+ }
12381238+ }
12391239+12401240+ /*
12411241+ * Simialr to above, this tells us whether or not to copy
12421242+ * the non-meta data portion of the file. We attempt to
12431243+ * remove (via unlink) the destination file if we fail.
12441244+ */
12451245+ if (COPYFILE_DATA & flags)
12461246+ {
12471247+ if ((ret = copyfile_data(s)) < 0)
12481248+ {
12491249+ copyfile_warn("error processing data");
12501250+ if (s->dst && unlink(s->dst))
12511251+ copyfile_warn("%s: remove", s->src ? s->src : "(null src)");
12521252+ goto exit;
12531253+ }
12541254+ }
12551255+12561256+ /*
12571257+ * COPYFILE_SECURITY requests that we copy the security, both
12581258+ * extended and mundane (that is, ACLs and POSIX).
12591259+ */
12601260+ if (COPYFILE_SECURITY & flags)
12611261+ {
12621262+ if ((ret = copyfile_security(s)) < 0)
12631263+ {
12641264+ copyfile_warn("error processing security information");
12651265+ goto exit;
12661266+ }
12671267+ }
12681268+12691269+ if (COPYFILE_STAT & flags)
12701270+ {
12711271+ if ((ret = copyfile_stat(s)) < 0)
12721272+ {
12731273+ copyfile_warn("error processing POSIX information");
12741274+ goto exit;
12751275+ }
12761276+ }
12771277+12781278+exit:
12791279+ return ret;
12801280+12811281+error_exit:
12821282+ ret = -1;
12831283+ goto exit;
12841284+}
12851285+12861286+/*
12871287+ * A publicly-visible routine, copyfile_state_alloc() sets up the state variable.
12881288+ */
12891289+copyfile_state_t copyfile_state_alloc(void)
12901290+{
12911291+ copyfile_state_t s = (copyfile_state_t) calloc(1, sizeof(struct _copyfile_state));
12921292+12931293+ if (s != NULL)
12941294+ {
12951295+ s->src_fd = -2;
12961296+ s->dst_fd = -2;
12971297+ if (s->fsec) {
12981298+ filesec_free(s->fsec);
12991299+ s->fsec = NULL;
13001300+ }
13011301+ s->fsec = filesec_init();
13021302+ } else
13031303+ errno = ENOMEM;
13041304+13051305+ return s;
13061306+}
13071307+13081308+/*
13091309+ * copyfile_state_free() returns the memory allocated to the state structure.
13101310+ * It also closes the file descriptors, if they've been opened.
13111311+ */
13121312+int copyfile_state_free(copyfile_state_t s)
13131313+{
13141314+ if (s != NULL)
13151315+ {
13161316+ if (s->fsec)
13171317+ filesec_free(s->fsec);
13181318+13191319+ if (s->original_fsec)
13201320+ filesec_free(s->original_fsec);
13211321+13221322+ if (s->permissive_fsec)
13231323+ filesec_free(s->permissive_fsec);
13241324+13251325+ if (s->qinfo)
13261326+ qtn_file_free(s->qinfo);
13271327+13281328+ if (copyfile_close(s) < 0)
13291329+ {
13301330+ copyfile_warn("error closing files");
13311331+ return -1;
13321332+ }
13331333+ if (s->xattr_name)
13341334+ free(s->xattr_name);
13351335+ if (s->dst)
13361336+ free(s->dst);
13371337+ if (s->src)
13381338+ free(s->src);
13391339+ free(s);
13401340+ }
13411341+ return 0;
13421342+}
13431343+13441344+/*
13451345+ * Should we worry if we can't close the source? NFS says we
13461346+ * should, but it's pretty late for us at this point.
13471347+ */
13481348+static int copyfile_close(copyfile_state_t s)
13491349+{
13501350+ if (s->src && s->src_fd >= 0)
13511351+ close(s->src_fd);
13521352+13531353+ if (s->dst && s->dst_fd >= 0) {
13541354+ if (close(s->dst_fd))
13551355+ return -1;
13561356+ }
13571357+13581358+ return 0;
13591359+}
13601360+13611361+/*
13621362+ * The purpose of this function is to set up a set of permissions
13631363+ * (ACL and traditional) that lets us write to the file. In the
13641364+ * case of ACLs, we do this by putting in a first entry that lets
13651365+ * us write data, attributes, and extended attributes. In the case
13661366+ * of traditional permissions, we set the S_IWUSR (user-write)
13671367+ * bit.
13681368+ */
13691369+static filesec_t copyfile_fix_perms(copyfile_state_t s __unused, filesec_t *fsec)
13701370+{
13711371+ filesec_t ret_fsec = NULL;
13721372+ mode_t mode;
13731373+ acl_t acl = NULL;
13741374+13751375+ if ((ret_fsec = filesec_dup(*fsec)) == NULL)
13761376+ goto error_exit;
13771377+13781378+ if (filesec_get_property(ret_fsec, FILESEC_ACL, &acl) == 0)
13791379+ {
13801380+#ifdef COPYFILE_RECURSIVE
13811381+ if (add_uberace(&acl))
13821382+ goto error_exit;
13831383+#else
13841384+ acl_entry_t entry;
13851385+ acl_permset_t permset;
13861386+ uuid_t qual;
13871387+13881388+ if (mbr_uid_to_uuid(getuid(), qual) != 0)
13891389+ goto error_exit;
13901390+13911391+ /*
13921392+ * First, we create an entry, and give it the special name
13931393+ * of ACL_FIRST_ENTRY, thus guaranteeing it will be first.
13941394+ * After that, we clear out all the permissions in it, and
13951395+ * add three permissions: WRITE_DATA, WRITE_ATTRIBUTES, and
13961396+ * WRITE_EXTATTRIBUTES. We put these into an ACE that allows
13971397+ * the functionality, and put this into the ACL.
13981398+ */
13991399+ if (acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY) == -1)
14001400+ goto error_exit;
14011401+ if (acl_get_permset(entry, &permset) == -1)
14021402+ goto error_exit;
14031403+ if (acl_clear_perms(permset) == -1)
14041404+ goto error_exit;
14051405+ if (acl_add_perm(permset, ACL_WRITE_DATA) == -1)
14061406+ goto error_exit;
14071407+ if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1)
14081408+ goto error_exit;
14091409+ if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1)
14101410+ goto error_exit;
14111411+ if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1)
14121412+ goto error_exit;
14131413+14141414+ if(acl_set_permset(entry, permset) == -1)
14151415+ goto error_exit;
14161416+ if(acl_set_qualifier(entry, qual) == -1)
14171417+ goto error_exit;
14181418+#endif
14191419+14201420+ if (filesec_set_property(ret_fsec, FILESEC_ACL, &acl) != 0)
14211421+ goto error_exit;
14221422+ }
14231423+14241424+ /*
14251425+ * This is for the normal, mundane, POSIX permission model.
14261426+ * We make sure that we can write to the file.
14271427+ */
14281428+ if (filesec_get_property(ret_fsec, FILESEC_MODE, &mode) == 0)
14291429+ {
14301430+ if ((mode & (S_IWUSR | S_IRUSR)) != (S_IWUSR | S_IRUSR))
14311431+ {
14321432+ mode |= S_IWUSR|S_IRUSR;
14331433+ if (filesec_set_property(ret_fsec, FILESEC_MODE, &mode) != 0)
14341434+ goto error_exit;
14351435+ }
14361436+ }
14371437+14381438+exit:
14391439+ if (acl)
14401440+ acl_free(acl);
14411441+14421442+ return ret_fsec;
14431443+14441444+error_exit:
14451445+ if (ret_fsec)
14461446+ {
14471447+ filesec_free(ret_fsec);
14481448+ ret_fsec = NULL;
14491449+ }
14501450+ goto exit;
14511451+}
14521452+14531453+/*
14541454+ * Used to clear out the BSD/POSIX security information from
14551455+ * a filesec
14561456+ */
14571457+static int
14581458+copyfile_unset_posix_fsec(filesec_t fsec)
14591459+{
14601460+ (void)filesec_set_property(fsec, FILESEC_OWNER, _FILESEC_UNSET_PROPERTY);
14611461+ (void)filesec_set_property(fsec, FILESEC_GROUP, _FILESEC_UNSET_PROPERTY);
14621462+ (void)filesec_set_property(fsec, FILESEC_MODE, _FILESEC_UNSET_PROPERTY);
14631463+ return 0;
14641464+}
14651465+14661466+/*
14671467+ * Used to remove acl information from a filesec_t
14681468+ * Unsetting the acl alone in Tiger was insufficient
14691469+ */
14701470+static int copyfile_unset_acl(copyfile_state_t s)
14711471+{
14721472+ int ret = 0;
14731473+ if (filesec_set_property(s->fsec, FILESEC_ACL, NULL) == -1)
14741474+ {
14751475+ copyfile_debug(5, "unsetting acl attribute on %s", s->dst ? s->dst : "(null dst)");
14761476+ ++ret;
14771477+ }
14781478+ if (filesec_set_property(s->fsec, FILESEC_UUID, NULL) == -1)
14791479+ {
14801480+ copyfile_debug(5, "unsetting uuid attribute on %s", s->dst ? s->dst : "(null dst)");
14811481+ ++ret;
14821482+ }
14831483+ if (filesec_set_property(s->fsec, FILESEC_GRPUUID, NULL) == -1)
14841484+ {
14851485+ copyfile_debug(5, "unsetting group uuid attribute on %s", s->dst ? s->dst : "(null dst)");
14861486+ ++ret;
14871487+ }
14881488+ return ret;
14891489+}
14901490+14911491+/*
14921492+ * copyfile_open() does what one expects: it opens up the files
14931493+ * given in the state structure, if they're not already open.
14941494+ * It also does some type validation, to ensure that we only
14951495+ * handle file types we know about.
14961496+ */
14971497+static int copyfile_open(copyfile_state_t s)
14981498+{
14991499+ int oflags = O_EXCL | O_CREAT | O_WRONLY;
15001500+ int islnk = 0, isdir = 0;
15011501+ int osrc = 0, dsrc = 0;
15021502+15031503+ if (s->src && s->src_fd == -2)
15041504+ {
15051505+ if ((COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np)
15061506+ (s->src, &s->sb, s->fsec))
15071507+ {
15081508+ copyfile_warn("stat on %s", s->src);
15091509+ return -1;
15101510+ }
15111511+15121512+ /* prevent copying on unsupported types */
15131513+ switch (s->sb.st_mode & S_IFMT)
15141514+ {
15151515+ case S_IFLNK:
15161516+ islnk = 1;
15171517+ if ((size_t)s->sb.st_size > SIZE_T_MAX) {
15181518+ s->err = ENOMEM; /* too big for us to copy */
15191519+ return -1;
15201520+ }
15211521+ osrc = O_SYMLINK;
15221522+ break;
15231523+ case S_IFDIR:
15241524+ isdir = 1;
15251525+ break;
15261526+ case S_IFREG:
15271527+ break;
15281528+ default:
15291529+ if (!(strcmp(s->src, "/dev/null") == 0 && (s->flags & COPYFILE_METADATA))) {
15301530+ s->err = ENOTSUP;
15311531+ return -1;
15321532+ }
15331533+ }
15341534+ /*
15351535+ * If we're packing, then we are actually
15361536+ * creating a file, no matter what the source
15371537+ * was.
15381538+ */
15391539+ if (s->flags & COPYFILE_PACK) {
15401540+ /*
15411541+ * O_SYMLINK and O_NOFOLLOW are not compatible options:
15421542+ * if the file is a symlink, and O_NOFOLLOW is specified,
15431543+ * open will return ELOOP, whether or not O_SYMLINK is set.
15441544+ * However, we know whether or not it was a symlink from
15451545+ * the stat above (although there is a potentiaal for a race
15461546+ * condition here, but it will err on the side of returning
15471547+ * ELOOP from open).
15481548+ */
15491549+ if (!islnk)
15501550+ osrc = (s->flags & COPYFILE_NOFOLLOW_SRC) ? O_NOFOLLOW : 0;
15511551+ isdir = islnk = 0;
15521552+ }
15531553+15541554+ if ((s->src_fd = open(s->src, O_RDONLY | osrc , 0)) < 0)
15551555+ {
15561556+ copyfile_warn("open on %s", s->src);
15571557+ return -1;
15581558+ } else
15591559+ copyfile_debug(2, "open successful on source (%s)", s->src);
15601560+15611561+ (void)copyfile_quarantine(s);
15621562+ }
15631563+15641564+ if (s->dst && s->dst_fd == -2)
15651565+ {
15661566+ /*
15671567+ * COPYFILE_UNLINK tells us to try removing the destination
15681568+ * before we create it. We don't care if the file doesn't
15691569+ * exist, so we ignore ENOENT.
15701570+ */
15711571+ if (COPYFILE_UNLINK & s->flags)
15721572+ {
15731573+ if (remove(s->dst) < 0 && errno != ENOENT)
15741574+ {
15751575+ copyfile_warn("%s: remove", s->dst);
15761576+ return -1;
15771577+ }
15781578+ }
15791579+15801580+ if (s->flags & COPYFILE_NOFOLLOW_DST) {
15811581+ struct stat st;
15821582+15831583+ dsrc = O_NOFOLLOW;
15841584+ if (lstat(s->dst, &st) != -1) {
15851585+ if ((st.st_mode & S_IFMT) == S_IFLNK)
15861586+ dsrc = O_SYMLINK;
15871587+ }
15881588+ }
15891589+15901590+ if (islnk) {
15911591+ size_t sz = (size_t)s->sb.st_size + 1;
15921592+ char *bp;
15931593+15941594+ bp = calloc(1, sz);
15951595+ if (bp == NULL) {
15961596+ copyfile_warn("cannot allocate %zd bytes", sz);
15971597+ return -1;
15981598+ }
15991599+ if (readlink(s->src, bp, sz-1) == -1) {
16001600+ copyfile_warn("cannot readlink %s", s->src);
16011601+ free(bp);
16021602+ return -1;
16031603+ }
16041604+ if (symlink(bp, s->dst) == -1) {
16051605+ if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) {
16061606+ copyfile_warn("Cannot make symlink %s", s->dst);
16071607+ free(bp);
16081608+ return -1;
16091609+ }
16101610+ }
16111611+ free(bp);
16121612+ s->dst_fd = open(s->dst, O_RDONLY | O_SYMLINK);
16131613+ if (s->dst_fd == -1) {
16141614+ copyfile_warn("Cannot open symlink %s for reading", s->dst);
16151615+ return -1;
16161616+ }
16171617+ } else if (isdir) {
16181618+ mode_t mode;
16191619+ mode = (s->sb.st_mode & ~S_IFMT) | S_IRWXU;
16201620+16211621+ if (mkdir(s->dst, mode) == -1) {
16221622+ if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) {
16231623+ copyfile_warn("Cannot make directory %s", s->dst);
16241624+ return -1;
16251625+ }
16261626+ }
16271627+ s->dst_fd = open(s->dst, O_RDONLY | dsrc);
16281628+ if (s->dst_fd == -1) {
16291629+ copyfile_warn("Cannot open directory %s for reading", s->dst);
16301630+ return -1;
16311631+ }
16321632+ } else while((s->dst_fd = open(s->dst, oflags | dsrc, s->sb.st_mode | S_IWUSR)) < 0)
16331633+ {
16341634+ /*
16351635+ * We set S_IWUSR because fsetxattr does not -- at the time this comment
16361636+ * was written -- allow one to set an extended attribute on a file descriptor
16371637+ * for a read-only file, even if the file descriptor is opened for writing.
16381638+ * This will only matter if the file does not already exist.
16391639+ */
16401640+ switch(errno)
16411641+ {
16421642+ case EEXIST:
16431643+ copyfile_debug(3, "open failed, retrying (%s)", s->dst);
16441644+ if (s->flags & COPYFILE_EXCL)
16451645+ break;
16461646+ oflags = oflags & ~O_CREAT;
16471647+ if (s->flags & (COPYFILE_PACK | COPYFILE_DATA))
16481648+ {
16491649+ copyfile_debug(4, "truncating existing file (%s)", s->dst);
16501650+ oflags |= O_TRUNC;
16511651+ }
16521652+ continue;
16531653+ case EACCES:
16541654+ if(chmod(s->dst, (s->sb.st_mode | S_IWUSR) & ~S_IFMT) == 0)
16551655+ continue;
16561656+ else {
16571657+ /*
16581658+ * If we're trying to write to a directory to which we don't
16591659+ * have access, the create above would have failed, but chmod
16601660+ * here would have given us ENOENT. But the real error is
16611661+ * still one of access, so we change the errno we're reporting.
16621662+ * This could cause confusion with a race condition.
16631663+ */
16641664+16651665+ if (errno == ENOENT)
16661666+ errno = EACCES;
16671667+ break;
16681668+ }
16691669+ case EISDIR:
16701670+ copyfile_debug(3, "open failed because it is a directory (%s)", s->dst);
16711671+ if (((s->flags & COPYFILE_EXCL) ||
16721672+ (!isdir && (s->flags & COPYFILE_DATA)))
16731673+ && !(s->flags & COPYFILE_UNPACK))
16741674+ break;
16751675+ oflags = (oflags & ~(O_WRONLY|O_CREAT|O_TRUNC)) | O_RDONLY;
16761676+ continue;
16771677+ }
16781678+ copyfile_warn("open on %s", s->dst);
16791679+ return -1;
16801680+ }
16811681+ copyfile_debug(2, "open successful on destination (%s)", s->dst);
16821682+ }
16831683+16841684+ if (s->dst_fd < 0 || s->src_fd < 0)
16851685+ {
16861686+ copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)",
16871687+ s->src_fd, s->dst_fd);
16881688+ s->err = EINVAL;
16891689+ return -1;
16901690+ }
16911691+ return 0;
16921692+}
16931693+16941694+16951695+/*
16961696+ * copyfile_check(), as described above, essentially tells you
16971697+ * what you'd have to copy, if you wanted it to copy the things
16981698+ * you asked it to copy.
16991699+ * In other words, if you pass in COPYFILE_ALL, and the file in
17001700+ * question had no extended attributes but did have an ACL, you'd
17011701+ * get back COPYFILE_ACL.
17021702+ */
17031703+static copyfile_flags_t copyfile_check(copyfile_state_t s)
17041704+{
17051705+ acl_t acl = NULL;
17061706+ copyfile_flags_t ret = 0;
17071707+ int nofollow = (s->flags & COPYFILE_NOFOLLOW_SRC);
17081708+ qtn_file_t qinfo;
17091709+17101710+ if (!s->src)
17111711+ {
17121712+ s->err = EINVAL;
17131713+ return -1;
17141714+ }
17151715+17161716+ /* check EAs */
17171717+ if (COPYFILE_XATTR & s->flags)
17181718+ if (listxattr(s->src, 0, 0, nofollow ? XATTR_NOFOLLOW : 0) > 0)
17191719+ {
17201720+ ret |= COPYFILE_XATTR;
17211721+ }
17221722+17231723+ if (COPYFILE_ACL & s->flags)
17241724+ {
17251725+ (COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np)
17261726+ (s->src, &s->sb, s->fsec);
17271727+17281728+ if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) == 0)
17291729+ ret |= COPYFILE_ACL;
17301730+ }
17311731+17321732+ copyfile_debug(2, "check result: %d (%s)", ret, s->src);
17331733+17341734+ if (acl)
17351735+ acl_free(acl);
17361736+17371737+ if (s->qinfo) {
17381738+ /* If the state has had quarantine info set already, we use that */
17391739+ ret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL);
17401740+ } else {
17411741+ qinfo = qtn_file_alloc();
17421742+ /*
17431743+ * For quarantine information, we need to see if the source file
17441744+ * has any. Since it may be a symlink, however, and we may, or
17451745+ * not be following, *and* there's no qtn* routine which can optionally
17461746+ * follow or not follow a symlink, we need to instead work around
17471747+ * this limitation.
17481748+ */
17491749+ if (qinfo) {
17501750+ int fd;
17511751+ int qret = 0;
17521752+ struct stat sbuf;
17531753+17541754+ /*
17551755+ * If we care about not following symlinks, *and* the file exists
17561756+ * (which is to say, lstat doesn't return an error), *and* the file
17571757+ * is a symlink, then we open it up (with O_SYMLINK), and use
17581758+ * qtn_file_init_with_fd(); if none of that is true, however, then
17591759+ * we can simply use qtn_file_init_with_path().
17601760+ */
17611761+ if (nofollow
17621762+ && lstat(s->src, &sbuf) == 0
17631763+ && ((sbuf.st_mode & S_IFMT) == S_IFLNK)) {
17641764+ fd = open(s->src, O_RDONLY | O_SYMLINK);
17651765+ if (fd != -1) {
17661766+ if (!qtn_file_init_with_fd(qinfo, fd)) {
17671767+ qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL);
17681768+ }
17691769+ close(fd);
17701770+ }
17711771+ } else {
17721772+ if (!qtn_file_init_with_path(qinfo, s->src)) {
17731773+ qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL);
17741774+ }
17751775+ }
17761776+ qtn_file_free(qinfo);
17771777+ ret |= qret;
17781778+ }
17791779+ }
17801780+ return ret;
17811781+}
17821782+17831783+/*
17841784+ * Attempt to copy the data section of a file. Using blockisize
17851785+ * is not necessarily the fastest -- it might be desirable to
17861786+ * specify a blocksize, somehow. But it's a size that should be
17871787+ * guaranteed to work.
17881788+ */
17891789+static int copyfile_data(copyfile_state_t s)
17901790+{
17911791+ size_t blen;
17921792+ char *bp = 0;
17931793+ ssize_t nread;
17941794+ int ret = 0;
17951795+ size_t iBlocksize = 0;
17961796+ size_t oBlocksize = 0;
17971797+ const size_t onegig = 1 << 30;
17981798+ struct statfs sfs;
17991799+ copyfile_callback_t status = s->statuscb;
18001800+18011801+ /* Unless it's a normal file, we don't copy. For now, anyway */
18021802+ if ((s->sb.st_mode & S_IFMT) != S_IFREG)
18031803+ return 0;
18041804+18051805+#ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION
18061806+ if (s->internal_flags & cfSawDecmpEA) {
18071807+ if (s->sb.st_flags & UF_COMPRESSED) {
18081808+ if ((s->flags & COPYFILE_STAT) == 0) {
18091809+ if (fchflags(s->dst_fd, UF_COMPRESSED) == 0) {
18101810+ goto exit;
18111811+ }
18121812+ }
18131813+ }
18141814+ }
18151815+#endif
18161816+18171817+ if (fstatfs(s->src_fd, &sfs) == -1) {
18181818+ iBlocksize = s->sb.st_blksize;
18191819+ } else {
18201820+ iBlocksize = sfs.f_iosize;
18211821+ }
18221822+18231823+ /* Work-around for 6453525, limit blocksize to 1G */
18241824+ if (iBlocksize > onegig) {
18251825+ iBlocksize = onegig;
18261826+ }
18271827+18281828+ if ((bp = malloc(iBlocksize)) == NULL)
18291829+ return -1;
18301830+18311831+ if (fstatfs(s->dst_fd, &sfs) == -1 || sfs.f_iosize == 0) {
18321832+ oBlocksize = iBlocksize;
18331833+ } else {
18341834+ oBlocksize = sfs.f_iosize;
18351835+ if (oBlocksize > onegig)
18361836+ oBlocksize = onegig;
18371837+ }
18381838+18391839+ blen = iBlocksize;
18401840+18411841+ s->totalCopied = 0;
18421842+/* If supported, do preallocation for Xsan / HFS volumes */
18431843+#ifdef F_PREALLOCATE
18441844+ {
18451845+ fstore_t fst;
18461846+18471847+ fst.fst_flags = 0;
18481848+ fst.fst_posmode = F_PEOFPOSMODE;
18491849+ fst.fst_offset = 0;
18501850+ fst.fst_length = s->sb.st_size;
18511851+ /* Ignore errors; this is merely advisory. */
18521852+ (void)fcntl(s->dst_fd, F_PREALLOCATE, &fst);
18531853+ }
18541854+#endif
18551855+18561856+ while ((nread = read(s->src_fd, bp, blen)) > 0)
18571857+ {
18581858+ ssize_t nwritten;
18591859+ size_t left = nread;
18601860+ void *ptr = bp;
18611861+ int loop = 0;
18621862+18631863+ while (left > 0) {
18641864+ nwritten = write(s->dst_fd, ptr, MIN(left, oBlocksize));
18651865+ switch (nwritten) {
18661866+ case 0:
18671867+ if (++loop > 5) {
18681868+ copyfile_warn("writing to output %d times resulted in 0 bytes written", loop);
18691869+ ret = -1;
18701870+ s->err = EAGAIN;
18711871+ goto exit;
18721872+ }
18731873+ break;
18741874+ case -1:
18751875+ copyfile_warn("writing to output file got error");
18761876+ if (status) {
18771877+ int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
18781878+ if (rv == COPYFILE_SKIP) { // Skip the data copy
18791879+ ret = 0;
18801880+ goto exit;
18811881+ }
18821882+ if (rv == COPYFILE_CONTINUE) { // Retry the write
18831883+ errno = 0;
18841884+ continue;
18851885+ }
18861886+ }
18871887+ ret = -1;
18881888+ goto exit;
18891889+ default:
18901890+ left -= nwritten;
18911891+ ptr = ((char*)ptr) + nwritten;
18921892+ loop = 0;
18931893+ break;
18941894+ }
18951895+ s->totalCopied += nwritten;
18961896+ if (status) {
18971897+ int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
18981898+ if (rv == COPYFILE_QUIT) {
18991899+ ret = -1; s->err = errno = ECANCELED;
19001900+ goto exit;
19011901+ }
19021902+ }
19031903+ }
19041904+ }
19051905+ if (nread < 0)
19061906+ {
19071907+ copyfile_warn("reading from %s", s->src ? s->src : "(null src)");
19081908+ ret = -1;
19091909+ goto exit;
19101910+ }
19111911+19121912+ if (ftruncate(s->dst_fd, s->totalCopied) < 0)
19131913+ {
19141914+ ret = -1;
19151915+ goto exit;
19161916+ }
19171917+19181918+exit:
19191919+ if (ret == -1)
19201920+ {
19211921+ s->err = errno;
19221922+ }
19231923+ free(bp);
19241924+ return ret;
19251925+}
19261926+19271927+/*
19281928+ * copyfile_security() will copy the ACL set, and the
19291929+ * POSIX set. Complexities come when dealing with
19301930+ * inheritied permissions, and when dealing with both
19311931+ * POSIX and ACL permissions.
19321932+ */
19331933+static int copyfile_security(copyfile_state_t s)
19341934+{
19351935+ int copied = 0;
19361936+ struct stat sb;
19371937+ acl_t acl_src = NULL, acl_tmp = NULL, acl_dst = NULL;
19381938+ int ret = 0;
19391939+ filesec_t tmp_fsec = NULL;
19401940+ filesec_t fsec_dst = filesec_init();
19411941+19421942+ if (fsec_dst == NULL)
19431943+ return -1;
19441944+19451945+19461946+ if (COPYFILE_ACL & s->flags)
19471947+ {
19481948+ if (filesec_get_property(s->fsec, FILESEC_ACL, &acl_src))
19491949+ {
19501950+ if (errno == ENOENT)
19511951+ acl_src = NULL;
19521952+ else
19531953+ goto error_exit;
19541954+ }
19551955+19561956+/* grab the destination acl
19571957+ cannot assume it's empty due to inheritance
19581958+*/
19591959+ if(fstatx_np(s->dst_fd, &sb, fsec_dst))
19601960+ goto error_exit;
19611961+19621962+ if (filesec_get_property(fsec_dst, FILESEC_ACL, &acl_dst))
19631963+ {
19641964+ if (errno == ENOENT)
19651965+ acl_dst = NULL;
19661966+ else
19671967+ goto error_exit;
19681968+ }
19691969+19701970+ if (acl_src == NULL && acl_dst == NULL)
19711971+ goto no_acl;
19721972+19731973+ acl_tmp = acl_init(4);
19741974+ if (acl_tmp == NULL)
19751975+ goto error_exit;
19761976+19771977+ if (acl_src) {
19781978+ acl_entry_t ace = NULL;
19791979+ acl_entry_t tmp = NULL;
19801980+ for (copied = 0;
19811981+ acl_get_entry(acl_src,
19821982+ ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY,
19831983+ &ace) == 0;)
19841984+ {
19851985+ acl_flagset_t flags = { 0 };
19861986+ acl_get_flagset_np(ace, &flags);
19871987+ if (!acl_get_flag_np(flags, ACL_ENTRY_INHERITED))
19881988+ {
19891989+ if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1)
19901990+ goto error_exit;
19911991+19921992+ if ((ret = acl_copy_entry(tmp, ace)) == -1)
19931993+ goto error_exit;
19941994+19951995+ copyfile_debug(2, "copied acl entry from %s to %s",
19961996+ s->src ? s->src : "(null src)",
19971997+ s->dst ? s->dst : "(null tmp)");
19981998+ copied++;
19991999+ }
20002000+ }
20012001+ }
20022002+ if (acl_dst) {
20032003+ acl_entry_t ace = NULL;
20042004+ acl_entry_t tmp = NULL;
20052005+ acl_flagset_t flags = { 0 };
20062006+ for (copied = 0;acl_get_entry(acl_dst,
20072007+ ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY,
20082008+ &ace) == 0;)
20092009+ {
20102010+ acl_get_flagset_np(ace, &flags);
20112011+ if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED))
20122012+ {
20132013+ if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1)
20142014+ goto error_exit;
20152015+20162016+ if ((ret = acl_copy_entry(tmp, ace)) == -1)
20172017+ goto error_exit;
20182018+20192019+ copyfile_debug(2, "copied acl entry from %s to %s",
20202020+ s->src ? s->src : "(null dst)",
20212021+ s->dst ? s->dst : "(null tmp)");
20222022+ copied++;
20232023+ }
20242024+ }
20252025+ }
20262026+ if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_tmp))
20272027+ {
20282028+ copyfile_debug(3, "altered acl");
20292029+ }
20302030+ }
20312031+no_acl:
20322032+ /*
20332033+ * The following code is attempting to ensure that only the requested
20342034+ * security information gets copied over to the destination file.
20352035+ * We essentially have four cases: COPYFILE_ACL, COPYFILE_STAT,
20362036+ * COPYFILE_(STAT|ACL), and none (in which case, we wouldn't be in
20372037+ * this function).
20382038+ *
20392039+ * If we have both flags, we copy everything; if we have ACL but not STAT,
20402040+ * we remove the POSIX information from the filesec object, and apply the
20412041+ * ACL; if we have STAT but not ACL, then we just use fchmod(), and ignore
20422042+ * the extended version.
20432043+ */
20442044+ tmp_fsec = filesec_dup(s->fsec);
20452045+ if (tmp_fsec == NULL) {
20462046+ goto error_exit;
20472047+ }
20482048+20492049+ switch (COPYFILE_SECURITY & s->flags) {
20502050+ case COPYFILE_ACL:
20512051+ copyfile_unset_posix_fsec(tmp_fsec);
20522052+ /* FALLTHROUGH */
20532053+ case COPYFILE_ACL | COPYFILE_STAT:
20542054+ if (fchmodx_np(s->dst_fd, tmp_fsec) < 0) {
20552055+ acl_t acl = NULL;
20562056+ /*
20572057+ * The call could have failed for a number of reasons, since
20582058+ * it does a number of things: it changes the mode of the file,
20592059+ * sets the owner and group, and applies an ACL (if one exists).
20602060+ * The typical failure is going to be trying to set the group of
20612061+ * the destination file to match the source file, when the process
20622062+ * doesn't have permission to put files in that group. We try to
20632063+ * work around this by breaking the steps out and doing them
20642064+ * discretely. We don't care if the fchown fails, but we do care
20652065+ * if the mode or ACL can't be set. For historical reasons, we
20662066+ * simply log those failures, however.
20672067+ *
20682068+ * Big warning here: we may NOT have COPYFILE_STAT set, since
20692069+ * we fell-through from COPYFILE_ACL. So check for the fchmod.
20702070+ */
20712071+20722072+#define NS(x) ((x) ? (x) : "(null string)")
20732073+ if ((s->flags & COPYFILE_STAT) &&
20742074+ fchmod(s->dst_fd, s->sb.st_mode) == -1) {
20752075+ copyfile_warn("could not change mode of destination file %s to match source file %s", NS(s->dst), NS(s->src));
20762076+ }
20772077+ (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid);
20782078+ if (filesec_get_property(tmp_fsec, FILESEC_ACL, &acl) == 0) {
20792079+ if (acl_set_fd(s->dst_fd, acl) == -1) {
20802080+ copyfile_warn("could not apply acl to destination file %s from source file %s", NS(s->dst), NS(s->src));
20812081+ }
20822082+ acl_free(acl);
20832083+ }
20842084+ }
20852085+#undef NS
20862086+ break;
20872087+ case COPYFILE_STAT:
20882088+ (void)fchmod(s->dst_fd, s->sb.st_mode);
20892089+ break;
20902090+ }
20912091+ filesec_free(tmp_fsec);
20922092+exit:
20932093+ filesec_free(fsec_dst);
20942094+ if (acl_src) acl_free(acl_src);
20952095+ if (acl_dst) acl_free(acl_dst);
20962096+ if (acl_tmp) acl_free(acl_tmp);
20972097+20982098+ return ret;
20992099+21002100+error_exit:
21012101+ ret = -1;
21022102+goto exit;
21032103+21042104+}
21052105+21062106+/*
21072107+ * Attempt to set the destination file's stat information -- including
21082108+ * flags and time-related fields -- to the source's.
21092109+ */
21102110+static int copyfile_stat(copyfile_state_t s)
21112111+{
21122112+ struct timeval tval[2];
21132113+ unsigned int added_flags = 0, dst_flags = 0;
21142114+ struct stat dst_sb;
21152115+21162116+ /*
21172117+ * NFS doesn't support chflags; ignore errors as a result, since
21182118+ * we don't return failure for this.
21192119+ */
21202120+ if (s->internal_flags & cfMakeFileInvisible)
21212121+ added_flags |= UF_HIDDEN;
21222122+21232123+ /*
21242124+ * We need to check if SF_RESTRICTED was set on the destination
21252125+ * by the kernel. If it was, don't drop it.
21262126+ */
21272127+ if (fstat(s->dst_fd, &dst_sb))
21282128+ return -1;
21292129+ if (dst_sb.st_flags & SF_RESTRICTED)
21302130+ added_flags |= SF_RESTRICTED;
21312131+21322132+ /* Copy file flags, masking out any we don't want to preserve */
21332133+ dst_flags = (s->sb.st_flags & ~COPYFILE_OMIT_FLAGS) | added_flags;
21342134+ (void)fchflags(s->dst_fd, dst_flags);
21352135+21362136+ /* If this fails, we don't care */
21372137+ (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid);
21382138+21392139+ /* This may have already been done in copyfile_security() */
21402140+ (void)fchmod(s->dst_fd, s->sb.st_mode & ~S_IFMT);
21412141+21422142+ tval[0].tv_sec = s->sb.st_atime;
21432143+ tval[1].tv_sec = s->sb.st_mtime;
21442144+ tval[0].tv_usec = tval[1].tv_usec = 0;
21452145+ (void)futimes(s->dst_fd, tval);
21462146+21472147+ return 0;
21482148+}
21492149+21502150+/*
21512151+ * Similar to copyfile_security() in some ways; this
21522152+ * routine copies the extended attributes from the source,
21532153+ * and sets them on the destination.
21542154+ * The procedure is pretty simple, even if it is verbose:
21552155+ * for each named attribute on the destination, get its name, and
21562156+ * remove it. We should have none after that.
21572157+ * For each named attribute on the source, get its name, get its
21582158+ * data, and set it on the destination.
21592159+ */
21602160+static int copyfile_xattr(copyfile_state_t s)
21612161+{
21622162+ char *name;
21632163+ char *namebuf, *end;
21642164+ ssize_t xa_size;
21652165+ void *xa_dataptr;
21662166+ ssize_t bufsize = 4096;
21672167+ ssize_t asize;
21682168+ ssize_t nsize;
21692169+ int ret = 0;
21702170+ int look_for_decmpea = 0;
21712171+21722172+ /* delete EAs on destination */
21732173+ if ((nsize = flistxattr(s->dst_fd, 0, 0, 0)) > 0)
21742174+ {
21752175+ if ((namebuf = (char *) malloc(nsize)) == NULL)
21762176+ return -1;
21772177+ else
21782178+ nsize = flistxattr(s->dst_fd, namebuf, nsize, 0);
21792179+21802180+ if (nsize > 0) {
21812181+ /*
21822182+ * With this, end points to the last byte of the allocated buffer
21832183+ * This *should* be NUL, from flistxattr, but if it's not, we can
21842184+ * set it anyway -- it'll result in a truncated name, which then
21852185+ * shouldn't match when we get them later.
21862186+ */
21872187+ end = namebuf + nsize - 1;
21882188+ if (*end != 0)
21892189+ *end = 0;
21902190+ for (name = namebuf; name <= end; name += strlen(name) + 1) {
21912191+ /* If the quarantine information shows up as an EA, we skip over it */
21922192+ if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) {
21932193+ continue;
21942194+ }
21952195+ fremovexattr(s->dst_fd, name,0);
21962196+ }
21972197+ }
21982198+ free(namebuf);
21992199+ } else
22002200+ if (nsize < 0)
22012201+ {
22022202+ if (errno == ENOTSUP || errno == EPERM)
22032203+ return 0;
22042204+ else
22052205+ return -1;
22062206+ }
22072207+22082208+#ifdef DECMPFS_XATTR_NAME
22092209+ if ((s->flags & COPYFILE_DATA) &&
22102210+ (s->sb.st_flags & UF_COMPRESSED) &&
22112211+ doesdecmpfs(s->src_fd) &&
22122212+ doesdecmpfs(s->dst_fd)) {
22132213+ look_for_decmpea = XATTR_SHOWCOMPRESSION;
22142214+ }
22152215+#endif
22162216+22172217+ /* get name list of EAs on source */
22182218+ if ((nsize = flistxattr(s->src_fd, 0, 0, look_for_decmpea)) < 0)
22192219+ {
22202220+ if (errno == ENOTSUP || errno == EPERM)
22212221+ return 0;
22222222+ else
22232223+ return -1;
22242224+ } else
22252225+ if (nsize == 0)
22262226+ return 0;
22272227+22282228+ if ((namebuf = (char *) malloc(nsize)) == NULL)
22292229+ return -1;
22302230+ else
22312231+ nsize = flistxattr(s->src_fd, namebuf, nsize, look_for_decmpea);
22322232+22332233+ if (nsize <= 0) {
22342234+ free(namebuf);
22352235+ return (int)nsize;
22362236+ }
22372237+22382238+ /*
22392239+ * With this, end points to the last byte of the allocated buffer
22402240+ * This *should* be NUL, from flistxattr, but if it's not, we can
22412241+ * set it anyway -- it'll result in a truncated name, which then
22422242+ * shouldn't match when we get them later.
22432243+ */
22442244+ end = namebuf + nsize - 1;
22452245+ if (*end != 0)
22462246+ *end = 0;
22472247+22482248+ if ((xa_dataptr = (void *) malloc(bufsize)) == NULL) {
22492249+ free(namebuf);
22502250+ return -1;
22512251+ }
22522252+22532253+ for (name = namebuf; name <= end; name += strlen(name) + 1)
22542254+ {
22552255+ if (s->xattr_name) {
22562256+ free(s->xattr_name);
22572257+ s->xattr_name = NULL;
22582258+ }
22592259+22602260+ /* If the quarantine information shows up as an EA, we skip over it */
22612261+ if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0)
22622262+ continue;
22632263+22642264+ if ((xa_size = fgetxattr(s->src_fd, name, 0, 0, 0, look_for_decmpea)) < 0)
22652265+ {
22662266+ continue;
22672267+ }
22682268+22692269+ if (xa_size > bufsize)
22702270+ {
22712271+ void *tdptr = xa_dataptr;
22722272+ bufsize = xa_size;
22732273+ if ((xa_dataptr =
22742274+ (void *) realloc((void *) xa_dataptr, bufsize)) == NULL)
22752275+ {
22762276+ free(tdptr);
22772277+ ret = -1;
22782278+ continue;
22792279+ }
22802280+ }
22812281+22822282+ if ((asize = fgetxattr(s->src_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea)) < 0)
22832283+ {
22842284+ continue;
22852285+ }
22862286+22872287+ if (xa_size != asize)
22882288+ xa_size = asize;
22892289+22902290+#ifdef DECMPFS_XATTR_NAME
22912291+ if (strncmp(name, DECMPFS_XATTR_NAME, end-name) == 0)
22922292+ {
22932293+ decmpfs_disk_header *hdr = xa_dataptr;
22942294+22952295+ /*
22962296+ * If the EA has the decmpfs name, but is too
22972297+ * small, or doesn't have the right magic number,
22982298+ * or isn't the right type, we'll just skip it.
22992299+ * This means it won't end up in the destination
23002300+ * file, and data copy will happen normally.
23012301+ */
23022302+ if ((size_t)xa_size < sizeof(decmpfs_disk_header)) {
23032303+ continue;
23042304+ }
23052305+ if (OSSwapLittleToHostInt32(hdr->compression_magic) != DECMPFS_MAGIC) {
23062306+ continue;
23072307+ }
23082308+ /*
23092309+ * From AppleFSCompression documentation:
23102310+ * "It is incumbent on the aware copy engine to identify
23112311+ * the type of compression being used, and to perform an
23122312+ * unaware copy of any file it does not recognize."
23132313+ *
23142314+ * Compression Types are defined in:
23152315+ * "AppleFSCompression/Common/compressorCommon.h"
23162316+ *
23172317+ * Unfortunately, they don't provide a way to dynamically
23182318+ * determine what possible compression_type values exist,
23192319+ * so we have to update this every time a new compression_type
23202320+ * is added (Types 7->10 were added in Yosemite)
23212321+ *
23222322+ * Ubiquity faulting file compression type 0x80000001 are
23232323+ * deprecated as of Yosemite, per rdar://17714998 don't copy the
23242324+ * decmpfs xattr on these files, zero byte files are safer
23252325+ * than a fault nobody knows how to handle.
23262326+ */
23272327+ switch (OSSwapLittleToHostInt32(hdr->compression_type)) {
23282328+ case 3: /* zlib-compressed data in xattr */
23292329+ case 4: /* 64k chunked zlib-compressed data in resource fork */
23302330+23312331+ case 7: /* LZVN-compressed data in xattr */
23322332+ case 8: /* 64k chunked LZVN-compressed data in resource fork */
23332333+23342334+ case 9: /* uncompressed data in xattr (similar to but not identical to CMP_Type1) */
23352335+ case 10: /* 64k chunked uncompressed data in resource fork */
23362336+23372337+ /* valid compression type, we want to copy. */
23382338+ break;
23392339+23402340+ case 5: /* specifies de-dup within the generation store. Don't copy decmpfs xattr. */
23412341+ copyfile_debug(3, "compression_type <5> on attribute com.apple.decmpfs for src file %s is not copied.",
23422342+ s->src ? s->src : "(null string)");
23432343+ continue;
23442344+23452345+ case 6: /* unused */
23462346+ case 0x80000001: /* faulting files are deprecated, don't copy decmpfs xattr */
23472347+ default:
23482348+ copyfile_warn("Invalid compression_type <%d> on attribute %s for src file %s",
23492349+ OSSwapLittleToHostInt32(hdr->compression_type), name, s->src ? s->src : "(null string)");
23502350+ continue;
23512351+ }
23522352+ s->internal_flags |= cfSawDecmpEA;
23532353+ }
23542354+#endif
23552355+23562356+ // If we have a copy intention stated, and the EA is to be ignored, we ignore it
23572357+ if (s->copyIntent
23582358+ && xattr_preserve_for_intent(name, s->copyIntent) == 0)
23592359+ continue;
23602360+23612361+ s->xattr_name = strdup(name);
23622362+23632363+ if (s->statuscb) {
23642364+ int rv;
23652365+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
23662366+ if (rv == COPYFILE_QUIT) {
23672367+ s->err = ECANCELED;
23682368+ goto out;
23692369+ } else if (rv == COPYFILE_SKIP) {
23702370+ continue;
23712371+ }
23722372+ }
23732373+ if (fsetxattr(s->dst_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea) < 0)
23742374+ {
23752375+ if (s->statuscb)
23762376+ {
23772377+ int rv;
23782378+ if (s->xattr_name == NULL)
23792379+ s->xattr_name = strdup(name);
23802380+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
23812381+ if (rv == COPYFILE_QUIT)
23822382+ {
23832383+ s->err = ECANCELED;
23842384+ ret = -1;
23852385+ goto out;
23862386+ }
23872387+ }
23882388+ else
23892389+ {
23902390+ ret = -1;
23912391+ copyfile_warn("could not set attributes %s on destination file descriptor: %s", name, strerror(errno));
23922392+ continue;
23932393+ }
23942394+ }
23952395+ if (s->statuscb) {
23962396+ int rv;
23972397+ if (s->xattr_name == NULL)
23982398+ s->xattr_name = strdup(name);
23992399+24002400+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
24012401+ if (rv == COPYFILE_QUIT) {
24022402+ s->err = ECANCELED;
24032403+ goto out;
24042404+ }
24052405+ }
24062406+ }
24072407+out:
24082408+ if (namebuf)
24092409+ free(namebuf);
24102410+ free((void *) xa_dataptr);
24112411+ if (s->xattr_name) {
24122412+ free(s->xattr_name);
24132413+ s->xattr_name = NULL;
24142414+ }
24152415+ return ret;
24162416+}
24172417+24182418+/*
24192419+ * API interface into getting data from the opaque data type.
24202420+ */
24212421+int copyfile_state_get(copyfile_state_t s, uint32_t flag, void *ret)
24222422+{
24232423+ if (ret == NULL)
24242424+ {
24252425+ errno = EFAULT;
24262426+ return -1;
24272427+ }
24282428+24292429+ switch(flag)
24302430+ {
24312431+ case COPYFILE_STATE_SRC_FD:
24322432+ *(int*)ret = s->src_fd;
24332433+ break;
24342434+ case COPYFILE_STATE_DST_FD:
24352435+ *(int*)ret = s->dst_fd;
24362436+ break;
24372437+ case COPYFILE_STATE_SRC_FILENAME:
24382438+ *(char**)ret = s->src;
24392439+ break;
24402440+ case COPYFILE_STATE_DST_FILENAME:
24412441+ *(char**)ret = s->dst;
24422442+ break;
24432443+ case COPYFILE_STATE_QUARANTINE:
24442444+ *(qtn_file_t*)ret = s->qinfo;
24452445+ break;
24462446+#if 0
24472447+ case COPYFILE_STATE_STATS:
24482448+ ret = s->stats.global;
24492449+ break;
24502450+ case COPYFILE_STATE_PROGRESS_CB:
24512451+ ret = s->callbacks.progress;
24522452+ break;
24532453+#endif
24542454+#ifdef COPYFILE_STATE_STATUS_CB
24552455+ case COPYFILE_STATE_STATUS_CB:
24562456+ *(copyfile_callback_t*)ret = s->statuscb;
24572457+ break;
24582458+ case COPYFILE_STATE_STATUS_CTX:
24592459+ *(void**)ret = s->ctx;
24602460+ break;
24612461+ case COPYFILE_STATE_COPIED:
24622462+ *(off_t*)ret = s->totalCopied;
24632463+ break;
24642464+#endif
24652465+#ifdef COPYFILE_STATE_XATTRNAME
24662466+ case COPYFILE_STATE_XATTRNAME:
24672467+ *(char**)ret = s->xattr_name;
24682468+ break;
24692469+#endif
24702470+#ifdef COPYFILE_STATE_INTENT
24712471+ case COPYFILE_STATE_INTENT:
24722472+ *(xattr_operation_intent_t*)ret = s->copyIntent;
24732473+ break;
24742474+#endif
24752475+ default:
24762476+ errno = EINVAL;
24772477+ ret = NULL;
24782478+ return -1;
24792479+ }
24802480+ return 0;
24812481+}
24822482+24832483+/*
24842484+ * Public API for setting state data (remember that the state is
24852485+ * an opaque data type).
24862486+ */
24872487+int copyfile_state_set(copyfile_state_t s, uint32_t flag, const void * thing)
24882488+{
24892489+#define copyfile_set_string(DST, SRC) \
24902490+ do { \
24912491+ if (SRC != NULL) { \
24922492+ DST = strdup((char *)SRC); \
24932493+ } else { \
24942494+ if (DST != NULL) { \
24952495+ free(DST); \
24962496+ } \
24972497+ DST = NULL; \
24982498+ } \
24992499+ } while (0)
25002500+25012501+ if (thing == NULL)
25022502+ {
25032503+ errno = EFAULT;
25042504+ return -1;
25052505+ }
25062506+25072507+ switch(flag)
25082508+ {
25092509+ case COPYFILE_STATE_SRC_FD:
25102510+ s->src_fd = *(int*)thing;
25112511+ break;
25122512+ case COPYFILE_STATE_DST_FD:
25132513+ s->dst_fd = *(int*)thing;
25142514+ break;
25152515+ case COPYFILE_STATE_SRC_FILENAME:
25162516+ copyfile_set_string(s->src, thing);
25172517+ break;
25182518+ case COPYFILE_STATE_DST_FILENAME:
25192519+ copyfile_set_string(s->dst, thing);
25202520+ break;
25212521+ case COPYFILE_STATE_QUARANTINE:
25222522+ if (s->qinfo)
25232523+ {
25242524+ qtn_file_free(s->qinfo);
25252525+ s->qinfo = NULL;
25262526+ }
25272527+ if (*(qtn_file_t*)thing)
25282528+ s->qinfo = qtn_file_clone(*(qtn_file_t*)thing);
25292529+ break;
25302530+#if 0
25312531+ case COPYFILE_STATE_STATS:
25322532+ s->stats.global = thing;
25332533+ break;
25342534+ case COPYFILE_STATE_PROGRESS_CB:
25352535+ s->callbacks.progress = thing;
25362536+ break;
25372537+#endif
25382538+#ifdef COPYFILE_STATE_STATUS_CB
25392539+ case COPYFILE_STATE_STATUS_CB:
25402540+ s->statuscb = (copyfile_callback_t)thing;
25412541+ break;
25422542+ case COPYFILE_STATE_STATUS_CTX:
25432543+ s->ctx = (void*)thing;
25442544+ break;
25452545+#endif
25462546+#ifdef COPYFILE_STATE_INTENT
25472547+ case COPYFILE_STATE_INTENT:
25482548+ s->copyIntent = *(xattr_operation_intent_t*)thing;
25492549+ break;
25502550+#endif
25512551+ default:
25522552+ errno = EINVAL;
25532553+ return -1;
25542554+ }
25552555+ return 0;
25562556+#undef copyfile_set_string
25572557+}
25582558+25592559+25602560+/*
25612561+ * Make this a standalone program for testing purposes by
25622562+ * defining _COPYFILE_TEST.
25632563+ */
25642564+#ifdef _COPYFILE_TEST
25652565+#define COPYFILE_OPTION(x) { #x, COPYFILE_ ## x },
25662566+25672567+struct {char *s; int v;} opts[] = {
25682568+ COPYFILE_OPTION(ACL)
25692569+ COPYFILE_OPTION(STAT)
25702570+ COPYFILE_OPTION(XATTR)
25712571+ COPYFILE_OPTION(DATA)
25722572+ COPYFILE_OPTION(SECURITY)
25732573+ COPYFILE_OPTION(METADATA)
25742574+ COPYFILE_OPTION(ALL)
25752575+ COPYFILE_OPTION(NOFOLLOW_SRC)
25762576+ COPYFILE_OPTION(NOFOLLOW_DST)
25772577+ COPYFILE_OPTION(NOFOLLOW)
25782578+ COPYFILE_OPTION(EXCL)
25792579+ COPYFILE_OPTION(MOVE)
25802580+ COPYFILE_OPTION(UNLINK)
25812581+ COPYFILE_OPTION(PACK)
25822582+ COPYFILE_OPTION(UNPACK)
25832583+ COPYFILE_OPTION(CHECK)
25842584+ COPYFILE_OPTION(VERBOSE)
25852585+ COPYFILE_OPTION(DEBUG)
25862586+ {NULL, 0}
25872587+};
25882588+25892589+int main(int c, char *v[])
25902590+{
25912591+ int i;
25922592+ int flags = 0;
25932593+25942594+ if (c < 3)
25952595+ errx(1, "insufficient arguments");
25962596+25972597+ while(c-- > 3)
25982598+ {
25992599+ for (i = 0; opts[i].s != NULL; ++i)
26002600+ {
26012601+ if (strcasecmp(opts[i].s, v[c]) == 0)
26022602+ {
26032603+ printf("option %d: %s <- %d\n", c, opts[i].s, opts[i].v);
26042604+ flags |= opts[i].v;
26052605+ break;
26062606+ }
26072607+ }
26082608+ }
26092609+26102610+ return copyfile(v[1], v[2], NULL, flags);
26112611+}
26122612+#endif
26132613+/*
26142614+ * Apple Double Create
26152615+ *
26162616+ * Create an Apple Double "._" file from a file's extented attributes
26172617+ *
26182618+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
26192619+ */
26202620+26212621+26222622+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
26232623+26242624+#define XATTR_MAXATTRLEN (16*1024*1024)
26252625+26262626+26272627+/*
26282628+ Typical "._" AppleDouble Header File layout:
26292629+ ------------------------------------------------------------
26302630+ MAGIC 0x00051607
26312631+ VERSION 0x00020000
26322632+ FILLER 0
26332633+ COUNT 2
26342634+ .-- AD ENTRY[0] Finder Info Entry (must be first)
26352635+ .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
26362636+ | '-> FINDER INFO
26372637+ | ///////////// Fixed Size Data (32 bytes)
26382638+ | EXT ATTR HDR
26392639+ | /////////////
26402640+ | ATTR ENTRY[0] --.
26412641+ | ATTR ENTRY[1] --+--.
26422642+ | ATTR ENTRY[2] --+--+--.
26432643+ | ... | | |
26442644+ | ATTR ENTRY[N] --+--+--+--.
26452645+ | ATTR DATA 0 <-' | | |
26462646+ | //////////// | | |
26472647+ | ATTR DATA 1 <----' | |
26482648+ | ///////////// | |
26492649+ | ATTR DATA 2 <-------' |
26502650+ | ///////////// |
26512651+ | ... |
26522652+ | ATTR DATA N <----------'
26532653+ | /////////////
26542654+ | Attribute Free Space
26552655+ |
26562656+ '----> RESOURCE FORK
26572657+ ///////////// Variable Sized Data
26582658+ /////////////
26592659+ /////////////
26602660+ /////////////
26612661+ /////////////
26622662+ /////////////
26632663+ ...
26642664+ /////////////
26652665+26662666+ ------------------------------------------------------------
26672667+26682668+ NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
26692669+ stored as part of the Finder Info. The length in the Finder
26702670+ Info AppleDouble entry includes the length of the extended
26712671+ attribute header, attribute entries, and attribute data.
26722672+*/
26732673+26742674+26752675+/*
26762676+ * On Disk Data Structures
26772677+ *
26782678+ * Note: Motorola 68K alignment and big-endian.
26792679+ *
26802680+ * See RFC 1740 for additional information about the AppleDouble file format.
26812681+ *
26822682+ */
26832683+26842684+#define ADH_MAGIC 0x00051607
26852685+#define ADH_VERSION 0x00020000
26862686+#define ADH_MACOSX "Mac OS X "
26872687+26882688+/*
26892689+ * AppleDouble Entry ID's
26902690+ */
26912691+#define AD_DATA 1 /* Data fork */
26922692+#define AD_RESOURCE 2 /* Resource fork */
26932693+#define AD_REALNAME 3 /* File's name on home file system */
26942694+#define AD_COMMENT 4 /* Standard Mac comment */
26952695+#define AD_ICONBW 5 /* Mac black & white icon */
26962696+#define AD_ICONCOLOR 6 /* Mac color icon */
26972697+#define AD_UNUSED 7 /* Not used */
26982698+#define AD_FILEDATES 8 /* File dates; create, modify, etc */
26992699+#define AD_FINDERINFO 9 /* Mac Finder info & extended info */
27002700+#define AD_MACINFO 10 /* Mac file info, attributes, etc */
27012701+#define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
27022702+#define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
27032703+#define AD_AFPNAME 13 /* Short name on AFP server */
27042704+#define AD_AFPINFO 14 /* AFP file info, attrib., etc */
27052705+#define AD_AFPDIRID 15 /* AFP directory ID */
27062706+#define AD_ATTRIBUTES AD_FINDERINFO
27072707+27082708+27092709+#define ATTR_FILE_PREFIX "._"
27102710+#define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
27112711+27122712+#define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
27132713+27142714+/* Implementation Limits */
27152715+#define ATTR_MAX_SIZE (16*1024*1024) /* 16 megabyte maximum attribute data size */
27162716+#define ATTR_MAX_NAME_LEN 128
27172717+#define ATTR_MAX_HDR_SIZE (65536+18)
27182718+27192719+/*
27202720+ * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
27212721+ * size supported (including the attribute entries). All of
27222722+ * the attribute entries must reside within this limit.
27232723+ */
27242724+27252725+27262726+#define FINDERINFOSIZE 32
27272727+27282728+typedef struct apple_double_entry
27292729+{
27302730+ u_int32_t type; /* entry type: see list, 0 invalid */
27312731+ u_int32_t offset; /* entry data offset from the beginning of the file. */
27322732+ u_int32_t length; /* entry data length in bytes. */
27332733+} __attribute__((aligned(2), packed)) apple_double_entry_t;
27342734+27352735+27362736+typedef struct apple_double_header
27372737+{
27382738+ u_int32_t magic; /* == ADH_MAGIC */
27392739+ u_int32_t version; /* format version: 2 = 0x00020000 */
27402740+ u_int32_t filler[4];
27412741+ u_int16_t numEntries; /* number of entries which follow */
27422742+ apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */
27432743+ u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */
27442744+ u_int8_t pad[2]; /* get better alignment inside attr_header */
27452745+} __attribute__((aligned(2), packed)) apple_double_header_t;
27462746+27472747+27482748+/* Entries are aligned on 4 byte boundaries */
27492749+typedef struct attr_entry
27502750+{
27512751+ u_int32_t offset; /* file offset to data */
27522752+ u_int32_t length; /* size of attribute data */
27532753+ u_int16_t flags;
27542754+ u_int8_t namelen; /* length of name including NULL termination char */
27552755+ u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
27562756+} __attribute__((aligned(2), packed)) attr_entry_t;
27572757+27582758+27592759+27602760+/* Header + entries must fit into 64K */
27612761+typedef struct attr_header
27622762+{
27632763+ apple_double_header_t appledouble;
27642764+ u_int32_t magic; /* == ATTR_HDR_MAGIC */
27652765+ u_int32_t debug_tag; /* for debugging == file id of owning file */
27662766+ u_int32_t total_size; /* total size of attribute header + entries + data */
27672767+ u_int32_t data_start; /* file offset to attribute data area */
27682768+ u_int32_t data_length; /* length of attribute data area */
27692769+ u_int32_t reserved[3];
27702770+ u_int16_t flags;
27712771+ u_int16_t num_attrs;
27722772+} __attribute__((aligned(2), packed)) attr_header_t;
27732773+27742774+/* Empty Resource Fork Header */
27752775+/* This comes by way of xnu's vfs_xattr.c */
27762776+typedef struct rsrcfork_header {
27772777+ u_int32_t fh_DataOffset;
27782778+ u_int32_t fh_MapOffset;
27792779+ u_int32_t fh_DataLength;
27802780+ u_int32_t fh_MapLength;
27812781+ u_int8_t systemData[112];
27822782+ u_int8_t appData[128];
27832783+ u_int32_t mh_DataOffset;
27842784+ u_int32_t mh_MapOffset;
27852785+ u_int32_t mh_DataLength;
27862786+ u_int32_t mh_MapLength;
27872787+ u_int32_t mh_Next;
27882788+ u_int16_t mh_RefNum;
27892789+ u_int8_t mh_Attr;
27902790+ u_int8_t mh_InMemoryAttr;
27912791+ u_int16_t mh_Types;
27922792+ u_int16_t mh_Names;
27932793+ u_int16_t typeCount;
27942794+} __attribute__((aligned(2), packed)) rsrcfork_header_t;
27952795+#define RF_FIRST_RESOURCE 256
27962796+#define RF_NULL_MAP_LENGTH 30
27972797+#define RF_EMPTY_TAG "This resource fork intentionally left blank "
27982798+27992799+static const rsrcfork_header_t empty_rsrcfork_header = {
28002800+ OSSwapHostToBigInt32(RF_FIRST_RESOURCE), // fh_DataOffset
28012801+ OSSwapHostToBigInt32(RF_FIRST_RESOURCE), // fh_MapOffset
28022802+ 0, // fh_DataLength
28032803+ OSSwapHostToBigInt32(RF_NULL_MAP_LENGTH), // fh_MapLength
28042804+ { RF_EMPTY_TAG, }, // systemData
28052805+ { 0 }, // appData
28062806+ OSSwapHostToBigInt32(RF_FIRST_RESOURCE), // mh_DataOffset
28072807+ OSSwapHostToBigInt32(RF_FIRST_RESOURCE), // mh_MapOffset
28082808+ 0, // mh_DataLength
28092809+ OSSwapHostToBigInt32(RF_NULL_MAP_LENGTH), // mh_MapLength
28102810+ 0, // mh_Next
28112811+ 0, // mh_RefNum
28122812+ 0, // mh_Attr
28132813+ 0, // mh_InMemoryAttr
28142814+ OSSwapHostToBigInt16(RF_NULL_MAP_LENGTH - 2), // mh_Types
28152815+ OSSwapHostToBigInt16(RF_NULL_MAP_LENGTH), // mh_Names
28162816+ OSSwapHostToBigInt16(-1), // typeCount
28172817+};
28182818+28192819+#define SWAP16(x) OSSwapBigToHostInt16(x)
28202820+#define SWAP32(x) OSSwapBigToHostInt32(x)
28212821+#define SWAP64(x) OSSwapBigToHostInt64(x)
28222822+28232823+#define ATTR_ALIGN 3L /* Use four-byte alignment */
28242824+28252825+#define ATTR_ENTRY_LENGTH(namelen) \
28262826+ ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
28272827+28282828+#define ATTR_NEXT(ae) \
28292829+ (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
28302830+28312831+#define XATTR_SECURITY_NAME "com.apple.acl.text"
28322832+28332833+/*
28342834+ * Endian swap Apple Double header
28352835+ */
28362836+static void
28372837+swap_adhdr(apple_double_header_t *adh)
28382838+{
28392839+#if BYTE_ORDER == LITTLE_ENDIAN
28402840+ int count;
28412841+ int i;
28422842+28432843+ count = (adh->magic == ADH_MAGIC) ? adh->numEntries : SWAP16(adh->numEntries);
28442844+28452845+ adh->magic = SWAP32 (adh->magic);
28462846+ adh->version = SWAP32 (adh->version);
28472847+ adh->numEntries = SWAP16 (adh->numEntries);
28482848+28492849+ for (i = 0; i < count; i++)
28502850+ {
28512851+ adh->entries[i].type = SWAP32 (adh->entries[i].type);
28522852+ adh->entries[i].offset = SWAP32 (adh->entries[i].offset);
28532853+ adh->entries[i].length = SWAP32 (adh->entries[i].length);
28542854+ }
28552855+#else
28562856+ (void)adh;
28572857+#endif
28582858+}
28592859+28602860+/*
28612861+ * Endian swap a single attr_entry_t
28622862+ */
28632863+static void
28642864+swap_attrhdr_entry(attr_entry_t *ae)
28652865+{
28662866+#if BYTE_ORDER == LITTLE_ENDIAN
28672867+ ae->offset = SWAP32 (ae->offset);
28682868+ ae->length = SWAP32 (ae->length);
28692869+ ae->flags = SWAP16 (ae->flags);
28702870+#else
28712871+ (void)ae;
28722872+#endif
28732873+}
28742874+28752875+/*
28762876+ * For a validated/endian swapped attr_header_t*
28772877+ * ah, endian swap all of the entries.
28782878+ */
28792879+static void
28802880+swap_attrhdr_entries(attr_header_t *ah)
28812881+{
28822882+#if BYTE_ORDER == LITTLE_ENDIAN
28832883+ int i;
28842884+ int count;
28852885+ attr_entry_t *entry;
28862886+ attr_entry_t *next;
28872887+28882888+ /* If we're in copyfile_pack, num_args is native endian,
28892889+ * if we're in _unpack, num_args is big endian. Use
28902890+ * the magic number to test for endianess.
28912891+ */
28922892+ count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs);
28932893+28942894+ entry = (attr_entry_t *)(&ah[1]);
28952895+ for (i = 0; i < count; i++) {
28962896+ next = ATTR_NEXT(entry);
28972897+ swap_attrhdr_entry(entry);
28982898+ entry = next;
28992899+ }
29002900+#else
29012901+ (void)ah;
29022902+#endif
29032903+}
29042904+29052905+/*
29062906+ * Endian swap extended attributes header
29072907+ */
29082908+static void
29092909+swap_attrhdr(attr_header_t *ah)
29102910+{
29112911+#if BYTE_ORDER == LITTLE_ENDIAN
29122912+ ah->magic = SWAP32 (ah->magic);
29132913+ ah->debug_tag = SWAP32 (ah->debug_tag);
29142914+ ah->total_size = SWAP32 (ah->total_size);
29152915+ ah->data_start = SWAP32 (ah->data_start);
29162916+ ah->data_length = SWAP32 (ah->data_length);
29172917+ ah->flags = SWAP16 (ah->flags);
29182918+ ah->num_attrs = SWAP16 (ah->num_attrs);
29192919+#else
29202920+ (void)ah;
29212921+#endif
29222922+}
29232923+29242924+static const u_int32_t emptyfinfo[8] = {0};
29252925+29262926+/*
29272927+ * Given an Apple Double file in src, turn it into a
29282928+ * normal file (possibly with multiple forks, EAs, and
29292929+ * ACLs) in dst.
29302930+ */
29312931+static int copyfile_unpack(copyfile_state_t s)
29322932+{
29332933+ ssize_t bytes;
29342934+ void * buffer, * endptr, * dataptr = NULL;
29352935+ apple_double_header_t *adhdr;
29362936+ ssize_t hdrsize;
29372937+ int error = 0;
29382938+29392939+ if (s->sb.st_size < ATTR_MAX_HDR_SIZE)
29402940+ hdrsize = (ssize_t)s->sb.st_size;
29412941+ else
29422942+ hdrsize = ATTR_MAX_HDR_SIZE;
29432943+29442944+ buffer = calloc(1, hdrsize);
29452945+ if (buffer == NULL) {
29462946+ copyfile_debug(1, "copyfile_unpack: calloc(1, %zu) returned NULL", hdrsize);
29472947+ error = -1;
29482948+ goto exit;
29492949+ } else
29502950+ endptr = (char*)buffer + hdrsize;
29512951+29522952+ bytes = pread(s->src_fd, buffer, hdrsize, 0);
29532953+29542954+ if (bytes < 0)
29552955+ {
29562956+ copyfile_debug(1, "pread returned: %zd", bytes);
29572957+ error = -1;
29582958+ goto exit;
29592959+ }
29602960+ if (bytes < hdrsize)
29612961+ {
29622962+ copyfile_debug(1,
29632963+ "pread couldn't read entire header: %d of %d",
29642964+ (int)bytes, (int)s->sb.st_size);
29652965+ error = -1;
29662966+ goto exit;
29672967+ }
29682968+ adhdr = (apple_double_header_t *)buffer;
29692969+29702970+ /*
29712971+ * Check for Apple Double file.
29722972+ */
29732973+ if ((size_t)bytes < sizeof(apple_double_header_t) - 2 ||
29742974+ SWAP32(adhdr->magic) != ADH_MAGIC ||
29752975+ SWAP32(adhdr->version) != ADH_VERSION ||
29762976+ SWAP16(adhdr->numEntries) != 2 ||
29772977+ SWAP32(adhdr->entries[0].type) != AD_FINDERINFO)
29782978+ {
29792979+ if (COPYFILE_VERBOSE & s->flags)
29802980+ copyfile_warn("Not a valid Apple Double header");
29812981+ error = -1;
29822982+ goto exit;
29832983+ }
29842984+ swap_adhdr(adhdr);
29852985+29862986+ /*
29872987+ * Remove any extended attributes on the target.
29882988+ */
29892989+29902990+ if ((bytes = flistxattr(s->dst_fd, 0, 0, 0)) > 0)
29912991+ {
29922992+ char *namebuf, *name;
29932993+29942994+ if ((namebuf = (char*) malloc(bytes)) == NULL)
29952995+ {
29962996+ s->err = ENOMEM;
29972997+ goto exit;
29982998+ }
29992999+ bytes = flistxattr(s->dst_fd, namebuf, bytes, 0);
30003000+30013001+ if (bytes > 0)
30023002+ for (name = namebuf; name < namebuf + bytes; name += strlen(name) + 1)
30033003+ (void)fremovexattr(s->dst_fd, name, 0);
30043004+30053005+ free(namebuf);
30063006+ }
30073007+ else if (bytes < 0)
30083008+ {
30093009+ if (errno != ENOTSUP && errno != EPERM)
30103010+ goto exit;
30113011+ }
30123012+30133013+ /*
30143014+ * Extract the extended attributes.
30153015+ *
30163016+ * >>> WARNING <<<
30173017+ * This assumes that the data is already in memory (not
30183018+ * the case when there are lots of attributes or one of
30193019+ * the attributes is very large.
30203020+ */
30213021+ if (adhdr->entries[0].length > FINDERINFOSIZE)
30223022+ {
30233023+ attr_header_t *attrhdr;
30243024+ attr_entry_t *entry;
30253025+ int count;
30263026+ int i;
30273027+30283028+ if ((size_t)hdrsize < sizeof(attr_header_t)) {
30293029+ copyfile_warn("bad attribute header: %zu < %zu", hdrsize, sizeof(attr_header_t));
30303030+ error = -1;
30313031+ goto exit;
30323032+ }
30333033+30343034+ attrhdr = (attr_header_t *)buffer;
30353035+ swap_attrhdr(attrhdr);
30363036+ if (attrhdr->magic != ATTR_HDR_MAGIC)
30373037+ {
30383038+ if (COPYFILE_VERBOSE & s->flags)
30393039+ copyfile_warn("bad attribute header");
30403040+ error = -1;
30413041+ goto exit;
30423042+ }
30433043+ count = attrhdr->num_attrs;
30443044+ entry = (attr_entry_t *)&attrhdr[1];
30453045+30463046+ for (i = 0; i < count; i++)
30473047+ {
30483048+ /*
30493049+ * First we do some simple sanity checking.
30503050+ * +) See if entry is within the buffer's range;
30513051+ *
30523052+ * +) Check the attribute name length; if it's longer than the
30533053+ * maximum, we truncate it down. (We could error out as well;
30543054+ * I'm not sure which is the better way to go here.)
30553055+ *
30563056+ * +) If, given the name length, it goes beyond the end of
30573057+ * the buffer, error out.
30583058+ *
30593059+ * +) If the last byte isn't a NUL, make it a NUL. (Since we
30603060+ * truncated the name length above, we truncate the name here.)
30613061+ *
30623062+ * +) If entry->offset is so large that it causes dataptr to
30633063+ * go beyond the end of the buffer -- or, worse, so large that
30643064+ * it wraps around! -- we error out.
30653065+ *
30663066+ * +) If entry->length would cause the entry to go beyond the
30673067+ * end of the buffer (or, worse, wrap around to before it),
30683068+ * *or* if the length is larger than the hdrsize, we error out.
30693069+ * (An explanation of that: what we're checking for there is
30703070+ * the small range of values such that offset+length would cause
30713071+ * it to go beyond endptr, and then wrap around past buffer. We
30723072+ * care about this because we are passing entry->length down to
30733073+ * fgetxattr() below, and an erroneously large value could cause
30743074+ * problems there. By making sure that it's less than hdrsize,
30753075+ * which has already been sanity-checked above, we're safe.
30763076+ * That may mean that the check against < buffer is unnecessary.)
30773077+ */
30783078+ if ((void*)entry >= endptr || (void*)entry < buffer) {
30793079+ if (COPYFILE_VERBOSE & s->flags)
30803080+ copyfile_warn("Incomplete or corrupt attribute entry");
30813081+ error = -1;
30823082+ s->err = EINVAL;
30833083+ goto exit;
30843084+ }
30853085+30863086+ if (((char*)entry + sizeof(*entry)) > (char*)endptr) {
30873087+ if (COPYFILE_VERBOSE & s->flags)
30883088+ copyfile_warn("Incomplete or corrupt attribute entry");
30893089+ error = -1;
30903090+ s->err = EINVAL;
30913091+ goto exit;
30923092+ }
30933093+30943094+ /*
30953095+ * Endian swap the entry we're looking at. Previously
30963096+ * we did this swap as part of swap_attrhdr, but that
30973097+ * allowed a maliciously constructed file to overrun
30983098+ * our allocation. Instead do the swap after we've verified
30993099+ * the entry struct is within the buffer's range.
31003100+ */
31013101+ swap_attrhdr_entry(entry);
31023102+31033103+ if (entry->namelen < 2) {
31043104+ if (COPYFILE_VERBOSE & s->flags)
31053105+ copyfile_warn("Corrupt attribute entry (only %d bytes)", entry->namelen);
31063106+ error = -1;
31073107+ s->err = EINVAL;
31083108+ goto exit;
31093109+ }
31103110+31113111+ if (entry->namelen > XATTR_MAXNAMELEN + 1) {
31123112+ if (COPYFILE_VERBOSE & s->flags)
31133113+ copyfile_warn("Corrupt attribute entry (name length is %d bytes)", entry->namelen);
31143114+ error = -1;
31153115+ s->err = EINVAL;
31163116+ goto exit;
31173117+ }
31183118+31193119+ if ((void*)(entry->name + entry->namelen) > endptr) {
31203120+ if (COPYFILE_VERBOSE & s->flags)
31213121+ copyfile_warn("Incomplete or corrupt attribute entry");
31223122+ error = -1;
31233123+ s->err = EINVAL;
31243124+ goto exit;
31253125+ }
31263126+31273127+ /* Because namelen includes the NUL, we check one byte back */
31283128+ if (entry->name[entry->namelen-1] != 0) {
31293129+ if (COPYFILE_VERBOSE & s->flags)
31303130+ copyfile_warn("Corrupt attribute entry (name is not NUL-terminated)");
31313131+ error = -1;
31323132+ s->err = EINVAL;
31333133+ goto exit;
31343134+ }
31353135+31363136+ copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u",
31373137+ entry->name, entry->length, entry->offset);
31383138+31393139+#if 0
31403140+ dataptr = (char *)attrhdr + entry->offset;
31413141+31423142+ if (dataptr > endptr || dataptr < buffer) {
31433143+ copyfile_debug(1, "Entry %d overflows: offset = %u", i, entry->offset);
31443144+ error = -1;
31453145+ s->err = EINVAL; /* Invalid buffer */
31463146+ goto exit;
31473147+ }
31483148+31493149+ if (((char*)dataptr + entry->length) > (char*)endptr ||
31503150+ (((char*)dataptr + entry->length) < (char*)buffer) ||
31513151+ (entry->length > (size_t)hdrsize)) {
31523152+ if (COPYFILE_VERBOSE & s->flags)
31533153+ copyfile_warn("Incomplete or corrupt attribute entry");
31543154+ copyfile_debug(1, "Entry %d length overflows: offset = %u, length = %u",
31553155+ i, entry->offset, entry->length);
31563156+ error = -1;
31573157+ s->err = EINVAL; /* Invalid buffer */
31583158+ goto exit;
31593159+ }
31603160+31613161+#else
31623162+ dataptr = malloc(entry->length);
31633163+ if (dataptr == NULL) {
31643164+ copyfile_debug(1, "no memory for %u bytes\n", entry->length);
31653165+ error = -1;
31663166+ s->err = ENOMEM;
31673167+ goto exit;
31683168+ }
31693169+ if (pread(s->src_fd, dataptr, entry->length, entry->offset) != (ssize_t)entry->length) {
31703170+ copyfile_debug(1, "failed to read %u bytes at offset %u\n", entry->length, entry->offset);
31713171+ error = -1;
31723172+ s->err = EINVAL;
31733173+ goto exit;
31743174+ }
31753175+#endif
31763176+31773177+ if (strcmp((char*)entry->name, XATTR_QUARANTINE_NAME) == 0)
31783178+ {
31793179+ qtn_file_t tqinfo = NULL;
31803180+31813181+ if (s->qinfo == NULL)
31823182+ {
31833183+ tqinfo = qtn_file_alloc();
31843184+ if (tqinfo)
31853185+ {
31863186+ int x;
31873187+ if ((x = qtn_file_init_with_data(tqinfo, dataptr, entry->length)) != 0)
31883188+ {
31893189+ copyfile_warn("qtn_file_init_with_data failed: %s", qtn_error(x));
31903190+ qtn_file_free(tqinfo);
31913191+ tqinfo = NULL;
31923192+ }
31933193+ }
31943194+ }
31953195+ else
31963196+ {
31973197+ tqinfo = s->qinfo;
31983198+ }
31993199+ if (tqinfo)
32003200+ {
32013201+ int x;
32023202+ x = qtn_file_apply_to_fd(tqinfo, s->dst_fd);
32033203+ if (x != 0) {
32043204+ copyfile_warn("qtn_file_apply_to_fd failed: %s", qtn_error(x));
32053205+ if (s->statuscb) {
32063206+ int rv;
32073207+ s->xattr_name = (char*)XATTR_QUARANTINE_NAME;
32083208+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
32093209+ s->xattr_name = NULL;
32103210+ if (rv == COPYFILE_QUIT) {
32113211+ error = s->err = x < 0 ? ENOTSUP : errno;
32123212+ goto exit;
32133213+ }
32143214+ } else {
32153215+ error = s->err = x < 0 ? ENOTSUP : errno;
32163216+ goto exit;
32173217+ }
32183218+ }
32193219+ }
32203220+ if (tqinfo && !s->qinfo)
32213221+ {
32223222+ qtn_file_free(tqinfo);
32233223+ }
32243224+ }
32253225+ /* Look for ACL data */
32263226+ else if (strcmp((char*)entry->name, XATTR_SECURITY_NAME) == 0)
32273227+ {
32283228+ acl_t acl;
32293229+ struct stat sb;
32303230+ int retry = 1;
32313231+ char *tcp = dataptr;
32323232+32333233+ if (entry->length == 0) {
32343234+ /* Not sure how we got here, but we had one case
32353235+ * where it was 0. In a normal EA, we can have a 0-byte
32363236+ * payload. That means nothing in this case, so we'll
32373237+ * simply skip the EA.
32383238+ */
32393239+ error = 0;
32403240+ goto acl_done;
32413241+ }
32423242+ /*
32433243+ * acl_from_text() requires a NUL-terminated string. The ACL EA,
32443244+ * however, may not be NUL-terminated. So in that case, we need to
32453245+ * copy it to a +1 sized buffer, to ensure it's got a terminated string.
32463246+ */
32473247+ if (tcp[entry->length - 1] != 0) {
32483248+ char *tmpstr = malloc(entry->length + 1);
32493249+ if (tmpstr == NULL) {
32503250+ error = -1;
32513251+ goto exit;
32523252+ }
32533253+ strlcpy(tmpstr, tcp, entry->length + 1);
32543254+ acl = acl_from_text(tmpstr);
32553255+ free(tmpstr);
32563256+ } else {
32573257+ acl = acl_from_text(tcp);
32583258+ }
32593259+32603260+ if (acl != NULL)
32613261+ {
32623262+ filesec_t fsec_tmp;
32633263+32643264+ if ((fsec_tmp = filesec_init()) == NULL)
32653265+ error = -1;
32663266+ else if((error = fstatx_np(s->dst_fd, &sb, fsec_tmp)) < 0)
32673267+ error = -1;
32683268+ else if (filesec_set_property(fsec_tmp, FILESEC_ACL, &acl) < 0)
32693269+ error = -1;
32703270+ else {
32713271+ while (fchmodx_np(s->dst_fd, fsec_tmp) < 0)
32723272+ {
32733273+ if (errno == ENOTSUP)
32743274+ {
32753275+ if (retry && !copyfile_unset_acl(s))
32763276+ {
32773277+ retry = 0;
32783278+ continue;
32793279+ }
32803280+ }
32813281+ copyfile_warn("setting security information");
32823282+ error = -1;
32833283+ break;
32843284+ }
32853285+ }
32863286+ acl_free(acl);
32873287+ filesec_free(fsec_tmp);
32883288+32893289+acl_done:
32903290+ if (error == -1)
32913291+ goto exit;
32923292+ }
32933293+ }
32943294+ /* And, finally, everything else */
32953295+ else
32963296+ {
32973297+ if (s->copyIntent ||
32983298+ xattr_preserve_for_intent((char*)entry->name, s->copyIntent) == 1) {
32993299+ if (s->statuscb) {
33003300+ int rv;
33013301+ s->xattr_name = strdup((char*)entry->name);
33023302+ s->totalCopied = 0;
33033303+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
33043304+ if (s->xattr_name) {
33053305+ free(s->xattr_name);
33063306+ s->xattr_name = NULL;
33073307+ }
33083308+ if (rv == COPYFILE_QUIT) {
33093309+ s->err = ECANCELED;
33103310+ error = -1;
33113311+ goto exit;
33123312+ }
33133313+ }
33143314+ if (fsetxattr(s->dst_fd, (char *)entry->name, dataptr, entry->length, 0, 0) == -1) {
33153315+ if (COPYFILE_VERBOSE & s->flags)
33163316+ copyfile_warn("error %d setting attribute %s", errno, entry->name);
33173317+ if (s->statuscb) {
33183318+ int rv;
33193319+33203320+ s->xattr_name = strdup((char*)entry->name);
33213321+ rv = (s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
33223322+ if (s->xattr_name) {
33233323+ free(s->xattr_name);
33243324+ s->xattr_name = NULL;
33253325+ }
33263326+ if (rv == COPYFILE_QUIT) {
33273327+ error = -1;
33283328+ goto exit;
33293329+ }
33303330+ } else {
33313331+ error = -1;
33323332+ goto exit;
33333333+ }
33343334+ } else if (s->statuscb) {
33353335+ int rv;
33363336+ s->xattr_name = strdup((char*)entry->name);
33373337+ s->totalCopied = entry->length;
33383338+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
33393339+ if (s->xattr_name) {
33403340+ free(s->xattr_name);
33413341+ s->xattr_name = NULL;
33423342+ }
33433343+ if (rv == COPYFILE_QUIT) {
33443344+ error = -1;
33453345+ s->err = ECANCELED;
33463346+ goto exit;
33473347+ }
33483348+ }
33493349+ }
33503350+ }
33513351+ if (dataptr) {
33523352+ free(dataptr);
33533353+ dataptr = NULL;
33543354+ }
33553355+ entry = ATTR_NEXT(entry);
33563356+ }
33573357+ }
33583358+33593359+ /*
33603360+ * Extract the Finder Info.
33613361+ */
33623362+ if (adhdr->entries[0].offset > (hdrsize - sizeof(emptyfinfo))) {
33633363+ error = -1;
33643364+ goto exit;
33653365+ }
33663366+33673367+ if (bcmp((u_int8_t*)buffer + adhdr->entries[0].offset, emptyfinfo, sizeof(emptyfinfo)) != 0)
33683368+ {
33693369+ uint16_t *fFlags;
33703370+ uint8_t *newFinfo;
33713371+ enum { kFinderInvisibleMask = 1 << 14 };
33723372+33733373+ newFinfo = (u_int8_t*)buffer + adhdr->entries[0].offset;
33743374+ fFlags = (uint16_t*)&newFinfo[8];
33753375+ copyfile_debug(3, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME);
33763376+ if (s->statuscb) {
33773377+ int rv;
33783378+ s->xattr_name = (char*)XATTR_FINDERINFO_NAME;
33793379+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
33803380+ s->xattr_name = NULL;
33813381+ if (rv == COPYFILE_QUIT) {
33823382+ error = -1;
33833383+ s->err = ECANCELED;
33843384+ goto exit;
33853385+ } else if (rv == COPYFILE_SKIP) {
33863386+ goto skip_fi;
33873387+ }
33883388+ }
33893389+ error = fsetxattr(s->dst_fd, XATTR_FINDERINFO_NAME, (u_int8_t*)buffer + adhdr->entries[0].offset, sizeof(emptyfinfo), 0, 0);
33903390+ if (error) {
33913391+ if (s->statuscb) {
33923392+ int rv;
33933393+ s->xattr_name = (char *)XATTR_FINDERINFO_NAME;
33943394+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
33953395+ s->xattr_name = NULL;
33963396+ if (rv == COPYFILE_QUIT) {
33973397+ error = -1;
33983398+ s->err = ECANCELED;
33993399+ goto exit;
34003400+ }
34013401+ }
34023402+ goto exit;
34033403+ } else if (s->statuscb) {
34043404+ int rv;
34053405+ s->xattr_name = (char *)XATTR_FINDERINFO_NAME;
34063406+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
34073407+ s->xattr_name = NULL;
34083408+ if (rv == COPYFILE_QUIT) {
34093409+ error = -1;
34103410+ s->err = ECANCELED;
34113411+ goto exit;
34123412+ }
34133413+ }
34143414+ if (SWAP16(*fFlags) & kFinderInvisibleMask)
34153415+ s->internal_flags |= cfMakeFileInvisible;
34163416+ }
34173417+skip_fi:
34183418+34193419+ /*
34203420+ * Extract the Resource Fork.
34213421+ */
34223422+ if (adhdr->entries[1].type == AD_RESOURCE &&
34233423+ adhdr->entries[1].length > 0)
34243424+ {
34253425+ void * rsrcforkdata = NULL;
34263426+ size_t length;
34273427+ off_t offset;
34283428+ struct stat sb;
34293429+ struct timeval tval[2];
34303430+34313431+ length = adhdr->entries[1].length;
34323432+ offset = adhdr->entries[1].offset;
34333433+ rsrcforkdata = malloc(length);
34343434+34353435+ if (rsrcforkdata == NULL) {
34363436+ copyfile_debug(1, "could not allocate %zu bytes for rsrcforkdata",
34373437+ length);
34383438+ error = -1;
34393439+ goto bad;
34403440+ }
34413441+34423442+ if (fstat(s->dst_fd, &sb) < 0)
34433443+ {
34443444+ copyfile_debug(1, "couldn't stat destination file");
34453445+ error = -1;
34463446+ goto bad;
34473447+ }
34483448+34493449+ bytes = pread(s->src_fd, rsrcforkdata, length, offset);
34503450+ if (bytes < (ssize_t)length)
34513451+ {
34523452+ if (bytes == -1)
34533453+ {
34543454+ copyfile_debug(1, "couldn't read resource fork");
34553455+ }
34563456+ else
34573457+ {
34583458+ copyfile_debug(1,
34593459+ "couldn't read resource fork (only read %d bytes of %d)",
34603460+ (int)bytes, (int)length);
34613461+ }
34623462+ error = -1;
34633463+ goto bad;
34643464+ }
34653465+ if (s->statuscb) {
34663466+ int rv;
34673467+ s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME;
34683468+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
34693469+ s->xattr_name = NULL;
34703470+ if (rv == COPYFILE_QUIT) {
34713471+ error = -1;
34723472+ s->err = ECANCELED;
34733473+ if (rsrcforkdata)
34743474+ free(rsrcforkdata);
34753475+ goto exit;
34763476+ } else if (rv == COPYFILE_SKIP) {
34773477+ goto bad;
34783478+ }
34793479+ }
34803480+ error = fsetxattr(s->dst_fd, XATTR_RESOURCEFORK_NAME, rsrcforkdata, bytes, 0, 0);
34813481+ if (error)
34823482+ {
34833483+ /*
34843484+ * For filesystems that do not natively support named attributes,
34853485+ * the kernel creates an AppleDouble file that -- for compatabilty
34863486+ * reasons -- has a resource fork containing nothing but a rsrcfork_header_t
34873487+ * structure that says there are no resources. So, if fsetxattr has
34883488+ * failed, and the resource fork is that empty structure, *and* the
34893489+ * target file is a directory, then we do nothing with it.
34903490+ */
34913491+ if ((bytes == sizeof(rsrcfork_header_t)) &&
34923492+ ((sb.st_mode & S_IFMT) == S_IFDIR) &&
34933493+ (memcmp(rsrcforkdata, &empty_rsrcfork_header, bytes) == 0)) {
34943494+ copyfile_debug(2, "not setting empty resource fork on directory");
34953495+ error = errno = 0;
34963496+ goto bad;
34973497+ }
34983498+ if (s->statuscb) {
34993499+ int rv;
35003500+ s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME;
35013501+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
35023502+ s->xattr_name = NULL;
35033503+ if (rv == COPYFILE_CONTINUE) {
35043504+ error = errno = 0;
35053505+ goto bad;
35063506+ }
35073507+ }
35083508+ copyfile_debug(1, "error %d setting resource fork attribute", error);
35093509+ error = -1;
35103510+ goto bad;
35113511+ } else if (s->statuscb) {
35123512+ int rv;
35133513+ s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME;
35143514+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
35153515+ s->xattr_name = NULL;
35163516+ if (rv == COPYFILE_QUIT) {
35173517+ error = -1;
35183518+ s->err = ECANCELED;
35193519+ if (rsrcforkdata)
35203520+ free(rsrcforkdata);
35213521+ goto exit;
35223522+ }
35233523+ }
35243524+ copyfile_debug(3, "extracting \"%s\" (%d bytes)",
35253525+ XATTR_RESOURCEFORK_NAME, (int)length);
35263526+35273527+ if (!(s->flags & COPYFILE_STAT))
35283528+ {
35293529+ tval[0].tv_sec = sb.st_atime;
35303530+ tval[1].tv_sec = sb.st_mtime;
35313531+ tval[0].tv_usec = tval[1].tv_usec = 0;
35323532+35333533+ if (futimes(s->dst_fd, tval))
35343534+ copyfile_warn("%s: set times", s->dst ? s->dst : "(null dst)");
35353535+ }
35363536+bad:
35373537+ if (rsrcforkdata)
35383538+ free(rsrcforkdata);
35393539+ }
35403540+35413541+ if (COPYFILE_STAT & s->flags)
35423542+ {
35433543+ error = copyfile_stat(s);
35443544+ }
35453545+exit:
35463546+ if (buffer) free(buffer);
35473547+ if (dataptr) free(dataptr);
35483548+ return error;
35493549+}
35503550+35513551+static int copyfile_pack_quarantine(copyfile_state_t s, void **buf, ssize_t *len)
35523552+{
35533553+ int ret = 0;
35543554+ char qbuf[QTN_SERIALIZED_DATA_MAX];
35553555+ size_t qlen = sizeof(qbuf);
35563556+35573557+ if (s->qinfo == NULL)
35583558+ {
35593559+ ret = -1;
35603560+ goto done;
35613561+ }
35623562+35633563+ if (qtn_file_to_data(s->qinfo, qbuf, &qlen) != 0)
35643564+ {
35653565+ ret = -1;
35663566+ goto done;
35673567+ }
35683568+35693569+ *buf = malloc(qlen);
35703570+ if (*buf)
35713571+ {
35723572+ memcpy(*buf, qbuf, qlen);
35733573+ *len = qlen;
35743574+ }
35753575+done:
35763576+ return ret;
35773577+}
35783578+35793579+static int copyfile_pack_acl(copyfile_state_t s, void **buf, ssize_t *len)
35803580+{
35813581+ int ret = 0;
35823582+ acl_t acl = NULL;
35833583+ char *acl_text;
35843584+35853585+ if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) < 0)
35863586+ {
35873587+ if (errno != ENOENT)
35883588+ {
35893589+ ret = -1;
35903590+ if (COPYFILE_VERBOSE & s->flags)
35913591+ copyfile_warn("getting acl");
35923592+ }
35933593+ *len = 0;
35943594+ goto exit;
35953595+ }
35963596+35973597+ if ((acl_text = acl_to_text(acl, len)) != NULL)
35983598+ {
35993599+ /*
36003600+ * acl_to_text() doesn't include the NUL at the endo
36013601+ * in it's count (*len). It does, however, promise to
36023602+ * return a valid C string, so we need to up the count
36033603+ * by 1.
36043604+ */
36053605+ *len = *len + 1;
36063606+ *buf = malloc(*len);
36073607+ if (*buf)
36083608+ memcpy(*buf, acl_text, *len);
36093609+ else
36103610+ *len = 0;
36113611+ acl_free(acl_text);
36123612+ }
36133613+ copyfile_debug(2, "copied acl (%ld) %p", *len, *buf);
36143614+exit:
36153615+ if (acl)
36163616+ acl_free(acl);
36173617+ return ret;
36183618+}
36193619+36203620+static int copyfile_pack_rsrcfork(copyfile_state_t s, attr_header_t *filehdr)
36213621+{
36223622+ ssize_t datasize;
36233623+ char *databuf = NULL;
36243624+ int ret = 0;
36253625+36263626+/*
36273627+ * XXX
36283628+ * do COPYFILE_COPY_XATTR here; no need to
36293629+ * the work if we want to skip.
36303630+ */
36313631+36323632+ if (s->statuscb)
36333633+ {
36343634+ int rv;
36353635+36363636+ s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME;
36373637+36383638+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
36393639+ s->xattr_name = NULL;
36403640+ if (rv == COPYFILE_SKIP) {
36413641+ ret = 0;
36423642+ goto done;
36433643+ }
36443644+ if (rv == COPYFILE_QUIT) {
36453645+ ret = -1;
36463646+ s->err = ECANCELED;
36473647+ goto done;
36483648+ }
36493649+ }
36503650+ /* Get the resource fork size */
36513651+ if ((datasize = fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0)) < 0)
36523652+ {
36533653+ if (COPYFILE_VERBOSE & s->flags)
36543654+ copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME, errno);
36553655+ return -1;
36563656+ }
36573657+36583658+ if (datasize > INT_MAX) {
36593659+ s->err = EINVAL;
36603660+ ret = -1;
36613661+ goto done;
36623662+ }
36633663+36643664+ if (s->statuscb) {
36653665+ int rv;
36663666+ s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME;
36673667+36683668+ s->totalCopied = 0;
36693669+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
36703670+ s->xattr_name = NULL;
36713671+ if (rv == COPYFILE_QUIT) {
36723672+ s->err = ECANCELED;
36733673+ ret = -1;
36743674+ goto done;
36753675+ }
36763676+ }
36773677+ if ((databuf = malloc(datasize)) == NULL)
36783678+ {
36793679+ copyfile_warn("malloc");
36803680+ ret = -1;
36813681+ goto done;
36823682+ }
36833683+36843684+ if (fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, databuf, datasize, 0, 0) != datasize)
36853685+ {
36863686+ if (COPYFILE_VERBOSE & s->flags)
36873687+ copyfile_warn("couldn't read entire resource fork");
36883688+ ret = -1;
36893689+ goto done;
36903690+ }
36913691+36923692+ /* Write the resource fork to disk. */
36933693+ if (pwrite(s->dst_fd, databuf, datasize, filehdr->appledouble.entries[1].offset) != datasize)
36943694+ {
36953695+ if (COPYFILE_VERBOSE & s->flags)
36963696+ copyfile_warn("couldn't write resource fork");
36973697+ }
36983698+ if (s->statuscb)
36993699+ {
37003700+ int rv;
37013701+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
37023702+ if (rv == COPYFILE_QUIT) {
37033703+ ret = -1;
37043704+ goto done;
37053705+ }
37063706+ }
37073707+ copyfile_debug(3, "copied %zd bytes of \"%s\" data @ offset 0x%08x",
37083708+ datasize, XATTR_RESOURCEFORK_NAME, filehdr->appledouble.entries[1].offset);
37093709+ filehdr->appledouble.entries[1].length = (u_int32_t)datasize;
37103710+37113711+done:
37123712+ if (ret == -1 && s->statuscb)
37133713+ {
37143714+ int rv;
37153715+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
37163716+ if (rv == COPYFILE_CONTINUE)
37173717+ ret = 0;
37183718+ }
37193719+ if (s->xattr_name) {
37203720+ s->xattr_name = NULL;
37213721+ }
37223722+ if (databuf)
37233723+ free(databuf);
37243724+37253725+/*
37263726+ * XXX
37273727+ * Do status callback here
37283728+ * If ret == -1, then error callback
37293729+ */
37303730+ return ret;
37313731+}
37323732+37333733+/*
37343734+ * The opposite of copyfile_unpack(), obviously.
37353735+ */
37363736+static int copyfile_pack(copyfile_state_t s)
37373737+{
37383738+ char *attrnamebuf = NULL, *endnamebuf;
37393739+ void *databuf = NULL;
37403740+ attr_header_t *filehdr, *endfilehdr;
37413741+ attr_entry_t *entry;
37423742+ ssize_t listsize = 0;
37433743+ char *nameptr;
37443744+ size_t namelen;
37453745+ size_t entrylen;
37463746+ ssize_t datasize;
37473747+ size_t offset = 0;
37483748+ int hasrsrcfork = 0;
37493749+ int error = 0;
37503750+ int seenq = 0; // Have we seen any quarantine info already?
37513751+37523752+ filehdr = (attr_header_t *) calloc(1, ATTR_MAX_HDR_SIZE);
37533753+37543754+ if (filehdr == NULL) {
37553755+ error = -1;
37563756+ goto exit;
37573757+ } else {
37583758+ endfilehdr = (attr_header_t*)(((char*)filehdr) + ATTR_MAX_HDR_SIZE);
37593759+ }
37603760+37613761+ attrnamebuf = calloc(1, ATTR_MAX_HDR_SIZE);
37623762+ if (attrnamebuf == NULL) {
37633763+ error = -1;
37643764+ goto exit;
37653765+ } else {
37663766+ endnamebuf = ((char*)attrnamebuf) + ATTR_MAX_HDR_SIZE;
37673767+ }
37683768+37693769+ /*
37703770+ * Fill in the Apple Double Header defaults.
37713771+ */
37723772+ filehdr->appledouble.magic = ADH_MAGIC;
37733773+ filehdr->appledouble.version = ADH_VERSION;
37743774+ filehdr->appledouble.numEntries = 2;
37753775+ filehdr->appledouble.entries[0].type = AD_FINDERINFO;
37763776+ filehdr->appledouble.entries[0].offset = (u_int32_t)offsetof(apple_double_header_t, finfo);
37773777+ filehdr->appledouble.entries[0].length = FINDERINFOSIZE;
37783778+ filehdr->appledouble.entries[1].type = AD_RESOURCE;
37793779+ filehdr->appledouble.entries[1].offset = (u_int32_t)offsetof(apple_double_header_t, pad);
37803780+ filehdr->appledouble.entries[1].length = 0;
37813781+ bcopy(ADH_MACOSX, filehdr->appledouble.filler, sizeof(filehdr->appledouble.filler));
37823782+37833783+ /*
37843784+ * Fill in the initial Attribute Header.
37853785+ */
37863786+ filehdr->magic = ATTR_HDR_MAGIC;
37873787+ filehdr->debug_tag = 0;
37883788+ filehdr->data_start = (u_int32_t)sizeof(attr_header_t);
37893789+37903790+ /*
37913791+ * Collect the attribute names.
37923792+ */
37933793+ entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t));
37943794+37953795+ /*
37963796+ * Test if there are acls to copy
37973797+ */
37983798+ if (COPYFILE_ACL & s->flags)
37993799+ {
38003800+ acl_t temp_acl = NULL;
38013801+ if (filesec_get_property(s->fsec, FILESEC_ACL, &temp_acl) < 0)
38023802+ {
38033803+ copyfile_debug(2, "no acl entries found (errno = %d)", errno);
38043804+ } else
38053805+ {
38063806+ offset = strlen(XATTR_SECURITY_NAME) + 1;
38073807+ strcpy(attrnamebuf, XATTR_SECURITY_NAME);
38083808+ endnamebuf = attrnamebuf + offset;
38093809+ }
38103810+ if (temp_acl)
38113811+ acl_free(temp_acl);
38123812+ }
38133813+38143814+ if (COPYFILE_XATTR & s->flags)
38153815+ {
38163816+ ssize_t left = ATTR_MAX_HDR_SIZE - offset;
38173817+ if ((listsize = flistxattr(s->src_fd, attrnamebuf + offset, left, 0)) <= 0)
38183818+ {
38193819+ copyfile_debug(2, "no extended attributes found (%d)", errno);
38203820+ }
38213821+ if (listsize > left)
38223822+ {
38233823+ copyfile_debug(1, "extended attribute list too long");
38243824+ listsize = left;
38253825+ }
38263826+38273827+ endnamebuf = attrnamebuf + offset + (listsize > 0 ? listsize : 0);
38283828+ if (endnamebuf > (attrnamebuf + ATTR_MAX_HDR_SIZE)) {
38293829+ error = -1;
38303830+ goto exit;
38313831+ }
38323832+38333833+ if (listsize > 0)
38343834+ sort_xattrname_list(attrnamebuf, endnamebuf - attrnamebuf);
38353835+38363836+ for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen)
38373837+ {
38383838+ namelen = strlen(nameptr) + 1;
38393839+ /* Skip over FinderInfo or Resource Fork names */
38403840+ if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0 ||
38413841+ strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0) {
38423842+ continue;
38433843+ }
38443844+ if (strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0) {
38453845+ seenq = 1;
38463846+ }
38473847+38483848+ /* The system should prevent this from happening, but... */
38493849+ if (namelen > XATTR_MAXNAMELEN + 1) {
38503850+ namelen = XATTR_MAXNAMELEN + 1;
38513851+ }
38523852+ if (s->copyIntent &&
38533853+ xattr_preserve_for_intent(nameptr, s->copyIntent) == 0) {
38543854+ // Skip it
38553855+ size_t amt = endnamebuf - (nameptr + namelen);
38563856+ memmove(nameptr, nameptr + namelen, amt);
38573857+ endnamebuf -= namelen;
38583858+ /* Set namelen to 0 so continue doesn't miss names */
38593859+ namelen = 0;
38603860+ continue;
38613861+ }
38623862+38633863+ if (s->statuscb) {
38643864+ int rv;
38653865+ char eaname[namelen];
38663866+ bcopy(nameptr, eaname, namelen);
38673867+ eaname[namelen - 1] = 0; // Just to be sure!
38683868+ s->xattr_name = eaname;
38693869+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
38703870+ s->xattr_name = NULL;
38713871+ if (rv == COPYFILE_QUIT) {
38723872+ error = -1;
38733873+ s->err = ECANCELED;
38743874+ goto exit;
38753875+ } else if (rv == COPYFILE_SKIP) {
38763876+ size_t amt = endnamebuf - (nameptr + namelen);
38773877+ memmove(nameptr, nameptr + namelen, amt);
38783878+ endnamebuf -= namelen;
38793879+ /* Set namelen to 0 so continue doesn't miss names */
38803880+ namelen = 0;
38813881+ continue;
38823882+ }
38833883+ }
38843884+ entry->namelen = namelen;
38853885+ entry->flags = 0;
38863886+ if (nameptr + namelen > endnamebuf) {
38873887+ error = -1;
38883888+ goto exit;
38893889+ }
38903890+38913891+ bcopy(nameptr, &entry->name[0], namelen);
38923892+ copyfile_debug(2, "copied name [%s]", entry->name);
38933893+38943894+ entrylen = ATTR_ENTRY_LENGTH(namelen);
38953895+ entry = (attr_entry_t *)(((char *)entry) + entrylen);
38963896+38973897+ if ((void*)entry >= (void*)endfilehdr) {
38983898+ error = -1;
38993899+ goto exit;
39003900+ }
39013901+39023902+ /* Update the attributes header. */
39033903+ filehdr->num_attrs++;
39043904+ filehdr->data_start += (u_int32_t)entrylen;
39053905+ }
39063906+ }
39073907+39083908+ /*
39093909+ * If we have any quarantine data, we always pack it.
39103910+ * But if we've already got it in the EA list, don't put it in again.
39113911+ */
39123912+ if (s->qinfo && !seenq)
39133913+ {
39143914+ ssize_t left = ATTR_MAX_HDR_SIZE - offset;
39153915+ /* strlcpy returns number of bytes copied, but we need offset to point to the next byte */
39163916+ offset += strlcpy(attrnamebuf + offset, XATTR_QUARANTINE_NAME, left) + 1;
39173917+ }
39183918+39193919+ seenq = 0;
39203920+ /*
39213921+ * Collect the attribute data.
39223922+ */
39233923+ entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t));
39243924+39253925+ for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen + 1)
39263926+ {
39273927+ namelen = strlen(nameptr);
39283928+39293929+ if (strcmp(nameptr, XATTR_SECURITY_NAME) == 0)
39303930+ copyfile_pack_acl(s, &databuf, &datasize);
39313931+ else if (s->qinfo && strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0)
39323932+ {
39333933+ copyfile_pack_quarantine(s, &databuf, &datasize);
39343934+ }
39353935+ /* Check for Finder Info. */
39363936+ else if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0)
39373937+ {
39383938+ if (s->statuscb)
39393939+ {
39403940+ int rv;
39413941+ s->xattr_name = (char*)XATTR_FINDERINFO_NAME;
39423942+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
39433943+ s->xattr_name = NULL;
39443944+ if (rv == COPYFILE_QUIT)
39453945+ {
39463946+ s->xattr_name = NULL;
39473947+ s->err = ECANCELED;
39483948+ error = -1;
39493949+ goto exit;
39503950+ }
39513951+ else if (rv == COPYFILE_SKIP)
39523952+ {
39533953+ s->xattr_name = NULL;
39543954+ continue;
39553955+ }
39563956+ s->totalCopied = 0;
39573957+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
39583958+ s->xattr_name = NULL;
39593959+ if (rv == COPYFILE_QUIT)
39603960+ {
39613961+ s->err = ECANCELED;
39623962+ error = -1;
39633963+ goto exit;
39643964+ }
39653965+ }
39663966+ datasize = fgetxattr(s->src_fd, nameptr, (u_int8_t*)filehdr + filehdr->appledouble.entries[0].offset, 32, 0, 0);
39673967+ if (datasize < 0)
39683968+ {
39693969+ if (s->statuscb) {
39703970+ int rv;
39713971+ s->xattr_name = strdup(nameptr);
39723972+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
39733973+ if (s->xattr_name) {
39743974+ free(s->xattr_name);
39753975+ s->xattr_name = NULL;
39763976+ }
39773977+ if (rv == COPYFILE_QUIT) {
39783978+ error = -1;
39793979+ goto exit;
39803980+ }
39813981+ }
39823982+ if (COPYFILE_VERBOSE & s->flags)
39833983+ copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno);
39843984+ } else if (datasize != 32)
39853985+ {
39863986+ if (COPYFILE_VERBOSE & s->flags)
39873987+ copyfile_warn("unexpected size (%ld) for \"%s\"", datasize, nameptr);
39883988+ } else
39893989+ {
39903990+ if (COPYFILE_VERBOSE & s->flags)
39913991+ copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x",
39923992+ XATTR_FINDERINFO_NAME, filehdr->appledouble.entries[0].offset);
39933993+ if (s->statuscb) {
39943994+ int rv;
39953995+ s->xattr_name = strdup(nameptr);
39963996+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
39973997+ if (s->xattr_name) {
39983998+ free(s->xattr_name);
39993999+ s->xattr_name = NULL;
40004000+ }
40014001+ if (rv == COPYFILE_QUIT) {
40024002+ error = -1;
40034003+ goto exit;
40044004+ }
40054005+ }
40064006+ }
40074007+ continue; /* finder info doesn't have an attribute entry */
40084008+ }
40094009+ /* Check for Resource Fork. */
40104010+ else if (strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0)
40114011+ {
40124012+ hasrsrcfork = 1;
40134013+ continue;
40144014+ } else
40154015+ {
40164016+ /* Just a normal attribute. */
40174017+ if (s->statuscb)
40184018+ {
40194019+ int rv;
40204020+ s->xattr_name = strdup(nameptr);
40214021+ s->totalCopied = 0;
40224022+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
40234023+ if (s->xattr_name) {
40244024+ free(s->xattr_name);
40254025+ s->xattr_name = NULL;
40264026+ }
40274027+ /*
40284028+ * Due to the nature of the packed file, we can't skip at this point.
40294029+ */
40304030+ if (rv == COPYFILE_QUIT)
40314031+ {
40324032+ s->err = ECANCELED;
40334033+ error = -1;
40344034+ goto exit;
40354035+ }
40364036+ }
40374037+ datasize = fgetxattr(s->src_fd, nameptr, NULL, 0, 0, 0);
40384038+ if (datasize == 0)
40394039+ goto next;
40404040+ if (datasize < 0)
40414041+ {
40424042+ if (COPYFILE_VERBOSE & s->flags)
40434043+ copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno);
40444044+ if (s->statuscb)
40454045+ {
40464046+ int rv;
40474047+ s->xattr_name = strdup(nameptr);
40484048+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
40494049+ if (s->xattr_name) {
40504050+ free(s->xattr_name);
40514051+ s->xattr_name = NULL;
40524052+ }
40534053+ if (rv == COPYFILE_QUIT)
40544054+ {
40554055+ s->err = ECANCELED;
40564056+ error = -1;
40574057+ goto exit;
40584058+ }
40594059+ }
40604060+ goto next;
40614061+ }
40624062+ if (datasize > XATTR_MAXATTRLEN)
40634063+ {
40644064+ if (COPYFILE_VERBOSE & s->flags)
40654065+ copyfile_warn("skipping attr \"%s\" (too big)", nameptr);
40664066+ goto next;
40674067+ }
40684068+ databuf = malloc(datasize);
40694069+ if (databuf == NULL) {
40704070+ error = -1;
40714071+ continue;
40724072+ }
40734073+ datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0);
40744074+ if (s->statuscb) {
40754075+ int rv;
40764076+ s->xattr_name = strdup(nameptr);
40774077+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
40784078+ if (s->xattr_name) {
40794079+ free(s->xattr_name);
40804080+ s->xattr_name = NULL;
40814081+ }
40824082+ if (rv == COPYFILE_QUIT) {
40834083+ s->err = ECANCELED;
40844084+ error = -1;
40854085+ goto exit;
40864086+ }
40874087+ }
40884088+ }
40894089+40904090+ entry->length = (u_int32_t)datasize;
40914091+ entry->offset = filehdr->data_start + filehdr->data_length;
40924092+40934093+ filehdr->data_length += (u_int32_t)datasize;
40944094+#if 0
40954095+ /*
40964096+ * >>> WARNING <<<
40974097+ * This assumes that the data is fits in memory (not
40984098+ * the case when there are lots of attributes or one of
40994099+ * the attributes is very large.
41004100+ */
41014101+ if (entry->offset > ATTR_MAX_SIZE ||
41024102+ (entry->offset + datasize > ATTR_MAX_SIZE)) {
41034103+ error = 1;
41044104+ } else {
41054105+ bcopy(databuf, (char*)filehdr + entry->offset, datasize);
41064106+ }
41074107+#else
41084108+ if (pwrite(s->dst_fd, databuf, datasize, entry->offset) != datasize) {
41094109+ error = 1;
41104110+ }
41114111+#endif
41124112+ free(databuf);
41134113+41144114+ copyfile_debug(3, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize, nameptr, entry->offset);
41154115+next:
41164116+ /* bump to next entry */
41174117+ entrylen = ATTR_ENTRY_LENGTH(entry->namelen);
41184118+ entry = (attr_entry_t *)((char *)entry + entrylen);
41194119+ }
41204120+41214121+ /* Now we know where the resource fork data starts. */
41224122+ filehdr->appledouble.entries[1].offset = (filehdr->data_start + filehdr->data_length);
41234123+41244124+ /* We also know the size of the "Finder Info entry. */
41254125+ filehdr->appledouble.entries[0].length =
41264126+ filehdr->appledouble.entries[1].offset - filehdr->appledouble.entries[0].offset;
41274127+41284128+ filehdr->total_size = filehdr->appledouble.entries[1].offset;
41294129+41304130+ /* Copy Resource Fork. */
41314131+ if (hasrsrcfork && (error = copyfile_pack_rsrcfork(s, filehdr)))
41324132+ goto exit;
41334133+41344134+ /* Write the header to disk. */
41354135+ datasize = filehdr->data_start;
41364136+41374137+ swap_adhdr(&filehdr->appledouble);
41384138+ swap_attrhdr(filehdr);
41394139+ swap_attrhdr_entries(filehdr);
41404140+41414141+ if (pwrite(s->dst_fd, filehdr, datasize, 0) != datasize)
41424142+ {
41434143+ if (COPYFILE_VERBOSE & s->flags)
41444144+ copyfile_warn("couldn't write file header");
41454145+ error = -1;
41464146+ goto exit;
41474147+ }
41484148+exit:
41494149+ if (filehdr) free(filehdr);
41504150+ if (attrnamebuf) free(attrnamebuf);
41514151+41524152+ if (error)
41534153+ return error;
41544154+ else
41554155+ return copyfile_stat(s);
41564156+}
+123
src/copyfile/copyfile.h
···11+/*
22+ * Copyright (c) 2004-2010 Apple, Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+#ifndef _COPYFILE_H_ /* version 0.1 */
2424+#define _COPYFILE_H_
2525+2626+/*
2727+ * This API facilitates the copying of files and their associated
2828+ * metadata. There are several open source projects that need
2929+ * modifications to support preserving extended attributes and ACLs
3030+ * and this API collapses several hundred lines of modifications into
3131+ * one or two calls.
3232+ */
3333+3434+/* private */
3535+#include <sys/cdefs.h>
3636+#include <stdint.h>
3737+3838+__BEGIN_DECLS
3939+struct _copyfile_state;
4040+typedef struct _copyfile_state * copyfile_state_t;
4141+typedef uint32_t copyfile_flags_t;
4242+4343+/* public */
4444+4545+/* receives:
4646+ * from path to source file system object
4747+ * to path to destination file system object
4848+ * state opaque blob for future extensibility
4949+ * Must be NULL in current implementation
5050+ * flags (described below)
5151+ * returns:
5252+ * int negative for error
5353+ */
5454+5555+int copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags);
5656+int fcopyfile(int from_fd, int to_fd, copyfile_state_t, copyfile_flags_t flags);
5757+5858+int copyfile_state_free(copyfile_state_t);
5959+copyfile_state_t copyfile_state_alloc(void);
6060+6161+6262+int copyfile_state_get(copyfile_state_t s, uint32_t flag, void * dst);
6363+int copyfile_state_set(copyfile_state_t s, uint32_t flag, const void * src);
6464+6565+typedef int (*copyfile_callback_t)(int, int, copyfile_state_t, const char *, const char *, void *);
6666+6767+#define COPYFILE_STATE_SRC_FD 1
6868+#define COPYFILE_STATE_SRC_FILENAME 2
6969+#define COPYFILE_STATE_DST_FD 3
7070+#define COPYFILE_STATE_DST_FILENAME 4
7171+#define COPYFILE_STATE_QUARANTINE 5
7272+#define COPYFILE_STATE_STATUS_CB 6
7373+#define COPYFILE_STATE_STATUS_CTX 7
7474+#define COPYFILE_STATE_COPIED 8
7575+#define COPYFILE_STATE_XATTRNAME 9
7676+7777+7878+#define COPYFILE_DISABLE_VAR "COPYFILE_DISABLE"
7979+8080+/* flags for copyfile */
8181+8282+#define COPYFILE_ACL (1<<0)
8383+#define COPYFILE_STAT (1<<1)
8484+#define COPYFILE_XATTR (1<<2)
8585+#define COPYFILE_DATA (1<<3)
8686+8787+#define COPYFILE_SECURITY (COPYFILE_STAT | COPYFILE_ACL)
8888+#define COPYFILE_METADATA (COPYFILE_SECURITY | COPYFILE_XATTR)
8989+#define COPYFILE_ALL (COPYFILE_METADATA | COPYFILE_DATA)
9090+9191+#define COPYFILE_RECURSIVE (1<<15) /* Descend into hierarchies */
9292+#define COPYFILE_CHECK (1<<16) /* return flags for xattr or acls if set */
9393+#define COPYFILE_EXCL (1<<17) /* fail if destination exists */
9494+#define COPYFILE_NOFOLLOW_SRC (1<<18) /* don't follow if source is a symlink */
9595+#define COPYFILE_NOFOLLOW_DST (1<<19) /* don't follow if dst is a symlink */
9696+#define COPYFILE_MOVE (1<<20) /* unlink src after copy */
9797+#define COPYFILE_UNLINK (1<<21) /* unlink dst before copy */
9898+#define COPYFILE_NOFOLLOW (COPYFILE_NOFOLLOW_SRC | COPYFILE_NOFOLLOW_DST)
9999+100100+#define COPYFILE_PACK (1<<22)
101101+#define COPYFILE_UNPACK (1<<23)
102102+103103+#define COPYFILE_VERBOSE (1<<30)
104104+105105+#define COPYFILE_RECURSE_ERROR 0
106106+#define COPYFILE_RECURSE_FILE 1
107107+#define COPYFILE_RECURSE_DIR 2
108108+#define COPYFILE_RECURSE_DIR_CLEANUP 3
109109+#define COPYFILE_COPY_DATA 4
110110+#define COPYFILE_COPY_XATTR 5
111111+112112+#define COPYFILE_START 1
113113+#define COPYFILE_FINISH 2
114114+#define COPYFILE_ERR 3
115115+#define COPYFILE_PROGRESS 4
116116+117117+#define COPYFILE_CONTINUE 0
118118+#define COPYFILE_SKIP 1
119119+#define COPYFILE_QUIT 2
120120+121121+__END_DECLS
122122+123123+#endif /* _COPYFILE_H_ */
+38
src/copyfile/copyfile_private.h
···11+/*
22+ * Copyright (c) 2013 Apple, Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#ifndef _COPYFILE_PRIVATE_H
2525+# define _COPYFILE_PRIVATE_H
2626+2727+/*
2828+ * Set (or get) the intent type; see xattr_properties.h for details.
2929+ * This command uses a pointer to CopyOperationIntent_t as the parameter.
3030+ */
3131+# define COPYFILE_STATE_INTENT 256
3232+3333+/*
3434+ * File flags that are not preserved when copying stat information.
3535+ */
3636+#define COPYFILE_OMIT_FLAGS (UF_TRACKED | SF_RESTRICTED)
3737+3838+#endif /* _COPYFILE_PRIVATE_H */
+340
src/copyfile/xattr_flags.c
···11+/*
22+ * Copyright (c) 2013 Apple, Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#include <stdio.h>
2525+#include <stdlib.h>
2626+#include <string.h>
2727+#include <unistd.h>
2828+#include <err.h>
2929+#include <errno.h>
3030+#include <sys/types.h>
3131+#include <sys/xattr.h>
3232+#include <dispatch/dispatch.h>
3333+#include <xpc/private.h>
3434+3535+#include <xattr_flags.h>
3636+3737+#define FLAG_DELIM_CHAR '#'
3838+#define FLAG_DELIM_STR "#"
3939+4040+/*
4141+ * Some default propeteries for EAs we know about internally.
4242+ */
4343+struct defaultList {
4444+ const char *eaName;
4545+ const char *propList;
4646+ int flags; // See below
4747+};
4848+4949+#define propFlagsPrefix 0x0001 // The name is a prefix, so only look at that part
5050+5151+static const struct defaultList *defaultPropertyTable = NULL;
5252+5353+static const struct defaultList
5454+defaultUnboxedPropertyTable[] = {
5555+ { "com.apple.quarantine", "PCS", 0 }, // not public
5656+ { "com.apple.TextEncoding", "CS", 0 }, // Content-dependent, public
5757+ { "com.apple.metadata:", "PS", propFlagsPrefix }, // Don't export, keep for copy & safe save
5858+ { "com.apple.security.", "S", propFlagsPrefix },
5959+ { XATTR_RESOURCEFORK_NAME, "PCS", 0 }, // Don't keep for safe save
6060+ { XATTR_FINDERINFO_NAME, "PCS", 0 }, // Same as ResourceFork
6161+ { 0, 0, 0 },
6262+};
6363+6464+static const struct defaultList
6565+defaultSandboxedPropertyTable[] = {
6666+ { "com.apple.quarantine", "PCS", 0 }, // not public
6767+ { "com.apple.TextEncoding", "CS", 0 }, // Content-dependent, public
6868+ { "com.apple.metadata:", "PS", propFlagsPrefix }, // Don't export, keep for copy & safe save
6969+ { "com.apple.security.", "N", propFlagsPrefix },
7070+ { XATTR_RESOURCEFORK_NAME, "PCS", 0 }, // Don't keep for safe save
7171+ { XATTR_FINDERINFO_NAME, "PCS", 0 }, // Same as ResourceFork
7272+ { 0, 0, 0 },
7373+};
7474+7575+/*
7676+ * The property lists on an EA are set by having a suffix character,
7777+ * and then a list of characters. In general, we're choosing upper-case
7878+ * to indicate the property is set, and lower-case to indicate it's to be
7979+ * cleared.
8080+ */
8181+struct propertyListMapping {
8282+ char enable; // Character to enable
8383+ char disable; // Character to disable -- usually lower-case of enable
8484+ xattr_operation_intent_t value;
8585+};
8686+static const struct propertyListMapping
8787+PropertyListMapTable[] = {
8888+ { 'C', 'c', XATTR_FLAG_CONTENT_DEPENDENT },
8989+ { 'P', 'p', XATTR_FLAG_NO_EXPORT },
9090+ { 'N', 'n', XATTR_FLAG_NEVER_PRESERVE },
9191+ { 'S', 's', XATTR_FLAG_SYNCABLE },
9292+ { 0, 0, 0 },
9393+};
9494+9595+/*
9696+ * Given a converted property list (that is, converted to the
9797+ * xattr_operation_intent_t type), and an intent, determine if
9898+ * it should be preserved or not.
9999+ *
100100+ * I've chosen to use a block instead of a simple mask on the belief
101101+ * that the question may be moderately complex. If it ends up not being
102102+ * so, then this can simply be turned into a mask of which bits to check
103103+ * as being exclusionary.
104104+ */
105105+static const struct divineIntent {
106106+ xattr_operation_intent_t intent;
107107+ int (^checker)(xattr_flags_t);
108108+} intentTable[] = {
109109+ { XATTR_OPERATION_INTENT_COPY, ^(xattr_flags_t flags) {
110110+ if (flags & XATTR_FLAG_NEVER_PRESERVE)
111111+ return 0;
112112+ return 1;
113113+ } },
114114+ { XATTR_OPERATION_INTENT_SAVE, ^(xattr_flags_t flags) {
115115+ if (flags & (XATTR_FLAG_CONTENT_DEPENDENT | XATTR_FLAG_NEVER_PRESERVE))
116116+ return 0;
117117+ return 1;
118118+ } },
119119+ { XATTR_OPERATION_INTENT_SHARE, ^(xattr_flags_t flags) {
120120+ if ((flags & (XATTR_FLAG_NO_EXPORT | XATTR_FLAG_NEVER_PRESERVE)) != 0)
121121+ return 0;
122122+ return 1;
123123+ } },
124124+ { XATTR_OPERATION_INTENT_SYNC, ^(xattr_flags_t flags) {
125125+ return (flags & (XATTR_FLAG_SYNCABLE | XATTR_FLAG_NEVER_PRESERVE)) == XATTR_FLAG_SYNCABLE;
126126+ } },
127127+ { 0, 0 },
128128+};
129129+130130+131131+/*
132132+ * If an EA name is in the default list, find it, and return the property
133133+ * list string for it.
134134+ */
135135+static const char *
136136+nameInDefaultList(const char *eaname)
137137+{
138138+ const struct defaultList *retval;
139139+ static dispatch_once_t onceToken;
140140+141141+ dispatch_once(&onceToken, ^{
142142+ if (_xpc_runtime_is_app_sandboxed()) {
143143+ defaultPropertyTable = defaultSandboxedPropertyTable;
144144+ } else {
145145+ defaultPropertyTable = defaultUnboxedPropertyTable;
146146+ }
147147+ });
148148+149149+ for (retval = defaultPropertyTable; retval->eaName; retval++) {
150150+ if ((retval->flags & propFlagsPrefix) != 0 &&
151151+ strncmp(retval->eaName, eaname, strlen(retval->eaName)) == 0)
152152+ return retval->propList;
153153+ if (strcmp(retval->eaName, eaname) == 0)
154154+ return retval->propList;
155155+ }
156156+ return NULL;
157157+}
158158+159159+/*
160160+ * Given an EA name, see if it has a property list in it, and
161161+ * return a pointer to it. All this is doing is looking for
162162+ * the delimiter, and returning the string after that. Returns
163163+ * NULL if the delimiter isn't found. Note that an empty string
164164+ * is a valid property list, as far as we're concerned.
165165+ */
166166+static const char *
167167+findPropertyList(const char *eaname)
168168+{
169169+ const char *ptr = strrchr(eaname, '#');
170170+ if (ptr)
171171+ return ptr+1;
172172+ return NULL;
173173+}
174174+175175+/*
176176+ * Convert a property list string (e.g., "pCd") into a
177177+ * xattr_operation_intent_t type.
178178+ */
179179+static xattr_operation_intent_t
180180+stringToProperties(const char *proplist)
181181+{
182182+ xattr_operation_intent_t retval = 0;
183183+ const char *ptr;
184184+185185+ // A switch would be more efficient, but less generic.
186186+ for (ptr = proplist; *ptr; ptr++) {
187187+ const struct propertyListMapping *mapPtr;
188188+ for (mapPtr = PropertyListMapTable; mapPtr->enable; mapPtr++) {
189189+ if (*ptr == mapPtr->enable) {
190190+ retval |= mapPtr->value;
191191+ } else if (*ptr == mapPtr->disable) {
192192+ retval &= ~mapPtr->value;
193193+ }
194194+ }
195195+ }
196196+ return retval;
197197+}
198198+199199+/*
200200+ * Given an EA name (e.g., "com.apple.lfs.hfs.test"), and a
201201+ * xattr_operation_intent_t value (it's currently an integral value, so
202202+ * just a bitmask), cycle through the list of known properties, and return
203203+ * a string with the EA name, and the property list appended. E.g., we
204204+ * might return "com.apple.lfs.hfs.test#pD".
205205+ *
206206+ * The tricky part of this funciton is that it will not append any letters
207207+ * if the value is only the default properites. In that case, it will copy
208208+ * the EA name, and return that.
209209+ *
210210+ * It returns NULL if there was an error. The two errors right now are
211211+ * no memory (strdup failed), in which case it will set errno to ENOMEM; and
212212+ * the resulting EA name is longer than XATTR_MAXNAMELEN, in which case it
213213+ * sets errno to ENAMETOOLONG.
214214+ *
215215+ * (Note that it also uses ENAMETOOLONG if the buffer it's trying to set
216216+ * gets too large. I honestly can't see how that would happen, but it's there
217217+ * for sanity checking. That would require having more than 64 bits to use.)
218218+ */
219219+char *
220220+xattr_name_with_flags(const char *orig, xattr_flags_t propList)
221221+{
222222+ char *retval = NULL;
223223+ char suffix[66] = { 0 }; // 66: uint64_t for property types, plus '#', plus NUL
224224+ char *cur = suffix;
225225+ const struct propertyListMapping *mapPtr;
226226+227227+ *cur++ = '#';
228228+ for (mapPtr = PropertyListMapTable; mapPtr->enable; mapPtr++) {
229229+ if ((propList & mapPtr->value) != 0) {
230230+ *cur++ = mapPtr->enable;
231231+ }
232232+ if (cur >= (suffix + sizeof(suffix))) {
233233+ errno = ENAMETOOLONG;
234234+ return NULL;
235235+ }
236236+237237+ }
238238+239239+240240+ if (cur == suffix + 1) {
241241+ // No changes made
242242+ retval = strdup(orig);
243243+ if (retval == NULL)
244244+ errno = ENOMEM;
245245+ } else {
246246+ const char *defaultEntry = NULL;
247247+ if ((defaultEntry = nameInDefaultList(orig)) != NULL &&
248248+ strcmp(defaultEntry, suffix + 1) == 0) {
249249+ // Just use the name passed in
250250+ retval = strdup(orig);
251251+ } else {
252252+ asprintf(&retval, "%s%s", orig, suffix);
253253+ }
254254+ if (retval == NULL) {
255255+ errno = ENOMEM;
256256+ } else {
257257+ if (strlen(retval) > XATTR_MAXNAMELEN) {
258258+ free(retval);
259259+ retval = NULL;
260260+ errno = ENAMETOOLONG;
261261+ }
262262+ }
263263+ }
264264+ return retval;
265265+}
266266+267267+char *
268268+xattr_name_without_flags(const char *eaname)
269269+{
270270+ char *retval = NULL;
271271+ char *tmp;
272272+273273+ if ((tmp = strrchr(eaname, FLAG_DELIM_CHAR)) == NULL) {
274274+ retval = strdup(eaname);
275275+ } else {
276276+ retval = calloc(tmp - eaname + 1, 1);
277277+ if (retval) {
278278+ strlcpy(retval, eaname, tmp - eaname + 1);
279279+ }
280280+ }
281281+ if (retval == NULL) {
282282+ errno = ENOMEM;
283283+ }
284284+ return retval;
285285+}
286286+287287+int
288288+xattr_intent_with_flags(xattr_operation_intent_t intent, xattr_flags_t flags)
289289+{
290290+ const struct divineIntent *ip;
291291+292292+ for (ip = intentTable; ip->intent; ip++) {
293293+ if (ip->intent == intent) {
294294+ return ip->checker(flags);
295295+ }
296296+ }
297297+ if ((flags & XATTR_FLAG_NEVER_PRESERVE) != 0)
298298+ return 0; // Special case, don't try to copy this one
299299+300300+ return 1; // Default
301301+}
302302+303303+xattr_flags_t
304304+xattr_flags_from_name(const char *eaname)
305305+{
306306+ xattr_flags_t retval = 0;
307307+ const char *propList;
308308+309309+ propList = findPropertyList(eaname);
310310+ if (propList == NULL) {
311311+ propList = nameInDefaultList(eaname);
312312+ }
313313+ if (propList != NULL) {
314314+ retval = stringToProperties(propList);
315315+ }
316316+317317+ return retval;
318318+}
319319+320320+/*
321321+ * Indicate whether an EA should be preserved, when using the
322322+ * given intent.
323323+ *
324324+ * This returns 0 if it should not be preserved, and 1 if it should.
325325+ *
326326+ * It simply looks through the tables we have above, and compares the
327327+ * xattr_operation_intent_t for the EA with the intent. If the
328328+ * EA doesn't have any properties, and it's not on the default list, the
329329+ * default is to preserve it.
330330+ */
331331+332332+int
333333+xattr_preserve_for_intent(const char *eaname, xattr_operation_intent_t intent)
334334+{
335335+ xattr_flags_t flags = xattr_flags_from_name(eaname);
336336+337337+ return xattr_intent_with_flags(intent, flags);
338338+}
339339+340340+#include "xattr_properties.h"
+149
src/copyfile/xattr_flags.h
···11+/*
22+ * Copyright (c) 2013 Apple, Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#ifndef _XATTR_FLAGS_H
2525+#define _XATTR_FLAGS_H
2626+2727+#include <stdint.h>
2828+2929+#include <sys/cdefs.h>
3030+#include <Availability.h>
3131+3232+__BEGIN_DECLS
3333+3434+/*
3535+ * xattr_operation_intent_t is used to declare what the intent of the copy is.
3636+ * Not a bit-field (for now, at least).
3737+ *
3838+ * XATTR_OPERATION_INTENT_COPY indicates that the EA is attached to an object
3939+ * that is simply being copied. E.g., cp src dst
4040+ *
4141+ * XATTR_OPERATION_INTENT_SAVE indicates that the EA is attached to an object
4242+ * being saved; as in a "safe save," the destination is being replaced by
4343+ * the source, so the question is whether the EA should be applied to the
4444+ * destination, or generated anew.
4545+ *
4646+ * XATTR_OPERATION_INTENT_SHARE indicates that the EA is attached to an object that
4747+ * is being given out to other people. For example, saving to a public folder,
4848+ * or attaching to an email message.
4949+ *
5050+ * XATTR_OPERATION_INTENT_SYNC indicates that the EA is attached to an object that
5151+ * is being synced to other storages for the same user. For example synced to
5252+ * iCloud.
5353+ */
5454+5555+#define XATTR_OPERATION_INTENT_COPY 1
5656+#define XATTR_OPERATION_INTENT_SAVE 2
5757+#define XATTR_OPERATION_INTENT_SHARE 3
5858+#define XATTR_OPERATION_INTENT_SYNC 4
5959+6060+typedef unsigned int xattr_operation_intent_t;
6161+6262+typedef uint64_t xattr_flags_t;
6363+6464+/*
6565+ * Various properties used to determine how to handle the xattr during
6666+ * copying. The intent is that the default is reasonable for most xattrs.
6767+ */
6868+6969+/*
7070+ * XATTR_FLAG_NO_EXPORT
7171+ * Declare that the extended property should not be exported; this is
7272+ * deliberately a bit vague, but this is used by XATTR_OPERATION_INTENT_SHARE
7373+ * to indicate not to preserve the xattr.
7474+ */
7575+#define XATTR_FLAG_NO_EXPORT ((xattr_flags_t)0x0001)
7676+7777+/*
7878+ * XATTR_FLAG_CONTENT_DEPENDENT
7979+ * Declares the extended attribute to be tied to the contents of the file (or
8080+ * vice versa), such that it should be re-created when the contents of the
8181+ * file change. Examples might include cryptographic keys, checksums, saved
8282+ * position or search information, and text encoding.
8383+ *
8484+ * This property causes the EA to be preserved for copy and share, but not for
8585+ * safe save. (In a safe save, the EA exists on the original, and will not
8686+ * be copied to the new version.)
8787+ */
8888+#define XATTR_FLAG_CONTENT_DEPENDENT ((xattr_flags_t)0x0002)
8989+9090+/*
9191+ * XATTR_FLAG_NEVER_PRESERVE
9292+ * Declares that the extended attribute is never to be copied, for any
9393+ * intention type.
9494+ */
9595+#define XATTR_FLAG_NEVER_PRESERVE ((xattr_flags_t)0x0004)
9696+9797+/*
9898+ * XATTR_FLAG_SYNCABLE
9999+ * Declares that the extended attribute is to be synced, used by the
100100+ * XATTR_OPERATION_ITENT_SYNC intention. Syncing tends to want to minimize the
101101+ * amount of metadata synced around, hence the default behavior is for the EA
102102+ * NOT to be synced, even if it would else be preserved for the
103103+ * XATTR_OPERATION_ITENT_COPY intention.
104104+ */
105105+#define XATTR_FLAG_SYNCABLE ((xattr_flags_t)0x0008)
106106+107107+/* Given a named extended attribute, and a copy intent, should the EA be preserved? */
108108+extern int xattr_preserve_for_intent(const char *, xattr_operation_intent_t) __OSX_AVAILABLE_STARTING( __MAC_10_10, __IPHONE_8_0);
109109+110110+/*
111111+ * Given an extended attribute name, and a set of properties, return an
112112+ * allocated C string with the name. This will return NULL on error;
113113+ * errno may be set to ENOMEM if the new name cannot be allocated, or
114114+ * ENAMETOOLONG if the new name is longer than the maximum for EAs (127 UTF8
115115+ * characters). The caller must deallocate the return value otherwise.
116116+ *
117117+ * If no properties are set, it returns a copy of the EA name.
118118+ *
119119+ * If the EA name is in the internal list, and the properties are the same as
120120+ * defined there, then it will also return an unmodified copy of the EA name.
121121+ */
122122+extern char *xattr_name_with_flags(const char *, xattr_flags_t) __OSX_AVAILABLE_STARTING( __MAC_10_10, __IPHONE_8_0);
123123+124124+/*
125125+ * Given an extended attribute name, which may or may not have properties encoded
126126+ * as a suffix, return just the name of the attribute. E.g., com.example.mine#P
127127+ * would return "com.example.mine". The return value will be NULL on error;
128128+ * errno will be set to ENOMEM if it cannot be allocated. The caller must deallocate
129129+ * the return value.
130130+ */
131131+extern char *xattr_name_without_flags(const char *) __OSX_AVAILABLE_STARTING( __MAC_10_10, __IPHONE_8_0);
132132+133133+/*
134134+ * Given an EA name, return the properties. If the name is in the internal list,
135135+ * those properties will be returned. Unknown property encodings are ignored.
136136+ */
137137+extern xattr_flags_t xattr_flags_from_name(const char *) __OSX_AVAILABLE_STARTING( __MAC_10_10, __IPHONE_8_0);
138138+139139+/*
140140+ * Given an xattr_operation_intent_t and an xattr_flags_t, return whether it should
141141+ * be preserved. The default (in case either flags or intent is 0, or unknown
142142+ * values) is to return 1; it only returns 0 if the flags and intent indicate it
143143+ * should not be preserved.
144144+ */
145145+extern int xattr_intent_with_flags(xattr_operation_intent_t, xattr_flags_t) __OSX_AVAILABLE_STARTING( __MAC_10_10, __IPHONE_8_0);
146146+147147+__END_DECLS
148148+149149+#endif /* _XATTR_FLAGS_H */
+123
src/copyfile/xattr_name_with_flags.3
···11+.\"
22+.\" Copyright (c) 2013 Apple Computer, Inc. All rights reserved.
33+.\"
44+.Dd October 7, 2013
55+.Dt XATTR_NAME_WITH_FLAGS 3
66+.Os
77+.Sh NAME
88+.Nm xattr_preserve_for_intent , xattr_name_with_flags , xattr_name_without_flags ,
99+.Nm xattr_flags_from_name , xattr_intent_with_flags
1010+.Sh LIBRARY
1111+.Lb libc
1212+.Sh SYNOPSIS
1313+.In xattr_properties.h
1414+.Ft int
1515+.Fn xattr_preserve_for_intent "const char *" "xattr_operation_intent_t"
1616+.Ft char *
1717+.Fn xattr_name_with_flags "const char *" "xattr_flags_t"
1818+.Ft char *
1919+.Fn xattr_name_without_flags "const char *"
2020+.Ft xattr_flags_t
2121+.Fn xattr_flags_from_name "const char *"
2222+.Ft int
2323+.Fn xattr_intent_with_flags "xattr_operation_intent_t" "xattr_flags_t"
2424+.Sh DESCRIPTION
2525+These functions are used in conjunction with copying extended attributes from
2626+one file to another. Various types of copying (an "intent") check flags to
2727+determine which is allowed or not.
2828+.Pp
2929+The
3030+.Fn xattr_name_with_flags
3131+function returns an extended attribute name with the appropriate flags encoded
3232+as a string; the
3333+.Fn xattr_name_without_flags
3434+undoes this, giving the name of the extended attribute without the flags
3535+encoding. The slight inverse of that is
3636+.Fn xattr_flags_from_name ,
3737+which will return the flags encoded in a name.
3838+.Pp
3939+The values returned by
4040+.Fn xattr_name_with_flags
4141+and
4242+.Fn xattr_name_without_flags
4343+are allocated using
4444+.Xr malloc 3 ,
4545+and should be released by the caller, using
4646+.Xr free 3 .
4747+.Pp
4848+These functions also have an internal table of pre-defined names, maintained
4949+by the operating system.
5050+.Pp
5151+The function
5252+.Fn xattr_intent_with_flags
5353+will return 0 if the
5454+.Ar flags
5555+argument indicates it should not be preserved for the given
5656+intent, or 1 if it should.
5757+.Pp
5858+The function
5959+.Fn xattr_presere_for_intent
6060+combines the functions above, and will return zero if the
6161+named extended attribute should be preserved during a copy for
6262+the given intent.
6363+.Sh INTENT
6464+The type
6565+.Dt xattr_operation_intent_t
6666+is an integral type, which is used to indicate what the intent for the operation
6767+is. The following intent values are defined:
6868+.Bl -tag -width XATTR_OPERATION_INTENT_SHARE
6969+.It Dv XATTR_OPERATION_INTENT_COPY
7070+Indicates that the intent is to simply copy from the source to the destination.
7171+E.g., with cp. Most extended attributes should generally be preserved in this
7272+case.
7373+.It Dv XATTR_OPERATION_INTENT_SAVE
7474+Indicates that intent is to perform a save (perhaps as in a "safe save").
7575+This differs from a copy in that the content may be changing; the destination
7676+may be over-writing or replacing the source, and som extended attributes should
7777+not be preserved during this process.
7878+.It Dv XATTR_OPERATION_INTENT_SHARE
7979+Indicates that the intent is to share, or export, the object. For example,
8080+saving as an attachment in an email message, or placing in a public folder.
8181+Sensitive information should probably not be preserved in this case.
8282+.It Dv XATTR_OPERATION_INTENT_SYNC
8383+Indicates that the intent is to sync the object to a service like iCloud.
8484+.El
8585+.Sh FLAGS
8686+Various flags are defined by the type
8787+.Dt xattr_flags_t ;
8888+the currently-defined values for this are
8989+.Bl -tag -width XATTR_FLAG_CONTENT_DEPENDENT
9090+.It Dv XATTR_FLAG_NO_EXPORT
9191+This indicates that the extended attribute should not be exported, or shared.
9292+This is used with
9393+.Dv XATTR_OPERATION_INTENT_SHARE .
9494+.It Dv XATTR_FLAG_CONTENT_DEPENDENT
9595+This indicates that the extended attribute is tied to the contents of the
9696+file (or vice versa), such that it should be re-created when the contents
9797+are changed. A checksum, for example, should not be copied, and would thus
9898+be marked with this flag.
9999+.It Dv XATTR_FLAG_NEVER_PRESERVE
100100+This indicates that the extended attribute should never be copied from a
101101+source object to a destination, no matter what the given intent is.
102102+.It Dv XATTR_FLAG_SYNCABLE
103103+This indicates that the extended attribute should be copied when the file
104104+is synced on services like iCloud. Sync services tends to want the metadata
105105+synced to be kept to a bare minimum, and may enforce additional restrictions
106106+on the acceptable size and number of extended attributes.
107107+.El
108108+.Sh EXAMPLE
109109+The following example is a simple function that, given an extended attribute
110110+name and an operation intent, will return whether or not the extended attribute
111111+should be copied. (This essentially does what
112112+.Fn xattr_preserve_for_intent
113113+does.)
114114+.Bd -literal -offset indent
115115+int
116116+ShouldCopyEA(const char *eaName, xattr_operation_intent_t intent)
117117+{
118118+ xattr_flags_t flags = xattr_flags_from_name(eaName);
119119+ return xattr_intent_with_flags(intent, flags);
120120+}
121121+.Ed
122122+.Sh HISTORY
123123+These functions first appeared in Mac OS in 2013.
+128
src/copyfile/xattr_properties.h
···11+/*
22+ * Copyright (c) 2013 Apple, Inc. All rights reserved.
33+ *
44+ * @APPLE_LICENSE_HEADER_START@
55+ *
66+ * This file contains Original Code and/or Modifications of Original Code
77+ * as defined in and that are subject to the Apple Public Source License
88+ * Version 2.0 (the 'License'). You may not use this file except in
99+ * compliance with the License. Please obtain a copy of the License at
1010+ * http://www.opensource.apple.com/apsl/ and read it before using this
1111+ * file.
1212+ *
1313+ * The Original Code and all software distributed under the License are
1414+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1515+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1616+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1717+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1818+ * Please see the License for the specific language governing rights and
1919+ * limitations under the License.
2020+ *
2121+ * @APPLE_LICENSE_HEADER_END@
2222+ */
2323+2424+#ifndef _XATTR_PROPERTIES_H
2525+#define _XATTR_PROPERTIES_H
2626+2727+#include <stdint.h>
2828+2929+#include <sys/cdefs.h>
3030+#include <Availability.h>
3131+3232+__BEGIN_DECLS
3333+3434+/*
3535+ * CopyOperationIntent_t is used to declare what the intent of the copy is.
3636+ * Not a bit-field (for now, at least).
3737+ *
3838+ * CopyOperationIntentCopy indicates that the EA is attached to an object
3939+ * that is simply being copied. E.g., cp src dst
4040+ *
4141+ * CopyOperationIntentSave indicates that the EA is attached to an object
4242+ * being saved; as in a "safe save," the destination is being replaced by
4343+ * the source, so the question is whether the EA should be applied to the
4444+ * destination, or generated anew.
4545+ *
4646+ * CopyOperationIntentShare indicates that the EA is attached to an object that
4747+ * is being given out to other people. For example, saving to a public folder,
4848+ * or attaching to an email message.
4949+ */
5050+5151+typedef enum {
5252+ CopyOperationIntentCopy = 1,
5353+ CopyOperationIntentSave,
5454+ CopyOperationIntentShare,
5555+} CopyOperationIntent_t;
5656+5757+typedef uint64_t CopyOperationProperties_t;
5858+5959+/*
6060+ * Various properties used to determine how to handle the xattr during
6161+ * copying. The intent is that the default is reasonable for most xattrs.
6262+ */
6363+6464+/*
6565+ * kCopyOperationPropertyNoExport
6666+ * Declare that the extended property should not be exported; this is
6767+ * deliberately a bit vague, but this is used by CopyOperationIntentShare
6868+ * to indicate not to preserve the xattr.
6969+ */
7070+#define kCopyOperationPropertyNoExport ((CopyOperationProperties_t)0x0001)
7171+7272+/*
7373+ * kCopyOperationPropertyContentDependent
7474+ * Declares the extended attribute to be tied to the contents of the file (or
7575+ * vice versa), such that it should be re-created when the contents of the
7676+ * file change. Examples might include cryptographic keys, checksums, saved
7777+ * position or search information, and text encoding.
7878+ *
7979+ * This property causes the EA to be preserved for copy and share, but not for
8080+ * safe save. (In a safe save, the EA exists on the original, and will not
8181+ * be copied to the new version.)
8282+ */
8383+#define kCopyOperationPropertyContentDependent ((CopyOperationProperties_t)0x0002)
8484+8585+/*
8686+ * kCopyOperationPropertyNeverPreserve
8787+ * Declares that the extended attribute is never to be copied, for any
8888+ * intention type.
8989+ */
9090+#define kCopyOperationPropertyNeverPreserve ((CopyOperationProperties_t)0x0004)
9191+9292+#if 0
9393+/*
9494+ * These are all going to be removed, and I don't believe anyone used them.
9595+ */
9696+/*
9797+ * Given an extended attribute name, and a set of properties, return an
9898+ * allocated C string with the name. This will return NULL on error;
9999+ * errno may be set to ENOMEM if the new name cannot be allocated, or
100100+ * ENAMETOOLONG if the new name is longer than the maximum for EAs (127 UTF8
101101+ * characters). The caller must deallocate the return value otherwise.
102102+ *
103103+ * If no properties are set, it returns a copy of the EA name.
104104+ *
105105+ * If the EA name is in the internal list, and the properties are the same as
106106+ * defined there, then it will also return an unmodified copy of the EA name.
107107+ */
108108+extern char *_xattrNameWithProperties(const char *, CopyOperationProperties_t) DEPRECATED_IN_MAC_OS_X_VERSION_10_10_AND_LATER;
109109+110110+/*
111111+ * Given an extended attribute name, which may or may not have properties encoded
112112+ * as a suffix, return just the name of the attribute. E.g., com.example.mine#P
113113+ * would return "com.example.mine". The return value will be NULL on error;
114114+ * errno will be set to ENOMEM if it cannot be allocated. The caller must deallocate
115115+ * the return value.
116116+ */
117117+extern char *_xattrNameWithoutProperties(const char *) DEPRECATED_IN_MAC_OS_X_VERSION_10_10_AND_LATER;
118118+119119+/*
120120+ * Given an EA name, return the properties. If the name is in the internal list,
121121+ * those properties will be returned. Unknown property encodings are ignored.
122122+ */
123123+extern CopyOperationProperties_t _xattrPropertiesFromName(const char *) DEPRECATED_IN_MAC_OS_X_VERSION_10_10_AND_LATER;
124124+#endif /* 0 */
125125+126126+__END_DECLS
127127+128128+#endif /* _XATTR_PROPERTIES_H */