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