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