]> git.saurik.com Git - apple/xnu.git/blame - bsd/vfs/vfs_xattr.c
xnu-1228.7.58.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/*
2d21ac55 63 * Cast to 'unsigned int' loses precision - hope that's OK...
91447636 64 */
2d21ac55
A
65#define MAKE_SHADOW_NAME(VP, NAME) \
66 snprintf((NAME), sizeof((NAME)), ".vfs_rsrc_stream_%x%08x%x", (unsigned int)(VP), (VP)->v_id, (unsigned int)(VP)->v_data);
67
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) {
397 vnode_t svp = *svpp;
398
399 /* Tag the vnode. */
400 vnode_lock(svp);
401 svp->v_flag |= VISNAMEDSTREAM;
402 vnode_unlock(svp);
cf7d32b8
A
403 /* Make the file its parent.
404 * Note: This parent link helps us distinguish vnodes for
405 * shadow stream files from vnodes for resource fork on file
406 * systems that support named streams natively (both have
407 * VISNAMEDSTREAM set) by allowing access to mount structure
408 * for checking MNTK_NAMED_STREAMS bit at many places in the code
409 */
2d21ac55
A
410 vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_PARENT);
411 }
412
413 return (error);
414}
415
416/*
417 * Make a named stream for vnode vp.
418 */
419errno_t
420vnode_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, int flags, vfs_context_t context)
421{
422 int error;
423
424 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS)
425 error = VNOP_MAKENAMEDSTREAM(vp, svpp, name, flags, context);
426 else
427 error = default_makenamedstream(vp, svpp, name, context);
428
429 if (error == 0) {
430 vnode_t svp = *svpp;
431
432 /* Tag the vnode. */
433 vnode_lock(svp);
434 svp->v_flag |= VISNAMEDSTREAM;
cf7d32b8
A
435 vnode_unlock(svp);
436 /* Make the file its parent.
437 * Note: This parent link helps us distinguish vnodes for
438 * shadow stream files from vnodes for resource fork on file
439 * systems that support named streams natively (both have
440 * VISNAMEDSTREAM set) by allowing access to mount structure
441 * for checking MNTK_NAMED_STREAMS bit at many places in the code
442 */
2d21ac55
A
443 vnode_update_identity(svp, vp, NULL, 0, 0, VNODE_UPDATE_PARENT);
444 }
445 return (error);
446}
447
448/*
449 * Remove a named stream from vnode vp.
450 */
451errno_t
452vnode_removenamedstream(vnode_t vp, vnode_t svp, const char *name, int flags, vfs_context_t context)
453{
454 int error;
455
456 if (vp->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS)
457 error = VNOP_REMOVENAMEDSTREAM(vp, svp, name, flags, context);
458 else
459 error = default_removenamedstream(vp, name, context);
460
461 return (error);
462}
463
464#define NS_IOBUFSIZE (128 * 1024)
465
466/*
467 * Release a named stream shadow file.
cf7d32b8
A
468 *
469 * Note: This function is called from two places where we do not need
470 * to check if the vnode has any references held before deleting the
471 * shadow file. Once from vclean() when the vnode is being reclaimed
472 * and we do not hold any references on the vnode. Second time from
473 * default_getnamedstream() when we get an error during shadow stream
474 * file initialization so that other processes who are waiting for the
475 * shadow stream file initialization by the creator will get opportunity
476 * to create and initialize the file again.
2d21ac55
A
477 */
478errno_t
479vnode_relenamedstream(vnode_t vp, vnode_t svp, vfs_context_t context)
480{
481 vnode_t dvp;
482 struct componentname cn;
483 char tmpname[48];
484 errno_t err;
485
2d21ac55
A
486 cache_purge(svp);
487
488 vnode_lock(svp);
489 MAKE_SHADOW_NAME(vp, tmpname);
490 vnode_unlock(svp);
491
492 cn.cn_nameiop = DELETE;
493 cn.cn_flags = ISLASTCN;
494 cn.cn_context = context;
495 cn.cn_pnbuf = tmpname;
496 cn.cn_pnlen = sizeof(tmpname);
497 cn.cn_nameptr = cn.cn_pnbuf;
498 cn.cn_namelen = strlen(tmpname);
499
500 /* Obtain the vnode for the shadow files directory. */
501 err = get_shadow_dir(&dvp, context);
502 if (err != 0) {
503 return err;
504 }
cf7d32b8
A
505
506 (void) VNOP_REMOVE(dvp, svp, &cn, 0, context);
2d21ac55
A
507 vnode_put(dvp);
508
509 return (0);
510}
511
512/*
513 * Flush a named stream shadow file.
514 */
515errno_t
516vnode_flushnamedstream(vnode_t vp, vnode_t svp, vfs_context_t context)
517{
518 struct vnode_attr va;
519 uio_t auio = NULL;
520 caddr_t bufptr = NULL;
521 size_t bufsize = 0;
522 size_t offset;
523 size_t iosize;
524 size_t datasize;
525 int error;
526
527 VATTR_INIT(&va);
528 VATTR_WANTED(&va, va_data_size);
529 if (VNOP_GETATTR(svp, &va, context) != 0 ||
530 !VATTR_IS_SUPPORTED(&va, va_data_size)) {
531 return (0);
532 }
533 datasize = va.va_data_size;
534 if ((datasize == 0)) {
535 (void) default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
536 return (0);
537 }
538
539 iosize = bufsize = MIN(datasize, NS_IOBUFSIZE);
540 if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize)) {
541 return (ENOMEM);
542 }
543 auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ);
544 offset = 0;
545
546 /*
547 * Copy the shadow stream file data into the resource fork.
548 */
549 error = VNOP_OPEN(svp, 0, context);
550 if (error) {
551 printf("vnode_flushnamedstream: err %d opening file\n", error);
552 goto out;
553 }
554 while (offset < datasize) {
555 iosize = MIN(datasize - offset, iosize);
556
557 uio_reset(auio, offset, UIO_SYSSPACE32, UIO_READ);
558 uio_addiov(auio, (uintptr_t)bufptr, iosize);
559 error = VNOP_READ(svp, auio, 0, context);
560 if (error) {
561 break;
562 }
563 /* Since there's no truncate xattr we must remove the resource fork. */
564 if (offset == 0) {
565 error = default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
566 if ((error != 0) && (error != ENOATTR)) {
567 break;
568 }
569 }
570 uio_reset(auio, offset, UIO_SYSSPACE32, UIO_WRITE);
571 uio_addiov(auio, (uintptr_t)bufptr, iosize);
572 error = vn_setxattr(vp, XATTR_RESOURCEFORK_NAME, auio, XATTR_NOSECURITY, context);
573 if (error) {
574 break;
575 }
576 offset += iosize;
577 }
578 (void) VNOP_CLOSE(svp, 0, context);
579out:
580 if (bufptr) {
581 kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize);
582 }
583 if (auio) {
584 uio_free(auio);
585 }
586 return (error);
587}
588
589
590static int
591getshadowfile(vnode_t vp, vnode_t *svpp, int makestream, size_t *rsrcsize,
592 int *creator, vfs_context_t context)
593{
594 vnode_t dvp = NULLVP;
595 vnode_t svp = NULLVP;
596 struct componentname cn;
597 struct vnode_attr va;
598 char tmpname[48];
599 size_t datasize = 0;
600 int error = 0;
601
602 *creator = 0;
603
604 /* Establish a unique file name. */
605 MAKE_SHADOW_NAME(vp, tmpname);
606 bzero(&cn, sizeof(cn));
607 cn.cn_nameiop = LOOKUP;
608 cn.cn_flags = ISLASTCN;
609 cn.cn_context = context;
610 cn.cn_pnbuf = tmpname;
611 cn.cn_pnlen = sizeof(tmpname);
612 cn.cn_nameptr = cn.cn_pnbuf;
613 cn.cn_namelen = strlen(tmpname);
614
615 /* Pick up uid, gid, mode and date from original file. */
616 VATTR_INIT(&va);
617 VATTR_WANTED(&va, va_uid);
618 VATTR_WANTED(&va, va_gid);
619 VATTR_WANTED(&va, va_mode);
620 VATTR_WANTED(&va, va_create_time);
621 VATTR_WANTED(&va, va_modify_time);
622 if (VNOP_GETATTR(vp, &va, context) != 0 ||
623 !VATTR_IS_SUPPORTED(&va, va_uid) ||
624 !VATTR_IS_SUPPORTED(&va, va_gid) ||
625 !VATTR_IS_SUPPORTED(&va, va_mode)) {
626 va.va_uid = KAUTH_UID_NONE;
627 va.va_gid = KAUTH_GID_NONE;
628 va.va_mode = S_IRUSR | S_IWUSR;
629 }
630 va.va_vaflags = VA_EXCLUSIVE;
631 VATTR_SET(&va, va_type, VREG);
632 /* We no longer change the access, but we still hide it. */
633 VATTR_SET(&va, va_flags, UF_HIDDEN);
634
635 /* Obtain the vnode for the shadow files directory. */
636 if (get_shadow_dir(&dvp, context) != 0) {
637 error = ENOTDIR;
638 goto out;
639 }
640 if (!makestream) {
641 /* See if someone else already has it open. */
642 if (VNOP_LOOKUP(dvp, &svp, &cn, context) == 0) {
643 /* Double check existence by asking for size. */
644 VATTR_INIT(&va);
645 VATTR_WANTED(&va, va_data_size);
646 if (VNOP_GETATTR(svp, &va, context) == 0 &&
647 VATTR_IS_SUPPORTED(&va, va_data_size)) {
648 goto out; /* OK to use. */
649 }
650 }
651
652 /* Otherwise make sure the resource fork data exists. */
653 error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &datasize,
654 XATTR_NOSECURITY, context);
655 /*
656 * To maintain binary compatibility with legacy Carbon
657 * emulated resource fork support, if the resource fork
658 * doesn't exist but the Finder Info does, then act as
659 * if an empty resource fork is present (see 4724359).
660 */
661 if ((error == ENOATTR) &&
662 (vn_getxattr(vp, XATTR_FINDERINFO_NAME, NULL, &datasize,
663 XATTR_NOSECURITY, context) == 0)) {
664 datasize = 0;
665 error = 0;
666 } else {
667 if (error) {
668 goto out;
669 }
670
671 /* If the resource fork exists, its size is expected to be non-zero. */
672 if (datasize == 0) {
673 error = ENOATTR;
674 goto out;
675 }
676 }
677 }
678 /* Create the shadow stream file. */
679 error = VNOP_CREATE(dvp, &svp, &cn, &va, context);
680 if (error == 0) {
681 *creator = 1;
682 } else if ((error == EEXIST) && !makestream) {
683 error = VNOP_LOOKUP(dvp, &svp, &cn, context);
684 }
685out:
686 if (dvp) {
687 vnode_put(dvp);
688 }
689 if (error) {
690 /* On errors, clean up shadow stream file. */
691 if (svp) {
692 vnode_put(svp);
693 svp = NULLVP;
694 }
695 }
696 *svpp = svp;
697 if (rsrcsize) {
698 *rsrcsize = datasize;
699 }
700 return (error);
701}
702
703
704static int
705default_getnamedstream(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation op, vfs_context_t context)
706{
707 vnode_t svp = NULLVP;
708 uio_t auio = NULL;
709 caddr_t bufptr = NULL;
710 size_t bufsize = 0;
711 size_t datasize = 0;
712 int creator;
713 int error;
714
715 /*
716 * Only the "com.apple.ResourceFork" stream is supported here.
717 */
718 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
719 *svpp = NULLVP;
720 return (ENOATTR);
721 }
722retry:
723 /*
724 * Obtain a shadow file for the resource fork I/O.
725 */
726 error = getshadowfile(vp, &svp, 0, &datasize, &creator, context);
727 if (error) {
728 *svpp = NULLVP;
729 return (error);
730 }
731
732 /*
733 * The creator of the shadow file provides its file data,
734 * all other threads should wait until its ready.
735 */
736 if (!creator) {
737 vnode_lock(svp);
738 if (svp->v_flag & VISNAMEDSTREAM) {
739 /* data is ready, go use it */
740 vnode_unlock(svp);
741 goto out;
742 } else {
743 /* its not ready, wait for it (sleep using v_parent as channel) */
744 msleep((caddr_t)&svp->v_parent, &svp->v_lock, PINOD | PDROP,
745 "getnamedstream", NULL);
746 vnode_put(svp);
747 svp = NULLVP;
748 goto retry;
749 }
750 }
751
752 /*
753 * Copy the real resource fork data into shadow stream file.
754 */
755 if (op == NS_OPEN && datasize != 0) {
756 size_t offset;
757 size_t iosize;
758
759 iosize = bufsize = MIN(datasize, NS_IOBUFSIZE);
760 if (kmem_alloc(kernel_map, (vm_offset_t *)&bufptr, bufsize)) {
761 error = ENOMEM;
762 goto out;
763 }
764
765 auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ);
766 offset = 0;
767
768 error = VNOP_OPEN(svp, 0, context);
769 if (error) {
770 goto out;
771 }
772 while (offset < datasize) {
773 size_t tmpsize;
774
775 iosize = MIN(datasize - offset, iosize);
776
777 uio_reset(auio, offset, UIO_SYSSPACE32, UIO_READ);
778 uio_addiov(auio, (uintptr_t)bufptr, iosize);
779 error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, auio, &tmpsize,
780 XATTR_NOSECURITY, context);
781 if (error) {
782 break;
783 }
784
785 uio_reset(auio, offset, UIO_SYSSPACE32, UIO_WRITE);
786 uio_addiov(auio, (uintptr_t)bufptr, iosize);
787 error = VNOP_WRITE(svp, auio, 0, context);
788 if (error) {
789 break;
790 }
791 offset += iosize;
792 }
793 (void) VNOP_CLOSE(svp, 0, context);
794 }
795out:
796 /* Wake up anyone waiting for svp file content */
797 if (creator) {
798 if (error == 0) {
799 vnode_lock(svp);
800 svp->v_flag |= VISNAMEDSTREAM;
801 wakeup((caddr_t)&svp->v_parent);
802 vnode_unlock(svp);
803 } else {
cf7d32b8
A
804 /* On post create errors, get rid of the shadow file. This
805 * way, if there is another process waiting for initialization
806 * of the shadow file by the current process, it will wake up
807 * and retry by creating and initializing the shadow file again.
808 */
809 (void) vnode_relenamedstream(vp, svp, context);
2d21ac55
A
810 wakeup((caddr_t)&svp->v_parent);
811 }
812 }
813
814 if (bufptr) {
815 kmem_free(kernel_map, (vm_offset_t)bufptr, bufsize);
816 }
817 if (auio) {
818 uio_free(auio);
819 }
820 if (error) {
821 /* On errors, clean up shadow stream file. */
822 if (svp) {
823 vnode_put(svp);
824 svp = NULLVP;
825 }
826 }
827 *svpp = svp;
828 return (error);
829}
830
831static int
832default_makenamedstream(vnode_t vp, vnode_t *svpp, const char *name, vfs_context_t context)
833{
834 int creator;
835 int error;
836
837 /*
838 * Only the "com.apple.ResourceFork" stream is supported here.
839 */
840 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
841 *svpp = NULLVP;
842 return (ENOATTR);
843 }
844 error = getshadowfile(vp, svpp, 1, NULL, &creator, context);
845
846 /*
847 * Wake up any waiters over in default_getnamedstream().
848 */
849 if ((error == 0) && (*svpp != NULL) && creator) {
850 vnode_t svp = *svpp;
851
852 vnode_lock(svp);
853 svp->v_flag |= VISNAMEDSTREAM;
854 /* Wakeup any waiters on the v_parent channel */
855 wakeup((caddr_t)&svp->v_parent);
856 vnode_unlock(svp);
857 }
858 return (error);
859}
860
861static int
862default_removenamedstream(vnode_t vp, const char *name, vfs_context_t context)
863{
864 /*
865 * Only the "com.apple.ResourceFork" stream is supported here.
866 */
867 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
868 return (ENOATTR);
869 }
870 /*
871 * XXX - what about other opened instances?
872 */
873 return default_removexattr(vp, XATTR_RESOURCEFORK_NAME, 0, context);
874}
875
876static int
877get_shadow_dir(vnode_t *sdvpp, vfs_context_t context)
878{
879 vnode_t dvp = NULLVP;
880 vnode_t sdvp = NULLVP;
881 struct componentname cn;
882 struct vnode_attr va;
883 char tmpname[48];
884 uint32_t tmp_fsid;
885 int error;
886
887 /* Check if we've already created it. */
888 if (shadow_dvp != NULLVP) {
889 if ((error = vnode_getwithvid(shadow_dvp, shadow_vid))) {
890 shadow_dvp = NULLVP;
891 } else {
892 *sdvpp = shadow_dvp;
893 return (0);
894 }
895 }
896
897 /* Obtain the vnode for "/tmp" directory. */
898 if (vnode_lookup("/tmp", 0, &dvp, context) != 0) {
899 error = ENOTSUP;
900 goto out;
901 }
902
903 /* Create the shadow stream directory. */
904 snprintf(tmpname, sizeof(tmpname), ".vfs_rsrc_streams_%x%x",
905 (unsigned int)rootvnode, shadow_sequence);
906 bzero(&cn, sizeof(cn));
907 cn.cn_nameiop = LOOKUP;
908 cn.cn_flags = ISLASTCN;
909 cn.cn_context = context;
910 cn.cn_pnbuf = tmpname;
911 cn.cn_pnlen = sizeof(tmpname);
912 cn.cn_nameptr = cn.cn_pnbuf;
913 cn.cn_namelen = strlen(tmpname);
914
915 /*
916 * owned by root, only readable by root, hidden
917 */
918 VATTR_INIT(&va);
919 VATTR_SET(&va, va_uid, 0);
920 VATTR_SET(&va, va_gid, 0);
921 VATTR_SET(&va, va_mode, S_IRUSR | S_IXUSR);
922 VATTR_SET(&va, va_type, VDIR);
923 VATTR_SET(&va, va_flags, UF_HIDDEN);
924 va.va_vaflags = VA_EXCLUSIVE;
925
926 error = VNOP_MKDIR(dvp, &sdvp, &cn, &va, context);
927
928 /*
929 * There can be only one winner for an exclusive create.
930 */
931 if (error == 0) {
932 /* Take a long term ref to keep this dir around. */
933 error = vnode_ref(sdvp);
934 if (error == 0) {
935 shadow_dvp = sdvp;
936 shadow_vid = sdvp->v_id;
937 }
938 } else if (error == EEXIST) {
939 /* loser has to look up directory */
940 error = VNOP_LOOKUP(dvp, &sdvp, &cn, context);
941 if (error == 0) {
942 /* Make sure its in fact a directory */
943 if (sdvp->v_type != VDIR) {
944 goto baddir;
945 }
946 /* Obtain the fsid for /tmp directory */
947 VATTR_INIT(&va);
948 VATTR_WANTED(&va, va_fsid);
949 if (VNOP_GETATTR(dvp, &va, context) != 0 ||
950 !VATTR_IS_SUPPORTED(&va, va_fsid)) {
951 goto baddir;
952 }
953 tmp_fsid = va.va_fsid;
954
955 VATTR_INIT(&va);
956 VATTR_WANTED(&va, va_uid);
957 VATTR_WANTED(&va, va_gid);
958 VATTR_WANTED(&va, va_mode);
959 VATTR_WANTED(&va, va_fsid);
960 VATTR_WANTED(&va, va_dirlinkcount);
961 VATTR_WANTED(&va, va_acl);
962 /* Provide defaults for attrs that may not be supported */
963 va.va_dirlinkcount = 1;
964 va.va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
965
966 if (VNOP_GETATTR(sdvp, &va, context) != 0 ||
967 !VATTR_IS_SUPPORTED(&va, va_uid) ||
968 !VATTR_IS_SUPPORTED(&va, va_gid) ||
969 !VATTR_IS_SUPPORTED(&va, va_mode) ||
970 !VATTR_IS_SUPPORTED(&va, va_fsid)) {
971 goto baddir;
972 }
973 /*
974 * Make sure its what we want:
975 * - owned by root
976 * - not writable by anyone
977 * - on same file system as /tmp
978 * - not a hard-linked directory
979 * - no ACLs (they might grant write access)
980 */
981 if ((va.va_uid != 0) || (va.va_gid != 0) ||
982 (va.va_mode & (S_IWUSR | S_IRWXG | S_IRWXO)) ||
983 (va.va_fsid != tmp_fsid) ||
984 (va.va_dirlinkcount != 1) ||
985 (va.va_acl != (kauth_acl_t) KAUTH_FILESEC_NONE)) {
986 goto baddir;
987 }
988 }
989 }
990out:
991 if (dvp) {
992 vnode_put(dvp);
993 }
994 if (error) {
995 /* On errors, clean up shadow stream directory. */
996 if (sdvp) {
997 vnode_put(sdvp);
998 sdvp = NULLVP;
999 }
1000 }
1001 *sdvpp = sdvp;
1002 return (error);
1003
1004baddir:
1005 /* This is not the dir we're looking for, move along */
1006 ++shadow_sequence; /* try something else next time */
1007 error = ENOTDIR;
1008 goto out;
1009}
1010#endif
1011
1012
1013
91447636
A
1014/*
1015 * Default Implementation (Non-native EA)
1016 */
1017
1018
1019/*
1020 Typical "._" AppleDouble Header File layout:
1021 ------------------------------------------------------------
1022 MAGIC 0x00051607
1023 VERSION 0x00020000
1024 FILLER 0
1025 COUNT 2
1026 .-- AD ENTRY[0] Finder Info Entry (must be first)
1027 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
1028 | '-> FINDER INFO
1029 | ///////////// Fixed Size Data (32 bytes)
1030 | EXT ATTR HDR
1031 | /////////////
1032 | ATTR ENTRY[0] --.
1033 | ATTR ENTRY[1] --+--.
1034 | ATTR ENTRY[2] --+--+--.
1035 | ... | | |
1036 | ATTR ENTRY[N] --+--+--+--.
1037 | ATTR DATA 0 <-' | | |
1038 | //////////// | | |
1039 | ATTR DATA 1 <----' | |
1040 | ///////////// | |
1041 | ATTR DATA 2 <-------' |
1042 | ///////////// |
1043 | ... |
1044 | ATTR DATA N <----------'
1045 | /////////////
1046 | Attribute Free Space
1047 |
1048 '----> RESOURCE FORK
1049 ///////////// Variable Sized Data
1050 /////////////
1051 /////////////
1052 /////////////
1053 /////////////
1054 /////////////
1055 ...
1056 /////////////
1057
1058 ------------------------------------------------------------
1059
1060 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
1061 stored as part of the Finder Info. The length in the Finder
1062 Info AppleDouble entry includes the length of the extended
1063 attribute header, attribute entries, and attribute data.
1064*/
1065
1066
1067/*
1068 * On Disk Data Structures
1069 *
1070 * Note: Motorola 68K alignment and big-endian.
1071 *
1072 * See RFC 1740 for additional information about the AppleDouble file format.
1073 *
1074 */
1075
1076#define ADH_MAGIC 0x00051607
1077#define ADH_VERSION 0x00020000
1078#define ADH_MACOSX "Mac OS X "
1079
1080/*
1081 * AppleDouble Entry ID's
1082 */
1083#define AD_DATA 1 /* Data fork */
1084#define AD_RESOURCE 2 /* Resource fork */
1085