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