]> git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_xattr.c
dacd51db9199289654e031c9045f82866236bf23
[apple/xnu.git] / bsd / vfs / vfs_xattr.c
1 /*
2 * Copyright (c) 2004-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
30 * support for mandatory and extensible security protections. This notice
31 * is included in support of clause 2.2 (b) of the Apple Public License,
32 * Version 2.0.
33 */
34
35 #include <sys/param.h>
36
37 #include <sys/fcntl.h>
38 #include <sys/fsevents.h>
39 #include <sys/kernel.h>
40 #include <sys/kauth.h>
41 #include <sys/malloc.h>
42 #include <sys/mount_internal.h>
43 #include <sys/namei.h>
44 #include <sys/proc_internal.h>
45 #include <sys/stat.h>
46 #include <sys/uio.h>
47 #include <sys/utfconv.h>
48 #include <sys/vnode.h>
49 #include <sys/vnode_internal.h>
50 #include <sys/xattr.h>
51
52 #include <libkern/OSByteOrder.h>
53 #include <vm/vm_kern.h>
54
55 #if CONFIG_MACF
56 #include <security/mac_framework.h>
57 #endif
58
59
60 #if NAMEDSTREAMS
61
62 static int shadow_sequence;
63
64 /*
65 * We use %p to prevent loss of precision for pointers on varying architectures.
66 */
67
68 #define SHADOW_NAME_FMT ".vfs_rsrc_stream_%p%08x%p"
69 #define SHADOW_DIR_FMT ".vfs_rsrc_streams_%p%x"
70 #define SHADOW_DIR_CONTAINER "/var/run"
71
72 #define MAKE_SHADOW_NAME(VP, NAME) \
73 snprintf((NAME), sizeof((NAME)), (SHADOW_NAME_FMT), \
74 ((void*)(VM_KERNEL_ADDRPERM(VP))), \
75 (VP)->v_id, \
76 ((void*)(VM_KERNEL_ADDRPERM((VP)->v_data))))
77
78 /* The full path to the shadow directory */
79 #define MAKE_SHADOW_DIRNAME(VP, NAME) \
80 snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_CONTAINER "/" SHADOW_DIR_FMT), \
81 ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence)
82
83 /* The shadow directory as a 'leaf' entry */
84 #define MAKE_SHADOW_DIR_LEAF(VP, NAME) \
85 snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_FMT), \
86 ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence)
87
88 static int default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context);
89
90 static int default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context);
91
92 static int default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context);
93
94 static int getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, int *creator, vfs_context_t context);
95
96 static int get_shadow_dir(vnode_t *sdvpp);
97
98 #endif /* NAMEDSTREAMS */
99
100 /*
101 * Default xattr support routines.
102 */
103
104 static int default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, int options,
105 vfs_context_t context);
106 static int default_setxattr(vnode_t vp, const char *name, uio_t uio, int options,
107 vfs_context_t context);
108 static int default_listxattr(vnode_t vp, uio_t uio, size_t *size, int options,
109 vfs_context_t context);
110 static int default_removexattr(vnode_t vp, const char *name, int options,
111 vfs_context_t context);
112
113 /*
114 * Retrieve the data of an extended attribute.
115 */
116 int
117 vn_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size,
118 int options, vfs_context_t context)
119 {
120 int error;
121
122 if (!XATTR_VNODE_SUPPORTED(vp)) {
123 return EPERM;
124 }
125 #if NAMEDSTREAMS
126 /* getxattr calls are not allowed for streams. */
127 if (vp->v_flag & VISNAMEDSTREAM) {
128 error = EPERM;
129 goto out;
130 }
131 #endif
132 /*
133 * Non-kernel request need extra checks performed.
134 *
135 * The XATTR_NOSECURITY flag implies a kernel request.
136 */
137 if (!(options & XATTR_NOSECURITY)) {
138 #if CONFIG_MACF
139 error = mac_vnode_check_getextattr(context, vp, name, uio);
140 if (error) {
141 goto out;
142 }
143 #endif /* MAC */
144 if ((error = xattr_validatename(name))) {
145 goto out;
146 }
147 if ((error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context))) {
148 goto out;
149 }
150 /* The offset can only be non-zero for resource forks. */
151 if (uio != NULL && uio_offset(uio) != 0 &&
152 bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
153 error = EINVAL;
154 goto out;
155 }
156 }
157
158 /* The offset can only be non-zero for resource forks. */
159 if (uio != NULL && uio_offset(uio) != 0 &&
160 bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
161 error = EINVAL;
162 goto out;
163 }
164
165 error = VNOP_GETXATTR(vp, name, uio, size, options, context);
166 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
167 /*
168 * A filesystem may keep some EAs natively and return ENOTSUP for others.
169 */
170 error = default_getxattr(vp, name, uio, size, options, context);
171 }
172 out:
173 return error;
174 }
175
176 /*
177 * Set the data of an extended attribute.
178 */
179 int
180 vn_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context)
181 {
182 int error;
183
184 if (!XATTR_VNODE_SUPPORTED(vp)) {
185 return EPERM;
186 }
187 #if NAMEDSTREAMS
188 /* setxattr calls are not allowed for streams. */
189 if (vp->v_flag & VISNAMEDSTREAM) {
190 error = EPERM;
191 goto out;
192 }
193 #endif
194 if ((options & (XATTR_REPLACE | XATTR_CREATE)) == (XATTR_REPLACE | XATTR_CREATE)) {
195 return EINVAL;
196 }
197 if ((error = xattr_validatename(name))) {
198 return error;
199 }
200 if (!(options & XATTR_NOSECURITY)) {
201 #if CONFIG_MACF
202 error = mac_vnode_check_setextattr(context, vp, name, uio);
203 if (error) {
204 goto out;
205 }
206 #endif /* MAC */
207 error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context);
208 if (error) {
209 goto out;
210 }
211 }
212 /* The offset can only be non-zero for resource forks. */
213 if (uio_offset(uio) != 0 &&
214 bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
215 error = EINVAL;
216 goto out;
217 }
218
219 error = VNOP_SETXATTR(vp, name, uio, options, context);
220 #ifdef DUAL_EAS
221 /*
222 * An EJUSTRETURN is from a filesystem which keeps this xattr
223 * natively as well as in a dot-underscore file. In this case the
224 * EJUSTRETURN means the filesytem has done nothing, but identifies the
225 * EA as one which may be represented natively and/or in a DU, and
226 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
227 * in vn_setxattr can we do the getxattrs needed to ascertain whether
228 * the XATTR_{CREATE,REPLACE} should yield an error.
229 */
230 if (error == EJUSTRETURN) {
231 int native = 0, dufile = 0;
232 size_t sz; /* not used */
233
234 native = VNOP_GETXATTR(vp, name, NULL, &sz, 0, context) ? 0 : 1;
235 dufile = default_getxattr(vp, name, NULL, &sz, 0, context) ? 0 : 1;
236 if (options & XATTR_CREATE && (native || dufile)) {
237 error = EEXIST;
238 goto out;
239 }
240 if (options & XATTR_REPLACE && !(native || dufile)) {
241 error = ENOATTR;
242 goto out;
243 }
244 /*
245 * Having determined no CREATE/REPLACE error should result, we
246 * zero those bits, so both backing stores get written to.
247 */
248 options &= ~(XATTR_CREATE | XATTR_REPLACE);
249 error = VNOP_SETXATTR(vp, name, uio, options, context);
250 /* the mainline path here is to have error==ENOTSUP ... */
251 }
252 #endif /* DUAL_EAS */
253 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
254 /*
255 * A filesystem may keep some EAs natively and return ENOTSUP for others.
256 */
257 error = default_setxattr(vp, name, uio, options, context);
258 }
259 #if CONFIG_MACF
260 if ((error == 0) && !(options & XATTR_NOSECURITY)) {
261 mac_vnode_notify_setextattr(context, vp, name, uio);
262 if (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL) {
263 mac_vnode_label_update_extattr(vnode_mount(vp), vp, name);
264 }
265 }
266 #endif
267 out:
268 return error;
269 }
270
271 /*
272 * Remove an extended attribute.
273 */
274 int
275 vn_removexattr(vnode_t vp, const char * name, int options, vfs_context_t context)
276 {
277 int error;
278
279 if (!XATTR_VNODE_SUPPORTED(vp)) {
280 return EPERM;
281 }
282 #if NAMEDSTREAMS
283 /* removexattr calls are not allowed for streams. */
284 if (vp->v_flag & VISNAMEDSTREAM) {
285 error = EPERM;
286 goto out;
287 }
288 #endif
289 if ((error = xattr_validatename(name))) {
290 return error;
291 }
292 if (!(options & XATTR_NOSECURITY)) {
293 #if CONFIG_MACF
294 error = mac_vnode_check_deleteextattr(context, vp, name);
295 if (error) {
296 goto out;
297 }
298 #endif /* MAC */
299 error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context);
300 if (error) {
301 goto out;
302 }
303 }
304 error = VNOP_REMOVEXATTR(vp, name, options, context);
305 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
306 /*
307 * A filesystem may keep some EAs natively and return ENOTSUP for others.
308 */
309 error = default_removexattr(vp, name, options, context);
310 #ifdef DUAL_EAS
311 } else if (error == EJUSTRETURN) {
312 /*
313 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
314 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
315 * a native xattr, so failure to find it in a DU file during
316 * default_removexattr should not be considered an error.
317 */
318 error = default_removexattr(vp, name, options, context);
319 if (error == ENOATTR) {
320 error = 0;
321 }
322 #endif /* DUAL_EAS */
323 }
324 #if CONFIG_MACF
325 if ((error == 0) && !(options & XATTR_NOSECURITY)) {
326 mac_vnode_notify_deleteextattr(context, vp, name);
327 if (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL) {
328 mac_vnode_label_update_extattr(vnode_mount(vp), vp, name);
329 }
330 }
331 #endif
332 out:
333 return error;
334 }
335
336 /*
337 * Retrieve the list of extended attribute names.
338 */
339 int
340 vn_listxattr(vnode_t vp, uio_t uio, size_t *size, int options, vfs_context_t context)
341 {
342 int error;
343
344 if (!XATTR_VNODE_SUPPORTED(vp)) {
345 return EPERM;
346 }
347 #if NAMEDSTREAMS
348 /* listxattr calls are not allowed for streams. */
349 if (vp->v_flag & VISNAMEDSTREAM) {
350 return EPERM;
351 }
352 #endif
353
354 if (!(options & XATTR_NOSECURITY)) {
355 #if CONFIG_MACF
356 error = mac_vnode_check_listextattr(context, vp);
357 if (error) {
358 goto out;
359 }
360 #endif /* MAC */
361
362 error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context);
363 if (error) {
364 goto out;
365 }
366 }
367
368 error = VNOP_LISTXATTR(vp, uio, size, options, context);
369 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
370 /*
371 * A filesystem may keep some but not all EAs natively, in which case
372 * the native EA names will have been uiomove-d out (or *size updated)
373 * and the default_listxattr here will finish the job.
374 */
375 error = default_listxattr(vp, uio, size, options, context);
376 }
377 out:
378 return error;
379 }
380
381 int
382 xattr_validatename(const char *name)
383 {
384 int namelen;
385
386 if (name == NULL || name[0] == '\0') {
387 return EINVAL;
388 }
389 namelen = strlen(name);
390 if (name[namelen] != '\0') {
391 return ENAMETOOLONG;
392 }
393
394 if (utf8_validatestr((const unsigned char *)name, namelen) != 0) {
395 return EINVAL;
396 }
397
398 return 0;
399 }
400
401
402 /*
403 * Determine whether an EA is a protected system attribute.
404 */
405 int
406 xattr_protected(const char *attrname)
407 {
408 return !strncmp(attrname, "com.apple.system.", 17);
409 }
410
411
412 static void
413 vnode_setasnamedstream_internal(vnode_t vp, vnode_t svp)
414 {
415 uint32_t streamflags = VISNAMEDSTREAM;
416
417 if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) {
418 streamflags |= VISSHADOW;
419 }
420
421 /* Tag the vnode. */
422 vnode_lock_spin(svp);
423 svp->v_flag |= streamflags;
424 vnode_unlock(svp);
425
426 /* Tag the parent so we know to flush credentials for streams on setattr */
427 vnode_lock_spin(vp);
428 vp->v_lflag |= VL_HASSTREAMS;
429 vnode_unlock(vp);
430
431 /* Make the file it's parent.
432 * Note: This parent link helps us distinguish vnodes for
433 * shadow stream files from vnodes for resource fork on file
434 * systems that support namedstream natively (both have
435 * VISNAMEDSTREAM set) by allowing access to mount structure
436 * for checking MNTK_NAMED_STREAMS bit at many places in the
437 * code.
438 */
439 vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_NAMEDSTREAM_PARENT);
440
441 return;
442 }
443
444 errno_t
445 vnode_setasnamedstream(vnode_t vp, vnode_t svp)
446 {
447 if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) {
448 return EINVAL;
449 }
450
451 vnode_setasnamedstream_internal(vp, svp);
452 return 0;
453 }
454
455 #if NAMEDSTREAMS
456
457 /*
458 * Obtain a named stream from vnode vp.
459 */
460 errno_t
461 vnode_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, int flags, vfs_context_t context)
462 {
463 int error;
464
465 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) {
466 error = VNOP_GETNAMEDSTREAM(vp, svpp, name, op, flags, context);
467 } else {
468 if (flags) {
469 error = ENOTSUP;
470 } else {
471 error = default_getnamedstream(vp, svpp, name, op, context);
472 }
473 }
474
475 if (error == 0) {
476 vnode_setasnamedstream_internal(vp, *svpp);
477 }
478
479 return error;
480 }
481
482 /*
483 * Make a named stream for vnode vp.
484 */
485 errno_t
486 vnode_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, int flags, vfs_context_t context)
487 {
488 int error;
489
490 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) {
491 error = VNOP_MAKENAMEDSTREAM(vp, svpp, name, flags, context);
492 } else {
493 error = default_makenamedstream(vp, svpp, name, context);
494 }
495
496 if (error == 0) {
497 vnode_setasnamedstream_internal(vp, *svpp);
498 }
499
500 return error;
501 }
502
503 /*
504 * Remove a named stream from vnode vp.
505 */
506 errno_t
507 vnode_removenamedstream(vnode_t vp, vnode_t svp, const char *name, int flags, vfs_context_t context)
508 {
509 int error;
510
511 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) {
512 error = VNOP_REMOVENAMEDSTREAM(vp, svp, name, flags, context);
513 } else {
514 error = default_removenamedstream(vp, name, context);
515 }
516
517 return error;
518 }
519
520 #define NS_IOBUFSIZE (128 * 1024)
521
522 /*
523 * Release a named stream shadow file.
524 *
525 * Note: This function is called from two places where we do not need
526 * to check if the vnode has any references held before deleting the
527 * shadow file. Once from vclean() when the vnode is being reclaimed
528 * and we do not hold any references on the vnode. Second time from
529 * default_getnamedstream() when we get an error during shadow stream
530 * file initialization so that other processes who are waiting for the
531 * shadow stream file initialization by the creator will get opportunity
532 * to create and initialize the file again.
533 */
534 errno_t
535 vnode_relenamedstream(vnode_t vp, vnode_t svp)
536 {
537 vnode_t dvp;
538 struct componentname cn;
539 char tmpname[80];
540 errno_t err;
541
542 /*
543 * We need to use the kernel context here. If we used the supplied
544 * VFS context we have no clue whether or not it originated from userland
545 * where it could be subject to a chroot jail. We need to ensure that all
546 * filesystem access to shadow files is done on the same FS regardless of
547 * userland process restrictions.
548 */
549 vfs_context_t kernelctx = vfs_context_kernel();
550
551 cache_purge(svp);
552
553 vnode_lock(svp);
554 MAKE_SHADOW_NAME(vp, tmpname);
555 vnode_unlock(svp);
556
557 cn.cn_nameiop = DELETE;
558 cn.cn_flags = ISLASTCN;
559 cn.cn_context = kernelctx;
560 cn.cn_pnbuf = tmpname;
561 cn.cn_pnlen = sizeof(tmpname);
562 cn.cn_nameptr = cn.cn_pnbuf;
563 cn.cn_namelen = strlen(tmpname);
564
565 /*
566 * Obtain the vnode for the shadow files directory. Make sure to
567 * use the kernel ctx as described above.
568 */
569 err = get_shadow_dir(&dvp);
570 if (err != 0) {
571 return err;
572 }
573
574 (void) VNOP_REMOVE(dvp, svp, &cn, 0, kernelctx);
575 vnode_put(dvp);
576
577 return 0;
578 }
579
580 /*
581 * Flush a named stream shadow file.
582 *
583 * 'vp' represents the AppleDouble file.
584 * 'svp' represents the shadow file.
585 */
586 errno_t
587 vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context)
588 {
589 struct vnode_attr va;
590 uio_t auio = NULL;
591 caddr_t bufptr = NULL;
592 size_t bufsize = 0;
593 size_t offset;
594 size_t iosize;
595 size_t datasize;
596 int error;
597 /*
598 * The kernel context must be used for all I/O to the shadow file
599 * and its namespace operations
600 */
601 vfs_context_t kernelctx = vfs_context_kernel();
602
603 /* The supplied context is used for access to the AD file itself */
604
605 VATTR_INIT(&va);
606 VATTR_WANTED(&va, va_data_size);
607 if (VNOP_GETATTR(svp, &va, context) != 0 ||
608 !VATTR_IS_SUPPORTED(&va, va_data_size)) {
609 return 0;
610 }
611 datasize = va.va_data_size;
612 if (datasize == 0) {
613 (void) default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
614 return 0;
615 }
616
617 iosize = bufsize = MIN(datasize, NS_IOBUFSIZE);
618 if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize, VM_KERN_MEMORY_FILE)) {
619 return ENOMEM;
620 }
621 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
622 offset = 0;
623
624 /*
625 * Copy the shadow stream file data into the resource fork.
626 */
627 error = VNOP_OPEN(svp, 0, kernelctx);
628 if (error) {
629 printf("vnode_flushnamedstream: err %d opening file\n", error);
630 goto out;
631 }
632 while (offset < datasize) {
633 iosize = MIN(datasize - offset, iosize);
634
635 uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ);
636 uio_addiov(auio, (uintptr_t)bufptr, iosize);
637 error = VNOP_READ(svp, auio, 0, kernelctx);
638 if (error) {
639 break;
640 }
641 /* Since there's no truncate xattr we must remove the resource fork. */
642 if (offset == 0) {
643 error = default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
644 if ((error != 0) && (error != ENOATTR)) {
645 break;
646 }
647 }
648 uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE);
649 uio_addiov(auio, (uintptr_t)bufptr, iosize);
650 error = vn_setxattr(vp, XATTR_RESOURCEFORK_NAME, auio, XATTR_NOSECURITY, context);
651 if (error) {
652 break;
653 }
654 offset += iosize;
655 }
656
657 /* close shadowfile */
658 (void) VNOP_CLOSE(svp, 0, kernelctx);
659 out:
660 if (bufptr) {
661 kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize);
662 }
663 if (auio) {
664 uio_free(auio);
665 }
666 return error;
667 }
668
669
670 /*
671 * Verify that the vnode 'vp' is a vnode that lives in the shadow
672 * directory. We can't just query the parent pointer directly since
673 * the shadowfile is hooked up to the actual file it's a stream for.
674 */
675 errno_t
676 vnode_verifynamedstream(vnode_t vp)
677 {
678 int error;
679 struct vnode *shadow_dvp = NULL;
680 struct vnode *shadowfile = NULL;
681 struct componentname cn;
682
683 /*
684 * We need to use the kernel context here. If we used the supplied
685 * VFS context we have no clue whether or not it originated from userland
686 * where it could be subject to a chroot jail. We need to ensure that all
687 * filesystem access to shadow files is done on the same FS regardless of
688 * userland process restrictions.
689 */
690 vfs_context_t kernelctx = vfs_context_kernel();
691 char tmpname[80];
692
693
694 /* Get the shadow directory vnode */
695 error = get_shadow_dir(&shadow_dvp);
696 if (error) {
697 return error;
698 }
699
700 /* Re-generate the shadow name in the buffer */
701 MAKE_SHADOW_NAME(vp, tmpname);
702
703 /* Look up item in shadow dir */
704 bzero(&cn, sizeof(cn));
705 cn.cn_nameiop = LOOKUP;
706 cn.cn_flags = ISLASTCN | CN_ALLOWRSRCFORK;
707 cn.cn_context = kernelctx;
708 cn.cn_pnbuf = tmpname;
709 cn.cn_pnlen = sizeof(tmpname);
710 cn.cn_nameptr = cn.cn_pnbuf;
711 cn.cn_namelen = strlen(tmpname);
712
713 if (VNOP_LOOKUP(shadow_dvp, &shadowfile, &cn, kernelctx) == 0) {
714 /* is the pointer the same? */
715 if (shadowfile == vp) {
716 error = 0;
717 } else {
718 error = EPERM;
719 }
720 /* drop the iocount acquired */
721 vnode_put(shadowfile);
722 }
723
724 /* Drop iocount on shadow dir */
725 vnode_put(shadow_dvp);
726 return error;
727 }
728
729 /*
730 * Access or create the shadow file as needed.
731 *
732 * 'makestream' with non-zero value means that we need to guarantee we were the
733 * creator of the shadow file.
734 *
735 * 'context' is the user supplied context for the original VFS operation that
736 * caused us to need a shadow file.
737 *
738 * int pointed to by 'creator' is nonzero if we created the shadowfile.
739 */
740 static int
741 getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize,
742 int *creator, vfs_context_t context)
743 {
744 vnode_t dvp = NULLVP;
745 vnode_t svp = NULLVP;
746 struct componentname cn;
747 struct vnode_attr va;
748 char tmpname[80];
749 size_t datasize = 0;
750 int error = 0;
751 int retries = 0;
752 vfs_context_t kernelctx = vfs_context_kernel();
753
754 retry_create:
755 *creator = 0;
756 /* Establish a unique file name. */
757 MAKE_SHADOW_NAME(vp, tmpname);
758 bzero(&cn, sizeof(cn));
759 cn.cn_nameiop = LOOKUP;
760 cn.cn_flags = ISLASTCN;
761 cn.cn_context = context;
762 cn.cn_pnbuf = tmpname;
763 cn.cn_pnlen = sizeof(tmpname);
764 cn.cn_nameptr = cn.cn_pnbuf;
765 cn.cn_namelen = strlen(tmpname);
766
767 /* Pick up uid, gid, mode and date from original file. */
768 VATTR_INIT(&va);
769 VATTR_WANTED(&va, va_uid);
770 VATTR_WANTED(&va, va_gid);
771 VATTR_WANTED(&va, va_mode);
772 VATTR_WANTED(&va, va_create_time);
773 VATTR_WANTED(&va, va_modify_time);
774 if (VNOP_GETATTR(vp, &va, context) != 0 ||
775 !VATTR_IS_SUPPORTED(&va, va_uid) ||
776 !VATTR_IS_SUPPORTED(&va, va_gid) ||
777 !VATTR_IS_SUPPORTED(&va, va_mode)) {
778 va.va_uid = KAUTH_UID_NONE;
779 va.va_gid = KAUTH_GID_NONE;
780 va.va_mode = S_IRUSR | S_IWUSR;
781 }
782 va.va_vaflags = VA_EXCLUSIVE;
783 VATTR_SET(&va, va_type, VREG);
784 /* We no longer change the access, but we still hide it. */
785 VATTR_SET(&va, va_flags, UF_HIDDEN);
786
787 /* Obtain the vnode for the shadow files directory. */
788 if (get_shadow_dir(&dvp) != 0) {
789 error = ENOTDIR;
790 goto out;
791 }
792 if (!makestream) {
793 /* See if someone else already has it open. */
794 if (VNOP_LOOKUP(dvp, &svp, &cn, kernelctx) == 0) {
795 /* Double check existence by asking for size. */
796 VATTR_INIT(&va);
797 VATTR_WANTED(&va, va_data_size);
798 if (VNOP_GETATTR(svp, &va, context) == 0 &&
799 VATTR_IS_SUPPORTED(&va, va_data_size)) {
800 goto out; /* OK to use. */
801 }
802 }
803
804 /*
805 * Otherwise make sure the resource fork data exists.
806 * Use the supplied context for accessing the AD file.
807 */
808 error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &datasize,
809 XATTR_NOSECURITY, context);
810 /*
811 * To maintain binary compatibility with legacy Carbon
812 * emulated resource fork support, if the resource fork
813 * doesn't exist but the Finder Info does, then act as
814 * if an empty resource fork is present (see 4724359).
815 */
816 if ((error == ENOATTR) &&
817 (vn_getxattr(vp, XATTR_FINDERINFO_NAME, NULL, &datasize,
818 XATTR_NOSECURITY, context) == 0)) {
819 datasize = 0;
820 error = 0;
821 } else {
822 if (error) {
823 goto out;
824 }
825
826 /* If the resource fork exists, its size is expected to be non-zero. */
827 if (datasize == 0) {
828 error = ENOATTR;
829 goto out;
830 }
831 }
832 }
833 /* Create the shadow stream file. */
834 error = VNOP_CREATE(dvp, &svp, &cn, &va, kernelctx);
835 if (error == 0) {
836 vnode_recycle(svp);
837 *creator = 1;
838 } else if ((error == EEXIST) && !makestream) {
839 error = VNOP_LOOKUP(dvp, &svp, &cn, kernelctx);
840 } else if ((error == ENOENT) && !makestream) {
841 /*
842 * We could have raced with a rmdir on the shadow directory
843 * post-lookup. Retry from the beginning, 1x only, to
844 * try and see if we need to re-create the shadow directory
845 * in get_shadow_dir.
846 */
847 if (retries == 0) {
848 retries++;
849 if (dvp) {
850 vnode_put(dvp);
851 dvp = NULLVP;
852 }
853 if (svp) {
854 vnode_put(svp);
855 svp = NULLVP;
856 }
857 goto retry_create;
858 }
859 /* Otherwise, just error out normally below */
860 }
861
862 out:
863 if (dvp) {
864 vnode_put(dvp);
865 }
866 if (error) {
867 /* On errors, clean up shadow stream file. */
868 if (svp) {
869 vnode_put(svp);
870 svp = NULLVP;
871 }
872 }
873 *svpp = svp;
874 if (rsrcsize) {
875 *rsrcsize = datasize;
876 }
877 return error;
878 }
879
880
881 static int
882 default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context)
883 {
884 vnode_t svp = NULLVP;
885 uio_t auio = NULL;
886 caddr_t bufptr = NULL;
887 size_t bufsize = 0;
888 size_t datasize = 0;
889 int creator;
890 int error;
891
892 /* need the kernel context for accessing the shadowfile */
893 vfs_context_t kernelctx = vfs_context_kernel();
894
895 /*
896 * Only the "com.apple.ResourceFork" stream is supported here.
897 */
898 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
899 *svpp = NULLVP;
900 return ENOATTR;
901 }
902 retry:
903 /*
904 * Obtain a shadow file for the resource fork I/O.
905 *
906 * Need to pass along the supplied context so that getshadowfile
907 * can access the AD file as needed, using it.
908 */
909 error = getshadowfile(vp, &svp, 0, &datasize, &creator, context);
910 if (error) {
911 *svpp = NULLVP;
912 return error;
913 }
914
915 /*
916 * The creator of the shadow file provides its file data,
917 * all other threads should wait until its ready. In order to
918 * prevent a deadlock during error codepaths, we need to check if the
919 * vnode is being created, or if it has failed out. Regardless of success or
920 * failure, we set the VISSHADOW bit on the vnode, so we check that
921 * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
922 * then we can infer the creator isn't done yet. If it's there, but
923 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
924 * try again.
925 */
926 if (!creator) {
927 vnode_lock(svp);
928 if (svp->v_flag & VISNAMEDSTREAM) {
929 /* data is ready, go use it */
930 vnode_unlock(svp);
931 goto out;
932 } else {
933 /* It's not ready, wait for it (sleep using v_parent as channel) */
934 if ((svp->v_flag & VISSHADOW)) {
935 /*
936 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
937 * thread is done with this vnode. Just unlock the vnode and try again
938 */
939 vnode_unlock(svp);
940 } else {
941 /* Otherwise, sleep if the shadow file is not created yet */
942 msleep((caddr_t)&svp->v_parent, &svp->v_lock, PINOD | PDROP,
943 "getnamedstream", NULL);
944 }
945 vnode_put(svp);
946 svp = NULLVP;
947 goto retry;
948 }
949 }
950
951 /*
952 * Copy the real resource fork data into shadow stream file.
953 */
954 if (op == NS_OPEN && datasize != 0) {
955 size_t offset;
956 size_t iosize;
957
958 iosize = bufsize = MIN(datasize, NS_IOBUFSIZE);
959 if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize, VM_KERN_MEMORY_FILE)) {
960 error = ENOMEM;
961 goto out;
962 }
963
964 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
965 offset = 0;
966
967 /* open the shadow file */
968 error = VNOP_OPEN(svp, 0, kernelctx);
969 if (error) {
970 goto out;
971 }
972 while (offset < datasize) {
973 size_t tmpsize;
974
975 iosize = MIN(datasize - offset, iosize);
976
977 uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ);
978 uio_addiov(auio, (uintptr_t)bufptr, iosize);
979 /* use supplied ctx for AD file */
980 error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, auio, &tmpsize,
981 XATTR_NOSECURITY, context);
982 if (error) {
983 break;
984 }
985
986 uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE);
987 uio_addiov(auio, (uintptr_t)bufptr, iosize);
988 /* kernel context for writing shadowfile */
989 error = VNOP_WRITE(svp, auio, 0, kernelctx);
990 if (error) {
991 break;
992 }
993 offset += iosize;
994 }
995
996 /* close shadow file */
997 (void) VNOP_CLOSE(svp, 0, kernelctx);
998 }
999 out:
1000 /* Wake up anyone waiting for svp file content */
1001 if (creator) {
1002 if (error == 0) {
1003 vnode_lock(svp);
1004 /* VISSHADOW would be set later on anyway, so we set it now */
1005 svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW);
1006 wakeup((caddr_t)&svp->v_parent);
1007 vnode_unlock(svp);
1008 } else {
1009 /* On post create errors, get rid of the shadow file. This
1010 * way if there is another process waiting for initialization
1011 * of the shadowfile by the current process will wake up and
1012 * retry by creating and initializing the shadow file again.
1013 * Also add the VISSHADOW bit here to indicate we're done operating
1014 * on this vnode.
1015 */
1016 (void)vnode_relenamedstream(vp, svp);
1017 vnode_lock(svp);
1018 svp->v_flag |= VISSHADOW;
1019 wakeup((caddr_t)&svp->v_parent);
1020 vnode_unlock(svp);
1021 }
1022 }
1023
1024 if (bufptr) {
1025 kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize);
1026 }
1027 if (auio) {
1028 uio_free(auio);
1029 }
1030 if (error) {
1031 /* On errors, clean up shadow stream file. */
1032 if (svp) {
1033 vnode_put(svp);
1034 svp = NULLVP;
1035 }
1036 }
1037 *svpp = svp;
1038 return error;
1039 }
1040
1041 static int
1042 default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context)
1043 {
1044 int creator;
1045 int error;
1046
1047 /*
1048 * Only the "com.apple.ResourceFork" stream is supported here.
1049 */
1050 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
1051 *svpp = NULLVP;
1052 return ENOATTR;
1053 }
1054
1055 /* Supply the context to getshadowfile so it can manipulate the AD file */
1056 error = getshadowfile(vp, svpp, 1, NULL, &creator, context);
1057
1058 /*
1059 * Wake up any waiters over in default_getnamedstream().
1060 */
1061 if ((error == 0) && (*svpp != NULL) && creator) {
1062 vnode_t svp = *svpp;
1063
1064 vnode_lock(svp);
1065 /* If we're the creator, mark it as a named stream */
1066 svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW);
1067 /* Wakeup any waiters on the v_parent channel */
1068 wakeup((caddr_t)&svp->v_parent);
1069 vnode_unlock(svp);
1070 }
1071
1072 return error;
1073 }
1074
1075 static int
1076 default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context)
1077 {
1078 /*
1079 * Only the "com.apple.ResourceFork" stream is supported here.
1080 */
1081 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
1082 return ENOATTR;
1083 }
1084 /*
1085 * XXX - what about other opened instances?
1086 */
1087 return default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
1088 }
1089
1090 static int
1091 get_shadow_dir(vnode_t *sdvpp)
1092 {
1093 vnode_t dvp = NULLVP;
1094 vnode_t sdvp = NULLVP;
1095 struct componentname cn;
1096 struct vnode_attr va;
1097 char tmpname[80];
1098 uint32_t tmp_fsid;
1099 int error;
1100 vfs_context_t kernelctx = vfs_context_kernel();
1101
1102 bzero(tmpname, sizeof(tmpname));
1103 MAKE_SHADOW_DIRNAME(rootvnode, tmpname);
1104 /*
1105 * Look up the shadow directory to ensure that it still exists.
1106 * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues
1107 * in caching it when multiple threads may be trying to manipulate the pointers.
1108 *
1109 * Make sure to use the kernel context. We want a singular view of
1110 * the shadow dir regardless of chrooted processes.
1111 */
1112 error = vnode_lookup(tmpname, 0, &sdvp, kernelctx);
1113 if (error == 0) {
1114 /*
1115 * If we get here, then we have successfully looked up the shadow dir,
1116 * and it has an iocount from the lookup. Return the vp in the output argument.
1117 */
1118 *sdvpp = sdvp;
1119 return 0;
1120 }
1121 /* In the failure case, no iocount is acquired */
1122 sdvp = NULLVP;
1123 bzero(tmpname, sizeof(tmpname));
1124
1125 /*
1126 * Obtain the vnode for "/var/run" directory using the kernel
1127 * context.
1128 *
1129 * This is defined in the SHADOW_DIR_CONTAINER macro
1130 */
1131 if (vnode_lookup(SHADOW_DIR_CONTAINER, 0, &dvp, kernelctx) != 0) {
1132 error = ENOTSUP;
1133 goto out;
1134 }
1135
1136 /*
1137 * Create the shadow stream directory.
1138 * 'dvp' below suggests the parent directory so
1139 * we only need to provide the leaf entry name
1140 */
1141 MAKE_SHADOW_DIR_LEAF(rootvnode, tmpname);
1142 bzero(&cn, sizeof(cn));
1143 cn.cn_nameiop = LOOKUP;
1144 cn.cn_flags = ISLASTCN;
1145 cn.cn_context = kernelctx;
1146 cn.cn_pnbuf = tmpname;
1147 cn.cn_pnlen = sizeof(tmpname);
1148 cn.cn_nameptr = cn.cn_pnbuf;
1149 cn.cn_namelen = strlen(tmpname);
1150
1151 /*
1152 * owned by root, only readable by root, hidden
1153 */
1154 VATTR_INIT(&va);
1155 VATTR_SET(&va, va_uid, 0);
1156 VATTR_SET(&va, va_gid, 0);
1157 VATTR_SET(&va, va_mode, S_IRUSR | S_IXUSR);
1158 VATTR_SET(&va, va_type, VDIR);
1159 VATTR_SET(&va, va_flags, UF_HIDDEN);
1160 va.va_vaflags = VA_EXCLUSIVE;
1161
1162 error = VNOP_MKDIR(dvp, &sdvp, &cn, &va, kernelctx);
1163
1164 /*
1165 * There can be only one winner for an exclusive create.
1166 */
1167 if (error == EEXIST) {
1168 /* loser has to look up directory */
1169 error = VNOP_LOOKUP(dvp, &sdvp, &cn, kernelctx);
1170 if (error == 0) {
1171 /* Make sure its in fact a directory */
1172 if (sdvp->v_type != VDIR) {
1173 goto baddir;
1174 }
1175 /* Obtain the fsid for /var/run directory */
1176 VATTR_INIT(&va);
1177 VATTR_WANTED(&va, va_fsid);
1178 if (VNOP_GETATTR(dvp, &va, kernelctx) != 0 ||
1179 !VATTR_IS_SUPPORTED(&va, va_fsid)) {
1180 goto baddir;
1181 }
1182 tmp_fsid = va.va_fsid;
1183
1184 VATTR_INIT(&va);
1185 VATTR_WANTED(&va, va_uid);
1186 VATTR_WANTED(&va, va_gid);
1187 VATTR_WANTED(&va, va_mode);
1188 VATTR_WANTED(&va, va_fsid);
1189 VATTR_WANTED(&va, va_dirlinkcount);
1190 VATTR_WANTED(&va, va_acl);
1191 /* Provide defaults for attrs that may not be supported */
1192 va.va_dirlinkcount = 1;
1193 va.va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
1194
1195 if (VNOP_GETATTR(sdvp, &va, kernelctx) != 0 ||
1196 !VATTR_IS_SUPPORTED(&va, va_uid) ||
1197 !VATTR_IS_SUPPORTED(&va, va_gid) ||
1198 !VATTR_IS_SUPPORTED(&va, va_mode) ||
1199 !VATTR_IS_SUPPORTED(&va, va_fsid)) {
1200 goto baddir;
1201 }
1202 /*
1203 * Make sure its what we want:
1204 * - owned by root
1205 * - not writable by anyone
1206 * - on same file system as /var/run
1207 * - not a hard-linked directory
1208 * - no ACLs (they might grant write access)
1209 */
1210 if ((va.va_uid != 0) || (va.va_gid != 0) ||
1211 (va.va_mode & (S_IWUSR | S_IRWXG | S_IRWXO)) ||
1212 (va.va_fsid != tmp_fsid) ||
1213 (va.va_dirlinkcount != 1) ||
1214 (va.va_acl != (kauth_acl_t) KAUTH_FILESEC_NONE)) {
1215 goto baddir;
1216 }
1217 }
1218 }
1219 out:
1220 if (dvp) {
1221 vnode_put(dvp);
1222 }
1223 if (error) {
1224 /* On errors, clean up shadow stream directory. */
1225 if (sdvp) {
1226 vnode_put(sdvp);
1227 sdvp = NULLVP;
1228 }
1229 }
1230 *sdvpp = sdvp;
1231 return error;
1232
1233 baddir:
1234 /* This is not the dir we're looking for, move along */
1235 ++shadow_sequence; /* try something else next time */
1236 error = ENOTDIR;
1237 goto out;
1238 }
1239 #endif /* NAMEDSTREAMS */
1240
1241
1242 #if CONFIG_APPLEDOUBLE
1243 /*
1244 * Default Implementation (Non-native EA)
1245 */
1246
1247
1248 /*
1249 * Typical "._" AppleDouble Header File layout:
1250 * ------------------------------------------------------------
1251 * MAGIC 0x00051607
1252 * VERSION 0x00020000
1253 * FILLER 0
1254 * COUNT 2
1255 * .-- AD ENTRY[0] Finder Info Entry (must be first)
1256 * .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1257 * | '-> FINDER INFO
1258 * | ///////////// Fixed Size Data (32 bytes)
1259 * | EXT ATTR HDR
1260 * | /////////////
1261 * | ATTR ENTRY[0] --.
1262 * | ATTR ENTRY[1] --+--.
1263 * | ATTR ENTRY[2] --+--+--.
1264 * | ... | | |
1265 * | ATTR ENTRY[N] --+--+--+--.
1266 * | ATTR DATA 0 <-' | | |
1267 * | //////////// | | |
1268 * | ATTR DATA 1 <----' | |
1269 * | ///////////// | |
1270 * | ATTR DATA 2 <-------' |
1271 * | ///////////// |
1272 * | ... |
1273 * | ATTR DATA N <----------'
1274 * | /////////////
1275 * | Attribute Free Space
1276 * |
1277 * '----> RESOURCE FORK
1278 * ///////////// Variable Sized Data
1279 * /////////////
1280 * /////////////
1281 * /////////////
1282 * /////////////
1283 * /////////////
1284 * ...
1285 * /////////////
1286 *
1287 * ------------------------------------------------------------
1288 *
1289 * NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1290 * stored as part of the Finder Info. The length in the Finder
1291 * Info AppleDouble entry includes the length of the extended
1292 * attribute header, attribute entries, and attribute data.
1293 */
1294
1295 /*
1296 * On Disk Data Structures
1297 *
1298 * Note: Motorola 68K alignment and big-endian.
1299 *
1300 * See RFC 1740 for additional information about the AppleDouble file format.
1301 *
1302 */
1303
1304 #define ADH_MAGIC 0x00051607
1305 #define ADH_VERSION 0x00020000
1306 #define ADH_MACOSX "Mac OS X "
1307
1308 /*
1309 * AppleDouble Entry ID's
1310 */
1311 #define AD_DATA 1 /* Data fork */
1312 #define AD_RESOURCE 2 /* Resource fork */
1313 #define AD_REALNAME 3 /* File's name on home file system */
1314 #define AD_COMMENT 4 /* Standard Mac comment */
1315 #define AD_ICONBW 5 /* Mac black & white icon */
1316 #define AD_ICONCOLOR 6 /* Mac color icon */
1317 #define AD_UNUSED 7 /* Not used */
1318 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1319 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1320 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1321 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1322 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1323 #define AD_AFPNAME 13 /* Short name on AFP server */
1324 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1325 #define AD_AFPDIRID 15 /* AFP directory ID */
1326 #define AD_ATTRIBUTES AD_FINDERINFO
1327
1328
1329 #define ATTR_FILE_PREFIX "._"
1330 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1331
1332 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1333
1334 /* Implementation Limits */
1335 #define ATTR_MAX_SIZE AD_XATTR_MAXSIZE
1336 #define ATTR_MAX_HDR_SIZE 65536
1337 /*
1338 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1339 * size supported (including the attribute entries). All of
1340 * the attribute entries must reside within this limit. If
1341 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1342 * boundry, then all of the attribute data I/O is performed
1343 * separately from the attribute header I/O.
1344 *
1345 * In particular, all of the attr_entry structures must lie
1346 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1347 * AppleDouble file. However, the attribute data (i.e. the
1348 * contents of the extended attributes) may extend beyond the
1349 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1350 * limit is to allow the implementation to optimize by reading
1351 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1352 */
1353
1354
1355 #define FINDERINFOSIZE 32
1356
1357 typedef struct apple_double_entry {
1358 u_int32_t type; /* entry type: see list, 0 invalid */
1359 u_int32_t offset; /* entry data offset from the beginning of the file. */
1360 u_int32_t length; /* entry data length in bytes. */
1361 } __attribute__((aligned(2), packed)) apple_double_entry_t;
1362
1363
1364 typedef struct apple_double_header {
1365 u_int32_t magic; /* == ADH_MAGIC */
1366 u_int32_t version; /* format version: 2 = 0x00020000 */
1367 u_int32_t filler[4];
1368 u_int16_t numEntries; /* number of entries which follow */
1369 apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */
1370 u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */
1371 u_int8_t pad[2]; /* get better alignment inside attr_header */
1372 } __attribute__((aligned(2), packed)) apple_double_header_t;
1373
1374 #define ADHDRSIZE (4+4+16+2)
1375
1376 /* Entries are aligned on 4 byte boundaries */
1377 typedef struct attr_entry {
1378 u_int32_t offset; /* file offset to data */
1379 u_int32_t length; /* size of attribute data */
1380 u_int16_t flags;
1381 u_int8_t namelen;
1382 u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1383 } __attribute__((aligned(2), packed)) attr_entry_t;
1384
1385
1386 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1387 typedef struct attr_header {
1388 apple_double_header_t appledouble;
1389 u_int32_t magic; /* == ATTR_HDR_MAGIC */
1390 u_int32_t debug_tag; /* for debugging == file id of owning file */
1391 u_int32_t total_size; /* file offset of end of attribute header + entries + data */
1392 u_int32_t data_start; /* file offset to attribute data area */
1393 u_int32_t data_length; /* length of attribute data area */
1394 u_int32_t reserved[3];
1395 u_int16_t flags;
1396 u_int16_t num_attrs;
1397 } __attribute__((aligned(2), packed)) attr_header_t;
1398
1399
1400 /* Empty Resource Fork Header */
1401 typedef struct rsrcfork_header {
1402 u_int32_t fh_DataOffset;
1403 u_int32_t fh_MapOffset;
1404 u_int32_t fh_DataLength;
1405 u_int32_t fh_MapLength;
1406 u_int8_t systemData[112];
1407 u_int8_t appData[128];
1408 u_int32_t mh_DataOffset;
1409 u_int32_t mh_MapOffset;
1410 u_int32_t mh_DataLength;
1411 u_int32_t mh_MapLength;
1412 u_int32_t mh_Next;
1413 u_int16_t mh_RefNum;
1414 u_int8_t mh_Attr;
1415 u_int8_t mh_InMemoryAttr;
1416 u_int16_t mh_Types;
1417 u_int16_t mh_Names;
1418 u_int16_t typeCount;
1419 } __attribute__((aligned(2), packed)) rsrcfork_header_t;
1420
1421 #define RF_FIRST_RESOURCE 256
1422 #define RF_NULL_MAP_LENGTH 30
1423 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1424
1425 /* Runtime information about the attribute file. */
1426 typedef struct attr_info {
1427 vfs_context_t context;
1428 vnode_t filevp;
1429 size_t filesize;
1430 size_t iosize;
1431 u_int8_t *rawdata;
1432 size_t rawsize; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1433 apple_double_header_t *filehdr;
1434 apple_double_entry_t *finderinfo;
1435 apple_double_entry_t *rsrcfork;
1436 attr_header_t *attrhdr;
1437 attr_entry_t *attr_entry;
1438 u_int8_t readonly;
1439 u_int8_t emptyfinderinfo;
1440 } attr_info_t;
1441
1442
1443 #define ATTR_SETTING 1
1444
1445 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1446
1447 #define ATTR_ENTRY_LENGTH(namelen) \
1448 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1449
1450 #define ATTR_NEXT(ae) \
1451 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1452
1453 #define ATTR_VALID(ae, ai) \
1454 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1455
1456 #define SWAP16(x) OSSwapBigToHostInt16((x))
1457 #define SWAP32(x) OSSwapBigToHostInt32((x))
1458 #define SWAP64(x) OSSwapBigToHostInt64((x))
1459
1460
1461 static u_int32_t emptyfinfo[8] = {0};
1462
1463
1464 /*
1465 * Local support routines
1466 */
1467 static void close_xattrfile(vnode_t xvp, int fileflags, vfs_context_t context);
1468
1469 static int open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context);
1470
1471 static int create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context);
1472
1473 static int remove_xattrfile(vnode_t xvp, vfs_context_t context);
1474
1475 static int get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context);
1476
1477 static void rel_xattrinfo(attr_info_t *ainfop);
1478
1479 static int write_xattrinfo(attr_info_t *ainfop);
1480
1481 static void init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr);
1482
1483 static int lock_xattrfile(vnode_t xvp, short locktype, vfs_context_t context);
1484
1485 static int unlock_xattrfile(vnode_t xvp, vfs_context_t context);
1486
1487
1488 #if BYTE_ORDER == LITTLE_ENDIAN
1489 static void swap_adhdr(apple_double_header_t *adh);
1490 static void swap_attrhdr(attr_header_t *ah, attr_info_t* info);
1491
1492 #else
1493 #define swap_adhdr(x)
1494 #define swap_attrhdr(x, y)
1495 #endif
1496
1497 static int check_and_swap_attrhdr(attr_header_t *ah, attr_info_t* ainfop);
1498 static int shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context);
1499 static int shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context);
1500
1501
1502 /*
1503 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1504 * is in big endian (as it would exist on disk). Verifies the following:
1505 * - magic field
1506 * - version field
1507 * - number of entries
1508 * - that each entry fits within the file size
1509 *
1510 * If the header is invalid, ENOATTR is returned.
1511 *
1512 * NOTE: Does not attempt to validate the extended attributes header that
1513 * may be embedded in the Finder Info entry.
1514 */
1515 static int
1516 check_and_swap_apple_double_header(attr_info_t *ainfop)
1517 {
1518 int i, j;
1519 u_int32_t header_end;
1520 u_int32_t entry_end;
1521 size_t rawsize;
1522 apple_double_header_t *header;
1523
1524 rawsize = ainfop->rawsize;
1525 header = (apple_double_header_t *) ainfop->rawdata;
1526
1527 /* Is the file big enough to contain an AppleDouble header? */
1528 if (rawsize < offsetof(apple_double_header_t, entries)) {
1529 return ENOATTR;
1530 }
1531
1532 /* Swap the AppleDouble header fields to native order */
1533 header->magic = SWAP32(header->magic);
1534 header->version = SWAP32(header->version);
1535 header->numEntries = SWAP16(header->numEntries);
1536
1537 /* Sanity check the AppleDouble header fields */
1538 if (header->magic != ADH_MAGIC ||
1539 header->version != ADH_VERSION ||
1540 header->numEntries < 1 ||
1541 header->numEntries > 15) {
1542 return ENOATTR;
1543 }
1544
1545 /* Calculate where the entries[] array ends */
1546 header_end = offsetof(apple_double_header_t, entries) +
1547 header->numEntries * sizeof(apple_double_entry_t);
1548
1549 /* Is the file big enough to contain the AppleDouble entries? */
1550 if (rawsize < header_end) {
1551 return ENOATTR;
1552 }
1553
1554 /* Swap and sanity check each AppleDouble entry */
1555 for (i = 0; i < header->numEntries; i++) {
1556 /* Swap the per-entry fields to native order */
1557 header->entries[i].type = SWAP32(header->entries[i].type);
1558 header->entries[i].offset = SWAP32(header->entries[i].offset);
1559 header->entries[i].length = SWAP32(header->entries[i].length);
1560
1561 entry_end = header->entries[i].offset + header->entries[i].length;
1562
1563 /*
1564 * Does the entry's content start within the header itself,
1565 * did the addition overflow, or does the entry's content
1566 * extend past the end of the file?
1567 */
1568 if (header->entries[i].offset < header_end ||
1569 entry_end < header->entries[i].offset ||
1570 entry_end > ainfop->filesize) {
1571 return ENOATTR;
1572 }
1573
1574 /*
1575 * Does the current entry's content overlap with a previous
1576 * entry's content?
1577 *
1578 * Yes, this is O(N**2), and there are more efficient algorithms
1579 * for testing pairwise overlap of N ranges when N is large.
1580 * But we have already ensured N < 16, and N is almost always 2.
1581 * So there's no point in using a more complex algorithm.
1582 */
1583
1584 for (j = 0; j < i; j++) {
1585 if (entry_end > header->entries[j].offset &&
1586 header->entries[j].offset + header->entries[j].length > header->entries[i].offset) {
1587 return ENOATTR;
1588 }
1589 }
1590 }
1591
1592 return 0;
1593 }
1594
1595
1596
1597 /*
1598 * Retrieve the data of an extended attribute.
1599 */
1600 static int
1601 default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size,
1602 __unused int options, vfs_context_t context)
1603 {
1604 vnode_t xvp = NULL;
1605 attr_info_t ainfo;
1606 attr_header_t *header;
1607 attr_entry_t *entry;
1608 u_int8_t *attrdata;
1609 size_t datalen;
1610 int namelen;
1611 int isrsrcfork;
1612 int fileflags;
1613 int i;
1614 int error;
1615
1616 fileflags = FREAD;
1617 if (strcmp(name, XATTR_RESOURCEFORK_NAME) == 0) {
1618 isrsrcfork = 1;
1619 /*
1620 * Open the file locked (shared) since the Carbon
1621 * File Manager may have the Apple Double file open
1622 * and could be changing the resource fork.
1623 */
1624 fileflags |= O_SHLOCK;
1625 } else {
1626 isrsrcfork = 0;
1627 }
1628
1629 if ((error = open_xattrfile(vp, fileflags, &xvp, context))) {
1630 return error;
1631 }
1632 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
1633 close_xattrfile(xvp, fileflags, context);
1634 return error;
1635 }
1636
1637 /* Get the Finder Info. */
1638 if (strcmp(name, XATTR_FINDERINFO_NAME) == 0) {
1639 if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) {
1640 error = ENOATTR;
1641 } else if (uio == NULL) {
1642 *size = FINDERINFOSIZE;
1643 error = 0;
1644 } else if (uio_offset(uio) != 0) {
1645 error = EINVAL;
1646 } else if (uio_resid(uio) < FINDERINFOSIZE) {
1647 error = ERANGE;
1648 } else {
1649 attrdata = (u_int8_t*)ainfo.filehdr + ainfo.finderinfo->offset;
1650 error = uiomove((caddr_t)attrdata, FINDERINFOSIZE, uio);
1651 }
1652 goto out;
1653 }
1654
1655 /* Read the Resource Fork. */
1656 if (isrsrcfork) {
1657 if (!vnode_isreg(vp)) {
1658 error = EPERM;
1659 } else if (ainfo.rsrcfork == NULL) {
1660 error = ENOATTR;
1661 } else if (uio == NULL) {
1662 *size = (size_t)ainfo.rsrcfork->length;
1663 } else {
1664 uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset);
1665 error = VNOP_READ(xvp, uio, 0, context);
1666 if (error == 0) {
1667 uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset);
1668 }
1669 }
1670 goto out;
1671 }
1672
1673 if (ainfo.attrhdr == NULL || ainfo.attr_entry == NULL) {
1674 error = ENOATTR;
1675 goto out;
1676 }
1677 if (uio_offset(uio) != 0) {
1678 error = EINVAL;
1679 goto out;
1680 }
1681 error = ENOATTR;
1682 namelen = strlen(name) + 1;
1683 header = ainfo.attrhdr;
1684 entry = ainfo.attr_entry;
1685 /*
1686 * Search for attribute name in the header.
1687 */
1688 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
1689 if (strncmp((const char *)entry->name, name, namelen) == 0) {
1690 datalen = (size_t)entry->length;
1691 if (uio == NULL) {
1692 *size = datalen;
1693 error = 0;
1694 break;
1695 }
1696 if (uio_resid(uio) < (user_ssize_t)datalen) {
1697 error = ERANGE;
1698 break;
1699 }
1700 if (entry->offset + datalen < ATTR_MAX_HDR_SIZE) {
1701 attrdata = ((u_int8_t *)header + entry->offset);
1702 error = uiomove((caddr_t)attrdata, datalen, uio);
1703 } else {
1704 uio_setoffset(uio, entry->offset);
1705 error = VNOP_READ(xvp, uio, 0, context);
1706 uio_setoffset(uio, 0);
1707 }
1708 break;
1709 }
1710 entry = ATTR_NEXT(entry);
1711 }
1712 out:
1713 rel_xattrinfo(&ainfo);
1714 close_xattrfile(xvp, fileflags, context);
1715
1716 return error;
1717 }
1718
1719 /*
1720 * Set the data of an extended attribute.
1721 */
1722 static int
1723 default_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context)
1724 {
1725 vnode_t xvp = NULL;
1726 attr_info_t ainfo;
1727 attr_header_t *header;
1728 attr_entry_t *entry;
1729 attr_entry_t *lastentry;
1730 u_int8_t *attrdata;
1731 size_t datalen;
1732 size_t entrylen;
1733 size_t datafreespace;
1734 int namelen;
1735 int found = 0;
1736 int i;
1737 int splitdata;
1738 int fileflags;
1739 int error;
1740 char finfo[FINDERINFOSIZE];
1741
1742 datalen = uio_resid(uio);
1743 namelen = strlen(name) + 1;
1744 entrylen = ATTR_ENTRY_LENGTH(namelen);
1745
1746 /*
1747 * By convention, Finder Info that is all zeroes is equivalent to not
1748 * having a Finder Info EA. So if we're trying to set the Finder Info
1749 * to all zeroes, then delete it instead. If a file didn't have an
1750 * AppleDouble file before, this prevents creating an AppleDouble file
1751 * with no useful content.
1752 *
1753 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1754 * for all zeroes Finder Info before opening the AppleDouble file.
1755 * But if either of those options were specified, we need to open the
1756 * AppleDouble file to see whether there was already Finder Info (so we
1757 * can return an error if needed); this case is handled further below.
1758 *
1759 * NOTE: this copies the Finder Info data into the "finfo" local.
1760 */
1761 if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1762 /*
1763 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1764 * That means we probably have to open_xattrfile and get_xattrinfo.
1765 */
1766 if (uio_offset(uio) != 0 || datalen != FINDERINFOSIZE) {
1767 return EINVAL;
1768 }
1769 error = uiomove(finfo, datalen, uio);
1770 if (error) {
1771 return error;
1772 }
1773 if ((options & (XATTR_CREATE | XATTR_REPLACE)) == 0 &&
1774 bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) {
1775 error = default_removexattr(vp, name, 0, context);
1776 if (error == ENOATTR) {
1777 error = 0;
1778 }
1779 return error;
1780 }
1781 }
1782
1783 start:
1784 /*
1785 * Open the file locked since setting an attribute
1786 * can change the layout of the Apple Double file.
1787 */
1788 fileflags = FREAD | FWRITE | O_EXLOCK;
1789 if ((error = open_xattrfile(vp, O_CREAT | fileflags, &xvp, context))) {
1790 return error;
1791 }
1792 if ((error = get_xattrinfo(xvp, ATTR_SETTING, &ainfo, context))) {
1793 close_xattrfile(xvp, fileflags, context);
1794 return error;
1795 }
1796
1797 /* Set the Finder Info. */
1798 if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1799 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
1800 /* attr exists and "create" was specified? */
1801 if (options & XATTR_CREATE) {
1802 error = EEXIST;
1803 goto out;
1804 }
1805 } else {
1806 /* attr doesn't exists and "replace" was specified? */
1807 if (options & XATTR_REPLACE) {
1808 error = ENOATTR;
1809 goto out;
1810 }
1811 }
1812 if (options != 0 && bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) {
1813 /*
1814 * Setting the Finder Info to all zeroes is equivalent to
1815 * removing it. Close the xattr file and let
1816 * default_removexattr do the work (including deleting
1817 * the xattr file if there are no other xattrs).
1818 *
1819 * Note that we have to handle the case where the
1820 * Finder Info was already all zeroes, and we ignore
1821 * ENOATTR.
1822 *
1823 * The common case where options == 0 was handled above.
1824 */
1825 rel_xattrinfo(&ainfo);
1826 close_xattrfile(xvp, fileflags, context);
1827 error = default_removexattr(vp, name, 0, context);
1828 if (error == ENOATTR) {
1829 error = 0;
1830 }
1831 return error;
1832 }
1833 if (ainfo.finderinfo) {
1834 attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset;
1835 bcopy(finfo, attrdata, datalen);
1836 ainfo.iosize = sizeof(attr_header_t);
1837 error = write_xattrinfo(&ainfo);
1838 goto out;
1839 }
1840 error = ENOATTR;
1841 goto out;
1842 }
1843
1844 /* Write the Resource Fork. */
1845 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
1846 u_int32_t endoffset;
1847
1848 if (!vnode_isreg(vp)) {
1849 error = EPERM;
1850 goto out;
1851 }
1852 /* Make sure we have a rsrc fork pointer.. */
1853 if (ainfo.rsrcfork == NULL) {
1854 error = ENOATTR;
1855 goto out;
1856 }
1857 if (ainfo.rsrcfork) {
1858 if (ainfo.rsrcfork->length != 0) {
1859 if (options & XATTR_CREATE) {
1860 /* attr exists, and create specified ? */
1861 error = EEXIST;
1862 goto out;
1863 }
1864 } else {
1865 /* Zero length AD rsrc fork */
1866 if (options & XATTR_REPLACE) {
1867 /* attr doesn't exist (0-length), but replace specified ? */
1868 error = ENOATTR;
1869 goto out;
1870 }
1871 }
1872 } else {
1873 /* We can't do much if we somehow didn't get an AD rsrc pointer */
1874 error = ENOATTR;
1875 goto out;
1876 }
1877
1878 endoffset = uio_resid(uio) + uio_offset(uio); /* new size */
1879 uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset);
1880 error = VNOP_WRITE(xvp, uio, 0, context);
1881 if (error) {
1882 goto out;
1883 }
1884 uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset);
1885 if (endoffset > ainfo.rsrcfork->length) {
1886 ainfo.rsrcfork->length = endoffset;
1887 ainfo.iosize = sizeof(attr_header_t);
1888 error = write_xattrinfo(&ainfo);
1889 goto out;
1890 }
1891 goto out;
1892 }
1893
1894 if (datalen > ATTR_MAX_SIZE) {
1895 return E2BIG; /* EINVAL instead ? */
1896 }
1897
1898 if (ainfo.attrhdr == NULL) {
1899 error = ENOATTR;
1900 goto out;
1901 }
1902 header = ainfo.attrhdr;
1903 entry = ainfo.attr_entry;
1904
1905 /* Check if data area crosses the maximum header size. */
1906 if ((header->data_start + header->data_length + entrylen + datalen) > ATTR_MAX_HDR_SIZE) {
1907 splitdata = 1; /* do data I/O separately */
1908 } else {
1909 splitdata = 0;
1910 }
1911
1912 /*
1913 * See if attribute already exists.
1914 */
1915 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
1916 if (strncmp((const char *)entry->name, name, namelen) == 0) {
1917 found = 1;
1918 break;
1919 }
1920 entry = ATTR_NEXT(entry);
1921 }
1922
1923 if (found) {
1924 if (options & XATTR_CREATE) {
1925 error = EEXIST;
1926 goto out;
1927 }
1928 if (datalen == entry->length) {
1929 if (splitdata) {
1930 uio_setoffset(uio, entry->offset);
1931 error = VNOP_WRITE(xvp, uio, 0, context);
1932 uio_setoffset(uio, 0);
1933 if (error) {
1934 printf("setxattr: VNOP_WRITE error %d\n", error);
1935 }
1936 } else {
1937 attrdata = (u_int8_t *)header + entry->offset;
1938 error = uiomove((caddr_t)attrdata, datalen, uio);
1939 if (error) {
1940 goto out;
1941 }
1942 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
1943 error = write_xattrinfo(&ainfo);
1944 if (error) {
1945 printf("setxattr: write_xattrinfo error %d\n", error);
1946 }
1947 }
1948 goto out;
1949 } else {
1950 /*
1951 * Brute force approach - just remove old entry and set new entry.
1952 */
1953 found = 0;
1954 rel_xattrinfo(&ainfo);
1955 close_xattrfile(xvp, fileflags, context);
1956 error = default_removexattr(vp, name, options, context);
1957 if (error) {
1958 return error;
1959 }
1960 /* Clear XATTR_REPLACE option since we just removed the attribute. */
1961 options &= ~XATTR_REPLACE;
1962 goto start; /* start over */
1963 }
1964 }
1965
1966 if (options & XATTR_REPLACE) {
1967 error = ENOATTR; /* nothing there to replace */
1968 goto out;
1969 }
1970 /* Check if header size limit has been reached. */
1971 if ((header->data_start + entrylen) > ATTR_MAX_HDR_SIZE) {
1972 error = ENOSPC;
1973 goto out;
1974 }
1975
1976 datafreespace = header->total_size - (header->data_start + header->data_length);
1977
1978 /* Check if we need more space. */
1979 if ((datalen + entrylen) > datafreespace) {
1980 size_t growsize;
1981
1982 growsize = roundup((datalen + entrylen) - datafreespace, ATTR_BUF_SIZE);
1983
1984 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
1985 if (!splitdata && (header->total_size + growsize) > ATTR_MAX_HDR_SIZE) {
1986 growsize = ATTR_MAX_HDR_SIZE - header->total_size;
1987 }
1988
1989 ainfo.filesize += growsize;
1990 error = vnode_setsize(xvp, ainfo.filesize, 0, context);
1991 if (error) {
1992 printf("setxattr: VNOP_TRUNCATE error %d\n", error);
1993 }
1994 if (error) {
1995 goto out;
1996 }
1997
1998 /*
1999 * Move the resource fork out of the way.
2000 */
2001 if (ainfo.rsrcfork) {
2002 if (ainfo.rsrcfork->length != 0) {
2003 shift_data_down(xvp,
2004 ainfo.rsrcfork->offset,
2005 ainfo.rsrcfork->length,
2006 growsize, context);
2007 }
2008 ainfo.rsrcfork->offset += growsize;
2009 }
2010 ainfo.finderinfo->length += growsize;
2011 header->total_size += growsize;
2012 }
2013
2014 /* Make space for a new entry. */
2015 if (splitdata) {
2016 shift_data_down(xvp,
2017 header->data_start,
2018 header->data_length,
2019 entrylen, context);
2020 } else {
2021 bcopy((u_int8_t *)header + header->data_start,
2022 (u_int8_t *)header + header->data_start + entrylen,
2023 header->data_length);
2024 }
2025 header->data_start += entrylen;
2026
2027 /* Fix up entry data offsets. */
2028 lastentry = entry;
2029 for (entry = ainfo.attr_entry; entry != lastentry && ATTR_VALID(entry, ainfo); entry = ATTR_NEXT(entry)) {
2030 entry->offset += entrylen;
2031 }
2032
2033 /*
2034 * If the attribute data area is entirely within
2035 * the header buffer, then just update the buffer,
2036 * otherwise we'll write it separately to the file.
2037 */
2038 if (splitdata) {
2039 off_t offset;
2040
2041 /* Write new attribute data after the end of existing data. */
2042 offset = header->data_start + header->data_length;
2043 uio_setoffset(uio, offset);
2044 error = VNOP_WRITE(xvp, uio, 0, context);
2045 uio_setoffset(uio, 0);
2046 if (error) {
2047 printf("setxattr: VNOP_WRITE error %d\n", error);
2048 goto out;
2049 }
2050 } else {
2051 attrdata = (u_int8_t *)header + header->data_start + header->data_length;
2052
2053 error = uiomove((caddr_t)attrdata, datalen, uio);
2054 if (error) {
2055 printf("setxattr: uiomove error %d\n", error);
2056 goto out;
2057 }
2058 }
2059
2060 /* Create the attribute entry. */
2061 lastentry->length = datalen;
2062 lastentry->offset = header->data_start + header->data_length;
2063 lastentry->namelen = namelen;
2064 lastentry->flags = 0;
2065 bcopy(name, &lastentry->name[0], namelen);
2066
2067 /* Update the attributes header. */
2068 header->num_attrs++;
2069 header->data_length += datalen;
2070
2071 if (splitdata) {
2072 /* Only write the entries, since the data was written separately. */
2073 ainfo.iosize = ainfo.attrhdr->data_start;
2074 } else {
2075 /* The entry and data are both in the header; write them together. */
2076 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
2077 }
2078 error = write_xattrinfo(&ainfo);
2079 if (error) {
2080 printf("setxattr: write_xattrinfo error %d\n", error);
2081 }
2082
2083 out:
2084 rel_xattrinfo(&ainfo);
2085 close_xattrfile(xvp, fileflags, context);
2086
2087 /* Touch the change time if we changed an attribute. */
2088 if (error == 0) {
2089 struct vnode_attr va;
2090
2091 /* Re-write the mtime to cause a ctime change. */
2092 VATTR_INIT(&va);
2093 VATTR_WANTED(&va, va_modify_time);
2094 if (vnode_getattr(vp, &va, context) == 0) {
2095 VATTR_INIT(&va);
2096 VATTR_SET(&va, va_modify_time, va.va_modify_time);
2097 (void) vnode_setattr(vp, &va, context);
2098 }
2099 }
2100
2101 post_event_if_success(vp, error, NOTE_ATTRIB);
2102
2103 return error;
2104 }
2105
2106
2107 /*
2108 * Remove an extended attribute.
2109 */
2110 static int
2111 default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_context_t context)
2112 {
2113 vnode_t xvp = NULL;
2114 attr_info_t ainfo;
2115 attr_header_t *header;
2116 attr_entry_t *entry;
2117 attr_entry_t *oldslot;
2118 u_int8_t *attrdata;
2119 u_int32_t dataoff;
2120 size_t datalen;
2121 size_t entrylen;
2122 int namelen;
2123 int found = 0, lastone = 0;
2124 int i;
2125 int splitdata;
2126 int attrcount = 0;
2127 int isrsrcfork;
2128 int fileflags;
2129 int error;
2130
2131 fileflags = FREAD | FWRITE;
2132 if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
2133 isrsrcfork = 1;
2134 /*
2135 * Open the file locked (exclusive) since the Carbon
2136 * File Manager may have the Apple Double file open
2137 * and could be changing the resource fork.
2138 */
2139 fileflags |= O_EXLOCK;
2140 } else {
2141 isrsrcfork = 0;
2142 }
2143
2144 if ((error = open_xattrfile(vp, fileflags, &xvp, context))) {
2145 return error;
2146 }
2147 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
2148 close_xattrfile(xvp, fileflags, context);
2149 return error;
2150 }
2151 if (ainfo.attrhdr) {
2152 attrcount += ainfo.attrhdr->num_attrs;
2153 }
2154 if (ainfo.rsrcfork) {
2155 ++attrcount;
2156 }
2157 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
2158 ++attrcount;
2159 }
2160
2161 /* Clear the Finder Info. */
2162 if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
2163 if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) {
2164 error = ENOATTR;
2165 goto out;
2166 }
2167 /* On removal of last attribute the ._ file is removed. */
2168 if (--attrcount == 0) {
2169 goto out;
2170 }
2171 attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset;
2172 bzero((caddr_t)attrdata, FINDERINFOSIZE);
2173 ainfo.iosize = sizeof(attr_header_t);
2174 error = write_xattrinfo(&ainfo);
2175 goto out;
2176 }
2177
2178 /* Clear the Resource Fork. */
2179 if (isrsrcfork) {
2180 if (!vnode_isreg(vp)) {
2181 error = EPERM;
2182 goto out;
2183 }
2184 if (ainfo.rsrcfork == NULL || ainfo.rsrcfork->length == 0) {
2185 error = ENOATTR;
2186 goto out;
2187 }
2188 /* On removal of last attribute the ._ file is removed. */
2189 if (--attrcount == 0) {
2190 goto out;
2191 }
2192 /*
2193 * XXX
2194 * If the resource fork isn't the last AppleDouble
2195 * entry then the space needs to be reclaimed by
2196 * shifting the entries after the resource fork.
2197 */
2198 if ((ainfo.rsrcfork->offset + ainfo.rsrcfork->length) == ainfo.filesize) {
2199 ainfo.filesize -= ainfo.rsrcfork->length;
2200 error = vnode_setsize(xvp, ainfo.filesize, 0, context);
2201 }
2202 if (error == 0) {
2203 ainfo.rsrcfork->length = 0;
2204 ainfo.iosize = sizeof(attr_header_t);
2205 error = write_xattrinfo(&ainfo);
2206 }
2207 goto out;
2208 }
2209
2210 if (ainfo.attrhdr == NULL) {
2211 error = ENOATTR;
2212 goto out;
2213 }
2214 namelen = strlen(name) + 1;
2215 header = ainfo.attrhdr;
2216 entry = ainfo.attr_entry;
2217
2218 /*
2219 * See if this attribute exists.
2220 */
2221 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
2222 if (strncmp((const char *)entry->name, name, namelen) == 0) {
2223 found = 1;
2224 if ((i + 1) == header->num_attrs) {
2225 lastone = 1;
2226 }
2227 break;
2228 }
2229 entry = ATTR_NEXT(entry);
2230 }
2231 if (!found) {
2232 error = ENOATTR;
2233 goto out;
2234 }
2235 /* On removal of last attribute the ._ file is removed. */
2236 if (--attrcount == 0) {
2237 goto out;
2238 }
2239
2240 datalen = entry->length;
2241 dataoff = entry->offset;
2242 entrylen = ATTR_ENTRY_LENGTH(namelen);
2243 if ((header->data_start + header->data_length) > ATTR_MAX_HDR_SIZE) {
2244 splitdata = 1;
2245 } else {
2246 splitdata = 0;
2247 }
2248
2249 /* Remove the attribute entry. */
2250 if (!lastone) {
2251 bcopy((u_int8_t *)entry + entrylen, (u_int8_t *)entry,
2252 ((size_t)header + header->data_start) - ((size_t)entry + entrylen));
2253 }
2254
2255 /* Adjust the attribute data. */
2256 if (splitdata) {
2257 shift_data_up(xvp,
2258 header->data_start,
2259 dataoff - header->data_start,
2260 entrylen,
2261 context);
2262 if (!lastone) {
2263 shift_data_up(xvp,
2264 dataoff + datalen,
2265 (header->data_start + header->data_length) - (dataoff + datalen),
2266 datalen + entrylen,
2267 context);
2268 }
2269 /* XXX write zeros to freed space ? */
2270 ainfo.iosize = ainfo.attrhdr->data_start - entrylen;
2271 } else {
2272 bcopy((u_int8_t *)header + header->data_start,
2273 (u_int8_t *)header + header->data_start - entrylen,
2274 dataoff - header->data_start);
2275 if (!lastone) {
2276 bcopy((u_int8_t *)header + dataoff + datalen,
2277 (u_int8_t *)header + dataoff - entrylen,
2278 (header->data_start + header->data_length) - (dataoff + datalen));
2279 }
2280 bzero(((u_int8_t *)header + header->data_start + header->data_length) - (datalen + entrylen), (datalen + entrylen));
2281 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
2282 }
2283
2284 /* Adjust the header values and entry offsets. */
2285 header->num_attrs--;
2286 header->data_start -= entrylen;
2287 header->data_length -= datalen;
2288
2289 oldslot = entry;
2290 entry = ainfo.attr_entry;
2291 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
2292 entry->offset -= entrylen;
2293 if (entry >= oldslot) {
2294 entry->offset -= datalen;
2295 }
2296 entry = ATTR_NEXT(entry);
2297 }
2298 error = write_xattrinfo(&ainfo);
2299 if (error) {
2300 printf("removexattr: write_xattrinfo error %d\n", error);
2301 }
2302 out:
2303 rel_xattrinfo(&ainfo);
2304
2305 /* When there are no more attributes remove the ._ file. */
2306 if (attrcount == 0) {
2307 if (fileflags & O_EXLOCK) {
2308 (void) unlock_xattrfile(xvp, context);
2309 }
2310 VNOP_CLOSE(xvp, fileflags, context);
2311 vnode_rele(xvp);
2312 error = remove_xattrfile(xvp, context);
2313 vnode_put(xvp);
2314 } else {
2315 close_xattrfile(xvp, fileflags, context);
2316 }
2317 /* Touch the change time if we changed an attribute. */
2318 if (error == 0) {
2319 struct vnode_attr va;
2320
2321 /* Re-write the mtime to cause a ctime change. */
2322 VATTR_INIT(&va);
2323 VATTR_WANTED(&va, va_modify_time);
2324 if (vnode_getattr(vp, &va, context) == 0) {
2325 VATTR_INIT(&va);
2326 VATTR_SET(&va, va_modify_time, va.va_modify_time);
2327 (void) vnode_setattr(vp, &va, context);
2328 }
2329 }
2330
2331 post_event_if_success(vp, error, NOTE_ATTRIB);
2332
2333 return error;
2334 }
2335
2336
2337 /*
2338 * Retrieve the list of extended attribute names.
2339 */
2340 static int
2341 default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs_context_t context)
2342 {
2343 vnode_t xvp = NULL;
2344 attr_info_t ainfo;
2345 attr_entry_t *entry;
2346 int i, count;
2347 int error;
2348
2349 /*
2350 * We do not zero "*size" here as we don't want to stomp a size set when
2351 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2352 * system call layer, up in listxattr or flistxattr.
2353 */
2354
2355 if ((error = open_xattrfile(vp, FREAD, &xvp, context))) {
2356 if (error == ENOATTR) {
2357 error = 0;
2358 }
2359 return error;
2360 }
2361 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
2362 if (error == ENOATTR) {
2363 error = 0;
2364 }
2365 close_xattrfile(xvp, FREAD, context);
2366 return error;
2367 }
2368
2369 /* Check for Finder Info. */
2370 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
2371 if (uio == NULL) {
2372 *size += sizeof(XATTR_FINDERINFO_NAME);
2373 } else if (uio_resid(uio) < (user_ssize_t)sizeof(XATTR_FINDERINFO_NAME)) {
2374 error = ERANGE;
2375 goto out;
2376 } else {
2377 error = uiomove(XATTR_FINDERINFO_NAME,
2378 sizeof(XATTR_FINDERINFO_NAME), uio);
2379 if (error) {
2380 error = ERANGE;
2381 goto out;
2382 }
2383 }
2384 }
2385
2386 /* Check for Resource Fork. */
2387 if (vnode_isreg(vp) && ainfo.rsrcfork) {
2388 if (uio == NULL) {
2389 *size += sizeof(XATTR_RESOURCEFORK_NAME);
2390 } else if (uio_resid(uio) < (user_ssize_t)sizeof(XATTR_RESOURCEFORK_NAME)) {
2391 error = ERANGE;
2392 goto out;
2393 } else {
2394 error = uiomove(XATTR_RESOURCEFORK_NAME,
2395 sizeof(XATTR_RESOURCEFORK_NAME), uio);
2396 if (error) {
2397 error = ERANGE;
2398 goto out;
2399 }
2400 }
2401 }
2402
2403 /* Check for attributes. */
2404 if (ainfo.attrhdr) {
2405 count = ainfo.attrhdr->num_attrs;
2406 for (i = 0, entry = ainfo.attr_entry; i < count && ATTR_VALID(entry, ainfo); i++) {
2407 if (xattr_protected((const char *)entry->name) ||
2408 ((entry->namelen < XATTR_MAXNAMELEN) &&
2409 (entry->name[entry->namelen] == '\0') &&
2410 (xattr_validatename((const char *)entry->name) != 0))) {
2411 entry = ATTR_NEXT(entry);
2412 continue;
2413 }
2414 if (uio == NULL) {
2415 *size += entry->namelen;
2416 entry = ATTR_NEXT(entry);
2417 continue;
2418 }
2419 if (uio_resid(uio) < entry->namelen) {
2420 error = ERANGE;
2421 break;
2422 }
2423 error = uiomove((caddr_t) entry->name, entry->namelen, uio);
2424 if (error) {
2425 if (error != EFAULT) {
2426 error = ERANGE;
2427 }
2428 break;
2429 }
2430 entry = ATTR_NEXT(entry);
2431 }
2432 }
2433 out:
2434 rel_xattrinfo(&ainfo);
2435 close_xattrfile(xvp, FREAD, context);
2436
2437 return error;
2438 }
2439
2440 static int
2441 open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context)
2442 {
2443 vnode_t xvp = NULLVP;
2444 vnode_t dvp = NULLVP;
2445 struct vnode_attr va;
2446 struct nameidata nd;
2447 char smallname[64];
2448 char *filename = NULL;
2449 const char *basename = NULL;
2450 size_t len;
2451 errno_t error;
2452 int opened = 0;
2453 int referenced = 0;
2454
2455 if (vnode_isvroot(vp) && vnode_isdir(vp)) {
2456 /*
2457 * For the root directory use "._." to hold the attributes.
2458 */
2459 filename = &smallname[0];
2460 snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, ".");
2461 dvp = vp; /* the "._." file resides in the root dir */
2462 goto lookup;
2463 }
2464 if ((dvp = vnode_getparent(vp)) == NULLVP) {
2465 error = ENOATTR;
2466 goto out;
2467 }
2468 if ((basename = vnode_getname(vp)) == NULL) {
2469 error = ENOATTR;
2470 goto out;
2471 }
2472
2473 /* "._" Attribute files cannot have attributes */
2474 if (vp->v_type == VREG && strlen(basename) > 2 &&
2475 basename[0] == '.' && basename[1] == '_') {
2476 error = EPERM;
2477 goto out;
2478 }
2479 filename = &smallname[0];
2480 len = snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, basename);
2481 if (len >= sizeof(smallname)) {
2482 len++; /* snprintf result doesn't include '\0' */
2483 MALLOC(filename, char *, len, M_TEMP, M_WAITOK);
2484 len = snprintf(filename, len, "%s%s", ATTR_FILE_PREFIX, basename);
2485 }
2486 /*
2487 * Note that the lookup here does not authorize. Since we are looking
2488 * up in the same directory that we already have the file vnode in,
2489 * we must have been given the file vnode legitimately. Read/write
2490 * access has already been authorized in layers above for calls from
2491 * userspace, and the authorization code using this path to read
2492 * file security from the EA must always get access
2493 */
2494 lookup:
2495 NDINIT(&nd, LOOKUP, OP_OPEN, LOCKLEAF | NOFOLLOW | USEDVP | DONOTAUTH,
2496 UIO_SYSSPACE, CAST_USER_ADDR_T(filename), context);
2497 nd.ni_dvp = dvp;
2498
2499 if (fileflags & O_CREAT) {
2500 nd.ni_cnd.cn_nameiop = CREATE;
2501 #if CONFIG_TRIGGERS
2502 nd.ni_op = OP_LINK;
2503 #endif
2504 if (dvp != vp) {
2505 nd.ni_cnd.cn_flags |= LOCKPARENT;
2506 }
2507 if ((error = namei(&nd))) {
2508 nd.ni_dvp = NULLVP;
2509 error = ENOATTR;
2510 goto out;
2511 }
2512 if ((xvp = nd.ni_vp) == NULLVP) {
2513 uid_t uid;
2514 gid_t gid;
2515 mode_t umode;
2516
2517 /*
2518 * Pick up uid/gid/mode from target file.
2519 */
2520 VATTR_INIT(&va);
2521 VATTR_WANTED(&va, va_uid);
2522 VATTR_WANTED(&va, va_gid);
2523 VATTR_WANTED(&va, va_mode);
2524 if (VNOP_GETATTR(vp, &va, context) == 0 &&
2525 VATTR_IS_SUPPORTED(&va, va_uid) &&
2526 VATTR_IS_SUPPORTED(&va, va_gid) &&
2527 VATTR_IS_SUPPORTED(&va, va_mode)) {
2528 uid = va.va_uid;
2529 gid = va.va_gid;
2530 umode = va.va_mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
2531 } else { /* fallback values */
2532 uid = KAUTH_UID_NONE;
2533 gid = KAUTH_GID_NONE;
2534 umode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
2535 }
2536
2537 VATTR_INIT(&va);
2538 VATTR_SET(&va, va_type, VREG);
2539 VATTR_SET(&va, va_mode, umode);
2540 if (uid != KAUTH_UID_NONE) {
2541 VATTR_SET(&va, va_uid, uid);
2542 }
2543 if (gid != KAUTH_GID_NONE) {
2544 VATTR_SET(&va, va_gid, gid);
2545 }
2546
2547 error = vn_create(dvp, &nd.ni_vp, &nd, &va,
2548 VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT | VN_CREATE_NOLABEL,
2549 0, NULL,
2550 context);
2551 if (error) {
2552 error = ENOATTR;
2553 } else {
2554 xvp = nd.ni_vp;
2555 }
2556 }
2557 nameidone(&nd);
2558 if (dvp != vp) {
2559 vnode_put(dvp); /* drop iocount from LOCKPARENT request above */
2560 }
2561 if (error) {
2562 goto out;
2563 }
2564 } else {
2565 if ((error = namei(&nd))) {
2566 nd.ni_dvp = NULLVP;
2567 error = ENOATTR;
2568 goto out;
2569 }
2570 xvp = nd.ni_vp;
2571 nameidone(&nd);
2572 }
2573 nd.ni_dvp = NULLVP;
2574
2575 if (xvp->v_type != VREG) {
2576 error = ENOATTR;
2577 goto out;
2578 }
2579 /*
2580 * Owners must match.
2581 */
2582 VATTR_INIT(&va);
2583 VATTR_WANTED(&va, va_uid);
2584 if (VNOP_GETATTR(vp, &va, context) == 0 && VATTR_IS_SUPPORTED(&va, va_uid)) {
2585 uid_t owner = va.va_uid;
2586
2587 VATTR_INIT(&va);
2588 VATTR_WANTED(&va, va_uid);
2589 if (VNOP_GETATTR(xvp, &va, context) == 0 && (owner != va.va_uid)) {
2590 error = ENOATTR; /* don't use this "._" file */
2591 goto out;
2592 }
2593 }
2594
2595 if ((error = VNOP_OPEN(xvp, fileflags & ~(O_EXLOCK | O_SHLOCK), context))) {
2596 error = ENOATTR;
2597 goto out;
2598 }
2599 opened = 1;
2600
2601 if ((error = vnode_ref(xvp))) {
2602 goto out;
2603 }
2604 referenced = 1;
2605
2606 /* If create was requested, make sure file header exists. */
2607 if (fileflags & O_CREAT) {
2608 VATTR_INIT(&va);
2609 VATTR_WANTED(&va, va_data_size);
2610 VATTR_WANTED(&va, va_fileid);
2611 VATTR_WANTED(&va, va_nlink);
2612 if ((error = vnode_getattr(xvp, &va, context)) != 0) {
2613 error = EPERM;
2614 goto out;
2615 }
2616
2617 /* If the file is empty then add a default header. */
2618 if (va.va_data_size == 0) {
2619 /* Don't adopt hard-linked "._" files. */
2620 if (VATTR_IS_SUPPORTED(&va, va_nlink) && va.va_nlink > 1) {
2621 error = EPERM;
2622 goto out;
2623 }
2624 if ((error = create_xattrfile(xvp, (u_int32_t)va.va_fileid, context))) {
2625 goto out;
2626 }
2627 }
2628 }
2629 /* Apply file locking if requested. */
2630 if (fileflags & (O_EXLOCK | O_SHLOCK)) {
2631 short locktype;
2632
2633 locktype = (fileflags & O_EXLOCK) ? F_WRLCK : F_RDLCK;
2634 error = lock_xattrfile(xvp, locktype, context);
2635 if (error) {
2636 error = ENOATTR;
2637 }
2638 }
2639 out:
2640 if (error) {
2641 if (xvp != NULLVP) {
2642 if (opened) {
2643 (void) VNOP_CLOSE(xvp, fileflags, context);
2644 }
2645
2646 if (fileflags & O_CREAT) {
2647 /* Delete the xattr file if we encountered any errors */
2648 (void) remove_xattrfile(xvp, context);
2649 }
2650
2651 if (referenced) {
2652 (void) vnode_rele(xvp);
2653 }
2654 (void) vnode_put(xvp);
2655 xvp = NULLVP;
2656 }
2657 if ((error == ENOATTR) && (fileflags & O_CREAT)) {
2658 error = EPERM;
2659 }
2660 }
2661 /* Release resources after error-handling */
2662 if (dvp && (dvp != vp)) {
2663 vnode_put(dvp);
2664 }
2665 if (basename) {
2666 vnode_putname(basename);
2667 }
2668 if (filename && filename != &smallname[0]) {
2669 FREE(filename, M_TEMP);
2670 }
2671
2672 *xvpp = xvp; /* return a referenced vnode */
2673 return error;
2674 }
2675
2676 static void
2677 close_xattrfile(vnode_t xvp, int fileflags, vfs_context_t context)
2678 {
2679 // if (fileflags & FWRITE)
2680 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
2681
2682 if (fileflags & (O_EXLOCK | O_SHLOCK)) {
2683 (void) unlock_xattrfile(xvp, context);
2684 }
2685
2686 (void) VNOP_CLOSE(xvp, fileflags, context);
2687 (void) vnode_rele(xvp);
2688 (void) vnode_put(xvp);
2689 }
2690
2691 static int
2692 remove_xattrfile(vnode_t xvp, vfs_context_t context)
2693 {
2694 vnode_t dvp;
2695 struct nameidata nd;
2696 char *path = NULL;
2697 int pathlen;
2698 int error = 0;
2699
2700 MALLOC_ZONE(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
2701 if (path == NULL) {
2702 return ENOMEM;
2703 }
2704
2705 pathlen = MAXPATHLEN;
2706 error = vn_getpath(xvp, path, &pathlen);
2707 if (error) {
2708 FREE_ZONE(path, MAXPATHLEN, M_NAMEI);
2709 return error;
2710 }
2711
2712 NDINIT(&nd, DELETE, OP_UNLINK, LOCKPARENT | NOFOLLOW | DONOTAUTH,
2713 UIO_SYSSPACE, CAST_USER_ADDR_T(path), context);
2714 error = namei(&nd);
2715 FREE_ZONE(path, MAXPATHLEN, M_NAMEI);
2716 if (error) {
2717 return error;
2718 }
2719 dvp = nd.ni_dvp;
2720 xvp = nd.ni_vp;
2721
2722 error = VNOP_REMOVE(dvp, xvp, &nd.ni_cnd, 0, context);
2723 nameidone(&nd);
2724 vnode_put(dvp);
2725 vnode_put(xvp);
2726
2727 return error;
2728 }
2729
2730 /*
2731 * Read in and parse the AppleDouble header and entries, and the extended
2732 * attribute header and entries if any. Populates the fields of ainfop
2733 * based on the headers and entries found.
2734 *
2735 * The basic idea is to:
2736 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
2737 * AppleDouble entries, the extended attribute header, and extended
2738 * attribute entries must lie within this part of the file; the rest of
2739 * the AppleDouble handling code assumes this. Plus it allows us to
2740 * somewhat optimize by doing a smaller number of larger I/Os.
2741 * - Swap and sanity check the AppleDouble header (including the AppleDouble
2742 * entries).
2743 * - Find the Finder Info and Resource Fork entries, if any.
2744 * - If we're going to be writing, try to make sure the Finder Info entry has
2745 * room to store the extended attribute header, plus some space for extended
2746 * attributes.
2747 * - Swap and sanity check the extended attribute header and entries (if any).
2748 */
2749 static int
2750 get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context)
2751 {
2752 uio_t auio = NULL;
2753 void * buffer = NULL;
2754 apple_double_header_t *filehdr;
2755 struct vnode_attr va;
2756 size_t iosize;
2757 int i;
2758 int error;
2759
2760 bzero(ainfop, sizeof(attr_info_t));
2761 ainfop->filevp = xvp;
2762 ainfop->context = context;
2763 VATTR_INIT(&va);
2764 VATTR_WANTED(&va, va_data_size);
2765 VATTR_WANTED(&va, va_fileid);
2766 if ((error = vnode_getattr(xvp, &va, context))) {
2767 goto bail;
2768 }
2769 ainfop->filesize = va.va_data_size;
2770
2771 /* When setting attributes, allow room for the header to grow. */
2772 if (setting) {
2773 iosize = ATTR_MAX_HDR_SIZE;
2774 } else {
2775 iosize = MIN(ATTR_MAX_HDR_SIZE, ainfop->filesize);
2776 }
2777
2778 if (iosize == 0) {
2779 error = ENOATTR;
2780 goto bail;
2781 }
2782 ainfop->iosize = iosize;
2783 MALLOC(buffer, void *, iosize, M_TEMP, M_WAITOK);
2784 if (buffer == NULL) {
2785 error = ENOMEM;
2786 goto bail;
2787 }
2788
2789 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
2790 uio_addiov(auio, (uintptr_t)buffer, iosize);
2791
2792 /* Read the file header. */
2793 error = VNOP_READ(xvp, auio, 0, context);
2794 if (error) {
2795 goto bail;
2796 }
2797 ainfop->rawsize = iosize - uio_resid(auio);
2798 ainfop->rawdata = (u_int8_t *)buffer;
2799
2800 filehdr = (apple_double_header_t *)buffer;
2801
2802 error = check_and_swap_apple_double_header(ainfop);
2803 if (error) {
2804 goto bail;
2805 }
2806
2807 ainfop->filehdr = filehdr; /* valid AppleDouble header */
2808
2809 /* rel_xattrinfo is responsible for freeing the header buffer */
2810 buffer = NULL;
2811
2812 /* Find the Finder Info and Resource Fork entries, if any */
2813 for (i = 0; i < filehdr->numEntries; ++i) {
2814 if (filehdr->entries[i].type == AD_FINDERINFO &&
2815 filehdr->entries[i].length >= FINDERINFOSIZE) {
2816 /* We found the Finder Info entry. */
2817 ainfop->finderinfo = &filehdr->entries[i];
2818
2819 /*
2820 * Is the Finder Info "empty" (all zeroes)? If so,
2821 * we'll pretend like the Finder Info extended attribute
2822 * does not exist.
2823 *
2824 * Note: we have to make sure the Finder Info is
2825 * contained within the buffer we have already read,
2826 * to avoid accidentally accessing a bogus address.
2827 * If it is outside the buffer, we just assume the
2828 * Finder Info is non-empty.
2829 */
2830 if (ainfop->finderinfo->offset + FINDERINFOSIZE <= ainfop->rawsize &&
2831 bcmp((u_int8_t*)ainfop->filehdr + ainfop->finderinfo->offset, emptyfinfo, sizeof(emptyfinfo)) == 0) {
2832 ainfop->emptyfinderinfo = 1;
2833 }
2834 }
2835 if (filehdr->entries[i].type == AD_RESOURCE) {
2836 /*
2837 * Ignore zero-length resource forks when getting. If setting,
2838 * we need to remember the resource fork entry so it can be
2839 * updated once the new content has been written.
2840 */
2841 if (filehdr->entries[i].length == 0 && !setting) {
2842 continue;
2843 }
2844
2845 /*
2846 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
2847 *
2848 * The "empty" resource headers we created have a system data tag of:
2849 * "This resource fork intentionally left blank "
2850 */
2851 if (filehdr->entries[i].length == sizeof(rsrcfork_header_t) && !setting) {
2852 uio_t rf_uio;
2853 u_int8_t systemData[64];
2854 int rf_err;
2855
2856
2857 /* Read the system data which starts at byte 16 */
2858 rf_uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
2859 uio_addiov(rf_uio, (uintptr_t)systemData, sizeof(systemData));
2860 uio_setoffset(rf_uio, filehdr->entries[i].offset + 16);
2861 rf_err = VNOP_READ(xvp, rf_uio, 0, context);
2862 uio_free(rf_uio);
2863
2864 if (rf_err != 0 ||
2865 bcmp(systemData, RF_EMPTY_TAG, sizeof(RF_EMPTY_TAG)) == 0) {
2866 continue; /* skip this resource fork */
2867 }
2868 }
2869 ainfop->rsrcfork = &filehdr->entries[i];
2870 if (i != (filehdr->numEntries - 1)) {
2871 printf("get_xattrinfo: resource fork not last entry\n");
2872 ainfop->readonly = 1;
2873 }
2874 continue;
2875 }
2876 }
2877
2878 /*
2879 * See if this file looks like it is laid out correctly to contain
2880 * extended attributes. If so, then do the following:
2881 *
2882 * - If we're going to be writing, try to make sure the Finder Info
2883 * entry has room to store the extended attribute header, plus some
2884 * space for extended attributes.
2885 *
2886 * - Swap and sanity check the extended attribute header and entries
2887 * (if any).
2888 */
2889 if (filehdr->numEntries == 2 &&
2890 ainfop->finderinfo == &filehdr->entries[0] &&
2891 ainfop->rsrcfork == &filehdr->entries[1] &&
2892 ainfop->finderinfo->offset == offsetof(apple_double_header_t, finfo)) {
2893 attr_header_t *attrhdr;
2894 attrhdr = (attr_header_t *)filehdr;
2895 /*
2896 * If we're going to be writing, try to make sure the Finder
2897 * Info entry has room to store the extended attribute header,
2898 * plus some space for extended attributes.
2899 */
2900 if (setting && ainfop->finderinfo->length == FINDERINFOSIZE) {
2901 size_t delta;
2902 size_t writesize;
2903
2904 delta = ATTR_BUF_SIZE - (filehdr->entries[0].offset + FINDERINFOSIZE);
2905 if (ainfop->rsrcfork && filehdr->entries[1].length) {
2906 /* Make some room before existing resource fork. */
2907 shift_data_down(xvp,
2908 filehdr->entries[1].offset,
2909 filehdr->entries[1].length,
2910 delta, context);
2911 writesize = sizeof(attr_header_t);
2912 } else {
2913 /* Create a new, empty resource fork. */
2914 rsrcfork_header_t *rsrcforkhdr;
2915
2916 vnode_setsize(xvp, filehdr->entries[1].offset + delta, 0, context);
2917
2918 /* Steal some space for an empty RF header. */
2919 delta -= sizeof(rsrcfork_header_t);
2920
2921 bzero(&attrhdr->appledouble.pad[0], delta);
2922 rsrcforkhdr = (rsrcfork_header_t *)((char *)filehdr + filehdr->entries[1].offset + delta);
2923
2924 /* Fill in Empty Resource Fork Header. */
2925 init_empty_resource_fork(rsrcforkhdr);
2926
2927 filehdr->entries[1].length = sizeof(rsrcfork_header_t);
2928 writesize = ATTR_BUF_SIZE;
2929 }
2930 filehdr->entries[0].length += delta;
2931 filehdr->entries[1].offset += delta;
2932
2933 /* Fill in Attribute Header. */
2934 attrhdr->magic = ATTR_HDR_MAGIC;
2935 attrhdr->debug_tag = (u_int32_t)va.va_fileid;
2936 attrhdr->total_size = filehdr->entries[1].offset;
2937 attrhdr->data_start = sizeof(attr_header_t);
2938 attrhdr->data_length = 0;
2939 attrhdr->reserved[0] = 0;
2940 attrhdr->reserved[1] = 0;
2941 attrhdr->reserved[2] = 0;
2942 attrhdr->flags = 0;
2943 attrhdr->num_attrs = 0;
2944
2945 /* Push out new header */
2946 uio_reset(auio, 0, UIO_SYSSPACE, UIO_WRITE);
2947 uio_addiov(auio, (uintptr_t)filehdr, writesize);
2948
2949 swap_adhdr(filehdr); /* to big endian */
2950 swap_attrhdr(attrhdr, ainfop); /* to big endian */
2951 error = VNOP_WRITE(xvp, auio, 0, context);
2952 swap_adhdr(filehdr); /* back to native */
2953 /* The attribute header gets swapped below. */
2954 }
2955 }
2956 /*
2957 * Swap and sanity check the extended attribute header and
2958 * entries (if any). The Finder Info content must be big enough
2959 * to include the extended attribute header; if not, we just
2960 * ignore it.
2961 *
2962 * Note that we're passing the offset + length (i.e. the end)
2963 * of the Finder Info instead of rawsize to validate_attrhdr.
2964 * This ensures that all extended attributes lie within the
2965 * Finder Info content according to the AppleDouble entry.
2966 *
2967 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
2968 * header was found.
2969 */
2970 if (ainfop->finderinfo &&
2971 ainfop->finderinfo == &filehdr->entries[0] &&
2972 ainfop->finderinfo->length >= (sizeof(attr_header_t) - sizeof(apple_double_header_t))) {
2973 attr_header_t *attrhdr = (attr_header_t*)filehdr;
2974
2975 if ((error = check_and_swap_attrhdr(attrhdr, ainfop)) == 0) {
2976 ainfop->attrhdr = attrhdr; /* valid attribute header */
2977 /* First attr_entry starts immediately following attribute header */
2978 ainfop->attr_entry = (attr_entry_t *)&attrhdr[1];
2979 }
2980 }
2981
2982 error = 0;
2983 bail:
2984 if (auio != NULL) {
2985 uio_free(auio);
2986 }
2987 if (buffer != NULL) {
2988 FREE(buffer, M_TEMP);
2989 }
2990 return error;
2991 }
2992
2993
2994 static int
2995 create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context)
2996 {
2997 attr_header_t *xah;
2998 rsrcfork_header_t *rsrcforkhdr;
2999 void * buffer;
3000 uio_t auio;
3001 int rsrcforksize;
3002 int error;
3003
3004 MALLOC(buffer, void *, ATTR_BUF_SIZE, M_TEMP, M_WAITOK);
3005 bzero(buffer, ATTR_BUF_SIZE);
3006
3007 xah = (attr_header_t *)buffer;
3008 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
3009 uio_addiov(auio, (uintptr_t)buffer, ATTR_BUF_SIZE);
3010 rsrcforksize = sizeof(rsrcfork_header_t);
3011 rsrcforkhdr = (rsrcfork_header_t *) ((char *)buffer + ATTR_BUF_SIZE - rsrcforksize);
3012
3013 /* Fill in Apple Double Header. */
3014 xah->appledouble.magic = SWAP32(ADH_MAGIC);
3015 xah->appledouble.version = SWAP32(ADH_VERSION);
3016 xah->appledouble.numEntries = SWAP16(2);
3017 xah->appledouble.entries[0].type = SWAP32(AD_FINDERINFO);
3018 xah->appledouble.entries[0].offset = SWAP32(offsetof(apple_double_header_t, finfo));
3019 xah->appledouble.entries[0].length = SWAP32(ATTR_BUF_SIZE - offsetof(apple_double_header_t, finfo) - rsrcforksize);
3020 xah->appledouble.entries[1].type = SWAP32(AD_RESOURCE);
3021 xah->appledouble.entries[1].offset = SWAP32(ATTR_BUF_SIZE - rsrcforksize);
3022 xah->appledouble.entries[1].length = SWAP32(rsrcforksize);
3023 bcopy(ADH_MACOSX, xah->appledouble.filler, sizeof(xah->appledouble.filler));
3024
3025 /* Fill in Attribute Header. */
3026 xah->magic = SWAP32(ATTR_HDR_MAGIC);
3027 xah->debug_tag = SWAP32(fileid);
3028 xah->total_size = SWAP32(ATTR_BUF_SIZE - rsrcforksize);
3029 xah->data_start = SWAP32(sizeof(attr_header_t));
3030
3031 /* Fill in Empty Resource Fork Header. */
3032 init_empty_resource_fork(rsrcforkhdr);
3033
3034 /* Push it out. */
3035 error = VNOP_WRITE(xvp, auio, IO_UNIT, context);
3036
3037 /* Did we write out the full uio? */
3038 if (uio_resid(auio) > 0) {
3039 error = ENOSPC;
3040 }
3041
3042 uio_free(auio);
3043 FREE(buffer, M_TEMP);
3044
3045 return error;
3046 }
3047
3048 static void
3049 init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr)
3050 {
3051 bzero(rsrcforkhdr, sizeof(rsrcfork_header_t));
3052 rsrcforkhdr->fh_DataOffset = SWAP32(RF_FIRST_RESOURCE);
3053 rsrcforkhdr->fh_MapOffset = SWAP32(RF_FIRST_RESOURCE);
3054 rsrcforkhdr->fh_MapLength = SWAP32(RF_NULL_MAP_LENGTH);
3055 rsrcforkhdr->mh_DataOffset = SWAP32(RF_FIRST_RESOURCE);
3056 rsrcforkhdr->mh_MapOffset = SWAP32(RF_FIRST_RESOURCE);
3057 rsrcforkhdr->mh_MapLength = SWAP32(RF_NULL_MAP_LENGTH);
3058 rsrcforkhdr->mh_Types = SWAP16(RF_NULL_MAP_LENGTH - 2 );
3059 rsrcforkhdr->mh_Names = SWAP16(RF_NULL_MAP_LENGTH);
3060 rsrcforkhdr->typeCount = SWAP16(-1);
3061 bcopy(RF_EMPTY_TAG, rsrcforkhdr->systemData, sizeof(RF_EMPTY_TAG));
3062 }
3063
3064 static void
3065 rel_xattrinfo(attr_info_t *ainfop)
3066 {
3067 FREE(ainfop->filehdr, M_TEMP);
3068 bzero(ainfop, sizeof(attr_info_t));
3069 }
3070
3071 static int
3072 write_xattrinfo(attr_info_t *ainfop)
3073 {
3074 uio_t auio;
3075 int error;
3076
3077 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
3078 uio_addiov(auio, (uintptr_t)ainfop->filehdr, ainfop->iosize);
3079
3080 swap_adhdr(ainfop->filehdr);
3081 if (ainfop->attrhdr != NULL) {
3082 swap_attrhdr(ainfop->attrhdr, ainfop);
3083 }
3084
3085 error = VNOP_WRITE(ainfop->filevp, auio, 0, ainfop->context);
3086
3087 swap_adhdr(ainfop->filehdr);
3088 if (ainfop->attrhdr != NULL) {
3089 swap_attrhdr(ainfop->attrhdr, ainfop);
3090 }
3091 uio_free(auio);
3092
3093 return error;
3094 }
3095
3096 #if BYTE_ORDER == LITTLE_ENDIAN
3097 /*
3098 * Endian swap apple double header
3099 */
3100 static void
3101 swap_adhdr(apple_double_header_t *adh)
3102 {
3103 int count;
3104 int i;
3105
3106 count = (adh->magic == ADH_MAGIC) ? adh->numEntries : SWAP16(adh->numEntries);
3107
3108 adh->magic = SWAP32(adh->magic);
3109 adh->version = SWAP32(adh->version);
3110 adh->numEntries = SWAP16(adh->numEntries);
3111
3112 for (i = 0; i < count; i++) {
3113 adh->entries[i].type = SWAP32(adh->entries[i].type);
3114 adh->entries[i].offset = SWAP32(adh->entries[i].offset);
3115 adh->entries[i].length = SWAP32(adh->entries[i].length);
3116 }
3117 }
3118
3119 /*
3120 * Endian swap extended attributes header
3121 */
3122 static void
3123 swap_attrhdr(attr_header_t *ah, attr_info_t* info)
3124 {
3125 attr_entry_t *ae;
3126 int count;
3127 int i;
3128
3129 count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs);
3130
3131 ah->magic = SWAP32(ah->magic);
3132 ah->debug_tag = SWAP32(ah->debug_tag);
3133 ah->total_size = SWAP32(ah->total_size);
3134 ah->data_start = SWAP32(ah->data_start);
3135 ah->data_length = SWAP32(ah->data_length);
3136 ah->flags = SWAP16(ah->flags);
3137 ah->num_attrs = SWAP16(ah->num_attrs);
3138
3139 ae = (attr_entry_t *)(&ah[1]);
3140 for (i = 0; i < count && ATTR_VALID(ae, *info); i++, ae = ATTR_NEXT(ae)) {
3141 ae->offset = SWAP32(ae->offset);
3142 ae->length = SWAP32(ae->length);
3143 ae->flags = SWAP16(ae->flags);
3144 }
3145 }
3146 #endif
3147
3148 /*
3149 * Validate and swap the attributes header contents, and each attribute's
3150 * attr_entry_t.
3151 *
3152 * Note: Assumes the caller has verified that the Finder Info content is large
3153 * enough to contain the attr_header structure itself. Therefore, we can
3154 * swap the header fields before sanity checking them.
3155 */
3156 static int
3157 check_and_swap_attrhdr(attr_header_t *ah, attr_info_t *ainfop)
3158 {
3159 attr_entry_t *ae;
3160 u_int8_t *buf_end;
3161 u_int32_t end;
3162 int count;
3163 int i;
3164
3165 if (ah == NULL) {
3166 return EINVAL;
3167 }
3168
3169 if (SWAP32(ah->magic) != ATTR_HDR_MAGIC) {
3170 return EINVAL;
3171 }
3172
3173 /* Swap the basic header fields */
3174 ah->magic = SWAP32(ah->magic);
3175 ah->debug_tag = SWAP32(ah->debug_tag);
3176 ah->total_size = SWAP32(ah->total_size);
3177 ah->data_start = SWAP32(ah->data_start);
3178 ah->data_length = SWAP32(ah->data_length);
3179 ah->flags = SWAP16(ah->flags);
3180 ah->num_attrs = SWAP16(ah->num_attrs);
3181
3182 /*
3183 * Make sure the total_size fits within the Finder Info area, and the
3184 * extended attribute data area fits within total_size.
3185 */
3186 end = ah->data_start + ah->data_length;
3187 if (ah->total_size > ainfop->finderinfo->offset + ainfop->finderinfo->length ||
3188 end < ah->data_start ||
3189 end > ah->total_size) {
3190 return EINVAL;
3191 }
3192
3193 /*
3194 * Make sure each of the attr_entry_t's fits within total_size.
3195 */
3196 buf_end = ainfop->rawdata + ah->total_size;
3197 count = ah->num_attrs;
3198 ae = (attr_entry_t *)(&ah[1]);
3199
3200 for (i = 0; i < count; i++) {
3201 /* Make sure the fixed-size part of this attr_entry_t fits. */
3202 if ((u_int8_t *) &ae[1] > buf_end) {
3203 return EINVAL;
3204 }
3205
3206 /* Make sure the variable-length name fits (+1 is for NUL terminator) */
3207 /* TODO: Make sure namelen matches strnlen(name,namelen+1)? */
3208 if (&ae->name[ae->namelen + 1] > buf_end) {
3209 return EINVAL;
3210 }
3211
3212 /* Swap the attribute entry fields */
3213 ae->offset = SWAP32(ae->offset);
3214 ae->length = SWAP32(ae->length);
3215 ae->flags = SWAP16(ae->flags);
3216
3217 /* Make sure the attribute content fits. */
3218 end = ae->offset + ae->length;
3219 if (end < ae->offset || end > ah->total_size) {
3220 return EINVAL;
3221 }
3222
3223 ae = ATTR_NEXT(ae);
3224 }
3225
3226 /*
3227 * TODO: Make sure the contents of attributes don't overlap the header
3228 * and don't overlap each other. The hard part is that we don't know
3229 * what the actual header size is until we have looped over all of the
3230 * variable-sized attribute entries.
3231 *
3232 * XXX Is there any guarantee that attribute entries are stored in
3233 * XXX order sorted by the contents' file offset? If so, that would
3234 * XXX make the pairwise overlap check much easier.
3235 */
3236
3237 return 0;
3238 }
3239
3240 //
3241 // "start" & "end" are byte offsets in the file.
3242 // "to" is the byte offset we want to move the
3243 // data to. "to" should be > "start".
3244 //
3245 // we do the copy backwards to avoid problems if
3246 // there's an overlap.
3247 //
3248 static int
3249 shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context)
3250 {
3251 int ret, iolen;
3252 size_t chunk, orig_chunk;
3253 char *buff;
3254 off_t pos;
3255 kauth_cred_t ucred = vfs_context_ucred(context);
3256 proc_t p = vfs_context_proc(context);
3257
3258 if (delta == 0 || len == 0) {
3259 return 0;
3260 }
3261
3262 chunk = 4096;
3263 if (len < chunk) {
3264 chunk = len;
3265 }
3266 orig_chunk = chunk;
3267
3268 if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk, VM_KERN_MEMORY_FILE)) {
3269 return ENOMEM;
3270 }
3271
3272 for (pos = start + len - chunk; pos >= start; pos -= chunk) {
3273 ret = vn_rdwr(UIO_READ, xvp, buff, chunk, pos, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p);
3274 if (iolen != 0) {
3275 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3276 pos, ret, chunk, ret);
3277 break;
3278 }
3279
3280 ret = vn_rdwr(UIO_WRITE, xvp, buff, chunk, pos + delta, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p);
3281 if (iolen != 0) {
3282 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3283 pos + delta, ret, chunk, ret);
3284 break;
3285 }
3286
3287 if ((pos - (off_t)chunk) < start) {
3288 chunk = pos - start;
3289
3290 if (chunk == 0) { // we're all done
3291 break;
3292 }
3293 }
3294 }
3295 kmem_free(kernel_map, (vm_offset_t)buff, orig_chunk);
3296
3297 return 0;
3298 }
3299
3300
3301 static int
3302 shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context)
3303 {
3304 int ret, iolen;
3305 size_t chunk, orig_chunk;
3306 char *buff;
3307 off_t pos;
3308 off_t end;
3309 kauth_cred_t ucred = vfs_context_ucred(context);
3310 proc_t p = vfs_context_proc(context);
3311
3312 if (delta == 0 || len == 0) {
3313 return 0;
3314 }
3315
3316 chunk = 4096;
3317 if (len < chunk) {
3318 chunk = len;
3319 }
3320 orig_chunk = chunk;
3321 end = start + len;
3322
3323 if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk, VM_KERN_MEMORY_FILE)) {
3324 return ENOMEM;
3325 }
3326
3327 for (pos = start; pos < end; pos += chunk) {
3328 ret = vn_rdwr(UIO_READ, xvp, buff, chunk, pos, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p);
3329 if (iolen != 0) {
3330 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
3331 pos, ret, chunk, ret);
3332 break;
3333 }
3334
3335 ret = vn_rdwr(UIO_WRITE, xvp, buff, chunk, pos - delta, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p);
3336 if (iolen != 0) {
3337 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
3338 pos + delta, ret, chunk, ret);
3339 break;
3340 }
3341
3342 if ((pos + (off_t)chunk) > end) {
3343 chunk = end - pos;
3344
3345 if (chunk == 0) { // we're all done
3346 break;
3347 }
3348 }
3349 }
3350 kmem_free(kernel_map, (vm_offset_t)buff, orig_chunk);
3351
3352 return 0;
3353 }
3354
3355 static int
3356 lock_xattrfile(vnode_t xvp, short locktype, vfs_context_t context)
3357 {
3358 struct flock lf;
3359 int error;
3360
3361 lf.l_whence = SEEK_SET;
3362 lf.l_start = 0;
3363 lf.l_len = 0;
3364 lf.l_type = locktype; /* F_WRLCK or F_RDLCK */
3365 /* Note: id is just a kernel address that's not a proc */
3366 error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_SETLK, &lf, F_FLOCK | F_WAIT, context, NULL);
3367 return error == ENOTSUP ? 0 : error;
3368 }
3369
3370 int
3371 unlock_xattrfile(vnode_t xvp, vfs_context_t context)
3372 {
3373 struct flock lf;
3374 int error;
3375
3376 lf.l_whence = SEEK_SET;
3377 lf.l_start = 0;
3378 lf.l_len = 0;
3379 lf.l_type = F_UNLCK;
3380 /* Note: id is just a kernel address that's not a proc */
3381 error = VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_UNLCK, &lf, F_FLOCK, context, NULL);
3382 return error == ENOTSUP ? 0 : error;
3383 }
3384
3385 #else /* CONFIG_APPLEDOUBLE */
3386
3387
3388 static int
3389 default_getxattr(__unused vnode_t vp, __unused const char *name,
3390 __unused uio_t uio, __unused size_t *size, __unused int options,
3391 __unused vfs_context_t context)
3392 {
3393 return ENOTSUP;
3394 }
3395
3396 static int
3397 default_setxattr(__unused vnode_t vp, __unused const char *name,
3398 __unused uio_t uio, __unused int options, __unused vfs_context_t context)
3399 {
3400 return ENOTSUP;
3401 }
3402
3403 static int
3404 default_listxattr(__unused vnode_t vp,
3405 __unused uio_t uio, __unused size_t *size, __unused int options,
3406 __unused vfs_context_t context)
3407 {
3408 return ENOTSUP;
3409 }
3410
3411 static int
3412 default_removexattr(__unused vnode_t vp, __unused const char *name,
3413 __unused int options, __unused vfs_context_t context)
3414 {
3415 return ENOTSUP;
3416 }
3417
3418 #endif /* CONFIG_APPLEDOUBLE */