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