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