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