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