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