1 /*
2 * Copyright (c) 2004-2012 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/file_internal.h>
39 #include <sys/fsevents.h>
40 #include <sys/kernel.h>
41 #include <sys/kauth.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 <kern/kalloc.h>
53 #include <kern/kern_types.h>
54 #include <kern/host.h>
55 #include <kern/ipc_misc.h>
56
57 #include <mach/doubleagent_mig_server.h>
58 #include <mach/doubleagent_types.h>
59 #include <mach/host_priv.h>
60 #include <mach/host_special_ports.h>
61
62 #include <libkern/OSByteOrder.h>
63 #include <vm/vm_kern.h>
64 #include <vm/vm_protos.h> /* XXX for ipc_port_release_send() */
65
66 #if CONFIG_MACF
67 #include <security/mac_framework.h>
68 #endif
69
70
71 #if NAMEDSTREAMS
72
73 static int shadow_sequence;
74
75 /*
76 * We use %p to prevent loss of precision for pointers on varying architectures.
77 */
78
79 #define SHADOW_NAME_FMT ".vfs_rsrc_stream_%p%08x%p"
80 #define SHADOW_DIR_FMT ".vfs_rsrc_streams_%p%x"
81 #define SHADOW_DIR_CONTAINER "/var/run"
82
83 #define MAKE_SHADOW_NAME(VP, NAME) \
84 snprintf((NAME), sizeof((NAME)), (SHADOW_NAME_FMT), \
85 ((void*)(VM_KERNEL_ADDRPERM(VP))), \
86 (VP)->v_id, \
87 ((void*)(VM_KERNEL_ADDRPERM((VP)->v_data))))
88
89 /* The full path to the shadow directory */
90 #define MAKE_SHADOW_DIRNAME(VP, NAME) \
91 snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_CONTAINER "/" SHADOW_DIR_FMT), \
92 ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence)
93
94 /* The shadow directory as a 'leaf' entry */
95 #define MAKE_SHADOW_DIR_LEAF(VP, NAME) \
96 snprintf((NAME), sizeof((NAME)), (SHADOW_DIR_FMT), \
97 ((void*)(VM_KERNEL_ADDRPERM(VP))), shadow_sequence)
98
99 static int default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context);
100
101 static int default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context);
102
103 static int default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context);
104
105 static int getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, int *creator, vfs_context_t context);
106
107 static int get_shadow_dir(vnode_t *sdvpp);
108
109 #endif /* NAMEDSTREAMS */
110
111 /*
112 * Default xattr support routines.
113 */
114
115 static int default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size, int options,
116 vfs_context_t context);
117 static int default_setxattr(vnode_t vp, const char *name, uio_t uio, int options,
118 vfs_context_t context);
119 static int default_listxattr(vnode_t vp, uio_t uio, size_t *size, int options,
120 vfs_context_t context);
121 static int default_removexattr(vnode_t vp, const char *name, int options,
122 vfs_context_t context);
123
124 /*
125 * Retrieve the data of an extended attribute.
126 */
127 int
vn_getxattr(vnode_t vp,const char * name,uio_t uio,size_t * size,int options,vfs_context_t context)128 vn_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size,
129 int options, vfs_context_t context)
130 {
131 int error;
132
133 if (!XATTR_VNODE_SUPPORTED(vp)) {
134 return EPERM;
135 }
136 #if NAMEDSTREAMS
137 /* getxattr calls are not allowed for streams. */
138 if (vp->v_flag & VISNAMEDSTREAM) {
139 error = EPERM;
140 goto out;
141 }
142 #endif
143 /*
144 * Non-kernel request need extra checks performed.
145 *
146 * The XATTR_NOSECURITY flag implies a kernel request.
147 */
148 if (!(options & XATTR_NOSECURITY)) {
149 #if CONFIG_MACF
150 error = mac_vnode_check_getextattr(context, vp, name, uio);
151 if (error) {
152 goto out;
153 }
154 #endif /* MAC */
155 if ((error = xattr_validatename(name))) {
156 goto out;
157 }
158 if ((error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context))) {
159 goto out;
160 }
161 }
162
163 /* The offset can only be non-zero for resource forks. */
164 if (uio_offset(uio) != 0 &&
165 strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
166 error = EINVAL;
167 goto out;
168 }
169
170 error = VNOP_GETXATTR(vp, name, uio, size, options, context);
171 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
172 /*
173 * A filesystem may keep some EAs natively and return ENOTSUP for others.
174 */
175 error = default_getxattr(vp, name, uio, size, options, context);
176 }
177 out:
178 return error;
179 }
180
181 /*
182 * Set the data of an extended attribute.
183 */
184 int
vn_setxattr(vnode_t vp,const char * name,uio_t uio,int options,vfs_context_t context)185 vn_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context)
186 {
187 int error;
188
189 if (!XATTR_VNODE_SUPPORTED(vp)) {
190 return EPERM;
191 }
192 #if NAMEDSTREAMS
193 /* setxattr calls are not allowed for streams. */
194 if (vp->v_flag & VISNAMEDSTREAM) {
195 error = EPERM;
196 goto out;
197 }
198 #endif
199 if ((options & (XATTR_REPLACE | XATTR_CREATE)) == (XATTR_REPLACE | XATTR_CREATE)) {
200 return EINVAL;
201 }
202 if ((error = xattr_validatename(name))) {
203 return error;
204 }
205 if (!(options & XATTR_NOSECURITY)) {
206 #if CONFIG_MACF
207 error = mac_vnode_check_setextattr(context, vp, name, uio);
208 if (error) {
209 goto out;
210 }
211 #endif /* MAC */
212 error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context);
213 if (error) {
214 goto out;
215 }
216 }
217 /* The offset can only be non-zero for resource forks. */
218 if (uio_offset(uio) != 0 &&
219 strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
220 error = EINVAL;
221 goto out;
222 }
223
224 error = VNOP_SETXATTR(vp, name, uio, options, context);
225 #ifdef DUAL_EAS
226 /*
227 * An EJUSTRETURN is from a filesystem which keeps this xattr
228 * natively as well as in a dot-underscore file. In this case the
229 * EJUSTRETURN means the filesytem has done nothing, but identifies the
230 * EA as one which may be represented natively and/or in a DU, and
231 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
232 * in vn_setxattr can we do the getxattrs needed to ascertain whether
233 * the XATTR_{CREATE,REPLACE} should yield an error.
234 */
235 if (error == EJUSTRETURN) {
236 int native = 0, dufile = 0;
237 size_t sz; /* not used */
238
239 native = VNOP_GETXATTR(vp, name, NULL, &sz, 0, context) ? 0 : 1;
240 dufile = default_getxattr(vp, name, NULL, &sz, 0, context) ? 0 : 1;
241 if (options & XATTR_CREATE && (native || dufile)) {
242 error = EEXIST;
243 goto out;
244 }
245 if (options & XATTR_REPLACE && !(native || dufile)) {
246 error = ENOATTR;
247 goto out;
248 }
249 /*
250 * Having determined no CREATE/REPLACE error should result, we
251 * zero those bits, so both backing stores get written to.
252 */
253 options &= ~(XATTR_CREATE | XATTR_REPLACE);
254 error = VNOP_SETXATTR(vp, name, uio, options, context);
255 /* the mainline path here is to have error==ENOTSUP ... */
256 }
257 #endif /* DUAL_EAS */
258 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
259 /*
260 * A filesystem may keep some EAs natively and return ENOTSUP for others.
261 */
262 error = default_setxattr(vp, name, uio, options, context);
263 }
264 #if CONFIG_MACF
265 if ((error == 0) && !(options & XATTR_NOSECURITY)) {
266 mac_vnode_notify_setextattr(context, vp, name, uio);
267 if (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL) {
268 mac_vnode_label_update_extattr(vnode_mount(vp), vp, name);
269 }
270 }
271 #endif
272 out:
273 return error;
274 }
275
276 /*
277 * Remove an extended attribute.
278 */
279 int
vn_removexattr(vnode_t vp,const char * name,int options,vfs_context_t context)280 vn_removexattr(vnode_t vp, const char * name, int options, vfs_context_t context)
281 {
282 int error;
283
284 if (!XATTR_VNODE_SUPPORTED(vp)) {
285 return EPERM;
286 }
287 #if NAMEDSTREAMS
288 /* removexattr calls are not allowed for streams. */
289 if (vp->v_flag & VISNAMEDSTREAM) {
290 error = EPERM;
291 goto out;
292 }
293 #endif
294 if ((error = xattr_validatename(name))) {
295 return error;
296 }
297 if (!(options & XATTR_NOSECURITY)) {
298 #if CONFIG_MACF
299 error = mac_vnode_check_deleteextattr(context, vp, name);
300 if (error) {
301 goto out;
302 }
303 #endif /* MAC */
304 error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context);
305 if (error) {
306 goto out;
307 }
308 }
309 error = VNOP_REMOVEXATTR(vp, name, options, context);
310 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
311 /*
312 * A filesystem may keep some EAs natively and return ENOTSUP for others.
313 */
314 error = default_removexattr(vp, name, options, context);
315 #ifdef DUAL_EAS
316 } else if (error == EJUSTRETURN) {
317 /*
318 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
319 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
320 * a native xattr, so failure to find it in a DU file during
321 * default_removexattr should not be considered an error.
322 */
323 error = default_removexattr(vp, name, options, context);
324 if (error == ENOATTR) {
325 error = 0;
326 }
327 #endif /* DUAL_EAS */
328 }
329 #if CONFIG_MACF
330 if ((error == 0) && !(options & XATTR_NOSECURITY)) {
331 mac_vnode_notify_deleteextattr(context, vp, name);
332 if (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL) {
333 mac_vnode_label_update_extattr(vnode_mount(vp), vp, name);
334 }
335 }
336 #endif
337 out:
338 return error;
339 }
340
341 /*
342 * Retrieve the list of extended attribute names.
343 */
344 int
vn_listxattr(vnode_t vp,uio_t uio,size_t * size,int options,vfs_context_t context)345 vn_listxattr(vnode_t vp, uio_t uio, size_t *size, int options, vfs_context_t context)
346 {
347 int error;
348
349 if (!XATTR_VNODE_SUPPORTED(vp)) {
350 return EPERM;
351 }
352 #if NAMEDSTREAMS
353 /* listxattr calls are not allowed for streams. */
354 if (vp->v_flag & VISNAMEDSTREAM) {
355 return EPERM;
356 }
357 #endif
358
359 if (!(options & XATTR_NOSECURITY)) {
360 #if CONFIG_MACF
361 error = mac_vnode_check_listextattr(context, vp);
362 if (error) {
363 goto out;
364 }
365 #endif /* MAC */
366
367 error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context);
368 if (error) {
369 goto out;
370 }
371 }
372
373 error = VNOP_LISTXATTR(vp, uio, size, options, context);
374 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
375 /*
376 * A filesystem may keep some but not all EAs natively, in which case
377 * the native EA names will have been uiomove-d out (or *size updated)
378 * and the default_listxattr here will finish the job.
379 */
380 error = default_listxattr(vp, uio, size, options, context);
381 }
382 out:
383 return error;
384 }
385
386 int
xattr_validatename(const char * name)387 xattr_validatename(const char *name)
388 {
389 size_t namelen;
390
391 if (name == NULL || name[0] == '\0') {
392 return EINVAL;
393 }
394 namelen = strlen(name);
395
396 if (utf8_validatestr((const unsigned char *)name, namelen) != 0) {
397 return EINVAL;
398 }
399
400 return 0;
401 }
402
403
404 /*
405 * Determine whether an EA is a protected system attribute.
406 */
407 int
xattr_protected(const char * attrname)408 xattr_protected(const char *attrname)
409 {
410 return !strncmp(attrname, "com.apple.system.", 17);
411 }
412
413
414 static void
vnode_setasnamedstream_internal(vnode_t vp,vnode_t svp)415 vnode_setasnamedstream_internal(vnode_t vp, vnode_t svp)
416 {
417 uint32_t streamflags = VISNAMEDSTREAM;
418
419 if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) {
420 streamflags |= VISSHADOW;
421 }
422
423 /* Tag the vnode. */
424 vnode_lock_spin(svp);
425 svp->v_flag |= streamflags;
426 vnode_unlock(svp);
427
428 /* Tag the parent so we know to flush credentials for streams on setattr */
429 vnode_lock_spin(vp);
430 vp->v_lflag |= VL_HASSTREAMS;
431 vnode_unlock(vp);
432
433 /* Make the file it's parent.
434 * Note: This parent link helps us distinguish vnodes for
435 * shadow stream files from vnodes for resource fork on file
436 * systems that support namedstream natively (both have
437 * VISNAMEDSTREAM set) by allowing access to mount structure
438 * for checking MNTK_NAMED_STREAMS bit at many places in the
439 * code.
440 */
441 vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_NAMEDSTREAM_PARENT);
442
443 if (vnode_isdyldsharedcache(vp)) {
444 vnode_lock_spin(svp);
445 svp->v_flag |= VSHARED_DYLD;
446 vnode_unlock(svp);
447 }
448
449 return;
450 }
451
452 errno_t
vnode_setasnamedstream(vnode_t vp,vnode_t svp)453 vnode_setasnamedstream(vnode_t vp, vnode_t svp)
454 {
455 if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) {
456 return EINVAL;
457 }
458
459 vnode_setasnamedstream_internal(vp, svp);
460 return 0;
461 }
462
463 #if NAMEDSTREAMS
464
465 /*
466 * Obtain a named stream from vnode vp.
467 */
468 errno_t
vnode_getnamedstream(vnode_t vp,vnode_t * svpp,const char * name,enum nsoperation op,int flags,vfs_context_t context)469 vnode_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, int flags, vfs_context_t context)
470 {
471 int error;
472
473 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) {
474 error = VNOP_GETNAMEDSTREAM(vp, svpp, name, op, flags, context);
475 } else {
476 if (flags) {
477 error = ENOTSUP;
478 } else {
479 error = default_getnamedstream(vp, svpp, name, op, context);
480 }
481 }
482
483 if (error == 0) {
484 vnode_setasnamedstream_internal(vp, *svpp);
485 }
486
487 return error;
488 }
489
490 /*
491 * Make a named stream for vnode vp.
492 */
493 errno_t
vnode_makenamedstream(vnode_t vp,vnode_t * svpp,const char * name,int flags,vfs_context_t context)494 vnode_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, int flags, vfs_context_t context)
495 {
496 int error;
497
498 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) {
499 error = VNOP_MAKENAMEDSTREAM(vp, svpp, name, flags, context);
500 } else {
501 error = default_makenamedstream(vp, svpp, name, context);
502 }
503
504 if (error == 0) {
505 vnode_setasnamedstream_internal(vp, *svpp);
506 }
507
508 return error;
509 }
510
511 /*
512 * Remove a named stream from vnode vp.
513 */
514 errno_t
vnode_removenamedstream(vnode_t vp,vnode_t svp,const char * name,int flags,vfs_context_t context)515 vnode_removenamedstream(vnode_t vp, vnode_t svp, const char *name, int flags, vfs_context_t context)
516 {
517 int error;
518
519 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) {
520 error = VNOP_REMOVENAMEDSTREAM(vp, svp, name, flags, context);
521 } else {
522 error = default_removenamedstream(vp, name, context);
523 }
524
525 return error;
526 }
527
528 #define NS_IOBUFSIZE (128 * 1024)
529
530 /*
531 * Release a named stream shadow file.
532 *
533 * Note: This function is called from two places where we do not need
534 * to check if the vnode has any references held before deleting the
535 * shadow file. Once from vclean() when the vnode is being reclaimed
536 * and we do not hold any references on the vnode. Second time from
537 * default_getnamedstream() when we get an error during shadow stream
538 * file initialization so that other processes who are waiting for the
539 * shadow stream file initialization by the creator will get opportunity
540 * to create and initialize the file again.
541 */
542 errno_t
vnode_relenamedstream(vnode_t vp,vnode_t svp)543 vnode_relenamedstream(vnode_t vp, vnode_t svp)
544 {
545 vnode_t dvp;
546 struct componentname cn;
547 char tmpname[80];
548 errno_t err;
549
550 /*
551 * We need to use the kernel context here. If we used the supplied
552 * VFS context we have no clue whether or not it originated from userland
553 * where it could be subject to a chroot jail. We need to ensure that all
554 * filesystem access to shadow files is done on the same FS regardless of
555 * userland process restrictions.
556 */
557 vfs_context_t kernelctx = vfs_context_kernel();
558
559 cache_purge(svp);
560
561 vnode_lock(svp);
562 MAKE_SHADOW_NAME(vp, tmpname);
563 vnode_unlock(svp);
564
565 cn.cn_nameiop = DELETE;
566 cn.cn_flags = ISLASTCN;
567 cn.cn_context = kernelctx;
568 cn.cn_pnbuf = tmpname;
569 cn.cn_pnlen = sizeof(tmpname);
570 cn.cn_nameptr = cn.cn_pnbuf;
571 cn.cn_namelen = (int)strlen(tmpname);
572
573 /*
574 * Obtain the vnode for the shadow files directory. Make sure to
575 * use the kernel ctx as described above.
576 */
577 err = get_shadow_dir(&dvp);
578 if (err != 0) {
579 return err;
580 }
581
582 (void) VNOP_REMOVE(dvp, svp, &cn, 0, kernelctx);
583 vnode_put(dvp);
584
585 return 0;
586 }
587
588 /*
589 * Flush a named stream shadow file.
590 *
591 * 'vp' represents the AppleDouble file.
592 * 'svp' represents the shadow file.
593 */
594 errno_t
vnode_flushnamedstream(vnode_t vp,vnode_t svp,vfs_context_t context)595 vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context)
596 {
597 struct vnode_attr va;
598 uio_t auio = NULL;
599 caddr_t bufptr = NULL;
600 size_t bufsize = 0;
601 size_t offset;
602 size_t iosize;
603 size_t datasize;
604 int error;
605 /*
606 * The kernel context must be used for all I/O to the shadow file
607 * and its namespace operations
608 */
609 vfs_context_t kernelctx = vfs_context_kernel();
610
611 /* The supplied context is used for access to the AD file itself */
612
613 VATTR_INIT(&va);
614 VATTR_WANTED(&va, va_data_size);
615 if (VNOP_GETATTR(svp, &va, context) != 0 ||
616 !VATTR_IS_SUPPORTED(&va, va_data_size)) {
617 return 0;
618 }
619 if (va.va_data_size > UINT32_MAX) {
620 return EINVAL;
621 }
622 datasize = (size_t)va.va_data_size;
623 if (datasize == 0) {
624 (void) default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
625 return 0;
626 }
627
628 iosize = bufsize = MIN(datasize, NS_IOBUFSIZE);
629 bufptr = kalloc_data(bufsize, Z_WAITOK);
630 if (bufptr == NULL) {
631 return ENOMEM;
632 }
633 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
634 offset = 0;
635
636 /*
637 * Copy the shadow stream file data into the resource fork.
638 */
639 error = VNOP_OPEN(svp, 0, kernelctx);
640 if (error) {
641 printf("vnode_flushnamedstream: err %d opening file\n", error);
642 goto out;
643 }
644 while (offset < datasize) {
645 iosize = MIN(datasize - offset, iosize);
646
647 uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ);
648 uio_addiov(auio, (uintptr_t)bufptr, iosize);
649 error = VNOP_READ(svp, auio, 0, kernelctx);
650 if (error) {
651 break;
652 }
653 /* Since there's no truncate xattr we must remove the resource fork. */
654 if (offset == 0) {
655 error = default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
656 if ((error != 0) && (error != ENOATTR)) {
657 break;
658 }
659 }
660 uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE);
661 uio_addiov(auio, (uintptr_t)bufptr, iosize);
662 error = vn_setxattr(vp, XATTR_RESOURCEFORK_NAME, auio, XATTR_NOSECURITY, context);
663 if (error) {
664 break;
665 }
666 offset += iosize;
667 }
668
669 /* close shadowfile */
670 (void) VNOP_CLOSE(svp, 0, kernelctx);
671 out:
672 kfree_data(bufptr, bufsize);
673 if (auio) {
674 uio_free(auio);
675 }
676 return error;
677 }
678
679
680 /*
681 * Verify that the vnode 'vp' is a vnode that lives in the shadow
682 * directory. We can't just query the parent pointer directly since
683 * the shadowfile is hooked up to the actual file it's a stream for.
684 */
685 errno_t
vnode_verifynamedstream(vnode_t vp)686 vnode_verifynamedstream(vnode_t vp)
687 {
688 int error;
689 struct vnode *shadow_dvp = NULL;
690 struct vnode *shadowfile = NULL;
691 struct componentname cn;
692
693 /*
694 * We need to use the kernel context here. If we used the supplied
695 * VFS context we have no clue whether or not it originated from userland
696 * where it could be subject to a chroot jail. We need to ensure that all
697 * filesystem access to shadow files is done on the same FS regardless of
698 * userland process restrictions.
699 */
700 vfs_context_t kernelctx = vfs_context_kernel();
701 char tmpname[80];
702
703
704 /* Get the shadow directory vnode */
705 error = get_shadow_dir(&shadow_dvp);
706 if (error) {
707 return error;
708 }
709
710 /* Re-generate the shadow name in the buffer */
711 MAKE_SHADOW_NAME(vp, tmpname);
712
713 /* Look up item in shadow dir */
714 bzero(&cn, sizeof(cn));
715 cn.cn_nameiop = LOOKUP;
716 cn.cn_flags = ISLASTCN | CN_ALLOWRSRCFORK;
717 cn.cn_context = kernelctx;
718 cn.cn_pnbuf = tmpname;
719 cn.cn_pnlen = sizeof(tmpname);
720 cn.cn_nameptr = cn.cn_pnbuf;
721 cn.cn_namelen = (int)strlen(tmpname);
722
723 if (VNOP_LOOKUP(shadow_dvp, &shadowfile, &cn, kernelctx) == 0) {
724 /* is the pointer the same? */
725 if (shadowfile == vp) {
726 error = 0;
727 } else {
728 error = EPERM;
729 }
730 /* drop the iocount acquired */
731 vnode_put(shadowfile);
732 }
733
734 /* Drop iocount on shadow dir */
735 vnode_put(shadow_dvp);
736 return error;
737 }
738
739 /*
740 * Access or create the shadow file as needed.
741 *
742 * 'makestream' with non-zero value means that we need to guarantee we were the
743 * creator of the shadow file.
744 *
745 * 'context' is the user supplied context for the original VFS operation that
746 * caused us to need a shadow file.
747 *
748 * int pointed to by 'creator' is nonzero if we created the shadowfile.
749 */
750 static int
getshadowfile(vnode_t vp,vnode_t * svpp,int makestream,size_t * rsrcsize,int * creator,vfs_context_t context)751 getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize,
752 int *creator, vfs_context_t context)
753 {
754 vnode_t dvp = NULLVP;
755 vnode_t svp = NULLVP;
756 struct componentname cn;
757 struct vnode_attr va;
758 char tmpname[80];
759 size_t datasize = 0;
760 int error = 0;
761 int retries = 0;
762 vfs_context_t kernelctx = vfs_context_kernel();
763
764 retry_create:
765 *creator = 0;
766 /* Establish a unique file name. */
767 MAKE_SHADOW_NAME(vp, tmpname);
768 bzero(&cn, sizeof(cn));
769 cn.cn_nameiop = LOOKUP;
770 cn.cn_flags = ISLASTCN;
771 cn.cn_context = context;
772 cn.cn_pnbuf = tmpname;
773 cn.cn_pnlen = sizeof(tmpname);
774 cn.cn_nameptr = cn.cn_pnbuf;
775 cn.cn_namelen = (int)strlen(tmpname);
776
777 /* Pick up uid, gid, mode and date from original file. */
778 VATTR_INIT(&va);
779 VATTR_WANTED(&va, va_uid);
780 VATTR_WANTED(&va, va_gid);
781 VATTR_WANTED(&va, va_mode);
782 VATTR_WANTED(&va, va_create_time);
783 VATTR_WANTED(&va, va_modify_time);
784 if (VNOP_GETATTR(vp, &va, context) != 0 ||
785 !VATTR_IS_SUPPORTED(&va, va_uid) ||
786 !VATTR_IS_SUPPORTED(&va, va_gid) ||
787 !VATTR_IS_SUPPORTED(&va, va_mode)) {
788 va.va_uid = KAUTH_UID_NONE;
789 va.va_gid = KAUTH_GID_NONE;
790 va.va_mode = S_IRUSR | S_IWUSR;
791 }
792 va.va_vaflags = VA_EXCLUSIVE;
793 VATTR_SET(&va, va_type, VREG);
794 /* We no longer change the access, but we still hide it. */
795 VATTR_SET(&va, va_flags, UF_HIDDEN);
796
797 /* Obtain the vnode for the shadow files directory. */
798 if (get_shadow_dir(&dvp) != 0) {
799 error = ENOTDIR;
800 goto out;
801 }
802 if (!makestream) {
803 /* See if someone else already has it open. */
804 if (VNOP_LOOKUP(dvp, &svp, &cn, kernelctx) == 0) {
805 /* Double check existence by asking for size. */
806 VATTR_INIT(&va);
807 VATTR_WANTED(&va, va_data_size);
808 if (VNOP_GETATTR(svp, &va, context) == 0 &&
809 VATTR_IS_SUPPORTED(&va, va_data_size)) {
810 goto out; /* OK to use. */
811 }
812 }
813
814 /*
815 * Otherwise make sure the resource fork data exists.
816 * Use the supplied context for accessing the AD file.
817 */
818 error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &datasize,
819 XATTR_NOSECURITY, context);
820 /*
821 * To maintain binary compatibility with legacy Carbon
822 * emulated resource fork support, if the resource fork
823 * doesn't exist but the Finder Info does, then act as
824 * if an empty resource fork is present (see 4724359).
825 */
826 if ((error == ENOATTR) &&
827 (vn_getxattr(vp, XATTR_FINDERINFO_NAME, NULL, &datasize,
828 XATTR_NOSECURITY, context) == 0)) {
829 datasize = 0;
830 error = 0;
831 } else {
832 if (error) {
833 goto out;
834 }
835
836 /* If the resource fork exists, its size is expected to be non-zero. */
837 if (datasize == 0) {
838 error = ENOATTR;
839 goto out;
840 }
841 }
842 }
843 /* Create the shadow stream file. */
844 error = VNOP_CREATE(dvp, &svp, &cn, &va, kernelctx);
845 if (error == 0) {
846 vnode_recycle(svp);
847 *creator = 1;
848 } else if ((error == EEXIST) && !makestream) {
849 error = VNOP_LOOKUP(dvp, &svp, &cn, kernelctx);
850 } else if ((error == ENOENT) && !makestream) {
851 /*
852 * We could have raced with a rmdir on the shadow directory
853 * post-lookup. Retry from the beginning, 1x only, to
854 * try and see if we need to re-create the shadow directory
855 * in get_shadow_dir.
856 */
857 if (retries == 0) {
858 retries++;
859 if (dvp) {
860 vnode_put(dvp);
861 dvp = NULLVP;
862 }
863 if (svp) {
864 vnode_put(svp);
865 svp = NULLVP;
866 }
867 goto retry_create;
868 }
869 /* Otherwise, just error out normally below */
870 }
871
872 out:
873 if (dvp) {
874 vnode_put(dvp);
875 }
876 if (error) {
877 /* On errors, clean up shadow stream file. */
878 if (svp) {
879 vnode_put(svp);
880 svp = NULLVP;
881 }
882 }
883 *svpp = svp;
884 if (rsrcsize) {
885 *rsrcsize = datasize;
886 }
887 return error;
888 }
889
890
891 static int
default_getnamedstream(vnode_t vp,vnode_t * svpp,const char * name,enum nsoperation op,vfs_context_t context)892 default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context)
893 {
894 vnode_t svp = NULLVP;
895 uio_t auio = NULL;
896 caddr_t bufptr = NULL;
897 size_t bufsize = 0;
898 size_t datasize = 0;
899 int creator;
900 int error;
901
902 /* need the kernel context for accessing the shadowfile */
903 vfs_context_t kernelctx = vfs_context_kernel();
904
905 /*
906 * Only the "com.apple.ResourceFork" stream is supported here.
907 */
908 if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
909 *svpp = NULLVP;
910 return ENOATTR;
911 }
912 retry:
913 /*
914 * Obtain a shadow file for the resource fork I/O.
915 *
916 * Need to pass along the supplied context so that getshadowfile
917 * can access the AD file as needed, using it.
918 */
919 error = getshadowfile(vp, &svp, 0, &datasize, &creator, context);
920 if (error) {
921 *svpp = NULLVP;
922 return error;
923 }
924
925 /*
926 * The creator of the shadow file provides its file data,
927 * all other threads should wait until its ready. In order to
928 * prevent a deadlock during error codepaths, we need to check if the
929 * vnode is being created, or if it has failed out. Regardless of success or
930 * failure, we set the VISSHADOW bit on the vnode, so we check that
931 * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
932 * then we can infer the creator isn't done yet. If it's there, but
933 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
934 * try again.
935 */
936 if (!creator) {
937 vnode_lock(svp);
938 if (svp->v_flag & VISNAMEDSTREAM) {
939 /* data is ready, go use it */
940 vnode_unlock(svp);
941 goto out;
942 } else {
943 /* It's not ready, wait for it (sleep using v_parent as channel) */
944 if ((svp->v_flag & VISSHADOW)) {
945 /*
946 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
947 * thread is done with this vnode. Just unlock the vnode and try again
948 */
949 vnode_unlock(svp);
950 } else {
951 /* Otherwise, sleep if the shadow file is not created yet */
952 msleep((caddr_t)&svp->v_parent, &svp->v_lock, PINOD | PDROP,
953 "getnamedstream", NULL);
954 }
955 vnode_put(svp);
956 svp = NULLVP;
957 goto retry;
958 }
959 }
960
961 /*
962 * Copy the real resource fork data into shadow stream file.
963 */
964 if (op == NS_OPEN && datasize != 0) {
965 size_t offset;
966 size_t iosize;
967
968 iosize = bufsize = MIN(datasize, NS_IOBUFSIZE);
969 bufptr = kalloc_data(bufsize, Z_WAITOK);
970 if (bufptr == NULL) {
971 error = ENOMEM;
972 goto out;
973 }
974
975 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
976 offset = 0;
977
978 /* open the shadow file */
979 error = VNOP_OPEN(svp, 0, kernelctx);
980 if (error) {
981 goto out;
982 }
983 while (offset < datasize) {
984 size_t tmpsize;
985
986 iosize = MIN(datasize - offset, iosize);
987
988 uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ);
989 uio_addiov(auio, (uintptr_t)bufptr, iosize);
990 /* use supplied ctx for AD file */
991 error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, auio, &tmpsize,
992 XATTR_NOSECURITY, context);
993 if (error) {
994 break;
995 }
996
997 uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE);
998 uio_addiov(auio, (uintptr_t)bufptr, iosize);
999 /* kernel context for writing shadowfile */
1000 error = VNOP_WRITE(svp, auio, 0, kernelctx);
1001 if (error) {
1002 break;
1003 }
1004 offset += iosize;
1005 }
1006
1007 /* close shadow file */
1008 (void) VNOP_CLOSE(svp, 0, kernelctx);
1009 }
1010 out:
1011 /* Wake up anyone waiting for svp file content */
1012 if (creator) {
1013 if (error == 0) {
1014 vnode_lock(svp);
1015 /* VISSHADOW would be set later on anyway, so we set it now */
1016 svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW);
1017 wakeup((caddr_t)&svp->v_parent);
1018 vnode_unlock(svp);
1019 } else {
1020 /* On post create errors, get rid of the shadow file. This
1021 * way if there is another process waiting for initialization
1022 * of the shadowfile by the current process will wake up and
1023 * retry by creating and initializing the shadow file again.
1024 * Also add the VISSHADOW bit here to indicate we're done operating
1025 * on this vnode.
1026 */
1027 (void)vnode_relenamedstream(vp, svp);
1028 vnode_lock(svp);
1029 svp->v_flag |= VISSHADOW;
1030 wakeup((caddr_t)&svp->v_parent);
1031 vnode_unlock(svp);
1032 }
1033 }
1034
1035 kfree_data(bufptr, bufsize);
1036 if (auio) {
1037 uio_free(auio);
1038 }
1039 if (error) {
1040 /* On errors, clean up shadow stream file. */
1041 if (svp) {
1042 vnode_put(svp);
1043 svp = NULLVP;
1044 }
1045 }
1046 *svpp = svp;
1047 return error;
1048 }
1049
1050 static int
default_makenamedstream(vnode_t vp,vnode_t * svpp,const char * name,vfs_context_t context)1051 default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context)
1052 {
1053 int creator;
1054 int error;
1055
1056 /*
1057 * Only the "com.apple.ResourceFork" stream is supported here.
1058 */
1059 if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
1060 *svpp = NULLVP;
1061 return ENOATTR;
1062 }
1063
1064 /* Supply the context to getshadowfile so it can manipulate the AD file */
1065 error = getshadowfile(vp, svpp, 1, NULL, &creator, context);
1066
1067 /*
1068 * Wake up any waiters over in default_getnamedstream().
1069 */
1070 if ((error == 0) && (*svpp != NULL) && creator) {
1071 vnode_t svp = *svpp;
1072
1073 vnode_lock(svp);
1074 /* If we're the creator, mark it as a named stream */
1075 svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW);
1076 /* Wakeup any waiters on the v_parent channel */
1077 wakeup((caddr_t)&svp->v_parent);
1078 vnode_unlock(svp);
1079 }
1080
1081 return error;
1082 }
1083
1084 static int
default_removenamedstream(vnode_t vp,const char * name,vfs_context_t context)1085 default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context)
1086 {
1087 /*
1088 * Only the "com.apple.ResourceFork" stream is supported here.
1089 */
1090 if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
1091 return ENOATTR;
1092 }
1093 /*
1094 * XXX - what about other opened instances?
1095 */
1096 return default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
1097 }
1098
1099 static int
get_shadow_dir(vnode_t * sdvpp)1100 get_shadow_dir(vnode_t *sdvpp)
1101 {
1102 vnode_t dvp = NULLVP;
1103 vnode_t sdvp = NULLVP;
1104 struct componentname cn;
1105 struct vnode_attr va;
1106 char tmpname[80];
1107 uint32_t tmp_fsid;
1108 int error;
1109 vfs_context_t kernelctx = vfs_context_kernel();
1110
1111 bzero(tmpname, sizeof(tmpname));
1112 MAKE_SHADOW_DIRNAME(rootvnode, tmpname);
1113 /*
1114 * Look up the shadow directory to ensure that it still exists.
1115 * By looking it up, we get an iocounted dvp to use, and avoid some coherency issues
1116 * in caching it when multiple threads may be trying to manipulate the pointers.
1117 *
1118 * Make sure to use the kernel context. We want a singular view of
1119 * the shadow dir regardless of chrooted processes.
1120 */
1121 error = vnode_lookup(tmpname, 0, &sdvp, kernelctx);
1122 if (error == 0) {
1123 /*
1124 * If we get here, then we have successfully looked up the shadow dir,
1125 * and it has an iocount from the lookup. Return the vp in the output argument.
1126 */
1127 *sdvpp = sdvp;
1128 return 0;
1129 }
1130 /* In the failure case, no iocount is acquired */
1131 sdvp = NULLVP;
1132 bzero(tmpname, sizeof(tmpname));
1133
1134 /*
1135 * Obtain the vnode for "/var/run" directory using the kernel
1136 * context.
1137 *
1138 * This is defined in the SHADOW_DIR_CONTAINER macro
1139 */
1140 if (vnode_lookup(SHADOW_DIR_CONTAINER, 0, &dvp, kernelctx) != 0) {
1141 error = ENOTSUP;
1142 goto out;
1143 }
1144
1145 /*
1146 * Create the shadow stream directory.
1147 * 'dvp' below suggests the parent directory so
1148 * we only need to provide the leaf entry name
1149 */
1150 MAKE_SHADOW_DIR_LEAF(rootvnode, tmpname);
1151 bzero(&cn, sizeof(cn));
1152 cn.cn_nameiop = LOOKUP;
1153 cn.cn_flags = ISLASTCN;
1154 cn.cn_context = kernelctx;
1155 cn.cn_pnbuf = tmpname;
1156 cn.cn_pnlen = sizeof(tmpname);
1157 cn.cn_nameptr = cn.cn_pnbuf;
1158 cn.cn_namelen = (int)strlen(tmpname);
1159
1160 /*
1161 * owned by root, only readable by root, hidden
1162 */
1163 VATTR_INIT(&va);
1164 VATTR_SET(&va, va_uid, 0);
1165 VATTR_SET(&va, va_gid, 0);
1166 VATTR_SET(&va, va_mode, S_IRUSR | S_IXUSR);
1167 VATTR_SET(&va, va_type, VDIR);
1168 VATTR_SET(&va, va_flags, UF_HIDDEN);
1169 va.va_vaflags = VA_EXCLUSIVE;
1170
1171 error = VNOP_MKDIR(dvp, &sdvp, &cn, &va, kernelctx);
1172
1173 /*
1174 * There can be only one winner for an exclusive create.
1175 */
1176 if (error == EEXIST) {
1177 /* loser has to look up directory */
1178 error = VNOP_LOOKUP(dvp, &sdvp, &cn, kernelctx);
1179 if (error == 0) {
1180 /* Make sure its in fact a directory */
1181 if (sdvp->v_type != VDIR) {
1182 goto baddir;
1183 }
1184 /* Obtain the fsid for /var/run directory */
1185 VATTR_INIT(&va);
1186 VATTR_WANTED(&va, va_fsid);
1187 if (VNOP_GETATTR(dvp, &va, kernelctx) != 0 ||
1188 !VATTR_IS_SUPPORTED(&va, va_fsid)) {
1189 goto baddir;
1190 }
1191 tmp_fsid = va.va_fsid;
1192
1193 VATTR_INIT(&va);
1194 VATTR_WANTED(&va, va_uid);
1195 VATTR_WANTED(&va, va_gid);
1196 VATTR_WANTED(&va, va_mode);
1197 VATTR_WANTED(&va, va_fsid);
1198 VATTR_WANTED(&va, va_dirlinkcount);
1199 VATTR_WANTED(&va, va_acl);
1200 /* Provide defaults for attrs that may not be supported */
1201 va.va_dirlinkcount = 1;
1202 va.va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
1203
1204 if (VNOP_GETATTR(sdvp, &va, kernelctx) != 0 ||
1205 !VATTR_IS_SUPPORTED(&va, va_uid) ||
1206 !VATTR_IS_SUPPORTED(&va, va_gid) ||
1207 !VATTR_IS_SUPPORTED(&va, va_mode) ||
1208 !VATTR_IS_SUPPORTED(&va, va_fsid)) {
1209 goto baddir;
1210 }
1211 /*
1212 * Make sure its what we want:
1213 * - owned by root
1214 * - not writable by anyone
1215 * - on same file system as /var/run
1216 * - not a hard-linked directory
1217 * - no ACLs (they might grant write access)
1218 */
1219 if ((va.va_uid != 0) || (va.va_gid != 0) ||
1220 (va.va_mode & (S_IWUSR | S_IRWXG | S_IRWXO)) ||
1221 (va.va_fsid != tmp_fsid) ||
1222 (va.va_dirlinkcount != 1) ||
1223 (va.va_acl != (kauth_acl_t) KAUTH_FILESEC_NONE)) {
1224 goto baddir;
1225 }
1226 }
1227 }
1228 out:
1229 if (dvp) {
1230 vnode_put(dvp);
1231 }
1232 if (error) {
1233 /* On errors, clean up shadow stream directory. */
1234 if (sdvp) {
1235 vnode_put(sdvp);
1236 sdvp = NULLVP;
1237 }
1238 }
1239 *sdvpp = sdvp;
1240 return error;
1241
1242 baddir:
1243 /* This is not the dir we're looking for, move along */
1244 ++shadow_sequence; /* try something else next time */
1245 error = ENOTDIR;
1246 goto out;
1247 }
1248 #endif /* NAMEDSTREAMS */
1249
1250
1251 #if CONFIG_APPLEDOUBLE
1252 /*
1253 * Default Implementation (Non-native EA)
1254 */
1255
1256
1257 /*
1258 * Typical "._" AppleDouble Header File layout:
1259 * ------------------------------------------------------------
1260 * MAGIC 0x00051607
1261 * VERSION 0x00020000
1262 * FILLER 0
1263 * COUNT 2
1264 * .-- AD ENTRY[0] Finder Info Entry (must be first)
1265 * .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1266 * | '-> FINDER INFO
1267 * | ///////////// Fixed Size Data (32 bytes)
1268 * | EXT ATTR HDR
1269 * | /////////////
1270 * | ATTR ENTRY[0] --.
1271 * | ATTR ENTRY[1] --+--.
1272 * | ATTR ENTRY[2] --+--+--.
1273 * | ... | | |
1274 * | ATTR ENTRY[N] --+--+--+--.
1275 * | ATTR DATA 0 <-' | | |
1276 * | //////////// | | |
1277 * | ATTR DATA 1 <----' | |
1278 * | ///////////// | |
1279 * | ATTR DATA 2 <-------' |
1280 * | ///////////// |
1281 * | ... |
1282 * | ATTR DATA N <----------'
1283 * | /////////////
1284 * | Attribute Free Space
1285 * |
1286 * '----> RESOURCE FORK
1287 * ///////////// Variable Sized Data
1288 * /////////////
1289 * /////////////
1290 * /////////////
1291 * /////////////
1292 * /////////////
1293 * ...
1294 * /////////////
1295 *
1296 * ------------------------------------------------------------
1297 *
1298 * NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1299 * stored as part of the Finder Info. The length in the Finder
1300 * Info AppleDouble entry includes the length of the extended
1301 * attribute header, attribute entries, and attribute data.
1302 */
1303
1304 /*
1305 * On Disk Data Structures
1306 *
1307 * Note: Motorola 68K alignment and big-endian.
1308 *
1309 * See RFC 1740 for additional information about the AppleDouble file format.
1310 *
1311 */
1312
1313 #define ADH_MAGIC 0x00051607
1314 #define ADH_VERSION 0x00020000
1315 #define ADH_MACOSX "Mac OS X "
1316
1317 /*
1318 * AppleDouble Entry ID's
1319 */
1320 #define AD_DATA 1 /* Data fork */
1321 #define AD_RESOURCE 2 /* Resource fork */
1322 #define AD_REALNAME 3 /* File's name on home file system */
1323 #define AD_COMMENT 4 /* Standard Mac comment */
1324 #define AD_ICONBW 5 /* Mac black & white icon */
1325 #define AD_ICONCOLOR 6 /* Mac color icon */
1326 #define AD_UNUSED 7 /* Not used */
1327 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1328 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1329 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1330 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1331 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1332 #define AD_AFPNAME 13 /* Short name on AFP server */
1333 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1334 #define AD_AFPDIRID 15 /* AFP directory ID */
1335 #define AD_ATTRIBUTES AD_FINDERINFO
1336
1337
1338 #define ATTR_FILE_PREFIX "._"
1339 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1340
1341 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1342
1343 /* Implementation Limits */
1344 #define ATTR_MAX_SIZE AD_XATTR_MAXSIZE
1345 #define ATTR_MAX_HDR_SIZE 65536
1346 /*
1347 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1348 * size supported (including the attribute entries). All of
1349 * the attribute entries must reside within this limit. If
1350 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1351 * boundry, then all of the attribute data I/O is performed
1352 * separately from the attribute header I/O.
1353 *
1354 * In particular, all of the attr_entry structures must lie
1355 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1356 * AppleDouble file. However, the attribute data (i.e. the
1357 * contents of the extended attributes) may extend beyond the
1358 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1359 * limit is to allow the implementation to optimize by reading
1360 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1361 */
1362
1363
1364 #define FINDERINFOSIZE 32
1365
1366 typedef struct apple_double_entry {
1367 u_int32_t type; /* entry type: see list, 0 invalid */
1368 u_int32_t offset; /* entry data offset from the beginning of the file. */
1369 u_int32_t length; /* entry data length in bytes. */
1370 } __attribute__((aligned(2), packed)) apple_double_entry_t;
1371
1372
1373 typedef struct apple_double_header {
1374 u_int32_t magic; /* == ADH_MAGIC */
1375 u_int32_t version; /* format version: 2 = 0x00020000 */
1376 u_int32_t filler[4];
1377 u_int16_t numEntries; /* number of entries which follow */
1378 apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */
1379 u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */
1380 u_int8_t pad[2]; /* get better alignment inside attr_header */
1381 } __attribute__((aligned(2), packed)) apple_double_header_t;
1382
1383 #define ADHDRSIZE (4+4+16+2)
1384
1385 /* Entries are aligned on 4 byte boundaries */
1386 typedef struct attr_entry {
1387 u_int32_t offset; /* file offset to data */
1388 u_int32_t length; /* size of attribute data */
1389 u_int16_t flags;
1390 u_int8_t namelen;
1391 u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1392 } __attribute__((aligned(2), packed)) attr_entry_t;
1393
1394
1395 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1396 typedef struct attr_header {
1397 apple_double_header_t appledouble;
1398 u_int32_t magic; /* == ATTR_HDR_MAGIC */
1399 u_int32_t debug_tag; /* for debugging == file id of owning file */
1400 u_int32_t total_size; /* file offset of end of attribute header + entries + data */
1401 u_int32_t data_start; /* file offset to attribute data area */
1402 u_int32_t data_length; /* length of attribute data area */
1403 u_int32_t reserved[3];
1404 u_int16_t flags;
1405 u_int16_t num_attrs;
1406 } __attribute__((aligned(2), packed)) attr_header_t;
1407
1408
1409 /* Empty Resource Fork Header */
1410 typedef struct rsrcfork_header {
1411 u_int32_t fh_DataOffset;
1412 u_int32_t fh_MapOffset;
1413 u_int32_t fh_DataLength;
1414 u_int32_t fh_MapLength;
1415 u_int8_t systemData[112];
1416 u_int8_t appData[128];
1417 u_int32_t mh_DataOffset;
1418 u_int32_t mh_MapOffset;
1419 u_int32_t mh_DataLength;
1420 u_int32_t mh_MapLength;
1421 u_int32_t mh_Next;
1422 u_int16_t mh_RefNum;
1423 u_int8_t mh_Attr;
1424 u_int8_t mh_InMemoryAttr;
1425 u_int16_t mh_Types;
1426 u_int16_t mh_Names;
1427 u_int16_t typeCount;
1428 } __attribute__((aligned(2), packed)) rsrcfork_header_t;
1429
1430 #define RF_FIRST_RESOURCE 256
1431 #define RF_NULL_MAP_LENGTH 30
1432 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1433
1434 /* Runtime information about the attribute file. */
1435 typedef struct attr_info {
1436 vfs_context_t context;
1437 vnode_t filevp;
1438 size_t filesize;
1439 size_t iosize;
1440 u_int8_t *rawdata;
1441 size_t rawsize; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1442 apple_double_header_t *filehdr;
1443 apple_double_entry_t *finderinfo;
1444 apple_double_entry_t *rsrcfork;
1445 attr_header_t *attrhdr;
1446 attr_entry_t *attr_entry;
1447 u_int8_t readonly;
1448 u_int8_t emptyfinderinfo;
1449 } attr_info_t;
1450
1451
1452 #define ATTR_SETTING 1
1453
1454 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1455
1456 #define ATTR_ENTRY_LENGTH(namelen) \
1457 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1458
1459 #define ATTR_NEXT(ae) \
1460 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1461
1462 #define ATTR_VALID(ae, ai) \
1463 ((&(ae)->namelen < ((ai).rawdata + (ai).rawsize)) && \
1464 (u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1465
1466 #define SWAP16(x) OSSwapBigToHostInt16((x))
1467 #define SWAP32(x) OSSwapBigToHostInt32((x))
1468 #define SWAP64(x) OSSwapBigToHostInt64((x))
1469
1470
1471 /*
1472 * sysctl stuff
1473 */
1474 static int vfs_xattr_doubleagent_enabled = 1;
1475 SYSCTL_DECL(_vfs_generic);
1476 SYSCTL_INT(_vfs_generic, OID_AUTO, xattr_doubleagent_enabled, CTLFLAG_RW | CTLFLAG_LOCKED, &vfs_xattr_doubleagent_enabled, 0, "");
1477
1478 static int get_doubleagentd_port(mach_port_t *doubleagentd_port);
1479
1480 /*
1481 * VFS default xattr functions
1482 */
1483 static int default_getxattr_vfs(vnode_t vp, const char *name, uio_t uio, size_t *size, int options,
1484 vfs_context_t context);
1485 static int default_setxattr_vfs(vnode_t vp, const char *name, uio_t uio, int options,
1486 vfs_context_t context);
1487 static int default_listxattr_vfs(vnode_t vp, uio_t uio, size_t *size, int options,
1488 vfs_context_t context);
1489 static int default_removexattr_vfs(vnode_t vp, const char *name, int options,
1490 vfs_context_t context);
1491
1492 /*
1493 * DoubleAgent default xattr functions
1494 */
1495 static int default_getxattr_doubleagent(vnode_t vp, const char *name,
1496 uio_t uio, size_t *size, int options, vfs_context_t context,
1497 mach_port_t);
1498 static int default_setxattr_doubleagent(vnode_t vp, const char *name,
1499 uio_t uio, int options, vfs_context_t context, mach_port_t);
1500 static int default_listxattr_doubleagent(vnode_t vp, uio_t uio, size_t *size,
1501 int options, vfs_context_t context, mach_port_t);
1502 static int default_removexattr_doubleagent(vnode_t vp, const char *name,
1503 int options, vfs_context_t context, mach_port_t);
1504
1505
1506 static u_int32_t emptyfinfo[8] = {0};
1507
1508
1509 /*
1510 * Local support routines
1511 */
1512 static void close_xattrfile(struct fileglob *xfg, bool have_iocount, bool drop_iocount, vfs_context_t context);
1513
1514 static int open_xattrfile(vnode_t vp, int fileflags, struct fileglob **xfgp,
1515 int64_t *file_sizep, vfs_context_t context);
1516
1517 static int create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context);
1518
1519 static void remove_xattrfile(struct fileglob *xfg, vnode_t xvp, vfs_context_t context);
1520
1521 static int get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context);
1522
1523 static void rel_xattrinfo(attr_info_t *ainfop);
1524
1525 static int write_xattrinfo(attr_info_t *ainfop);
1526
1527 static void init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr);
1528
1529 static int make_xattrfile_port(struct fileglob *xfg, ipc_port_t *portp);
1530
1531 #if BYTE_ORDER == LITTLE_ENDIAN
1532 static void swap_adhdr(apple_double_header_t *adh);
1533 static void swap_attrhdr(attr_header_t *ah, attr_info_t* info);
1534
1535 #else
1536 #define swap_adhdr(x)
1537 #define swap_attrhdr(x, y)
1538 #endif
1539
1540 static int check_and_swap_attrhdr(attr_header_t *ah, attr_info_t* ainfop);
1541 static int shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context);
1542 static int shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context);
1543
1544
1545 /*
1546 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1547 * is in big endian (as it would exist on disk). Verifies the following:
1548 * - magic field
1549 * - version field
1550 * - number of entries
1551 * - that each entry fits within the file size
1552 *
1553 * If the header is invalid, ENOATTR is returned.
1554 *
1555 * NOTE: Does not attempt to validate the extended attributes header that
1556 * may be embedded in the Finder Info entry.
1557 */
1558 static int
check_and_swap_apple_double_header(attr_info_t * ainfop)1559 check_and_swap_apple_double_header(attr_info_t *ainfop)
1560 {
1561 int i, j;
1562 u_int32_t header_end;
1563 u_int32_t entry_end;
1564 size_t rawsize;
1565 apple_double_header_t *header;
1566
1567 rawsize = ainfop->rawsize;
1568 header = (apple_double_header_t *) ainfop->rawdata;
1569
1570 /* Is the file big enough to contain an AppleDouble header? */
1571 if (rawsize < offsetof(apple_double_header_t, entries)) {
1572 return ENOATTR;
1573 }
1574
1575 /* Swap the AppleDouble header fields to native order */
1576 header->magic = SWAP32(header->magic);
1577 header->version = SWAP32(header->version);
1578 header->numEntries = SWAP16(header->numEntries);
1579
1580 /* Sanity check the AppleDouble header fields */
1581 if (header->magic != ADH_MAGIC ||
1582 header->version != ADH_VERSION ||
1583 header->numEntries < 1 ||
1584 header->numEntries > 15) {
1585 return ENOATTR;
1586 }
1587
1588 /* Calculate where the entries[] array ends */
1589 header_end = offsetof(apple_double_header_t, entries) +
1590 header->numEntries * sizeof(apple_double_entry_t);
1591
1592 /* Is the file big enough to contain the AppleDouble entries? */
1593 if (rawsize < header_end) {
1594 return ENOATTR;
1595 }
1596
1597 /* Swap and sanity check each AppleDouble entry */
1598 for (i = 0; i < header->numEntries; i++) {
1599 /* Swap the per-entry fields to native order */
1600 header->entries[i].type = SWAP32(header->entries[i].type);
1601 header->entries[i].offset = SWAP32(header->entries[i].offset);
1602 header->entries[i].length = SWAP32(header->entries[i].length);
1603
1604 entry_end = header->entries[i].offset + header->entries[i].length;
1605
1606 /*
1607 * Does the entry's content start within the header itself,
1608 * did the addition overflow, or does the entry's content
1609 * extend past the end of the file?
1610 */
1611 if (header->entries[i].offset < header_end ||
1612 entry_end < header->entries[i].offset ||
1613 entry_end > ainfop->filesize) {
1614 return ENOATTR;
1615 }
1616
1617 /*
1618 * Does the current entry's content overlap with a previous
1619 * entry's content?
1620 *
1621 * Yes, this is O(N**2), and there are more efficient algorithms
1622 * for testing pairwise overlap of N ranges when N is large.
1623 * But we have already ensured N < 16, and N is almost always 2.
1624 * So there's no point in using a more complex algorithm.
1625 */
1626
1627 for (j = 0; j < i; j++) {
1628 if (entry_end > header->entries[j].offset &&
1629 header->entries[j].offset + header->entries[j].length > header->entries[i].offset) {
1630 return ENOATTR;
1631 }
1632 }
1633 }
1634
1635 return 0;
1636 }
1637
1638 /*
1639 * Retrieve the data of an extended attribute.
1640 */
1641 static int
default_getxattr(vnode_t vp,const char * name,uio_t uio,size_t * size,__unused int options,vfs_context_t context)1642 default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size,
1643 __unused int options, vfs_context_t context)
1644 {
1645 mach_port_t port;
1646 int error;
1647
1648 if (vfs_xattr_doubleagent_enabled &&
1649 get_doubleagentd_port(&port) == 0) {
1650 error = default_getxattr_doubleagent(vp, name, uio, size,
1651 options, context, port);
1652 ipc_port_release_send(port);
1653 } else {
1654 error = default_getxattr_vfs(vp, name, uio, size, options,
1655 context);
1656 }
1657 return error;
1658 }
1659
1660 /*
1661 * Set the data of an extended attribute.
1662 */
1663 static int __attribute__((noinline))
default_setxattr(vnode_t vp,const char * name,uio_t uio,int options,vfs_context_t context)1664 default_setxattr(vnode_t vp, const char *name, uio_t uio, int options,
1665 vfs_context_t context)
1666 {
1667 mach_port_t port;
1668 int error;
1669
1670 if (vfs_xattr_doubleagent_enabled &&
1671 get_doubleagentd_port(&port) == 0) {
1672 error = default_setxattr_doubleagent(vp, name, uio, options,
1673 context, port);
1674 ipc_port_release_send(port);
1675 } else {
1676 error = default_setxattr_vfs(vp, name, uio, options, context);
1677 }
1678 return error;
1679 }
1680
1681 /*
1682 * Remove an extended attribute.
1683 */
1684 static int
default_removexattr(vnode_t vp,const char * name,__unused int options,vfs_context_t context)1685 default_removexattr(vnode_t vp, const char *name, __unused int options,
1686 vfs_context_t context)
1687 {
1688 mach_port_t port;
1689 int error;
1690
1691 if (vfs_xattr_doubleagent_enabled &&
1692 get_doubleagentd_port(&port) == 0) {
1693 error = default_removexattr_doubleagent(vp, name, options,
1694 context, port);
1695 ipc_port_release_send(port);
1696 } else {
1697 error = default_removexattr_vfs(vp, name, options, context);
1698 }
1699 return error;
1700 }
1701
1702 /*
1703 * Retrieve the list of extended attribute names.
1704 */
1705 static int
default_listxattr(vnode_t vp,uio_t uio,size_t * size,__unused int options,vfs_context_t context)1706 default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options,
1707 vfs_context_t context)
1708 {
1709 mach_port_t port;
1710 int error;
1711
1712 if (vfs_xattr_doubleagent_enabled &&
1713 get_doubleagentd_port(&port) == 0) {
1714 error = default_listxattr_doubleagent(vp, uio, size, options,
1715 context, port);
1716 ipc_port_release_send(port);
1717 } else {
1718 error = default_listxattr_vfs(vp, uio, size, options, context);
1719 }
1720 return error;
1721 }
1722
1723 /*
1724 * Retrieve the data of an extended attribute.
1725 * (VFS implementation).
1726 */
1727 static int
default_getxattr_vfs(vnode_t vp,const char * name,uio_t uio,size_t * size,__unused int options,vfs_context_t context)1728 default_getxattr_vfs(vnode_t vp, const char *name, uio_t uio, size_t *size,
1729 __unused int options, vfs_context_t context)
1730 {
1731 vnode_t xvp = NULL;
1732 struct fileglob *xfg = NULL;
1733 attr_info_t ainfo;
1734 attr_header_t *header;
1735 attr_entry_t *entry;
1736 u_int8_t *attrdata;
1737 u_int32_t datalen;
1738 size_t namelen;
1739 int isrsrcfork;
1740 int fileflags;
1741 int i;
1742 int error;
1743
1744 fileflags = FREAD | O_SHLOCK;
1745 isrsrcfork = strncmp(name, XATTR_RESOURCEFORK_NAME,
1746 sizeof(XATTR_RESOURCEFORK_NAME)) == 0;
1747
1748 if ((error = open_xattrfile(vp, fileflags, &xfg, NULL, context))) {
1749 return error;
1750 }
1751 xvp = fg_get_data(xfg);
1752
1753 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
1754 close_xattrfile(xfg, true, true, context);
1755 return error;
1756 }
1757
1758 /* Get the Finder Info. */
1759 if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1760 if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) {
1761 error = ENOATTR;
1762 } else if (uio == NULL) {
1763 *size = FINDERINFOSIZE;
1764 error = 0;
1765 } else if (uio_offset(uio) != 0) {
1766 error = EINVAL;
1767 } else if (uio_resid(uio) < FINDERINFOSIZE) {
1768 error = ERANGE;
1769 } else {
1770 attrdata = (u_int8_t*)ainfo.filehdr + ainfo.finderinfo->offset;
1771 error = uiomove((caddr_t)attrdata, FINDERINFOSIZE, uio);
1772 }
1773 goto out;
1774 }
1775
1776 /* Read the Resource Fork. */
1777 if (isrsrcfork) {
1778 if (!vnode_isreg(vp)) {
1779 error = EPERM;
1780 } else if (ainfo.rsrcfork == NULL) {
1781 error = ENOATTR;
1782 } else if (uio == NULL) {
1783 *size = (size_t)ainfo.rsrcfork->length;
1784 } else {
1785 uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset);
1786 error = VNOP_READ(xvp, uio, 0, context);
1787 if (error == 0) {
1788 uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset);
1789 }
1790 }
1791 goto out;
1792 }
1793
1794 if (ainfo.attrhdr == NULL || ainfo.attr_entry == NULL) {
1795 error = ENOATTR;
1796 goto out;
1797 }
1798 if (uio_offset(uio) != 0) {
1799 error = EINVAL;
1800 goto out;
1801 }
1802 error = ENOATTR;
1803 namelen = strlen(name) + 1;
1804 header = ainfo.attrhdr;
1805 entry = ainfo.attr_entry;
1806 /*
1807 * Search for attribute name in the header.
1808 */
1809 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
1810 if (strncmp((const char *)entry->name, name, namelen) == 0) {
1811 datalen = entry->length;
1812 if (uio == NULL) {
1813 *size = datalen;
1814 error = 0;
1815 break;
1816 }
1817 if (uio_resid(uio) < (user_ssize_t)datalen) {
1818 error = ERANGE;
1819 break;
1820 }
1821 if (entry->offset + datalen < ATTR_MAX_HDR_SIZE) {
1822 attrdata = ((u_int8_t *)header + entry->offset);
1823 error = uiomove((caddr_t)attrdata, datalen, uio);
1824 } else {
1825 uio_setoffset(uio, entry->offset);
1826 error = VNOP_READ(xvp, uio, 0, context);
1827 uio_setoffset(uio, 0);
1828 }
1829 break;
1830 }
1831 entry = ATTR_NEXT(entry);
1832 }
1833 out:
1834 rel_xattrinfo(&ainfo);
1835 close_xattrfile(xfg, true, true, context);
1836
1837 return error;
1838 }
1839
1840 /*
1841 * Set the data of an extended attribute.
1842 * (VFS implementation).
1843 */
1844 static int __attribute__((noinline))
default_setxattr_vfs(vnode_t vp,const char * name,uio_t uio,int options,vfs_context_t context)1845 default_setxattr_vfs(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context)
1846 {
1847 vnode_t xvp = NULL;
1848 struct fileglob *xfg = NULL;
1849 attr_info_t ainfo;
1850 attr_header_t *header;
1851 attr_entry_t *entry;
1852 attr_entry_t *lastentry;
1853 u_int8_t *attrdata;
1854 size_t datalen;
1855 size_t entrylen;
1856 size_t datafreespace;
1857 int namelen;
1858 int found = 0;
1859 int i;
1860 int splitdata;
1861 int fileflags;
1862 int error;
1863 char finfo[FINDERINFOSIZE];
1864
1865 datalen = uio_resid(uio);
1866 if (datalen > XATTR_MAXSIZE) {
1867 return E2BIG;
1868 }
1869 namelen = (int)strlen(name) + 1;
1870 if (namelen > UINT8_MAX) {
1871 return EINVAL;
1872 }
1873 entrylen = ATTR_ENTRY_LENGTH(namelen);
1874
1875 /*
1876 * By convention, Finder Info that is all zeroes is equivalent to not
1877 * having a Finder Info EA. So if we're trying to set the Finder Info
1878 * to all zeroes, then delete it instead. If a file didn't have an
1879 * AppleDouble file before, this prevents creating an AppleDouble file
1880 * with no useful content.
1881 *
1882 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1883 * for all zeroes Finder Info before opening the AppleDouble file.
1884 * But if either of those options were specified, we need to open the
1885 * AppleDouble file to see whether there was already Finder Info (so we
1886 * can return an error if needed); this case is handled further below.
1887 *
1888 * NOTE: this copies the Finder Info data into the "finfo" local.
1889 */
1890 if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1891 /*
1892 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1893 * That means we probably have to open_xattrfile and get_xattrinfo.
1894 */
1895 if (uio_offset(uio) != 0) {
1896 return EINVAL;
1897 }
1898
1899 if (datalen != FINDERINFOSIZE) {
1900 return ERANGE;
1901 }
1902
1903 error = uiomove(finfo, (int)datalen, uio);
1904 if (error) {
1905 return error;
1906 }
1907 if ((options & (XATTR_CREATE | XATTR_REPLACE)) == 0 &&
1908 bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) {
1909 error = default_removexattr(vp, name, 0, context);
1910 if (error == ENOATTR) {
1911 error = 0;
1912 }
1913 return error;
1914 }
1915 }
1916
1917 start:
1918 /*
1919 * Open the file locked since setting an attribute
1920 * can change the layout of the Apple Double file.
1921 */
1922 fileflags = FREAD | FWRITE | O_EXLOCK;
1923 if ((error = open_xattrfile(vp, O_CREAT | fileflags, &xfg, NULL, context))) {
1924 return error;
1925 }
1926 xvp = fg_get_data(xfg);
1927
1928 if ((error = get_xattrinfo(xvp, ATTR_SETTING, &ainfo, context))) {
1929 close_xattrfile(xfg, true, true, context);
1930 return error;
1931 }
1932
1933 /* Set the Finder Info. */
1934 if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1935 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
1936 /* attr exists and "create" was specified? */
1937 if (options & XATTR_CREATE) {
1938 error = EEXIST;
1939 goto out;
1940 }
1941 } else {
1942 /* attr doesn't exists and "replace" was specified? */
1943 if (options & XATTR_REPLACE) {
1944 error = ENOATTR;
1945 goto out;
1946 }
1947 }
1948 if (options != 0 && bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) {
1949 /*
1950 * Setting the Finder Info to all zeroes is equivalent to
1951 * removing it. Close the xattr file and let
1952 * default_removexattr do the work (including deleting
1953 * the xattr file if there are no other xattrs).
1954 *
1955 * Note that we have to handle the case where the
1956 * Finder Info was already all zeroes, and we ignore
1957 * ENOATTR.
1958 *
1959 * The common case where options == 0 was handled above.
1960 */
1961 rel_xattrinfo(&ainfo);
1962 close_xattrfile(xfg, true, true, context);
1963 error = default_removexattr(vp, name, 0, context);
1964 if (error == ENOATTR) {
1965 error = 0;
1966 }
1967 return error;
1968 }
1969 if (ainfo.finderinfo) {
1970 attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset;
1971 bcopy(finfo, attrdata, datalen);
1972 ainfo.iosize = sizeof(attr_header_t);
1973 error = write_xattrinfo(&ainfo);
1974 goto out;
1975 }
1976 error = ENOATTR;
1977 goto out;
1978 }
1979
1980 /* Write the Resource Fork. */
1981 if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
1982 off_t endoffset;
1983
1984 if (!vnode_isreg(vp)) {
1985 error = EPERM;
1986 goto out;
1987 }
1988 /* Make sure we have a rsrc fork pointer.. */
1989 if (ainfo.rsrcfork == NULL) {
1990 error = ENOATTR;
1991 goto out;
1992 }
1993 if (ainfo.rsrcfork) {
1994 if (ainfo.rsrcfork->length != 0) {
1995 if (options & XATTR_CREATE) {
1996 /* attr exists, and create specified ? */
1997 error = EEXIST;
1998 goto out;
1999 }
2000 } else {
2001 /* Zero length AD rsrc fork */
2002 if (options & XATTR_REPLACE) {
2003 /* attr doesn't exist (0-length), but replace specified ? */
2004 error = ENOATTR;
2005 goto out;
2006 }
2007 }
2008 } else {
2009 /* We can't do much if we somehow didn't get an AD rsrc pointer */
2010 error = ENOATTR;
2011 goto out;
2012 }
2013
2014 endoffset = uio_resid(uio) + uio_offset(uio); /* new size */
2015 if (endoffset > UINT32_MAX || endoffset < 0) {
2016 error = EINVAL;
2017 goto out;
2018 }
2019 uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset);
2020 error = VNOP_WRITE(xvp, uio, 0, context);
2021 if (error) {
2022 goto out;
2023 }
2024 uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset);
2025 if (endoffset > ainfo.rsrcfork->length) {
2026 ainfo.rsrcfork->length = (u_int32_t)endoffset;
2027 ainfo.iosize = sizeof(attr_header_t);
2028 error = write_xattrinfo(&ainfo);
2029 goto out;
2030 }
2031 goto out;
2032 }
2033
2034 if (datalen > ATTR_MAX_SIZE) {
2035 return E2BIG; /* EINVAL instead ? */
2036 }
2037
2038 if (ainfo.attrhdr == NULL) {
2039 error = ENOATTR;
2040 goto out;
2041 }
2042 header = ainfo.attrhdr;
2043 entry = ainfo.attr_entry;
2044
2045 /* Check if data area crosses the maximum header size. */
2046 if ((header->data_start + header->data_length + entrylen + datalen) > ATTR_MAX_HDR_SIZE) {
2047 splitdata = 1; /* do data I/O separately */
2048 } else {
2049 splitdata = 0;
2050 }
2051
2052 /*
2053 * See if attribute already exists.
2054 */
2055 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
2056 if (strncmp((const char *)entry->name, name, namelen) == 0) {
2057 found = 1;
2058 break;
2059 }
2060 entry = ATTR_NEXT(entry);
2061 }
2062
2063 if (found) {
2064 if (options & XATTR_CREATE) {
2065 error = EEXIST;
2066 goto out;
2067 }
2068 if (datalen == entry->length) {
2069 if (splitdata) {
2070 uio_setoffset(uio, entry->offset);
2071 error = VNOP_WRITE(xvp, uio, 0, context);
2072 uio_setoffset(uio, 0);
2073 if (error) {
2074 printf("setxattr: VNOP_WRITE error %d\n", error);
2075 }
2076 } else {
2077 attrdata = (u_int8_t *)header + entry->offset;
2078 error = uiomove((caddr_t)attrdata, (int)datalen, uio);
2079 if (error) {
2080 goto out;
2081 }
2082 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
2083 error = write_xattrinfo(&ainfo);
2084 if (error) {
2085 printf("setxattr: write_xattrinfo error %d\n", error);
2086 }
2087 }
2088 goto out;
2089 } else {
2090 /*
2091 * Brute force approach - just remove old entry and set new entry.
2092 */
2093 found = 0;
2094 rel_xattrinfo(&ainfo);
2095 close_xattrfile(xfg, true, true, context);
2096 error = default_removexattr(vp, name, options, context);
2097 if (error) {
2098 return error;
2099 }
2100 /* Clear XATTR_REPLACE option since we just removed the attribute. */
2101 options &= ~XATTR_REPLACE;
2102 goto start; /* start over */
2103 }
2104 } else {
2105 if (!ATTR_VALID(entry, ainfo)) {
2106 error = ENOSPC;
2107 goto out;
2108 }
2109 }
2110
2111 if (options & XATTR_REPLACE) {
2112 error = ENOATTR; /* nothing there to replace */
2113 goto out;
2114 }
2115 /* Check if header size limit has been reached. */
2116 if ((header->data_start + entrylen) > ATTR_MAX_HDR_SIZE) {
2117 error = ENOSPC;
2118 goto out;
2119 }
2120
2121 datafreespace = header->total_size - (header->data_start + header->data_length);
2122
2123 /* Check if we need more space. */
2124 if ((datalen + entrylen) > datafreespace) {
2125 size_t growsize;
2126
2127 growsize = roundup((datalen + entrylen) - datafreespace, ATTR_BUF_SIZE);
2128
2129 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
2130 if (!splitdata && (header->total_size + growsize) > ATTR_MAX_HDR_SIZE) {
2131 growsize = ATTR_MAX_HDR_SIZE - header->total_size;
2132 }
2133
2134 ainfo.filesize += growsize;
2135 error = vnode_setsize(xvp, ainfo.filesize, 0, context);
2136 if (error) {
2137 printf("setxattr: VNOP_TRUNCATE error %d\n", error);
2138 }
2139 if (error) {
2140 goto out;
2141 }
2142
2143 /*
2144 * Move the resource fork out of the way.
2145 */
2146 if (ainfo.rsrcfork) {
2147 if (ainfo.rsrcfork->length != 0) {
2148 shift_data_down(xvp,
2149 ainfo.rsrcfork->offset,
2150 ainfo.rsrcfork->length,
2151 growsize, context);
2152 }
2153 ainfo.rsrcfork->offset += growsize;
2154 }
2155 ainfo.finderinfo->length += growsize;
2156 header->total_size += growsize;
2157 }
2158
2159 /* Make space for a new entry. */
2160 if (splitdata) {
2161 shift_data_down(xvp,
2162 header->data_start,
2163 header->data_length,
2164 entrylen, context);
2165 } else {
2166 bcopy((u_int8_t *)header + header->data_start,
2167 (u_int8_t *)header + header->data_start + entrylen,
2168 header->data_length);
2169 }
2170 header->data_start += entrylen;
2171
2172 /* Fix up entry data offsets. */
2173 lastentry = entry;
2174 for (entry = ainfo.attr_entry; entry != lastentry && ATTR_VALID(entry, ainfo); entry = ATTR_NEXT(entry)) {
2175 entry->offset += entrylen;
2176 }
2177
2178 /*
2179 * If the attribute data area is entirely within
2180 * the header buffer, then just update the buffer,
2181 * otherwise we'll write it separately to the file.
2182 */
2183 if (splitdata) {
2184 off_t offset;
2185
2186 /* Write new attribute data after the end of existing data. */
2187 offset = header->data_start + header->data_length;
2188 uio_setoffset(uio, offset);
2189 error = VNOP_WRITE(xvp, uio, 0, context);
2190 uio_setoffset(uio, 0);
2191 if (error) {
2192 printf("setxattr: VNOP_WRITE error %d\n", error);
2193 goto out;
2194 }
2195 } else {
2196 attrdata = (u_int8_t *)header + header->data_start + header->data_length;
2197
2198 error = uiomove((caddr_t)attrdata, (int)datalen, uio);
2199 if (error) {
2200 printf("setxattr: uiomove error %d\n", error);
2201 goto out;
2202 }
2203 }
2204
2205 /* Create the attribute entry. */
2206 lastentry->length = (u_int32_t)datalen;
2207 lastentry->offset = header->data_start + header->data_length;
2208 lastentry->namelen = (u_int8_t)namelen;
2209 lastentry->flags = 0;
2210 bcopy(name, &lastentry->name[0], namelen);
2211
2212 /* Update the attributes header. */
2213 header->num_attrs++;
2214 header->data_length += datalen;
2215
2216 if (splitdata) {
2217 /* Only write the entries, since the data was written separately. */
2218 ainfo.iosize = ainfo.attrhdr->data_start;
2219 } else {
2220 /* The entry and data are both in the header; write them together. */
2221 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
2222 }
2223 error = write_xattrinfo(&ainfo);
2224 if (error) {
2225 printf("setxattr: write_xattrinfo error %d\n", error);
2226 }
2227
2228 out:
2229 rel_xattrinfo(&ainfo);
2230 close_xattrfile(xfg, true, true, context);
2231
2232 /* Touch the change time if we changed an attribute. */
2233 if (error == 0) {
2234 struct vnode_attr va;
2235
2236 /* Re-write the mtime to cause a ctime change. */
2237 VATTR_INIT(&va);
2238 VATTR_WANTED(&va, va_modify_time);
2239 if (vnode_getattr(vp, &va, context) == 0) {
2240 VATTR_INIT(&va);
2241 VATTR_SET(&va, va_modify_time, va.va_modify_time);
2242 (void) vnode_setattr(vp, &va, context);
2243 }
2244 }
2245
2246 post_event_if_success(vp, error, NOTE_ATTRIB);
2247
2248 return error;
2249 }
2250
2251
2252 /*
2253 * Remove an extended attribute.
2254 * (VFS implementation).
2255 */
2256 static int
default_removexattr_vfs(vnode_t vp,const char * name,__unused int options,vfs_context_t context)2257 default_removexattr_vfs(vnode_t vp, const char *name, __unused int options, vfs_context_t context)
2258 {
2259 vnode_t xvp = NULL;
2260 struct fileglob *xfg = NULL;
2261 attr_info_t ainfo;
2262 attr_header_t *header;
2263 attr_entry_t *entry;
2264 attr_entry_t *oldslot;
2265 u_int8_t *attrdata;
2266 u_int32_t dataoff;
2267 size_t datalen;
2268 size_t entrylen;
2269 int namelen;
2270 int found = 0, lastone = 0;
2271 int i;
2272 int splitdata;
2273 int attrcount = 0;
2274 int isrsrcfork;
2275 int fileflags;
2276 int error;
2277
2278 fileflags = FREAD | FWRITE | O_EXLOCK;
2279 isrsrcfork = strncmp(name, XATTR_RESOURCEFORK_NAME,
2280 sizeof(XATTR_RESOURCEFORK_NAME)) == 0;
2281
2282 if ((error = open_xattrfile(vp, fileflags, &xfg, NULL, context))) {
2283 return error;
2284 }
2285 xvp = fg_get_data(xfg);
2286
2287 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
2288 close_xattrfile(xfg, true, true, context);
2289 return error;
2290 }
2291 if (ainfo.attrhdr) {
2292 attrcount += ainfo.attrhdr->num_attrs;
2293 }
2294 if (ainfo.rsrcfork) {
2295 ++attrcount;
2296 }
2297 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
2298 ++attrcount;
2299 }
2300
2301 /* Clear the Finder Info. */
2302 if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
2303 if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) {
2304 error = ENOATTR;
2305 goto out;
2306 }
2307 /* On removal of last attribute the ._ file is removed. */
2308 if (--attrcount == 0) {
2309 goto out;
2310 }
2311 attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset;
2312 bzero((caddr_t)attrdata, FINDERINFOSIZE);
2313 error = write_xattrinfo(&ainfo);
2314 goto out;
2315 }
2316
2317 /* Clear the Resource Fork. */
2318 if (isrsrcfork) {
2319 if (!vnode_isreg(vp)) {
2320 error = EPERM;
2321 goto out;
2322 }
2323 if (ainfo.rsrcfork == NULL || ainfo.rsrcfork->length == 0) {
2324 error = ENOATTR;
2325 goto out;
2326 }
2327 /* On removal of last attribute the ._ file is removed. */
2328 if (--attrcount == 0) {
2329 goto out;
2330 }
2331 /*
2332 * XXX
2333 * If the resource fork isn't the last AppleDouble
2334 * entry then the space needs to be reclaimed by
2335 * shifting the entries after the resource fork.
2336 */
2337 if ((ainfo.rsrcfork->offset + ainfo.rsrcfork->length) == ainfo.filesize) {
2338 ainfo.filesize -= ainfo.rsrcfork->length;
2339 error = vnode_setsize(xvp, ainfo.filesize, 0, context);
2340 }
2341 if (error == 0) {
2342 ainfo.rsrcfork->length = 0;
2343 ainfo.iosize = sizeof(attr_header_t);
2344 error = write_xattrinfo(&ainfo);
2345 }
2346 goto out;
2347 }
2348
2349 if (ainfo.attrhdr == NULL) {
2350 error = ENOATTR;
2351 goto out;
2352 }
2353 namelen = (int)strlen(name) + 1;
2354 header = ainfo.attrhdr;
2355 entry = ainfo.attr_entry;
2356
2357 /*
2358 * See if this attribute exists.
2359 */
2360 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
2361 if (strncmp((const char *)entry->name, name, namelen) == 0) {
2362 found = 1;
2363 if ((i + 1) == header->num_attrs) {
2364 lastone = 1;
2365 }
2366 break;
2367 }
2368 entry = ATTR_NEXT(entry);
2369 }
2370 if (!found) {
2371 error = ENOATTR;
2372 goto out;
2373 }
2374 /* On removal of last attribute the ._ file is removed. */
2375 if (--attrcount == 0) {
2376 goto out;
2377 }
2378
2379 datalen = entry->length;
2380 dataoff = entry->offset;
2381 entrylen = ATTR_ENTRY_LENGTH(namelen);
2382 if ((header->data_start + header->data_length) > ATTR_MAX_HDR_SIZE) {
2383 splitdata = 1;
2384 } else {
2385 splitdata = 0;
2386 }
2387
2388 /* Remove the attribute entry. */
2389 if (!lastone) {
2390 bcopy((u_int8_t *)entry + entrylen, (u_int8_t *)entry,
2391 ((size_t)header + header->data_start) - ((size_t)entry + entrylen));
2392 }
2393
2394 /* Adjust the attribute data. */
2395 if (splitdata) {
2396 shift_data_up(xvp,
2397 header->data_start,
2398 dataoff - header->data_start,
2399 entrylen,
2400 context);
2401 if (!lastone) {
2402 shift_data_up(xvp,
2403 dataoff + datalen,
2404 (header->data_start + header->data_length) - (dataoff + datalen),
2405 datalen + entrylen,
2406 context);
2407 }
2408 /* XXX write zeros to freed space ? */
2409 ainfo.iosize = ainfo.attrhdr->data_start - entrylen;
2410 } else {
2411 bcopy((u_int8_t *)header + header->data_start,
2412 (u_int8_t *)header + header->data_start - entrylen,
2413 dataoff - header->data_start);
2414 if (!lastone) {
2415 bcopy((u_int8_t *)header + dataoff + datalen,
2416 (u_int8_t *)header + dataoff - entrylen,
2417 (header->data_start + header->data_length) - (dataoff + datalen));
2418 }
2419 bzero(((u_int8_t *)header + header->data_start + header->data_length) - (datalen + entrylen), (datalen + entrylen));
2420 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
2421 }
2422
2423 /* Adjust the header values and entry offsets. */
2424 header->num_attrs--;
2425 header->data_start -= entrylen;
2426 header->data_length -= datalen;
2427
2428 oldslot = entry;
2429 entry = ainfo.attr_entry;
2430 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
2431 entry->offset -= entrylen;
2432 if (entry >= oldslot) {
2433 entry->offset -= datalen;
2434 }
2435 entry = ATTR_NEXT(entry);
2436 }
2437 error = write_xattrinfo(&ainfo);
2438 if (error) {
2439 printf("removexattr: write_xattrinfo error %d\n", error);
2440 }
2441 out:
2442 rel_xattrinfo(&ainfo);
2443
2444 /* When there are no more attributes remove the ._ file. */
2445 if (attrcount == 0) {
2446 remove_xattrfile(xfg, xvp, context);
2447 } else {
2448 close_xattrfile(xfg, true, true, context);
2449 }
2450 /* Touch the change time if we changed an attribute. */
2451 if (error == 0) {
2452 struct vnode_attr va;
2453
2454 /* Re-write the mtime to cause a ctime change. */
2455 VATTR_INIT(&va);
2456 VATTR_WANTED(&va, va_modify_time);
2457 if (vnode_getattr(vp, &va, context) == 0) {
2458 VATTR_INIT(&va);
2459 VATTR_SET(&va, va_modify_time, va.va_modify_time);
2460 (void) vnode_setattr(vp, &va, context);
2461 }
2462 }
2463
2464 post_event_if_success(vp, error, NOTE_ATTRIB);
2465
2466 return error;
2467 }
2468
2469
2470 /*
2471 * Retrieve the list of extended attribute names.
2472 * (VFS implementation).
2473 */
2474 static int
default_listxattr_vfs(vnode_t vp,uio_t uio,size_t * size,__unused int options,vfs_context_t context)2475 default_listxattr_vfs(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs_context_t context)
2476 {
2477 vnode_t xvp = NULL;
2478 struct fileglob *xfg = NULL;
2479 attr_info_t ainfo;
2480 attr_entry_t *entry;
2481 int i, count;
2482 int error;
2483
2484 /*
2485 * We do not zero "*size" here as we don't want to stomp a size set when
2486 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2487 * system call layer, up in listxattr or flistxattr.
2488 */
2489
2490 if ((error = open_xattrfile(vp, FREAD | O_SHLOCK, &xfg, NULL, context))) {
2491 if (error == ENOATTR) {
2492 error = 0;
2493 }
2494 return error;
2495 }
2496 xvp = fg_get_data(xfg);
2497
2498 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
2499 if (error == ENOATTR) {
2500 error = 0;
2501 }
2502 close_xattrfile(xfg, true, true, context);
2503 return error;
2504 }
2505
2506 /* Check for Finder Info. */
2507 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
2508 if (uio == NULL) {
2509 *size += sizeof(XATTR_FINDERINFO_NAME);
2510 } else if (uio_resid(uio) < (user_ssize_t)sizeof(XATTR_FINDERINFO_NAME)) {
2511 error = ERANGE;
2512 goto out;
2513 } else {
2514 error = uiomove(XATTR_FINDERINFO_NAME,
2515 sizeof(XATTR_FINDERINFO_NAME), uio);
2516 if (error) {
2517 error = ERANGE;
2518 goto out;
2519 }
2520 }
2521 }
2522
2523 /* Check for Resource Fork. */
2524 if (vnode_isreg(vp) && ainfo.rsrcfork) {
2525 if (uio == NULL) {
2526 *size += sizeof(XATTR_RESOURCEFORK_NAME);
2527 } else if (uio_resid(uio) < (user_ssize_t)sizeof(XATTR_RESOURCEFORK_NAME)) {
2528 error = ERANGE;
2529 goto out;
2530 } else {
2531 error = uiomove(XATTR_RESOURCEFORK_NAME,
2532 sizeof(XATTR_RESOURCEFORK_NAME), uio);
2533 if (error) {
2534 error = ERANGE;
2535 goto out;
2536 }
2537 }
2538 }
2539
2540 /* Check for attributes. */
2541 if (ainfo.attrhdr) {
2542 count = ainfo.attrhdr->num_attrs;
2543 for (i = 0, entry = ainfo.attr_entry; i < count && ATTR_VALID(entry, ainfo); i++) {
2544 if (xattr_protected((const char *)entry->name) ||
2545 ((entry->namelen < XATTR_MAXNAMELEN) &&
2546 (entry->name[entry->namelen] == '\0') &&
2547 (xattr_validatename((const char *)entry->name) != 0))) {
2548 entry = ATTR_NEXT(entry);
2549 continue;
2550 }
2551 if (uio == NULL) {
2552 *size += entry->namelen;
2553 entry = ATTR_NEXT(entry);
2554 continue;
2555 }
2556 if (uio_resid(uio) < entry->namelen) {
2557 error = ERANGE;
2558 break;
2559 }
2560 error = uiomove((caddr_t) entry->name, entry->namelen, uio);
2561 if (error) {
2562 if (error != EFAULT) {
2563 error = ERANGE;
2564 }
2565 break;
2566 }
2567 entry = ATTR_NEXT(entry);
2568 }
2569 }
2570 out:
2571 rel_xattrinfo(&ainfo);
2572 close_xattrfile(xfg, true, true, context);
2573
2574 return error;
2575 }
2576
2577 static int
get_doubleagentd_port(mach_port_t * doubleagentd_port)2578 get_doubleagentd_port(mach_port_t *doubleagentd_port)
2579 {
2580 kern_return_t ret;
2581
2582 *doubleagentd_port = MACH_PORT_NULL;
2583 ret = host_get_doubleagentd_port(host_priv_self(), doubleagentd_port);
2584 if (ret != KERN_SUCCESS) {
2585 printf("vfs_xattr: can't get doubleagentd port, status 0x%08x\n", ret);
2586 return EIO;
2587 }
2588 if (!IPC_PORT_VALID(*doubleagentd_port)) {
2589 printf("vfs_xattr: doubleagentd port not valid\n");
2590 return EIO;
2591 }
2592 return 0;
2593 }
2594
2595 /*
2596 * Retrieve the data of an extended attribute.
2597 * (Using DoubleAgent to parse the AD file).
2598 */
2599 static int
default_getxattr_doubleagent(vnode_t vp,const char * name,uio_t uio,size_t * size,__unused int options,vfs_context_t context,mach_port_t doubleagentd_port)2600 default_getxattr_doubleagent(vnode_t vp, const char *name, uio_t uio,
2601 size_t *size, __unused int options, vfs_context_t context,
2602 mach_port_t doubleagentd_port)
2603 {
2604 vnode_t xvp = NULL;
2605 struct fileglob *xfg = NULL;
2606 mach_port_t fileport = MACH_PORT_NULL;
2607 uint64_t value_offset = 0;
2608 uint64_t value_length = 0;
2609 int64_t fsize;
2610 int isrsrcfork;
2611 int fileflags;
2612 int error;
2613 kern_return_t kr;
2614 char cName[XATTR_MAXNAMELEN] = {0};
2615 bool have_iocount = true;
2616
2617 fileflags = FREAD | O_SHLOCK;
2618 isrsrcfork = strncmp(name, XATTR_RESOURCEFORK_NAME,
2619 sizeof(XATTR_RESOURCEFORK_NAME)) == 0;
2620
2621 if ((error = open_xattrfile(vp, fileflags, &xfg, &fsize, context))) {
2622 goto out;
2623 }
2624 xvp = fg_get_data(xfg);
2625 if ((error = make_xattrfile_port(xfg, &fileport))) {
2626 goto out;
2627 }
2628
2629 /* Drop the iocount before upcalling to doubleagentd. */
2630 vnode_put(xvp);
2631 have_iocount = false;
2632
2633 strncpy(cName, name, XATTR_MAXNAMELEN);
2634
2635 /*
2636 * Call doubleagentd to look up the xattr. The fileport argument
2637 * is declared move-send, so the Mig stub consumes it.
2638 */
2639 kr = doubleagent_lookup_xattr(doubleagentd_port, fileport, fsize, cName,
2640 &error, &value_offset, &value_length);
2641 if (kr != KERN_SUCCESS) {
2642 error = EIO;
2643 }
2644 if (error == 0) {
2645 error = vnode_getwithref(xvp);
2646 }
2647 if (error) {
2648 goto out;
2649 }
2650 have_iocount = true;
2651 if (uio != NULL) {
2652 if (isrsrcfork) {
2653 // Resource Fork case is a bit different,
2654 // as we can have a non-zero uio offset.
2655 uio_setoffset(uio, uio_offset(uio) + value_offset);
2656 error = VNOP_READ(xvp, uio, 0, context);
2657 if (error == 0) {
2658 uio_setoffset(uio, uio_offset(uio) - value_offset);
2659 }
2660 } else {
2661 if (uio_resid(uio) < value_length) {
2662 error = ERANGE;
2663 goto out;
2664 }
2665
2666 // Read from the relevant offset in the AD file into the uio.
2667 user_ssize_t orig_resid = uio_resid(uio);
2668 uio_setoffset(uio, value_offset);
2669 uio_setresid(uio, value_length);
2670
2671 error = VNOP_READ(xvp, uio, 0, context);
2672
2673 uio_setoffset(uio, 0);
2674 uio_setresid(uio, orig_resid - value_length + uio_resid(uio));
2675 }
2676 }
2677
2678 *size = value_length;
2679
2680 out:
2681 if (xfg != NULL) {
2682 close_xattrfile(xfg, have_iocount, true, context);
2683 }
2684 return error;
2685 }
2686
2687 /*
2688 * Retrieve the list of extended attribute names.
2689 * (Using DoubleAgent to parse the AD file).
2690 */
2691 static int
default_listxattr_doubleagent(vnode_t vp,uio_t uio,size_t * size,__unused int options,vfs_context_t context,mach_port_t doubleagentd_port)2692 default_listxattr_doubleagent(vnode_t vp, uio_t uio, size_t *size,
2693 __unused int options, vfs_context_t context, mach_port_t doubleagentd_port)
2694 {
2695 vnode_t xvp = NULL;
2696 struct fileglob *xfg = NULL;
2697 int64_t fsize;
2698 int error;
2699
2700 mach_port_t fileport = MACH_PORT_NULL;
2701 kern_return_t kr;
2702 void *buf = NULL;
2703 listxattrs_result_t *result;
2704 bool have_iocount = true;
2705
2706 /*
2707 * We do not zero "*size" here as we don't want to stomp a size set
2708 * when VNOP_LISTXATTR() processed any native EAs. That size is
2709 * initially zeroed by the system call layer, up in listxattr() or
2710 * flistxattr().
2711 */
2712
2713 if ((error = open_xattrfile(vp, FREAD | O_SHLOCK, &xfg, &fsize,
2714 context))) {
2715 if (error == ENOATTR) {
2716 error = 0;
2717 }
2718 goto out;
2719 }
2720 xvp = fg_get_data(xfg);
2721 if ((error = make_xattrfile_port(xfg, &fileport))) {
2722 goto out;
2723 }
2724
2725 /* Drop the iocount before upcalling to doubleagentd. */
2726 vnode_put(xvp);
2727 have_iocount = false;
2728
2729 buf = kalloc_data(sizeof(listxattrs_result_t), Z_WAITOK);
2730 result = (listxattrs_result_t *)buf;
2731
2732 /*
2733 * Call doubleagentd to list the xattrs. The fileport argument
2734 * is declared move-send, so the Mig stub consumes it.
2735 */
2736 kr = doubleagent_list_xattrs(doubleagentd_port, fileport, fsize, &error,
2737 result);
2738 if (kr != KERN_SUCCESS) {
2739 error = EIO;
2740 }
2741 if (error == 0) {
2742 error = vnode_getwithref(xvp);
2743 }
2744 if (error) {
2745 goto out;
2746 }
2747 have_iocount = true;
2748
2749 if (uio != NULL) {
2750 if (uio_resid(uio) < result->namesLength) {
2751 error = ERANGE;
2752 goto out;
2753 }
2754 // copy the relevant part of the result into the uio.
2755 error = uiomove((const char *)result->data, (int)result->namesLength, uio);
2756 if (error) {
2757 if (error != EFAULT) {
2758 error = ERANGE;
2759 }
2760 goto out;
2761 }
2762 }
2763
2764 /*
2765 * Set *size, while preserving any previous value from
2766 * VNOP_LISTXATTR().
2767 */
2768 *size += result->namesLength;
2769
2770 out:
2771 if (xfg != NULL) {
2772 close_xattrfile(xfg, have_iocount, true, context);
2773 }
2774 if (buf != NULL) {
2775 kfree_data(buf, sizeof(listxattrs_result_t));
2776 }
2777 return error;
2778 }
2779
2780 /*
2781 * Set the data of an extended attribute.
2782 * (Using DoubleAgent to parse the AD file).
2783 */
2784 static int __attribute__((noinline))
default_setxattr_doubleagent(vnode_t vp,const char * name,uio_t uio,int options,vfs_context_t context,mach_port_t doubleagentd_port)2785 default_setxattr_doubleagent(vnode_t vp, const char *name, uio_t uio,
2786 int options, vfs_context_t context, mach_port_t doubleagentd_port)
2787 {
2788 vnode_t xvp = NULL;
2789 struct fileglob *xfg = NULL;
2790 size_t datalen;
2791 int namelen;
2792 int fileflags;
2793 int error;
2794 char cName[XATTR_MAXNAMELEN] = {0};
2795 char finfo[FINDERINFOSIZE];
2796 uio_t finfo_uio = NULL;
2797 mach_port_t fileport = MACH_PORT_NULL;
2798 uint64_t value_offset = 0;
2799 int64_t fsize;
2800 kern_return_t kr;
2801 bool have_iocount = true;
2802
2803 datalen = uio_resid(uio);
2804 if (datalen > XATTR_MAXSIZE) {
2805 return E2BIG;
2806 }
2807 namelen = (int)strlen(name) + 1;
2808 if (namelen > UINT8_MAX) {
2809 return EINVAL;
2810 }
2811
2812 /*
2813 * By convention, Finder Info that is all zeroes is equivalent to not
2814 * having a Finder Info EA. So if we're trying to set the Finder Info
2815 * to all zeroes, then delete it instead. If a file didn't have an
2816 * AppleDouble file before, this prevents creating an AppleDouble file
2817 * with no useful content.
2818 *
2819 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
2820 * for all zeroes Finder Info before opening the AppleDouble file.
2821 * But if either of those options were specified, we need to open the
2822 * AppleDouble file to see whether there was already Finder Info (so we
2823 * can return an error if needed); this case is handled in DoubleAgent.
2824 */
2825 if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
2826 if (uio_offset(uio) != 0) {
2827 return EINVAL;
2828 }
2829
2830 if (datalen != FINDERINFOSIZE) {
2831 return ERANGE;
2832 }
2833
2834 // Duplicate the uio to keep it as-is for later.
2835 finfo_uio = uio_duplicate(uio);
2836 // Get the finfo data from the duplicated uio.
2837 error = uiomove(finfo, (int)datalen, finfo_uio);
2838 uio_free(finfo_uio);
2839 if (error) {
2840 return error;
2841 }
2842 if ((options & (XATTR_CREATE | XATTR_REPLACE)) == 0 &&
2843 bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) {
2844 error = default_removexattr(vp, name, 0, context);
2845 if (error == ENOATTR) {
2846 error = 0;
2847 }
2848 return error;
2849 }
2850 }
2851
2852 if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
2853 /*
2854 * For ResourceFork we allow offset to be != 0, so adjust datalen accordingly
2855 * so doubleagent will adjust the file length accordingly
2856 *
2857 */
2858 if (__improbable(os_add_overflow(datalen, uio_offset(uio), &datalen))) {
2859 return EINVAL;
2860 }
2861
2862 if (datalen > UINT32_MAX) {
2863 return EINVAL;
2864 }
2865 }
2866
2867 /*
2868 * Open the file locked since setting an attribute
2869 * can change the layout of the Apple Double file.
2870 */
2871 fileflags = FREAD | FWRITE | O_EXLOCK;
2872 if ((error = open_xattrfile(vp, O_CREAT | fileflags, &xfg,
2873 &fsize, context))) {
2874 goto out;
2875 }
2876 xvp = fg_get_data(xfg);
2877 if ((error = make_xattrfile_port(xfg, &fileport))) {
2878 goto out;
2879 }
2880
2881 /* Drop the iocount before upcalling to doubleagentd. */
2882 vnode_put(xvp);
2883 have_iocount = false;
2884
2885 strncpy(cName, name, XATTR_MAXNAMELEN);
2886
2887 /*
2888 * Call doubleagentd to allocate space for the xattr. The
2889 * fileport argument is declared move-send, so the Mig stub
2890 * consumes it.
2891 */
2892 kr = doubleagent_allocate_xattr(doubleagentd_port, fileport, fsize,
2893 cName, datalen, options, &error, &value_offset);
2894 if (kr != KERN_SUCCESS) {
2895 error = EIO;
2896 }
2897 if (error == 0) {
2898 error = vnode_getwithref(xvp);
2899 }
2900 if (error) {
2901 goto out;
2902 }
2903 have_iocount = true;
2904
2905 /*
2906 * write the uio data into the offset we got from doubleagent,
2907 * while adding the given uio offset (could be non-zero only for
2908 * resource fork; it is being checked earlier).
2909 */
2910 uio_setoffset(uio, uio_offset(uio) + value_offset);
2911 error = VNOP_WRITE(xvp, uio, 0, context);
2912 uio_setoffset(uio, 0);
2913
2914 out:
2915 if (xfg != NULL) {
2916 close_xattrfile(xfg, have_iocount, true, context);
2917 }
2918
2919 /* Touch the change time if we changed an attribute. */
2920 if (error == 0) {
2921 struct vnode_attr va;
2922
2923 /* Re-write the mtime to cause a ctime change. */
2924 VATTR_INIT(&va);
2925 VATTR_WANTED(&va, va_modify_time);
2926 if (vnode_getattr(vp, &va, context) == 0) {
2927 VATTR_INIT(&va);
2928 VATTR_SET(&va, va_modify_time, va.va_modify_time);
2929 (void) vnode_setattr(vp, &va, context);
2930 }
2931 }
2932
2933 post_event_if_success(vp, error, NOTE_ATTRIB);
2934
2935 return error;
2936 }
2937
2938 /*
2939 * Remove an extended attribute.
2940 * (Using DoubleAgent to parse the AD file).
2941 */
2942 static int
default_removexattr_doubleagent(vnode_t vp,const char * name,__unused int options,vfs_context_t context,mach_port_t doubleagentd_port)2943 default_removexattr_doubleagent(vnode_t vp, const char *name,
2944 __unused int options, vfs_context_t context,
2945 mach_port_t doubleagentd_port)
2946 {
2947 vnode_t xvp = NULL;
2948 struct fileglob *xfg = NULL;
2949 int isrsrcfork;
2950 int fileflags;
2951 int error;
2952 int64_t fsize;
2953 boolean_t is_empty = false;
2954 char cName[XATTR_MAXNAMELEN] = {0};
2955 mach_port_t fileport = MACH_PORT_NULL;
2956 kern_return_t kr;
2957 bool have_iocount = true;
2958
2959 fileflags = FREAD | FWRITE | O_EXLOCK;
2960 isrsrcfork = strncmp(name, XATTR_RESOURCEFORK_NAME,
2961 sizeof(XATTR_RESOURCEFORK_NAME)) == 0;
2962
2963 if ((error = open_xattrfile(vp, fileflags, &xfg, &fsize, context))) {
2964 goto out;
2965 }
2966 xvp = fg_get_data(xfg);
2967 if ((error = make_xattrfile_port(xfg, &fileport))) {
2968 goto out;
2969 }
2970
2971 /* Drop the iocount before upcalling to doubleagentd. */
2972 vnode_put(xvp);
2973 have_iocount = false;
2974
2975 strncpy(cName, name, XATTR_MAXNAMELEN);
2976
2977 /*
2978 * Call doubleagentd to remove the xattr. The fileport argument
2979 * is declared move-send, so the Mig stub consumes it.
2980 */
2981 kr = doubleagent_remove_xattr(doubleagentd_port, fileport, fsize, cName,
2982 &error, &is_empty);
2983 if (kr != KERN_SUCCESS) {
2984 error = EIO;
2985 }
2986 if (error == 0) {
2987 error = vnode_getwithref(xvp);
2988 }
2989 if (error) {
2990 goto out;
2991 }
2992 have_iocount = true;
2993
2994 out:
2995 if (error == 0) {
2996 /* When there are no more attributes remove the ._ file. */
2997 if (is_empty) {
2998 remove_xattrfile(xfg, xvp, context);
2999 } else {
3000 close_xattrfile(xfg, have_iocount, true, context);
3001 }
3002 xfg = NULL;
3003
3004 /* Touch the change time if we changed an attribute. */
3005 struct vnode_attr va;
3006 /* Re-write the mtime to cause a ctime change. */
3007 VATTR_INIT(&va);
3008 VATTR_WANTED(&va, va_modify_time);
3009 if (vnode_getattr(vp, &va, context) == 0) {
3010 VATTR_INIT(&va);
3011 VATTR_SET(&va, va_modify_time, va.va_modify_time);
3012 (void) vnode_setattr(vp, &va, context);
3013 }
3014 }
3015
3016 post_event_if_success(vp, error, NOTE_ATTRIB);
3017
3018 if (xfg != NULL) {
3019 close_xattrfile(xfg, have_iocount, true, context);
3020 }
3021 return error;
3022 }
3023
3024 static int
open_xattrfile(vnode_t vp,int fileflags,struct fileglob ** xfgp,int64_t * file_sizep,vfs_context_t context)3025 open_xattrfile(vnode_t vp, int fileflags, struct fileglob **xfgp,
3026 int64_t *file_sizep, vfs_context_t context)
3027 {
3028 extern const struct fileops vnops; /* XXX */
3029 vnode_t xvp = NULLVP;
3030 vnode_t dvp = NULLVP;
3031 struct fileglob *fg = NULL;
3032 struct vnode_attr *va = NULL;
3033 struct nameidata *nd = NULL;
3034 char smallname[64];
3035 char *filename = NULL;
3036 const char *basename = NULL;
3037 size_t alloc_len = 0;
3038 size_t copy_len;
3039 errno_t error;
3040 int opened = 0;
3041 int referenced = 0;
3042 bool created_xattr_file = false;
3043
3044 if (vnode_isvroot(vp) && vnode_isdir(vp)) {
3045 /*
3046 * For the root directory use "._." to hold the attributes.
3047 */
3048 filename = &smallname[0];
3049 snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, ".");
3050 dvp = vp; /* the "._." file resides in the root dir */
3051 goto lookup;
3052 }
3053 if ((dvp = vnode_getparent(vp)) == NULLVP) {
3054 error = ENOATTR;
3055 goto out;
3056 }
3057 if ((basename = vnode_getname(vp)) == NULL) {
3058 error = ENOATTR;
3059 goto out;
3060 }
3061
3062 /* "._" Attribute files cannot have attributes */
3063 if (vp->v_type == VREG && strlen(basename) > 2 &&
3064 basename[0] == '.' && basename[1] == '_') {
3065 error = EPERM;
3066 goto out;
3067 }
3068 filename = &smallname[0];
3069 alloc_len = snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, basename);
3070 if (alloc_len >= sizeof(smallname)) {
3071 alloc_len++; /* snprintf result doesn't include '\0' */
3072 filename = kalloc_data(alloc_len, Z_WAITOK);
3073 copy_len = snprintf(filename, alloc_len, "%s%s", ATTR_FILE_PREFIX, basename);
3074 }
3075 /*
3076 * Note that the lookup here does not authorize. Since we are looking
3077 * up in the same directory that we already have the file vnode in,
3078 * we must have been given the file vnode legitimately. Read/write
3079 * access has already been authorized in layers above for calls from
3080 * userspace, and the authorization code using this path to read
3081 * file security from the EA must always get access
3082 */
3083 lookup:
3084 nd = kalloc_type(struct nameidata, Z_WAITOK);
3085 NDINIT(nd, LOOKUP, OP_OPEN, LOCKLEAF | NOFOLLOW | USEDVP | DONOTAUTH,
3086 UIO_SYSSPACE, CAST_USER_ADDR_T(filename), context);
3087 nd->ni_dvp = dvp;
3088
3089 va = kalloc_type(struct vnode_attr, Z_WAITOK);
3090
3091 if (fileflags & O_CREAT) {
3092 nd->ni_cnd.cn_nameiop = CREATE;
3093 #if CONFIG_TRIGGERS
3094 nd->ni_op = OP_LINK;
3095 #endif
3096 if (dvp != vp) {
3097 nd->ni_cnd.cn_flags |= LOCKPARENT;
3098 }
3099 if ((error = namei(nd))) {
3100 nd->ni_dvp = NULLVP;
3101 error = ENOATTR;
3102 goto out;
3103 }
3104 if ((xvp = nd->ni_vp) == NULLVP) {
3105 uid_t uid;
3106 gid_t gid;
3107 mode_t umode;
3108
3109 /*
3110 * Pick up uid/gid/mode from target file.
3111 */
3112 VATTR_INIT(va);
3113 VATTR_WANTED(va, va_uid);
3114 VATTR_WANTED(va, va_gid);
3115 VATTR_WANTED(va, va_mode);
3116 if (VNOP_GETATTR(vp, va, context) == 0 &&
3117 VATTR_IS_SUPPORTED(va, va_uid) &&
3118 VATTR_IS_SUPPORTED(va, va_gid) &&
3119 VATTR_IS_SUPPORTED(va, va_mode)) {
3120 uid = va->va_uid;
3121 gid = va->va_gid;
3122 umode = va->va_mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
3123 } else { /* fallback values */
3124 uid = KAUTH_UID_NONE;
3125 gid = KAUTH_GID_NONE;
3126 umode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
3127 }
3128
3129 VATTR_INIT(va);
3130 VATTR_SET(va, va_type, VREG);
3131 VATTR_SET(va, va_mode, umode);
3132 if (uid != KAUTH_UID_NONE) {
3133 VATTR_SET(va, va_uid, uid);
3134 }
3135 if (gid != KAUTH_GID_NONE) {
3136 VATTR_SET(va, va_gid, gid);
3137 }
3138
3139 error = vn_create(dvp, &nd->ni_vp, nd, va,
3140 VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT | VN_CREATE_NOLABEL,
3141 0, NULL,
3142 context);
3143 if (error) {
3144 error = ENOATTR;
3145 } else {
3146 xvp = nd->ni_vp;
3147 created_xattr_file = true;
3148 }
3149 }
3150 nameidone(nd);
3151 if (dvp != vp) {
3152 vnode_put(dvp); /* drop iocount from LOCKPARENT request above */
3153 }
3154 if (error) {
3155 goto out;
3156 }
3157 } else {
3158 if ((error = namei(nd))) {
3159 nd->ni_dvp = NULLVP;
3160 error = ENOATTR;
3161 goto out;
3162 }
3163 xvp = nd->ni_vp;
3164 nameidone(nd);
3165 }
3166 nd->ni_dvp = NULLVP;
3167
3168 if (xvp->v_type != VREG) {
3169 error = ENOATTR;
3170 goto out;
3171 }
3172 /*
3173 * Owners must match.
3174 */
3175 VATTR_INIT(va);
3176 VATTR_WANTED(va, va_uid);
3177 if (VNOP_GETATTR(vp, va, context) == 0 && VATTR_IS_SUPPORTED(va, va_uid)) {
3178 uid_t owner = va->va_uid;
3179
3180 VATTR_INIT(va);
3181 VATTR_WANTED(va, va_uid);
3182 if (VNOP_GETATTR(xvp, va, context) == 0 && (owner != va->va_uid)) {
3183 error = ENOATTR; /* don't use this "._" file */
3184 goto out;
3185 }
3186 }
3187
3188 if ((error = VNOP_OPEN(xvp, fileflags & ~(O_EXLOCK | O_SHLOCK), context))) {
3189 error = ENOATTR;
3190 goto out;
3191 }
3192 opened = 1;
3193
3194 if ((error = vnode_ref_ext(xvp, fileflags, 0)) != 0) {
3195 goto out;
3196 }
3197 referenced = 1;
3198
3199 /*
3200 * If create was requested, make sure file header exists.
3201 * This is only done in the non-DoubleAgent case.
3202 * XXX And will be garbage-collected in due time.
3203 */
3204 if (!vfs_xattr_doubleagent_enabled && (fileflags & O_CREAT) != 0) {
3205 VATTR_INIT(va);
3206 VATTR_WANTED(va, va_data_size);
3207 VATTR_WANTED(va, va_fileid);
3208 VATTR_WANTED(va, va_nlink);
3209 if ((error = vnode_getattr(xvp, va, context)) != 0) {
3210 error = EPERM;
3211 goto out;
3212 }
3213
3214 /* If the file is empty then add a default header. */
3215 if (va->va_data_size == 0) {
3216 /* Don't adopt hard-linked "._" files. */
3217 if (VATTR_IS_SUPPORTED(va, va_nlink) && va->va_nlink > 1) {
3218 error = EPERM;
3219 goto out;
3220 }
3221 if ((error = create_xattrfile(xvp, (u_int32_t)va->va_fileid, context))) {
3222 goto out;
3223 }
3224 }
3225 }
3226
3227 /*
3228 * Allocate a file object for the referenced vnode.
3229 * This file object now owns the vnode reference,
3230 * and the caller owns the iocount, which will be
3231 * dropped in close_xattrfile().
3232 */
3233 fg = fg_alloc_init(context);
3234 fg->fg_flag = fileflags & FMASK;
3235 fg->fg_ops = &vnops;
3236 fg_set_data(fg, xvp);
3237
3238 /* Apply file locking if requested. */
3239 if (fileflags & (O_EXLOCK | O_SHLOCK)) {
3240 struct flock lf = {
3241 .l_whence = SEEK_SET,
3242 };
3243
3244 if (fileflags & O_EXLOCK) {
3245 lf.l_type = F_WRLCK;
3246 } else {
3247 lf.l_type = F_RDLCK;
3248 }
3249 error = VNOP_ADVLOCK(xvp, (caddr_t)fg, F_SETLK, &lf, F_FLOCK | F_WAIT, context, NULL);
3250 if (error == ENOTSUP) {
3251 error = 0;
3252 } else if (error) {
3253 error = ENOATTR;
3254 goto out;
3255 } else { // error == 0
3256 fg->fg_flag |= FWASLOCKED;
3257 }
3258 }
3259
3260 if (file_sizep != NULL) {
3261 /*
3262 * Now that the file is locked, get the file's size.
3263 */
3264 VATTR_INIT(va);
3265 VATTR_WANTED(va, va_data_size);
3266 if ((error = vnode_getattr(xvp, va, context)) != 0) {
3267 error = ENOATTR;
3268 goto out;
3269 }
3270 *file_sizep = va->va_data_size;
3271 }
3272 out:
3273 if (error) {
3274 if (fg != NULL) {
3275 /* Let the normal close path handle this. */
3276 if (created_xattr_file) {
3277 remove_xattrfile(fg, xvp, context);
3278 } else {
3279 close_xattrfile(fg, true, true, context);
3280 }
3281 fg = NULL;
3282 xvp = NULLVP;
3283 } else if (xvp != NULLVP) {
3284 if (opened) {
3285 (void) VNOP_CLOSE(xvp, fileflags, context);
3286 }
3287 if (created_xattr_file) {
3288 remove_xattrfile(NULL, xvp, context);
3289 }
3290 if (referenced) {
3291 (void) vnode_rele(xvp);
3292 }
3293 /* remove_xattrfile() would have dropped the iocount already */
3294 if (!created_xattr_file) {
3295 (void) vnode_put(xvp);
3296 }
3297 xvp = NULLVP;
3298 }
3299 if ((error == ENOATTR) && (fileflags & O_CREAT)) {
3300 error = EPERM;
3301 }
3302 }
3303
3304 /* Release resources after error-handling */
3305 kfree_type(struct nameidata, nd);
3306 kfree_type(struct vnode_attr, va);
3307 if (dvp && (dvp != vp)) {
3308 vnode_put(dvp);
3309 }
3310 if (basename) {
3311 vnode_putname(basename);
3312 }
3313 if (filename && filename != &smallname[0]) {
3314 kfree_data(filename, alloc_len);
3315 }
3316
3317 *xfgp = fg;
3318 return error;
3319 }
3320
3321 static void
close_xattrfile(struct fileglob * xfg,bool have_iocount,bool drop_iocount,vfs_context_t context)3322 close_xattrfile(struct fileglob *xfg, bool have_iocount, bool drop_iocount,
3323 vfs_context_t context)
3324 {
3325 vnode_t xvp = fg_get_data(xfg);
3326
3327 /*
3328 * N.B. The only time have_iocount would be false would be when
3329 * a vnode_getwithref() calls fails after coming back from a
3330 * doubleagentd upcall. If that happens, then it would mean
3331 * that the old vnode identity is gone, and our advisory lock
3332 * would have been garbage-collected when the vnode was reclaimed.
3333 */
3334 if (have_iocount) {
3335 /*
3336 * fg_drop() won't drop our advisory lock because we are not
3337 * following POSIX semantics. Drop it here.
3338 */
3339 struct flock lf = {
3340 .l_whence = SEEK_SET,
3341 .l_type = F_UNLCK,
3342 };
3343 (void)VNOP_ADVLOCK(xvp, (caddr_t)xfg, F_UNLCK, &lf, F_FLOCK,
3344 context, NULL);
3345
3346 /* (Maybe) drop the iocount we took in open_xattrfile(). */
3347 if (drop_iocount) {
3348 vnode_put(xvp);
3349 }
3350 }
3351
3352 (void) fg_drop(current_proc(), xfg);
3353 }
3354
3355 static void
remove_xattrfile(struct fileglob * xfg,vnode_t xvp,vfs_context_t context)3356 remove_xattrfile(struct fileglob *xfg, vnode_t xvp, vfs_context_t context)
3357 {
3358 vnode_t dvp = NULL, rvp = NULL;
3359 struct nameidata nd;
3360 char *path = NULL;
3361 int pathlen;
3362 int error;
3363
3364 if (xfg != NULL) {
3365 /*
3366 * Close the xattr file but don't dispose of the
3367 * iocount acquired in open_xattrfile() while doing
3368 * so. We'll do that below once we have performed
3369 * the unlink operation.
3370 */
3371 close_xattrfile(xfg, true, false, context);
3372 }
3373
3374 path = zalloc(ZV_NAMEI);
3375 pathlen = MAXPATHLEN;
3376 error = vn_getpath(xvp, path, &pathlen);
3377 if (error) {
3378 zfree(ZV_NAMEI, path);
3379 goto out;
3380 }
3381
3382 NDINIT(&nd, DELETE, OP_UNLINK, LOCKPARENT | NOFOLLOW | DONOTAUTH,
3383 UIO_SYSSPACE, CAST_USER_ADDR_T(path), context);
3384 error = namei(&nd);
3385 zfree(ZV_NAMEI, path);
3386 if (error) {
3387 goto out;
3388 }
3389 dvp = nd.ni_dvp;
3390 rvp = nd.ni_vp;
3391
3392 /*
3393 * Only remove if the namei() returned to us the same vnode that
3394 * we think we are supposed to be removing. If they're not the
3395 * same, we could have raced against something else trying to
3396 * unlink it, and we don't want to remove someone else's (possibly
3397 * very important) file.
3398 */
3399 if (rvp == xvp) {
3400 (void) VNOP_REMOVE(dvp, rvp, &nd.ni_cnd, 0, context);
3401 }
3402 nameidone(&nd);
3403
3404 out:
3405 vnode_put(xvp);
3406 if (dvp != NULLVP) {
3407 vnode_put(dvp);
3408 }
3409 if (rvp != NULLVP) {
3410 vnode_put(rvp);
3411 }
3412 }
3413
3414 /*
3415 * Read in and parse the AppleDouble header and entries, and the extended
3416 * attribute header and entries if any. Populates the fields of ainfop
3417 * based on the headers and entries found.
3418 *
3419 * The basic idea is to:
3420 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
3421 * AppleDouble entries, the extended attribute header, and extended
3422 * attribute entries must lie within this part of the file; the rest of
3423 * the AppleDouble handling code assumes this. Plus it allows us to
3424 * somewhat optimize by doing a smaller number of larger I/Os.
3425 * - Swap and sanity check the AppleDouble header (including the AppleDouble
3426 * entries).
3427 * - Find the Finder Info and Resource Fork entries, if any.
3428 * - If we're going to be writing, try to make sure the Finder Info entry has
3429 * room to store the extended attribute header, plus some space for extended
3430 * attributes.
3431 * - Swap and sanity check the extended attribute header and entries (if any).
3432 */
3433 static int
get_xattrinfo(vnode_t xvp,int setting,attr_info_t * ainfop,vfs_context_t context)3434 get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context)
3435 {
3436 uio_t auio = NULL;
3437 void * buffer = NULL;
3438 apple_double_header_t *filehdr;
3439 struct vnode_attr va;
3440 size_t iosize = 0;
3441 int i;
3442 int error;
3443
3444 bzero(ainfop, sizeof(attr_info_t));
3445 ainfop->filevp = xvp;
3446 ainfop->context = context;
3447 VATTR_INIT(&va);
3448 VATTR_WANTED(&va, va_data_size);
3449 VATTR_WANTED(&va, va_fileid);
3450 if ((error = vnode_getattr(xvp, &va, context))) {
3451 goto bail;
3452 }
3453 ainfop->filesize = va.va_data_size;
3454
3455 /* When setting attributes, allow room for the header to grow. */
3456 if (setting) {
3457 iosize = ATTR_MAX_HDR_SIZE;
3458 } else {
3459 iosize = MIN(ATTR_MAX_HDR_SIZE, ainfop->filesize);
3460 }
3461
3462 if (iosize == 0 || iosize < sizeof(apple_double_header_t)) {
3463 error = ENOATTR;
3464 goto bail;
3465 }
3466
3467 ainfop->iosize = iosize;
3468 buffer = kalloc_data(iosize, Z_WAITOK | Z_ZERO);
3469 if (buffer == NULL) {
3470 error = ENOMEM;
3471 goto bail;
3472 }
3473
3474 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
3475 uio_addiov(auio, (uintptr_t)buffer, iosize);
3476
3477 /* Read the file header. */
3478 error = VNOP_READ(xvp, auio, 0, context);
3479 if (error) {
3480 goto bail;
3481 }
3482 ainfop->rawsize = iosize - uio_resid(auio);
3483 ainfop->rawdata = (u_int8_t *)buffer;
3484
3485 filehdr = (apple_double_header_t *)buffer;
3486
3487 error = check_and_swap_apple_double_header(ainfop);
3488 if (error) {
3489 goto bail;
3490 }
3491
3492 ainfop->filehdr = filehdr; /* valid AppleDouble header */
3493
3494 /* rel_xattrinfo is responsible for freeing the header buffer */
3495 buffer = NULL;
3496
3497 /* Find the Finder Info and Resource Fork entries, if any */
3498 for (i = 0; i < filehdr->numEntries; ++i) {
3499 if (filehdr->entries[i].type == AD_FINDERINFO &&
3500 filehdr->entries[i].length >= FINDERINFOSIZE) {
3501 /* We found the Finder Info entry. */
3502 ainfop->finderinfo = &filehdr->entries[i];
3503
3504 /* At this point check_and_swap_apple_double_header() call above
3505 * verified that all apple double entires are valid:
3506 * they point somewhere within the file.
3507 *
3508 * Now for finderinfo make sure that the fixed portion
3509 * is within the buffer we read in.
3510 */
3511 if (((ainfop->finderinfo->offset + FINDERINFOSIZE) > ainfop->finderinfo->offset) &&
3512 ((ainfop->finderinfo->offset + FINDERINFOSIZE) <= ainfop->rawsize)) {
3513 /*
3514 * Is the Finder Info "empty" (all zeroes)? If so,
3515 * we'll pretend like the Finder Info extended attribute
3516 * does not exist.
3517 */
3518 if (bcmp((u_int8_t*)ainfop->filehdr + ainfop->finderinfo->offset, emptyfinfo, sizeof(emptyfinfo)) == 0) {
3519 ainfop->emptyfinderinfo = 1;
3520 }
3521 } else {
3522 error = ENOATTR;
3523 goto bail;
3524 }
3525 }
3526 if (filehdr->entries[i].type == AD_RESOURCE) {
3527 /*
3528 * Ignore zero-length resource forks when getting. If setting,
3529 * we need to remember the resource fork entry so it can be
3530 * updated once the new content has been written.
3531 */
3532 if (filehdr->entries[i].length == 0 && !setting) {
3533 continue;
3534 }
3535
3536 /*
3537 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
3538 *
3539 * The "empty" resource headers we created have a system data tag of:
3540 * "This resource fork intentionally left blank "
3541 */
3542 if (filehdr->entries[i].length == sizeof(rsrcfork_header_t) && !setting) {
3543 uio_t rf_uio;
3544 u_int8_t systemData[64];
3545 int rf_err;
3546
3547
3548 /* Read the system data which starts at byte 16 */
3549 rf_uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
3550 uio_addiov(rf_uio, (uintptr_t)systemData, sizeof(systemData));
3551 uio_setoffset(rf_uio, filehdr->entries[i].offset + 16);
3552 rf_err = VNOP_READ(xvp, rf_uio, 0, context);
3553 uio_free(rf_uio);
3554
3555 if (rf_err != 0 ||
3556 bcmp(systemData, RF_EMPTY_TAG, sizeof(RF_EMPTY_TAG)) == 0) {
3557 continue; /* skip this resource fork */
3558 }
3559 }
3560 ainfop->rsrcfork = &filehdr->entries[i];
3561 if (i != (filehdr->numEntries - 1)) {
3562 printf("get_xattrinfo: resource fork not last entry\n");
3563 ainfop->readonly = 1;
3564 }
3565 continue;
3566 }
3567 }
3568
3569 /*
3570 * See if this file looks like it is laid out correctly to contain
3571 * extended attributes. If so, then do the following:
3572 *
3573 * - If we're going to be writing, try to make sure the Finder Info
3574 * entry has room to store the extended attribute header, plus some
3575 * space for extended attributes.
3576 *
3577 * - Swap and sanity check the extended attribute header and entries
3578 * (if any).
3579 */
3580 if (filehdr->numEntries == 2 &&
3581 ainfop->finderinfo == &filehdr->entries[0] &&
3582 ainfop->rsrcfork == &filehdr->entries[1] &&
3583 ainfop->finderinfo->offset == offsetof(apple_double_header_t, finfo)) {
3584 attr_header_t *attrhdr;
3585 attrhdr = (attr_header_t *)filehdr;
3586 /*
3587 * If we're going to be writing, try to make sure the Finder
3588 * Info entry has room to store the extended attribute header,
3589 * plus some space for extended attributes.
3590 */
3591 if (setting && ainfop->finderinfo->length == FINDERINFOSIZE) {
3592 size_t delta;
3593 size_t writesize;
3594
3595 delta = ATTR_BUF_SIZE - (filehdr->entries[0].offset + FINDERINFOSIZE);
3596 if (ainfop->rsrcfork && filehdr->entries[1].length) {
3597 /* Make some room before existing resource fork. */
3598 shift_data_down(xvp,
3599 filehdr->entries[1].offset,
3600 filehdr->entries[1].length,
3601 delta, context);
3602 writesize = sizeof(attr_header_t);
3603 } else {
3604 /* We are in case where existing resource fork of length 0, try to create a new, empty resource fork. */
3605 rsrcfork_header_t *rsrcforkhdr;
3606
3607 /* Do we have enough space in the header buffer for empty resource fork */
3608 if (filehdr->entries[1].offset + delta + sizeof(rsrcfork_header_t) > ainfop->iosize) {
3609 /* we do not have space, bail for now */
3610 error = ENOATTR;
3611 goto bail;
3612 }
3613
3614 vnode_setsize(xvp, filehdr->entries[1].offset + delta, 0, context);
3615
3616 /* Steal some space for an empty RF header. */
3617 delta -= sizeof(rsrcfork_header_t);
3618
3619 bzero(&attrhdr->appledouble.pad[0], delta);
3620 rsrcforkhdr = (rsrcfork_header_t *)((char *)filehdr + filehdr->entries[1].offset + delta);
3621
3622 /* Fill in Empty Resource Fork Header. */
3623 init_empty_resource_fork(rsrcforkhdr);
3624
3625 filehdr->entries[1].length = sizeof(rsrcfork_header_t);
3626 writesize = ATTR_BUF_SIZE;
3627 }
3628 filehdr->entries[0].length += delta;
3629 filehdr->entries[1].offset += delta;
3630
3631 /* Fill in Attribute Header. */
3632 attrhdr->magic = ATTR_HDR_MAGIC;
3633 attrhdr->debug_tag = (u_int32_t)va.va_fileid;
3634 attrhdr->total_size = filehdr->entries[1].offset;
3635 attrhdr->data_start = sizeof(attr_header_t);
3636 attrhdr->data_length = 0;
3637 attrhdr->reserved[0] = 0;
3638 attrhdr->reserved[1] = 0;
3639 attrhdr->reserved[2] = 0;
3640 attrhdr->flags = 0;
3641 attrhdr->num_attrs = 0;
3642
3643 /* Push out new header */
3644 uio_reset(auio, 0, UIO_SYSSPACE, UIO_WRITE);
3645 uio_addiov(auio, (uintptr_t)filehdr, writesize);
3646
3647 swap_adhdr(filehdr); /* to big endian */
3648 swap_attrhdr(attrhdr, ainfop); /* to big endian */
3649 error = VNOP_WRITE(xvp, auio, 0, context);
3650 swap_adhdr(filehdr); /* back to native */
3651 /* The attribute header gets swapped below. */
3652 }
3653 }
3654 /*
3655 * Swap and sanity check the extended attribute header and
3656 * entries (if any). The Finder Info content must be big enough
3657 * to include the extended attribute header; if not, we just
3658 * ignore it.
3659 *
3660 * Note that we're passing the offset + length (i.e. the end)
3661 * of the Finder Info instead of rawsize to validate_attrhdr.
3662 * This ensures that all extended attributes lie within the
3663 * Finder Info content according to the AppleDouble entry.
3664 *
3665 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
3666 * header was found.
3667 */
3668 if (ainfop->finderinfo &&
3669 ainfop->finderinfo == &filehdr->entries[0] &&
3670 ainfop->finderinfo->length >= (sizeof(attr_header_t) - sizeof(apple_double_header_t))) {
3671 attr_header_t *attrhdr = (attr_header_t*)filehdr;
3672
3673 if (ainfop->finderinfo->offset != offsetof(apple_double_header_t, finfo)) {
3674 error = ENOATTR;
3675 goto bail;
3676 }
3677
3678 if ((error = check_and_swap_attrhdr(attrhdr, ainfop)) == 0) {
3679 ainfop->attrhdr = attrhdr; /* valid attribute header */
3680 /* First attr_entry starts immediately following attribute header */
3681 ainfop->attr_entry = (attr_entry_t *)&attrhdr[1];
3682 }
3683 }
3684
3685 error = 0;
3686 bail:
3687 if (auio != NULL) {
3688 uio_free(auio);
3689 }
3690 kfree_data(buffer, iosize);
3691 return error;
3692 }
3693
3694
3695 static int
create_xattrfile(vnode_t xvp,u_int32_t fileid,vfs_context_t context)3696 create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context)
3697 {
3698 attr_header_t *xah;
3699 rsrcfork_header_t *rsrcforkhdr;
3700 void * buffer;
3701 uio_t auio;
3702 int rsrcforksize;
3703 int error;
3704
3705 buffer = kalloc_data(ATTR_BUF_SIZE, Z_WAITOK | Z_ZERO);
3706
3707 xah = (attr_header_t *)buffer;
3708 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
3709 uio_addiov(auio, (uintptr_t)buffer, ATTR_BUF_SIZE);
3710 rsrcforksize = sizeof(rsrcfork_header_t);
3711 rsrcforkhdr = (rsrcfork_header_t *) ((char *)buffer + ATTR_BUF_SIZE - rsrcforksize);
3712
3713 /* Fill in Apple Double Header. */
3714 xah->appledouble.magic = SWAP32(ADH_MAGIC);
3715 xah->appledouble.version = SWAP32(ADH_VERSION);
3716 xah->appledouble.numEntries = SWAP16(2);
3717 xah->appledouble.entries[0].type = SWAP32(AD_FINDERINFO);
3718 xah->appledouble.entries[0].offset = SWAP32(offsetof(apple_double_header_t, finfo));
3719 xah->appledouble.entries[0].length = SWAP32(ATTR_BUF_SIZE - offsetof(apple_double_header_t, finfo) - rsrcforksize);
3720 xah->appledouble.entries[1].type = SWAP32(AD_RESOURCE);
3721 xah->appledouble.entries[1].offset = SWAP32(ATTR_BUF_SIZE - rsrcforksize);
3722 xah->appledouble.entries[1].length = SWAP32(rsrcforksize);
3723 bcopy(ADH_MACOSX, xah->appledouble.filler, sizeof(xah->appledouble.filler));
3724
3725 /* Fill in Attribute Header. */
3726 xah->magic = SWAP32(ATTR_HDR_MAGIC);
3727 xah->debug_tag = SWAP32(fileid);
3728 xah->total_size = SWAP32(ATTR_BUF_SIZE - rsrcforksize);
3729 xah->data_start = SWAP32(sizeof(attr_header_t));
3730
3731 /* Fill in Empty Resource Fork Header. */
3732 init_empty_resource_fork(rsrcforkhdr);
3733
3734 /* Push it out. */
3735 error = VNOP_WRITE(xvp, auio, IO_UNIT, context);
3736
3737 /* Did we write out the full uio? */
3738 if (uio_resid(auio) > 0) {
3739 error = ENOSPC;
3740 }
3741
3742 uio_free(auio);
3743 kfree_data(buffer, ATTR_BUF_SIZE);
3744
3745 return error;
3746 }
3747
3748 static void
init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr)3749 init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr)
3750 {
3751 bzero(rsrcforkhdr, sizeof(rsrcfork_header_t));
3752 rsrcforkhdr->fh_DataOffset = SWAP32(RF_FIRST_RESOURCE);
3753 rsrcforkhdr->fh_MapOffset = SWAP32(RF_FIRST_RESOURCE);
3754 rsrcforkhdr->fh_MapLength = SWAP32(RF_NULL_MAP_LENGTH);
3755 rsrcforkhdr->mh_DataOffset = SWAP32(RF_FIRST_RESOURCE);
3756 rsrcforkhdr->mh_MapOffset = SWAP32(RF_FIRST_RESOURCE);
3757 rsrcforkhdr->mh_MapLength = SWAP32(RF_NULL_MAP_LENGTH);
3758 rsrcforkhdr->mh_Types = SWAP16(RF_NULL_MAP_LENGTH - 2 );
3759 rsrcforkhdr->mh_Names = SWAP16(RF_NULL_MAP_LENGTH);
3760 rsrcforkhdr->typeCount = SWAP16(-1);
3761 bcopy(RF_EMPTY_TAG, rsrcforkhdr->systemData, sizeof(RF_EMPTY_TAG));
3762 }
3763
3764 static void
rel_xattrinfo(attr_info_t * ainfop)3765 rel_xattrinfo(attr_info_t *ainfop)
3766 {
3767 kfree_data_addr(ainfop->filehdr);
3768 bzero(ainfop, sizeof(attr_info_t));
3769 }
3770
3771 static int
write_xattrinfo(attr_info_t * ainfop)3772 write_xattrinfo(attr_info_t *ainfop)
3773 {
3774 uio_t auio;
3775 int error;
3776
3777 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
3778 uio_addiov(auio, (uintptr_t)ainfop->filehdr, ainfop->iosize);
3779
3780 swap_adhdr(ainfop->filehdr);
3781 if (ainfop->attrhdr != NULL) {
3782 swap_attrhdr(ainfop->attrhdr, ainfop);
3783 }
3784
3785 error = VNOP_WRITE(ainfop->filevp, auio, 0, ainfop->context);
3786
3787 swap_adhdr(ainfop->filehdr);
3788 if (ainfop->attrhdr != NULL) {
3789 swap_attrhdr(ainfop->attrhdr, ainfop);
3790 }
3791 uio_free(auio);
3792
3793 return error;
3794 }
3795
3796 #if BYTE_ORDER == LITTLE_ENDIAN
3797 /*
3798 * Endian swap apple double header
3799 */
3800 static void
swap_adhdr(apple_double_header_t * adh)3801 swap_adhdr(apple_double_header_t *adh)
3802 {
3803 int count;
3804 int i;
3805
3806 count = (adh->magic == ADH_MAGIC) ? adh->numEntries : SWAP16(adh->numEntries);
3807
3808 adh->magic = SWAP32(adh->magic);
3809 adh->version = SWAP32(adh->version);
3810 adh->numEntries = SWAP16(adh->numEntries);
3811
3812 for (i = 0; i < count; i++) {
3813 adh->entries[i].type = SWAP32(adh->entries[i].type);
3814 adh->entries[i].offset = SWAP32(adh->entries[i].offset);
3815 adh->entries[i].length = SWAP32(adh->entries[i].length);
3816 }
3817 }
3818
3819 /*
3820 * Endian swap extended attributes header
3821 */
3822 static void
swap_attrhdr(attr_header_t * ah,attr_info_t * info)3823 swap_attrhdr(attr_header_t *ah, attr_info_t* info)
3824 {
3825 attr_entry_t *ae;
3826 int count;
3827 int i;
3828
3829 count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs);
3830
3831 ah->magic = SWAP32(ah->magic);
3832 ah->debug_tag = SWAP32(ah->debug_tag);
3833 ah->total_size = SWAP32(ah->total_size);
3834 ah->data_start = SWAP32(ah->data_start);
3835 ah->data_length = SWAP32(ah->data_length);
3836 ah->flags = SWAP16(ah->flags);
3837 ah->num_attrs = SWAP16(ah->num_attrs);
3838
3839 ae = (attr_entry_t *)(&ah[1]);
3840 for (i = 0; i < count && ATTR_VALID(ae, *info); i++, ae = ATTR_NEXT(ae)) {
3841 ae->offset = SWAP32(ae->offset);
3842 ae->length = SWAP32(ae->length);
3843 ae->flags = SWAP16(ae->flags);
3844 }
3845 }
3846 #endif
3847
3848 /*
3849 * Validate and swap the attributes header contents, and each attribute's
3850 * attr_entry_t.
3851 *
3852 * Note: Assumes the caller has verified that the Finder Info content is large
3853 * enough to contain the attr_header structure itself. Therefore, we can
3854 * swap the header fields before sanity checking them.
3855 */
3856 static int
check_and_swap_attrhdr(attr_header_t * ah,attr_info_t * ainfop)3857 check_and_swap_attrhdr(attr_header_t *ah, attr_info_t *ainfop)
3858 {
3859 attr_entry_t *ae;
3860 u_int8_t *buf_end;
3861 u_int32_t end;
3862 int count;
3863 int i;
3864 uint32_t total_header_size;
3865 uint32_t total_data_size;
3866
3867 if (ah == NULL) {
3868 return EINVAL;
3869 }
3870
3871 if (SWAP32(ah->magic) != ATTR_HDR_MAGIC) {
3872 return EINVAL;
3873 }
3874
3875 /* Swap the basic header fields */
3876 ah->magic = SWAP32(ah->magic);
3877 ah->debug_tag = SWAP32(ah->debug_tag);
3878 ah->total_size = SWAP32(ah->total_size);
3879 ah->data_start = SWAP32(ah->data_start);
3880 ah->data_length = SWAP32(ah->data_length);
3881 ah->flags = SWAP16(ah->flags);
3882 ah->num_attrs = SWAP16(ah->num_attrs);
3883
3884 /*
3885 * Make sure the total_size fits within the Finder Info area, and the
3886 * extended attribute data area fits within total_size.
3887 */
3888 end = ah->data_start + ah->data_length;
3889 if (ah->total_size > ainfop->finderinfo->offset + ainfop->finderinfo->length ||
3890 ah->data_start < sizeof(attr_header_t) ||
3891 end < ah->data_start ||
3892 end > ah->total_size) {
3893 return EINVAL;
3894 }
3895
3896 /*
3897 * Make sure each of the attr_entry_t's fits within total_size.
3898 */
3899 buf_end = ainfop->rawdata + ah->data_start;
3900 if (buf_end > ainfop->rawdata + ainfop->rawsize) {
3901 return EINVAL;
3902 }
3903 count = ah->num_attrs;
3904 if (count > 256) {
3905 return EINVAL;
3906 }
3907 ae = (attr_entry_t *)(&ah[1]);
3908
3909 total_header_size = sizeof(attr_header_t);
3910 total_data_size = 0;
3911 for (i = 0; i < count; i++) {
3912 /* Make sure the fixed-size part of this attr_entry_t fits. */
3913 if ((u_int8_t *) &ae[1] > buf_end) {
3914 return EINVAL;
3915 }
3916
3917 /* Make sure the variable-length name fits */
3918 if (&ae->name[ae->namelen] > buf_end) {
3919 return EINVAL;
3920 }
3921
3922 /* Make sure that namelen is matching name's real length, namelen included NUL */
3923 if (strnlen((const char *)ae->name, ae->namelen) != ae->namelen - 1) {
3924 return EINVAL;
3925 }
3926
3927 /* Swap the attribute entry fields */
3928 ae->offset = SWAP32(ae->offset);
3929 ae->length = SWAP32(ae->length);
3930 ae->flags = SWAP16(ae->flags);
3931
3932 /* Make sure the attribute content fits and points to the data part */
3933 end = ae->offset + ae->length;
3934 if (end < ae->offset || end > ah->total_size) {
3935 return EINVAL;
3936 }
3937
3938 /* Make sure entry points to data section and not header */
3939 if (ae->offset < ah->data_start || end > ah->data_start + ah->data_length) {
3940 return EINVAL;
3941 }
3942
3943 /* We verified namelen is ok above, so add this entry's size to a total */
3944 if (os_add_overflow(total_header_size, ATTR_ENTRY_LENGTH(ae->namelen), &total_header_size)) {
3945 return EINVAL;
3946 }
3947
3948 /* We verified that entry's length is within data section, so add it to running size total */
3949 if (os_add_overflow(total_data_size, ae->length, &total_data_size)) {
3950 return EINVAL;
3951 }
3952
3953 ae = ATTR_NEXT(ae);
3954 }
3955
3956
3957 /* make sure data_start is actually after all the xattr key entries */
3958 if (ah->data_start < total_header_size) {
3959 return EINVAL;
3960 }
3961
3962 /* make sure all entries' data length add to header's idea of data length */
3963 if (total_data_size != ah->data_length) {
3964 return EINVAL;
3965 }
3966
3967 return 0;
3968 }
3969
3970 //
3971 // "start" & "end" are byte offsets in the file.
3972 // "to" is the byte offset we want to move the
3973 // data to. "to" should be > "start".
3974 //
3975 // we do the copy backwards to avoid problems if
3976 // there's an overlap.
3977 //
3978 static int
shift_data_down(vnode_t xvp,off_t start,size_t len,off_t delta,vfs_context_t context)3979 shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context)
3980 {
3981 int ret, iolen;
3982 size_t chunk, orig_chunk;
3983 char *buff;
3984 off_t pos;
3985 kauth_cred_t ucred = vfs_context_ucred(context);
3986 proc_t p = vfs_context_proc(context);
3987
3988 if (delta == 0 || len == 0) {
3989 return 0;
3990 }
3991
3992 chunk = 4096;
3993 if (len < chunk) {
3994 chunk = len;
3995 }
3996 orig_chunk = chunk;
3997
3998 buff = kalloc_data(chunk, Z_WAITOK);
3999 if (buff == NULL) {
4000 return ENOMEM;
4001 }
4002
4003 for (pos = start + len - chunk; pos >= start; pos -= chunk) {
4004 ret = vn_rdwr(UIO_READ, xvp, buff, (int)chunk, pos, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p);
4005 if (iolen != 0) {
4006 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
4007 pos, ret, chunk, ret);
4008 break;
4009 }
4010
4011 ret = vn_rdwr(UIO_WRITE, xvp, buff, (int)chunk, pos + delta, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p);
4012 if (iolen != 0) {
4013 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
4014 pos + delta, ret, chunk, ret);
4015 break;
4016 }
4017
4018 if ((pos - (off_t)chunk) < start) {
4019 chunk = pos - start;
4020
4021 if (chunk == 0) { // we're all done
4022 break;
4023 }
4024 }
4025 }
4026
4027 kfree_data(buff, orig_chunk);
4028 return 0;
4029 }
4030
4031
4032 static int
shift_data_up(vnode_t xvp,off_t start,size_t len,off_t delta,vfs_context_t context)4033 shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context)
4034 {
4035 int ret, iolen;
4036 size_t chunk, orig_chunk;
4037 char *buff;
4038 off_t pos;
4039 off_t end;
4040 kauth_cred_t ucred = vfs_context_ucred(context);
4041 proc_t p = vfs_context_proc(context);
4042
4043 if (delta == 0 || len == 0) {
4044 return 0;
4045 }
4046
4047 chunk = 4096;
4048 if (len < chunk) {
4049 chunk = len;
4050 }
4051 orig_chunk = chunk;
4052 end = start + len;
4053
4054 buff = kalloc_data(chunk, Z_WAITOK);
4055 if (buff == NULL) {
4056 return ENOMEM;
4057 }
4058
4059 for (pos = start; pos < end; pos += chunk) {
4060 ret = vn_rdwr(UIO_READ, xvp, buff, (int)chunk, pos, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p);
4061 if (iolen != 0) {
4062 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
4063 pos, ret, chunk, ret);
4064 break;
4065 }
4066
4067 ret = vn_rdwr(UIO_WRITE, xvp, buff, (int)chunk, pos - delta, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p);
4068 if (iolen != 0) {
4069 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
4070 pos + delta, ret, chunk, ret);
4071 break;
4072 }
4073
4074 if ((pos + (off_t)chunk) > end) {
4075 chunk = end - pos;
4076
4077 if (chunk == 0) { // we're all done
4078 break;
4079 }
4080 }
4081 }
4082
4083 kfree_data(buff, orig_chunk);
4084 return 0;
4085 }
4086
4087 static int
make_xattrfile_port(struct fileglob * fg,ipc_port_t * portp)4088 make_xattrfile_port(struct fileglob *fg, ipc_port_t *portp)
4089 {
4090 /*
4091 * This is essentially a stripped-down copy of
4092 * sys_fileport_makeport().
4093 */
4094 ipc_port_t fileport;
4095 int error = 0;
4096
4097 /* Dropped when port is deallocated. */
4098 fg_ref(FG_NOPROC, fg);
4099
4100 fileport = fileport_alloc(fg);
4101 if (fileport == IPC_PORT_NULL) {
4102 fg_drop_live(fg);
4103 error = EIO;
4104 } else {
4105 /* Tag the fileglob for debugging purposes */
4106 lck_mtx_lock_spin(&fg->fg_lock);
4107 fg->fg_lflags |= FG_PORTMADE;
4108 lck_mtx_unlock(&fg->fg_lock);
4109 }
4110
4111 /*
4112 * The Mig defs for doubleagentd declare the fileport argument
4113 * as move-send. If we ever decide we want to cache the fileport
4114 * here in the kernel, we will either need to change the Mig
4115 * defs back to the default mach_port_t (which is a copy-send)
4116 * or explicitly ipc_port_copy_send_any() the right before
4117 * sending it in the Mig stub.
4118 */
4119
4120 *portp = fileport;
4121 return error;
4122 }
4123
4124 #else /* CONFIG_APPLEDOUBLE */
4125
4126
4127 static int
default_getxattr(__unused vnode_t vp,__unused const char * name,__unused uio_t uio,__unused size_t * size,__unused int options,__unused vfs_context_t context)4128 default_getxattr(__unused vnode_t vp, __unused const char *name,
4129 __unused uio_t uio, __unused size_t *size, __unused int options,
4130 __unused vfs_context_t context)
4131 {
4132 return ENOTSUP;
4133 }
4134
4135 static int
default_setxattr(__unused vnode_t vp,__unused const char * name,__unused uio_t uio,__unused int options,__unused vfs_context_t context)4136 default_setxattr(__unused vnode_t vp, __unused const char *name,
4137 __unused uio_t uio, __unused int options, __unused vfs_context_t context)
4138 {
4139 return ENOTSUP;
4140 }
4141
4142 static int
default_listxattr(__unused vnode_t vp,__unused uio_t uio,__unused size_t * size,__unused int options,__unused vfs_context_t context)4143 default_listxattr(__unused vnode_t vp,
4144 __unused uio_t uio, __unused size_t *size, __unused int options,
4145 __unused vfs_context_t context)
4146 {
4147 return ENOTSUP;
4148 }
4149
4150 static int
default_removexattr(__unused vnode_t vp,__unused const char * name,__unused int options,__unused vfs_context_t context)4151 default_removexattr(__unused vnode_t vp, __unused const char *name,
4152 __unused int options, __unused vfs_context_t context)
4153 {
4154 return ENOTSUP;
4155 }
4156
4157 #endif /* CONFIG_APPLEDOUBLE */
4158