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