]> git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_xattr.c
7530731b23e53faf1831d7fce93a85666b8d012c
[apple/xnu.git] / bsd / vfs / vfs_xattr.c
1 /*
2 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <sys/param.h>
30
31 #include <sys/fcntl.h>
32 #include <sys/fsevents.h>
33 #include <sys/kernel.h>
34 #include <sys/kauth.h>
35 #include <sys/malloc.h>
36 #include <sys/namei.h>
37 #include <sys/proc_internal.h>
38 #include <sys/stat.h>
39 #include <sys/uio.h>
40 #include <sys/utfconv.h>
41 #include <sys/vnode.h>
42 #include <sys/vnode_internal.h>
43
44 #include <sys/xattr.h>
45
46 #include <architecture/byte_order.h>
47 #include <vm/vm_kern.h>
48
49 /*
50 * Default xattr support routines.
51 */
52 static int default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size,
53 int options, vfs_context_t context);
54
55 static int default_setxattr(vnode_t vp, const char *name, uio_t uio,
56 int options, vfs_context_t context);
57
58 static int default_removexattr(vnode_t vp, const char *name, int options, vfs_context_t context);
59
60 static int default_listxattr(vnode_t vp, uio_t uio, size_t *size, int options,
61 vfs_context_t context);
62
63
64
65 /*
66 * Retrieve the data of an extended attribute.
67 */
68 int
69 vn_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size,
70 int options, vfs_context_t context)
71 {
72 int error;
73
74 if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) {
75 return (EPERM);
76 }
77 if ((error = xattr_validatename(name))) {
78 return (error);
79 }
80 if (!(options & XATTR_NOSECURITY) && (error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context)))
81 goto out;
82
83 /* The offset can only be non-zero for resource forks. */
84 if (uio != NULL && uio_offset(uio) != 0 &&
85 bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
86 error = EINVAL;
87 goto out;
88 }
89
90 error = VNOP_GETXATTR(vp, name, uio, size, options, context);
91 if (error == ENOTSUP) {
92 /*
93 * A filesystem may keep some EAs natively and return ENOTSUP for others.
94 * SMB returns ENOTSUP for finderinfo and resource forks.
95 */
96 error = default_getxattr(vp, name, uio, size, options, context);
97 }
98 out:
99 return (error);
100 }
101
102 /*
103 * Set the data of an extended attribute.
104 */
105 int
106 vn_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context)
107 {
108 int error;
109
110 if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) {
111 return (EPERM);
112 }
113 if ((options & (XATTR_REPLACE|XATTR_CREATE)) == (XATTR_REPLACE|XATTR_CREATE)) {
114 return (EINVAL);
115 }
116 if ((error = xattr_validatename(name))) {
117 return (error);
118 }
119 if (!(options & XATTR_NOSECURITY) && (error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context)))
120 goto out;
121
122 /* The offset can only be non-zero for resource forks. */
123 if (uio_offset(uio) != 0 &&
124 bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0 ) {
125 error = EINVAL;
126 goto out;
127 }
128
129 error = VNOP_SETXATTR(vp, name, uio, options, context);
130 #ifdef DUAL_EAS
131 /*
132 * An EJUSTRETURN is from a filesystem which keeps this xattr
133 * natively as well as in a dot-underscore file. In this case the
134 * EJUSTRETURN means the filesytem has done nothing, but identifies the
135 * EA as one which may be represented natively and/or in a DU, and
136 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
137 * in vn_setxattr can we do the getxattrs needed to ascertain whether
138 * the XATTR_{CREATE,REPLACE} should yield an error.
139 */
140 if (error == EJUSTRETURN) {
141 int native = 0, dufile = 0;
142 size_t sz; /* not used */
143
144 native = VNOP_GETXATTR(vp, name, NULL, &sz, 0, context) ? 0 : 1;
145 dufile = default_getxattr(vp, name, NULL, &sz, 0, context) ? 0 : 1;
146 if (options & XATTR_CREATE && (native || dufile)) {
147 error = EEXIST;
148 goto out;
149 }
150 if (options & XATTR_REPLACE && !(native || dufile)) {
151 error = ENOATTR;
152 goto out;
153 }
154 /*
155 * Having determined no CREATE/REPLACE error should result, we
156 * zero those bits, so both backing stores get written to.
157 */
158 options &= ~(XATTR_CREATE | XATTR_REPLACE);
159 error = VNOP_SETXATTR(vp, name, uio, options, context);
160 /* the mainline path here is to have error==ENOTSUP ... */
161 }
162 #endif /* DUAL_EAS */
163 if (error == ENOTSUP) {
164 /*
165 * A filesystem may keep some EAs natively and return ENOTSUP for others.
166 * SMB returns ENOTSUP for finderinfo and resource forks.
167 */
168 error = default_setxattr(vp, name, uio, options, context);
169 }
170 out:
171 return (error);
172 }
173
174 /*
175 * Remove an extended attribute.
176 */
177 int
178 vn_removexattr(vnode_t vp, const char * name, int options, vfs_context_t context)
179 {
180 int error;
181
182 if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) {
183 return (EPERM);
184 }
185 if ((error = xattr_validatename(name))) {
186 return (error);
187 }
188 if (!(options & XATTR_NOSECURITY) && (error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_EXTATTRIBUTES, context)))
189 goto out;
190 error = VNOP_REMOVEXATTR(vp, name, options, context);
191 if (error == ENOTSUP) {
192 /*
193 * A filesystem may keep some EAs natively and return ENOTSUP for others.
194 * SMB returns ENOTSUP for finderinfo and resource forks.
195 */
196 error = default_removexattr(vp, name, options, context);
197 #ifdef DUAL_EAS
198 } else if (error == EJUSTRETURN) {
199 /*
200 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
201 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
202 * a native xattr, so failure to find it in a DU file during
203 * default_removexattr should not be considered an error.
204 */
205 error = default_removexattr(vp, name, options, context);
206 if (error == ENOATTR)
207 error = 0;
208 #endif /* DUAL_EAS */
209 }
210 out:
211 return (error);
212 }
213
214 /*
215 * Retrieve the list of extended attribute names.
216 */
217 int
218 vn_listxattr(vnode_t vp, uio_t uio, size_t *size, int options, vfs_context_t context)
219 {
220 int error;
221
222 if (!(vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VLNK)) {
223 return (EPERM);
224 }
225 if (!(options & XATTR_NOSECURITY) && (error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_EXTATTRIBUTES, context)))
226 goto out;
227
228 error = VNOP_LISTXATTR(vp, uio, size, options, context);
229 if (error == ENOTSUP) {
230 /*
231 * A filesystem may keep some but not all EAs natively, in which case
232 * the native EA names will have been uiomove-d out (or *size updated)
233 * and the default_listxattr here will finish the job. Note SMB takes
234 * advantage of this for its finder-info and resource forks.
235 */
236 error = default_listxattr(vp, uio, size, options, context);
237 }
238 out:
239 return (error);
240 }
241
242 int
243 xattr_validatename(const char *name)
244 {
245 int namelen;
246
247 if (name == NULL || name[0] == '\0') {
248 return (EINVAL);
249 }
250 namelen = strlen(name);
251 if (namelen > XATTR_MAXNAMELEN) {
252 return (ENAMETOOLONG);
253 }
254 if (utf8_validatestr(name, namelen) != 0) {
255 return (EINVAL);
256 }
257 return (0);
258 }
259
260
261 /*
262 * Determine whether an EA is a protected system attribute.
263 */
264 int
265 xattr_protected(const char *attrname)
266 {
267 return(!strncmp(attrname, "com.apple.system.", 17));
268 }
269
270
271 /*
272 * Default Implementation (Non-native EA)
273 */
274
275
276 /*
277 Typical "._" AppleDouble Header File layout:
278 ------------------------------------------------------------
279 MAGIC 0x00051607
280 VERSION 0x00020000
281 FILLER 0
282 COUNT 2
283 .-- AD ENTRY[0] Finder Info Entry (must be first)
284 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
285 | '-> FINDER INFO
286 | ///////////// Fixed Size Data (32 bytes)
287 | EXT ATTR HDR
288 | /////////////
289 | ATTR ENTRY[0] --.
290 | ATTR ENTRY[1] --+--.
291 | ATTR ENTRY[2] --+--+--.
292 | ... | | |
293 | ATTR ENTRY[N] --+--+--+--.
294 | ATTR DATA 0 <-' | | |
295 | //////////// | | |
296 | ATTR DATA 1 <----' | |
297 | ///////////// | |
298 | ATTR DATA 2 <-------' |
299 | ///////////// |
300 | ... |
301 | ATTR DATA N <----------'
302 | /////////////
303 | Attribute Free Space
304 |
305 '----> RESOURCE FORK
306 ///////////// Variable Sized Data
307 /////////////
308 /////////////
309 /////////////
310 /////////////
311 /////////////
312 ...
313 /////////////
314
315 ------------------------------------------------------------
316
317 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
318 stored as part of the Finder Info. The length in the Finder
319 Info AppleDouble entry includes the length of the extended
320 attribute header, attribute entries, and attribute data.
321 */
322
323
324 /*
325 * On Disk Data Structures
326 *
327 * Note: Motorola 68K alignment and big-endian.
328 *
329 * See RFC 1740 for additional information about the AppleDouble file format.
330 *
331 */
332
333 #define ADH_MAGIC 0x00051607
334 #define ADH_VERSION 0x00020000
335 #define ADH_MACOSX "Mac OS X "
336
337 /*
338 * AppleDouble Entry ID's
339 */
340 #define AD_DATA 1 /* Data fork */
341 #define AD_RESOURCE 2 /* Resource fork */
342 #define AD_REALNAME 3 /* FileÕs name on home file system */
343 #define AD_COMMENT 4 /* Standard Mac comment */
344 #define AD_ICONBW 5 /* Mac black & white icon */
345 #define AD_ICONCOLOR 6 /* Mac color icon */
346 #define AD_UNUSED 7 /* Not used */
347 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
348 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
349 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
350 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
351 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
352 #define AD_AFPNAME 13 /* Short name on AFP server */
353 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
354 #define AD_AFPDIRID 15 /* AFP directory ID */
355 #define AD_ATTRIBUTES AD_FINDERINFO
356
357
358 #define ATTR_FILE_PREFIX "._"
359 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
360
361 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
362
363 /* Implementation Limits */
364 #define ATTR_MAX_SIZE (128*1024) /* 128K maximum attribute data size */
365 #define ATTR_MAX_HDR_SIZE 65536
366 /*
367 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
368 * size supported (including the attribute entries). All of
369 * the attribute entries must reside within this limit. If
370 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
371 * boundry, then all of the attribute data I/O is performed
372 * seperately from the attribute header I/O.
373 */
374
375
376 #pragma options align=mac68k
377
378 #define FINDERINFOSIZE 32
379
380 typedef struct apple_double_entry {
381 u_int32_t type; /* entry type: see list, 0 invalid */
382 u_int32_t offset; /* entry data offset from the beginning of the file. */
383 u_int32_t length; /* entry data length in bytes. */
384 } apple_double_entry_t;
385
386
387 typedef struct apple_double_header {
388 u_int32_t magic; /* == ADH_MAGIC */
389 u_int32_t version; /* format version: 2 = 0x00020000 */
390 u_int32_t filler[4];
391 u_int16_t numEntries; /* number of entries which follow */
392 apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */
393 u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */
394 u_int8_t pad[2]; /* get better alignment inside attr_header */
395 } apple_double_header_t;
396
397 #define ADHDRSIZE (4+4+16+2)
398
399 /* Entries are aligned on 4 byte boundaries */
400 typedef struct attr_entry {
401 u_int32_t offset; /* file offset to data */
402 u_int32_t length; /* size of attribute data */
403 u_int16_t flags;
404 u_int8_t namelen;
405 u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
406 } attr_entry_t;
407
408
409 /* Header + entries must fit into 64K */
410 typedef struct attr_header {
411 apple_double_header_t appledouble;
412 u_int32_t magic; /* == ATTR_HDR_MAGIC */
413 u_int32_t debug_tag; /* for debugging == file id of owning file */
414 u_int32_t total_size; /* total size of attribute header + entries + data */
415 u_int32_t data_start; /* file offset to attribute data area */
416 u_int32_t data_length; /* length of attribute data area */
417 u_int32_t reserved[3];
418 u_int16_t flags;
419 u_int16_t num_attrs;
420 } attr_header_t;
421
422
423 /* Empty Resource Fork Header */
424 typedef struct rsrcfork_header {
425 u_int32_t fh_DataOffset;
426 u_int32_t fh_MapOffset;
427 u_int32_t fh_DataLength;
428 u_int32_t fh_MapLength;
429 u_int8_t systemData[112];
430 u_int8_t appData[128];
431 u_int32_t mh_DataOffset;
432 u_int32_t mh_MapOffset;
433 u_int32_t mh_DataLength;
434 u_int32_t mh_MapLength;
435 u_int32_t mh_Next;
436 u_int16_t mh_RefNum;
437 u_int8_t mh_Attr;
438 u_int8_t mh_InMemoryAttr;
439 u_int16_t mh_Types;
440 u_int16_t mh_Names;
441 u_int16_t typeCount;
442 } rsrcfork_header_t;
443
444 #define RF_FIRST_RESOURCE 256
445 #define RF_NULL_MAP_LENGTH 30
446 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
447
448 #pragma options align=reset
449
450 /* Runtime information about the attribute file. */
451 typedef struct attr_info {
452 vfs_context_t context;
453 vnode_t filevp;
454 size_t filesize;
455 size_t iosize;
456 u_int8_t *rawdata;
457 size_t rawsize; /* raw size of AppleDouble file */
458 apple_double_header_t *filehdr;
459 apple_double_entry_t *finderinfo;
460 apple_double_entry_t *rsrcfork;
461 attr_header_t *attrhdr;
462 attr_entry_t *attr_entry;
463 u_int8_t readonly;
464 u_int8_t emptyfinderinfo;
465 } attr_info_t;
466
467
468 #define ATTR_SETTING 1
469
470 #define ATTR_ALIGN 3L /* Use four-byte alignment */
471
472 #define ATTR_ENTRY_LENGTH(namelen) \
473 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
474
475 #define ATTR_NEXT(ae) \
476 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
477
478 #define ATTR_VALID(ae, ai) \
479 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
480
481
482 #define SWAP16(x) NXSwapBigShortToHost((x))
483 #define SWAP32(x) NXSwapBigIntToHost((x))
484 #define SWAP64(x) NXSwapBigLongLongToHost((x))
485
486
487 static u_int32_t emptyfinfo[8] = {0};
488
489
490 /*
491 * Local support routines
492 */
493 static void close_xattrfile(vnode_t xvp, int fileflags, vfs_context_t context);
494
495 static int open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context);
496
497 static int create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context);
498
499 static int remove_xattrfile(vnode_t xvp, vfs_context_t context);
500
501 static int get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context);
502
503 static void rel_xattrinfo(attr_info_t *ainfop);
504
505 static int write_xattrinfo(attr_info_t *ainfop);
506
507 static void init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr);
508
509 static int lock_xattrfile(vnode_t xvp, short locktype, vfs_context_t context);
510
511 static int unlock_xattrfile(vnode_t xvp, vfs_context_t context);
512
513
514 #if BYTE_ORDER == LITTLE_ENDIAN
515 static void swap_adhdr(apple_double_header_t *adh);
516 static void swap_attrhdr(attr_header_t *ah);
517
518 #else
519 #define swap_adhdr(x)
520 #define swap_attrhdr(x)
521 #endif
522
523 static int validate_attrhdr(attr_header_t *ah, size_t bufsize);
524 static int shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context);
525 static int shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context);
526
527
528 /*
529 * Retrieve the data of an extended attribute.
530 */
531 static int
532 default_getxattr(vnode_t vp, const char *name, uio_t uio, size_t *size,
533 __unused int options, vfs_context_t context)
534 {
535 vnode_t xvp = NULL;
536 attr_info_t ainfo;
537 attr_header_t *header;
538 attr_entry_t *entry;
539 u_int8_t *attrdata;
540 size_t datalen;
541 int namelen;
542 int isrsrcfork;
543 int fileflags;
544 int i;
545 int error;
546
547 fileflags = FREAD;
548 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
549 isrsrcfork = 1;
550 /*
551 * Open the file locked (shared) since the Carbon
552 * File Manager may have the Apple Double file open
553 * and could be changing the resource fork.
554 */
555 fileflags |= O_SHLOCK;
556 } else {
557 isrsrcfork = 0;
558 }
559
560 if ((error = open_xattrfile(vp, fileflags, &xvp, context))) {
561 return (error);
562 }
563 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
564 close_xattrfile(xvp, fileflags, context);
565 return (error);
566 }
567
568 /* Get the Finder Info. */
569 if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
570
571 if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) {
572 error = ENOATTR;
573 } else if (uio == NULL) {
574 *size = FINDERINFOSIZE;
575 error = 0;
576 } else if (uio_offset(uio) != 0) {
577 error = EINVAL;
578 } else if (uio_resid(uio) < FINDERINFOSIZE) {
579 error = ERANGE;
580 } else {
581 attrdata = (u_int8_t*)ainfo.filehdr + ainfo.finderinfo->offset;
582 error = uiomove((caddr_t)attrdata, FINDERINFOSIZE, uio);
583 }
584 goto out;
585 }
586
587 /* Read the Resource Fork. */
588 if (isrsrcfork) {
589 if (!vnode_isreg(vp)) {
590 error = EPERM;
591 } else if (ainfo.rsrcfork == NULL) {
592 error = ENOATTR;
593 } else if (uio == NULL) {
594 *size = (size_t)ainfo.rsrcfork->length;
595 } else {
596 uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset);
597 error = VNOP_READ(xvp, uio, 0, context);
598 if (error == 0)
599 uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset);
600 }
601 goto out;
602 }
603
604 if (ainfo.attrhdr == NULL || ainfo.attr_entry == NULL) {
605 error = ENOATTR;
606 goto out;
607 }
608 if (uio_offset(uio) != 0) {
609 error = EINVAL;
610 goto out;
611 }
612 error = ENOATTR;
613 namelen = strlen(name) + 1;
614 header = ainfo.attrhdr;
615 entry = ainfo.attr_entry;
616 /*
617 * Search for attribute name in the header.
618 */
619 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
620 if (strncmp(entry->name, name, namelen) == 0) {
621 datalen = (size_t)entry->length;
622 if (uio == NULL) {
623 *size = datalen;
624 error = 0;
625 break;
626 }
627 if (uio_resid(uio) < datalen) {
628 error = ERANGE;
629 break;
630 }
631 if (entry->offset + datalen < ATTR_MAX_HDR_SIZE) {
632 attrdata = ((u_int8_t *)header + entry->offset);
633 error = uiomove((caddr_t)attrdata, datalen, uio);
634 } else {
635 uio_setoffset(uio, entry->offset);
636 error = VNOP_READ(xvp, uio, 0, context);
637 uio_setoffset(uio, 0);
638 }
639 break;
640 }
641 entry = ATTR_NEXT(entry);
642 }
643 out:
644 rel_xattrinfo(&ainfo);
645 close_xattrfile(xvp, fileflags, context);
646
647 return (error);
648 }
649
650 /*
651 * Set the data of an extended attribute.
652 */
653 static int
654 default_setxattr(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t context)
655 {
656 vnode_t xvp = NULL;
657 attr_info_t ainfo;
658 attr_header_t *header;
659 attr_entry_t *entry;
660 attr_entry_t *lastentry;
661 u_int8_t *attrdata;
662 size_t datalen;
663 size_t entrylen;
664 size_t datafreespace;
665 int namelen;
666 int found = 0;
667 int i;
668 int splitdata;
669 int fileflags;
670 int error;
671
672 datalen = uio_resid(uio);
673 namelen = strlen(name) + 1;
674 entrylen = ATTR_ENTRY_LENGTH(namelen);
675
676 if (datalen > ATTR_MAX_SIZE) {
677 return (E2BIG); /* EINVAL instead ? */
678 }
679 start:
680 /*
681 * Open the file locked since setting an attribute
682 * can change the layout of the Apple Double file.
683 */
684 fileflags = FREAD | FWRITE | O_EXLOCK;
685 if ((error = open_xattrfile(vp, O_CREAT | fileflags, &xvp, context))) {
686 return (error);
687 }
688 if ((error = get_xattrinfo(xvp, ATTR_SETTING, &ainfo, context))) {
689 close_xattrfile(xvp, fileflags, context);
690 return (error);
691 }
692
693 /* Set the Finder Info. */
694 if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
695 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
696 /* attr exists and "create" was specified? */
697 if (options & XATTR_CREATE) {
698 error = EEXIST;
699 goto out;
700 }
701 } else {
702 /* attr doesn't exists and "replace" was specified? */
703 if (options & XATTR_REPLACE) {
704 error = ENOATTR;
705 goto out;
706 }
707 }
708 if (uio_offset(uio) != 0 || datalen != FINDERINFOSIZE) {
709 error = EINVAL;
710 goto out;
711 }
712 if (ainfo.finderinfo) {
713 attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset;
714 error = uiomove((caddr_t)attrdata, datalen, uio);
715 if (error)
716 goto out;
717 ainfo.iosize = sizeof(attr_header_t);
718 error = write_xattrinfo(&ainfo);
719 goto out;
720 }
721 error = ENOATTR;
722 goto out;
723 }
724
725 /* Write the Resource Fork. */
726 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
727 u_int32_t endoffset;
728
729 if (!vnode_isreg(vp)) {
730 error = EPERM;
731 goto out;
732 }
733 if (ainfo.rsrcfork && ainfo.rsrcfork->length) {
734 /* attr exists and "create" was specified? */
735 if (options & XATTR_CREATE) {
736 error = EEXIST;
737 goto out;
738 }
739 } else {
740 /* attr doesn't exists and "replace" was specified? */
741 if (options & XATTR_REPLACE) {
742 error = ENOATTR;
743 goto out;
744 }
745 }
746 endoffset = uio_resid(uio) + uio_offset(uio); /* new size */
747 uio_setoffset(uio, uio_offset(uio) + ainfo.rsrcfork->offset);
748 error = VNOP_WRITE(xvp, uio, 0, context);
749 if (error)
750 goto out;
751 uio_setoffset(uio, uio_offset(uio) - ainfo.rsrcfork->offset);
752 if (endoffset > ainfo.rsrcfork->length) {
753 ainfo.rsrcfork->length = endoffset;
754 ainfo.iosize = sizeof(attr_header_t);
755 error = write_xattrinfo(&ainfo);
756 goto out;
757 }
758 goto out;
759 }
760
761 if (ainfo.attrhdr == NULL) {
762 error = ENOATTR;
763 goto out;
764 }
765 header = ainfo.attrhdr;
766 entry = ainfo.attr_entry;
767
768 /* Check if data area crosses the maximum header size. */
769 if ((header->data_start + header->data_length + entrylen + datalen) > ATTR_MAX_HDR_SIZE)
770 splitdata = 1; /* do data I/O separately */
771 else
772 splitdata = 0;
773
774 /*
775 * See if attribute already exists.
776 */
777 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
778 if (strncmp(entry->name, name, namelen) == 0) {
779 found = 1;
780 break;
781 }
782 entry = ATTR_NEXT(entry);
783 }
784
785 if (found) {
786 if (options & XATTR_CREATE) {
787 error = EEXIST;
788 goto out;
789 }
790 if (datalen == entry->length) {
791 if (splitdata) {
792 uio_setoffset(uio, entry->offset);
793 error = VNOP_WRITE(xvp, uio, 0, context);
794 uio_setoffset(uio, 0);
795 if (error) {
796 printf("setxattr: VNOP_WRITE error %d\n", error);
797 }
798 } else {
799 attrdata = (u_int8_t *)header + entry->offset;
800 error = uiomove((caddr_t)attrdata, datalen, uio);
801 if (error)
802 goto out;
803 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
804 error = write_xattrinfo(&ainfo);
805 if (error) {
806 printf("setxattr: write_xattrinfo error %d\n", error);
807 }
808 }
809 goto out;
810 } else {
811 /*
812 * Brute force approach - just remove old entry and set new entry.
813 */
814 found = 0;
815 rel_xattrinfo(&ainfo);
816 close_xattrfile(xvp, fileflags, context);
817 error = default_removexattr(vp, name, options, context);
818 if (error) {
819 goto out;
820 }
821 goto start; /* start over */
822 }
823
824 }
825
826 if (options & XATTR_REPLACE) {
827 error = ENOATTR; /* nothing there to replace */
828 goto out;
829 }
830 /* Check if header size limit has been reached. */
831 if ((header->data_start + entrylen) > ATTR_MAX_HDR_SIZE) {
832 error = ENOSPC;
833 goto out;
834 }
835
836 datafreespace = header->total_size - (header->data_start + header->data_length);
837
838 /* Check if we need more space. */
839 if ((datalen + entrylen) > datafreespace) {
840 size_t growsize;
841
842 growsize = roundup((datalen + entrylen) - datafreespace, ATTR_BUF_SIZE);
843
844 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
845 if (!splitdata && (header->total_size + growsize) > ATTR_MAX_HDR_SIZE) {
846 growsize = ATTR_MAX_HDR_SIZE - header->total_size;
847 }
848
849 ainfo.filesize += growsize;
850 error = vnode_setsize(xvp, ainfo.filesize, 0, context);
851 if (error) {
852 printf("setxattr: VNOP_TRUNCATE error %d\n", error);
853 }
854 if (error)
855 goto out;
856
857 /*
858 * Move the resource fork out of the way.
859 */
860 if (ainfo.rsrcfork) {
861 if (ainfo.rsrcfork->length != 0) {
862 shift_data_down(xvp,
863 ainfo.rsrcfork->offset,
864 ainfo.rsrcfork->length,
865 growsize, context);
866 }
867 ainfo.rsrcfork->offset += growsize;
868 }
869 ainfo.finderinfo->length += growsize;
870 header->total_size += growsize;
871 }
872
873 /* Make space for a new entry. */
874 if (splitdata) {
875 shift_data_down(xvp,
876 header->data_start,
877 header->data_length,
878 entrylen, context);
879 } else {
880 bcopy((u_int8_t *)header + header->data_start,
881 (u_int8_t *)header + header->data_start + entrylen,
882 header->data_length);
883 }
884 header->data_start += entrylen;
885
886 /* Fix up entry data offsets. */
887 lastentry = entry;
888 for (entry = ainfo.attr_entry; entry != lastentry && ATTR_VALID(entry, ainfo); entry = ATTR_NEXT(entry)) {
889 entry->offset += entrylen;
890 }
891
892 /*
893 * If the attribute data area is entirely within
894 * the header buffer, then just update the buffer,
895 * otherwise we'll write it separately to the file.
896 */
897 if (splitdata) {
898 off_t offset;
899
900 /* Write new attribute data after the end of existing data. */
901 offset = header->data_start + header->data_length;
902 uio_setoffset(uio, offset);
903 error = VNOP_WRITE(xvp, uio, 0, context);
904 uio_setoffset(uio, 0);
905 if (error) {
906 printf("setxattr: VNOP_WRITE error %d\n", error);
907 goto out;
908 }
909 } else {
910 attrdata = (u_int8_t *)header + header->data_start + header->data_length;
911
912 error = uiomove((caddr_t)attrdata, datalen, uio);
913 if (error) {
914 printf("setxattr: uiomove error %d\n", error);
915 goto out;
916 }
917 }
918
919 /* Create the attribute entry. */
920 lastentry->length = datalen;
921 lastentry->offset = header->data_start + header->data_length;
922 lastentry->namelen = namelen;
923 lastentry->flags = 0;
924 bcopy(name, &lastentry->name[0], namelen);
925
926 /* Update the attributes header. */
927 header->num_attrs++;
928 header->data_length += datalen;
929
930 if (splitdata) {
931 /* Only write the entries, since the data was written separately. */
932 ainfo.iosize = ainfo.attrhdr->data_start;
933 } else {
934 /* The entry and data are both in the header; write them together. */
935 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
936 }
937 error = write_xattrinfo(&ainfo);
938 if (error) {
939 printf("setxattr: write_xattrinfo error %d\n", error);
940 }
941
942 out:
943 rel_xattrinfo(&ainfo);
944 close_xattrfile(xvp, fileflags, context);
945
946 /* Touch the change time if we changed an attribute. */
947 if (error == 0) {
948 struct vnode_attr va;
949
950 /* Re-write the mtime to cause a ctime change. */
951 VATTR_INIT(&va);
952 VATTR_WANTED(&va, va_modify_time);
953 if (vnode_getattr(vp, &va, context) == 0) {
954 VATTR_INIT(&va);
955 VATTR_SET(&va, va_modify_time, va.va_modify_time);
956 (void) vnode_setattr(vp, &va, context);
957 }
958 }
959 return (error);
960 }
961
962
963 /*
964 * Remove an extended attribute.
965 */
966 static int
967 default_removexattr(vnode_t vp, const char *name, __unused int options, vfs_context_t context)
968 {
969 vnode_t xvp = NULL;
970 attr_info_t ainfo;
971 attr_header_t *header;
972 attr_entry_t *entry;
973 attr_entry_t *oldslot;
974 u_int8_t *attrdata;
975 u_int32_t dataoff;
976 size_t datalen;
977 size_t entrylen;
978 int namelen;
979 int found = 0, lastone = 0;
980 int i;
981 int splitdata;
982 int attrcount = 0;
983 int isrsrcfork;
984 int fileflags;
985 int error;
986
987 fileflags = FREAD | FWRITE;
988 if (bcmp(name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
989 isrsrcfork = 1;
990 /*
991 * Open the file locked (exclusive) since the Carbon
992 * File Manager may have the Apple Double file open
993 * and could be changing the resource fork.
994 */
995 fileflags |= O_EXLOCK;
996 } else {
997 isrsrcfork = 0;
998 }
999
1000 if ((error = open_xattrfile(vp, fileflags, &xvp, context))) {
1001 return (error);
1002 }
1003 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
1004 close_xattrfile(xvp, fileflags, context);
1005 return (error);
1006 }
1007 if (ainfo.attrhdr)
1008 attrcount += ainfo.attrhdr->num_attrs;
1009 if (ainfo.rsrcfork)
1010 ++attrcount;
1011 if (ainfo.finderinfo && !ainfo.emptyfinderinfo)
1012 ++attrcount;
1013
1014 /* Clear the Finder Info. */
1015 if (bcmp(name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
1016 if (ainfo.finderinfo == NULL || ainfo.emptyfinderinfo) {
1017 error = ENOATTR;
1018 goto out;
1019 }
1020 /* On removal of last attribute the ._ file is removed. */
1021 if (--attrcount == 0)
1022 goto out;
1023 attrdata = (u_int8_t *)ainfo.filehdr + ainfo.finderinfo->offset;
1024 bzero((caddr_t)attrdata, FINDERINFOSIZE);
1025 ainfo.iosize = sizeof(attr_header_t);
1026 error = write_xattrinfo(&ainfo);
1027 goto out;
1028 }
1029
1030 /* Clear the Resource Fork. */
1031 if (isrsrcfork) {
1032 if (!vnode_isreg(vp)) {
1033 error = EPERM;
1034 goto out;
1035 }
1036 if (ainfo.rsrcfork == NULL || ainfo.rsrcfork->length == 0) {
1037 error = ENOATTR;
1038 goto out;
1039 }
1040 /* On removal of last attribute the ._ file is removed. */
1041 if (--attrcount == 0)
1042 goto out;
1043 /*
1044 * XXX
1045 * If the resource fork isn't the last AppleDouble
1046 * entry then the space needs to be reclaimed by
1047 * shifting the entries after the resource fork.
1048 */
1049 if ((ainfo.rsrcfork->offset + ainfo.rsrcfork->length) == ainfo.filesize) {
1050 ainfo.filesize -= ainfo.rsrcfork->length;
1051 error = vnode_setsize(xvp, ainfo.filesize, 0, context);
1052 }
1053 if (error == 0) {
1054 ainfo.rsrcfork->length = 0;
1055 ainfo.iosize = sizeof(attr_header_t);
1056 error = write_xattrinfo(&ainfo);
1057 }
1058 goto out;
1059 }
1060
1061 if (ainfo.attrhdr == NULL) {
1062 error = ENOATTR;
1063 goto out;
1064 }
1065 namelen = strlen(name) + 1;
1066 header = ainfo.attrhdr;
1067 entry = ainfo.attr_entry;
1068
1069 /*
1070 * See if this attribute exists.
1071 */
1072 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
1073 if (strncmp(entry->name, name, namelen) == 0) {
1074 found = 1;
1075 if ((i+1) == header->num_attrs)
1076 lastone = 1;
1077 break;
1078 }
1079 entry = ATTR_NEXT(entry);
1080 }
1081 if (!found) {
1082 error = ENOATTR;
1083 goto out;
1084 }
1085 /* On removal of last attribute the ._ file is removed. */
1086 if (--attrcount == 0)
1087 goto out;
1088
1089 datalen = entry->length;
1090 dataoff = entry->offset;
1091 entrylen = ATTR_ENTRY_LENGTH(namelen);
1092 if ((header->data_start + header->data_length) > ATTR_MAX_HDR_SIZE)
1093 splitdata = 1;
1094 else
1095 splitdata = 0;
1096
1097 /* Remove the attribute entry. */
1098 if (!lastone) {
1099 bcopy((u_int8_t *)entry + entrylen, (u_int8_t *)entry,
1100 ((size_t)header + header->data_start) - ((size_t)entry + entrylen));
1101 }
1102
1103 /* Adjust the attribute data. */
1104 if (splitdata) {
1105 shift_data_up(xvp,
1106 header->data_start,
1107 dataoff - header->data_start,
1108 entrylen,
1109 context);
1110 if (!lastone) {
1111 shift_data_up(xvp,
1112 dataoff + datalen,
1113 (header->data_start + header->data_length) - (dataoff + datalen),
1114 datalen + entrylen,
1115 context);
1116 }
1117 /* XXX write zeros to freed space ? */
1118 ainfo.iosize = ainfo.attrhdr->data_start - entrylen;
1119 } else {
1120
1121
1122 bcopy((u_int8_t *)header + header->data_start,
1123 (u_int8_t *)header + header->data_start - entrylen,
1124 dataoff - header->data_start);
1125 if (!lastone) {
1126 bcopy((u_int8_t *)header + dataoff + datalen,
1127 (u_int8_t *)header + dataoff - entrylen,
1128 (header->data_start + header->data_length) - (dataoff + datalen));
1129 }
1130 bzero (((u_int8_t *)header + header->data_start + header->data_length) - (datalen + entrylen), (datalen + entrylen));
1131 ainfo.iosize = ainfo.attrhdr->data_start + ainfo.attrhdr->data_length;
1132 }
1133
1134 /* Adjust the header values and entry offsets. */
1135 header->num_attrs--;
1136 header->data_start -= entrylen;
1137 header->data_length -= datalen;
1138
1139 oldslot = entry;
1140 entry = ainfo.attr_entry;
1141 for (i = 0; i < header->num_attrs && ATTR_VALID(entry, ainfo); i++) {
1142 entry->offset -= entrylen;
1143 if (entry >= oldslot)
1144 entry->offset -= datalen;
1145 entry = ATTR_NEXT(entry);
1146 }
1147 error = write_xattrinfo(&ainfo);
1148 if (error) {
1149 printf("removexattr: write_xattrinfo error %d\n", error);
1150 }
1151 out:
1152 rel_xattrinfo(&ainfo);
1153
1154 /* When there are no more attributes remove the ._ file. */
1155 if (attrcount == 0) {
1156 if (fileflags & O_EXLOCK)
1157 (void) unlock_xattrfile(xvp, context);
1158 VNOP_CLOSE(xvp, fileflags, context);
1159 vnode_rele(xvp);
1160 error = remove_xattrfile(xvp, context);
1161 vnode_put(xvp);
1162 } else {
1163 close_xattrfile(xvp, fileflags, context);
1164 }
1165 /* Touch the change time if we changed an attribute. */
1166 if (error == 0) {
1167 struct vnode_attr va;
1168
1169 /* Re-write the mtime to cause a ctime change. */
1170 VATTR_INIT(&va);
1171 VATTR_WANTED(&va, va_modify_time);
1172 if (vnode_getattr(vp, &va, context) == 0) {
1173 VATTR_INIT(&va);
1174 VATTR_SET(&va, va_modify_time, va.va_modify_time);
1175 (void) vnode_setattr(vp, &va, context);
1176 }
1177 }
1178 return (error);
1179
1180 }
1181
1182
1183 /*
1184 * Retrieve the list of extended attribute names.
1185 */
1186 static int
1187 default_listxattr(vnode_t vp, uio_t uio, size_t *size, __unused int options, vfs_context_t context)
1188 {
1189 vnode_t xvp = NULL;
1190 attr_info_t ainfo;
1191 attr_entry_t *entry;
1192 int i, count;
1193 int error;
1194
1195 /*
1196 * We do not zero "*size" here as we don't want to stomp a size set when
1197 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
1198 * system call layer, up in listxattr or flistxattr.
1199 */
1200
1201 if ((error = open_xattrfile(vp, FREAD, &xvp, context))) {
1202 if (error == ENOATTR)
1203 error = 0;
1204 return (error);
1205 }
1206 if ((error = get_xattrinfo(xvp, 0, &ainfo, context))) {
1207 close_xattrfile(xvp, FREAD, context);
1208 return (error);
1209 }
1210
1211 /* Check for Finder Info. */
1212 if (ainfo.finderinfo && !ainfo.emptyfinderinfo) {
1213 if (uio == NULL) {
1214 *size += sizeof(XATTR_FINDERINFO_NAME);
1215 } else if (uio_resid(uio) < sizeof(XATTR_FINDERINFO_NAME)) {
1216 error = ERANGE;
1217 goto out;
1218 } else {
1219 error = uiomove((caddr_t)XATTR_FINDERINFO_NAME,
1220 sizeof(XATTR_FINDERINFO_NAME), uio);
1221 if (error) {
1222 error = ERANGE;
1223 goto out;
1224 }
1225 }
1226 }
1227
1228 /* Check for Resource Fork. */
1229 if (vnode_isreg(vp) && ainfo.rsrcfork) {
1230 if (uio == NULL) {
1231 *size += sizeof(XATTR_RESOURCEFORK_NAME);
1232 } else if (uio_resid(uio) < sizeof(XATTR_RESOURCEFORK_NAME)) {
1233 error = ERANGE;
1234 goto out;
1235 } else {
1236 error = uiomove((caddr_t)XATTR_RESOURCEFORK_NAME,
1237 sizeof(XATTR_RESOURCEFORK_NAME), uio);
1238 if (error) {
1239 error = ERANGE;
1240 goto out;
1241 }
1242 }
1243 }
1244
1245 /* Check for attributes. */
1246 if (ainfo.attrhdr) {
1247 count = ainfo.attrhdr->num_attrs;
1248 for (i = 0, entry = ainfo.attr_entry; i < count && ATTR_VALID(entry, ainfo); i++) {
1249 if (xattr_protected(entry->name) ||
1250 xattr_validatename(entry->name) != 0) {
1251 entry = ATTR_NEXT(entry);
1252 continue;
1253 }
1254 if (uio == NULL) {
1255 *size += entry->namelen;
1256 entry = ATTR_NEXT(entry);
1257 continue;
1258 }
1259 if (uio_resid(uio) < entry->namelen) {
1260 error = ERANGE;
1261 break;
1262 }
1263 error = uiomove((caddr_t) entry->name, entry->namelen, uio);
1264 if (error) {
1265 if (error != EFAULT)
1266 error = ERANGE;
1267 break;
1268 }
1269 entry = ATTR_NEXT(entry);
1270 }
1271 }
1272 out:
1273 rel_xattrinfo(&ainfo);
1274 close_xattrfile(xvp, FREAD, context);
1275
1276 return (error);
1277 }
1278
1279 static int
1280 open_xattrfile(vnode_t vp, int fileflags, vnode_t *xvpp, vfs_context_t context)
1281 {
1282 vnode_t xvp = NULLVP;
1283 vnode_t dvp = NULLVP;
1284 struct vnode_attr va;
1285 struct nameidata nd;
1286 char smallname[64];
1287 char *filename = NULL;
1288 char *basename = NULL;
1289 size_t len;
1290 errno_t error;
1291 int opened = 0;
1292 int referenced = 0;
1293
1294 if (vnode_isvroot(vp) && vnode_isdir(vp)) {
1295 /*
1296 * For the root directory use "._." to hold the attributes.
1297 */
1298 filename = &smallname[0];
1299 sprintf(filename, "%s%s", ATTR_FILE_PREFIX, ".");
1300 dvp = vp; /* the "._." file resides in the root dir */
1301 goto lookup;
1302 }
1303 if ( (dvp = vnode_getparent(vp)) == NULLVP) {
1304 error = ENOATTR;
1305 goto out;
1306 }
1307 if ( (basename = vnode_getname(vp)) == NULL) {
1308 error = ENOATTR;
1309 goto out;
1310 }
1311
1312 /* "._" Attribute files cannot have attributes */
1313 if (vp->v_type == VREG && strlen(basename) > 2 &&
1314 basename[0] == '.' && basename[1] == '_') {
1315 error = EPERM;
1316 goto out;
1317 }
1318 filename = &smallname[0];
1319 len = snprintf(filename, sizeof(smallname), "%s%s", ATTR_FILE_PREFIX, basename);
1320 if (len >= sizeof(smallname)) {
1321 len++; /* snprintf result doesn't include '\0' */
1322 MALLOC(filename, char *, len, M_TEMP, M_WAITOK);
1323 len = snprintf(filename, len, "%s%s", ATTR_FILE_PREFIX, basename);
1324 }
1325 /*
1326 * Note that the lookup here does not authorize. Since we are looking
1327 * up in the same directory that we already have the file vnode in,
1328 * we must have been given the file vnode legitimately. Read/write
1329 * access has already been authorized in layers above for calls from
1330 * userspace, and the authorization code using this path to read
1331 * file security from the EA must always get access
1332 */
1333 lookup:
1334 NDINIT(&nd, LOOKUP, LOCKLEAF | NOFOLLOW | USEDVP | DONOTAUTH, UIO_SYSSPACE,
1335 CAST_USER_ADDR_T(filename), context);
1336 nd.ni_dvp = dvp;
1337
1338 if (fileflags & O_CREAT) {
1339 nd.ni_cnd.cn_nameiop = CREATE;
1340 if (dvp != vp) {
1341 nd.ni_cnd.cn_flags |= LOCKPARENT;
1342 }
1343 if ( (error = namei(&nd))) {
1344 nd.ni_dvp = NULLVP;
1345 error = ENOATTR;
1346 goto out;
1347 }
1348 if ( (xvp = nd.ni_vp) == NULLVP) {
1349 uid_t uid;
1350 gid_t gid;
1351 mode_t umode;
1352
1353 /*
1354 * Pick up uid/gid/mode from target file.
1355 */
1356 VATTR_INIT(&va);
1357 VATTR_WANTED(&va, va_uid);
1358 VATTR_WANTED(&va, va_gid);
1359 VATTR_WANTED(&va, va_mode);
1360 if (VNOP_GETATTR(vp, &va, context) == 0 &&
1361 VATTR_IS_SUPPORTED(&va, va_uid) &&
1362 VATTR_IS_SUPPORTED(&va, va_gid) &&
1363 VATTR_IS_SUPPORTED(&va, va_mode)) {
1364 uid = va.va_uid;
1365 gid = va.va_gid;
1366 umode = va.va_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
1367 } else /* fallback values */ {
1368 uid = KAUTH_UID_NONE;
1369 gid = KAUTH_GID_NONE;
1370 umode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
1371 }
1372
1373 VATTR_INIT(&va);
1374 VATTR_SET(&va, va_type, VREG);
1375 VATTR_SET(&va, va_mode, umode);
1376 if (uid != KAUTH_UID_NONE)
1377 VATTR_SET(&va, va_uid, uid);
1378 if (gid != KAUTH_GID_NONE)
1379 VATTR_SET(&va, va_gid, gid);
1380
1381 error = vn_create(dvp, &nd.ni_vp, &nd.ni_cnd, &va,
1382 VN_CREATE_NOAUTH | VN_CREATE_NOINHERIT,
1383 context);
1384 if (error == 0)
1385 xvp = nd.ni_vp;
1386 }
1387 nameidone(&nd);
1388 if (dvp != vp) {
1389 vnode_put(dvp); /* drop iocount from LOCKPARENT request above */
1390 }
1391 if (error)
1392 goto out;
1393 } else {
1394 if ((error = namei(&nd))) {
1395 nd.ni_dvp = NULLVP;
1396 error = ENOATTR;
1397 goto out;
1398 }
1399 xvp = nd.ni_vp;
1400 nameidone(&nd);
1401 }
1402 nd.ni_dvp = NULLVP;
1403
1404 if (xvp->v_type != VREG) {
1405 error = ENOATTR;
1406 goto out;
1407 }
1408 /*
1409 * Owners must match.
1410 */
1411 VATTR_INIT(&va);
1412 VATTR_WANTED(&va, va_uid);
1413 if (VNOP_GETATTR(vp, &va, context) == 0 && VATTR_IS_SUPPORTED(&va, va_uid)) {
1414 uid_t owner = va.va_uid;
1415
1416 VATTR_INIT(&va);
1417 VATTR_WANTED(&va, va_uid);
1418 if (VNOP_GETATTR(xvp, &va, context) == 0 && (owner != va.va_uid)) {
1419 error = ENOATTR; /* don't use this "._" file */
1420 goto out;
1421 }
1422 }
1423
1424 if ( (error = VNOP_OPEN(xvp, fileflags, context))) {
1425 error = ENOATTR;
1426 goto out;
1427 }
1428 opened = 1;
1429
1430 if ((error = vnode_ref(xvp))) {
1431 goto out;
1432 }
1433 referenced = 1;
1434
1435 /* If create was requested, make sure file header exists. */
1436 if (fileflags & O_CREAT) {
1437 VATTR_INIT(&va);
1438 VATTR_WANTED(&va, va_data_size);
1439 VATTR_WANTED(&va, va_fileid);
1440 VATTR_WANTED(&va, va_nlink);
1441 if ( (error = vnode_getattr(xvp, &va, context)) != 0) {
1442 error = EPERM;
1443 goto out;
1444 }
1445
1446 /* If the file is empty then add a default header. */
1447 if (va.va_data_size == 0) {
1448 /* Don't adopt hard-linked "._" files. */
1449 if (VATTR_IS_SUPPORTED(&va, va_nlink) && va.va_nlink > 1) {
1450 error = EPERM;
1451 goto out;
1452 }
1453 if ( (error = create_xattrfile(xvp, (u_int32_t)va.va_fileid, context)))
1454 goto out;
1455 }
1456 }
1457 /* Apply file locking if requested. */
1458 if (fileflags & (O_EXLOCK | O_SHLOCK)) {
1459 short locktype;
1460
1461 locktype = (fileflags & O_EXLOCK) ? F_WRLCK : F_RDLCK;
1462 error = lock_xattrfile(xvp, locktype, context);
1463 }
1464 out:
1465 if (dvp && (dvp != vp)) {
1466 vnode_put(dvp);
1467 }
1468 if (basename) {
1469 vnode_putname(basename);
1470 }
1471 if (filename && filename != &smallname[0]) {
1472 FREE(filename, M_TEMP);
1473 }
1474 if (error) {
1475 if (xvp != NULLVP) {
1476 if (opened) {
1477 (void) VNOP_CLOSE(xvp, fileflags, context);
1478 }
1479 if (referenced) {
1480 (void) vnode_rele(xvp);
1481 }
1482 (void) vnode_put(xvp);
1483 xvp = NULLVP;
1484 }
1485 if ((error == ENOATTR) && (fileflags & O_CREAT)) {
1486 error = EPERM;
1487 }
1488 }
1489 *xvpp = xvp; /* return a referenced vnode */
1490 return (error);
1491 }
1492
1493 static void
1494 close_xattrfile(vnode_t xvp, int fileflags, vfs_context_t context)
1495 {
1496 // if (fileflags & FWRITE)
1497 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
1498
1499 if (fileflags & (O_EXLOCK | O_SHLOCK))
1500 (void) unlock_xattrfile(xvp, context);
1501
1502 (void) VNOP_CLOSE(xvp, fileflags, context);
1503 (void) vnode_rele(xvp);
1504 (void) vnode_put(xvp);
1505 }
1506
1507 static int
1508 remove_xattrfile(vnode_t xvp, vfs_context_t context)
1509 {
1510 vnode_t dvp;
1511 struct nameidata nd;
1512 char *path;
1513 int pathlen;
1514 int error = 0;
1515
1516 path = get_pathbuff();
1517 pathlen = MAXPATHLEN;
1518 vn_getpath(xvp, path, &pathlen);
1519
1520 NDINIT(&nd, DELETE, LOCKPARENT | NOFOLLOW | DONOTAUTH,
1521 UIO_SYSSPACE, CAST_USER_ADDR_T(path), context);
1522 error = namei(&nd);
1523 release_pathbuff(path);
1524 if (error) {
1525 return (error);
1526 }
1527 dvp = nd.ni_dvp;
1528 xvp = nd.ni_vp;
1529
1530 error = VNOP_REMOVE(dvp, xvp, &nd.ni_cnd, 0, context);
1531 nameidone(&nd);
1532 vnode_put(dvp);
1533 vnode_put(xvp);
1534
1535 return (error);
1536 }
1537
1538 static int
1539 get_xattrinfo(vnode_t xvp, int setting, attr_info_t *ainfop, vfs_context_t context)
1540 {
1541 uio_t auio = NULL;
1542 void * buffer = NULL;
1543 apple_double_header_t *filehdr;
1544 attr_header_t *attrhdr;
1545 struct vnode_attr va;
1546 size_t iosize;
1547 int i;
1548 int error;
1549
1550 bzero(ainfop, sizeof(attr_info_t));
1551 ainfop->filevp = xvp;
1552 ainfop->context = context;
1553 VATTR_INIT(&va);
1554 VATTR_WANTED(&va, va_data_size);
1555 VATTR_WANTED(&va, va_fileid);
1556 if ((error = vnode_getattr(xvp, &va, context))) {
1557 goto bail;
1558 }
1559 ainfop->filesize = va.va_data_size;
1560
1561 /* When setting attributes, allow room for the header to grow. */
1562 if (setting)
1563 iosize = ATTR_MAX_HDR_SIZE;
1564 else
1565 iosize = MIN(ATTR_MAX_HDR_SIZE, ainfop->filesize);
1566
1567 if (iosize == 0) {
1568 error = ENOATTR;
1569 goto bail;
1570 }
1571 ainfop->iosize = iosize;
1572 MALLOC(buffer, void *, iosize, M_TEMP, M_WAITOK);
1573 auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_READ);
1574 uio_addiov(auio, (uintptr_t)buffer, iosize);
1575
1576 /* Read the file header. */
1577 error = VNOP_READ(xvp, auio, 0, context);
1578 if (error) {
1579 goto bail;
1580 }
1581 ainfop->rawsize = iosize - uio_resid(auio);
1582 ainfop->rawdata = (u_int8_t *)buffer;
1583
1584 filehdr = (apple_double_header_t *)buffer;
1585
1586 /* Check for Apple Double file. */
1587 if (SWAP32(filehdr->magic) != ADH_MAGIC ||
1588 SWAP32(filehdr->version) != ADH_VERSION ||
1589 SWAP16(filehdr->numEntries) < 1 ||
1590 SWAP16(filehdr->numEntries) > 15) {
1591 error = ENOATTR;
1592 goto bail;
1593 }
1594 if (ADHDRSIZE + (SWAP16(filehdr->numEntries) * sizeof(apple_double_entry_t)) > ainfop->rawsize) {
1595 error = EINVAL;
1596 goto bail;
1597 }
1598
1599 swap_adhdr(filehdr);
1600 ainfop->filehdr = filehdr; /* valid AppleDouble header */
1601 /* rel_xattrinfo is responsible for freeing the header buffer */
1602 buffer = NULL;
1603
1604 /* Check the AppleDouble entries. */
1605 for (i = 0; i < filehdr->numEntries; ++i) {
1606 if (filehdr->entries[i].type == AD_FINDERINFO &&
1607 filehdr->entries[i].length > 0) {
1608 ainfop->finderinfo = &filehdr->entries[i];
1609 attrhdr = (attr_header_t *)filehdr;
1610
1611 if (bcmp((u_int8_t*)ainfop->filehdr + ainfop->finderinfo->offset,
1612 emptyfinfo, sizeof(emptyfinfo)) == 0) {
1613 ainfop->emptyfinderinfo = 1;
1614 }
1615
1616 if (i != 0) {
1617 continue;
1618 }
1619 /* See if we need to convert this AppleDouble file. */
1620 if (filehdr->entries[0].length == FINDERINFOSIZE) {
1621 size_t delta;
1622 size_t writesize;
1623
1624 if (!setting ||
1625 filehdr->entries[1].type != AD_RESOURCE ||
1626 filehdr->numEntries > 2) {
1627 continue; /* not expected layout */
1628 }
1629 delta = ATTR_BUF_SIZE - (filehdr->entries[0].offset + FINDERINFOSIZE);
1630 if (filehdr->entries[1].length) {
1631 /* Make some room. */
1632 shift_data_down(xvp,
1633 filehdr->entries[1].offset,
1634 filehdr->entries[1].length,
1635 delta, context);
1636 writesize = sizeof(attr_header_t);
1637 } else {
1638 rsrcfork_header_t *rsrcforkhdr;
1639
1640 vnode_setsize(xvp, filehdr->entries[1].offset + delta, 0, context);
1641
1642 /* Steal some space for an empty RF header. */
1643 delta -= sizeof(rsrcfork_header_t);
1644
1645 bzero(&attrhdr->appledouble.pad[0], delta);
1646 rsrcforkhdr = (rsrcfork_header_t *)((char *)filehdr + filehdr->entries[1].offset + delta);
1647
1648 /* Fill in Empty Resource Fork Header. */
1649 init_empty_resource_fork(rsrcforkhdr);
1650
1651 filehdr->entries[1].length = sizeof(rsrcfork_header_t);
1652 writesize = ATTR_BUF_SIZE;
1653 }
1654 filehdr->entries[0].length += delta;
1655 filehdr->entries[1].offset += delta;
1656
1657 /* Fill in Attribute Header. */
1658 attrhdr->magic = ATTR_HDR_MAGIC;
1659 attrhdr->debug_tag = (u_int32_t)va.va_fileid;
1660 attrhdr->total_size = filehdr->entries[1].offset;
1661 attrhdr->data_start = sizeof(attr_header_t);
1662 attrhdr->data_length = 0;
1663 attrhdr->reserved[0] = 0;
1664 attrhdr->reserved[1] = 0;
1665 attrhdr->reserved[2] = 0;
1666 attrhdr->flags = 0;
1667 attrhdr->num_attrs = 0;
1668
1669 /* Push out new header */
1670 uio_reset(auio, 0, UIO_SYSSPACE32, UIO_WRITE);
1671 uio_addiov(auio, (uintptr_t)filehdr, writesize);
1672
1673 swap_adhdr(filehdr);
1674 swap_attrhdr(attrhdr);
1675 error = VNOP_WRITE(xvp, auio, 0, context);
1676 swap_adhdr(filehdr);
1677 /* The attribute header gets swapped below. */
1678 }
1679 if (SWAP32 (attrhdr->magic) != ATTR_HDR_MAGIC ||
1680 validate_attrhdr(attrhdr, ainfop->rawsize) != 0) {
1681 printf("get_xattrinfo: invalid attribute header\n");
1682 continue;
1683 }
1684 swap_attrhdr(attrhdr);
1685 ainfop->attrhdr = attrhdr; /* valid attribute header */
1686 ainfop->attr_entry = (attr_entry_t *)&attrhdr[1];
1687 continue;
1688 }
1689 if (filehdr->entries[i].type == AD_RESOURCE &&
1690 (filehdr->entries[i].length > sizeof(rsrcfork_header_t) || setting)) {
1691 ainfop->rsrcfork = &filehdr->entries[i];
1692 if (i != (filehdr->numEntries - 1)) {
1693 printf("get_xattrinfo: resource fork not last entry\n");
1694 ainfop->readonly = 1;
1695 }
1696 continue;
1697 }
1698 }
1699 error = 0;
1700 bail:
1701 if (auio != NULL)
1702 uio_free(auio);
1703 if (buffer != NULL)
1704 FREE(buffer, M_TEMP);
1705 return (error);
1706 }
1707
1708
1709 static int
1710 create_xattrfile(vnode_t xvp, u_int32_t fileid, vfs_context_t context)
1711 {
1712 attr_header_t *xah;
1713 rsrcfork_header_t *rsrcforkhdr;
1714 void * buffer;
1715 uio_t auio;
1716 int rsrcforksize;
1717 int error;
1718
1719 MALLOC(buffer, void *, ATTR_BUF_SIZE, M_TEMP, M_WAITOK);
1720 bzero(buffer, ATTR_BUF_SIZE);
1721
1722 xah = (attr_header_t *)buffer;
1723 auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_WRITE);
1724 uio_addiov(auio, (uintptr_t)buffer, ATTR_BUF_SIZE);
1725 rsrcforksize = sizeof(rsrcfork_header_t);
1726 rsrcforkhdr = (rsrcfork_header_t *) ((char *)buffer + ATTR_BUF_SIZE - rsrcforksize);
1727
1728 /* Fill in Apple Double Header. */
1729 xah->appledouble.magic = SWAP32 (ADH_MAGIC);
1730 xah->appledouble.version = SWAP32 (ADH_VERSION);
1731 xah->appledouble.numEntries = SWAP16 (2);
1732 xah->appledouble.entries[0].type = SWAP32 (AD_FINDERINFO);
1733 xah->appledouble.entries[0].offset = SWAP32 (offsetof(apple_double_header_t, finfo));
1734 xah->appledouble.entries[0].length = SWAP32 (ATTR_BUF_SIZE - offsetof(apple_double_header_t, finfo) - rsrcforksize);
1735 xah->appledouble.entries[1].type = SWAP32 (AD_RESOURCE);
1736 xah->appledouble.entries[1].offset = SWAP32 (ATTR_BUF_SIZE - rsrcforksize);
1737 xah->appledouble.entries[1].length = SWAP32 (rsrcforksize);
1738 bcopy(ADH_MACOSX, xah->appledouble.filler, sizeof(xah->appledouble.filler));
1739
1740 /* Fill in Attribute Header. */
1741 xah->magic = SWAP32 (ATTR_HDR_MAGIC);
1742 xah->debug_tag = SWAP32 (fileid);
1743 xah->total_size = SWAP32 (ATTR_BUF_SIZE - rsrcforksize);
1744 xah->data_start = SWAP32 (sizeof(attr_header_t));
1745
1746 /* Fill in Empty Resource Fork Header. */
1747 init_empty_resource_fork(rsrcforkhdr);
1748
1749 /* Push it out. */
1750 error = VNOP_WRITE(xvp, auio, 0, context);
1751
1752 uio_free(auio);
1753 FREE(buffer, M_TEMP);
1754
1755 return (error);
1756 }
1757
1758 static void
1759 init_empty_resource_fork(rsrcfork_header_t * rsrcforkhdr)
1760 {
1761 bzero(rsrcforkhdr, sizeof(rsrcfork_header_t));
1762 rsrcforkhdr->fh_DataOffset = SWAP32 (RF_FIRST_RESOURCE);
1763 rsrcforkhdr->fh_MapOffset = SWAP32 (RF_FIRST_RESOURCE);
1764 rsrcforkhdr->fh_MapLength = SWAP32 (RF_NULL_MAP_LENGTH);
1765 rsrcforkhdr->mh_DataOffset = SWAP32 (RF_FIRST_RESOURCE);
1766 rsrcforkhdr->mh_MapOffset = SWAP32 (RF_FIRST_RESOURCE);
1767 rsrcforkhdr->mh_MapLength = SWAP32 (RF_NULL_MAP_LENGTH);
1768 rsrcforkhdr->mh_Types = SWAP16 (RF_NULL_MAP_LENGTH - 2 );
1769 rsrcforkhdr->mh_Names = SWAP16 (RF_NULL_MAP_LENGTH);
1770 rsrcforkhdr->typeCount = SWAP16 (-1);
1771 bcopy(RF_EMPTY_TAG, rsrcforkhdr->systemData, sizeof(RF_EMPTY_TAG));
1772 }
1773
1774 static void
1775 rel_xattrinfo(attr_info_t *ainfop)
1776 {
1777 FREE(ainfop->filehdr, M_TEMP);
1778 bzero(ainfop, sizeof(attr_info_t));
1779 }
1780
1781 static int
1782 write_xattrinfo(attr_info_t *ainfop)
1783 {
1784 uio_t auio;
1785 int error;
1786
1787 auio = uio_create(1, 0, UIO_SYSSPACE32, UIO_WRITE);
1788 uio_addiov(auio, (uintptr_t)ainfop->filehdr, ainfop->iosize);
1789
1790 swap_adhdr(ainfop->filehdr);
1791 swap_attrhdr(ainfop->attrhdr);
1792
1793 error = VNOP_WRITE(ainfop->filevp, auio, 0, ainfop->context);
1794
1795 swap_adhdr(ainfop->filehdr);
1796 swap_attrhdr(ainfop->attrhdr);
1797 return (error);
1798 }
1799
1800 #if BYTE_ORDER == LITTLE_ENDIAN
1801 /*
1802 * Endian swap apple double header
1803 */
1804 static void
1805 swap_adhdr(apple_double_header_t *adh)
1806 {
1807 int count;
1808 int i;
1809
1810 count = (adh->magic == ADH_MAGIC) ? adh->numEntries : SWAP16(adh->numEntries);
1811
1812 adh->magic = SWAP32 (adh->magic);
1813 adh->version = SWAP32 (adh->version);
1814 adh->numEntries = SWAP16 (adh->numEntries);
1815
1816 for (i = 0; i < count; i++) {
1817 adh->entries[i].type = SWAP32 (adh->entries[i].type);
1818 adh->entries[i].offset = SWAP32 (adh->entries[i].offset);
1819 adh->entries[i].length = SWAP32 (adh->entries[i].length);
1820 }
1821 }
1822
1823 /*
1824 * Endian swap extended attributes header
1825 */
1826 static void
1827 swap_attrhdr(attr_header_t *ah)
1828 {
1829 attr_entry_t *ae;
1830 int count;
1831 int i;
1832
1833 count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs);
1834
1835 ah->magic = SWAP32 (ah->magic);
1836 ah->debug_tag = SWAP32 (ah->debug_tag);
1837 ah->total_size = SWAP32 (ah->total_size);
1838 ah->data_start = SWAP32 (ah->data_start);
1839 ah->data_length = SWAP32 (ah->data_length);
1840 ah->flags = SWAP16 (ah->flags);
1841 ah->num_attrs = SWAP16 (ah->num_attrs);
1842
1843 ae = (attr_entry_t *)(&ah[1]);
1844 for (i = 0; i < count; i++, ae = ATTR_NEXT(ae)) {
1845 ae->offset = SWAP32 (ae->offset);
1846 ae->length = SWAP32 (ae->length);
1847 ae->flags = SWAP16 (ae->flags);
1848 }
1849 }
1850 #endif
1851
1852 /*
1853 * Validate attributes header contents
1854 */
1855 static int
1856 validate_attrhdr(attr_header_t *ah, size_t bufsize)
1857 {
1858 attr_entry_t *ae;
1859 u_int8_t *bufend;
1860 int count;
1861 int i;
1862
1863 if (ah == NULL)
1864 return (EINVAL);
1865
1866 bufend = (u_int8_t *)ah + bufsize;
1867 count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs);
1868
1869 ae = (attr_entry_t *)(&ah[1]);
1870 for (i = 0; i < count && (u_int8_t *)ae < bufend; i++, ae = ATTR_NEXT(ae)) {
1871 }
1872 return (i < count ? EINVAL : 0);
1873 }
1874
1875 //
1876 // "start" & "end" are byte offsets in the file.
1877 // "to" is the byte offset we want to move the
1878 // data to. "to" should be > "start".
1879 //
1880 // we do the copy backwards to avoid problems if
1881 // there's an overlap.
1882 //
1883 static int
1884 shift_data_down(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context)
1885 {
1886 int ret, iolen;
1887 size_t chunk, orig_chunk;
1888 char *buff;
1889 off_t pos;
1890 ucred_t ucred = vfs_context_ucred(context);
1891 proc_t p = vfs_context_proc(context);
1892
1893 if (delta == 0 || len == 0) {
1894 return 0;
1895 }
1896
1897 chunk = 4096;
1898 if (len < chunk) {
1899 chunk = len;
1900 }
1901 orig_chunk = chunk;
1902
1903 if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk)) {
1904 return ENOMEM;
1905 }
1906
1907 for(pos=start+len-chunk; pos >= start; pos-=chunk) {
1908 ret = vn_rdwr(UIO_READ, xvp, buff, chunk, pos, UIO_SYSSPACE, IO_NODELOCKED, ucred, &iolen, p);
1909 if (iolen != 0) {
1910 printf("xattr:shift_data: error reading data @ %lld (read %d of %d) (%d)\n",
1911 pos, ret, chunk, ret);
1912 break;
1913 }
1914
1915 ret = vn_rdwr(UIO_WRITE, xvp, buff, chunk, pos + delta, UIO_SYSSPACE, IO_NODELOCKED, ucred, &iolen, p);
1916 if (iolen != 0) {
1917 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %d) (%d)\n",
1918 pos+delta, ret, chunk, ret);
1919 break;
1920 }
1921
1922 if ((pos - chunk) < start) {
1923 chunk = pos - start;
1924
1925 if (chunk == 0) { // we're all done
1926 break;
1927 }
1928 }
1929 }
1930 kmem_free(kernel_map, (vm_offset_t)buff, orig_chunk);
1931
1932 return 0;
1933 }
1934
1935
1936 static int
1937 shift_data_up(vnode_t xvp, off_t start, size_t len, off_t delta, vfs_context_t context)
1938 {
1939 int ret, iolen;
1940 size_t chunk, orig_chunk;
1941 char *buff;
1942 off_t pos;
1943 off_t end;
1944 ucred_t ucred = vfs_context_ucred(context);
1945 proc_t p = vfs_context_proc(context);
1946
1947 if (delta == 0 || len == 0) {
1948 return 0;
1949 }
1950
1951 chunk = 4096;
1952 if (len < chunk) {
1953 chunk = len;
1954 }
1955 orig_chunk = chunk;
1956 end = start + len;
1957
1958 if (kmem_alloc(kernel_map, (vm_offset_t *)&buff, chunk)) {
1959 return ENOMEM;
1960 }
1961
1962 for(pos = start; pos < end; pos += chunk) {
1963 ret = vn_rdwr(UIO_READ, xvp, buff, chunk, pos, UIO_SYSSPACE, IO_NODELOCKED, ucred, &iolen, p);
1964 if (iolen != 0) {
1965 printf("xattr:shift_data: error reading data @ %lld (read %d of %d) (%d)\n",
1966 pos, ret, chunk, ret);
1967 break;
1968 }
1969
1970 ret = vn_rdwr(UIO_WRITE, xvp, buff, chunk, pos - delta, UIO_SYSSPACE, IO_NODELOCKED, ucred, &iolen, p);
1971 if (iolen != 0) {
1972 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %d) (%d)\n",
1973 pos+delta, ret, chunk, ret);
1974 break;
1975 }
1976
1977 if ((pos + chunk) > end) {
1978 chunk = end - pos;
1979
1980 if (chunk == 0) { // we're all done
1981 break;
1982 }
1983 }
1984 }
1985 kmem_free(kernel_map, (vm_offset_t)buff, orig_chunk);
1986
1987 return 0;
1988 }
1989
1990 static int
1991 lock_xattrfile(vnode_t xvp, short locktype, vfs_context_t context)
1992 {
1993 struct flock lf;
1994
1995 lf.l_whence = SEEK_SET;
1996 lf.l_start = 0;
1997 lf.l_len = 0;
1998 lf.l_type = locktype; /* F_WRLCK or F_RDLCK */
1999 /* Note: id is just a kernel address that's not a proc */
2000 return VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_SETLK, &lf, F_FLOCK, context);
2001 }
2002
2003 static int
2004 unlock_xattrfile(vnode_t xvp, vfs_context_t context)
2005 {
2006 struct flock lf;
2007
2008 lf.l_whence = SEEK_SET;
2009 lf.l_start = 0;
2010 lf.l_len = 0;
2011 lf.l_type = F_UNLCK;
2012 /* Note: id is just a kernel address that's not a proc */
2013 return VNOP_ADVLOCK(xvp, (caddr_t)xvp, F_UNLCK, &lf, F_FLOCK, context);
2014 }
2015