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