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