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