2 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
23 #include <sys/param.h>
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>
34 #include <sys/utfconv.h>
35 #include <sys/vnode.h>
36 #include <sys/vnode_internal.h>
38 #include <sys/xattr.h>
40 #include <architecture/byte_order.h>
41 #include <vm/vm_kern.h>
44 * Default xattr support routines.
46 static int default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
47 int options
, vfs_context_t context
);
49 static int default_setxattr(vnode_t vp
, const char *name
, uio_t uio
,
50 int options
, vfs_context_t context
);
52 static int default_removexattr(vnode_t vp
, const char *name
, int options
, vfs_context_t context
);
54 static int default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
,
55 vfs_context_t context
);
60 * Retrieve the data of an extended attribute.
63 vn_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
64 int options
, vfs_context_t context
)
68 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
71 if ((error
= xattr_validatename(name
))) {
74 if (!(options
& XATTR_NOSECURITY
) && (error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
)))
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) {
84 error
= VNOP_GETXATTR(vp
, name
, uio
, size
, options
, context
);
85 if (error
== ENOTSUP
) {
87 * A filesystem may keep some EAs natively and return ENOTSUP for others.
88 * SMB returns ENOTSUP for finderinfo and resource forks.
90 error
= default_getxattr(vp
, name
, uio
, size
, options
, context
);
97 * Set the data of an extended attribute.
100 vn_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
104 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
107 if ((options
& (XATTR_REPLACE
|XATTR_CREATE
)) == (XATTR_REPLACE
|XATTR_CREATE
)) {
110 if ((error
= xattr_validatename(name
))) {
113 if (!(options
& XATTR_NOSECURITY
) && (error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
)))
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 ) {
123 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
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.
134 if (error
== EJUSTRETURN
) {
135 int native
= 0, dufile
= 0;
136 size_t sz
; /* not used */
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
)) {
144 if (options
& XATTR_REPLACE
&& !(native
|| dufile
)) {
149 * Having determined no CREATE/REPLACE error should result, we
150 * zero those bits, so both backing stores get written to.
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 ... */
156 #endif /* DUAL_EAS */
157 if (error
== ENOTSUP
) {
159 * A filesystem may keep some EAs natively and return ENOTSUP for others.
160 * SMB returns ENOTSUP for finderinfo and resource forks.
162 error
= default_setxattr(vp
, name
, uio
, options
, context
);
169 * Remove an extended attribute.
172 vn_removexattr(vnode_t vp
, const char * name
, int options
, vfs_context_t context
)
176 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
179 if ((error
= xattr_validatename(name
))) {
182 if (!(options
& XATTR_NOSECURITY
) && (error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
)))
184 error
= VNOP_REMOVEXATTR(vp
, name
, options
, context
);
185 if (error
== ENOTSUP
) {
187 * A filesystem may keep some EAs natively and return ENOTSUP for others.
188 * SMB returns ENOTSUP for finderinfo and resource forks.
190 error
= default_removexattr(vp
, name
, options
, context
);
192 } else if (error
== EJUSTRETURN
) {
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.
199 error
= default_removexattr(vp
, name
, options
, context
);
200 if (error
== ENOATTR
)
202 #endif /* DUAL_EAS */
209 * Retrieve the list of extended attribute names.
212 vn_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
, vfs_context_t context
)
216 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
219 if (!(options
& XATTR_NOSECURITY
) && (error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
)))
222 error
= VNOP_LISTXATTR(vp
, uio
, size
, options
, context
);
223 if (error
== ENOTSUP
) {
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.
230 error
= default_listxattr(vp
, uio
, size
, options
, context
);
237 xattr_validatename(const char *name
)
241 if (name
== NULL
|| name
[0] == '\0') {
244 namelen
= strlen(name
);
245 if (namelen
> XATTR_MAXNAMELEN
) {
246 return (ENAMETOOLONG
);
248 if (utf8_validatestr(name
, namelen
) != 0) {
256 * Determine whether an EA is a protected system attribute.
259 xattr_protected(const char *attrname
)
261 return(!strncmp(attrname
, "com.apple.system.", 17));
266 * Default Implementation (Non-native EA)
271 Typical "._" AppleDouble Header File layout:
272 ------------------------------------------------------------
277 .-- AD ENTRY[0] Finder Info Entry (must be first)
278 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
280 | ///////////// Fixed Size Data (32 bytes)
284 | ATTR ENTRY[1] --+--.
285 | ATTR ENTRY[2] --+--+--.
287 | ATTR ENTRY[N] --+--+--+--.
288 | ATTR DATA 0 <-' | | |
290 | ATTR DATA 1 <----' | |
292 | ATTR DATA 2 <-------' |
295 | ATTR DATA N <----------'
297 | Attribute Free Space
300 ///////////// Variable Sized Data
309 ------------------------------------------------------------
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.
319 * On Disk Data Structures
321 * Note: Motorola 68K alignment and big-endian.
323 * See RFC 1740 for additional information about the AppleDouble file format.
327 #define ADH_MAGIC 0x00051607
328 #define ADH_VERSION 0x00020000
329 #define ADH_MACOSX "Mac OS X "
332 * AppleDouble Entry ID's
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
352 #define ATTR_FILE_PREFIX "._"
353 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
355 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
357 /* Implementation Limits */
358 #define ATTR_MAX_SIZE (128*1024) /* 128K maximum attribute data size */
359 #define ATTR_MAX_HDR_SIZE 65536
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.
370 #pragma options align=mac68k
372 #define FINDERINFOSIZE 32
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
;
381 typedef struct apple_double_header
{
382 u_int32_t magic
; /* == ADH_MAGIC */
383 u_int32_t version
; /* format version: 2 = 0x00020000 */
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
;
391 #define ADHDRSIZE (4+4+16+2)
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 */
399 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
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];
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
;
432 u_int8_t mh_InMemoryAttr
;
438 #define RF_FIRST_RESOURCE 256
439 #define RF_NULL_MAP_LENGTH 30
440 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
442 #pragma options align=reset
444 /* Runtime information about the attribute file. */
445 typedef struct attr_info
{
446 vfs_context_t context
;
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
;
458 u_int8_t emptyfinderinfo
;
462 #define ATTR_SETTING 1
464 #define ATTR_ALIGN 3L /* Use four-byte alignment */
466 #define ATTR_ENTRY_LENGTH(namelen) \
467 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
469 #define ATTR_NEXT(ae) \
470 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
472 #define ATTR_VALID(ae, ai) \
473 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
476 #define SWAP16(x) NXSwapBigShortToHost((x))
477 #define SWAP32(x) NXSwapBigIntToHost((x))
478 #define SWAP64(x) NXSwapBigLongLongToHost((x))
481 static u_int32_t emptyfinfo
[8] = {0};
485 * Local support routines
487 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
489 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
491 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
493 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
495 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
497 static void rel_xattrinfo(attr_info_t
*ainfop
);
499 static int write_xattrinfo(attr_info_t
*ainfop
);
501 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
503 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
505 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
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
);
513 #define swap_adhdr(x)
514 #define swap_attrhdr(x)
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
);
523 * Retrieve the data of an extended attribute.
526 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
527 __unused
int options
, vfs_context_t context
)
531 attr_header_t
*header
;
542 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
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.
549 fileflags
|= O_SHLOCK
;
554 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
557 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
558 close_xattrfile(xvp
, fileflags
, context
);
562 /* Get the Finder Info. */
563 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
565 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
567 } else if (uio
== NULL
) {
568 *size
= FINDERINFOSIZE
;
570 } else if (uio_offset(uio
) != 0) {
572 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
575 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
576 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
581 /* Read the Resource Fork. */
583 if (!vnode_isreg(vp
)) {
585 } else if (ainfo
.rsrcfork
== NULL
) {
587 } else if (uio
== NULL
) {
588 *size
= (size_t)ainfo
.rsrcfork
->length
;
590 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
591 error
= VNOP_READ(xvp
, uio
, 0, context
);
593 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
598 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
602 if (uio_offset(uio
) != 0) {
607 namelen
= strlen(name
) + 1;
608 header
= ainfo
.attrhdr
;
609 entry
= ainfo
.attr_entry
;
611 * Search for attribute name in the header.
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
;
621 if (uio_resid(uio
) < datalen
) {
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
);
629 uio_setoffset(uio
, entry
->offset
);
630 error
= VNOP_READ(xvp
, uio
, 0, context
);
631 uio_setoffset(uio
, 0);
635 entry
= ATTR_NEXT(entry
);
638 rel_xattrinfo(&ainfo
);
639 close_xattrfile(xvp
, fileflags
, context
);
645 * Set the data of an extended attribute.
648 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
652 attr_header_t
*header
;
654 attr_entry_t
*lastentry
;
658 size_t datafreespace
;
666 datalen
= uio_resid(uio
);
667 namelen
= strlen(name
) + 1;
668 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
670 if (datalen
> ATTR_MAX_SIZE
) {
671 return (E2BIG
); /* EINVAL instead ? */
675 * Open the file locked since setting an attribute
676 * can change the layout of the Apple Double file.
678 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
679 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
682 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
683 close_xattrfile(xvp
, fileflags
, context
);
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
) {
696 /* attr doesn't exists and "replace" was specified? */
697 if (options
& XATTR_REPLACE
) {
702 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
706 if (ainfo
.finderinfo
) {
707 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
708 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
711 ainfo
.iosize
= sizeof(attr_header_t
);
712 error
= write_xattrinfo(&ainfo
);
719 /* Write the Resource Fork. */
720 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
723 if (!vnode_isreg(vp
)) {
727 if (ainfo
.rsrcfork
&& ainfo
.rsrcfork
->length
) {
728 /* attr exists and "create" was specified? */
729 if (options
& XATTR_CREATE
) {
734 /* attr doesn't exists and "replace" was specified? */
735 if (options
& XATTR_REPLACE
) {
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
);
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
);
755 if (ainfo
.attrhdr
== NULL
) {
759 header
= ainfo
.attrhdr
;
760 entry
= ainfo
.attr_entry
;
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 */
769 * See if attribute already exists.
771 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
772 if (strncmp(entry
->name
, name
, namelen
) == 0) {
776 entry
= ATTR_NEXT(entry
);
780 if (options
& XATTR_CREATE
) {
784 if (datalen
== entry
->length
) {
786 uio_setoffset(uio
, entry
->offset
);
787 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
788 uio_setoffset(uio
, 0);
790 printf("setxattr: VNOP_WRITE error %d\n", error
);
793 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
794 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
797 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
798 error
= write_xattrinfo(&ainfo
);
800 printf("setxattr: write_xattrinfo error %d\n", error
);
806 * Brute force approach - just remove old entry and set new entry.
809 rel_xattrinfo(&ainfo
);
810 close_xattrfile(xvp
, fileflags
, context
);
811 error
= default_removexattr(vp
, name
, options
, context
);
815 goto start
; /* start over */
820 if (options
& XATTR_REPLACE
) {
821 error
= ENOATTR
; /* nothing there to replace */
824 /* Check if header size limit has been reached. */
825 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
830 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
832 /* Check if we need more space. */
833 if ((datalen
+ entrylen
) > datafreespace
) {
836 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
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
;
843 ainfo
.filesize
+= growsize
;
844 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
846 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
852 * Move the resource fork out of the way.
854 if (ainfo
.rsrcfork
) {
855 if (ainfo
.rsrcfork
->length
!= 0) {
857 ainfo
.rsrcfork
->offset
,
858 ainfo
.rsrcfork
->length
,
861 ainfo
.rsrcfork
->offset
+= growsize
;
863 ainfo
.finderinfo
->length
+= growsize
;
864 header
->total_size
+= growsize
;
867 /* Make space for a new entry. */
874 bcopy((u_int8_t
*)header
+ header
->data_start
,
875 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
876 header
->data_length
);
878 header
->data_start
+= entrylen
;
880 /* Fix up entry data offsets. */
882 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
883 entry
->offset
+= entrylen
;
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.
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);
900 printf("setxattr: VNOP_WRITE error %d\n", error
);
904 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
906 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
908 printf("setxattr: uiomove error %d\n", error
);
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
);
920 /* Update the attributes header. */
922 header
->data_length
+= datalen
;
925 /* Only write the entries, since the data was written separately. */
926 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
928 /* The entry and data are both in the header; write them together. */
929 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
931 error
= write_xattrinfo(&ainfo
);
933 printf("setxattr: write_xattrinfo error %d\n", error
);
937 rel_xattrinfo(&ainfo
);
938 close_xattrfile(xvp
, fileflags
, context
);
940 /* Touch the change time if we changed an attribute. */
942 struct vnode_attr va
;
944 /* Re-write the mtime to cause a ctime change. */
946 VATTR_WANTED(&va
, va_modify_time
);
947 if (vnode_getattr(vp
, &va
, context
) == 0) {
949 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
950 (void) vnode_setattr(vp
, &va
, context
);
958 * Remove an extended attribute.
961 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
965 attr_header_t
*header
;
967 attr_entry_t
*oldslot
;
973 int found
= 0, lastone
= 0;
981 fileflags
= FREAD
| FWRITE
;
982 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
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.
989 fileflags
|= O_EXLOCK
;
994 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
997 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
998 close_xattrfile(xvp
, fileflags
, context
);
1002 attrcount
+= ainfo
.attrhdr
->num_attrs
;
1005 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
1008 /* Clear the Finder Info. */
1009 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1010 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1014 /* On removal of last attribute the ._ file is removed. */
1015 if (--attrcount
== 0)
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
);
1024 /* Clear the Resource Fork. */
1026 if (!vnode_isreg(vp
)) {
1030 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
1034 /* On removal of last attribute the ._ file is removed. */
1035 if (--attrcount
== 0)
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.
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
);
1048 ainfo
.rsrcfork
->length
= 0;
1049 ainfo
.iosize
= sizeof(attr_header_t
);
1050 error
= write_xattrinfo(&ainfo
);
1055 if (ainfo
.attrhdr
== NULL
) {
1059 namelen
= strlen(name
) + 1;
1060 header
= ainfo
.attrhdr
;
1061 entry
= ainfo
.attr_entry
;
1064 * See if this attribute exists.
1066 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1067 if (strncmp(entry
->name
, name
, namelen
) == 0) {
1069 if ((i
+1) == header
->num_attrs
)
1073 entry
= ATTR_NEXT(entry
);
1079 /* On removal of last attribute the ._ file is removed. */
1080 if (--attrcount
== 0)
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
)
1091 /* Remove the attribute entry. */
1093 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
1094 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
1097 /* Adjust the attribute data. */
1101 dataoff
- header
->data_start
,
1107 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
1111 /* XXX write zeros to freed space ? */
1112 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
1116 bcopy((u_int8_t
*)header
+ header
->data_start
,
1117 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
1118 dataoff
- header
->data_start
);
1120 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
1121 (u_int8_t
*)header
+ dataoff
- entrylen
,
1122 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
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
;
1128 /* Adjust the header values and entry offsets. */
1129 header
->num_attrs
--;
1130 header
->data_start
-= entrylen
;
1131 header
->data_length
-= datalen
;
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
);
1141 error
= write_xattrinfo(&ainfo
);
1143 printf("removexattr: write_xattrinfo error %d\n", error
);
1146 rel_xattrinfo(&ainfo
);
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
);
1154 error
= remove_xattrfile(xvp
, context
);
1157 close_xattrfile(xvp
, fileflags
, context
);
1159 /* Touch the change time if we changed an attribute. */
1161 struct vnode_attr va
;
1163 /* Re-write the mtime to cause a ctime change. */
1165 VATTR_WANTED(&va
, va_modify_time
);
1166 if (vnode_getattr(vp
, &va
, context
) == 0) {
1168 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
1169 (void) vnode_setattr(vp
, &va
, context
);
1178 * Retrieve the list of extended attribute names.
1181 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
1185 attr_entry_t
*entry
;
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.
1195 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
1196 if (error
== ENOATTR
)
1200 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1201 close_xattrfile(xvp
, FREAD
, context
);
1205 /* Check for Finder Info. */
1206 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1208 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
1209 } else if (uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
1213 error
= uiomove((caddr_t
)XATTR_FINDERINFO_NAME
,
1214 sizeof(XATTR_FINDERINFO_NAME
), uio
);
1222 /* Check for Resource Fork. */
1223 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
1225 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
1226 } else if (uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
1230 error
= uiomove((caddr_t
)XATTR_RESOURCEFORK_NAME
,
1231 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
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
);
1249 *size
+= entry
->namelen
;
1250 entry
= ATTR_NEXT(entry
);
1253 if (uio_resid(uio
) < entry
->namelen
) {
1257 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
1259 if (error
!= EFAULT
)
1263 entry
= ATTR_NEXT(entry
);
1267 rel_xattrinfo(&ainfo
);
1268 close_xattrfile(xvp
, FREAD
, context
);
1274 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
1276 vnode_t xvp
= NULLVP
;
1277 vnode_t dvp
= NULLVP
;
1278 struct vnode_attr va
;
1279 struct nameidata nd
;
1281 char *filename
= NULL
;
1282 char *basename
= NULL
;
1288 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
1290 * For the root directory use "._." to hold the attributes.
1292 filename
= &smallname
[0];
1293 sprintf(filename
, "%s%s", ATTR_FILE_PREFIX
, ".");
1294 dvp
= vp
; /* the "._." file resides in the root dir */
1297 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
1301 if ( (basename
= vnode_getname(vp
)) == NULL
) {
1306 /* "._" Attribute files cannot have attributes */
1307 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
1308 basename
[0] == '.' && basename
[1] == '_') {
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
);
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
1328 NDINIT(&nd
, LOOKUP
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
, UIO_SYSSPACE
,
1329 CAST_USER_ADDR_T(filename
), context
);
1332 if (fileflags
& O_CREAT
) {
1333 nd
.ni_cnd
.cn_nameiop
= CREATE
;
1334 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
1336 if ( (error
= namei(&nd
))) {
1341 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
1347 * Pick up uid/gid/mode from target file.
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
)) {
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
;
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
);
1374 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, &va
,
1375 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
,
1381 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
1386 if ((error
= namei(&nd
))) {
1396 if (xvp
->v_type
!= VREG
) {
1401 * Owners must match.
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
;
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 */
1416 if ( (error
= VNOP_OPEN(xvp
, fileflags
, context
))) {
1422 if ((error
= vnode_ref(xvp
))) {
1427 /* If create was requested, make sure file header exists. */
1428 if (fileflags
& O_CREAT
) {
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) {
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) {
1445 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
1449 /* Apply file locking if requested. */
1450 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
1453 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
1454 error
= lock_xattrfile(xvp
, locktype
, context
);
1457 if (dvp
&& (dvp
!= vp
)) {
1461 vnode_putname(basename
);
1463 if (filename
&& filename
!= &smallname
[0]) {
1464 FREE(filename
, M_TEMP
);
1467 if (xvp
!= NULLVP
) {
1469 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
1472 (void) vnode_rele(xvp
);
1474 (void) vnode_put(xvp
);
1477 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
1481 *xvpp
= xvp
; /* return a referenced vnode */
1486 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
1488 // if (fileflags & FWRITE)
1489 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
1491 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
1492 (void) unlock_xattrfile(xvp
, context
);
1494 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
1495 (void) vnode_rele(xvp
);
1496 (void) vnode_put(xvp
);
1500 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
1503 struct nameidata nd
;
1508 path
= get_pathbuff();
1509 pathlen
= MAXPATHLEN
;
1510 vn_getpath(xvp
, path
, &pathlen
);
1512 NDINIT(&nd
, DELETE
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
1513 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
1515 release_pathbuff(path
);
1522 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
1531 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
1534 void * buffer
= NULL
;
1535 apple_double_header_t
*filehdr
;
1536 attr_header_t
*attrhdr
;
1537 struct vnode_attr va
;
1542 bzero(ainfop
, sizeof(attr_info_t
));
1543 ainfop
->filevp
= xvp
;
1544 ainfop
->context
= context
;
1546 VATTR_WANTED(&va
, va_data_size
);
1547 VATTR_WANTED(&va
, va_fileid
);
1548 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
1551 ainfop
->filesize
= va
.va_data_size
;
1553 /* When setting attributes, allow room for the header to grow. */
1555 iosize
= ATTR_MAX_HDR_SIZE
;
1557 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
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
);
1568 /* Read the file header. */
1569 error
= VNOP_READ(xvp
, auio
, 0, context
);
1573 ainfop
->rawsize
= iosize
- uio_resid(auio
);
1574 ainfop
->rawdata
= (u_int8_t
*)buffer
;
1576 filehdr
= (apple_double_header_t
*)buffer
;
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) {
1586 if (ADHDRSIZE
+ (SWAP16(filehdr
->numEntries
) * sizeof(apple_double_entry_t
)) > ainfop
->rawsize
) {
1591 swap_adhdr(filehdr
);
1592 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
1593 /* rel_xattrinfo is responsible for freeing the header buffer */
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
;
1603 if (bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
,
1604 emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
1605 ainfop
->emptyfinderinfo
= 1;
1611 /* See if we need to convert this AppleDouble file. */
1612 if (filehdr
->entries
[0].length
== FINDERINFOSIZE
) {
1617 filehdr
->entries
[1].type
!= AD_RESOURCE
||
1618 filehdr
->numEntries
> 2) {
1619 continue; /* not expected layout */
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
,
1628 writesize
= sizeof(attr_header_t
);
1630 rsrcfork_header_t
*rsrcforkhdr
;
1632 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
1634 /* Steal some space for an empty RF header. */
1635 delta
-= sizeof(rsrcfork_header_t
);
1637 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
1638 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
1640 /* Fill in Empty Resource Fork Header. */
1641 init_empty_resource_fork(rsrcforkhdr
);
1643 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
1644 writesize
= ATTR_BUF_SIZE
;
1646 filehdr
->entries
[0].length
+= delta
;
1647 filehdr
->entries
[1].offset
+= delta
;
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;
1659 attrhdr
->num_attrs
= 0;
1661 /* Push out new header */
1662 uio_reset(auio
, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1663 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
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. */
1671 if (SWAP32 (attrhdr
->magic
) != ATTR_HDR_MAGIC
||
1672 validate_attrhdr(attrhdr
, ainfop
->rawsize
) != 0) {
1673 printf("get_xattrinfo: invalid attribute header\n");
1676 swap_attrhdr(attrhdr
);
1677 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
1678 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
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;
1696 FREE(buffer
, M_TEMP
);
1702 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
1705 rsrcfork_header_t
*rsrcforkhdr
;
1711 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
1712 bzero(buffer
, ATTR_BUF_SIZE
);
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
);
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
));
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
));
1738 /* Fill in Empty Resource Fork Header. */
1739 init_empty_resource_fork(rsrcforkhdr
);
1742 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
1745 FREE(buffer
, M_TEMP
);
1751 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
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
));
1767 rel_xattrinfo(attr_info_t
*ainfop
)
1769 FREE(ainfop
->filehdr
, M_TEMP
);
1770 bzero(ainfop
, sizeof(attr_info_t
));
1774 write_xattrinfo(attr_info_t
*ainfop
)
1779 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1780 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
1782 swap_adhdr(ainfop
->filehdr
);
1783 swap_attrhdr(ainfop
->attrhdr
);
1785 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
1787 swap_adhdr(ainfop
->filehdr
);
1788 swap_attrhdr(ainfop
->attrhdr
);
1792 #if BYTE_ORDER == LITTLE_ENDIAN
1794 * Endian swap apple double header
1797 swap_adhdr(apple_double_header_t
*adh
)
1802 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
1804 adh
->magic
= SWAP32 (adh
->magic
);
1805 adh
->version
= SWAP32 (adh
->version
);
1806 adh
->numEntries
= SWAP16 (adh
->numEntries
);
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
);
1816 * Endian swap extended attributes header
1819 swap_attrhdr(attr_header_t
*ah
)
1825 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
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
);
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
);
1845 * Validate attributes header contents
1848 validate_attrhdr(attr_header_t
*ah
, size_t bufsize
)
1858 bufend
= (u_int8_t
*)ah
+ bufsize
;
1859 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
1861 ae
= (attr_entry_t
*)(&ah
[1]);
1862 for (i
= 0; i
< count
&& (u_int8_t
*)ae
< bufend
; i
++, ae
= ATTR_NEXT(ae
)) {
1864 return (i
< count
? EINVAL
: 0);
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".
1872 // we do the copy backwards to avoid problems if
1873 // there's an overlap.
1876 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
1879 size_t chunk
, orig_chunk
;
1882 ucred_t ucred
= vfs_context_ucred(context
);
1883 proc_t p
= vfs_context_proc(context
);
1885 if (delta
== 0 || len
== 0) {
1895 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
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
);
1902 printf("xattr:shift_data: error reading data @ %lld (read %d of %d) (%d)\n",
1903 pos
, ret
, chunk
, ret
);
1907 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1909 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %d) (%d)\n",
1910 pos
+delta
, ret
, chunk
, ret
);
1914 if ((pos
- chunk
) < start
) {
1915 chunk
= pos
- start
;
1917 if (chunk
== 0) { // we're all done
1922 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
1929 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
1932 size_t chunk
, orig_chunk
;
1936 ucred_t ucred
= vfs_context_ucred(context
);
1937 proc_t p
= vfs_context_proc(context
);
1939 if (delta
== 0 || len
== 0) {
1950 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
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
);
1957 printf("xattr:shift_data: error reading data @ %lld (read %d of %d) (%d)\n",
1958 pos
, ret
, chunk
, ret
);
1962 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1964 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %d) (%d)\n",
1965 pos
+delta
, ret
, chunk
, ret
);
1969 if ((pos
+ chunk
) > end
) {
1972 if (chunk
== 0) { // we're all done
1977 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
1983 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
1987 lf
.l_whence
= SEEK_SET
;
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
);
1996 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
2000 lf
.l_whence
= SEEK_SET
;
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
);