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