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