2 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <sys/param.h>
26 #include <sys/fcntl.h>
27 #include <sys/fsevents.h>
28 #include <sys/kernel.h>
29 #include <sys/kauth.h>
30 #include <sys/malloc.h>
31 #include <sys/namei.h>
32 #include <sys/proc_internal.h>
35 #include <sys/utfconv.h>
36 #include <sys/vnode.h>
37 #include <sys/vnode_internal.h>
39 #include <sys/xattr.h>
41 #include <architecture/byte_order.h>
42 #include <vm/vm_kern.h>
45 * Default xattr support routines.
47 static int default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
48 int options
, vfs_context_t context
);
50 static int default_setxattr(vnode_t vp
, const char *name
, uio_t uio
,
51 int options
, vfs_context_t context
);
53 static int default_removexattr(vnode_t vp
, const char *name
, int options
, vfs_context_t context
);
55 static int default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
,
56 vfs_context_t context
);
61 * Retrieve the data of an extended attribute.
64 vn_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
65 int options
, vfs_context_t context
)
69 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
72 if ((error
= xattr_validatename(name
))) {
75 if (!(options
& XATTR_NOSECURITY
) && (error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
)))
78 /* The offset can only be non-zero for resource forks. */
79 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
80 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
85 error
= VNOP_GETXATTR(vp
, name
, uio
, size
, options
, context
);
86 if (error
== ENOTSUP
) {
88 * A filesystem may keep some EAs natively and return ENOTSUP for others.
89 * SMB returns ENOTSUP for finderinfo and resource forks.
91 error
= default_getxattr(vp
, name
, uio
, size
, options
, context
);
98 * Set the data of an extended attribute.
101 vn_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
105 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
108 if ((options
& (XATTR_REPLACE
|XATTR_CREATE
)) == (XATTR_REPLACE
|XATTR_CREATE
)) {
111 if ((error
= xattr_validatename(name
))) {
114 if (!(options
& XATTR_NOSECURITY
) && (error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
)))
117 /* The offset can only be non-zero for resource forks. */
118 if (uio_offset(uio
) != 0 &&
119 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0 ) {
124 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
127 * An EJUSTRETURN is from a filesystem which keeps this xattr
128 * natively as well as in a dot-underscore file. In this case the
129 * EJUSTRETURN means the filesytem has done nothing, but identifies the
130 * EA as one which may be represented natively and/or in a DU, and
131 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
132 * in vn_setxattr can we do the getxattrs needed to ascertain whether
133 * the XATTR_{CREATE,REPLACE} should yield an error.
135 if (error
== EJUSTRETURN
) {
136 int native
= 0, dufile
= 0;
137 size_t sz
; /* not used */
139 native
= VNOP_GETXATTR(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
140 dufile
= default_getxattr(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
141 if (options
& XATTR_CREATE
&& (native
|| dufile
)) {
145 if (options
& XATTR_REPLACE
&& !(native
|| dufile
)) {
150 * Having determined no CREATE/REPLACE error should result, we
151 * zero those bits, so both backing stores get written to.
153 options
&= ~(XATTR_CREATE
| XATTR_REPLACE
);
154 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
155 /* the mainline path here is to have error==ENOTSUP ... */
157 #endif /* DUAL_EAS */
158 if (error
== ENOTSUP
) {
160 * A filesystem may keep some EAs natively and return ENOTSUP for others.
161 * SMB returns ENOTSUP for finderinfo and resource forks.
163 error
= default_setxattr(vp
, name
, uio
, options
, context
);
170 * Remove an extended attribute.
173 vn_removexattr(vnode_t vp
, const char * name
, int options
, vfs_context_t context
)
177 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
180 if ((error
= xattr_validatename(name
))) {
183 if (!(options
& XATTR_NOSECURITY
) && (error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
)))
185 error
= VNOP_REMOVEXATTR(vp
, name
, options
, context
);
186 if (error
== ENOTSUP
) {
188 * A filesystem may keep some EAs natively and return ENOTSUP for others.
189 * SMB returns ENOTSUP for finderinfo and resource forks.
191 error
= default_removexattr(vp
, name
, options
, context
);
193 } else if (error
== EJUSTRETURN
) {
195 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
196 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
197 * a native xattr, so failure to find it in a DU file during
198 * default_removexattr should not be considered an error.
200 error
= default_removexattr(vp
, name
, options
, context
);
201 if (error
== ENOATTR
)
203 #endif /* DUAL_EAS */
210 * Retrieve the list of extended attribute names.
213 vn_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
, vfs_context_t context
)
217 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
220 if (!(options
& XATTR_NOSECURITY
) && (error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
)))
223 error
= VNOP_LISTXATTR(vp
, uio
, size
, options
, context
);
224 if (error
== ENOTSUP
) {
226 * A filesystem may keep some but not all EAs natively, in which case
227 * the native EA names will have been uiomove-d out (or *size updated)
228 * and the default_listxattr here will finish the job. Note SMB takes
229 * advantage of this for its finder-info and resource forks.
231 error
= default_listxattr(vp
, uio
, size
, options
, context
);
238 xattr_validatename(const char *name
)
242 if (name
== NULL
|| name
[0] == '\0') {
245 namelen
= strlen(name
);
246 if (namelen
> XATTR_MAXNAMELEN
) {
247 return (ENAMETOOLONG
);
249 if (utf8_validatestr(name
, namelen
) != 0) {
257 * Determine whether an EA is a protected system attribute.
260 xattr_protected(const char *attrname
)
262 return(!strncmp(attrname
, "com.apple.system.", 17));
267 * Default Implementation (Non-native EA)
272 Typical "._" AppleDouble Header File layout:
273 ------------------------------------------------------------
278 .-- AD ENTRY[0] Finder Info Entry (must be first)
279 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
281 | ///////////// Fixed Size Data (32 bytes)
285 | ATTR ENTRY[1] --+--.
286 | ATTR ENTRY[2] --+--+--.
288 | ATTR ENTRY[N] --+--+--+--.
289 | ATTR DATA 0 <-' | | |
291 | ATTR DATA 1 <----' | |
293 | ATTR DATA 2 <-------' |
296 | ATTR DATA N <----------'
298 | Attribute Free Space
301 ///////////// Variable Sized Data
310 ------------------------------------------------------------
312 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
313 stored as part of the Finder Info. The length in the Finder
314 Info AppleDouble entry includes the length of the extended
315 attribute header, attribute entries, and attribute data.
320 * On Disk Data Structures
322 * Note: Motorola 68K alignment and big-endian.
324 * See RFC 1740 for additional information about the AppleDouble file format.
328 #define ADH_MAGIC 0x00051607
329 #define ADH_VERSION 0x00020000
330 #define ADH_MACOSX "Mac OS X "
333 * AppleDouble Entry ID's
335 #define AD_DATA 1 /* Data fork */
336 #define AD_RESOURCE 2 /* Resource fork */
337 #define AD_REALNAME 3 /* FileÕs name on home file system */
338 #define AD_COMMENT 4 /* Standard Mac comment */
339 #define AD_ICONBW 5 /* Mac black & white icon */
340 #define AD_ICONCOLOR 6 /* Mac color icon */
341 #define AD_UNUSED 7 /* Not used */
342 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
343 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
344 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
345 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
346 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
347 #define AD_AFPNAME 13 /* Short name on AFP server */
348 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
349 #define AD_AFPDIRID 15 /* AFP directory ID */
350 #define AD_ATTRIBUTES AD_FINDERINFO
353 #define ATTR_FILE_PREFIX "._"
354 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
356 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
358 /* Implementation Limits */
359 #define ATTR_MAX_SIZE (128*1024) /* 128K maximum attribute data size */
360 #define ATTR_MAX_HDR_SIZE 65536
362 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
363 * size supported (including the attribute entries). All of
364 * the attribute entries must reside within this limit. If
365 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
366 * boundry, then all of the attribute data I/O is performed
367 * seperately from the attribute header I/O.
371 #pragma options align=mac68k
373 #define FINDERINFOSIZE 32
375 typedef struct apple_double_entry
{
376 u_int32_t type
; /* entry type: see list, 0 invalid */
377 u_int32_t offset
; /* entry data offset from the beginning of the file. */
378 u_int32_t length
; /* entry data length in bytes. */
379 } apple_double_entry_t
;
382 typedef struct apple_double_header
{
383 u_int32_t magic
; /* == ADH_MAGIC */
384 u_int32_t version
; /* format version: 2 = 0x00020000 */
386 u_int16_t numEntries
; /* number of entries which follow */
387 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
388 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
389 u_int8_t pad
[2]; /* get better alignment inside attr_header */
390 } apple_double_header_t
;
392 #define ADHDRSIZE (4+4+16+2)
394 /* Entries are aligned on 4 byte boundaries */
395 typedef struct attr_entry
{
396 u_int32_t offset
; /* file offset to data */
397 u_int32_t length
; /* size of attribute data */
400 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
404 /* Header + entries must fit into 64K */
405 typedef struct attr_header
{
406 apple_double_header_t appledouble
;
407 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
408 u_int32_t debug_tag
; /* for debugging == file id of owning file */
409 u_int32_t total_size
; /* total size of attribute header + entries + data */
410 u_int32_t data_start
; /* file offset to attribute data area */
411 u_int32_t data_length
; /* length of attribute data area */
412 u_int32_t reserved
[3];
418 /* Empty Resource Fork Header */
419 typedef struct rsrcfork_header
{
420 u_int32_t fh_DataOffset
;
421 u_int32_t fh_MapOffset
;
422 u_int32_t fh_DataLength
;
423 u_int32_t fh_MapLength
;
424 u_int8_t systemData
[112];
425 u_int8_t appData
[128];
426 u_int32_t mh_DataOffset
;
427 u_int32_t mh_MapOffset
;
428 u_int32_t mh_DataLength
;
429 u_int32_t mh_MapLength
;
433 u_int8_t mh_InMemoryAttr
;
439 #define RF_FIRST_RESOURCE 256
440 #define RF_NULL_MAP_LENGTH 30
441 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
443 #pragma options align=reset
445 /* Runtime information about the attribute file. */
446 typedef struct attr_info
{
447 vfs_context_t context
;
452 size_t rawsize
; /* raw size of AppleDouble file */
453 apple_double_header_t
*filehdr
;
454 apple_double_entry_t
*finderinfo
;
455 apple_double_entry_t
*rsrcfork
;
456 attr_header_t
*attrhdr
;
457 attr_entry_t
*attr_entry
;
459 u_int8_t emptyfinderinfo
;
463 #define ATTR_SETTING 1
465 #define ATTR_ALIGN 3L /* Use four-byte alignment */
467 #define ATTR_ENTRY_LENGTH(namelen) \
468 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
470 #define ATTR_NEXT(ae) \
471 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
473 #define ATTR_VALID(ae, ai) \
474 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
477 #define SWAP16(x) NXSwapBigShortToHost((x))
478 #define SWAP32(x) NXSwapBigIntToHost((x))
479 #define SWAP64(x) NXSwapBigLongLongToHost((x))
482 static u_int32_t emptyfinfo
[8] = {0};
486 * Local support routines
488 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
490 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
492 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
494 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
496 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
498 static void rel_xattrinfo(attr_info_t
*ainfop
);
500 static int write_xattrinfo(attr_info_t
*ainfop
);
502 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
504 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
506 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
509 #if BYTE_ORDER == LITTLE_ENDIAN
510 static void swap_adhdr(apple_double_header_t
*adh
);
511 static void swap_attrhdr(attr_header_t
*ah
);
514 #define swap_adhdr(x)
515 #define swap_attrhdr(x)
518 static int validate_attrhdr(attr_header_t
*ah
, size_t bufsize
);
519 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
520 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
524 * Retrieve the data of an extended attribute.
527 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
528 __unused
int options
, vfs_context_t context
)
532 attr_header_t
*header
;
543 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
546 * Open the file locked (shared) since the Carbon
547 * File Manager may have the Apple Double file open
548 * and could be changing the resource fork.
550 fileflags
|= O_SHLOCK
;
555 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
558 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
559 close_xattrfile(xvp
, fileflags
, context
);
563 /* Get the Finder Info. */
564 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
566 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
568 } else if (uio
== NULL
) {
569 *size
= FINDERINFOSIZE
;
571 } else if (uio_offset(uio
) != 0) {
573 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
576 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
577 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
582 /* Read the Resource Fork. */
584 if (!vnode_isreg(vp
)) {
586 } else if (ainfo
.rsrcfork
== NULL
) {
588 } else if (uio
== NULL
) {
589 *size
= (size_t)ainfo
.rsrcfork
->length
;
591 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
592 error
= VNOP_READ(xvp
, uio
, 0, context
);
594 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
599 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
603 if (uio_offset(uio
) != 0) {
608 namelen
= strlen(name
) + 1;
609 header
= ainfo
.attrhdr
;
610 entry
= ainfo
.attr_entry
;
612 * Search for attribute name in the header.
614 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
615 if (strncmp(entry
->name
, name
, namelen
) == 0) {
616 datalen
= (size_t)entry
->length
;
622 if (uio_resid(uio
) < datalen
) {
626 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
627 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
628 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
630 uio_setoffset(uio
, entry
->offset
);
631 error
= VNOP_READ(xvp
, uio
, 0, context
);
632 uio_setoffset(uio
, 0);
636 entry
= ATTR_NEXT(entry
);
639 rel_xattrinfo(&ainfo
);
640 close_xattrfile(xvp
, fileflags
, context
);
646 * Set the data of an extended attribute.
649 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
653 attr_header_t
*header
;
655 attr_entry_t
*lastentry
;
659 size_t datafreespace
;
667 datalen
= uio_resid(uio
);
668 namelen
= strlen(name
) + 1;
669 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
671 if (datalen
> ATTR_MAX_SIZE
) {
672 return (E2BIG
); /* EINVAL instead ? */
676 * Open the file locked since setting an attribute
677 * can change the layout of the Apple Double file.
679 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
680 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
683 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
684 close_xattrfile(xvp
, fileflags
, context
);
688 /* Set the Finder Info. */
689 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
690 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
691 /* attr exists and "create" was specified? */
692 if (options
& XATTR_CREATE
) {
697 /* attr doesn't exists and "replace" was specified? */
698 if (options
& XATTR_REPLACE
) {
703 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
707 if (ainfo
.finderinfo
) {
708 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
709 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
712 ainfo
.iosize
= sizeof(attr_header_t
);
713 error
= write_xattrinfo(&ainfo
);
720 /* Write the Resource Fork. */
721 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
724 if (!vnode_isreg(vp
)) {
728 if (ainfo
.rsrcfork
&& ainfo
.rsrcfork
->length
) {
729 /* attr exists and "create" was specified? */
730 if (options
& XATTR_CREATE
) {
735 /* attr doesn't exists and "replace" was specified? */
736 if (options
& XATTR_REPLACE
) {
741 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
742 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
743 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
746 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
747 if (endoffset
> ainfo
.rsrcfork
->length
) {
748 ainfo
.rsrcfork
->length
= endoffset
;
749 ainfo
.iosize
= sizeof(attr_header_t
);
750 error
= write_xattrinfo(&ainfo
);
756 if (ainfo
.attrhdr
== NULL
) {
760 header
= ainfo
.attrhdr
;
761 entry
= ainfo
.attr_entry
;
763 /* Check if data area crosses the maximum header size. */
764 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
)
765 splitdata
= 1; /* do data I/O separately */
770 * See if attribute already exists.
772 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
773 if (strncmp(entry
->name
, name
, namelen
) == 0) {
777 entry
= ATTR_NEXT(entry
);
781 if (options
& XATTR_CREATE
) {
785 if (datalen
== entry
->length
) {
787 uio_setoffset(uio
, entry
->offset
);
788 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
789 uio_setoffset(uio
, 0);
791 printf("setxattr: VNOP_WRITE error %d\n", error
);
794 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
795 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
798 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
799 error
= write_xattrinfo(&ainfo
);
801 printf("setxattr: write_xattrinfo error %d\n", error
);
807 * Brute force approach - just remove old entry and set new entry.
810 rel_xattrinfo(&ainfo
);
811 close_xattrfile(xvp
, fileflags
, context
);
812 error
= default_removexattr(vp
, name
, options
, context
);
816 goto start
; /* start over */
821 if (options
& XATTR_REPLACE
) {
822 error
= ENOATTR
; /* nothing there to replace */
825 /* Check if header size limit has been reached. */
826 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
831 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
833 /* Check if we need more space. */
834 if ((datalen
+ entrylen
) > datafreespace
) {
837 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
839 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
840 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
841 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
844 ainfo
.filesize
+= growsize
;
845 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
847 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
853 * Move the resource fork out of the way.
855 if (ainfo
.rsrcfork
) {
856 if (ainfo
.rsrcfork
->length
!= 0) {
858 ainfo
.rsrcfork
->offset
,
859 ainfo
.rsrcfork
->length
,
862 ainfo
.rsrcfork
->offset
+= growsize
;
864 ainfo
.finderinfo
->length
+= growsize
;
865 header
->total_size
+= growsize
;
868 /* Make space for a new entry. */
875 bcopy((u_int8_t
*)header
+ header
->data_start
,
876 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
877 header
->data_length
);
879 header
->data_start
+= entrylen
;
881 /* Fix up entry data offsets. */
883 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
884 entry
->offset
+= entrylen
;
888 * If the attribute data area is entirely within
889 * the header buffer, then just update the buffer,
890 * otherwise we'll write it separately to the file.
895 /* Write new attribute data after the end of existing data. */
896 offset
= header
->data_start
+ header
->data_length
;
897 uio_setoffset(uio
, offset
);
898 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
899 uio_setoffset(uio
, 0);
901 printf("setxattr: VNOP_WRITE error %d\n", error
);
905 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
907 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
909 printf("setxattr: uiomove error %d\n", error
);
914 /* Create the attribute entry. */
915 lastentry
->length
= datalen
;
916 lastentry
->offset
= header
->data_start
+ header
->data_length
;
917 lastentry
->namelen
= namelen
;
918 lastentry
->flags
= 0;
919 bcopy(name
, &lastentry
->name
[0], namelen
);
921 /* Update the attributes header. */
923 header
->data_length
+= datalen
;
926 /* Only write the entries, since the data was written separately. */
927 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
929 /* The entry and data are both in the header; write them together. */
930 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
932 error
= write_xattrinfo(&ainfo
);
934 printf("setxattr: write_xattrinfo error %d\n", error
);
938 rel_xattrinfo(&ainfo
);
939 close_xattrfile(xvp
, fileflags
, context
);
941 /* Touch the change time if we changed an attribute. */
943 struct vnode_attr va
;
945 /* Re-write the mtime to cause a ctime change. */
947 VATTR_WANTED(&va
, va_modify_time
);
948 if (vnode_getattr(vp
, &va
, context
) == 0) {
950 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
951 (void) vnode_setattr(vp
, &va
, context
);
959 * Remove an extended attribute.
962 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
966 attr_header_t
*header
;
968 attr_entry_t
*oldslot
;
974 int found
= 0, lastone
= 0;
982 fileflags
= FREAD
| FWRITE
;
983 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
986 * Open the file locked (exclusive) since the Carbon
987 * File Manager may have the Apple Double file open
988 * and could be changing the resource fork.
990 fileflags
|= O_EXLOCK
;
995 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
998 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
999 close_xattrfile(xvp
, fileflags
, context
);
1003 attrcount
+= ainfo
.attrhdr
->num_attrs
;
1006 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
1009 /* Clear the Finder Info. */
1010 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1011 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1015 /* On removal of last attribute the ._ file is removed. */
1016 if (--attrcount
== 0)
1018 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1019 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
1020 ainfo
.iosize
= sizeof(attr_header_t
);
1021 error
= write_xattrinfo(&ainfo
);
1025 /* Clear the Resource Fork. */
1027 if (!vnode_isreg(vp
)) {
1031 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
1035 /* On removal of last attribute the ._ file is removed. */
1036 if (--attrcount
== 0)
1040 * If the resource fork isn't the last AppleDouble
1041 * entry then the space needs to be reclaimed by
1042 * shifting the entries after the resource fork.
1044 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
1045 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
1046 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1049 ainfo
.rsrcfork
->length
= 0;
1050 ainfo
.iosize
= sizeof(attr_header_t
);
1051 error
= write_xattrinfo(&ainfo
);
1056 if (ainfo
.attrhdr
== NULL
) {
1060 namelen
= strlen(name
) + 1;
1061 header
= ainfo
.attrhdr
;
1062 entry
= ainfo
.attr_entry
;
1065 * See if this attribute exists.
1067 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1068 if (strncmp(entry
->name
, name
, namelen
) == 0) {
1070 if ((i
+1) == header
->num_attrs
)
1074 entry
= ATTR_NEXT(entry
);
1080 /* On removal of last attribute the ._ file is removed. */
1081 if (--attrcount
== 0)
1084 datalen
= entry
->length
;
1085 dataoff
= entry
->offset
;
1086 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1087 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
)
1092 /* Remove the attribute entry. */
1094 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
1095 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
1098 /* Adjust the attribute data. */
1102 dataoff
- header
->data_start
,
1108 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
1112 /* XXX write zeros to freed space ? */
1113 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
1117 bcopy((u_int8_t
*)header
+ header
->data_start
,
1118 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
1119 dataoff
- header
->data_start
);
1121 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
1122 (u_int8_t
*)header
+ dataoff
- entrylen
,
1123 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
1125 bzero (((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
1126 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1129 /* Adjust the header values and entry offsets. */
1130 header
->num_attrs
--;
1131 header
->data_start
-= entrylen
;
1132 header
->data_length
-= datalen
;
1135 entry
= ainfo
.attr_entry
;
1136 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1137 entry
->offset
-= entrylen
;
1138 if (entry
>= oldslot
)
1139 entry
->offset
-= datalen
;
1140 entry
= ATTR_NEXT(entry
);
1142 error
= write_xattrinfo(&ainfo
);
1144 printf("removexattr: write_xattrinfo error %d\n", error
);
1147 rel_xattrinfo(&ainfo
);
1149 /* When there are no more attributes remove the ._ file. */
1150 if (attrcount
== 0) {
1151 if (fileflags
& O_EXLOCK
)
1152 (void) unlock_xattrfile(xvp
, context
);
1153 VNOP_CLOSE(xvp
, fileflags
, context
);
1155 error
= remove_xattrfile(xvp
, context
);
1158 close_xattrfile(xvp
, fileflags
, context
);
1160 /* Touch the change time if we changed an attribute. */
1162 struct vnode_attr va
;
1164 /* Re-write the mtime to cause a ctime change. */
1166 VATTR_WANTED(&va
, va_modify_time
);
1167 if (vnode_getattr(vp
, &va
, context
) == 0) {
1169 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
1170 (void) vnode_setattr(vp
, &va
, context
);
1179 * Retrieve the list of extended attribute names.
1182 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
1186 attr_entry_t
*entry
;
1191 * We do not zero "*size" here as we don't want to stomp a size set when
1192 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
1193 * system call layer, up in listxattr or flistxattr.
1196 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
1197 if (error
== ENOATTR
)
1201 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1202 close_xattrfile(xvp
, FREAD
, context
);
1206 /* Check for Finder Info. */
1207 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1209 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
1210 } else if (uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
1214 error
= uiomove((caddr_t
)XATTR_FINDERINFO_NAME
,
1215 sizeof(XATTR_FINDERINFO_NAME
), uio
);
1223 /* Check for Resource Fork. */
1224 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
1226 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
1227 } else if (uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
1231 error
= uiomove((caddr_t
)XATTR_RESOURCEFORK_NAME
,
1232 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
1240 /* Check for attributes. */
1241 if (ainfo
.attrhdr
) {
1242 count
= ainfo
.attrhdr
->num_attrs
;
1243 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
1244 if (xattr_protected(entry
->name
) ||
1245 xattr_validatename(entry
->name
) != 0) {
1246 entry
= ATTR_NEXT(entry
);
1250 *size
+= entry
->namelen
;
1251 entry
= ATTR_NEXT(entry
);
1254 if (uio_resid(uio
) < entry
->namelen
) {
1258 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
1260 if (error
!= EFAULT
)
1264 entry
= ATTR_NEXT(entry
);
1268 rel_xattrinfo(&ainfo
);
1269 close_xattrfile(xvp
, FREAD
, context
);
1275 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
1277 vnode_t xvp
= NULLVP
;
1278 vnode_t dvp
= NULLVP
;
1279 struct vnode_attr va
;
1280 struct nameidata nd
;
1282 char *filename
= NULL
;
1283 char *basename
= NULL
;
1289 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
1291 * For the root directory use "._." to hold the attributes.
1293 filename
= &smallname
[0];
1294 sprintf(filename
, "%s%s", ATTR_FILE_PREFIX
, ".");
1295 dvp
= vp
; /* the "._." file resides in the root dir */
1298 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
1302 if ( (basename
= vnode_getname(vp
)) == NULL
) {
1307 /* "._" Attribute files cannot have attributes */
1308 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
1309 basename
[0] == '.' && basename
[1] == '_') {
1313 filename
= &smallname
[0];
1314 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
1315 if (len
>= sizeof(smallname
)) {
1316 len
++; /* snprintf result doesn't include '\0' */
1317 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
1318 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
1321 * Note that the lookup here does not authorize. Since we are looking
1322 * up in the same directory that we already have the file vnode in,
1323 * we must have been given the file vnode legitimately. Read/write
1324 * access has already been authorized in layers above for calls from
1325 * userspace, and the authorization code using this path to read
1326 * file security from the EA must always get access
1329 NDINIT(&nd
, LOOKUP
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
, UIO_SYSSPACE
,
1330 CAST_USER_ADDR_T(filename
), context
);
1333 if (fileflags
& O_CREAT
) {
1334 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
,
1382 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
1387 if ((error
= namei(&nd
))) {
1397 if (xvp
->v_type
!= VREG
) {
1402 * Owners must match.
1405 VATTR_WANTED(&va
, va_uid
);
1406 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
1407 uid_t owner
= va
.va_uid
;
1410 VATTR_WANTED(&va
, va_uid
);
1411 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
1412 error
= ENOATTR
; /* don't use this "._" file */
1417 if ( (error
= VNOP_OPEN(xvp
, fileflags
, context
))) {
1423 if ((error
= vnode_ref(xvp
))) {
1428 /* If create was requested, make sure file header exists. */
1429 if (fileflags
& O_CREAT
) {
1431 VATTR_WANTED(&va
, va_data_size
);
1432 VATTR_WANTED(&va
, va_fileid
);
1433 VATTR_WANTED(&va
, va_nlink
);
1434 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
1439 /* If the file is empty then add a default header. */
1440 if (va
.va_data_size
== 0) {
1441 /* Don't adopt hard-linked "._" files. */
1442 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
1446 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
1450 /* Apply file locking if requested. */
1451 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
1454 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
1455 error
= lock_xattrfile(xvp
, locktype
, context
);
1458 if (dvp
&& (dvp
!= vp
)) {
1462 vnode_putname(basename
);
1464 if (filename
&& filename
!= &smallname
[0]) {
1465 FREE(filename
, M_TEMP
);
1468 if (xvp
!= NULLVP
) {
1470 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
1473 (void) vnode_rele(xvp
);
1475 (void) vnode_put(xvp
);
1478 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
1482 *xvpp
= xvp
; /* return a referenced vnode */
1487 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
1489 // if (fileflags & FWRITE)
1490 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
1492 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
1493 (void) unlock_xattrfile(xvp
, context
);
1495 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
1496 (void) vnode_rele(xvp
);
1497 (void) vnode_put(xvp
);
1501 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
1504 struct nameidata nd
;
1509 path
= get_pathbuff();
1510 pathlen
= MAXPATHLEN
;
1511 vn_getpath(xvp
, path
, &pathlen
);
1513 NDINIT(&nd
, DELETE
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
1514 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
1516 release_pathbuff(path
);
1523 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
1532 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
1535 void * buffer
= NULL
;
1536 apple_double_header_t
*filehdr
;
1537 attr_header_t
*attrhdr
;
1538 struct vnode_attr va
;
1543 bzero(ainfop
, sizeof(attr_info_t
));
1544 ainfop
->filevp
= xvp
;
1545 ainfop
->context
= context
;
1547 VATTR_WANTED(&va
, va_data_size
);
1548 VATTR_WANTED(&va
, va_fileid
);
1549 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
1552 ainfop
->filesize
= va
.va_data_size
;
1554 /* When setting attributes, allow room for the header to grow. */
1556 iosize
= ATTR_MAX_HDR_SIZE
;
1558 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
1564 ainfop
->iosize
= iosize
;
1565 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
1566 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
1567 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
1569 /* Read the file header. */
1570 error
= VNOP_READ(xvp
, auio
, 0, context
);
1574 ainfop
->rawsize
= iosize
- uio_resid(auio
);
1575 ainfop
->rawdata
= (u_int8_t
*)buffer
;
1577 filehdr
= (apple_double_header_t
*)buffer
;
1579 /* Check for Apple Double file. */
1580 if (SWAP32(filehdr
->magic
) != ADH_MAGIC
||
1581 SWAP32(filehdr
->version
) != ADH_VERSION
||
1582 SWAP16(filehdr
->numEntries
) < 1 ||
1583 SWAP16(filehdr
->numEntries
) > 15) {
1587 if (ADHDRSIZE
+ (SWAP16(filehdr
->numEntries
) * sizeof(apple_double_entry_t
)) > ainfop
->rawsize
) {
1592 swap_adhdr(filehdr
);
1593 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
1594 /* rel_xattrinfo is responsible for freeing the header buffer */
1597 /* Check the AppleDouble entries. */
1598 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
1599 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
1600 filehdr
->entries
[i
].length
> 0) {
1601 ainfop
->finderinfo
= &filehdr
->entries
[i
];
1602 attrhdr
= (attr_header_t
*)filehdr
;
1604 if (bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
,
1605 emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
1606 ainfop
->emptyfinderinfo
= 1;
1612 /* See if we need to convert this AppleDouble file. */
1613 if (filehdr
->entries
[0].length
== FINDERINFOSIZE
) {
1618 filehdr
->entries
[1].type
!= AD_RESOURCE
||
1619 filehdr
->numEntries
> 2) {
1620 continue; /* not expected layout */
1622 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
1623 if (filehdr
->entries
[1].length
) {
1624 /* Make some room. */
1625 shift_data_down(xvp
,
1626 filehdr
->entries
[1].offset
,
1627 filehdr
->entries
[1].length
,
1629 writesize
= sizeof(attr_header_t
);
1631 rsrcfork_header_t
*rsrcforkhdr
;
1633 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
1635 /* Steal some space for an empty RF header. */
1636 delta
-= sizeof(rsrcfork_header_t
);
1638 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
1639 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
1641 /* Fill in Empty Resource Fork Header. */
1642 init_empty_resource_fork(rsrcforkhdr
);
1644 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
1645 writesize
= ATTR_BUF_SIZE
;
1647 filehdr
->entries
[0].length
+= delta
;
1648 filehdr
->entries
[1].offset
+= delta
;
1650 /* Fill in Attribute Header. */
1651 attrhdr
->magic
= ATTR_HDR_MAGIC
;
1652 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
1653 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
1654 attrhdr
->data_start
= sizeof(attr_header_t
);
1655 attrhdr
->data_length
= 0;
1656 attrhdr
->reserved
[0] = 0;
1657 attrhdr
->reserved
[1] = 0;
1658 attrhdr
->reserved
[2] = 0;
1660 attrhdr
->num_attrs
= 0;
1662 /* Push out new header */
1663 uio_reset(auio
, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1664 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
1666 swap_adhdr(filehdr
);
1667 swap_attrhdr(attrhdr
);
1668 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
1669 swap_adhdr(filehdr
);
1670 /* The attribute header gets swapped below. */
1672 if (SWAP32 (attrhdr
->magic
) != ATTR_HDR_MAGIC
||
1673 validate_attrhdr(attrhdr
, ainfop
->rawsize
) != 0) {
1674 printf("get_xattrinfo: invalid attribute header\n");
1677 swap_attrhdr(attrhdr
);
1678 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
1679 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
1682 if (filehdr
->entries
[i
].type
== AD_RESOURCE
&&
1683 (filehdr
->entries
[i
].length
> sizeof(rsrcfork_header_t
) || setting
)) {
1684 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
1685 if (i
!= (filehdr
->numEntries
- 1)) {
1686 printf("get_xattrinfo: resource fork not last entry\n");
1687 ainfop
->readonly
= 1;
1697 FREE(buffer
, M_TEMP
);
1703 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
1706 rsrcfork_header_t
*rsrcforkhdr
;
1712 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
1713 bzero(buffer
, ATTR_BUF_SIZE
);
1715 xah
= (attr_header_t
*)buffer
;
1716 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1717 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
1718 rsrcforksize
= sizeof(rsrcfork_header_t
);
1719 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
1721 /* Fill in Apple Double Header. */
1722 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
1723 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
1724 xah
->appledouble
.numEntries
= SWAP16 (2);
1725 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
1726 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
1727 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
1728 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
1729 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
1730 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
1731 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
1733 /* Fill in Attribute Header. */
1734 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
1735 xah
->debug_tag
= SWAP32 (fileid
);
1736 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
1737 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
1739 /* Fill in Empty Resource Fork Header. */
1740 init_empty_resource_fork(rsrcforkhdr
);
1743 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
1746 FREE(buffer
, M_TEMP
);
1752 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
1754 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
1755 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
1756 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
1757 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
1758 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
1759 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
1760 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
1761 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
1762 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
1763 rsrcforkhdr
->typeCount
= SWAP16 (-1);
1764 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
1768 rel_xattrinfo(attr_info_t
*ainfop
)
1770 FREE(ainfop
->filehdr
, M_TEMP
);
1771 bzero(ainfop
, sizeof(attr_info_t
));
1775 write_xattrinfo(attr_info_t
*ainfop
)
1780 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1781 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
1783 swap_adhdr(ainfop
->filehdr
);
1784 swap_attrhdr(ainfop
->attrhdr
);
1786 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
1788 swap_adhdr(ainfop
->filehdr
);
1789 swap_attrhdr(ainfop
->attrhdr
);
1793 #if BYTE_ORDER == LITTLE_ENDIAN
1795 * Endian swap apple double header
1798 swap_adhdr(apple_double_header_t
*adh
)
1803 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
1805 adh
->magic
= SWAP32 (adh
->magic
);
1806 adh
->version
= SWAP32 (adh
->version
);
1807 adh
->numEntries
= SWAP16 (adh
->numEntries
);
1809 for (i
= 0; i
< count
; i
++) {
1810 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
1811 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
1812 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
1817 * Endian swap extended attributes header
1820 swap_attrhdr(attr_header_t
*ah
)
1826 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
1828 ah
->magic
= SWAP32 (ah
->magic
);
1829 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
1830 ah
->total_size
= SWAP32 (ah
->total_size
);
1831 ah
->data_start
= SWAP32 (ah
->data_start
);
1832 ah
->data_length
= SWAP32 (ah
->data_length
);
1833 ah
->flags
= SWAP16 (ah
->flags
);
1834 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
1836 ae
= (attr_entry_t
*)(&ah
[1]);
1837 for (i
= 0; i
< count
; i
++, ae
= ATTR_NEXT(ae
)) {
1838 ae
->offset
= SWAP32 (ae
->offset
);
1839 ae
->length
= SWAP32 (ae
->length
);
1840 ae
->flags
= SWAP16 (ae
->flags
);
1846 * Validate attributes header contents
1849 validate_attrhdr(attr_header_t
*ah
, size_t bufsize
)
1859 bufend
= (u_int8_t
*)ah
+ bufsize
;
1860 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
1862 ae
= (attr_entry_t
*)(&ah
[1]);
1863 for (i
= 0; i
< count
&& (u_int8_t
*)ae
< bufend
; i
++, ae
= ATTR_NEXT(ae
)) {
1865 return (i
< count
? EINVAL
: 0);
1869 // "start" & "end" are byte offsets in the file.
1870 // "to" is the byte offset we want to move the
1871 // data to. "to" should be > "start".
1873 // we do the copy backwards to avoid problems if
1874 // there's an overlap.
1877 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
1880 size_t chunk
, orig_chunk
;
1883 ucred_t ucred
= vfs_context_ucred(context
);
1884 proc_t p
= vfs_context_proc(context
);
1886 if (delta
== 0 || len
== 0) {
1896 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
1900 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
1901 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1903 printf("xattr:shift_data: error reading data @ %lld (read %d of %d) (%d)\n",
1904 pos
, ret
, chunk
, ret
);
1908 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1910 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %d) (%d)\n",
1911 pos
+delta
, ret
, chunk
, ret
);
1915 if ((pos
- chunk
) < start
) {
1916 chunk
= pos
- start
;
1918 if (chunk
== 0) { // we're all done
1923 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
1930 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
1933 size_t chunk
, orig_chunk
;
1937 ucred_t ucred
= vfs_context_ucred(context
);
1938 proc_t p
= vfs_context_proc(context
);
1940 if (delta
== 0 || len
== 0) {
1951 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
1955 for(pos
= start
; pos
< end
; pos
+= chunk
) {
1956 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1958 printf("xattr:shift_data: error reading data @ %lld (read %d of %d) (%d)\n",
1959 pos
, ret
, chunk
, ret
);
1963 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1965 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %d) (%d)\n",
1966 pos
+delta
, ret
, chunk
, ret
);
1970 if ((pos
+ chunk
) > end
) {
1973 if (chunk
== 0) { // we're all done
1978 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
1984 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
1988 lf
.l_whence
= SEEK_SET
;
1991 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
1992 /* Note: id is just a kernel address that's not a proc */
1993 return VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
, context
);
1997 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
2001 lf
.l_whence
= SEEK_SET
;
2004 lf
.l_type
= F_UNLCK
;
2005 /* Note: id is just a kernel address that's not a proc */
2006 return VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
);