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