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