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