]> git.saurik.com Git - apple/xnu.git/blame - bsd/vfs/vfs_xattr.c
xnu-1504.9.17.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_xattr.c
CommitLineData
91447636 1/*
cf7d32b8 2 * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
91447636 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
91447636 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
91447636 27 */
2d21ac55
A
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
91447636
A
35#include <sys/param.h>
36
37#include <sys/fcntl.h>
38#include <sys/fsevents.h>
39#include <sys/kernel.h>
40#include <sys/kauth.h>
41#include <sys/malloc.h>
2d21ac55 42#include <sys/mount_internal.h>
91447636
A
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>
91447636
A
50#include <sys/xattr.h>
51
4452a7af 52#include <libkern/OSByteOrder.h>
91447636
A
53#include <vm/vm_kern.h>
54
2d21ac55
A
55#if CONFIG_MACF
56#include <security/mac_framework.h>
57#endif
58
59
60#if NAMEDSTREAMS
61
91447636 62/*
b0d623f7 63 * We use %p to prevent loss of precision for pointers on varying architectures.
91447636 64 */
2d21ac55 65#define MAKE_SHADOW_NAME(VP, NAME) \
b0d623f7 66 snprintf((NAME), sizeof((NAME)), ".vfs_rsrc_stream_%p%08x%p", (void*)(VP), (VP)->v_id, (VP)->v_data);
2d21ac55
A
67
68static vnode_t shadow_dvp; /* tmp directory to hold stream shadow files */
69static int shadow_vid;
70static int shadow_sequence;
71
72
73static int default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context);
74
75static int default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context);
91447636 76
2d21ac55 77static int default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context);
91447636 78
2d21ac55
A
79static int getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize, int *creator, vfs_context_t context);
80
81static int get_shadow_dir(vnode_t *sdvpp, vfs_context_t context);
82
83#endif
84
85
86/*
87 * Default xattr support routines.
88 */
91447636
A
89
90static int default_listxattr(vnode_t vp, uio_t uio, size_t *size, int options,
91 vfs_context_t context);
92
93
94
95/*
96 * Retrieve the data of an extended attribute.
97 */
98int
99vn_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size,
100 int options, vfs_context_t context)
101{
102 int error;
103
104 if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) {
105 return (EPERM);
106 }
2d21ac55
A
107#if NAMEDSTREAMS
108 /* getxattr calls are not allowed for streams. */
109 if (vp->v_flag & VISNAMEDSTREAM) {
110 error = EPERM;
91447636 111 goto out;
2d21ac55
A
112 }
113#endif
114 /*
115 * Non-kernel request need extra checks performed.
116 *
117 * The XATTR_NOSECURITY flag implies a kernel request.
118 */
119 if (!(options & XATTR_NOSECURITY)) {
120#if CONFIG_MACF
121 error = mac_vnode_check_getextattr(context, vp, name, uio);
122 if (error)
123 goto out;
124#endif /* MAC */
125 if ((error = xattr_validatename(name))) {
126 goto out;
127 }
128 if ((error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context))) {
129 goto out;
130 }
131 /* The offset can only be non-zero for resource forks. */
132 if (uio != NULL && uio_offset(uio) != 0 &&
133 bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
134 error = EINVAL;
135 goto out;
136 }
137 }
91447636
A
138
139 /* The offset can only be non-zero for resource forks. */
140 if (uio != NULL && uio_offset(uio) != 0 &&
141 bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
142 error = EINVAL;
143 goto out;
144 }
145
146 error = VNOP_GETXATTR(vp, name, uio, size, options, context);
2d21ac55 147 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
91447636
A
148 /*
149 * A filesystem may keep some EAs natively and return ENOTSUP for others.
150 * SMB returns ENOTSUP for finderinfo and resource forks.
151 */
152 error = default_getxattr(vp, name, uio, size, options, context);
153 }
154out:
155 return (error);
156}
157
158/*
159 * Set the data of an extended attribute.
160 */
161int
162vn_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context)
163{
164 int error;
165
166 if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) {
167 return (EPERM);
168 }
2d21ac55
A
169#if NAMEDSTREAMS
170 /* setxattr calls are not allowed for streams. */
171 if (vp->v_flag & VISNAMEDSTREAM) {
172 error = EPERM;
173 goto out;
174 }
175#endif
91447636
A
176 if ((options & (XATTR_REPLACE|XATTR_CREATE)) == (XATTR_REPLACE|XATTR_CREATE)) {
177 return (EINVAL);
178 }
179 if ((error = xattr_validatename(name))) {
180 return (error);
181 }
2d21ac55
A
182 if (!(options & XATTR_NOSECURITY)) {
183#if CONFIG_MACF
184 error = mac_vnode_check_setextattr(context, vp, name, uio);
185 if (error)
186 goto out;
187#endif /* MAC */
188 error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context);
189 if (error)
190 goto out;
191 }
91447636
A
192 /* The offset can only be non-zero for resource forks. */
193 if (uio_offset(uio) != 0 &&
194 bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0 ) {
195 error = EINVAL;
196 goto out;
197 }
198
199 error = VNOP_SETXATTR(vp, name, uio, options, context);
200#ifdef DUAL_EAS
201 /*
202 * An EJUSTRETURN is from a filesystem which keeps this xattr
203 * natively as well as in a dot-underscore file. In this case the
204 * EJUSTRETURN means the filesytem has done nothing, but identifies the
205 * EA as one which may be represented natively and/or in a DU, and
206 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
207 * in vn_setxattr can we do the getxattrs needed to ascertain whether
208 * the XATTR_{CREATE,REPLACE} should yield an error.
209 */
210 if (error == EJUSTRETURN) {
211 int native = 0, dufile = 0;
212 size_t sz; /* not used */
213
214 native = VNOP_GETXATTR(vp, name, NULL, &sz, 0, context) ? 0 : 1;
215 dufile = default_getxattr(vp, name, NULL, &sz, 0, context) ? 0 : 1;
216 if (options & XATTR_CREATE && (native || dufile)) {
217 error = EEXIST;
218 goto out;
219 }
220 if (options & XATTR_REPLACE && !(native || dufile)) {
221 error = ENOATTR;
222 goto out;
223 }
224 /*
225 * Having determined no CREATE/REPLACE error should result, we
226 * zero those bits, so both backing stores get written to.
227 */
228 options &= ~(XATTR_CREATE | XATTR_REPLACE);
229 error = VNOP_SETXATTR(vp, name, uio, options, context);
230 /* the mainline path here is to have error==ENOTSUP ... */
231 }
232#endif /* DUAL_EAS */
2d21ac55 233 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
91447636
A
234 /*
235 * A filesystem may keep some EAs natively and return ENOTSUP for others.
236 * SMB returns ENOTSUP for finderinfo and resource forks.
237 */
238 error = default_setxattr(vp, name, uio, options, context);
239 }
2d21ac55
A
240#if CONFIG_MACF
241 if ((error == 0) && !(options & XATTR_NOSECURITY) &&
242 (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL))
243 mac_vnode_label_update_extattr(vnode_mount(vp), vp, name);
244#endif
91447636
A
245out:
246 return (error);
247}
248
249/*
250 * Remove an extended attribute.
251 */
252int
253vn_removexattr(vnode_t vp, const char * name, int options, vfs_context_t context)
254{
255 int error;
256
257 if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) {
258 return (EPERM);
259 }
2d21ac55
A
260#if NAMEDSTREAMS
261 /* removexattr calls are not allowed for streams. */
262 if (vp->v_flag & VISNAMEDSTREAM) {
263 error = EPERM;
264 goto out;
265 }
266#endif
91447636
A
267 if ((error = xattr_validatename(name))) {
268 return (error);
269 }
2d21ac55
A
270 if (!(options & XATTR_NOSECURITY)) {
271#if CONFIG_MACF
272 error = mac_vnode_check_deleteextattr(context, vp, name);
273 if (error)
274 goto out;
275#endif /* MAC */
276 error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context);
277 if (error)
278 goto out;
279 }
91447636 280 error = VNOP_REMOVEXATTR(vp, name, options, context);
2d21ac55 281 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
91447636
A
282 /*
283 * A filesystem may keep some EAs natively and return ENOTSUP for others.
284 * SMB returns ENOTSUP for finderinfo and resource forks.
285 */
286 error = default_removexattr(vp, name, options, context);
287#ifdef DUAL_EAS
288 } else if (error == EJUSTRETURN) {
289 /*
290 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
291 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
292 * a native xattr, so failure to find it in a DU file during
293 * default_removexattr should not be considered an error.
294 */
295 error = default_removexattr(vp, name, options, context);
296 if (error == ENOATTR)
297 error = 0;
298#endif /* DUAL_EAS */
299 }
2d21ac55
A
300#if CONFIG_MACF
301 if ((error == 0) && !(options & XATTR_NOSECURITY) &&
302 (vfs_flags(vnode_mount(vp)) & MNT_MULTILABEL))
303 mac_vnode_label_update_extattr(vnode_mount(vp), vp, name);
304#endif
91447636
A
305out:
306 return (error);
307}
308
309/*
310 * Retrieve the list of extended attribute names.
311 */
312int
313vn_listxattr(vnode_t vp, uio_t uio, size_t *size, int options, vfs_context_t context)
314{
315 int error;
316
317 if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) {
318 return (EPERM);
319 }
2d21ac55
A
320#if NAMEDSTREAMS
321 /* listxattr calls are not allowed for streams. */
322 if (vp->v_flag & VISNAMEDSTREAM) {
323 return (EPERM);
324 }
325#endif
326
327 if (!(options & XATTR_NOSECURITY)) {
328#if CONFIG_MACF
329 error = mac_vnode_check_listextattr(context, vp);
330 if (error)
331 goto out;
332#endif /* MAC */
333
334 error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context);
335 if (error)
336 goto out;
337 }
91447636
A
338
339 error = VNOP_LISTXATTR(vp, uio, size, options, context);
2d21ac55 340 if (error == ENOTSUP && !(options & XATTR_NODEFAULT)) {
91447636
A
341 /*
342 * A filesystem may keep some but not all EAs natively, in which case
343 * the native EA names will have been uiomove-d out (or *size updated)
344 * and the default_listxattr here will finish the job. Note SMB takes
345 * advantage of this for its finder-info and resource forks.
346 */
347 error = default_listxattr(vp, uio, size, options, context);
348 }
349out:
350 return (error);
351}
352
353int
354xattr_validatename(const char *name)
355{
356 int namelen;
357
358 if (name == NULL || name[0] == '\0') {
359 return (EINVAL);
360 }
6601e61a
A
361 namelen = strnlen(name, XATTR_MAXNAMELEN);
362 if (name[namelen] != '\0')
91447636 363 return (ENAMETOOLONG);
2d21ac55
A
364
365 if (utf8_validatestr((const unsigned char *)name, namelen) != 0)
91447636 366 return (EINVAL);
6601e61a 367
91447636
A
368 return (0);
369}
370
371
372/*
373 * Determine whether an EA is a protected system attribute.
374 */
375int
376xattr_protected(const char *attrname)
377{
378 return(!strncmp(attrname, "com.apple.system.", 17));
379}
380
381
2d21ac55
A
382#if NAMEDSTREAMS
383/*
384 * Obtain a named stream from vnode vp.
385 */
386errno_t
387vnode_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, int flags, vfs_context_t context)
388{
389 int error;
390
391 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS)
392 error = VNOP_GETNAMEDSTREAM(vp, svpp, name, op, flags, context);
393 else
394 error = default_getnamedstream(vp, svpp, name, op, context);
395
396 if (error == 0) {
c910b4d9 397 uint32_t streamflags = VISNAMEDSTREAM;
2d21ac55 398 vnode_t svp = *svpp;
c910b4d9 399
b0d623f7 400 if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) {
c910b4d9 401 streamflags |= VISSHADOW;
b0d623f7
A
402 }
403
2d21ac55 404 /* Tag the vnode. */
c910b4d9
A
405 vnode_lock_spin(svp);
406 svp->v_flag |= streamflags;
2d21ac55 407 vnode_unlock(svp);
b0d623f7
A
408
409 /* Tag the parent so we know to flush credentials for streams on setattr */
410 vnode_lock_spin(vp);
411 vp->v_lflag |= VL_HASSTREAMS;
412 vnode_unlock(vp);
413
414 /* Make the file it's parent.
415 * Note: This parent link helps us distinguish vnodes for
416 * shadow stream files from vnodes for resource fork on file
417 * systems that support namedstream natively (both have
418 * VISNAMEDSTREAM set) by allowing access to mount structure
419 * for checking MNTK_NAMED_STREAMS bit at many places in the
420 * code.
cf7d32b8 421 */
2d21ac55
A
422 vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_PARENT);
423 }
424
425 return (error);
426}
427
428/*
429 * Make a named stream for vnode vp.
430 */
431errno_t
432vnode_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, int flags, vfs_context_t context)
433{
434 int error;
435
436 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS)
437 error = VNOP_MAKENAMEDSTREAM(vp, svpp, name, flags, context);
438 else
439 error = default_makenamedstream(vp, svpp, name, context);
440
441 if (error == 0) {
c910b4d9 442 uint32_t streamflags = VISNAMEDSTREAM;
2d21ac55
A
443 vnode_t svp = *svpp;
444
445 /* Tag the vnode. */
c910b4d9
A
446 if ((vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0) {
447 streamflags |= VISSHADOW;
448 }
b0d623f7 449
c910b4d9
A
450 /* Tag the vnode. */
451 vnode_lock_spin(svp);
452 svp->v_flag |= streamflags;
453 vnode_unlock(svp);
454
b0d623f7
A
455 /* Tag the parent so we know to flush credentials for streams on setattr */
456 vnode_lock_spin(vp);
457 vp->v_lflag |= VL_HASSTREAMS;
458 vnode_unlock(vp);
459
460 /* Make the file it's parent.
461 * Note: This parent link helps us distinguish vnodes for
462 * shadow stream files from vnodes for resource fork on file
463 * systems that support namedstream natively (both have
464 * VISNAMEDSTREAM set) by allowing access to mount structure
465 * for checking MNTK_NAMED_STREAMS bit at many places in the
466 * code.
cf7d32b8 467 */
2d21ac55
A
468 vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_PARENT);
469 }
470 return (error);
471}
472
473/*
474 * Remove a named stream from vnode vp.
475 */
476errno_t
477vnode_removenamedstream(vnode_t vp, vnode_t svp, const char *name, int flags, vfs_context_t context)
478{
479 int error;
480
481 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS)
482 error = VNOP_REMOVENAMEDSTREAM(vp, svp, name, flags, context);
483 else
484 error = default_removenamedstream(vp, name, context);
485
486 return (error);
487}
488
489#define NS_IOBUFSIZE (128 * 1024)
490
491/*
492 * Release a named stream shadow file.
cf7d32b8
A
493 *
494 * Note: This function is called from two places where we do not need
495 * to check if the vnode has any references held before deleting the
496 * shadow file. Once from vclean() when the vnode is being reclaimed
497 * and we do not hold any references on the vnode. Second time from
498 * default_getnamedstream() when we get an error during shadow stream
499 * file initialization so that other processes who are waiting for the
500 * shadow stream file initialization by the creator will get opportunity
501 * to create and initialize the file again.
2d21ac55
A
502 */
503errno_t
504vnode_relenamedstream(vnode_t vp, vnode_t svp, vfs_context_t context)
505{
506 vnode_t dvp;
507 struct componentname cn;
b0d623f7 508 char tmpname[80];
2d21ac55
A
509 errno_t err;
510
2d21ac55
A
511 cache_purge(svp);
512
513 vnode_lock(svp);
514 MAKE_SHADOW_NAME(vp, tmpname);
515 vnode_unlock(svp);
516
517 cn.cn_nameiop = DELETE;
518 cn.cn_flags = ISLASTCN;
519 cn.cn_context = context;
520 cn.cn_pnbuf = tmpname;
521 cn.cn_pnlen = sizeof(tmpname);
522 cn.cn_nameptr = cn.cn_pnbuf;
523 cn.cn_namelen = strlen(tmpname);
524
525 /* Obtain the vnode for the shadow files directory. */
526 err = get_shadow_dir(&dvp, context);
527 if (err != 0) {
528 return err;
529 }
b0d623f7 530
cf7d32b8 531 (void) VNOP_REMOVE(dvp, svp, &cn, 0, context);
2d21ac55
A
532 vnode_put(dvp);
533
534 return (0);
535}
536
537/*
538 * Flush a named stream shadow file.
539 */
540errno_t
541vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context)
542{
543 struct vnode_attr va;
544 uio_t auio = NULL;
545 caddr_t bufptr = NULL;
546 size_t bufsize = 0;
547 size_t offset;
548 size_t iosize;
549 size_t datasize;
550 int error;
551
552 VATTR_INIT(&va);
553 VATTR_WANTED(&va, va_data_size);
554 if (VNOP_GETATTR(svp, &va, context) != 0 ||
555 !VATTR_IS_SUPPORTED(&va, va_data_size)) {
556 return (0);
557 }
558 datasize = va.va_data_size;
559 if ((datasize == 0)) {
560 (void) default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
561 return (0);
562 }
563
564 iosize = bufsize = MIN(datasize, NS_IOBUFSIZE);
565 if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize)) {
566 return (ENOMEM);
567 }
b0d623f7 568 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
2d21ac55
A
569 offset = 0;
570
571 /*
572 * Copy the shadow stream file data into the resource fork.
573 */
574 error = VNOP_OPEN(svp, 0, context);
575 if (error) {
576 printf("vnode_flushnamedstream: err %d opening file\n", error);
577 goto out;
578 }
579 while (offset < datasize) {
580 iosize = MIN(datasize - offset, iosize);
581
b0d623f7 582 uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ);
2d21ac55
A
583 uio_addiov(auio, (uintptr_t)bufptr, iosize);
584 error = VNOP_READ(svp, auio, 0, context);
585 if (error) {
586 break;
587 }
588 /* Since there's no truncate xattr we must remove the resource fork. */
589 if (offset == 0) {
590 error = default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
591 if ((error != 0) && (error != ENOATTR)) {
592 break;
593 }
594 }
b0d623f7 595 uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE);
2d21ac55
A
596 uio_addiov(auio, (uintptr_t)bufptr, iosize);
597 error = vn_setxattr(vp, XATTR_RESOURCEFORK_NAME, auio, XATTR_NOSECURITY, context);
598 if (error) {
599 break;
600 }
601 offset += iosize;
602 }
603 (void) VNOP_CLOSE(svp, 0, context);
604out:
605 if (bufptr) {
606 kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize);
607 }
608 if (auio) {
609 uio_free(auio);
610 }
611 return (error);
612}
613
614
615static int
616getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize,
617 int *creator, vfs_context_t context)
618{
619 vnode_t dvp = NULLVP;
620 vnode_t svp = NULLVP;
621 struct componentname cn;
622 struct vnode_attr va;
b0d623f7 623 char tmpname[80];
2d21ac55
A
624 size_t datasize = 0;
625 int error = 0;
626
627 *creator = 0;
628
629 /* Establish a unique file name. */
630 MAKE_SHADOW_NAME(vp, tmpname);
631 bzero(&cn, sizeof(cn));
632 cn.cn_nameiop = LOOKUP;
633 cn.cn_flags = ISLASTCN;
634 cn.cn_context = context;
635 cn.cn_pnbuf = tmpname;
636 cn.cn_pnlen = sizeof(tmpname);
637 cn.cn_nameptr = cn.cn_pnbuf;
638 cn.cn_namelen = strlen(tmpname);
639
640 /* Pick up uid, gid, mode and date from original file. */
641 VATTR_INIT(&va);
642 VATTR_WANTED(&va, va_uid);
643 VATTR_WANTED(&va, va_gid);
644 VATTR_WANTED(&va, va_mode);
645 VATTR_WANTED(&va, va_create_time);
646 VATTR_WANTED(&va, va_modify_time);
647 if (VNOP_GETATTR(vp, &va, context) != 0 ||
648 !VATTR_IS_SUPPORTED(&va, va_uid) ||
649 !VATTR_IS_SUPPORTED(&va, va_gid) ||
650 !VATTR_IS_SUPPORTED(&va, va_mode)) {
651 va.va_uid = KAUTH_UID_NONE;
652 va.va_gid = KAUTH_GID_NONE;
653 va.va_mode = S_IRUSR | S_IWUSR;
654 }
655 va.va_vaflags = VA_EXCLUSIVE;
656 VATTR_SET(&va, va_type, VREG);
657 /* We no longer change the access, but we still hide it. */
658 VATTR_SET(&va, va_flags, UF_HIDDEN);
659
660 /* Obtain the vnode for the shadow files directory. */
661 if (get_shadow_dir(&dvp, context) != 0) {
662 error = ENOTDIR;
663 goto out;
664 }
665 if (!makestream) {
666 /* See if someone else already has it open. */
667 if (VNOP_LOOKUP(dvp, &svp, &cn, context) == 0) {
668 /* Double check existence by asking for size. */
669 VATTR_INIT(&va);
670 VATTR_WANTED(&va, va_data_size);
671 if (VNOP_GETATTR(svp, &va, context) == 0 &&
672 VATTR_IS_SUPPORTED(&va, va_data_size)) {
673 goto out; /* OK to use. */
674 }
675 }
676
677 /* Otherwise make sure the resource fork data exists. */
678 error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &datasize,
679 XATTR_NOSECURITY, context);
680 /*
681 * To maintain binary compatibility with legacy Carbon
682 * emulated resource fork support, if the resource fork
683 * doesn't exist but the Finder Info does, then act as
684 * if an empty resource fork is present (see 4724359).
685 */
686 if ((error == ENOATTR) &&
687 (vn_getxattr(vp, XATTR_FINDERINFO_NAME, NULL, &datasize,
688 XATTR_NOSECURITY, context) == 0)) {
689 datasize = 0;
690 error = 0;
691 } else {
692 if (error) {
693 goto out;
694 }
695
696 /* If the resource fork exists, its size is expected to be non-zero. */
697 if (datasize == 0) {
698 error = ENOATTR;
699 goto out;
700 }
701 }
702 }
703 /* Create the shadow stream file. */
704 error = VNOP_CREATE(dvp, &svp, &cn, &va, context);
705 if (error == 0) {
7e4a7d39 706 vnode_recycle(svp);
2d21ac55
A
707 *creator = 1;
708 } else if ((error == EEXIST) && !makestream) {
709 error = VNOP_LOOKUP(dvp, &svp, &cn, context);
710 }
711out:
712 if (dvp) {
713 vnode_put(dvp);
714 }
715 if (error) {
716 /* On errors, clean up shadow stream file. */
717 if (svp) {
718 vnode_put(svp);
719 svp = NULLVP;
720 }
721 }
722 *svpp = svp;
723 if (rsrcsize) {
724 *rsrcsize = datasize;
725 }
726 return (error);
727}
728
729
730static int
731default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context)
732{
733 vnode_t svp = NULLVP;
734 uio_t auio = NULL;
735 caddr_t bufptr = NULL;
736 size_t bufsize = 0;
737 size_t datasize = 0;
738 int creator;
739 int error;
740
741 /*
742 * Only the "com.apple.ResourceFork" stream is supported here.
743 */
744 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
745 *svpp = NULLVP;
746 return (ENOATTR);
747 }
748retry:
749 /*
750 * Obtain a shadow file for the resource fork I/O.
751 */
752 error = getshadowfile(vp, &svp, 0, &datasize, &creator, context);
753 if (error) {
754 *svpp = NULLVP;
755 return (error);
756 }
757
758 /*
759 * The creator of the shadow file provides its file data,
b0d623f7
A
760 * all other threads should wait until its ready. In order to
761 * prevent a deadlock during error codepaths, we need to check if the
762 * vnode is being created, or if it has failed out. Regardless of success or
763 * failure, we set the VISSHADOW bit on the vnode, so we check that
764 * if the vnode's flags don't have VISNAMEDSTREAM set. If it doesn't,
765 * then we can infer the creator isn't done yet. If it's there, but
766 * VISNAMEDSTREAM is not set, then we can infer it errored out and we should
767 * try again.
2d21ac55
A
768 */
769 if (!creator) {
770 vnode_lock(svp);
771 if (svp->v_flag & VISNAMEDSTREAM) {
772 /* data is ready, go use it */
773 vnode_unlock(svp);
774 goto out;
775 } else {
b0d623f7
A
776 /* It's not ready, wait for it (sleep using v_parent as channel) */
777 if ((svp->v_flag & VISSHADOW)) {
778 /*
779 * No VISNAMEDSTREAM, but we did see VISSHADOW, indicating that the other
780 * thread is done with this vnode. Just unlock the vnode and try again
781 */
782 vnode_unlock(svp);
783 }
784 else {
785 /* Otherwise, sleep if the shadow file is not created yet */
786 msleep((caddr_t)&svp->v_parent, &svp->v_lock, PINOD | PDROP,
787 "getnamedstream", NULL);
788 }
2d21ac55
A
789 vnode_put(svp);
790 svp = NULLVP;
791 goto retry;
792 }
793 }
794
795 /*
796 * Copy the real resource fork data into shadow stream file.
797 */
798 if (op == NS_OPEN && datasize != 0) {
799 size_t offset;
800 size_t iosize;
801
802 iosize = bufsize = MIN(datasize, NS_IOBUFSIZE);
803 if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize)) {
804 error = ENOMEM;
805 goto out;
806 }
807
b0d623f7 808 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ);
2d21ac55
A
809 offset = 0;
810
811 error = VNOP_OPEN(svp, 0, context);
812 if (error) {
813 goto out;
814 }
815 while (offset < datasize) {
816 size_t tmpsize;
817
818 iosize = MIN(datasize - offset, iosize);
819
b0d623f7 820 uio_reset(auio, offset, UIO_SYSSPACE, UIO_READ);
2d21ac55
A
821 uio_addiov(auio, (uintptr_t)bufptr, iosize);
822 error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, auio, &tmpsize,
823 XATTR_NOSECURITY, context);
824 if (error) {
825 break;
826 }
827
b0d623f7 828 uio_reset(auio, offset, UIO_SYSSPACE, UIO_WRITE);
2d21ac55
A
829 uio_addiov(auio, (uintptr_t)bufptr, iosize);
830 error = VNOP_WRITE(svp, auio, 0, context);
831 if (error) {
832 break;
833 }
834 offset += iosize;
835 }
836 (void) VNOP_CLOSE(svp, 0, context);
837 }
838out:
839 /* Wake up anyone waiting for svp file content */
840 if (creator) {
841 if (error == 0) {
842 vnode_lock(svp);
b0d623f7
A
843 /* VISSHADOW would be set later on anyway, so we set it now */
844 svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW);
2d21ac55
A
845 wakeup((caddr_t)&svp->v_parent);
846 vnode_unlock(svp);
847 } else {
b0d623f7
A
848 /* On post create errors, get rid of the shadow file. This
849 * way if there is another process waiting for initialization
850 * of the shadowfile by the current process will wake up and
851 * retry by creating and initializing the shadow file again.
852 * Also add the VISSHADOW bit here to indicate we're done operating
853 * on this vnode.
cf7d32b8 854 */
b0d623f7
A
855 (void)vnode_relenamedstream(vp, svp, context);
856 vnode_lock (svp);
857 svp->v_flag |= VISSHADOW;
2d21ac55 858 wakeup((caddr_t)&svp->v_parent);
b0d623f7 859 vnode_unlock(svp);
2d21ac55
A
860 }
861 }
862
863 if (bufptr) {
864 kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize);
865 }
866 if (auio) {
867 uio_free(auio);
868 }
869 if (error) {
870 /* On errors, clean up shadow stream file. */
871 if (svp) {
872 vnode_put(svp);
873 svp = NULLVP;
874 }
875 }
876 *svpp = svp;
877 return (error);
878}
879
880static int
881default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context)
882{
883 int creator;
884 int error;
885
886 /*
887 * Only the "com.apple.ResourceFork" stream is supported here.
888 */
889 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
890 *svpp = NULLVP;
891 return (ENOATTR);
892 }
893 error = getshadowfile(vp, svpp, 1, NULL, &creator, context);
894
895 /*
896 * Wake up any waiters over in default_getnamedstream().
897 */
898 if ((error == 0) && (*svpp != NULL) && creator) {
899 vnode_t svp = *svpp;
900
901 vnode_lock(svp);
b0d623f7
A
902 /* If we're the creator, mark it as a named stream */
903 svp->v_flag |= (VISNAMEDSTREAM | VISSHADOW);
2d21ac55
A
904 /* Wakeup any waiters on the v_parent channel */
905 wakeup((caddr_t)&svp->v_parent);
906 vnode_unlock(svp);
b0d623f7 907
2d21ac55 908 }
b0d623f7 909
2d21ac55
A
910 return (error);
911}
912
913static int
914default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context)
915{
916 /*
917 * Only the "com.apple.ResourceFork" stream is supported here.
918 */
919 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
920 return (ENOATTR);
921 }
922 /*
923 * XXX - what about other opened instances?
924 */
925 return default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
926}
927
928static int
929get_shadow_dir(vnode_t *sdvpp, vfs_context_t context)
930{
931 vnode_t dvp = NULLVP;
932 vnode_t sdvp = NULLVP;
933 struct componentname cn;
934 struct vnode_attr va;
b0d623f7 935 char tmpname[80];
2d21ac55
A
936 uint32_t tmp_fsid;
937 int error;
938
939 /* Check if we've already created it. */
940 if (shadow_dvp != NULLVP) {
941 if ((error = vnode_getwithvid(shadow_dvp, shadow_vid))) {
942 shadow_dvp = NULLVP;
943 } else {
944 *sdvpp = shadow_dvp;
945 return (0);
946 }
947 }
948
0b4c1975
A
949 /* Obtain the vnode for "/var/run" directory. */
950 if (vnode_lookup("/var/run", 0, &dvp, context) != 0) {
2d21ac55
A
951 error = ENOTSUP;
952 goto out;
953 }
954
955 /* Create the shadow stream directory. */
b0d623f7
A
956 snprintf(tmpname, sizeof(tmpname), ".vfs_rsrc_streams_%p%x",
957 (void*)rootvnode, shadow_sequence);
2d21ac55
A
958 bzero(&cn, sizeof(cn));
959 cn.cn_nameiop = LOOKUP;
960 cn.cn_flags = ISLASTCN;
961 cn.cn_context = context;
962 cn.cn_pnbuf = tmpname;
963 cn.cn_pnlen = sizeof(tmpname);
964 cn.cn_nameptr = cn.cn_pnbuf;
965 cn.cn_namelen = strlen(tmpname);
966
967 /*
968 * owned by root, only readable by root, hidden
969 */
970 VATTR_INIT(&va);
971 VATTR_SET(&va, va_uid, 0);
972 VATTR_SET(&va, va_gid, 0);
973 VATTR_SET(&va, va_mode, S_IRUSR | S_IXUSR);
974 VATTR_SET(&va, va_type, VDIR);
975 VATTR_SET(&va, va_flags, UF_HIDDEN);
976 va.va_vaflags = VA_EXCLUSIVE;
977
978 error = VNOP_MKDIR(dvp, &sdvp, &cn, &va, context);
979
980 /*
981 * There can be only one winner for an exclusive create.
982 */
983 if (error == 0) {
984 /* Take a long term ref to keep this dir around. */
985 error = vnode_ref(sdvp);
986 if (error == 0) {
987 shadow_dvp = sdvp;
988 shadow_vid = sdvp->v_id;
989 }
990 } else if (error == EEXIST) {
991 /* loser has to look up directory */
992 error = VNOP_LOOKUP(dvp, &sdvp, &cn, context);
993 if (error == 0) {
994 /* Make sure its in fact a directory */
995 if (sdvp->v_type != VDIR) {
996 goto baddir;
997 }
0b4c1975 998 /* Obtain the fsid for /var/run directory */
2d21ac55
A
999 VATTR_INIT(&va);
1000 VATTR_WANTED(&va, va_fsid);
1001 if (VNOP_GETATTR(dvp, &va, context) != 0 ||
1002 !VATTR_IS_SUPPORTED(&va, va_fsid)) {
1003 goto baddir;
1004 }
1005 tmp_fsid = va.va_fsid;
1006
1007 VATTR_INIT(&va);
1008 VATTR_WANTED(&va, va_uid);
1009 VATTR_WANTED(&va, va_gid);
1010 VATTR_WANTED(&va, va_mode);
1011 VATTR_WANTED(&va, va_fsid);
1012 VATTR_WANTED(&va, va_dirlinkcount);
1013 VATTR_WANTED(&va, va_acl);
1014 /* Provide defaults for attrs that may not be supported */
1015 va.va_dirlinkcount = 1;
1016 va.va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
1017
1018 if (VNOP_GETATTR(sdvp, &va, context) != 0 ||
1019 !VATTR_IS_SUPPORTED(&va, va_uid) ||
1020 !VATTR_IS_SUPPORTED(&va, va_gid) ||
1021 !VATTR_IS_SUPPORTED(&va, va_mode) ||
1022 !VATTR_IS_SUPPORTED(&va, va_fsid)) {
1023 goto baddir;
1024 }
1025 /*
1026 * Make sure its what we want:
1027 * - owned by root
1028 * - not writable by anyone
1029 * - on same file system as /tmp
1030 * - not a hard-linked directory
1031 * - no ACLs (they might grant write access)
1032 */
1033 if ((va.va_uid != 0) || (va.va_gid != 0) ||
1034 (va.va_mode & (S_IWUSR | S_IRWXG | S_IRWXO)) ||
1035 (va.va_fsid != tmp_fsid) ||
1036 (va.va_dirlinkcount != 1) ||
1037 (va.va_acl != (kauth_acl_t) KAUTH_FILESEC_NONE)) {
1038 goto baddir;
1039 }
1040 }
1041 }
1042out:
1043 if (dvp) {
1044 vnode_put(dvp);
1045 }
1046 if (error) {
1047 /* On errors, clean up shadow stream directory. */
1048 if (sdvp) {
1049 vnode_put(sdvp);
1050 sdvp = NULLVP;
1051 }
1052 }
1053 *sdvpp = sdvp;
1054 return (error);
1055
1056baddir:
1057 /* This is not the dir we're looking for, move along */
1058 ++shadow_sequence; /* try something else next time */
1059 error = ENOTDIR;
1060 goto out;
1061}
1062#endif
1063
1064
1065
91447636
A
1066/*
1067 * Default Implementation (Non-native EA)
1068 */
1069
1070
1071/*
1072 Typical "._" AppleDouble Header File layout:
1073 ------------------------------------------------------------
1074 MAGIC 0x00051607
1075 VERSION 0x00020000
1076 FILLER 0
1077 COUNT 2
1078 .-- AD ENTRY[0] Finder Info Entry (must be first)
1079 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1080 | '-> FINDER INFO
1081 | ///////////// Fixed Size Data (32 bytes)
1082 | EXT ATTR HDR
1083 | /////////////
1084 | ATTR ENTRY[0] --.
1085 | ATTR ENTRY[1] --+--.
1086 | ATTR ENTRY[2] --+--+--.
1087 | ... | | |
1088 | ATTR ENTRY[N] --+--+--+--.
1089 | ATTR DATA 0 <-' | | |
1090 | //////////// | | |
1091 | ATTR DATA 1 <----' | |
1092 | ///////////// | |
1093 | ATTR DATA 2 <-------' |
1094 | ///////////// |
1095 | ... |
1096 | ATTR DATA N <----------'
1097 | /////////////
1098 | Attribute Free Space
1099 |
1100 '----> RESOURCE FORK
1101 ///////////// Variable Sized Data
1102 /////////////
1103 /////////////
1104 /////////////
1105 /////////////
1106 /////////////
1107 ...
1108 /////////////
1109
1110 ------------------------------------------------------------
1111
1112 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1113 stored as part of the Finder Info. The length in the Finder
1114 Info AppleDouble entry includes the length of the extended
1115 attribute header, attribute entries, and attribute data.
1116*/
1117
1118
1119/*
1120 * On Disk Data Structures
1121 *
1122 * Note: Motorola 68K alignment and big-endian.
1123 *
1124 * See RFC 1740 for additional information about the AppleDouble file format.
1125 *
1126 */
1127
1128#define ADH_MAGIC 0x00051607
1129#define ADH_VERSION 0x00020000
1130#define ADH_MACOSX "Mac OS X "
1131
1132/*
1133 * AppleDouble Entry ID's
1134 */
1135#define AD_DATA 1 /* Data fork */
1136#define AD_RESOURCE 2 /* Resource fork */
1137