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 <libkern/OSByteOrder.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) OSSwapBigToHostInt16((x))
477 #define SWAP32(x) OSSwapBigToHostInt32((x))
478 #define SWAP64(x) OSSwapBigToHostInt64((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
;
1335 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
1337 if ( (error
= namei(&nd
))) {
1342 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
1348 * Pick up uid/gid/mode from target file.
1351 VATTR_WANTED(&va
, va_uid
);
1352 VATTR_WANTED(&va
, va_gid
);
1353 VATTR_WANTED(&va
, va_mode
);
1354 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
1355 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
1356 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
1357 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
1360 umode
= va
.va_mode
& (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
1361 } else /* fallback values */ {
1362 uid
= KAUTH_UID_NONE
;
1363 gid
= KAUTH_GID_NONE
;
1364 umode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
;
1368 VATTR_SET(&va
, va_type
, VREG
);
1369 VATTR_SET(&va
, va_mode
, umode
);
1370 if (uid
!= KAUTH_UID_NONE
)
1371 VATTR_SET(&va
, va_uid
, uid
);
1372 if (gid
!= KAUTH_GID_NONE
)
1373 VATTR_SET(&va
, va_gid
, gid
);
1375 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, &va
,
1376 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
,
1383 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
1388 if ((error
= namei(&nd
))) {
1398 if (xvp
->v_type
!= VREG
) {
1403 * Owners must match.
1406 VATTR_WANTED(&va
, va_uid
);
1407 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
1408 uid_t owner
= va
.va_uid
;
1411 VATTR_WANTED(&va
, va_uid
);
1412 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
1413 error
= ENOATTR
; /* don't use this "._" file */
1418 if ( (error
= VNOP_OPEN(xvp
, fileflags
, context
))) {
1424 if ((error
= vnode_ref(xvp
))) {
1429 /* If create was requested, make sure file header exists. */
1430 if (fileflags
& O_CREAT
) {
1432 VATTR_WANTED(&va
, va_data_size
);
1433 VATTR_WANTED(&va
, va_fileid
);
1434 VATTR_WANTED(&va
, va_nlink
);
1435 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
1440 /* If the file is empty then add a default header. */
1441 if (va
.va_data_size
== 0) {
1442 /* Don't adopt hard-linked "._" files. */
1443 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
1447 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
1451 /* Apply file locking if requested. */
1452 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
1455 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
1456 error
= lock_xattrfile(xvp
, locktype
, context
);
1459 if (dvp
&& (dvp
!= vp
)) {
1463 vnode_putname(basename
);
1465 if (filename
&& filename
!= &smallname
[0]) {
1466 FREE(filename
, M_TEMP
);
1469 if (xvp
!= NULLVP
) {
1471 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
1474 (void) vnode_rele(xvp
);
1476 (void) vnode_put(xvp
);
1479 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
1483 *xvpp
= xvp
; /* return a referenced vnode */
1488 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
1490 // if (fileflags & FWRITE)
1491 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
1493 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
1494 (void) unlock_xattrfile(xvp
, context
);
1496 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
1497 (void) vnode_rele(xvp
);
1498 (void) vnode_put(xvp
);
1502 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
1505 struct nameidata nd
;
1510 path
= get_pathbuff();
1511 pathlen
= MAXPATHLEN
;
1512 vn_getpath(xvp
, path
, &pathlen
);
1514 NDINIT(&nd
, DELETE
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
1515 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
1517 release_pathbuff(path
);
1524 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
1533 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
1536 void * buffer
= NULL
;
1537 apple_double_header_t
*filehdr
;
1538 attr_header_t
*attrhdr
;
1539 struct vnode_attr va
;
1544 bzero(ainfop
, sizeof(attr_info_t
));
1545 ainfop
->filevp
= xvp
;
1546 ainfop
->context
= context
;
1548 VATTR_WANTED(&va
, va_data_size
);
1549 VATTR_WANTED(&va
, va_fileid
);
1550 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
1553 ainfop
->filesize
= va
.va_data_size
;
1555 /* When setting attributes, allow room for the header to grow. */
1557 iosize
= ATTR_MAX_HDR_SIZE
;
1559 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
1565 ainfop
->iosize
= iosize
;
1566 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
1567 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
1568 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
1570 /* Read the file header. */
1571 error
= VNOP_READ(xvp
, auio
, 0, context
);
1575 ainfop
->rawsize
= iosize
- uio_resid(auio
);
1576 ainfop
->rawdata
= (u_int8_t
*)buffer
;
1578 filehdr
= (apple_double_header_t
*)buffer
;
1580 /* Check for Apple Double file. */
1581 if (SWAP32(filehdr
->magic
) != ADH_MAGIC
||
1582 SWAP32(filehdr
->version
) != ADH_VERSION
||
1583 SWAP16(filehdr
->numEntries
) < 1 ||
1584 SWAP16(filehdr
->numEntries
) > 15) {
1588 if (ADHDRSIZE
+ (SWAP16(filehdr
->numEntries
) * sizeof(apple_double_entry_t
)) > ainfop
->rawsize
) {
1593 swap_adhdr(filehdr
);
1594 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
1595 /* rel_xattrinfo is responsible for freeing the header buffer */
1598 /* Check the AppleDouble entries. */
1599 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
1600 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
1601 filehdr
->entries
[i
].length
> 0) {
1602 ainfop
->finderinfo
= &filehdr
->entries
[i
];
1603 attrhdr
= (attr_header_t
*)filehdr
;
1605 if (bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
,
1606 emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
1607 ainfop
->emptyfinderinfo
= 1;
1613 /* See if we need to convert this AppleDouble file. */
1614 if (filehdr
->entries
[0].length
== FINDERINFOSIZE
) {
1619 filehdr
->entries
[1].type
!= AD_RESOURCE
||
1620 filehdr
->numEntries
> 2) {
1621 continue; /* not expected layout */
1623 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
1624 if (filehdr
->entries
[1].length
) {
1625 /* Make some room. */
1626 shift_data_down(xvp
,
1627 filehdr
->entries
[1].offset
,
1628 filehdr
->entries
[1].length
,
1630 writesize
= sizeof(attr_header_t
);
1632 rsrcfork_header_t
*rsrcforkhdr
;
1634 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
1636 /* Steal some space for an empty RF header. */
1637 delta
-= sizeof(rsrcfork_header_t
);
1639 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
1640 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
1642 /* Fill in Empty Resource Fork Header. */
1643 init_empty_resource_fork(rsrcforkhdr
);
1645 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
1646 writesize
= ATTR_BUF_SIZE
;
1648 filehdr
->entries
[0].length
+= delta
;
1649 filehdr
->entries
[1].offset
+= delta
;
1651 /* Fill in Attribute Header. */
1652 attrhdr
->magic
= ATTR_HDR_MAGIC
;
1653 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
1654 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
1655 attrhdr
->data_start
= sizeof(attr_header_t
);
1656 attrhdr
->data_length
= 0;
1657 attrhdr
->reserved
[0] = 0;
1658 attrhdr
->reserved
[1] = 0;
1659 attrhdr
->reserved
[2] = 0;
1661 attrhdr
->num_attrs
= 0;
1663 /* Push out new header */
1664 uio_reset(auio
, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1665 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
1667 swap_adhdr(filehdr
);
1668 swap_attrhdr(attrhdr
);
1669 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
1670 swap_adhdr(filehdr
);
1671 /* The attribute header gets swapped below. */
1673 if (SWAP32 (attrhdr
->magic
) != ATTR_HDR_MAGIC
||
1674 validate_attrhdr(attrhdr
, ainfop
->rawsize
) != 0) {
1675 printf("get_xattrinfo: invalid attribute header\n");
1678 swap_attrhdr(attrhdr
);
1679 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
1680 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
1683 if (filehdr
->entries
[i
].type
== AD_RESOURCE
&&
1684 (filehdr
->entries
[i
].length
> sizeof(rsrcfork_header_t
) || setting
)) {
1685 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
1686 if (i
!= (filehdr
->numEntries
- 1)) {
1687 printf("get_xattrinfo: resource fork not last entry\n");
1688 ainfop
->readonly
= 1;
1698 FREE(buffer
, M_TEMP
);
1704 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
1707 rsrcfork_header_t
*rsrcforkhdr
;
1713 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
1714 bzero(buffer
, ATTR_BUF_SIZE
);
1716 xah
= (attr_header_t
*)buffer
;
1717 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1718 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
1719 rsrcforksize
= sizeof(rsrcfork_header_t
);
1720 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
1722 /* Fill in Apple Double Header. */
1723 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
1724 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
1725 xah
->appledouble
.numEntries
= SWAP16 (2);
1726 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
1727 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
1728 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
1729 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
1730 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
1731 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
1732 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
1734 /* Fill in Attribute Header. */
1735 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
1736 xah
->debug_tag
= SWAP32 (fileid
);
1737 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
1738 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
1740 /* Fill in Empty Resource Fork Header. */
1741 init_empty_resource_fork(rsrcforkhdr
);
1744 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
1747 FREE(buffer
, M_TEMP
);
1753 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
1755 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
1756 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
1757 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
1758 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
1759 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
1760 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
1761 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
1762 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
1763 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
1764 rsrcforkhdr
->typeCount
= SWAP16 (-1);
1765 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
1769 rel_xattrinfo(attr_info_t
*ainfop
)
1771 FREE(ainfop
->filehdr
, M_TEMP
);
1772 bzero(ainfop
, sizeof(attr_info_t
));
1776 write_xattrinfo(attr_info_t
*ainfop
)
1781 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1782 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
1784 swap_adhdr(ainfop
->filehdr
);
1785 if (ainfop
->attrhdr
!= NULL
)
1786 swap_attrhdr(ainfop
->attrhdr
);
1788 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
1790 swap_adhdr(ainfop
->filehdr
);
1791 if (ainfop
->attrhdr
!= NULL
)
1792 swap_attrhdr(ainfop
->attrhdr
);
1796 #if BYTE_ORDER == LITTLE_ENDIAN
1798 * Endian swap apple double header
1801 swap_adhdr(apple_double_header_t
*adh
)
1806 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
1808 adh
->magic
= SWAP32 (adh
->magic
);
1809 adh
->version
= SWAP32 (adh
->version
);
1810 adh
->numEntries
= SWAP16 (adh
->numEntries
);
1812 for (i
= 0; i
< count
; i
++) {
1813 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
1814 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
1815 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
1820 * Endian swap extended attributes header
1823 swap_attrhdr(attr_header_t
*ah
)
1829 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
1831 ah
->magic
= SWAP32 (ah
->magic
);
1832 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
1833 ah
->total_size
= SWAP32 (ah
->total_size
);
1834 ah
->data_start
= SWAP32 (ah
->data_start
);
1835 ah
->data_length
= SWAP32 (ah
->data_length
);
1836 ah
->flags
= SWAP16 (ah
->flags
);
1837 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
1839 ae
= (attr_entry_t
*)(&ah
[1]);
1840 for (i
= 0; i
< count
; i
++, ae
= ATTR_NEXT(ae
)) {
1841 ae
->offset
= SWAP32 (ae
->offset
);
1842 ae
->length
= SWAP32 (ae
->length
);
1843 ae
->flags
= SWAP16 (ae
->flags
);
1849 * Validate attributes header contents
1852 validate_attrhdr(attr_header_t
*ah
, size_t bufsize
)
1862 bufend
= (u_int8_t
*)ah
+ bufsize
;
1863 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
1865 ae
= (attr_entry_t
*)(&ah
[1]);
1866 for (i
= 0; i
< count
&& (u_int8_t
*)ae
< bufend
; i
++, ae
= ATTR_NEXT(ae
)) {
1868 return (i
< count
? EINVAL
: 0);
1872 // "start" & "end" are byte offsets in the file.
1873 // "to" is the byte offset we want to move the
1874 // data to. "to" should be > "start".
1876 // we do the copy backwards to avoid problems if
1877 // there's an overlap.
1880 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
1883 size_t chunk
, orig_chunk
;
1886 ucred_t ucred
= vfs_context_ucred(context
);
1887 proc_t p
= vfs_context_proc(context
);
1889 if (delta
== 0 || len
== 0) {
1899 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
1903 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
1904 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1906 printf("xattr:shift_data: error reading data @ %lld (read %d of %d) (%d)\n",
1907 pos
, ret
, chunk
, ret
);
1911 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1913 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %d) (%d)\n",
1914 pos
+delta
, ret
, chunk
, ret
);
1918 if ((pos
- chunk
) < start
) {
1919 chunk
= pos
- start
;
1921 if (chunk
== 0) { // we're all done
1926 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
1933 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
1936 size_t chunk
, orig_chunk
;
1940 ucred_t ucred
= vfs_context_ucred(context
);
1941 proc_t p
= vfs_context_proc(context
);
1943 if (delta
== 0 || len
== 0) {
1954 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
1958 for(pos
= start
; pos
< end
; pos
+= chunk
) {
1959 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1961 printf("xattr:shift_data: error reading data @ %lld (read %d of %d) (%d)\n",
1962 pos
, ret
, chunk
, ret
);
1966 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1968 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %d) (%d)\n",
1969 pos
+delta
, ret
, chunk
, ret
);
1973 if ((pos
+ chunk
) > end
) {
1976 if (chunk
== 0) { // we're all done
1981 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
1987 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
1991 lf
.l_whence
= SEEK_SET
;
1994 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
1995 /* Note: id is just a kernel address that's not a proc */
1996 return VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
, context
);
2000 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
2004 lf
.l_whence
= SEEK_SET
;
2007 lf
.l_type
= F_UNLCK
;
2008 /* Note: id is just a kernel address that's not a proc */
2009 return VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
);