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