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