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