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