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;
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 /*
1517 * sysctl stuff
1518 */
1519 static int vfs_xattr_doubleagent_enabled = 1;
1520 SYSCTL_DECL(_vfs_generic);
1521 SYSCTL_INT(_vfs_generic, OID_AUTO, xattr_doubleagent_enabled, CTLFLAG_RW | CTLFLAG_LOCKED, &vfs_xattr_doubleagent_enabled, 0, "");
1522
1523 static int get_doubleagentd_port(mach_port_t *doubleagentd_port);
1524
1525 /*
1526 * VFS default xattr functions
1527 */
1528 static int default_getxattr_vfs(vnode_t vp, const char *name, uio_t uio, size_t *size, int options,
1529 vfs_context_t context);
1530 static int default_setxattr_vfs(vnode_t vp, const char *name, uio_t uio, int options,
1531 vfs_context_t context);
1532 static int default_listxattr_vfs(vnode_t vp, uio_t uio, size_t *size, int options,
1533 vfs_context_t context);
1534 static int default_removexattr_vfs(vnode_t vp, const char *name, int options,
1535 vfs_context_t context);
1536
1537 /*
1538 * DoubleAgent default xattr functions
1539 */
1540 static int default_getxattr_doubleagent(vnode_t vp, const char *name,
1541 uio_t uio, size_t *size, int options, vfs_context_t context,
1542 mach_port_t);
1543 static int default_setxattr_doubleagent(vnode_t vp, const char *name,
1544 uio_t uio, int options, vfs_context_t context, mach_port_t);
1545 static int default_listxattr_doubleagent(vnode_t vp, uio_t uio, size_t *size,
1546 int options, vfs_context_t context, mach_port_t);
1547 static int default_removexattr_doubleagent(vnode_t vp, const char *name,
1548 int options, vfs_context_t context, mach_port_t);
1549
1550
1551 static u_int32_t emptyfinfo[8] = {0};
1552
1553
1554 /*
1555 * Local support routines
1556 */
1557 static void close_xattrfile(struct fileglob *xfg, bool have_iocount, bool drop_iocount, vfs_context_t context);
1558
1559 static int open_xattrfile(vnode_t vp, int fileflags, struct fileglob **xfgp,
1560 int64_t *file_sizep, vfs_context_t context);
1561
1562 static int create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context);
1563
1564 static void remove_xattrfile(struct fileglob *xfg, vnode_t xvp, vfs_context_t context);
1565
1566 static int get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context);
1567
1568 static void rel_xattrinfo(attr_info_t *ainfop);
1569
1570 static int write_xattrinfo(attr_info_t *ainfop);
1571
1572 static void init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr);
1573
1574 static int make_xattrfile_port(struct fileglob *xfg, ipc_port_t *portp);
1575
1576 #if BYTE_ORDER == LITTLE_ENDIAN
1577 static void swap_adhdr(apple_double_header_t *adh);
1578 static void swap_attrhdr(attr_header_t *ah, attr_info_t* info);
1579
1580 #else
1581 #define swap_adhdr(x)
1582 #define swap_attrhdr(x, y)
1583 #endif
1584
1585 static int check_and_swap_attrhdr(attr_header_t *ah, attr_info_t* ainfop);
1586 static int shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context);
1587 static int shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context);
1588
1589
1590 /*
1591 * Sanity check and swap the header of an AppleDouble file. Assumes the buffer
1592 * is in big endian (as it would exist on disk). Verifies the following:
1593 * - magic field
1594 * - version field
1595 * - number of entries
1596 * - that each entry fits within the file size
1597 *
1598 * If the header is invalid, ENOATTR is returned.
1599 *
1600 * NOTE: Does not attempt to validate the extended attributes header that
1601 * may be embedded in the Finder Info entry.
1602 */
1603 static int
check_and_swap_apple_double_header(attr_info_t * ainfop)1604 check_and_swap_apple_double_header(attr_info_t *ainfop)
1605 {
1606 int i, j;
1607 u_int32_t header_end;
1608 u_int32_t entry_end;
1609 size_t rawsize;
1610 apple_double_header_t *header;
1611
1612 rawsize = ainfop->rawsize;
1613 header = (apple_double_header_t *) ainfop->rawdata;
1614
1615 /* Is the file big enough to contain an AppleDouble header? */
1616 if (rawsize < offsetof(apple_double_header_t, entries)) {
1617 return ENOATTR;
1618 }
1619
1620 /* Swap the AppleDouble header fields to native order */
1621 header->magic = SWAP32(header->magic);
1622 header->version = SWAP32(header->version);
1623 header->numEntries = SWAP16(header->numEntries);
1624
1625 /* Sanity check the AppleDouble header fields */
1626 if (header->magic != ADH_MAGIC ||
1627 header->version != ADH_VERSION ||
1628 header->numEntries < 1 ||
1629 header->numEntries > 15) {
1630 return ENOATTR;
1631 }
1632
1633 /* Calculate where the entries[] array ends */
1634 header_end = offsetof(apple_double_header_t, entries) +
1635 header->numEntries * sizeof(apple_double_entry_t);
1636
1637 /* Is the file big enough to contain the AppleDouble entries? */
1638 if (rawsize < header_end) {
1639 return ENOATTR;
1640 }
1641
1642 /* Swap and sanity check each AppleDouble entry */
1643 for (i = 0; i < header->numEntries; i++) {
1644 /* Swap the per-entry fields to native order */
1645 header->entries[i].type = SWAP32(header->entries[i].type);
1646 header->entries[i].offset = SWAP32(header->entries[i].offset);
1647 header->entries[i].length = SWAP32(header->entries[i].length);
1648
1649 entry_end = header->entries[i].offset + header->entries[i].length;
1650
1651 /*
1652 * Does the entry's content start within the header itself,
1653 * did the addition overflow, or does the entry's content
1654 * extend past the end of the file?
1655 */
1656 if (header->entries[i].offset < header_end ||
1657 entry_end < header->entries[i].offset ||
1658 entry_end > ainfop->filesize) {
1659 return ENOATTR;
1660 }
1661
1662 /*
1663 * Does the current entry's content overlap with a previous
1664 * entry's content?
1665 *
1666 * Yes, this is O(N**2), and there are more efficient algorithms
1667 * for testing pairwise overlap of N ranges when N is large.
1668 * But we have already ensured N < 16, and N is almost always 2.
1669 * So there's no point in using a more complex algorithm.
1670 */
1671
1672 for (j = 0; j < i; j++) {
1673 if (entry_end > header->entries[j].offset &&
1674 header->entries[j].offset + header->entries[j].length > header->entries[i].offset) {
1675 return ENOATTR;
1676 }
1677 }
1678 }
1679
1680 return 0;
1681 }
1682
1683 /*
1684 * Retrieve the data of an extended attribute.
1685 */
1686 static int
default_getxattr(vnode_t vp,const char * name,uio_t uio,size_t * size,__unused int options,vfs_context_t context)1687 default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size,
1688 __unused int options, vfs_context_t context)
1689 {
1690 mach_port_t port;
1691 int error;
1692
1693 if (vfs_xattr_doubleagent_enabled &&
1694 get_doubleagentd_port(&port) == 0) {
1695 error = default_getxattr_doubleagent(vp, name, uio, size,
1696 options, context, port);
1697 ipc_port_release_send(port);
1698 } else {
1699 error = default_getxattr_vfs(vp, name, uio, size, options,
1700 context);
1701 }
1702 return error;
1703 }
1704
1705 /*
1706 * Set the data of an extended attribute.
1707 */
1708 static int __attribute__((noinline))
default_setxattr(vnode_t vp,const char * name,uio_t uio,int options,vfs_context_t context)1709 default_setxattr(vnode_t vp, const char *name, uio_t uio, int options,
1710 vfs_context_t context)
1711 {
1712 mach_port_t port;
1713 int error;
1714
1715 if (vfs_xattr_doubleagent_enabled &&
1716 get_doubleagentd_port(&port) == 0) {
1717 error = default_setxattr_doubleagent(vp, name, uio, options,
1718 context, port);
1719 ipc_port_release_send(port);
1720 } else {
1721 error = default_setxattr_vfs(vp, name, uio, options, context);
1722 }
1723 return error;
1724 }
1725
1726 /*
1727 * Remove an extended attribute.
1728 */
1729 static int
default_removexattr(vnode_t vp,const char * name,__unused int options,vfs_context_t context)1730 default_removexattr(vnode_t vp, const char *name, __unused int options,
1731 vfs_context_t context)
1732 {
1733 mach_port_t port;
1734 int error;
1735
1736 if (vfs_xattr_doubleagent_enabled &&
1737 get_doubleagentd_port(&port) == 0) {
1738 error = default_removexattr_doubleagent(vp, name, options,
1739 context, port);
1740 ipc_port_release_send(port);
1741 } else {
1742 error = default_removexattr_vfs(vp, name, options, context);
1743 }
1744 return error;
1745 }
1746
1747 /*
1748 * Retrieve the list of extended attribute names.
1749 */
1750 static int
default_listxattr(vnode_t vp,uio_t uio,size_t * size,__unused int options,vfs_context_t context)1751 default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options,
1752 vfs_context_t context)
1753 {
1754 mach_port_t port;
1755 int error;
1756
1757 if (vfs_xattr_doubleagent_enabled &&
1758 get_doubleagentd_port(&port) == 0) {
1759 error = default_listxattr_doubleagent(vp, uio, size, options,
1760 context, port);
1761 ipc_port_release_send(port);
1762 } else {
1763 error = default_listxattr_vfs(vp, uio, size, options, context);
1764 }
1765 return error;
1766 }
1767
1768 /*
1769 * Retrieve the data of an extended attribute.
1770 * (VFS implementation).
1771 */
1772 static int
default_getxattr_vfs(vnode_t vp,const char * name,uio_t uio,size_t * size,__unused int options,vfs_context_t context)1773 default_getxattr_vfs(vnode_t vp, const char *name, uio_t uio, size_t *size,
1774 __unused int options, vfs_context_t context)
1775 {
1776 vnode_t xvp = NULL;
1777 struct fileglob *xfg = NULL;
1778 attr_info_t ainfo;
1779 attr_header_t *header;
1780 attr_entry_t *entry;
1781 u_int8_t *attrdata;
1782 u_int32_t datalen;
1783 size_t namelen;
1784 int isrsrcfork;
1785 int fileflags;
1786 int i;
1787 int error;
1788
1789 fileflags = FREAD | O_SHLOCK;
1790 isrsrcfork = strncmp(name, XATTR_RESOURCEFORK_NAME,
1791 sizeof(XATTR_RESOURCEFORK_NAME)) == 0;
1792
1793 if ((error = open_xattrfile(vp, fileflags, &xfg, NULL, context))) {
1794 return error;
1795 }
1796 xvp = fg_get_data(xfg);
1797
1798 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
1799 close_xattrfile(xfg, true, true, context);
1800 return error;
1801 }
1802
1803 /* Get the Finder Info. */
1804 if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1805 if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) {
1806 error = ENOATTR;
1807 } else if (uio == NULL) {
1808 *size = FINDERINFOSIZE;
1809 error = 0;
1810 } else if (uio_offset(uio) != 0) {
1811 error = EINVAL;
1812 } else if (uio_resid(uio) < FINDERINFOSIZE) {
1813 error = ERANGE;
1814 } else {
1815 attrdata = (u_int8_t*)ainfo.filehdr + ainfo.finderinfo->offset;
1816 error = uiomove((caddr_t)attrdata, FINDERINFOSIZE, uio);
1817 }
1818 goto out;
1819 }
1820
1821 /* Read the Resource Fork. */
1822 if (isrsrcfork) {
1823 if (!vnode_isreg(vp)) {
1824 error = EPERM;
1825 } else if (ainfo.rsrcfork == NULL) {
1826 error = ENOATTR;
1827 } else if (uio == NULL) {
1828 *size = (size_t)ainfo.rsrcfork->length;
1829 } else {
1830 uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset);
1831 error = VNOP_READ(xvp, uio, 0, context);
1832 if (error == 0) {
1833 uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset);
1834 }
1835 }
1836 goto out;
1837 }
1838
1839 if (ainfo.attrhdr == NULL || ainfo.attr_entry == NULL) {
1840 error = ENOATTR;
1841 goto out;
1842 }
1843 if (uio_offset(uio) != 0) {
1844 error = EINVAL;
1845 goto out;
1846 }
1847 error = ENOATTR;
1848 namelen = strlen(name) + 1;
1849 header = ainfo.attrhdr;
1850 entry = ainfo.attr_entry;
1851 /*
1852 * Search for attribute name in the header.
1853 */
1854 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
1855 if (strncmp((const char *)entry->name, name, namelen) == 0) {
1856 datalen = entry->length;
1857 if (uio == NULL) {
1858 *size = datalen;
1859 error = 0;
1860 break;
1861 }
1862 if (uio_resid(uio) < (user_ssize_t)datalen) {
1863 error = ERANGE;
1864 break;
1865 }
1866 if (entry->offset + datalen < ATTR_MAX_HDR_SIZE) {
1867 attrdata = ((u_int8_t *)header + entry->offset);
1868 error = uiomove((caddr_t)attrdata, datalen, uio);
1869 } else {
1870 uio_setoffset(uio, entry->offset);
1871 error = VNOP_READ(xvp, uio, 0, context);
1872 uio_setoffset(uio, 0);
1873 }
1874 break;
1875 }
1876 entry = ATTR_NEXT(entry);
1877 }
1878 out:
1879 rel_xattrinfo(&ainfo);
1880 close_xattrfile(xfg, true, true, context);
1881
1882 return error;
1883 }
1884
1885 /*
1886 * Set the data of an extended attribute.
1887 * (VFS implementation).
1888 */
1889 static int __attribute__((noinline))
default_setxattr_vfs(vnode_t vp,const char * name,uio_t uio,int options,vfs_context_t context)1890 default_setxattr_vfs(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context)
1891 {
1892 vnode_t xvp = NULL;
1893 struct fileglob *xfg = NULL;
1894 attr_info_t ainfo;
1895 attr_header_t *header;
1896 attr_entry_t *entry;
1897 attr_entry_t *lastentry;
1898 u_int8_t *attrdata;
1899 size_t datalen;
1900 size_t entrylen;
1901 size_t datafreespace;
1902 int namelen;
1903 int found = 0;
1904 int i;
1905 int splitdata;
1906 int fileflags;
1907 int error;
1908 char finfo[FINDERINFOSIZE];
1909
1910 datalen = uio_resid(uio);
1911 if (datalen > XATTR_MAXSIZE) {
1912 return E2BIG;
1913 }
1914 namelen = (int)strlen(name) + 1;
1915 if (namelen > UINT8_MAX) {
1916 return EINVAL;
1917 }
1918 entrylen = ATTR_ENTRY_LENGTH(namelen);
1919
1920 /*
1921 * By convention, Finder Info that is all zeroes is equivalent to not
1922 * having a Finder Info EA. So if we're trying to set the Finder Info
1923 * to all zeroes, then delete it instead. If a file didn't have an
1924 * AppleDouble file before, this prevents creating an AppleDouble file
1925 * with no useful content.
1926 *
1927 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
1928 * for all zeroes Finder Info before opening the AppleDouble file.
1929 * But if either of those options were specified, we need to open the
1930 * AppleDouble file to see whether there was already Finder Info (so we
1931 * can return an error if needed); this case is handled further below.
1932 *
1933 * NOTE: this copies the Finder Info data into the "finfo" local.
1934 */
1935 if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1936 /*
1937 * TODO: check the XATTR_CREATE and XATTR_REPLACE flags.
1938 * That means we probably have to open_xattrfile and get_xattrinfo.
1939 */
1940 if (uio_offset(uio) != 0) {
1941 return EINVAL;
1942 }
1943
1944 if (datalen != FINDERINFOSIZE) {
1945 return ERANGE;
1946 }
1947
1948 error = uiomove(finfo, (int)datalen, uio);
1949 if (error) {
1950 return error;
1951 }
1952 if ((options & (XATTR_CREATE | XATTR_REPLACE)) == 0 &&
1953 bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) {
1954 error = default_removexattr(vp, name, 0, context);
1955 if (error == ENOATTR) {
1956 error = 0;
1957 }
1958 return error;
1959 }
1960 }
1961
1962 start:
1963 /*
1964 * Open the file locked since setting an attribute
1965 * can change the layout of the Apple Double file.
1966 */
1967 fileflags = FREAD | FWRITE | O_EXLOCK;
1968 if ((error = open_xattrfile(vp, O_CREAT | fileflags, &xfg, NULL, context))) {
1969 return error;
1970 }
1971 xvp = fg_get_data(xfg);
1972
1973 if ((error = get_xattrinfo(xvp, ATTR_SETTING, &ainfo, context))) {
1974 close_xattrfile(xfg, true, true, context);
1975 return error;
1976 }
1977
1978 /* Set the Finder Info. */
1979 if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1980 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
1981 /* attr exists and "create" was specified? */
1982 if (options & XATTR_CREATE) {
1983 error = EEXIST;
1984 goto out;
1985 }
1986 } else {
1987 /* attr doesn't exists and "replace" was specified? */
1988 if (options & XATTR_REPLACE) {
1989 error = ENOATTR;
1990 goto out;
1991 }
1992 }
1993 if (options != 0 && bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) {
1994 /*
1995 * Setting the Finder Info to all zeroes is equivalent to
1996 * removing it. Close the xattr file and let
1997 * default_removexattr do the work (including deleting
1998 * the xattr file if there are no other xattrs).
1999 *
2000 * Note that we have to handle the case where the
2001 * Finder Info was already all zeroes, and we ignore
2002 * ENOATTR.
2003 *
2004 * The common case where options == 0 was handled above.
2005 */
2006 rel_xattrinfo(&ainfo);
2007 close_xattrfile(xfg, true, true, context);
2008 error = default_removexattr(vp, name, 0, context);
2009 if (error == ENOATTR) {
2010 error = 0;
2011 }
2012 return error;
2013 }
2014 if (ainfo.finderinfo) {
2015 attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset;
2016 bcopy(finfo, attrdata, datalen);
2017 ainfo.iosize = sizeof(attr_header_t);
2018 error = write_xattrinfo(&ainfo);
2019 goto out;
2020 }
2021 error = ENOATTR;
2022 goto out;
2023 }
2024
2025 /* Write the Resource Fork. */
2026 if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
2027 off_t endoffset;
2028
2029 if (!vnode_isreg(vp)) {
2030 error = EPERM;
2031 goto out;
2032 }
2033 /* Make sure we have a rsrc fork pointer.. */
2034 if (ainfo.rsrcfork == NULL) {
2035 error = ENOATTR;
2036 goto out;
2037 }
2038 if (ainfo.rsrcfork) {
2039 if (ainfo.rsrcfork->length != 0) {
2040 if (options & XATTR_CREATE) {
2041 /* attr exists, and create specified ? */
2042 error = EEXIST;
2043 goto out;
2044 }
2045 } else {
2046 /* Zero length AD rsrc fork */
2047 if (options & XATTR_REPLACE) {
2048 /* attr doesn't exist (0-length), but replace specified ? */
2049 error = ENOATTR;
2050 goto out;
2051 }
2052 }
2053 } else {
2054 /* We can't do much if we somehow didn't get an AD rsrc pointer */
2055 error = ENOATTR;
2056 goto out;
2057 }
2058
2059 endoffset = uio_resid(uio) + uio_offset(uio); /* new size */
2060 if (endoffset > UINT32_MAX || endoffset < 0) {
2061 error = EINVAL;
2062 goto out;
2063 }
2064 uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset);
2065 error = VNOP_WRITE(xvp, uio, 0, context);
2066 if (error) {
2067 goto out;
2068 }
2069 uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset);
2070 if (endoffset > ainfo.rsrcfork->length) {
2071 ainfo.rsrcfork->length = (u_int32_t)endoffset;
2072 ainfo.iosize = sizeof(attr_header_t);
2073 error = write_xattrinfo(&ainfo);
2074 goto out;
2075 }
2076 goto out;
2077 }
2078
2079 if (datalen > ATTR_MAX_SIZE) {
2080 return E2BIG; /* EINVAL instead ? */
2081 }
2082
2083 if (ainfo.attrhdr == NULL) {
2084 error = ENOATTR;
2085 goto out;
2086 }
2087 header = ainfo.attrhdr;
2088 entry = ainfo.attr_entry;
2089
2090 /* Check if data area crosses the maximum header size. */
2091 if ((header->data_start + header->data_length + entrylen + datalen) > ATTR_MAX_HDR_SIZE) {
2092 splitdata = 1; /* do data I/O separately */
2093 } else {
2094 splitdata = 0;
2095 }
2096
2097 /*
2098 * See if attribute already exists.
2099 */
2100 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
2101 if (strncmp((const char *)entry->name, name, namelen) == 0) {
2102 found = 1;
2103 break;
2104 }
2105 entry = ATTR_NEXT(entry);
2106 }
2107
2108 if (found) {
2109 if (options & XATTR_CREATE) {
2110 error = EEXIST;
2111 goto out;
2112 }
2113 if (datalen == entry->length) {
2114 if (splitdata) {
2115 uio_setoffset(uio, entry->offset);
2116 error = VNOP_WRITE(xvp, uio, 0, context);
2117 uio_setoffset(uio, 0);
2118 if (error) {
2119 printf("setxattr: VNOP_WRITE error %d\n", error);
2120 }
2121 } else {
2122 attrdata = (u_int8_t *)header + entry->offset;
2123 error = uiomove((caddr_t)attrdata, (int)datalen, uio);
2124 if (error) {
2125 goto out;
2126 }
2127 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
2128 error = write_xattrinfo(&ainfo);
2129 if (error) {
2130 printf("setxattr: write_xattrinfo error %d\n", error);
2131 }
2132 }
2133 goto out;
2134 } else {
2135 /*
2136 * Brute force approach - just remove old entry and set new entry.
2137 */
2138 found = 0;
2139 rel_xattrinfo(&ainfo);
2140 close_xattrfile(xfg, true, true, context);
2141 error = default_removexattr(vp, name, options, context);
2142 if (error) {
2143 return error;
2144 }
2145 /* Clear XATTR_REPLACE option since we just removed the attribute. */
2146 options &= ~XATTR_REPLACE;
2147 goto start; /* start over */
2148 }
2149 } else {
2150 if (!ATTR_VALID(entry, ainfo)) {
2151 error = ENOSPC;
2152 goto out;
2153 }
2154 }
2155
2156 if (options & XATTR_REPLACE) {
2157 error = ENOATTR; /* nothing there to replace */
2158 goto out;
2159 }
2160 /* Check if header size limit has been reached. */
2161 if ((header->data_start + entrylen) > ATTR_MAX_HDR_SIZE) {
2162 error = ENOSPC;
2163 goto out;
2164 }
2165
2166 datafreespace = header->total_size - (header->data_start + header->data_length);
2167
2168 /* Check if we need more space. */
2169 if ((datalen + entrylen) > datafreespace) {
2170 size_t growsize;
2171
2172 growsize = roundup((datalen + entrylen) - datafreespace, ATTR_BUF_SIZE);
2173
2174 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
2175 if (!splitdata && (header->total_size + growsize) > ATTR_MAX_HDR_SIZE) {
2176 growsize = ATTR_MAX_HDR_SIZE - header->total_size;
2177 }
2178
2179 ainfo.filesize += growsize;
2180 error = vnode_setsize(xvp, ainfo.filesize, 0, context);
2181 if (error) {
2182 printf("setxattr: VNOP_TRUNCATE error %d\n", error);
2183 }
2184 if (error) {
2185 goto out;
2186 }
2187
2188 /*
2189 * Move the resource fork out of the way.
2190 */
2191 if (ainfo.rsrcfork) {
2192 if (ainfo.rsrcfork->length != 0) {
2193 shift_data_down(xvp,
2194 ainfo.rsrcfork->offset,
2195 ainfo.rsrcfork->length,
2196 growsize, context);
2197 }
2198 ainfo.rsrcfork->offset += growsize;
2199 }
2200 ainfo.finderinfo->length += growsize;
2201 header->total_size += growsize;
2202 }
2203
2204 /* Make space for a new entry. */
2205 if (splitdata) {
2206 shift_data_down(xvp,
2207 header->data_start,
2208 header->data_length,
2209 entrylen, context);
2210 } else {
2211 bcopy((u_int8_t *)header + header->data_start,
2212 (u_int8_t *)header + header->data_start + entrylen,
2213 header->data_length);
2214 }
2215 header->data_start += entrylen;
2216
2217 /* Fix up entry data offsets. */
2218 lastentry = entry;
2219 for (entry = ainfo.attr_entry; entry != lastentry && ATTR_VALID(entry, ainfo); entry = ATTR_NEXT(entry)) {
2220 entry->offset += entrylen;
2221 }
2222
2223 /*
2224 * If the attribute data area is entirely within
2225 * the header buffer, then just update the buffer,
2226 * otherwise we'll write it separately to the file.
2227 */
2228 if (splitdata) {
2229 off_t offset;
2230
2231 /* Write new attribute data after the end of existing data. */
2232 offset = header->data_start + header->data_length;
2233 uio_setoffset(uio, offset);
2234 error = VNOP_WRITE(xvp, uio, 0, context);
2235 uio_setoffset(uio, 0);
2236 if (error) {
2237 printf("setxattr: VNOP_WRITE error %d\n", error);
2238 goto out;
2239 }
2240 } else {
2241 attrdata = (u_int8_t *)header + header->data_start + header->data_length;
2242
2243 error = uiomove((caddr_t)attrdata, (int)datalen, uio);
2244 if (error) {
2245 printf("setxattr: uiomove error %d\n", error);
2246 goto out;
2247 }
2248 }
2249
2250 /* Create the attribute entry. */
2251 lastentry->length = (u_int32_t)datalen;
2252 lastentry->offset = header->data_start + header->data_length;
2253 lastentry->namelen = (u_int8_t)namelen;
2254 lastentry->flags = 0;
2255 bcopy(name, &lastentry->name[0], namelen);
2256
2257 /* Update the attributes header. */
2258 header->num_attrs++;
2259 header->data_length += datalen;
2260
2261 if (splitdata) {
2262 /* Only write the entries, since the data was written separately. */
2263 ainfo.iosize = ainfo.attrhdr->data_start;
2264 } else {
2265 /* The entry and data are both in the header; write them together. */
2266 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
2267 }
2268 error = write_xattrinfo(&ainfo);
2269 if (error) {
2270 printf("setxattr: write_xattrinfo error %d\n", error);
2271 }
2272
2273 out:
2274 rel_xattrinfo(&ainfo);
2275 close_xattrfile(xfg, true, true, context);
2276
2277 /* Touch the change time if we changed an attribute. */
2278 if (error == 0) {
2279 struct vnode_attr va;
2280
2281 /* Re-write the mtime to cause a ctime change. */
2282 VATTR_INIT(&va);
2283 VATTR_WANTED(&va, va_modify_time);
2284 if (vnode_getattr(vp, &va, context) == 0) {
2285 VATTR_INIT(&va);
2286 VATTR_SET(&va, va_modify_time, va.va_modify_time);
2287 (void) vnode_setattr(vp, &va, context);
2288 }
2289 }
2290
2291 post_event_if_success(vp, error, NOTE_ATTRIB);
2292
2293 return error;
2294 }
2295
2296
2297 /*
2298 * Remove an extended attribute.
2299 * (VFS implementation).
2300 */
2301 static int
default_removexattr_vfs(vnode_t vp,const char * name,__unused int options,vfs_context_t context)2302 default_removexattr_vfs(vnode_t vp, const char *name, __unused int options, vfs_context_t context)
2303 {
2304 vnode_t xvp = NULL;
2305 struct fileglob *xfg = NULL;
2306 attr_info_t ainfo;
2307 attr_header_t *header;
2308 attr_entry_t *entry;
2309 attr_entry_t *oldslot;
2310 u_int8_t *attrdata;
2311 u_int32_t dataoff;
2312 size_t datalen;
2313 size_t entrylen;
2314 int namelen;
2315 int found = 0, lastone = 0;
2316 int i;
2317 int splitdata;
2318 int attrcount = 0;
2319 int isrsrcfork;
2320 int fileflags;
2321 int error;
2322
2323 fileflags = FREAD | FWRITE | O_EXLOCK;
2324 isrsrcfork = strncmp(name, XATTR_RESOURCEFORK_NAME,
2325 sizeof(XATTR_RESOURCEFORK_NAME)) == 0;
2326
2327 if ((error = open_xattrfile(vp, fileflags, &xfg, NULL, context))) {
2328 return error;
2329 }
2330 xvp = fg_get_data(xfg);
2331
2332 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
2333 close_xattrfile(xfg, true, true, context);
2334 return error;
2335 }
2336 if (ainfo.attrhdr) {
2337 attrcount += ainfo.attrhdr->num_attrs;
2338 }
2339 if (ainfo.rsrcfork) {
2340 ++attrcount;
2341 }
2342 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
2343 ++attrcount;
2344 }
2345
2346 /* Clear the Finder Info. */
2347 if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
2348 if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) {
2349 error = ENOATTR;
2350 goto out;
2351 }
2352 /* On removal of last attribute the ._ file is removed. */
2353 if (--attrcount == 0) {
2354 goto out;
2355 }
2356 attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset;
2357 bzero((caddr_t)attrdata, FINDERINFOSIZE);
2358 error = write_xattrinfo(&ainfo);
2359 goto out;
2360 }
2361
2362 /* Clear the Resource Fork. */
2363 if (isrsrcfork) {
2364 if (!vnode_isreg(vp)) {
2365 error = EPERM;
2366 goto out;
2367 }
2368 if (ainfo.rsrcfork == NULL || ainfo.rsrcfork->length == 0) {
2369 error = ENOATTR;
2370 goto out;
2371 }
2372 /* On removal of last attribute the ._ file is removed. */
2373 if (--attrcount == 0) {
2374 goto out;
2375 }
2376 /*
2377 * XXX
2378 * If the resource fork isn't the last AppleDouble
2379 * entry then the space needs to be reclaimed by
2380 * shifting the entries after the resource fork.
2381 */
2382 if ((ainfo.rsrcfork->offset + ainfo.rsrcfork->length) == ainfo.filesize) {
2383 ainfo.filesize -= ainfo.rsrcfork->length;
2384 error = vnode_setsize(xvp, ainfo.filesize, 0, context);
2385 }
2386 if (error == 0) {
2387 ainfo.rsrcfork->length = 0;
2388 ainfo.iosize = sizeof(attr_header_t);
2389 error = write_xattrinfo(&ainfo);
2390 }
2391 goto out;
2392 }
2393
2394 if (ainfo.attrhdr == NULL) {
2395 error = ENOATTR;
2396 goto out;
2397 }
2398 namelen = (int)strlen(name) + 1;
2399 header = ainfo.attrhdr;
2400 entry = ainfo.attr_entry;
2401
2402 /*
2403 * See if this attribute exists.
2404 */
2405 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
2406 if (strncmp((const char *)entry->name, name, namelen) == 0) {
2407 found = 1;
2408 if ((i + 1) == header->num_attrs) {
2409 lastone = 1;
2410 }
2411 break;
2412 }
2413 entry = ATTR_NEXT(entry);
2414 }
2415 if (!found) {
2416 error = ENOATTR;
2417 goto out;
2418 }
2419 /* On removal of last attribute the ._ file is removed. */
2420 if (--attrcount == 0) {
2421 goto out;
2422 }
2423
2424 datalen = entry->length;
2425 dataoff = entry->offset;
2426 entrylen = ATTR_ENTRY_LENGTH(namelen);
2427 if ((header->data_start + header->data_length) > ATTR_MAX_HDR_SIZE) {
2428 splitdata = 1;
2429 } else {
2430 splitdata = 0;
2431 }
2432
2433 /* Remove the attribute entry. */
2434 if (!lastone) {
2435 bcopy((u_int8_t *)entry + entrylen, (u_int8_t *)entry,
2436 ((size_t)header + header->data_start) - ((size_t)entry + entrylen));
2437 }
2438
2439 /* Adjust the attribute data. */
2440 if (splitdata) {
2441 shift_data_up(xvp,
2442 header->data_start,
2443 dataoff - header->data_start,
2444 entrylen,
2445 context);
2446 if (!lastone) {
2447 shift_data_up(xvp,
2448 dataoff + datalen,
2449 (header->data_start + header->data_length) - (dataoff + datalen),
2450 datalen + entrylen,
2451 context);
2452 }
2453 /* XXX write zeros to freed space ? */
2454 ainfo.iosize = ainfo.attrhdr->data_start - entrylen;
2455 } else {
2456 bcopy((u_int8_t *)header + header->data_start,
2457 (u_int8_t *)header + header->data_start - entrylen,
2458 dataoff - header->data_start);
2459 if (!lastone) {
2460 bcopy((u_int8_t *)header + dataoff + datalen,
2461 (u_int8_t *)header + dataoff - entrylen,
2462 (header->data_start + header->data_length) - (dataoff + datalen));
2463 }
2464 bzero(((u_int8_t *)header + header->data_start + header->data_length) - (datalen + entrylen), (datalen + entrylen));
2465 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
2466 }
2467
2468 /* Adjust the header values and entry offsets. */
2469 header->num_attrs--;
2470 header->data_start -= entrylen;
2471 header->data_length -= datalen;
2472
2473 oldslot = entry;
2474 entry = ainfo.attr_entry;
2475 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
2476 entry->offset -= entrylen;
2477 if (entry >= oldslot) {
2478 entry->offset -= datalen;
2479 }
2480 entry = ATTR_NEXT(entry);
2481 }
2482 error = write_xattrinfo(&ainfo);
2483 if (error) {
2484 printf("removexattr: write_xattrinfo error %d\n", error);
2485 }
2486 out:
2487 rel_xattrinfo(&ainfo);
2488
2489 /* When there are no more attributes remove the ._ file. */
2490 if (attrcount == 0) {
2491 remove_xattrfile(xfg, xvp, context);
2492 } else {
2493 close_xattrfile(xfg, true, true, context);
2494 }
2495 /* Touch the change time if we changed an attribute. */
2496 if (error == 0) {
2497 struct vnode_attr va;
2498
2499 /* Re-write the mtime to cause a ctime change. */
2500 VATTR_INIT(&va);
2501 VATTR_WANTED(&va, va_modify_time);
2502 if (vnode_getattr(vp, &va, context) == 0) {
2503 VATTR_INIT(&va);
2504 VATTR_SET(&va, va_modify_time, va.va_modify_time);
2505 (void) vnode_setattr(vp, &va, context);
2506 }
2507 }
2508
2509 post_event_if_success(vp, error, NOTE_ATTRIB);
2510
2511 return error;
2512 }
2513
2514
2515 /*
2516 * Retrieve the list of extended attribute names.
2517 * (VFS implementation).
2518 */
2519 static int
default_listxattr_vfs(vnode_t vp,uio_t uio,size_t * size,__unused int options,vfs_context_t context)2520 default_listxattr_vfs(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs_context_t context)
2521 {
2522 vnode_t xvp = NULL;
2523 struct fileglob *xfg = NULL;
2524 attr_info_t ainfo;
2525 attr_entry_t *entry;
2526 int i, count;
2527 int error;
2528
2529 /*
2530 * We do not zero "*size" here as we don't want to stomp a size set when
2531 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
2532 * system call layer, up in listxattr or flistxattr.
2533 */
2534
2535 if ((error = open_xattrfile(vp, FREAD | O_SHLOCK, &xfg, NULL, context))) {
2536 if (error == ENOATTR) {
2537 error = 0;
2538 }
2539 return error;
2540 }
2541 xvp = fg_get_data(xfg);
2542
2543 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
2544 if (error == ENOATTR) {
2545 error = 0;
2546 }
2547 close_xattrfile(xfg, true, true, context);
2548 return error;
2549 }
2550
2551 /* Check for Finder Info. */
2552 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
2553 if (uio == NULL) {
2554 *size += sizeof(XATTR_FINDERINFO_NAME);
2555 } else if (uio_resid(uio) < (user_ssize_t)sizeof(XATTR_FINDERINFO_NAME)) {
2556 error = ERANGE;
2557 goto out;
2558 } else {
2559 error = uiomove(XATTR_FINDERINFO_NAME,
2560 sizeof(XATTR_FINDERINFO_NAME), uio);
2561 if (error) {
2562 error = ERANGE;
2563 goto out;
2564 }
2565 }
2566 }
2567
2568 /* Check for Resource Fork. */
2569 if (vnode_isreg(vp) && ainfo.rsrcfork) {
2570 if (uio == NULL) {
2571 *size += sizeof(XATTR_RESOURCEFORK_NAME);
2572 } else if (uio_resid(uio) < (user_ssize_t)sizeof(XATTR_RESOURCEFORK_NAME)) {
2573 error = ERANGE;
2574 goto out;
2575 } else {
2576 error = uiomove(XATTR_RESOURCEFORK_NAME,
2577 sizeof(XATTR_RESOURCEFORK_NAME), uio);
2578 if (error) {
2579 error = ERANGE;
2580 goto out;
2581 }
2582 }
2583 }
2584
2585 /* Check for attributes. */
2586 if (ainfo.attrhdr) {
2587 count = ainfo.attrhdr->num_attrs;
2588 for (i = 0, entry = ainfo.attr_entry; i < count && ATTR_VALID(entry, ainfo); i++) {
2589 if (xattr_protected((const char *)entry->name) ||
2590 ((entry->namelen < XATTR_MAXNAMELEN) &&
2591 (entry->name[entry->namelen] == '\0') &&
2592 (xattr_validatename((const char *)entry->name) != 0))) {
2593 entry = ATTR_NEXT(entry);
2594 continue;
2595 }
2596 if (uio == NULL) {
2597 *size += entry->namelen;
2598 entry = ATTR_NEXT(entry);
2599 continue;
2600 }
2601 if (uio_resid(uio) < entry->namelen) {
2602 error = ERANGE;
2603 break;
2604 }
2605 error = uiomove((caddr_t) entry->name, entry->namelen, uio);
2606 if (error) {
2607 if (error != EFAULT) {
2608 error = ERANGE;
2609 }
2610 break;
2611 }
2612 entry = ATTR_NEXT(entry);
2613 }
2614 }
2615 out:
2616 rel_xattrinfo(&ainfo);
2617 close_xattrfile(xfg, true, true, context);
2618
2619 return error;
2620 }
2621
2622 static int
get_doubleagentd_port(mach_port_t * doubleagentd_port)2623 get_doubleagentd_port(mach_port_t *doubleagentd_port)
2624 {
2625 kern_return_t ret;
2626
2627 *doubleagentd_port = MACH_PORT_NULL;
2628 ret = host_get_doubleagentd_port(host_priv_self(), doubleagentd_port);
2629 if (ret != KERN_SUCCESS) {
2630 printf("vfs_xattr: can't get doubleagentd port, status 0x%08x\n", ret);
2631 return EIO;
2632 }
2633 if (!IPC_PORT_VALID(*doubleagentd_port)) {
2634 printf("vfs_xattr: doubleagentd port not valid\n");
2635 return EIO;
2636 }
2637 return 0;
2638 }
2639
2640 /*
2641 * Retrieve the data of an extended attribute.
2642 * (Using DoubleAgent to parse the AD file).
2643 */
2644 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)2645 default_getxattr_doubleagent(vnode_t vp, const char *name, uio_t uio,
2646 size_t *size, __unused int options, vfs_context_t context,
2647 mach_port_t doubleagentd_port)
2648 {
2649 vnode_t xvp = NULL;
2650 struct fileglob *xfg = NULL;
2651 mach_port_t fileport = MACH_PORT_NULL;
2652 uint64_t value_offset = 0;
2653 uint64_t value_length = 0;
2654 int64_t fsize;
2655 int isrsrcfork;
2656 int fileflags;
2657 int error;
2658 kern_return_t kr;
2659 char cName[XATTR_MAXNAMELEN] = {0};
2660 bool have_iocount = true;
2661
2662 fileflags = FREAD | O_SHLOCK;
2663 isrsrcfork = strncmp(name, XATTR_RESOURCEFORK_NAME,
2664 sizeof(XATTR_RESOURCEFORK_NAME)) == 0;
2665
2666 if ((error = open_xattrfile(vp, fileflags, &xfg, &fsize, context))) {
2667 goto out;
2668 }
2669 xvp = fg_get_data(xfg);
2670 if ((error = make_xattrfile_port(xfg, &fileport))) {
2671 goto out;
2672 }
2673
2674 /* Drop the iocount before upcalling to doubleagentd. */
2675 vnode_put(xvp);
2676 have_iocount = false;
2677
2678 strncpy(cName, name, XATTR_MAXNAMELEN);
2679
2680 /*
2681 * Call doubleagentd to look up the xattr. The fileport argument
2682 * is declared move-send, so the Mig stub consumes it.
2683 */
2684 kr = doubleagent_lookup_xattr(doubleagentd_port, fileport, fsize, cName,
2685 &error, &value_offset, &value_length);
2686 if (kr != KERN_SUCCESS) {
2687 error = EIO;
2688 }
2689 if (error == 0) {
2690 error = vnode_getwithref(xvp);
2691 }
2692 if (error) {
2693 goto out;
2694 }
2695 have_iocount = true;
2696 if (uio != NULL) {
2697 if (isrsrcfork) {
2698 // Resource Fork case is a bit different,
2699 // as we can have a non-zero uio offset.
2700 uio_setoffset(uio, uio_offset(uio) + value_offset);
2701 error = VNOP_READ(xvp, uio, 0, context);
2702 if (error == 0) {
2703 uio_setoffset(uio, uio_offset(uio) - value_offset);
2704 }
2705 } else {
2706 if (uio_resid(uio) < value_length) {
2707 error = ERANGE;
2708 goto out;
2709 }
2710
2711 // Read from the relevant offset in the AD file into the uio.
2712 user_ssize_t orig_resid = uio_resid(uio);
2713 uio_setoffset(uio, value_offset);
2714 uio_setresid(uio, value_length);
2715
2716 error = VNOP_READ(xvp, uio, 0, context);
2717
2718 uio_setoffset(uio, 0);
2719 uio_setresid(uio, orig_resid - value_length + uio_resid(uio));
2720 }
2721 }
2722
2723 *size = value_length;
2724
2725 out:
2726 if (xfg != NULL) {
2727 close_xattrfile(xfg, have_iocount, true, context);
2728 }
2729 return error;
2730 }
2731
2732 /*
2733 * Retrieve the list of extended attribute names.
2734 * (Using DoubleAgent to parse the AD file).
2735 */
2736 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)2737 default_listxattr_doubleagent(vnode_t vp, uio_t uio, size_t *size,
2738 __unused int options, vfs_context_t context, mach_port_t doubleagentd_port)
2739 {
2740 vnode_t xvp = NULL;
2741 struct fileglob *xfg = NULL;
2742 int64_t fsize;
2743 int error;
2744
2745 mach_port_t fileport = MACH_PORT_NULL;
2746 kern_return_t kr;
2747 void *buf = NULL;
2748 listxattrs_result_t *result;
2749 bool have_iocount = true;
2750
2751 /*
2752 * We do not zero "*size" here as we don't want to stomp a size set
2753 * when VNOP_LISTXATTR() processed any native EAs. That size is
2754 * initially zeroed by the system call layer, up in listxattr() or
2755 * flistxattr().
2756 */
2757
2758 if ((error = open_xattrfile(vp, FREAD | O_SHLOCK, &xfg, &fsize,
2759 context))) {
2760 if (error == ENOATTR) {
2761 error = 0;
2762 }
2763 goto out;
2764 }
2765 xvp = fg_get_data(xfg);
2766 if ((error = make_xattrfile_port(xfg, &fileport))) {
2767 goto out;
2768 }
2769
2770 /* Drop the iocount before upcalling to doubleagentd. */
2771 vnode_put(xvp);
2772 have_iocount = false;
2773
2774 buf = kalloc_data(sizeof(listxattrs_result_t), Z_WAITOK);
2775 result = (listxattrs_result_t *)buf;
2776
2777 /*
2778 * Call doubleagentd to list the xattrs. The fileport argument
2779 * is declared move-send, so the Mig stub consumes it.
2780 */
2781 kr = doubleagent_list_xattrs(doubleagentd_port, fileport, fsize, &error,
2782 result);
2783 if (kr != KERN_SUCCESS) {
2784 error = EIO;
2785 }
2786 if (error == 0) {
2787 error = vnode_getwithref(xvp);
2788 }
2789 if (error) {
2790 goto out;
2791 }
2792 have_iocount = true;
2793
2794 if (uio != NULL) {
2795 if (uio_resid(uio) < result->namesLength) {
2796 error = ERANGE;
2797 goto out;
2798 }
2799 // copy the relevant part of the result into the uio.
2800 error = uiomove((const char *)result->data, (int)result->namesLength, uio);
2801 if (error) {
2802 if (error != EFAULT) {
2803 error = ERANGE;
2804 }
2805 goto out;
2806 }
2807 }
2808
2809 /*
2810 * Set *size, while preserving any previous value from
2811 * VNOP_LISTXATTR().
2812 */
2813 *size += result->namesLength;
2814
2815 out:
2816 if (xfg != NULL) {
2817 close_xattrfile(xfg, have_iocount, true, context);
2818 }
2819 if (buf != NULL) {
2820 kfree_data(buf, sizeof(listxattrs_result_t));
2821 }
2822 return error;
2823 }
2824
2825 /*
2826 * Set the data of an extended attribute.
2827 * (Using DoubleAgent to parse the AD file).
2828 */
2829 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)2830 default_setxattr_doubleagent(vnode_t vp, const char *name, uio_t uio,
2831 int options, vfs_context_t context, mach_port_t doubleagentd_port)
2832 {
2833 vnode_t xvp = NULL;
2834 struct fileglob *xfg = NULL;
2835 size_t datalen;
2836 int namelen;
2837 int fileflags;
2838 int error;
2839 char cName[XATTR_MAXNAMELEN] = {0};
2840 char finfo[FINDERINFOSIZE];
2841 uio_t finfo_uio = NULL;
2842 mach_port_t fileport = MACH_PORT_NULL;
2843 uint64_t value_offset = 0;
2844 int64_t fsize;
2845 kern_return_t kr;
2846 bool have_iocount = true;
2847
2848 datalen = uio_resid(uio);
2849 if (datalen > XATTR_MAXSIZE) {
2850 return E2BIG;
2851 }
2852 namelen = (int)strlen(name) + 1;
2853 if (namelen > UINT8_MAX) {
2854 return EINVAL;
2855 }
2856
2857 /*
2858 * By convention, Finder Info that is all zeroes is equivalent to not
2859 * having a Finder Info EA. So if we're trying to set the Finder Info
2860 * to all zeroes, then delete it instead. If a file didn't have an
2861 * AppleDouble file before, this prevents creating an AppleDouble file
2862 * with no useful content.
2863 *
2864 * If neither XATTR_CREATE nor XATTR_REPLACE were specified, we check
2865 * for all zeroes Finder Info before opening the AppleDouble file.
2866 * But if either of those options were specified, we need to open the
2867 * AppleDouble file to see whether there was already Finder Info (so we
2868 * can return an error if needed); this case is handled in DoubleAgent.
2869 */
2870 if (strncmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
2871 if (uio_offset(uio) != 0) {
2872 return EINVAL;
2873 }
2874
2875 if (datalen != FINDERINFOSIZE) {
2876 return ERANGE;
2877 }
2878
2879 // Duplicate the uio to keep it as-is for later.
2880 finfo_uio = uio_duplicate(uio);
2881 // Get the finfo data from the duplicated uio.
2882 error = uiomove(finfo, (int)datalen, finfo_uio);
2883 uio_free(finfo_uio);
2884 if (error) {
2885 return error;
2886 }
2887 if ((options & (XATTR_CREATE | XATTR_REPLACE)) == 0 &&
2888 bcmp(finfo, emptyfinfo, FINDERINFOSIZE) == 0) {
2889 error = default_removexattr(vp, name, 0, context);
2890 if (error == ENOATTR) {
2891 error = 0;
2892 }
2893 return error;
2894 }
2895 }
2896
2897 if (strncmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
2898 /*
2899 * For ResourceFork we allow offset to be != 0, so adjust datalen accordingly
2900 * so doubleagent will adjust the file length accordingly
2901 *
2902 */
2903 if (__improbable(os_add_overflow(datalen, uio_offset(uio), &datalen))) {
2904 return EINVAL;
2905 }
2906
2907 if (datalen > UINT32_MAX) {
2908 return EINVAL;
2909 }
2910 }
2911
2912 /*
2913 * Open the file locked since setting an attribute
2914 * can change the layout of the Apple Double file.
2915 */
2916 fileflags = FREAD | FWRITE | O_EXLOCK;
2917 if ((error = open_xattrfile(vp, O_CREAT | fileflags, &xfg,
2918 &fsize, context))) {
2919 goto out;
2920 }
2921 xvp = fg_get_data(xfg);
2922 if ((error = make_xattrfile_port(xfg, &fileport))) {
2923 goto out;
2924 }
2925
2926 /* Drop the iocount before upcalling to doubleagentd. */
2927 vnode_put(xvp);
2928 have_iocount = false;
2929
2930 strncpy(cName, name, XATTR_MAXNAMELEN);
2931
2932 /*
2933 * Call doubleagentd to allocate space for the xattr. The
2934 * fileport argument is declared move-send, so the Mig stub
2935 * consumes it.
2936 */
2937 kr = doubleagent_allocate_xattr(doubleagentd_port, fileport, fsize,
2938 cName, datalen, options, &error, &value_offset);
2939 if (kr != KERN_SUCCESS) {
2940 error = EIO;
2941 }
2942 if (error == 0) {
2943 error = vnode_getwithref(xvp);
2944 }
2945 if (error) {
2946 goto out;
2947 }
2948 have_iocount = true;
2949
2950 /*
2951 * write the uio data into the offset we got from doubleagent,
2952 * while adding the given uio offset (could be non-zero only for
2953 * resource fork; it is being checked earlier).
2954 */
2955 uio_setoffset(uio, uio_offset(uio) + value_offset);
2956 error = VNOP_WRITE(xvp, uio, 0, context);
2957 uio_setoffset(uio, 0);
2958
2959 out:
2960 if (xfg != NULL) {
2961 close_xattrfile(xfg, have_iocount, true, context);
2962 }
2963
2964 /* Touch the change time if we changed an attribute. */
2965 if (error == 0) {
2966 struct vnode_attr va;
2967
2968 /* Re-write the mtime to cause a ctime change. */
2969 VATTR_INIT(&va);
2970 VATTR_WANTED(&va, va_modify_time);
2971 if (vnode_getattr(vp, &va, context) == 0) {
2972 VATTR_INIT(&va);
2973 VATTR_SET(&va, va_modify_time, va.va_modify_time);
2974 (void) vnode_setattr(vp, &va, context);
2975 }
2976 }
2977
2978 post_event_if_success(vp, error, NOTE_ATTRIB);
2979
2980 return error;
2981 }
2982
2983 /*
2984 * Remove an extended attribute.
2985 * (Using DoubleAgent to parse the AD file).
2986 */
2987 static int
default_removexattr_doubleagent(vnode_t vp,const char * name,__unused int options,vfs_context_t context,mach_port_t doubleagentd_port)2988 default_removexattr_doubleagent(vnode_t vp, const char *name,
2989 __unused int options, vfs_context_t context,
2990 mach_port_t doubleagentd_port)
2991 {
2992 vnode_t xvp = NULL;
2993 struct fileglob *xfg = NULL;
2994 int isrsrcfork;
2995 int fileflags;
2996 int error;
2997 int64_t fsize;
2998 boolean_t is_empty = false;
2999 char cName[XATTR_MAXNAMELEN] = {0};
3000 mach_port_t fileport = MACH_PORT_NULL;
3001 kern_return_t kr;
3002 bool have_iocount = true;
3003
3004 fileflags = FREAD | FWRITE | O_EXLOCK;
3005 isrsrcfork = strncmp(name, XATTR_RESOURCEFORK_NAME,
3006 sizeof(XATTR_RESOURCEFORK_NAME)) == 0;
3007
3008 if ((error = open_xattrfile(vp, fileflags, &xfg, &fsize, context))) {
3009 goto out;
3010 }
3011 xvp = fg_get_data(xfg);
3012 if ((error = make_xattrfile_port(xfg, &fileport))) {
3013 goto out;
3014 }
3015
3016 /* Drop the iocount before upcalling to doubleagentd. */
3017 vnode_put(xvp);
3018 have_iocount = false;
3019
3020 strncpy(cName, name, XATTR_MAXNAMELEN);
3021
3022 /*
3023 * Call doubleagentd to remove the xattr. The fileport argument
3024 * is declared move-send, so the Mig stub consumes it.
3025 */
3026 kr = doubleagent_remove_xattr(doubleagentd_port, fileport, fsize, cName,
3027 &error, &is_empty);
3028 if (kr != KERN_SUCCESS) {
3029 error = EIO;
3030 }
3031 if (error == 0) {
3032 error = vnode_getwithref(xvp);
3033 }
3034 if (error) {
3035 goto out;
3036 }
3037 have_iocount = true;
3038
3039 out:
3040 if (error == 0) {
3041 /* When there are no more attributes remove the ._ file. */
3042 if (is_empty) {
3043 remove_xattrfile(xfg, xvp, context);
3044 } else {
3045 close_xattrfile(xfg, have_iocount, true, context);
3046 }
3047 xfg = NULL;
3048
3049 /* Touch the change time if we changed an attribute. */
3050 struct vnode_attr va;
3051 /* Re-write the mtime to cause a ctime change. */
3052 VATTR_INIT(&va);
3053 VATTR_WANTED(&va, va_modify_time);
3054 if (vnode_getattr(vp, &va, context) == 0) {
3055 VATTR_INIT(&va);
3056 VATTR_SET(&va, va_modify_time, va.va_modify_time);
3057 (void) vnode_setattr(vp, &va, context);
3058 }
3059 }
3060
3061 post_event_if_success(vp, error, NOTE_ATTRIB);
3062
3063 if (xfg != NULL) {
3064 close_xattrfile(xfg, have_iocount, true, context);
3065 }
3066 return error;
3067 }
3068
3069 static int
open_xattrfile(vnode_t vp,int fileflags,struct fileglob ** xfgp,int64_t * file_sizep,vfs_context_t context)3070 open_xattrfile(vnode_t vp, int fileflags, struct fileglob **xfgp,
3071 int64_t *file_sizep, vfs_context_t context)
3072 {
3073 extern const struct fileops vnops; /* XXX */
3074 vnode_t xvp = NULLVP;
3075 vnode_t dvp = NULLVP;
3076 struct fileglob *fg = NULL;
3077 struct vnode_attr *va = NULL;
3078 struct nameidata *nd = NULL;
3079 char smallname[64];
3080 char *filename = NULL;
3081 const char *basename = NULL;
3082 size_t alloc_len = 0;
3083 size_t copy_len;
3084 errno_t error;
3085 int opened = 0;
3086 int referenced = 0;
3087 bool created_xattr_file = false;
3088
3089 if (vnode_isvroot(vp) && vnode_isdir(vp)) {
3090 /*
3091 * For the root directory use "._." to hold the attributes.
3092 */
3093 filename = &smallname[0];
3094 snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, ".");
3095 dvp = vp; /* the "._." file resides in the root dir */
3096 goto lookup;
3097 }
3098 if ((dvp = vnode_getparent(vp)) == NULLVP) {
3099 error = ENOATTR;
3100 goto out;
3101 }
3102 if ((basename = vnode_getname(vp)) == NULL) {
3103 error = ENOATTR;
3104 goto out;
3105 }
3106
3107 /* "._" Attribute files cannot have attributes */
3108 if (vp->v_type == VREG && strlen(basename) > 2 &&
3109 basename[0] == '.' && basename[1] == '_') {
3110 error = EPERM;
3111 goto out;
3112 }
3113 filename = &smallname[0];
3114 alloc_len = snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, basename);
3115 if (alloc_len >= sizeof(smallname)) {
3116 alloc_len++; /* snprintf result doesn't include '\0' */
3117 filename = kalloc_data(alloc_len, Z_WAITOK);
3118 copy_len = snprintf(filename, alloc_len, "%s%s", ATTR_FILE_PREFIX, basename);
3119 }
3120 /*
3121 * Note that the lookup here does not authorize. Since we are looking
3122 * up in the same directory that we already have the file vnode in,
3123 * we must have been given the file vnode legitimately. Read/write
3124 * access has already been authorized in layers above for calls from
3125 * userspace, and the authorization code using this path to read
3126 * file security from the EA must always get access
3127 */
3128 lookup:
3129 nd = kalloc_type(struct nameidata, Z_WAITOK);
3130 NDINIT(nd, LOOKUP, OP_OPEN, LOCKLEAF | NOFOLLOW | USEDVP | DONOTAUTH,
3131 UIO_SYSSPACE, CAST_USER_ADDR_T(filename), context);
3132 nd->ni_dvp = dvp;
3133
3134 va = kalloc_type(struct vnode_attr, Z_WAITOK);
3135
3136 if (fileflags & O_CREAT) {
3137 nd->ni_cnd.cn_nameiop = CREATE;
3138 #if CONFIG_TRIGGERS
3139 nd->ni_op = OP_LINK;
3140 #endif
3141 if (dvp != vp) {
3142 nd->ni_cnd.cn_flags |= LOCKPARENT;
3143 }
3144 if ((error = namei(nd))) {
3145 nd->ni_dvp = NULLVP;
3146 error = ENOATTR;
3147 goto out;
3148 }
3149 if ((xvp = nd->ni_vp) == NULLVP) {
3150 uid_t uid;
3151 gid_t gid;
3152 mode_t umode;
3153
3154 /*
3155 * Pick up uid/gid/mode from target file.
3156 */
3157 VATTR_INIT(va);
3158 VATTR_WANTED(va, va_uid);
3159 VATTR_WANTED(va, va_gid);
3160 VATTR_WANTED(va, va_mode);
3161 if (VNOP_GETATTR(vp, va, context) == 0 &&
3162 VATTR_IS_SUPPORTED(va, va_uid) &&
3163 VATTR_IS_SUPPORTED(va, va_gid) &&
3164 VATTR_IS_SUPPORTED(va, va_mode)) {
3165 uid = va->va_uid;
3166 gid = va->va_gid;
3167 umode = va->va_mode & (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
3168 } else { /* fallback values */
3169 uid = KAUTH_UID_NONE;
3170 gid = KAUTH_GID_NONE;
3171 umode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
3172 }
3173
3174 VATTR_INIT(va);
3175 VATTR_SET(va, va_type, VREG);
3176 VATTR_SET(va, va_mode, umode);
3177 if (uid != KAUTH_UID_NONE) {
3178 VATTR_SET(va, va_uid, uid);
3179 }
3180 if (gid != KAUTH_GID_NONE) {
3181 VATTR_SET(va, va_gid, gid);
3182 }
3183
3184 error = vn_create(dvp, &nd->ni_vp, nd, va,
3185 VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT | VN_CREATE_NOLABEL,
3186 0, NULL,
3187 context);
3188 if (error) {
3189 error = ENOATTR;
3190 } else {
3191 xvp = nd->ni_vp;
3192 created_xattr_file = true;
3193 }
3194 }
3195 nameidone(nd);
3196 if (dvp != vp) {
3197 vnode_put(dvp); /* drop iocount from LOCKPARENT request above */
3198 }
3199 if (error) {
3200 goto out;
3201 }
3202 } else {
3203 if ((error = namei(nd))) {
3204 nd->ni_dvp = NULLVP;
3205 error = ENOATTR;
3206 goto out;
3207 }
3208 xvp = nd->ni_vp;
3209 nameidone(nd);
3210 }
3211 nd->ni_dvp = NULLVP;
3212
3213 if (xvp->v_type != VREG) {
3214 error = ENOATTR;
3215 goto out;
3216 }
3217 /*
3218 * Owners must match.
3219 */
3220 VATTR_INIT(va);
3221 VATTR_WANTED(va, va_uid);
3222 if (VNOP_GETATTR(vp, va, context) == 0 && VATTR_IS_SUPPORTED(va, va_uid)) {
3223 uid_t owner = va->va_uid;
3224
3225 VATTR_INIT(va);
3226 VATTR_WANTED(va, va_uid);
3227 if (VNOP_GETATTR(xvp, va, context) == 0 && (owner != va->va_uid)) {
3228 error = ENOATTR; /* don't use this "._" file */
3229 goto out;
3230 }
3231 }
3232
3233 if ((error = VNOP_OPEN(xvp, fileflags & ~(O_EXLOCK | O_SHLOCK), context))) {
3234 error = ENOATTR;
3235 goto out;
3236 }
3237 opened = 1;
3238
3239 if ((error = vnode_ref_ext(xvp, fileflags, 0)) != 0) {
3240 goto out;
3241 }
3242 referenced = 1;
3243
3244 /*
3245 * If create was requested, make sure file header exists.
3246 * This is only done in the non-DoubleAgent case.
3247 * XXX And will be garbage-collected in due time.
3248 */
3249 if (!vfs_xattr_doubleagent_enabled && (fileflags & O_CREAT) != 0) {
3250 VATTR_INIT(va);
3251 VATTR_WANTED(va, va_data_size);
3252 VATTR_WANTED(va, va_fileid);
3253 VATTR_WANTED(va, va_nlink);
3254 if ((error = vnode_getattr(xvp, va, context)) != 0) {
3255 error = EPERM;
3256 goto out;
3257 }
3258
3259 /* If the file is empty then add a default header. */
3260 if (va->va_data_size == 0) {
3261 /* Don't adopt hard-linked "._" files. */
3262 if (VATTR_IS_SUPPORTED(va, va_nlink) && va->va_nlink > 1) {
3263 error = EPERM;
3264 goto out;
3265 }
3266 if ((error = create_xattrfile(xvp, (u_int32_t)va->va_fileid, context))) {
3267 goto out;
3268 }
3269 }
3270 }
3271
3272 /*
3273 * Allocate a file object for the referenced vnode.
3274 * This file object now owns the vnode reference,
3275 * and the caller owns the iocount, which will be
3276 * dropped in close_xattrfile().
3277 */
3278 fg = fg_alloc_init(context);
3279 fg->fg_flag = fileflags & FMASK;
3280 fg->fg_ops = &vnops;
3281 fg_set_data(fg, xvp);
3282
3283 /* Apply file locking if requested. */
3284 if (fileflags & (O_EXLOCK | O_SHLOCK)) {
3285 struct flock lf = {
3286 .l_whence = SEEK_SET,
3287 };
3288
3289 if (fileflags & O_EXLOCK) {
3290 lf.l_type = F_WRLCK;
3291 } else {
3292 lf.l_type = F_RDLCK;
3293 }
3294 error = VNOP_ADVLOCK(xvp, (caddr_t)fg, F_SETLK, &lf, F_FLOCK | F_WAIT, context, NULL);
3295 if (error == ENOTSUP) {
3296 error = 0;
3297 } else if (error) {
3298 error = ENOATTR;
3299 goto out;
3300 } else { // error == 0
3301 fg->fg_flag |= FWASLOCKED;
3302 }
3303 }
3304
3305 if (file_sizep != NULL) {
3306 /*
3307 * Now that the file is locked, get the file's size.
3308 */
3309 VATTR_INIT(va);
3310 VATTR_WANTED(va, va_data_size);
3311 if ((error = vnode_getattr(xvp, va, context)) != 0) {
3312 error = ENOATTR;
3313 goto out;
3314 }
3315 *file_sizep = va->va_data_size;
3316 }
3317 out:
3318 if (error) {
3319 if (fg != NULL) {
3320 /* Let the normal close path handle this. */
3321 if (created_xattr_file) {
3322 remove_xattrfile(fg, xvp, context);
3323 } else {
3324 close_xattrfile(fg, true, true, context);
3325 }
3326 fg = NULL;
3327 xvp = NULLVP;
3328 } else if (xvp != NULLVP) {
3329 if (opened) {
3330 (void) VNOP_CLOSE(xvp, fileflags, context);
3331 }
3332 if (created_xattr_file) {
3333 remove_xattrfile(NULL, xvp, context);
3334 }
3335 if (referenced) {
3336 (void) vnode_rele(xvp);
3337 }
3338 /* remove_xattrfile() would have dropped the iocount already */
3339 if (!created_xattr_file) {
3340 (void) vnode_put(xvp);
3341 }
3342 xvp = NULLVP;
3343 }
3344 if ((error == ENOATTR) && (fileflags & O_CREAT)) {
3345 error = EPERM;
3346 }
3347 }
3348
3349 /* Release resources after error-handling */
3350 kfree_type(struct nameidata, nd);
3351 kfree_type(struct vnode_attr, va);
3352 if (dvp && (dvp != vp)) {
3353 vnode_put(dvp);
3354 }
3355 if (basename) {
3356 vnode_putname(basename);
3357 }
3358 if (filename && filename != &smallname[0]) {
3359 kfree_data(filename, alloc_len);
3360 }
3361
3362 *xfgp = fg;
3363 return error;
3364 }
3365
3366 static void
close_xattrfile(struct fileglob * xfg,bool have_iocount,bool drop_iocount,vfs_context_t context)3367 close_xattrfile(struct fileglob *xfg, bool have_iocount, bool drop_iocount,
3368 vfs_context_t context)
3369 {
3370 vnode_t xvp = fg_get_data(xfg);
3371
3372 /*
3373 * N.B. The only time have_iocount would be false would be when
3374 * a vnode_getwithref() calls fails after coming back from a
3375 * doubleagentd upcall. If that happens, then it would mean
3376 * that the old vnode identity is gone, and our advisory lock
3377 * would have been garbage-collected when the vnode was reclaimed.
3378 */
3379 if (have_iocount) {
3380 /*
3381 * fg_drop() won't drop our advisory lock because we are not
3382 * following POSIX semantics. Drop it here.
3383 */
3384 struct flock lf = {
3385 .l_whence = SEEK_SET,
3386 .l_type = F_UNLCK,
3387 };
3388 (void)VNOP_ADVLOCK(xvp, (caddr_t)xfg, F_UNLCK, &lf, F_FLOCK,
3389 context, NULL);
3390
3391 /* (Maybe) drop the iocount we took in open_xattrfile(). */
3392 if (drop_iocount) {
3393 vnode_put(xvp);
3394 }
3395 }
3396
3397 (void) fg_drop(current_proc(), xfg);
3398 }
3399
3400 static void
remove_xattrfile(struct fileglob * xfg,vnode_t xvp,vfs_context_t context)3401 remove_xattrfile(struct fileglob *xfg, vnode_t xvp, vfs_context_t context)
3402 {
3403 vnode_t dvp = NULL, rvp = NULL;
3404 struct nameidata nd;
3405 char *path = NULL;
3406 int pathlen;
3407 int error;
3408
3409 if (xfg != NULL) {
3410 /*
3411 * Close the xattr file but don't dispose of the
3412 * iocount acquired in open_xattrfile() while doing
3413 * so. We'll do that below once we have performed
3414 * the unlink operation.
3415 */
3416 close_xattrfile(xfg, true, false, context);
3417 }
3418
3419 path = zalloc(ZV_NAMEI);
3420 pathlen = MAXPATHLEN;
3421 error = vn_getpath(xvp, path, &pathlen);
3422 if (error) {
3423 zfree(ZV_NAMEI, path);
3424 goto out;
3425 }
3426
3427 NDINIT(&nd, DELETE, OP_UNLINK, LOCKPARENT | NOFOLLOW | DONOTAUTH,
3428 UIO_SYSSPACE, CAST_USER_ADDR_T(path), context);
3429 error = namei(&nd);
3430 zfree(ZV_NAMEI, path);
3431 if (error) {
3432 goto out;
3433 }
3434 dvp = nd.ni_dvp;
3435 rvp = nd.ni_vp;
3436
3437 /*
3438 * Only remove if the namei() returned to us the same vnode that
3439 * we think we are supposed to be removing. If they're not the
3440 * same, we could have raced against something else trying to
3441 * unlink it, and we don't want to remove someone else's (possibly
3442 * very important) file.
3443 */
3444 if (rvp == xvp) {
3445 (void) VNOP_REMOVE(dvp, rvp, &nd.ni_cnd, 0, context);
3446 }
3447 nameidone(&nd);
3448
3449 out:
3450 vnode_put(xvp);
3451 if (dvp != NULLVP) {
3452 vnode_put(dvp);
3453 }
3454 if (rvp != NULLVP) {
3455 vnode_put(rvp);
3456 }
3457 }
3458
3459 /*
3460 * Read in and parse the AppleDouble header and entries, and the extended
3461 * attribute header and entries if any. Populates the fields of ainfop
3462 * based on the headers and entries found.
3463 *
3464 * The basic idea is to:
3465 * - Read in up to ATTR_MAX_HDR_SIZE bytes of the start of the file. All
3466 * AppleDouble entries, the extended attribute header, and extended
3467 * attribute entries must lie within this part of the file; the rest of
3468 * the AppleDouble handling code assumes this. Plus it allows us to
3469 * somewhat optimize by doing a smaller number of larger I/Os.
3470 * - Swap and sanity check the AppleDouble header (including the AppleDouble
3471 * entries).
3472 * - Find the Finder Info and Resource Fork entries, if any.
3473 * - If we're going to be writing, try to make sure the Finder Info entry has
3474 * room to store the extended attribute header, plus some space for extended
3475 * attributes.
3476 * - Swap and sanity check the extended attribute header and entries (if any).
3477 */
3478 static int
get_xattrinfo(vnode_t xvp,int setting,attr_info_t * ainfop,vfs_context_t context)3479 get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context)
3480 {
3481 uio_t auio = NULL;
3482 void * buffer = NULL;
3483 apple_double_header_t *filehdr;
3484 struct vnode_attr va;
3485 size_t iosize = 0;
3486 int i;
3487 int error;
3488
3489 bzero(ainfop, sizeof(attr_info_t));
3490 ainfop->filevp = xvp;
3491 ainfop->context = context;
3492 VATTR_INIT(&va);
3493 VATTR_WANTED(&va, va_data_size);
3494 VATTR_WANTED(&va, va_fileid);
3495 if ((error = vnode_getattr(xvp, &va, context))) {
3496 goto bail;
3497 }
3498 ainfop->filesize = va.va_data_size;
3499
3500 /* When setting attributes, allow room for the header to grow. */
3501 if (setting) {
3502 iosize = ATTR_MAX_HDR_SIZE;
3503 } else {
3504 iosize = MIN(ATTR_MAX_HDR_SIZE, ainfop->filesize);
3505 }
3506
3507 if (iosize == 0 || iosize < sizeof(apple_double_header_t)) {
3508 error = ENOATTR;
3509 goto bail;
3510 }
3511
3512 ainfop->iosize = iosize;
3513 buffer = kalloc_data(iosize, Z_WAITOK | Z_ZERO);
3514 if (buffer == NULL) {
3515 error = ENOMEM;
3516 goto bail;
3517 }
3518
3519 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
3520 uio_addiov(auio, (uintptr_t)buffer, iosize);
3521
3522 /* Read the file header. */
3523 error = VNOP_READ(xvp, auio, 0, context);
3524 if (error) {
3525 goto bail;
3526 }
3527 ainfop->rawsize = iosize - uio_resid(auio);
3528 ainfop->rawdata = (u_int8_t *)buffer;
3529
3530 filehdr = (apple_double_header_t *)buffer;
3531
3532 error = check_and_swap_apple_double_header(ainfop);
3533 if (error) {
3534 goto bail;
3535 }
3536
3537 ainfop->filehdr = filehdr; /* valid AppleDouble header */
3538
3539 /* rel_xattrinfo is responsible for freeing the header buffer */
3540 buffer = NULL;
3541
3542 /* Find the Finder Info and Resource Fork entries, if any */
3543 for (i = 0; i < filehdr->numEntries; ++i) {
3544 if (filehdr->entries[i].type == AD_FINDERINFO &&
3545 filehdr->entries[i].length >= FINDERINFOSIZE) {
3546 /* We found the Finder Info entry. */
3547 ainfop->finderinfo = &filehdr->entries[i];
3548
3549 /* At this point check_and_swap_apple_double_header() call above
3550 * verified that all apple double entires are valid:
3551 * they point somewhere within the file.
3552 *
3553 * Now for finderinfo make sure that the fixed portion
3554 * is within the buffer we read in.
3555 */
3556 if (((ainfop->finderinfo->offset + FINDERINFOSIZE) > ainfop->finderinfo->offset) &&
3557 ((ainfop->finderinfo->offset + FINDERINFOSIZE) <= ainfop->rawsize)) {
3558 /*
3559 * Is the Finder Info "empty" (all zeroes)? If so,
3560 * we'll pretend like the Finder Info extended attribute
3561 * does not exist.
3562 */
3563 if (bcmp((u_int8_t*)ainfop->filehdr + ainfop->finderinfo->offset, emptyfinfo, sizeof(emptyfinfo)) == 0) {
3564 ainfop->emptyfinderinfo = 1;
3565 }
3566 } else {
3567 error = ENOATTR;
3568 goto bail;
3569 }
3570 }
3571 if (filehdr->entries[i].type == AD_RESOURCE) {
3572 /*
3573 * Ignore zero-length resource forks when getting. If setting,
3574 * we need to remember the resource fork entry so it can be
3575 * updated once the new content has been written.
3576 */
3577 if (filehdr->entries[i].length == 0 && !setting) {
3578 continue;
3579 }
3580
3581 /*
3582 * Check to see if any "empty" resource fork is ours (i.e. is ignorable).
3583 *
3584 * The "empty" resource headers we created have a system data tag of:
3585 * "This resource fork intentionally left blank "
3586 */
3587 if (filehdr->entries[i].length == sizeof(rsrcfork_header_t) && !setting) {
3588 uio_t rf_uio;
3589 u_int8_t systemData[64];
3590 int rf_err;
3591
3592
3593 /* Read the system data which starts at byte 16 */
3594 rf_uio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
3595 uio_addiov(rf_uio, (uintptr_t)systemData, sizeof(systemData));
3596 uio_setoffset(rf_uio, filehdr->entries[i].offset + 16);
3597 rf_err = VNOP_READ(xvp, rf_uio, 0, context);
3598 uio_free(rf_uio);
3599
3600 if (rf_err != 0 ||
3601 bcmp(systemData, RF_EMPTY_TAG, sizeof(RF_EMPTY_TAG)) == 0) {
3602 continue; /* skip this resource fork */
3603 }
3604 }
3605 ainfop->rsrcfork = &filehdr->entries[i];
3606 if (i != (filehdr->numEntries - 1)) {
3607 printf("get_xattrinfo: resource fork not last entry\n");
3608 ainfop->readonly = 1;
3609 }
3610 continue;
3611 }
3612 }
3613
3614 /*
3615 * See if this file looks like it is laid out correctly to contain
3616 * extended attributes. If so, then do the following:
3617 *
3618 * - If we're going to be writing, try to make sure the Finder Info
3619 * entry has room to store the extended attribute header, plus some
3620 * space for extended attributes.
3621 *
3622 * - Swap and sanity check the extended attribute header and entries
3623 * (if any).
3624 */
3625 if (filehdr->numEntries == 2 &&
3626 ainfop->finderinfo == &filehdr->entries[0] &&
3627 ainfop->rsrcfork == &filehdr->entries[1] &&
3628 ainfop->finderinfo->offset == offsetof(apple_double_header_t, finfo)) {
3629 attr_header_t *attrhdr;
3630 attrhdr = (attr_header_t *)filehdr;
3631 /*
3632 * If we're going to be writing, try to make sure the Finder
3633 * Info entry has room to store the extended attribute header,
3634 * plus some space for extended attributes.
3635 */
3636 if (setting && ainfop->finderinfo->length == FINDERINFOSIZE) {
3637 size_t delta;
3638 size_t writesize;
3639
3640 delta = ATTR_BUF_SIZE - (filehdr->entries[0].offset + FINDERINFOSIZE);
3641 if (ainfop->rsrcfork && filehdr->entries[1].length) {
3642 /* Make some room before existing resource fork. */
3643 shift_data_down(xvp,
3644 filehdr->entries[1].offset,
3645 filehdr->entries[1].length,
3646 delta, context);
3647 writesize = sizeof(attr_header_t);
3648 } else {
3649 /* We are in case where existing resource fork of length 0, try to create a new, empty resource fork. */
3650 rsrcfork_header_t *rsrcforkhdr;
3651
3652 /* Do we have enough space in the header buffer for empty resource fork */
3653 if (filehdr->entries[1].offset + delta + sizeof(rsrcfork_header_t) > ainfop->iosize) {
3654 /* we do not have space, bail for now */
3655 error = ENOATTR;
3656 goto bail;
3657 }
3658
3659 vnode_setsize(xvp, filehdr->entries[1].offset + delta, 0, context);
3660
3661 /* Steal some space for an empty RF header. */
3662 delta -= sizeof(rsrcfork_header_t);
3663
3664 bzero(&attrhdr->appledouble.pad[0], delta);
3665 rsrcforkhdr = (rsrcfork_header_t *)((char *)filehdr + filehdr->entries[1].offset + delta);
3666
3667 /* Fill in Empty Resource Fork Header. */
3668 init_empty_resource_fork(rsrcforkhdr);
3669
3670 filehdr->entries[1].length = sizeof(rsrcfork_header_t);
3671 writesize = ATTR_BUF_SIZE;
3672 }
3673 filehdr->entries[0].length += delta;
3674 filehdr->entries[1].offset += delta;
3675
3676 /* Fill in Attribute Header. */
3677 attrhdr->magic = ATTR_HDR_MAGIC;
3678 attrhdr->debug_tag = (u_int32_t)va.va_fileid;
3679 attrhdr->total_size = filehdr->entries[1].offset;
3680 attrhdr->data_start = sizeof(attr_header_t);
3681 attrhdr->data_length = 0;
3682 attrhdr->reserved[0] = 0;
3683 attrhdr->reserved[1] = 0;
3684 attrhdr->reserved[2] = 0;
3685 attrhdr->flags = 0;
3686 attrhdr->num_attrs = 0;
3687
3688 /* Push out new header */
3689 uio_reset(auio, 0, UIO_SYSSPACE, UIO_WRITE);
3690 uio_addiov(auio, (uintptr_t)filehdr, writesize);
3691
3692 swap_adhdr(filehdr); /* to big endian */
3693 swap_attrhdr(attrhdr, ainfop); /* to big endian */
3694 error = VNOP_WRITE(xvp, auio, 0, context);
3695 swap_adhdr(filehdr); /* back to native */
3696 /* The attribute header gets swapped below. */
3697 }
3698 }
3699 /*
3700 * Swap and sanity check the extended attribute header and
3701 * entries (if any). The Finder Info content must be big enough
3702 * to include the extended attribute header; if not, we just
3703 * ignore it.
3704 *
3705 * Note that we're passing the offset + length (i.e. the end)
3706 * of the Finder Info instead of rawsize to validate_attrhdr.
3707 * This ensures that all extended attributes lie within the
3708 * Finder Info content according to the AppleDouble entry.
3709 *
3710 * Sets ainfop->attrhdr and ainfop->attr_entry if a valid
3711 * header was found.
3712 */
3713 if (ainfop->finderinfo &&
3714 ainfop->finderinfo == &filehdr->entries[0] &&
3715 ainfop->finderinfo->length >= (sizeof(attr_header_t) - sizeof(apple_double_header_t))) {
3716 attr_header_t *attrhdr = (attr_header_t*)filehdr;
3717
3718 if (ainfop->finderinfo->offset != offsetof(apple_double_header_t, finfo)) {
3719 error = ENOATTR;
3720 goto bail;
3721 }
3722
3723 if ((error = check_and_swap_attrhdr(attrhdr, ainfop)) == 0) {
3724 ainfop->attrhdr = attrhdr; /* valid attribute header */
3725 /* First attr_entry starts immediately following attribute header */
3726 ainfop->attr_entry = (attr_entry_t *)&attrhdr[1];
3727 }
3728 }
3729
3730 error = 0;
3731 bail:
3732 if (auio != NULL) {
3733 uio_free(auio);
3734 }
3735 kfree_data(buffer, iosize);
3736 return error;
3737 }
3738
3739
3740 static int
create_xattrfile(vnode_t xvp,u_int32_t fileid,vfs_context_t context)3741 create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context)
3742 {
3743 attr_header_t *xah;
3744 rsrcfork_header_t *rsrcforkhdr;
3745 void * buffer;
3746 uio_t auio;
3747 int rsrcforksize;
3748 int error;
3749
3750 buffer = kalloc_data(ATTR_BUF_SIZE, Z_WAITOK | Z_ZERO);
3751
3752 xah = (attr_header_t *)buffer;
3753 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
3754 uio_addiov(auio, (uintptr_t)buffer, ATTR_BUF_SIZE);
3755 rsrcforksize = sizeof(rsrcfork_header_t);
3756 rsrcforkhdr = (rsrcfork_header_t *) ((char *)buffer + ATTR_BUF_SIZE - rsrcforksize);
3757
3758 /* Fill in Apple Double Header. */
3759 xah->appledouble.magic = SWAP32(ADH_MAGIC);
3760 xah->appledouble.version = SWAP32(ADH_VERSION);
3761 xah->appledouble.numEntries = SWAP16(2);
3762 xah->appledouble.entries[0].type = SWAP32(AD_FINDERINFO);
3763 xah->appledouble.entries[0].offset = SWAP32(offsetof(apple_double_header_t, finfo));
3764 xah->appledouble.entries[0].length = SWAP32(ATTR_BUF_SIZE - offsetof(apple_double_header_t, finfo) - rsrcforksize);
3765 xah->appledouble.entries[1].type = SWAP32(AD_RESOURCE);
3766 xah->appledouble.entries[1].offset = SWAP32(ATTR_BUF_SIZE - rsrcforksize);
3767 xah->appledouble.entries[1].length = SWAP32(rsrcforksize);
3768 bcopy(ADH_MACOSX, xah->appledouble.filler, sizeof(xah->appledouble.filler));
3769
3770 /* Fill in Attribute Header. */
3771 xah->magic = SWAP32(ATTR_HDR_MAGIC);
3772 xah->debug_tag = SWAP32(fileid);
3773 xah->total_size = SWAP32(ATTR_BUF_SIZE - rsrcforksize);
3774 xah->data_start = SWAP32(sizeof(attr_header_t));
3775
3776 /* Fill in Empty Resource Fork Header. */
3777 init_empty_resource_fork(rsrcforkhdr);
3778
3779 /* Push it out. */
3780 error = VNOP_WRITE(xvp, auio, IO_UNIT, context);
3781
3782 /* Did we write out the full uio? */
3783 if (uio_resid(auio) > 0) {
3784 error = ENOSPC;
3785 }
3786
3787 uio_free(auio);
3788 kfree_data(buffer, ATTR_BUF_SIZE);
3789
3790 return error;
3791 }
3792
3793 static void
init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr)3794 init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr)
3795 {
3796 bzero(rsrcforkhdr, sizeof(rsrcfork_header_t));
3797 rsrcforkhdr->fh_DataOffset = SWAP32(RF_FIRST_RESOURCE);
3798 rsrcforkhdr->fh_MapOffset = SWAP32(RF_FIRST_RESOURCE);
3799 rsrcforkhdr->fh_MapLength = SWAP32(RF_NULL_MAP_LENGTH);
3800 rsrcforkhdr->mh_DataOffset = SWAP32(RF_FIRST_RESOURCE);
3801 rsrcforkhdr->mh_MapOffset = SWAP32(RF_FIRST_RESOURCE);
3802 rsrcforkhdr->mh_MapLength = SWAP32(RF_NULL_MAP_LENGTH);
3803 rsrcforkhdr->mh_Types = SWAP16(RF_NULL_MAP_LENGTH - 2 );
3804 rsrcforkhdr->mh_Names = SWAP16(RF_NULL_MAP_LENGTH);
3805 rsrcforkhdr->typeCount = SWAP16(-1);
3806 bcopy(RF_EMPTY_TAG, rsrcforkhdr->systemData, sizeof(RF_EMPTY_TAG));
3807 }
3808
3809 static void
rel_xattrinfo(attr_info_t * ainfop)3810 rel_xattrinfo(attr_info_t *ainfop)
3811 {
3812 kfree_data_addr(ainfop->filehdr);
3813 bzero(ainfop, sizeof(attr_info_t));
3814 }
3815
3816 static int
write_xattrinfo(attr_info_t * ainfop)3817 write_xattrinfo(attr_info_t *ainfop)
3818 {
3819 uio_t auio;
3820 int error;
3821
3822 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
3823 uio_addiov(auio, (uintptr_t)ainfop->filehdr, ainfop->iosize);
3824
3825 swap_adhdr(ainfop->filehdr);
3826 if (ainfop->attrhdr != NULL) {
3827 swap_attrhdr(ainfop->attrhdr, ainfop);
3828 }
3829
3830 error = VNOP_WRITE(ainfop->filevp, auio, 0, ainfop->context);
3831
3832 swap_adhdr(ainfop->filehdr);
3833 if (ainfop->attrhdr != NULL) {
3834 swap_attrhdr(ainfop->attrhdr, ainfop);
3835 }
3836 uio_free(auio);
3837
3838 return error;
3839 }
3840
3841 #if BYTE_ORDER == LITTLE_ENDIAN
3842 /*
3843 * Endian swap apple double header
3844 */
3845 static void
swap_adhdr(apple_double_header_t * adh)3846 swap_adhdr(apple_double_header_t *adh)
3847 {
3848 int count;
3849 int i;
3850
3851 count = (adh->magic == ADH_MAGIC) ? adh->numEntries : SWAP16(adh->numEntries);
3852
3853 adh->magic = SWAP32(adh->magic);
3854 adh->version = SWAP32(adh->version);
3855 adh->numEntries = SWAP16(adh->numEntries);
3856
3857 for (i = 0; i < count; i++) {
3858 adh->entries[i].type = SWAP32(adh->entries[i].type);
3859 adh->entries[i].offset = SWAP32(adh->entries[i].offset);
3860 adh->entries[i].length = SWAP32(adh->entries[i].length);
3861 }
3862 }
3863
3864 /*
3865 * Endian swap extended attributes header
3866 */
3867 static void
swap_attrhdr(attr_header_t * ah,attr_info_t * info)3868 swap_attrhdr(attr_header_t *ah, attr_info_t* info)
3869 {
3870 attr_entry_t *ae;
3871 int count;
3872 int i;
3873
3874 count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs);
3875
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 ae = (attr_entry_t *)(&ah[1]);
3885 for (i = 0; i < count && ATTR_VALID(ae, *info); i++, ae = ATTR_NEXT(ae)) {
3886 ae->offset = SWAP32(ae->offset);
3887 ae->length = SWAP32(ae->length);
3888 ae->flags = SWAP16(ae->flags);
3889 }
3890 }
3891 #endif
3892
3893 /*
3894 * Validate and swap the attributes header contents, and each attribute's
3895 * attr_entry_t.
3896 *
3897 * Note: Assumes the caller has verified that the Finder Info content is large
3898 * enough to contain the attr_header structure itself. Therefore, we can
3899 * swap the header fields before sanity checking them.
3900 */
3901 static int
check_and_swap_attrhdr(attr_header_t * ah,attr_info_t * ainfop)3902 check_and_swap_attrhdr(attr_header_t *ah, attr_info_t *ainfop)
3903 {
3904 attr_entry_t *ae;
3905 u_int8_t *buf_end;
3906 u_int32_t end;
3907 int count;
3908 int i;
3909 uint32_t total_header_size;
3910 uint32_t total_data_size;
3911
3912 if (ah == NULL) {
3913 return EINVAL;
3914 }
3915
3916 if (SWAP32(ah->magic) != ATTR_HDR_MAGIC) {
3917 return EINVAL;
3918 }
3919
3920 /* Swap the basic header fields */
3921 ah->magic = SWAP32(ah->magic);
3922 ah->debug_tag = SWAP32(ah->debug_tag);
3923 ah->total_size = SWAP32(ah->total_size);
3924 ah->data_start = SWAP32(ah->data_start);
3925 ah->data_length = SWAP32(ah->data_length);
3926 ah->flags = SWAP16(ah->flags);
3927 ah->num_attrs = SWAP16(ah->num_attrs);
3928
3929 /*
3930 * Make sure the total_size fits within the Finder Info area, and the
3931 * extended attribute data area fits within total_size.
3932 */
3933 end = ah->data_start + ah->data_length;
3934 if (ah->total_size > ainfop->finderinfo->offset + ainfop->finderinfo->length ||
3935 ah->data_start < sizeof(attr_header_t) ||
3936 end < ah->data_start ||
3937 end > ah->total_size) {
3938 return EINVAL;
3939 }
3940
3941 /*
3942 * Make sure each of the attr_entry_t's fits within total_size.
3943 */
3944 buf_end = ainfop->rawdata + ah->data_start;
3945 if (buf_end > ainfop->rawdata + ainfop->rawsize) {
3946 return EINVAL;
3947 }
3948 count = ah->num_attrs;
3949 if (count > 256) {
3950 return EINVAL;
3951 }
3952 ae = (attr_entry_t *)(&ah[1]);
3953
3954 total_header_size = sizeof(attr_header_t);
3955 total_data_size = 0;
3956 for (i = 0; i < count; i++) {
3957 /* Make sure the fixed-size part of this attr_entry_t fits. */
3958 if ((u_int8_t *) &ae[1] > buf_end) {
3959 return EINVAL;
3960 }
3961
3962 /* Make sure the variable-length name fits */
3963 if (&ae->name[ae->namelen] > buf_end) {
3964 return EINVAL;
3965 }
3966
3967 /* Make sure that namelen is matching name's real length, namelen included NUL */
3968 if (strnlen((const char *)ae->name, ae->namelen) != ae->namelen - 1) {
3969 return EINVAL;
3970 }
3971
3972 /* Swap the attribute entry fields */
3973 ae->offset = SWAP32(ae->offset);
3974 ae->length = SWAP32(ae->length);
3975 ae->flags = SWAP16(ae->flags);
3976
3977 /* Make sure the attribute content fits and points to the data part */
3978 end = ae->offset + ae->length;
3979 if (end < ae->offset || end > ah->total_size) {
3980 return EINVAL;
3981 }
3982
3983 /* Make sure entry points to data section and not header */
3984 if (ae->offset < ah->data_start || end > ah->data_start + ah->data_length) {
3985 return EINVAL;
3986 }
3987
3988 /* We verified namelen is ok above, so add this entry's size to a total */
3989 if (os_add_overflow(total_header_size, ATTR_ENTRY_LENGTH(ae->namelen), &total_header_size)) {
3990 return EINVAL;
3991 }
3992
3993 /* We verified that entry's length is within data section, so add it to running size total */
3994 if (os_add_overflow(total_data_size, ae->length, &total_data_size)) {
3995 return EINVAL;
3996 }
3997
3998 ae = ATTR_NEXT(ae);
3999 }
4000
4001
4002 /* make sure data_start is actually after all the xattr key entries */
4003 if (ah->data_start < total_header_size) {
4004 return EINVAL;
4005 }
4006
4007 /* make sure all entries' data length add to header's idea of data length */
4008 if (total_data_size != ah->data_length) {
4009 return EINVAL;
4010 }
4011
4012 return 0;
4013 }
4014
4015 //
4016 // "start" & "end" are byte offsets in the file.
4017 // "to" is the byte offset we want to move the
4018 // data to. "to" should be > "start".
4019 //
4020 // we do the copy backwards to avoid problems if
4021 // there's an overlap.
4022 //
4023 static int
shift_data_down(vnode_t xvp,off_t start,size_t len,off_t delta,vfs_context_t context)4024 shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context)
4025 {
4026 int ret, iolen;
4027 size_t chunk, orig_chunk;
4028 char *buff;
4029 off_t pos;
4030 kauth_cred_t ucred = vfs_context_ucred(context);
4031 proc_t p = vfs_context_proc(context);
4032
4033 if (delta == 0 || len == 0) {
4034 return 0;
4035 }
4036
4037 chunk = 4096;
4038 if (len < chunk) {
4039 chunk = len;
4040 }
4041 orig_chunk = chunk;
4042
4043 buff = kalloc_data(chunk, Z_WAITOK);
4044 if (buff == NULL) {
4045 return ENOMEM;
4046 }
4047
4048 for (pos = start + len - chunk; pos >= start; pos -= chunk) {
4049 ret = vn_rdwr(UIO_READ, xvp, buff, (int)chunk, pos, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p);
4050 if (iolen != 0) {
4051 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
4052 pos, ret, chunk, ret);
4053 break;
4054 }
4055
4056 ret = vn_rdwr(UIO_WRITE, xvp, buff, (int)chunk, pos + delta, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p);
4057 if (iolen != 0) {
4058 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
4059 pos + delta, ret, chunk, ret);
4060 break;
4061 }
4062
4063 if ((pos - (off_t)chunk) < start) {
4064 chunk = pos - start;
4065
4066 if (chunk == 0) { // we're all done
4067 break;
4068 }
4069 }
4070 }
4071
4072 kfree_data(buff, orig_chunk);
4073 return 0;
4074 }
4075
4076
4077 static int
shift_data_up(vnode_t xvp,off_t start,size_t len,off_t delta,vfs_context_t context)4078 shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context)
4079 {
4080 int ret, iolen;
4081 size_t chunk, orig_chunk;
4082 char *buff;
4083 off_t pos;
4084 off_t end;
4085 kauth_cred_t ucred = vfs_context_ucred(context);
4086 proc_t p = vfs_context_proc(context);
4087
4088 if (delta == 0 || len == 0) {
4089 return 0;
4090 }
4091
4092 chunk = 4096;
4093 if (len < chunk) {
4094 chunk = len;
4095 }
4096 orig_chunk = chunk;
4097 end = start + len;
4098
4099 buff = kalloc_data(chunk, Z_WAITOK);
4100 if (buff == NULL) {
4101 return ENOMEM;
4102 }
4103
4104 for (pos = start; pos < end; pos += chunk) {
4105 ret = vn_rdwr(UIO_READ, xvp, buff, (int)chunk, pos, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p);
4106 if (iolen != 0) {
4107 printf("xattr:shift_data: error reading data @ %lld (read %d of %lu) (%d)\n",
4108 pos, ret, chunk, ret);
4109 break;
4110 }
4111
4112 ret = vn_rdwr(UIO_WRITE, xvp, buff, (int)chunk, pos - delta, UIO_SYSSPACE, IO_NODELOCKED | IO_NOAUTH, ucred, &iolen, p);
4113 if (iolen != 0) {
4114 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %lu) (%d)\n",
4115 pos + delta, ret, chunk, ret);
4116 break;
4117 }
4118
4119 if ((pos + (off_t)chunk) > end) {
4120 chunk = end - pos;
4121
4122 if (chunk == 0) { // we're all done
4123 break;
4124 }
4125 }
4126 }
4127
4128 kfree_data(buff, orig_chunk);
4129 return 0;
4130 }
4131
4132 static int
make_xattrfile_port(struct fileglob * fg,ipc_port_t * portp)4133 make_xattrfile_port(struct fileglob *fg, ipc_port_t *portp)
4134 {
4135 /*
4136 * This is essentially a stripped-down copy of
4137 * sys_fileport_makeport().
4138 */
4139 ipc_port_t fileport;
4140 int error = 0;
4141
4142 /* Dropped when port is deallocated. */
4143 fg_ref(FG_NOPROC, fg);
4144
4145 fileport = fileport_alloc(fg);
4146 if (fileport == IPC_PORT_NULL) {
4147 fg_drop_live(fg);
4148 error = EIO;
4149 } else {
4150 /* Tag the fileglob for debugging purposes */
4151 lck_mtx_lock_spin(&fg->fg_lock);
4152 fg->fg_lflags |= FG_PORTMADE;
4153 lck_mtx_unlock(&fg->fg_lock);
4154 }
4155
4156 /*
4157 * The Mig defs for doubleagentd declare the fileport argument
4158 * as move-send. If we ever decide we want to cache the fileport
4159 * here in the kernel, we will either need to change the Mig
4160 * defs back to the default mach_port_t (which is a copy-send)
4161 * or explicitly ipc_port_copy_send_any() the right before
4162 * sending it in the Mig stub.
4163 */
4164
4165 *portp = fileport;
4166 return error;
4167 }
4168
4169 #else /* CONFIG_APPLEDOUBLE */
4170
4171
4172 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)4173 default_getxattr(__unused vnode_t vp, __unused const char *name,
4174 __unused uio_t uio, __unused size_t *size, __unused int options,
4175 __unused vfs_context_t context)
4176 {
4177 return ENOTSUP;
4178 }
4179
4180 static int
default_setxattr(__unused vnode_t vp,__unused const char * name,__unused uio_t uio,__unused int options,__unused vfs_context_t context)4181 default_setxattr(__unused vnode_t vp, __unused const char *name,
4182 __unused uio_t uio, __unused int options, __unused vfs_context_t context)
4183 {
4184 return ENOTSUP;
4185 }
4186
4187 static int
default_listxattr(__unused vnode_t vp,__unused uio_t uio,__unused size_t * size,__unused int options,__unused vfs_context_t context)4188 default_listxattr(__unused vnode_t vp,
4189 __unused uio_t uio, __unused size_t *size, __unused int options,
4190 __unused vfs_context_t context)
4191 {
4192 return ENOTSUP;
4193 }
4194
4195 static int
default_removexattr(__unused vnode_t vp,__unused const char * name,__unused int options,__unused vfs_context_t context)4196 default_removexattr(__unused vnode_t vp, __unused const char *name,
4197 __unused int options, __unused vfs_context_t context)
4198 {
4199 return ENOTSUP;
4200 }
4201
4202 #endif /* CONFIG_APPLEDOUBLE */
4203