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 "/private/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 | VNODE_UPDATE_FORCE_PARENT_REF));
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 | MARKISSHADOW;
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 bool
is_shadow_dir_valid(vnode_t parent_sdvp,vnode_t sdvp,vfs_context_t kernelctx)1100 is_shadow_dir_valid(vnode_t parent_sdvp, vnode_t sdvp, vfs_context_t kernelctx)
1101 {
1102 struct vnode_attr va;
1103 uint32_t tmp_fsid;
1104 bool is_valid = false;
1105
1106 /* Make sure it's in fact a directory */
1107 if (sdvp->v_type != VDIR) {
1108 goto out;
1109 }
1110
1111 /* Obtain the fsid for what should be the /private/var/run directory. */
1112 VATTR_INIT(&va);
1113 VATTR_WANTED(&va, va_fsid);
1114 if (VNOP_GETATTR(parent_sdvp, &va, kernelctx) != 0 ||
1115 !VATTR_IS_SUPPORTED(&va, va_fsid)) {
1116 goto out;
1117 }
1118
1119 tmp_fsid = va.va_fsid;
1120
1121 VATTR_INIT(&va);
1122 VATTR_WANTED(&va, va_uid);
1123 VATTR_WANTED(&va, va_gid);
1124 VATTR_WANTED(&va, va_mode);
1125 VATTR_WANTED(&va, va_fsid);
1126 VATTR_WANTED(&va, va_dirlinkcount);
1127 VATTR_WANTED(&va, va_acl);
1128 /* Provide defaults for attrs that may not be supported */
1129 va.va_dirlinkcount = 1;
1130 va.va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
1131
1132 if (VNOP_GETATTR(sdvp, &va, kernelctx) != 0 ||
1133 !VATTR_IS_SUPPORTED(&va, va_uid) ||
1134 !VATTR_IS_SUPPORTED(&va, va_gid) ||
1135 !VATTR_IS_SUPPORTED(&va, va_mode) ||
1136 !VATTR_IS_SUPPORTED(&va, va_fsid)) {
1137 goto out;
1138 }
1139
1140 /*
1141 * Make sure its what we want:
1142 * - owned by root
1143 * - not writable by anyone
1144 * - on same file system as /private/var/run
1145 * - not a hard-linked directory
1146 * - no ACLs (they might grant write access)
1147 */
1148 if ((va.va_uid != 0) || (va.va_gid != 0) ||
1149 (va.va_mode & (S_IWUSR | S_IRWXG | S_IRWXO)) ||
1150 (va.va_fsid != tmp_fsid) ||
1151 (va.va_dirlinkcount != 1) ||
1152 (va.va_acl != (kauth_acl_t) KAUTH_FILESEC_NONE)) {
1153 goto out;
1154 }
1155
1156 /* If we get here, then the shadow dir is valid. */
1157 is_valid = true;
1158
1159 out:
1160 return is_valid;
1161 }
1162
1163 static int
get_shadow_dir(vnode_t * sdvpp)1164 get_shadow_dir(vnode_t *sdvpp)
1165 {
1166 vnode_t dvp = NULLVP;
1167 vnode_t sdvp = NULLVP;
1168 struct componentname cn;
1169 struct vnode_attr va;
1170 char tmpname[80];
1171 int error;
1172 vfs_context_t kernelctx = vfs_context_kernel();
1173
1174 /*
1175 * Make sure to use the kernel context. We want a singular view of
1176 * the shadow dir regardless of chrooted processes.
1177 */
1178
1179 /*
1180 * Obtain the vnode for "/private/var/run" directory using the kernel
1181 * context.
1182 *
1183 * This is defined in the SHADOW_DIR_CONTAINER macro
1184 */
1185 error = vnode_lookup(SHADOW_DIR_CONTAINER, VNODE_LOOKUP_NOFOLLOW_ANY, &dvp,
1186 kernelctx);
1187 if (error) {
1188 error = ENOTSUP;
1189 goto out;
1190 }
1191
1192 /*
1193 * Create the shadow stream directory.
1194 * 'dvp' below suggests the parent directory so
1195 * we only need to provide the leaf entry name
1196 */
1197 bzero(tmpname, sizeof(tmpname));
1198 MAKE_SHADOW_DIR_LEAF(rootvnode, tmpname);
1199
1200 /*
1201 * Look up the shadow directory to ensure that it still exists.
1202 * By looking it up, we get an iocounted sdvp to use, and avoid some
1203 * coherency issues in caching it when multiple threads may be trying to
1204 * manipulate the pointers.
1205 */
1206 error = vnode_lookupat(tmpname, VNODE_LOOKUP_NOFOLLOW, &sdvp, kernelctx, dvp);
1207 if (error == 0) {
1208 if (is_shadow_dir_valid(dvp, sdvp, kernelctx)) {
1209 /*
1210 * If we get here, then we have successfully looked up the shadow
1211 * dir, and it has an iocount from the lookup. Return the vp in the
1212 * output argument.
1213 */
1214 goto out;
1215 }
1216
1217 /*
1218 * Lookup returned us something that is not a valid shadow dir.
1219 * Remove it and proceed with recreating the shadow dir.
1220 */
1221 bzero(&cn, sizeof(cn));
1222 cn.cn_nameiop = DELETE;
1223 cn.cn_flags = ISLASTCN;
1224 cn.cn_context = kernelctx;
1225 cn.cn_pnbuf = tmpname;
1226 cn.cn_pnlen = sizeof(tmpname);
1227 cn.cn_nameptr = cn.cn_pnbuf;
1228 cn.cn_namelen = (int)strlen(tmpname);
1229
1230 error = VNOP_REMOVE(dvp, sdvp, &cn, 0, kernelctx);
1231 if (error) {
1232 error = ENOTSUP;
1233 goto out;
1234 }
1235
1236 vnode_put(sdvp);
1237 }
1238
1239 /* In the failure case, no iocount is acquired */
1240 sdvp = NULLVP;
1241 bzero(&cn, sizeof(cn));
1242 cn.cn_nameiop = LOOKUP;
1243 cn.cn_flags = ISLASTCN;
1244 cn.cn_context = kernelctx;
1245 cn.cn_pnbuf = tmpname;
1246 cn.cn_pnlen = sizeof(tmpname);
1247 cn.cn_nameptr = cn.cn_pnbuf;
1248 cn.cn_namelen = (int)strlen(tmpname);
1249
1250 /*
1251 * owned by root, only readable by root, hidden
1252 */
1253 VATTR_INIT(&va);
1254 VATTR_SET(&va, va_uid, 0);
1255 VATTR_SET(&va, va_gid, 0);
1256 VATTR_SET(&va, va_mode, S_IRUSR | S_IXUSR);
1257 VATTR_SET(&va, va_type, VDIR);
1258 VATTR_SET(&va, va_flags, UF_HIDDEN);
1259 va.va_vaflags = VA_EXCLUSIVE;
1260
1261 error = VNOP_MKDIR(dvp, &sdvp, &cn, &va, kernelctx);
1262
1263 /*
1264 * There can be only one winner for an exclusive create.
1265 */
1266 if (error == EEXIST) {
1267 /* loser has to look up directory */
1268 error = VNOP_LOOKUP(dvp, &sdvp, &cn, kernelctx);
1269 if (error == 0 && is_shadow_dir_valid(dvp, sdvp, kernelctx) == false) {
1270 goto baddir;
1271 }
1272 }
1273 out:
1274 if (dvp) {
1275 vnode_put(dvp);
1276 }
1277 if (error) {
1278 /* On errors, clean up shadow stream directory. */
1279 if (sdvp) {
1280 vnode_put(sdvp);
1281 sdvp = NULLVP;
1282 }
1283 }
1284 *sdvpp = sdvp;
1285 return error;
1286
1287 baddir:
1288 /* This is not the dir we're looking for, move along */
1289 ++shadow_sequence; /* try something else next time */
1290 error = ENOTDIR;
1291 goto out;
1292 }
1293 #endif /* NAMEDSTREAMS */
1294
1295
1296 #if CONFIG_APPLEDOUBLE
1297 /*
1298 * Default Implementation (Non-native EA)
1299 */
1300
1301
1302 /*
1303 * Typical "._" AppleDouble Header File layout:
1304 * ------------------------------------------------------------
1305 * MAGIC 0x00051607
1306 * VERSION 0x00020000
1307 * FILLER 0
1308 * COUNT 2
1309 * .-- AD ENTRY[0] Finder Info Entry (must be first)
1310 * .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1311 * | '-> FINDER INFO
1312 * | ///////////// Fixed Size Data (32 bytes)
1313 * | EXT ATTR HDR
1314 * | /////////////
1315 * | ATTR ENTRY[0] --.
1316 * | ATTR ENTRY[1] --+--.
1317 * | ATTR ENTRY[2] --+--+--.
1318 * | ... | | |
1319 * | ATTR ENTRY[N] --+--+--+--.
1320 * | ATTR DATA 0 <-' | | |
1321 * | //////////// | | |
1322 * | ATTR DATA 1 <----' | |
1323 * | ///////////// | |
1324 * | ATTR DATA 2 <-------' |
1325 * | ///////////// |
1326 * | ... |
1327 * | ATTR DATA N <----------'
1328 * | /////////////
1329 * | Attribute Free Space
1330 * |
1331 * '----> RESOURCE FORK
1332 * ///////////// Variable Sized Data
1333 * /////////////
1334 * /////////////
1335 * /////////////
1336 * /////////////
1337 * /////////////
1338 * ...
1339 * /////////////
1340 *
1341 * ------------------------------------------------------------
1342 *
1343 * NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1344 * stored as part of the Finder Info. The length in the Finder
1345 * Info AppleDouble entry includes the length of the extended
1346 * attribute header, attribute entries, and attribute data.
1347 */
1348
1349 /*
1350 * On Disk Data Structures
1351 *
1352 * Note: Motorola 68K alignment and big-endian.
1353 *
1354 * See RFC 1740 for additional information about the AppleDouble file format.
1355 *
1356 */
1357
1358 #define ADH_MAGIC 0x00051607
1359 #define ADH_VERSION 0x00020000
1360 #define ADH_MACOSX "Mac OS X "
1361
1362 /*
1363 * AppleDouble Entry ID's
1364 */
1365 #define AD_DATA 1 /* Data fork */
1366 #define AD_RESOURCE 2 /* Resource fork */
1367 #define AD_REALNAME 3 /* File's name on home file system */
1368 #define AD_COMMENT 4 /* Standard Mac comment */
1369 #define AD_ICONBW 5 /* Mac black & white icon */
1370 #define AD_ICONCOLOR 6 /* Mac color icon */
1371 #define AD_UNUSED 7 /* Not used */
1372 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
1373 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
1374 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
1375 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
1376 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
1377 #define AD_AFPNAME 13 /* Short name on AFP server */
1378 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1379 #define AD_AFPDIRID 15 /* AFP directory ID */
1380 #define AD_ATTRIBUTES AD_FINDERINFO
1381
1382
1383 #define ATTR_FILE_PREFIX "._"
1384 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
1385
1386 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
1387
1388 /* Implementation Limits */
1389 #define ATTR_MAX_SIZE AD_XATTR_MAXSIZE
1390 #define ATTR_MAX_HDR_SIZE 65536
1391 /*
1392 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
1393 * size supported (including the attribute entries). All of
1394 * the attribute entries must reside within this limit. If
1395 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
1396 * boundry, then all of the attribute data I/O is performed
1397 * separately from the attribute header I/O.
1398 *
1399 * In particular, all of the attr_entry structures must lie
1400 * completely within the first ATTR_MAX_HDR_SIZE bytes of the
1401 * AppleDouble file. However, the attribute data (i.e. the
1402 * contents of the extended attributes) may extend beyond the
1403 * first ATTR_MAX_HDR_SIZE bytes of the file. Note that this
1404 * limit is to allow the implementation to optimize by reading
1405 * the first ATTR_MAX_HDR_SIZE bytes of the file.
1406 */
1407
1408
1409 #define FINDERINFOSIZE 32
1410
1411 typedef struct apple_double_entry {
1412 u_int32_t type; /* entry type: see list, 0 invalid */
1413 u_int32_t offset; /* entry data offset from the beginning of the file. */
1414 u_int32_t length; /* entry data length in bytes. */
1415 } __attribute__((aligned(2), packed)) apple_double_entry_t;
1416
1417
1418 typedef struct apple_double_header {
1419 u_int32_t magic; /* == ADH_MAGIC */
1420 u_int32_t version; /* format version: 2 = 0x00020000 */
1421 u_int32_t filler[4];
1422 u_int16_t numEntries; /* number of entries which follow */
1423 apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */
1424 u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */
1425 u_int8_t pad[2]; /* get better alignment inside attr_header */
1426 } __attribute__((aligned(2), packed)) apple_double_header_t;
1427
1428 #define ADHDRSIZE (4+4+16+2)
1429
1430 /* Entries are aligned on 4 byte boundaries */
1431 typedef struct attr_entry {
1432 u_int32_t offset; /* file offset to data */
1433 u_int32_t length; /* size of attribute data */
1434 u_int16_t flags;
1435 u_int8_t namelen;
1436 u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
1437 } __attribute__((aligned(2), packed)) attr_entry_t;
1438
1439
1440 /* Header + entries must fit into 64K. Data may extend beyond 64K. */
1441 typedef struct attr_header {
1442 apple_double_header_t appledouble;
1443 u_int32_t magic; /* == ATTR_HDR_MAGIC */
1444 u_int32_t debug_tag; /* for debugging == file id of owning file */
1445 u_int32_t total_size; /* file offset of end of attribute header + entries + data */
1446 u_int32_t data_start; /* file offset to attribute data area */
1447 u_int32_t data_length; /* length of attribute data area */
1448 u_int32_t reserved[3];
1449 u_int16_t flags;
1450 u_int16_t num_attrs;
1451 } __attribute__((aligned(2), packed)) attr_header_t;
1452
1453
1454 /* Empty Resource Fork Header */
1455 typedef struct rsrcfork_header {
1456 u_int32_t fh_DataOffset;
1457 u_int32_t fh_MapOffset;
1458 u_int32_t fh_DataLength;
1459 u_int32_t fh_MapLength;
1460 u_int8_t systemData[112];
1461 u_int8_t appData[128];
1462 u_int32_t mh_DataOffset;
1463 u_int32_t mh_MapOffset;
1464 u_int32_t mh_DataLength;
1465 u_int32_t mh_MapLength;
1466 u_int32_t mh_Next;
1467 u_int16_t mh_RefNum;
1468 u_int8_t mh_Attr;
1469 u_int8_t mh_InMemoryAttr;
1470 u_int16_t mh_Types;
1471 u_int16_t mh_Names;
1472 u_int16_t typeCount;
1473 } __attribute__((aligned(2), packed)) rsrcfork_header_t;
1474
1475 #define RF_FIRST_RESOURCE 256
1476 #define RF_NULL_MAP_LENGTH 30
1477 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
1478
1479 /* Runtime information about the attribute file. */
1480 typedef struct attr_info {
1481 vfs_context_t context;
1482 vnode_t filevp;
1483 size_t filesize;
1484 size_t iosize;
1485 u_int8_t *rawdata;
1486 size_t rawsize; /* minimum of filesize or ATTR_MAX_HDR_SIZE */
1487 apple_double_header_t *filehdr;
1488 apple_double_entry_t *finderinfo;
1489 apple_double_entry_t *rsrcfork;
1490 attr_header_t *attrhdr;
1491 attr_entry_t *attr_entry;
1492 u_int8_t readonly;
1493 u_int8_t emptyfinderinfo;
1494 } attr_info_t;
1495
1496
1497 #define ATTR_SETTING 1
1498
1499 #define ATTR_ALIGN 3L /* Use four-byte alignment */
1500
1501 #define ATTR_ENTRY_LENGTH(namelen) \
1502 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
1503
1504 #define ATTR_NEXT(ae) \
1505 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
1506
1507 #define ATTR_VALID(ae, ai) \
1508 ((&(ae)->namelen < ((ai).rawdata + (ai).rawsize)) && \
1509 (u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
1510
1511 #define SWAP16(x) OSSwapBigToHostInt16((x))
1512 #define SWAP32(x) OSSwapBigToHostInt32((x))
1513 #define SWAP64(x) OSSwapBigToHostInt64((x))
1514
1515
1516 static int get_doubleagentd_port(mach_port_t *doubleagentd_port);
1517
1518 /*
1519 * DoubleAgent default xattr functions
1520 */
1521 static int default_getxattr_doubleagent(vnode_t vp, const char *name,
1522 uio_t uio, size_t *size, int options, vfs_context_t context,
1523 mach_port_t);
1524 static int default_setxattr_doubleagent(vnode_t vp, const char *name,
1525 uio_t uio, int options, vfs_context_t context, mach_port_t);
1526 static int default_listxattr_doubleagent(vnode_t vp, uio_t uio, size_t *size,
1527 int options, vfs_context_t context, mach_port_t);
1528 static int default_removexattr_doubleagent(vnode_t vp, const char *name,
1529 int options, vfs_context_t context, mach_port_t);
1530
1531
1532 static u_int32_t emptyfinfo[8] = {0};
1533
1534
1535 /*
1536 * Local support routines
1537 */
1538 static void close_xattrfile(struct fileglob *xfg, bool have_iocount, bool drop_iocount, vfs_context_t context);
1539
1540 static int open_xattrfile(vnode_t vp, int fileflags, struct fileglob **xfgp,
1541 int64_t *file_sizep, bool *created_xattr_filep, vfs_context_t context);
1542
1543 static void remove_xattrfile(struct fileglob *xfg, vnode_t xvp, vfs_context_t context);
1544
1545 static int make_xattrfile_port(struct fileglob *xfg, ipc_port_t *portp);
1546
1547
1548 /*
1549 * Retrieve the data of an extended attribute.
1550 */
1551 static int
default_getxattr(vnode_t vp,const char * name,uio_t uio,size_t * size,__unused int options,vfs_context_t context)1552 default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size,
1553 __unused int options, vfs_context_t context)
1554 {
1555 mach_port_t port;
1556 int error;
1557
1558 if (get_doubleagentd_port(&port) == 0) {
1559 error = default_getxattr_doubleagent(vp, name, uio, size,
1560 options, context, port);
1561 ipc_port_release_send(port);
1562 } else {
1563 error = ENOATTR;
1564 }
1565 return error;
1566 }
1567
1568 /*
1569 * Set the data of an extended attribute.
1570 */
1571 static int __attribute__((noinline))
default_setxattr(vnode_t vp,const char * name,uio_t uio,int options,vfs_context_t context)1572 default_setxattr(vnode_t vp, const char *name, uio_t uio, int options,
1573 vfs_context_t context)
1574 {
1575 mach_port_t port;
1576 int error;
1577
1578 if (get_doubleagentd_port(&port) == 0) {
1579 error = default_setxattr_doubleagent(vp, name, uio, options,
1580 context, port);
1581 ipc_port_release_send(port);
1582 } else {
1583 error = ENOATTR;
1584 }
1585 return error;
1586 }
1587
1588 /*
1589 * Remove an extended attribute.
1590 */
1591 static int
default_removexattr(vnode_t vp,const char * name,__unused int options,vfs_context_t context)1592 default_removexattr(vnode_t vp, const char *name, __unused int options,
1593 vfs_context_t context)
1594 {
1595 mach_port_t port;
1596 int error;
1597
1598 if (get_doubleagentd_port(&port) == 0) {
1599 error = default_removexattr_doubleagent(vp, name, options,
1600 context, port);
1601 ipc_port_release_send(port);
1602 } else {
1603 error = ENOATTR;
1604 }
1605 return error;
1606 }
1607
1608 /*
1609 * Retrieve the list of extended attribute names.
1610 */
1611 static int
default_listxattr(vnode_t vp,uio_t uio,size_t * size,__unused int options,vfs_context_t context)1612 default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options,
1613 vfs_context_t context)
1614 {
1615 mach_port_t port;
1616 int error;
1617
1618 if (get_doubleagentd_port(&port) == 0) {
1619 error = default_listxattr_doubleagent(vp, uio, size, options,
1620 context, port);
1621 ipc_port_release_send(port);
1622 } else {
1623 error = 0;
1624 }
1625 return error;
1626 }
1627
1628 static int
get_doubleagentd_port(mach_port_t * doubleagentd_port)1629 get_doubleagentd_port(mach_port_t *doubleagentd_port)
1630 {
1631 kern_return_t ret;
1632
1633 *doubleagentd_port = MACH_PORT_NULL;
1634 ret = host_get_doubleagentd_port(host_priv_self(), doubleagentd_port);
1635 if (ret != KERN_SUCCESS) {
1636 printf("vfs_xattr: can't get doubleagentd port, status 0x%08x\n", ret);
1637 return EIO;
1638 }
1639 if (!IPC_PORT_VALID(*doubleagentd_port)) {
1640 printf("vfs_xattr: doubleagentd port not valid\n");
1641 return EIO;
1642 }
1643 return 0;
1644 }
1645
1646 /*
1647 * Retrieve the data of an extended attribute.
1648 * (Using DoubleAgent to parse the AD file).
1649 */
1650 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)1651 default_getxattr_doubleagent(vnode_t vp, const char *name, uio_t uio,
1652 size_t *size, __unused int options, vfs_context_t context,
1653 mach_port_t doubleagentd_port)
1654 {
1655 vnode_t xvp = NULL;
1656 struct fileglob *xfg = NULL;
1657 mach_port_t fileport = MACH_PORT_NULL;
1658 uint64_t value_offset = 0;
1659 uint64_t value_length = 0;
1660 int64_t fsize;
1661 int isrsrcfork;
1662 int fileflags;
1663 int error;
1664 kern_return_t kr;
1665 char cName[XATTR_MAXNAMELEN] = {0};
1666 bool have_iocount = true;
1667
1668 fileflags = FREAD | O_SHLOCK;
1669 isrsrcfork = strncmp(name, XATTR_RESOURCEFORK_NAME,
1670 sizeof(XATTR_RESOURCEFORK_NAME)) == 0;
1671
1672 if ((error = open_xattrfile(vp, fileflags, &xfg, &fsize, NULL, context))) {
1673 goto out;
1674 }
1675 xvp = fg_get_data(xfg);
1676 if ((error = make_xattrfile_port(xfg, &fileport))) {
1677 goto out;
1678 }
1679
1680 /* Drop the iocount before upcalling to doubleagentd. */
1681 vnode_put(xvp);
1682 have_iocount = false;
1683
1684 strncpy(cName, name, XATTR_MAXNAMELEN);
1685
1686 /*
1687 * Call doubleagentd to look up the xattr. The fileport argument
1688 * is declared move-send, so the Mig stub consumes it.
1689 */
1690 kr = doubleagent_lookup_xattr(doubleagentd_port, fileport, fsize, cName,
1691 &error, &value_offset, &value_length);
1692 if (kr != KERN_SUCCESS) {
1693 error = EIO;
1694 }
1695 if (error == 0) {
1696 error = vnode_getwithref(xvp);
1697 }
1698 if (error) {
1699 goto out;
1700 }
1701 have_iocount = true;
1702 if (uio != NULL) {
1703 if (isrsrcfork) {
1704 // Resource Fork case is a bit different,
1705 // as we can have a non-zero uio offset.
1706 uio_setoffset(uio, uio_offset(uio) + value_offset);
1707 error = VNOP_READ(xvp, uio, 0, context);
1708 if (error == 0) {
1709 uio_setoffset(uio, uio_offset(uio) - value_offset);
1710 }
1711 } else {
1712 if (uio_resid(uio) < value_length) {
1713 error = ERANGE;
1714 goto out;
1715 }
1716
1717 // Read from the relevant offset in the AD file into the uio.
1718 user_ssize_t orig_resid = uio_resid(uio);
1719 uio_setoffset(uio, value_offset);
1720 uio_setresid(uio, value_length);
1721
1722 error = VNOP_READ(xvp, uio, 0, context);
1723
1724 uio_setoffset(uio, 0);
1725 uio_setresid(uio, orig_resid - value_length + uio_resid(uio));
1726 }
1727 }
1728
1729 *size = value_length;
1730
1731 out:
1732 if (xfg != NULL) {
1733 close_xattrfile(xfg, have_iocount, true, context);
1734 }
1735 return error;
1736 }
1737
1738 /*
1739 * Retrieve the list of extended attribute names.
1740 * (Using DoubleAgent to parse the AD file).
1741 */
1742 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)1743 default_listxattr_doubleagent(vnode_t vp, uio_t uio, size_t *size,
1744 __unused int options, vfs_context_t context, mach_port_t doubleagentd_port)
1745 {
1746 vnode_t xvp = NULL;
1747 struct fileglob *xfg = NULL;
1748 int64_t fsize;
1749 int error;
1750
1751 mach_port_t fileport = MACH_PORT_NULL;
1752 kern_return_t kr;
1753 void *buf = NULL;
1754 listxattrs_result_t *result;
1755 bool have_iocount = true;
1756
1757 /*
1758 * We do not zero "*size" here as we don't want to stomp a size set
1759 * when VNOP_LISTXATTR() processed any native EAs. That size is
1760 * initially zeroed by the system call layer, up in listxattr() or
1761 * flistxattr().
1762 */
1763
1764 if ((error = open_xattrfile(vp, FREAD | O_SHLOCK, &xfg, &fsize, NULL,
1765 context))) {
1766 if (error == ENOATTR) {
1767 error = 0;
1768 }
1769 goto out;
1770 }
1771 xvp = fg_get_data(xfg);
1772 if ((error = make_xattrfile_port(xfg, &fileport))) {
1773 goto out;
1774 }
1775
1776 /* Drop the iocount before upcalling to doubleagentd. */
1777 vnode_put(xvp);
1778 have_iocount = false;
1779
1780 buf = kalloc_data(sizeof(listxattrs_result_t), Z_WAITOK);
1781 result = (listxattrs_result_t *)buf;
1782
1783 /*
1784 * Call doubleagentd to list the xattrs. The fileport argument
1785 * is declared move-send, so the Mig stub consumes it.
1786 */
1787 kr = doubleagent_list_xattrs(doubleagentd_port, fileport, fsize, &error,
1788 result);
1789 if (kr != KERN_SUCCESS) {
1790 error = EIO;
1791 }
1792 if (error == 0) {
1793 error = vnode_getwithref(xvp);
1794 }
1795 if (error) {
1796 goto out;
1797 }
1798 have_iocount = true;
1799
1800 if (uio != NULL) {
1801 if (uio_resid(uio) < result->namesLength) {
1802 error = ERANGE;
1803 goto out;
1804 }
1805 // copy the relevant part of the result into the uio.
1806 error = uiomove((const char *)result->data, (int)result->namesLength, uio);
1807 if (error) {
1808 if (error != EFAULT) {
1809 error = ERANGE;
1810 }
1811 goto out;
1812 }
1813 }
1814
1815 /*
1816 * Set *size, while preserving any previous value from
1817 * VNOP_LISTXATTR().
1818 */
1819 *size += result->namesLength;
1820
1821 out:
1822 if (xfg != NULL) {
1823 close_xattrfile(xfg, have_iocount, true, context);
1824 }
1825 if (buf != NULL) {
1826 kfree_data(buf, sizeof(listxattrs_result_t));
1827 }
1828 return error;
1829 }
1830
1831 /*
1832 * Set the data of an extended attribute.
1833 * (Using DoubleAgent to parse the AD file).
1834 */
1835 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)1836 default_setxattr_doubleagent(vnode_t vp, const char *name, uio_t uio,
1837 int options, vfs_context_t context, mach_port_t doubleagentd_port)
1838 {
1839 vnode_t xvp = NULL;
1840 struct fileglob *xfg = NULL;
1841 size_t datalen;
1842 int namelen;
1843 int fileflags;
1844 int error;
1845 char cName[XATTR_MAXNAMELEN] = {0};
1846 char finfo[FINDERINFOSIZE];
1847 uio_t finfo_uio = NULL;
1848 mach_port_t fileport = MACH_PORT_NULL;
1849 uint64_t value_offset = 0;
1850 int64_t fsize;
1851 kern_return_t kr;
1852 bool have_iocount = true;
1853 bool created_xattr_file = false;
1854 bool removed_xattr_file = false;
1855
1856 datalen = uio_resid(uio);
1857 if (datalen > XATTR_MAXSIZE) {
1858 return E2BIG;
1859 }
1860 namelen = (int)strlen(name) + 1;
1861 if (namelen > UINT8_MAX) {
1862 return EINVAL;
1863 }
1864
1865 /*
1866 * By convention, Finder Info that is all zeroes is equivalent to not
1867 * having a Finder Info EA. So if we're trying to set the Finder Info
1868 * to all zeroes, then delete it instead. If a file didn't have an
1869 * AppleDouble file before, this prevents creating an AppleDouble file
1870 * with no useful content.
1871 *
1872 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1873 * for all zeroes Finder Info before opening the AppleDouble file.
1874 * But if either of those options were specified, we need to open the
1875 * AppleDouble file to see whether there was already Finder Info (so we
1876 * can return an error if needed); this case is handled in DoubleAgent.
1877 */
1878 if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1879 if (uio_offset(uio) != 0) {
1880 return EINVAL;
1881 }
1882
1883 if (datalen != FINDERINFOSIZE) {
1884 return ERANGE;
1885 }
1886
1887 // Duplicate the uio to keep it as-is for later.
1888 finfo_uio = uio_duplicate(uio);
1889 // Get the finfo data from the duplicated uio.
1890 error = uiomove(finfo, (int)datalen, finfo_uio);
1891 uio_free(finfo_uio);
1892 if (error) {
1893 return error;
1894 }
1895 if ((options & (XATTR_CREATE | XATTR_REPLACE)) == 0 &&
1896 bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) {
1897 error = default_removexattr(vp, name, 0, context);
1898 if (error == ENOATTR) {
1899 error = 0;
1900 }
1901 return error;
1902 }
1903 }
1904
1905 if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
1906 /*
1907 * For ResourceFork we allow offset to be != 0, so adjust datalen accordingly
1908 * so doubleagent will adjust the file length accordingly
1909 *
1910 */
1911 if (__improbable(os_add_overflow(datalen, uio_offset(uio), &datalen))) {
1912 return EINVAL;
1913 }
1914
1915 if (datalen > UINT32_MAX) {
1916 return EINVAL;
1917 }
1918 }
1919
1920 /*
1921 * Open the file locked since setting an attribute
1922 * can change the layout of the Apple Double file.
1923 */
1924 fileflags = FREAD | FWRITE | O_EXLOCK;
1925 if ((error = open_xattrfile(vp, O_CREAT | fileflags, &xfg, &fsize,
1926 &created_xattr_file, context))) {
1927 goto out;
1928 }
1929 xvp = fg_get_data(xfg);
1930 if ((error = make_xattrfile_port(xfg, &fileport))) {
1931 goto out;
1932 }
1933
1934 /* Drop the iocount before upcalling to doubleagentd. */
1935 vnode_put(xvp);
1936 have_iocount = false;
1937
1938 strncpy(cName, name, XATTR_MAXNAMELEN);
1939
1940 /*
1941 * Call doubleagentd to allocate space for the xattr. The
1942 * fileport argument is declared move-send, so the Mig stub
1943 * consumes it.
1944 */
1945 kr = doubleagent_allocate_xattr(doubleagentd_port, fileport, fsize,
1946 cName, datalen, options, &error, &value_offset);
1947 if (kr != KERN_SUCCESS) {
1948 error = EIO;
1949 }
1950 if (error == 0) {
1951 error = vnode_getwithref(xvp);
1952 }
1953 if (error) {
1954 goto out;
1955 }
1956 have_iocount = true;
1957
1958 /*
1959 * write the uio data into the offset we got from doubleagent,
1960 * while adding the given uio offset (could be non-zero only for
1961 * resource fork; it is being checked earlier).
1962 */
1963 uio_setoffset(uio, uio_offset(uio) + value_offset);
1964 error = VNOP_WRITE(xvp, uio, 0, context);
1965 uio_setoffset(uio, 0);
1966
1967 out:
1968 if (xfg != NULL) {
1969 /*
1970 * In case we have just created the AppleDouble file, and DoubleAgent
1971 * couldn't allocate space for the xattr, remove it so we won't leave
1972 * an uninitialized AppleDouble file.
1973 */
1974 if (error && created_xattr_file) {
1975 /* remove_xattrfile() assumes we have an iocount on the vnode */
1976 if (vnode_getwithref(xvp) == 0) {
1977 remove_xattrfile(xfg, xvp, context);
1978 removed_xattr_file = true;
1979 }
1980 }
1981 /* remove_xattrfile() would call close_xattrfile already */
1982 if (!removed_xattr_file) {
1983 close_xattrfile(xfg, have_iocount, true, context);
1984 }
1985 }
1986
1987 /* Touch the change time if we changed an attribute. */
1988 if (error == 0) {
1989 struct vnode_attr va;
1990
1991 /* Re-write the mtime to cause a ctime change. */
1992 VATTR_INIT(&va);
1993 VATTR_WANTED(&va, va_modify_time);
1994 if (vnode_getattr(vp, &va, context) == 0) {
1995 VATTR_INIT(&va);
1996 VATTR_SET(&va, va_modify_time, va.va_modify_time);
1997 (void) vnode_setattr(vp, &va, context);
1998 }
1999 }
2000
2001 post_event_if_success(vp, error, NOTE_ATTRIB);
2002
2003 return error;
2004 }
2005
2006 /*
2007 * Remove an extended attribute.
2008 * (Using DoubleAgent to parse the AD file).
2009 */
2010 static int
default_removexattr_doubleagent(vnode_t vp,const char * name,__unused int options,vfs_context_t context,mach_port_t doubleagentd_port)2011 default_removexattr_doubleagent(vnode_t vp, const char *name,
2012 __unused int options, vfs_context_t context,
2013 mach_port_t doubleagentd_port)
2014 {
2015 vnode_t xvp = NULL;
2016 struct fileglob *xfg = NULL;
2017 int isrsrcfork;
2018 int fileflags;
2019 int error;
2020 int64_t fsize;
2021 boolean_t is_empty = false;
2022 char cName[XATTR_MAXNAMELEN] = {0};
2023 mach_port_t fileport = MACH_PORT_NULL;
2024 kern_return_t kr;
2025 bool have_iocount = true;
2026
2027 fileflags = FREAD | FWRITE | O_EXLOCK;
2028 isrsrcfork = strncmp(name, XATTR_RESOURCEFORK_NAME,
2029 sizeof(XATTR_RESOURCEFORK_NAME)) == 0;
2030
2031 if ((error = open_xattrfile(vp, fileflags, &xfg, &fsize, NULL, context))) {
2032 goto out;
2033 }
2034 xvp = fg_get_data(xfg);
2035 if ((error = make_xattrfile_port(xfg, &fileport))) {
2036 goto out;
2037 }
2038
2039 /* Drop the iocount before upcalling to doubleagentd. */
2040 vnode_put(xvp);
2041 have_iocount = false;
2042
2043 strncpy(cName, name, XATTR_MAXNAMELEN);
2044
2045 /*
2046 * Call doubleagentd to remove the xattr. The fileport argument
2047 * is declared move-send, so the Mig stub consumes it.
2048 */
2049 kr = doubleagent_remove_xattr(doubleagentd_port, fileport, fsize, cName,
2050 &error, &is_empty);
2051 if (kr != KERN_SUCCESS) {
2052 error = EIO;
2053 }
2054 if (error == 0) {
2055 error = vnode_getwithref(xvp);
2056 }
2057 if (error) {
2058 goto out;
2059 }
2060 have_iocount = true;
2061
2062 out:
2063 if (error == 0) {
2064 /* When there are no more attributes remove the ._ file. */
2065 if (is_empty) {
2066 remove_xattrfile(xfg, xvp, context);
2067 } else {
2068 close_xattrfile(xfg, have_iocount, true, context);
2069 }
2070 xfg = NULL;
2071
2072 /* Touch the change time if we changed an attribute. */
2073 struct vnode_attr va;
2074 /* Re-write the mtime to cause a ctime change. */
2075 VATTR_INIT(&va);
2076 VATTR_WANTED(&va, va_modify_time);
2077 if (vnode_getattr(vp, &va, context) == 0) {
2078 VATTR_INIT(&va);
2079 VATTR_SET(&va, va_modify_time, va.va_modify_time);
2080 (void) vnode_setattr(vp, &va, context);
2081 }
2082 }
2083
2084 post_event_if_success(vp, error, NOTE_ATTRIB);
2085
2086 if (xfg != NULL) {
2087 close_xattrfile(xfg, have_iocount, true, context);
2088 }
2089 return error;
2090 }
2091
2092 static int
open_xattrfile(vnode_t vp,int fileflags,struct fileglob ** xfgp,int64_t * file_sizep,bool * created_xattr_filep,vfs_context_t context)2093 open_xattrfile(vnode_t vp, int fileflags, struct fileglob **xfgp,
2094 int64_t *file_sizep, bool *created_xattr_filep, vfs_context_t context)
2095 {
2096 extern const struct fileops vnops; /* XXX */
2097 vnode_t xvp = NULLVP;
2098 vnode_t dvp = NULLVP;
2099 struct fileglob *fg = NULL;
2100 struct vnode_attr *va = NULL;
2101 struct nameidata *nd = NULL;
2102 char smallname[64];
2103 char *filename = NULL;
2104 const char *basename = NULL;
2105 size_t alloc_len = 0;
2106 size_t copy_len;
2107 errno_t error;
2108 int opened = 0;
2109 int referenced = 0;
2110 bool created_xattr_file = false;
2111
2112 if (vnode_isvroot(vp) && vnode_isdir(vp)) {
2113 /*
2114 * For the root directory use "._." to hold the attributes.
2115 */
2116 filename = &smallname[0];
2117 snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, ".");
2118 dvp = vp; /* the "._." file resides in the root dir */
2119 goto lookup;
2120 }
2121 if ((dvp = vnode_getparent(vp)) == NULLVP) {
2122 error = ENOATTR;
2123 goto out;
2124 }
2125 if ((basename = vnode_getname(vp)) == NULL) {
2126 error = ENOATTR;
2127 goto out;
2128 }
2129
2130 /* "._" Attribute files cannot have attributes */
2131 if (vp->v_type == VREG && strlen(basename) > 2 &&
2132 basename[0] == '.' && basename[1] == '_') {
2133 error = EPERM;
2134 goto out;
2135 }
2136 filename = &smallname[0];
2137 alloc_len = snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, basename);
2138 if (alloc_len >= sizeof(smallname)) {
2139 alloc_len++; /* snprintf result doesn't include '\0' */
2140 filename = kalloc_data(alloc_len, Z_WAITOK);
2141 copy_len = snprintf(filename, alloc_len, "%s%s", ATTR_FILE_PREFIX, basename);
2142 }
2143 /*
2144 * Note that the lookup here does not authorize. Since we are looking
2145 * up in the same directory that we already have the file vnode in,
2146 * we must have been given the file vnode legitimately. Read/write
2147 * access has already been authorized in layers above for calls from
2148 * userspace, and the authorization code using this path to read
2149 * file security from the EA must always get access
2150 */
2151 lookup:
2152 nd = kalloc_type(struct nameidata, Z_WAITOK);
2153 NDINIT(nd, LOOKUP, OP_OPEN, LOCKLEAF | NOFOLLOW | USEDVP | DONOTAUTH,
2154 UIO_SYSSPACE, CAST_USER_ADDR_T(filename), context);
2155 nd->ni_dvp = dvp;
2156
2157 va = kalloc_type(struct vnode_attr, Z_WAITOK);
2158
2159 if (fileflags & O_CREAT) {
2160 nd->ni_cnd.cn_nameiop = CREATE;
2161 #if CONFIG_TRIGGERS
2162 nd->ni_op = OP_LINK;
2163 #endif
2164 if (dvp != vp) {
2165 nd->ni_cnd.cn_flags |= LOCKPARENT;
2166 }
2167 if ((error = namei(nd))) {
2168 nd->ni_dvp = NULLVP;
2169 error = ENOATTR;
2170 goto out;
2171 }
2172 if ((xvp = nd->ni_vp) == NULLVP) {
2173 uid_t uid;
2174 gid_t gid;
2175 mode_t umode;
2176
2177 /*
2178 * Pick up uid/gid/mode from target file.
2179 */
2180 VATTR_INIT(va);
2181 VATTR_WANTED(va, va_uid);
2182 VATTR_WANTED(va, va_gid);
2183 VATTR_WANTED(va, va_mode);
2184 if (VNOP_GETATTR(vp, va, context) == 0 &&
2185 VATTR_IS_SUPPORTED(va, va_uid) &&
2186 VATTR_IS_SUPPORTED(va, va_gid) &&
2187 VATTR_IS_SUPPORTED(va, va_mode)) {
2188 uid = va->va_uid;
2189 gid = va->va_gid;
2190 umode = va->va_mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
2191 } else { /* fallback values */
2192 uid = KAUTH_UID_NONE;
2193 gid = KAUTH_GID_NONE;
2194 umode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
2195 }
2196
2197 VATTR_INIT(va);
2198 VATTR_SET(va, va_type, VREG);
2199 VATTR_SET(va, va_mode, umode);
2200 if (uid != KAUTH_UID_NONE) {
2201 VATTR_SET(va, va_uid, uid);
2202 }
2203 if (gid != KAUTH_GID_NONE) {
2204 VATTR_SET(va, va_gid, gid);
2205 }
2206
2207 error = vn_create(dvp, &nd->ni_vp, nd, va,
2208 VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT | VN_CREATE_NOLABEL,
2209 0, NULL,
2210 context);
2211 if (error) {
2212 error = ENOATTR;
2213 } else {
2214 xvp = nd->ni_vp;
2215 created_xattr_file = true;
2216 if (created_xattr_filep) {
2217 *created_xattr_filep = true;
2218 }
2219 }
2220 }
2221 nameidone(nd);
2222 if (dvp != vp) {
2223 vnode_put(dvp); /* drop iocount from LOCKPARENT request above */
2224 }
2225 if (error) {
2226 goto out;
2227 }
2228 } else {
2229 if ((error = namei(nd))) {
2230 nd->ni_dvp = NULLVP;
2231 error = ENOATTR;
2232 goto out;
2233 }
2234 xvp = nd->ni_vp;
2235 nameidone(nd);
2236 }
2237 nd->ni_dvp = NULLVP;
2238
2239 if (xvp->v_type != VREG) {
2240 error = ENOATTR;
2241 goto out;
2242 }
2243 /*
2244 * Owners must match.
2245 */
2246 VATTR_INIT(va);
2247 VATTR_WANTED(va, va_uid);
2248 if (VNOP_GETATTR(vp, va, context) == 0 && VATTR_IS_SUPPORTED(va, va_uid)) {
2249 uid_t owner = va->va_uid;
2250
2251 VATTR_INIT(va);
2252 VATTR_WANTED(va, va_uid);
2253 if (VNOP_GETATTR(xvp, va, context) == 0 && (owner != va->va_uid)) {
2254 error = ENOATTR; /* don't use this "._" file */
2255 goto out;
2256 }
2257 }
2258
2259 if ((error = VNOP_OPEN(xvp, fileflags & ~(O_EXLOCK | O_SHLOCK), context))) {
2260 error = ENOATTR;
2261 goto out;
2262 }
2263 opened = 1;
2264
2265 if ((error = vnode_ref_ext(xvp, fileflags, 0)) != 0) {
2266 goto out;
2267 }
2268 referenced = 1;
2269
2270 /*
2271 * Allocate a file object for the referenced vnode.
2272 * This file object now owns the vnode reference,
2273 * and the caller owns the iocount, which will be
2274 * dropped in close_xattrfile().
2275 */
2276 fg = fg_alloc_init(context);
2277 fg->fg_flag = fileflags & FMASK;
2278 fg->fg_ops = &vnops;
2279 fg_set_data(fg, xvp);
2280
2281 /* Apply file locking if requested. */
2282 if (fileflags & (O_EXLOCK | O_SHLOCK)) {
2283 struct flock lf = {
2284 .l_whence = SEEK_SET,
2285 };
2286
2287 if (fileflags & O_EXLOCK) {
2288 lf.l_type = F_WRLCK;
2289 } else {
2290 lf.l_type = F_RDLCK;
2291 }
2292 error = VNOP_ADVLOCK(xvp, (caddr_t)fg, F_SETLK, &lf, F_FLOCK | F_WAIT, context, NULL);
2293 if (error == ENOTSUP) {
2294 error = 0;
2295 } else if (error) {
2296 error = ENOATTR;
2297 goto out;
2298 } else { // error == 0
2299 fg->fg_flag |= FWASLOCKED;
2300 }
2301 }
2302
2303 if (file_sizep != NULL) {
2304 /*
2305 * Now that the file is locked, get the file's size.
2306 */
2307 VATTR_INIT(va);
2308 VATTR_WANTED(va, va_data_size);
2309 if ((error = vnode_getattr(xvp, va, context)) != 0) {
2310 error = ENOATTR;
2311 goto out;
2312 }
2313 *file_sizep = va->va_data_size;
2314 }
2315 out:
2316 if (error) {
2317 if (fg != NULL) {
2318 /* Let the normal close path handle this. */
2319 if (created_xattr_file) {
2320 remove_xattrfile(fg, xvp, context);
2321 } else {
2322 close_xattrfile(fg, true, true, context);
2323 }
2324 fg = NULL;
2325 xvp = NULLVP;
2326 } else if (xvp != NULLVP) {
2327 if (opened) {
2328 (void) VNOP_CLOSE(xvp, fileflags, context);
2329 }
2330 if (created_xattr_file) {
2331 remove_xattrfile(NULL, xvp, context);
2332 }
2333 if (referenced) {
2334 (void) vnode_rele(xvp);
2335 }
2336 /* remove_xattrfile() would have dropped the iocount already */
2337 if (!created_xattr_file) {
2338 (void) vnode_put(xvp);
2339 }
2340 xvp = NULLVP;
2341 }
2342 if ((error == ENOATTR) && (fileflags & O_CREAT)) {
2343 error = EPERM;
2344 }
2345 }
2346
2347 /* Release resources after error-handling */
2348 kfree_type(struct nameidata, nd);
2349 kfree_type(struct vnode_attr, va);
2350 if (dvp && (dvp != vp)) {
2351 vnode_put(dvp);
2352 }
2353 if (basename) {
2354 vnode_putname(basename);
2355 }
2356 if (filename && filename != &smallname[0]) {
2357 kfree_data(filename, alloc_len);
2358 }
2359
2360 *xfgp = fg;
2361 return error;
2362 }
2363
2364 static void
close_xattrfile(struct fileglob * xfg,bool have_iocount,bool drop_iocount,vfs_context_t context)2365 close_xattrfile(struct fileglob *xfg, bool have_iocount, bool drop_iocount,
2366 vfs_context_t context)
2367 {
2368 vnode_t xvp = fg_get_data(xfg);
2369
2370 /*
2371 * N.B. The only time have_iocount would be false would be when
2372 * a vnode_getwithref() calls fails after coming back from a
2373 * doubleagentd upcall. If that happens, then it would mean
2374 * that the old vnode identity is gone, and our advisory lock
2375 * would have been garbage-collected when the vnode was reclaimed.
2376 */
2377 if (have_iocount) {
2378 /*
2379 * fg_drop() won't drop our advisory lock because we are not
2380 * following POSIX semantics. Drop it here.
2381 */
2382 struct flock lf = {
2383 .l_whence = SEEK_SET,
2384 .l_type = F_UNLCK,
2385 };
2386 (void)VNOP_ADVLOCK(xvp, (caddr_t)xfg, F_UNLCK, &lf, F_FLOCK,
2387 context, NULL);
2388
2389 /* (Maybe) drop the iocount we took in open_xattrfile(). */
2390 if (drop_iocount) {
2391 vnode_put(xvp);
2392 }
2393 }
2394
2395 (void) fg_drop(current_proc(), xfg);
2396 }
2397
2398 static void
remove_xattrfile(struct fileglob * xfg,vnode_t xvp,vfs_context_t context)2399 remove_xattrfile(struct fileglob *xfg, vnode_t xvp, vfs_context_t context)
2400 {
2401 vnode_t dvp = NULL, rvp = NULL;
2402 struct nameidata nd;
2403 char *path = NULL;
2404 int pathlen;
2405 int error;
2406
2407 if (xfg != NULL) {
2408 /*
2409 * Close the xattr file but don't dispose of the
2410 * iocount acquired in open_xattrfile() while doing
2411 * so. We'll do that below once we have performed
2412 * the unlink operation.
2413 */
2414 close_xattrfile(xfg, true, false, context);
2415 }
2416
2417 path = zalloc(ZV_NAMEI);
2418 pathlen = MAXPATHLEN;
2419 error = vn_getpath(xvp, path, &pathlen);
2420 if (error) {
2421 zfree(ZV_NAMEI, path);
2422 goto out;
2423 }
2424
2425 NDINIT(&nd, DELETE, OP_UNLINK, LOCKPARENT | NOFOLLOW | DONOTAUTH,
2426 UIO_SYSSPACE, CAST_USER_ADDR_T(path), context);
2427 error = namei(&nd);
2428 zfree(ZV_NAMEI, path);
2429 if (error) {
2430 goto out;
2431 }
2432 dvp = nd.ni_dvp;
2433 rvp = nd.ni_vp;
2434
2435 /*
2436 * Only remove if the namei() returned to us the same vnode that
2437 * we think we are supposed to be removing. If they're not the
2438 * same, we could have raced against something else trying to
2439 * unlink it, and we don't want to remove someone else's (possibly
2440 * very important) file.
2441 */
2442 if (rvp == xvp) {
2443 (void) VNOP_REMOVE(dvp, rvp, &nd.ni_cnd, 0, context);
2444 }
2445 nameidone(&nd);
2446
2447 out:
2448 vnode_put(xvp);
2449 if (dvp != NULLVP) {
2450 vnode_put(dvp);
2451 }
2452 if (rvp != NULLVP) {
2453 vnode_put(rvp);
2454 }
2455 }
2456
2457 static int
make_xattrfile_port(struct fileglob * fg,ipc_port_t * portp)2458 make_xattrfile_port(struct fileglob *fg, ipc_port_t *portp)
2459 {
2460 /*
2461 * This is essentially a stripped-down copy of
2462 * sys_fileport_makeport().
2463 */
2464 ipc_port_t fileport;
2465 int error = 0;
2466
2467 /* Dropped when port is deallocated. */
2468 fg_ref(FG_NOPROC, fg);
2469
2470 fileport = fileport_alloc(fg);
2471 if (fileport == IPC_PORT_NULL) {
2472 fg_drop_live(fg);
2473 error = EIO;
2474 } else {
2475 /* Tag the fileglob for debugging purposes */
2476 lck_mtx_lock_spin(&fg->fg_lock);
2477 fg->fg_lflags |= FG_PORTMADE;
2478 lck_mtx_unlock(&fg->fg_lock);
2479 }
2480
2481 /*
2482 * The Mig defs for doubleagentd declare the fileport argument
2483 * as move-send. If we ever decide we want to cache the fileport
2484 * here in the kernel, we will either need to change the Mig
2485 * defs back to the default mach_port_t (which is a copy-send)
2486 * or explicitly ipc_port_copy_send_any() the right before
2487 * sending it in the Mig stub.
2488 */
2489
2490 *portp = fileport;
2491 return error;
2492 }
2493
2494 #else /* CONFIG_APPLEDOUBLE */
2495
2496
2497 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)2498 default_getxattr(__unused vnode_t vp, __unused const char *name,
2499 __unused uio_t uio, __unused size_t *size, __unused int options,
2500 __unused vfs_context_t context)
2501 {
2502 return ENOTSUP;
2503 }
2504
2505 static int
default_setxattr(__unused vnode_t vp,__unused const char * name,__unused uio_t uio,__unused int options,__unused vfs_context_t context)2506 default_setxattr(__unused vnode_t vp, __unused const char *name,
2507 __unused uio_t uio, __unused int options, __unused vfs_context_t context)
2508 {
2509 return ENOTSUP;
2510 }
2511
2512 static int
default_listxattr(__unused vnode_t vp,__unused uio_t uio,__unused size_t * size,__unused int options,__unused vfs_context_t context)2513 default_listxattr(__unused vnode_t vp,
2514 __unused uio_t uio, __unused size_t *size, __unused int options,
2515 __unused vfs_context_t context)
2516 {
2517 return ENOTSUP;
2518 }
2519
2520 static int
default_removexattr(__unused vnode_t vp,__unused const char * name,__unused int options,__unused vfs_context_t context)2521 default_removexattr(__unused vnode_t vp, __unused const char *name,
2522 __unused int options, __unused vfs_context_t context)
2523 {
2524 return ENOTSUP;
2525 }
2526
2527 #endif /* CONFIG_APPLEDOUBLE */
2528