]>
Commit | Line | Data |
---|---|---|
3d9156a7 A |
1 | /* |
2 | * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include <err.h> | |
25 | #include <errno.h> | |
26 | #include <sys/types.h> | |
27 | #include <sys/acl.h> | |
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
31 | #include <stdint.h> | |
32 | #include <syslog.h> | |
33 | #include <unistd.h> | |
34 | #include <fcntl.h> | |
35 | #include <sys/errno.h> | |
36 | #include <sys/stat.h> | |
37 | #include <sys/xattr.h> | |
38 | #include <sys/syscall.h> | |
39 | #include <sys/param.h> | |
40 | #include <sys/acl.h> | |
41 | #include <libkern/OSByteOrder.h> | |
42 | #include <membership.h> | |
43 | ||
44 | #include <copyfile.h> | |
45 | ||
46 | struct _copyfile_state | |
47 | { | |
48 | char *src; | |
49 | char *dst; | |
50 | int src_fd; | |
51 | int dst_fd; | |
52 | struct stat sb; | |
53 | filesec_t fsec; | |
54 | copyfile_flags_t flags; | |
55 | void *stats; | |
56 | uint32_t debug; | |
57 | }; | |
58 | ||
59 | static int copyfile_open (copyfile_state_t); | |
60 | static int copyfile_close (copyfile_state_t); | |
61 | static int copyfile_data (copyfile_state_t); | |
62 | static int copyfile_stat (copyfile_state_t); | |
63 | static int copyfile_security (copyfile_state_t); | |
64 | static int copyfile_xattr (copyfile_state_t); | |
65 | static int copyfile_pack (copyfile_state_t); | |
66 | static int copyfile_unpack (copyfile_state_t); | |
67 | ||
68 | static copyfile_flags_t copyfile_check (copyfile_state_t); | |
69 | static int copyfile_fix_perms(copyfile_state_t, filesec_t *, int); | |
70 | ||
71 | #define COPYFILE_DEBUG (1<<31) | |
72 | ||
73 | #ifndef _COPYFILE_TEST | |
74 | # define copyfile_warn(str, ...) syslog(LOG_WARNING, str ": %m", ## __VA_ARGS__) | |
75 | # define copyfile_debug(d, str, ...) \ | |
76 | if (s && (d <= s->debug)) {\ | |
77 | syslog(LOG_DEBUG, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \ | |
78 | } else | |
79 | #else | |
80 | #define copyfile_warn(str, ...) \ | |
81 | fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : "") | |
82 | # define copyfile_debug(d, str, ...) \ | |
83 | if (s && (d <= s->debug)) {\ | |
84 | fprintf(stderr, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \ | |
85 | } else | |
86 | #endif | |
87 | ||
88 | int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_flags_t flags) | |
89 | { | |
90 | int ret = 0; | |
91 | copyfile_state_t s = state; | |
92 | filesec_t original_fsec; | |
93 | int fix_perms = 0; | |
94 | ||
95 | original_fsec = filesec_init(); | |
96 | if (s == NULL && (s = copyfile_init()) == NULL) | |
97 | return -1; | |
98 | ||
99 | if (src != NULL) | |
100 | { | |
101 | if (s->src_fd != -2 && s->src && !strncmp(src, s->src, MAXPATHLEN)) | |
102 | close(s->src_fd); | |
103 | s->src = strdup(src); | |
104 | } | |
105 | ||
106 | if (dst != NULL) | |
107 | { | |
108 | if (s->dst_fd != -2 && s->dst && !strncmp(dst, s->dst, MAXPATHLEN)) | |
109 | close(s->dst_fd); | |
110 | s->dst = strdup(dst); | |
111 | } | |
112 | ||
113 | s->flags = flags; | |
114 | ||
115 | if (COPYFILE_DEBUG & s->flags) | |
116 | { | |
117 | char *e; | |
118 | if ((e = getenv("COPYFILE_DEBUG"))) | |
119 | { | |
120 | s->debug = strtol(e, NULL, 0); | |
121 | if (s->debug < 1) | |
122 | s->debug = 1; | |
123 | } | |
124 | copyfile_debug(1, "debug value set to: %d\n", s->debug); | |
125 | } | |
eb1cde05 | 126 | |
3d9156a7 A |
127 | if (COPYFILE_CHECK & flags) |
128 | return copyfile_check(s); | |
129 | ||
130 | if (copyfile_open(s) < 0) | |
131 | ret = -1; | |
132 | else | |
133 | { | |
134 | if (s->dst_fd == -2 || s->src_fd == -2) | |
135 | return 0; | |
136 | ||
137 | if (COPYFILE_PACK & flags) | |
138 | { | |
139 | if (copyfile_pack(s) < 0) | |
140 | { | |
141 | unlink(s->dst); | |
142 | ret = -1; | |
143 | } | |
144 | } else if (COPYFILE_UNPACK & flags) | |
145 | { | |
0f515e26 A |
146 | if (!(COPYFILE_STAT & flags || COPYFILE_ACL & flags)) |
147 | fix_perms = !copyfile_fix_perms(s, &original_fsec, 1); | |
148 | ||
3d9156a7 A |
149 | if (copyfile_unpack(s) < 0) |
150 | ret = -1; | |
151 | } else | |
152 | { | |
153 | if (COPYFILE_SECURITY & flags) | |
154 | { | |
155 | if (copyfile_security(s) < 0) | |
156 | { | |
157 | copyfile_warn("error processing security information"); | |
158 | ret -= 1; | |
159 | } | |
160 | } else if (COPYFILE_UNPACK & flags) | |
161 | { | |
162 | fix_perms = !copyfile_fix_perms(s, &original_fsec, 1); | |
163 | if (copyfile_unpack(s) < 0) | |
164 | ret = -1; | |
165 | } else | |
166 | { | |
167 | if (COPYFILE_SECURITY & flags) | |
168 | { | |
169 | copyfile_warn("error processing stat information"); | |
170 | ret -= 1; | |
171 | } | |
172 | } | |
173 | fix_perms = !copyfile_fix_perms(s, &original_fsec, 1); | |
174 | ||
175 | if (COPYFILE_XATTR & flags) | |
176 | { | |
177 | if (copyfile_xattr(s) < 0) | |
178 | { | |
179 | copyfile_warn("error processing extended attributes"); | |
180 | ret -= 1; | |
181 | } | |
182 | } | |
183 | if (COPYFILE_DATA & flags) | |
184 | { | |
185 | if (copyfile_data(s) < 0) | |
186 | { | |
187 | copyfile_warn("error processing data"); | |
188 | ret = -1; | |
189 | if (s->dst && unlink(s->dst)) | |
190 | copyfile_warn("%s: remove", s->src); | |
191 | goto exit; | |
192 | } | |
193 | } | |
194 | } | |
195 | } | |
196 | exit: | |
197 | if (fix_perms) | |
198 | copyfile_fix_perms(s, &original_fsec, 0); | |
199 | ||
200 | filesec_free(original_fsec); | |
201 | ||
eb1cde05 A |
202 | if (state == NULL) |
203 | ret -= copyfile_free(s); | |
3d9156a7 A |
204 | return ret; |
205 | } | |
206 | ||
207 | copyfile_state_t copyfile_init(void) | |
208 | { | |
209 | copyfile_state_t s = (copyfile_state_t) calloc(1, sizeof(struct _copyfile_state)); | |
210 | ||
211 | if (s != NULL) | |
212 | { | |
213 | s->src_fd = -2; | |
214 | s->dst_fd = -2; | |
215 | s->fsec = filesec_init(); | |
216 | } | |
217 | ||
218 | return s; | |
219 | } | |
220 | ||
221 | int copyfile_free(copyfile_state_t s) | |
222 | { | |
223 | if (s != NULL) | |
224 | { | |
225 | if (s->fsec) | |
226 | filesec_free(s->fsec); | |
227 | ||
228 | if (s->dst) | |
229 | free(s->dst); | |
230 | if (s->src) | |
231 | free(s->src); | |
232 | if (copyfile_close(s) < 0) | |
233 | { | |
234 | copyfile_warn("error closing files"); | |
235 | return -1; | |
236 | } | |
237 | free(s); | |
238 | } | |
239 | return 0; | |
240 | } | |
241 | ||
242 | static int copyfile_close(copyfile_state_t s) | |
243 | { | |
eb1cde05 | 244 | if (s->src_fd != -2) |
3d9156a7 A |
245 | close(s->src_fd); |
246 | ||
eb1cde05 | 247 | if (s->dst_fd != -2 && close(s->dst_fd)) |
3d9156a7 A |
248 | { |
249 | copyfile_warn("close on %s", s->dst); | |
250 | return -1; | |
251 | } | |
252 | return 0; | |
253 | } | |
254 | ||
255 | static int copyfile_fix_perms(copyfile_state_t s, filesec_t *fsec, int on) | |
256 | { | |
257 | filesec_t tmp_fsec; | |
258 | struct stat sb; | |
259 | mode_t mode; | |
260 | acl_t acl; | |
261 | ||
262 | if (on) | |
263 | { | |
eb1cde05 | 264 | if(statx_np(s->dst, &sb, *fsec)) |
3d9156a7 A |
265 | goto error; |
266 | ||
267 | tmp_fsec = filesec_dup(*fsec); | |
268 | ||
269 | if (!filesec_get_property(tmp_fsec, FILESEC_ACL, &acl)) | |
270 | { | |
271 | acl_entry_t entry; | |
272 | acl_permset_t permset; | |
273 | uuid_t qual; | |
274 | ||
275 | if (mbr_uid_to_uuid(getuid(), qual) != 0) | |
276 | goto error; | |
277 | ||
278 | if (acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY) == -1) | |
279 | goto error; | |
280 | if (acl_get_permset(entry, &permset) == -1) | |
281 | goto error; | |
282 | if (acl_clear_perms(permset) == -1) | |
283 | goto error; | |
284 | if (acl_add_perm(permset, ACL_WRITE_DATA) == -1) | |
285 | goto error; | |
286 | if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1) | |
287 | goto error; | |
288 | if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1) | |
289 | goto error; | |
290 | if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1) | |
291 | goto error; | |
292 | ||
293 | if(acl_set_permset(entry, permset) == -1) | |
294 | goto error; | |
295 | if(acl_set_qualifier(entry, qual) == -1) | |
296 | goto error; | |
297 | ||
298 | if (filesec_set_property(tmp_fsec, FILESEC_ACL, &acl) != 0) | |
299 | goto error; | |
300 | } | |
301 | ||
302 | if (filesec_get_property(tmp_fsec, FILESEC_MODE, &mode) == 0) | |
303 | { | |
304 | if (~mode & S_IWUSR) | |
305 | { | |
306 | mode |= S_IWUSR; | |
307 | if (filesec_set_property(tmp_fsec, FILESEC_MODE, &mode) != 0) | |
308 | goto error; | |
309 | } | |
310 | } | |
311 | if (fchmodx_np(s->dst_fd, tmp_fsec) < 0 && errno != ENOTSUP) | |
312 | copyfile_warn("setting security information"); | |
313 | filesec_free(tmp_fsec); | |
314 | } else | |
315 | if (fchmodx_np(s->dst_fd, *fsec) < 0 && errno != ENOTSUP) | |
316 | copyfile_warn("setting security information"); | |
317 | ||
318 | return 0; | |
319 | error: | |
320 | filesec_free(*fsec); | |
321 | return -1; | |
322 | } | |
323 | ||
324 | ||
325 | static int copyfile_open(copyfile_state_t s) | |
326 | { | |
327 | int oflags = O_EXCL | O_CREAT; | |
328 | ||
329 | if (s->src && s->src_fd == -2) | |
330 | { | |
331 | if ((COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np) | |
332 | (s->src, &s->sb, s->fsec)) | |
333 | { | |
334 | copyfile_warn("stat on %s", s->src); | |
335 | return -1; | |
336 | } | |
337 | if ((s->src_fd = open(s->src, O_RDONLY, 0)) < 0) | |
338 | { | |
339 | copyfile_warn("open on %s", s->src); | |
340 | return -1; | |
341 | } | |
342 | } | |
343 | ||
344 | if (s->dst && s->dst_fd == -2) | |
345 | { | |
346 | if (COPYFILE_DATA & s->flags || COPYFILE_PACK & s->flags) | |
347 | oflags |= O_WRONLY; | |
348 | ||
349 | if (COPYFILE_ACL & ~s->flags) | |
350 | { | |
351 | if (filesec_set_property(s->fsec, FILESEC_ACL, NULL) == -1) | |
352 | { | |
353 | copyfile_debug(1, "unsetting acl attribute on %s", s->dst); | |
354 | } | |
355 | } | |
356 | if (COPYFILE_STAT & ~s->flags) | |
357 | { | |
358 | if (filesec_set_property(s->fsec, FILESEC_MODE, NULL) == -1) | |
359 | { | |
360 | copyfile_debug(1, "unsetting mode attribute on %s", s->dst); | |
361 | } | |
362 | } | |
363 | if (COPYFILE_PACK & s->flags) | |
364 | { | |
365 | mode_t m = S_IRUSR; | |
366 | if (filesec_set_property(s->fsec, FILESEC_MODE, &m) == -1) | |
367 | { | |
368 | mode_t m = S_IRUSR | S_IWUSR; | |
369 | if (filesec_set_property(s->fsec, FILESEC_MODE, &m) == -1) | |
370 | { | |
371 | copyfile_debug(1, "setting mode attribute on %s", s->dst); | |
372 | } | |
373 | } | |
374 | if (filesec_set_property(s->fsec, FILESEC_OWNER, NULL) == -1) | |
375 | { | |
376 | copyfile_debug(1, "unsetting uid attribute on %s", s->dst); | |
377 | } | |
378 | if (filesec_set_property(s->fsec, FILESEC_UUID, NULL) == -1) | |
379 | { | |
380 | copyfile_debug(1, "unsetting uuid attribute on %s", s->dst); | |
381 | } | |
382 | if (filesec_set_property(s->fsec, FILESEC_GROUP, NULL) == -1) | |
383 | { | |
384 | copyfile_debug(1, "unsetting gid attribute on %s", s->dst); | |
385 | } | |
386 | } | |
387 | ||
388 | if (COPYFILE_UNLINK & s->flags && unlink(s->dst) < 0) | |
389 | { | |
390 | copyfile_warn("%s: remove", s->dst); | |
391 | return -1; | |
392 | } | |
393 | ||
eb1cde05 | 394 | while((s->dst_fd = openx_np(s->dst, oflags, s->fsec)) < 0) |
3d9156a7 | 395 | { |
eb1cde05 | 396 | if (EEXIST == errno) |
3d9156a7 | 397 | { |
eb1cde05 A |
398 | oflags = oflags & ~O_CREAT; |
399 | continue; | |
3d9156a7 A |
400 | } |
401 | copyfile_warn("open on %s", s->dst); | |
402 | return -1; | |
403 | } | |
404 | } | |
405 | return 0; | |
406 | } | |
407 | ||
408 | static copyfile_flags_t copyfile_check(copyfile_state_t s) | |
409 | { | |
410 | acl_t acl; | |
411 | copyfile_flags_t ret = 0; | |
412 | ||
413 | if (!s->src) | |
414 | return ret; | |
415 | ||
416 | // check EAs | |
417 | if (COPYFILE_XATTR & s->flags) | |
418 | if (listxattr(s->src, 0, 0, 0) > 0) | |
419 | ret |= COPYFILE_XATTR; | |
420 | ||
421 | if (COPYFILE_ACL & s->flags) | |
422 | { | |
423 | (COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np) | |
424 | (s->src, &s->sb, s->fsec); | |
425 | ||
426 | if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) == 0) | |
427 | ret |= COPYFILE_ACL; | |
428 | } | |
429 | ||
430 | return ret; | |
431 | } | |
432 | ||
433 | static int copyfile_data(copyfile_state_t s) | |
434 | { | |
435 | unsigned int blen; | |
436 | char *bp; | |
437 | int nread; | |
438 | int ret; | |
439 | ||
440 | if ((bp = malloc((size_t)s->sb.st_blksize)) == NULL) | |
441 | { | |
442 | blen = 0; | |
443 | warnx("malloc failed"); | |
444 | return -1; | |
445 | } | |
446 | blen = s->sb.st_blksize; | |
447 | ||
448 | while ((nread = read(s->src_fd, bp, (size_t)blen)) > 0) | |
449 | { | |
450 | if (write(s->dst_fd, bp, (size_t)nread) != nread) | |
451 | { | |
452 | copyfile_warn("writing to %s", s->dst); | |
453 | return -1; | |
454 | } | |
455 | } | |
456 | if (nread < 0) | |
457 | { | |
458 | copyfile_warn("reading from %s", s->src); | |
459 | ret = -1; | |
460 | } | |
461 | ||
462 | free(bp); | |
463 | ||
464 | if (ftruncate(s->dst_fd, s->sb.st_size) < 0) | |
465 | ret = -1; | |
466 | ||
467 | return ret; | |
468 | } | |
469 | ||
470 | static int copyfile_security(copyfile_state_t s) | |
471 | { | |
472 | filesec_t fsec_dst = filesec_init(); | |
473 | ||
474 | int copied = 0; | |
475 | acl_flagset_t flags; | |
476 | struct stat sb; | |
477 | acl_entry_t entry_src = NULL, entry_dst = NULL; | |
478 | acl_t acl_src, acl_dst; | |
479 | int inited_dst = 0, inited_src = 0, ret = 0; | |
480 | ||
481 | if (COPYFILE_ACL & s->flags) | |
482 | { | |
483 | if(fstatx_np(s->dst_fd, &sb, fsec_dst)) | |
484 | { | |
485 | goto cleanup; | |
486 | } | |
487 | ||
488 | if (filesec_get_property(fsec_dst, FILESEC_ACL, &acl_dst)) | |
489 | { | |
490 | if (errno == ENOENT) | |
491 | { | |
492 | acl_dst = acl_init(4); | |
493 | inited_dst = 1; | |
494 | } | |
495 | else | |
496 | { | |
497 | ret = -1; | |
498 | goto cleanup; | |
499 | } | |
500 | } | |
501 | ||
502 | if (filesec_get_property(s->fsec, FILESEC_ACL, &acl_src)) | |
503 | { | |
504 | if (errno == ENOENT) | |
505 | { | |
506 | if (inited_dst) | |
507 | goto no_acl; | |
508 | acl_dst = acl_init(4); | |
509 | inited_src = 1; | |
510 | } | |
511 | else | |
512 | { | |
513 | ret = -1; | |
514 | goto cleanup; | |
515 | } | |
516 | } | |
517 | ||
518 | for (;acl_get_entry(acl_src, | |
519 | entry_src == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, | |
520 | &entry_src) == 0;) | |
521 | { | |
522 | acl_get_flagset_np(entry_src, &flags); | |
523 | if (!acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) | |
524 | { | |
525 | if ((ret = acl_create_entry(&acl_dst, &entry_dst)) == -1) | |
526 | goto cleanup; | |
527 | ||
528 | if ((ret = acl_copy_entry(entry_dst, entry_src)) == -1) | |
529 | goto cleanup; | |
530 | ||
531 | copyfile_debug(1, "copied acl entry from %s to %s", s->src, s->dst); | |
532 | copied++; | |
533 | } | |
534 | } | |
535 | ||
eb1cde05 | 536 | if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_dst)) |
3d9156a7 A |
537 | { |
538 | copyfile_debug(1, "altered acl"); | |
539 | } | |
540 | } | |
541 | no_acl: | |
eb1cde05 | 542 | if (fchmodx_np(s->dst_fd, s->fsec) < 0 && errno != ENOTSUP) |
3d9156a7 A |
543 | copyfile_warn("setting security information: %s", s->dst); |
544 | ||
545 | cleanup: | |
546 | filesec_free(fsec_dst); | |
547 | if (inited_src) acl_free(acl_src); | |
548 | if (inited_dst) acl_free(acl_dst); | |
549 | ||
550 | return ret; | |
551 | } | |
552 | ||
553 | static int copyfile_stat(copyfile_state_t s) | |
554 | { | |
555 | struct timeval tval[2]; | |
556 | /* | |
557 | * NFS doesn't support chflags; ignore errors unless there's reason | |
558 | * to believe we're losing bits. (Note, this still won't be right | |
559 | * if the server supports flags and we were trying to *remove* flags | |
560 | * on a file that we copied, i.e., that we didn't create.) | |
561 | */ | |
562 | if (chflags(s->dst, (u_long)s->sb.st_flags)) | |
563 | if (errno != EOPNOTSUPP || s->sb.st_flags != 0) | |
564 | copyfile_warn("%s: set flags (was: 0%07o)", s->dst, s->sb.st_flags); | |
565 | ||
566 | tval[0].tv_sec = s->sb.st_atime; | |
567 | tval[1].tv_sec = s->sb.st_mtime; | |
568 | tval[0].tv_usec = tval[1].tv_usec = 0; | |
569 | if (utimes(s->dst, tval)) | |
570 | copyfile_warn("%s: set times", s->dst); | |
571 | return 0; | |
572 | } | |
573 | ||
574 | static int copyfile_xattr(copyfile_state_t s) | |
575 | { | |
576 | char *name; | |
577 | char *namebuf; | |
578 | size_t xa_size; | |
579 | void *xa_dataptr; | |
580 | size_t bufsize = 4096; | |
581 | ssize_t asize; | |
582 | ssize_t nsize; | |
583 | int ret = 0; | |
584 | int flags = 0; | |
585 | ||
586 | if (COPYFILE_NOFOLLOW_SRC & s->flags) | |
587 | flags |= XATTR_NOFOLLOW; | |
588 | ||
589 | /* delete EAs on destination */ | |
590 | if ((nsize = flistxattr(s->dst_fd, 0, 0, 0)) > 0) | |
591 | { | |
592 | if ((namebuf = (char *) malloc(nsize)) == NULL) | |
593 | return -1; | |
594 | else | |
595 | nsize = flistxattr(s->dst_fd, namebuf, nsize, 0); | |
596 | ||
597 | if (nsize > 0) | |
598 | for (name = namebuf; name < namebuf + nsize; name += strlen(name) + 1) | |
599 | fremovexattr(s->dst_fd, name,flags); | |
600 | ||
601 | free(namebuf); | |
602 | } else if (nsize < 0) | |
603 | { | |
604 | if (errno == ENOTSUP) | |
605 | return 0; | |
606 | else | |
607 | return -1; | |
608 | } | |
609 | ||
610 | /* get name list of EAs on source */ | |
611 | if ((nsize = flistxattr(s->src_fd, 0, 0, 0)) < 0) | |
612 | { | |
613 | if (errno == ENOTSUP) | |
614 | return 0; | |
615 | else | |
616 | return -1; | |
617 | } else if (nsize == 0) | |
618 | return 0; | |
619 | ||
620 | if ((namebuf = (char *) malloc(nsize)) == NULL) | |
621 | return -1; | |
622 | else | |
623 | nsize = flistxattr(s->src_fd, namebuf, nsize, 0); | |
624 | ||
625 | if (nsize <= 0) | |
626 | return nsize; | |
627 | ||
628 | if ((xa_dataptr = (void *) malloc(bufsize)) == NULL) | |
629 | return -1; | |
630 | ||
631 | for (name = namebuf; name < namebuf + nsize; name += strlen(name) + 1) | |
632 | { | |
633 | if ((xa_size = fgetxattr(s->src_fd, name, 0, 0, 0, flags)) < 0) | |
634 | { | |
635 | ret = -1; | |
636 | continue; | |
637 | } | |
638 | ||
639 | if (xa_size > bufsize) | |
640 | { | |
641 | bufsize = xa_size; | |
642 | if ((xa_dataptr = | |
643 | (void *) realloc((void *) xa_dataptr, bufsize)) == NULL) | |
644 | { | |
645 | ret = -1; | |
646 | continue; | |
647 | } | |
648 | } | |
649 | ||
650 | if ((asize = fgetxattr(s->src_fd, name, xa_dataptr, xa_size, 0, flags)) < 0) | |
651 | { | |
652 | ret = -1; | |
653 | continue; | |
654 | } | |
655 | ||
656 | if (xa_size != asize) | |
657 | xa_size = asize; | |
658 | ||
659 | if (fsetxattr(s->dst_fd, name, xa_dataptr, xa_size, 0, flags) < 0) | |
660 | { | |
661 | ret = -1; | |
662 | continue; | |
663 | } | |
664 | } | |
665 | free((void *) xa_dataptr); | |
666 | return ret; | |
667 | } | |
668 | ||
669 | ||
670 | #ifdef _COPYFILE_TEST | |
671 | #define COPYFILE_OPTION(x) { #x, COPYFILE_ ## x }, | |
672 | ||
673 | struct {char *s; int v;} opts[] = { | |
674 | COPYFILE_OPTION(ACL) | |
675 | COPYFILE_OPTION(STAT) | |
676 | COPYFILE_OPTION(XATTR) | |
677 | COPYFILE_OPTION(DATA) | |
678 | COPYFILE_OPTION(SECURITY) | |
679 | COPYFILE_OPTION(METADATA) | |
680 | COPYFILE_OPTION(ALL) | |
681 | COPYFILE_OPTION(NOFOLLOW_SRC) | |
682 | COPYFILE_OPTION(NOFOLLOW_DST) | |
683 | COPYFILE_OPTION(NOFOLLOW) | |
684 | COPYFILE_OPTION(EXCL) | |
685 | COPYFILE_OPTION(MOVE) | |
686 | COPYFILE_OPTION(UNLINK) | |
687 | COPYFILE_OPTION(PACK) | |
688 | COPYFILE_OPTION(UNPACK) | |
689 | COPYFILE_OPTION(CHECK) | |
690 | COPYFILE_OPTION(VERBOSE) | |
691 | COPYFILE_OPTION(DEBUG) | |
692 | {NULL, 0} | |
693 | }; | |
694 | ||
695 | int main(int c, char *v[]) | |
696 | { | |
697 | int i; | |
698 | int flags = 0; | |
699 | ||
700 | if (c < 3) | |
701 | errx(1, "insufficient arguments"); | |
702 | ||
703 | while(c-- > 3) | |
704 | { | |
705 | for (i = 0; opts[i].s != NULL; ++i) | |
706 | { | |
707 | if (strcasecmp(opts[i].s, v[c]) == 0) | |
708 | { | |
709 | printf("option %d: %s <- %d\n", c, opts[i].s, opts[i].v); | |
710 | flags |= opts[i].v; | |
711 | break; | |
712 | } | |
713 | } | |
714 | } | |
715 | ||
716 | return copyfile(v[1], v[2], NULL, flags); | |
717 | } | |
718 | #endif | |
719 | /* | |
720 | * Apple Double Create | |
721 | * | |
722 | * Create an Apple Double "._" file from a file's extented attributes | |
723 | * | |
724 | * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. | |
725 | */ | |
726 | ||
727 | ||
728 | #define offsetof(type, member) ((size_t)(&((type *)0)->member)) | |
729 | ||
730 | #define XATTR_MAXATTRLEN (4*1024) | |
731 | ||
732 | ||
733 | /* | |
734 | Typical "._" AppleDouble Header File layout: | |
735 | ------------------------------------------------------------ | |
736 | MAGIC 0x00051607 | |
737 | VERSION 0x00020000 | |
738 | FILLER 0 | |
739 | COUNT 2 | |
740 | .-- AD ENTRY[0] Finder Info Entry (must be first) | |
741 | .--+-- AD ENTRY[1] Resource Fork Entry (must be last) | |
742 | | '-> FINDER INFO | |
743 | | ///////////// Fixed Size Data (32 bytes) | |
744 | | EXT ATTR HDR | |
745 | | ///////////// | |
746 | | ATTR ENTRY[0] --. | |
747 | | ATTR ENTRY[1] --+--. | |
748 | | ATTR ENTRY[2] --+--+--. | |
749 | | ... | | | | |
750 | | ATTR ENTRY[N] --+--+--+--. | |
751 | | ATTR DATA 0 <-' | | | | |
752 | | //////////// | | | | |
753 | | ATTR DATA 1 <----' | | | |
754 | | ///////////// | | | |
755 | | ATTR DATA 2 <-------' | | |
756 | | ///////////// | | |
757 | | ... | | |
758 | | ATTR DATA N <----------' | |
759 | | ///////////// | |
760 | | Attribute Free Space | |
761 | | | |
762 | '----> RESOURCE FORK | |
763 | ///////////// Variable Sized Data | |
764 | ///////////// | |
765 | ///////////// | |
766 | ///////////// | |
767 | ///////////// | |
768 | ///////////// | |
769 | ... | |
770 | ///////////// | |
771 | ||
772 | ------------------------------------------------------------ | |
773 | ||
774 | NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are | |
775 | stored as part of the Finder Info. The length in the Finder | |
776 | Info AppleDouble entry includes the length of the extended | |
777 | attribute header, attribute entries, and attribute data. | |
778 | */ | |
779 | ||
780 | ||
781 | /* | |
782 | * On Disk Data Structures | |
783 | * | |
784 | * Note: Motorola 68K alignment and big-endian. | |
785 | * | |
786 | * See RFC 1740 for additional information about the AppleDouble file format. | |
787 | * | |
788 | */ | |
789 | ||
790 | #define ADH_MAGIC 0x00051607 | |
791 | #define ADH_VERSION 0x00020000 | |
792 | #define ADH_MACOSX "Mac OS X " | |
793 | ||
794 | /* | |
795 | * AppleDouble Entry ID's | |
796 | */ | |
797 | #define AD_DATA 1 /* Data fork */ | |
798 | #define AD_RESOURCE 2 /* Resource fork */ | |
799 |