2 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/param.h>
31 #include <sys/fcntl.h>
32 #include <sys/fsevents.h>
33 #include <sys/kernel.h>
34 #include <sys/kauth.h>
35 #include <sys/malloc.h>
36 #include <sys/namei.h>
37 #include <sys/proc_internal.h>
40 #include <sys/utfconv.h>
41 #include <sys/vnode.h>
42 #include <sys/vnode_internal.h>
44 #include <sys/xattr.h>
46 #include <architecture/byte_order.h>
47 #include <vm/vm_kern.h>
50 * Default xattr support routines.
52 static int default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
53 int options
, vfs_context_t context
);
55 static int default_setxattr(vnode_t vp
, const char *name
, uio_t uio
,
56 int options
, vfs_context_t context
);
58 static int default_removexattr(vnode_t vp
, const char *name
, int options
, vfs_context_t context
);
60 static int default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
,
61 vfs_context_t context
);
66 * Retrieve the data of an extended attribute.
69 vn_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
70 int options
, vfs_context_t context
)
74 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
77 if ((error
= xattr_validatename(name
))) {
80 if (!(options
& XATTR_NOSECURITY
) && (error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
)))
83 /* The offset can only be non-zero for resource forks. */
84 if (uio
!= NULL
&& uio_offset(uio
) != 0 &&
85 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0) {
90 error
= VNOP_GETXATTR(vp
, name
, uio
, size
, options
, context
);
91 if (error
== ENOTSUP
) {
93 * A filesystem may keep some EAs natively and return ENOTSUP for others.
94 * SMB returns ENOTSUP for finderinfo and resource forks.
96 error
= default_getxattr(vp
, name
, uio
, size
, options
, context
);
103 * Set the data of an extended attribute.
106 vn_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
110 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
113 if ((options
& (XATTR_REPLACE
|XATTR_CREATE
)) == (XATTR_REPLACE
|XATTR_CREATE
)) {
116 if ((error
= xattr_validatename(name
))) {
119 if (!(options
& XATTR_NOSECURITY
) && (error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
)))
122 /* The offset can only be non-zero for resource forks. */
123 if (uio_offset(uio
) != 0 &&
124 bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) != 0 ) {
129 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
132 * An EJUSTRETURN is from a filesystem which keeps this xattr
133 * natively as well as in a dot-underscore file. In this case the
134 * EJUSTRETURN means the filesytem has done nothing, but identifies the
135 * EA as one which may be represented natively and/or in a DU, and
136 * since XATTR_CREATE or XATTR_REPLACE was specified, only up here in
137 * in vn_setxattr can we do the getxattrs needed to ascertain whether
138 * the XATTR_{CREATE,REPLACE} should yield an error.
140 if (error
== EJUSTRETURN
) {
141 int native
= 0, dufile
= 0;
142 size_t sz
; /* not used */
144 native
= VNOP_GETXATTR(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
145 dufile
= default_getxattr(vp
, name
, NULL
, &sz
, 0, context
) ? 0 : 1;
146 if (options
& XATTR_CREATE
&& (native
|| dufile
)) {
150 if (options
& XATTR_REPLACE
&& !(native
|| dufile
)) {
155 * Having determined no CREATE/REPLACE error should result, we
156 * zero those bits, so both backing stores get written to.
158 options
&= ~(XATTR_CREATE
| XATTR_REPLACE
);
159 error
= VNOP_SETXATTR(vp
, name
, uio
, options
, context
);
160 /* the mainline path here is to have error==ENOTSUP ... */
162 #endif /* DUAL_EAS */
163 if (error
== ENOTSUP
) {
165 * A filesystem may keep some EAs natively and return ENOTSUP for others.
166 * SMB returns ENOTSUP for finderinfo and resource forks.
168 error
= default_setxattr(vp
, name
, uio
, options
, context
);
175 * Remove an extended attribute.
178 vn_removexattr(vnode_t vp
, const char * name
, int options
, vfs_context_t context
)
182 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
185 if ((error
= xattr_validatename(name
))) {
188 if (!(options
& XATTR_NOSECURITY
) && (error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_WRITE_EXTATTRIBUTES
, context
)))
190 error
= VNOP_REMOVEXATTR(vp
, name
, options
, context
);
191 if (error
== ENOTSUP
) {
193 * A filesystem may keep some EAs natively and return ENOTSUP for others.
194 * SMB returns ENOTSUP for finderinfo and resource forks.
196 error
= default_removexattr(vp
, name
, options
, context
);
198 } else if (error
== EJUSTRETURN
) {
200 * EJUSTRETURN is from a filesystem which keeps this xattr natively as well
201 * as in a dot-underscore file. EJUSTRETURN means the filesytem did remove
202 * a native xattr, so failure to find it in a DU file during
203 * default_removexattr should not be considered an error.
205 error
= default_removexattr(vp
, name
, options
, context
);
206 if (error
== ENOATTR
)
208 #endif /* DUAL_EAS */
215 * Retrieve the list of extended attribute names.
218 vn_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, int options
, vfs_context_t context
)
222 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VDIR
|| vp
->v_type
== VLNK
)) {
225 if (!(options
& XATTR_NOSECURITY
) && (error
= vnode_authorize(vp
, NULL
, KAUTH_VNODE_READ_EXTATTRIBUTES
, context
)))
228 error
= VNOP_LISTXATTR(vp
, uio
, size
, options
, context
);
229 if (error
== ENOTSUP
) {
231 * A filesystem may keep some but not all EAs natively, in which case
232 * the native EA names will have been uiomove-d out (or *size updated)
233 * and the default_listxattr here will finish the job. Note SMB takes
234 * advantage of this for its finder-info and resource forks.
236 error
= default_listxattr(vp
, uio
, size
, options
, context
);
243 xattr_validatename(const char *name
)
247 if (name
== NULL
|| name
[0] == '\0') {
250 namelen
= strlen(name
);
251 if (namelen
> XATTR_MAXNAMELEN
) {
252 return (ENAMETOOLONG
);
254 if (utf8_validatestr(name
, namelen
) != 0) {
262 * Determine whether an EA is a protected system attribute.
265 xattr_protected(const char *attrname
)
267 return(!strncmp(attrname
, "com.apple.system.", 17));
272 * Default Implementation (Non-native EA)
277 Typical "._" AppleDouble Header File layout:
278 ------------------------------------------------------------
283 .-- AD ENTRY[0] Finder Info Entry (must be first)
284 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
286 | ///////////// Fixed Size Data (32 bytes)
290 | ATTR ENTRY[1] --+--.
291 | ATTR ENTRY[2] --+--+--.
293 | ATTR ENTRY[N] --+--+--+--.
294 | ATTR DATA 0 <-' | | |
296 | ATTR DATA 1 <----' | |
298 | ATTR DATA 2 <-------' |
301 | ATTR DATA N <----------'
303 | Attribute Free Space
306 ///////////// Variable Sized Data
315 ------------------------------------------------------------
317 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
318 stored as part of the Finder Info. The length in the Finder
319 Info AppleDouble entry includes the length of the extended
320 attribute header, attribute entries, and attribute data.
325 * On Disk Data Structures
327 * Note: Motorola 68K alignment and big-endian.
329 * See RFC 1740 for additional information about the AppleDouble file format.
333 #define ADH_MAGIC 0x00051607
334 #define ADH_VERSION 0x00020000
335 #define ADH_MACOSX "Mac OS X "
338 * AppleDouble Entry ID's
340 #define AD_DATA 1 /* Data fork */
341 #define AD_RESOURCE 2 /* Resource fork */
342 #define AD_REALNAME 3 /* FileÕs name on home file system */
343 #define AD_COMMENT 4 /* Standard Mac comment */
344 #define AD_ICONBW 5 /* Mac black & white icon */
345 #define AD_ICONCOLOR 6 /* Mac color icon */
346 #define AD_UNUSED 7 /* Not used */
347 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
348 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
349 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
350 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
351 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
352 #define AD_AFPNAME 13 /* Short name on AFP server */
353 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
354 #define AD_AFPDIRID 15 /* AFP directory ID */
355 #define AD_ATTRIBUTES AD_FINDERINFO
358 #define ATTR_FILE_PREFIX "._"
359 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
361 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
363 /* Implementation Limits */
364 #define ATTR_MAX_SIZE (128*1024) /* 128K maximum attribute data size */
365 #define ATTR_MAX_HDR_SIZE 65536
367 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
368 * size supported (including the attribute entries). All of
369 * the attribute entries must reside within this limit. If
370 * any of the attribute data crosses the ATTR_MAX_HDR_SIZE
371 * boundry, then all of the attribute data I/O is performed
372 * seperately from the attribute header I/O.
376 #pragma options align=mac68k
378 #define FINDERINFOSIZE 32
380 typedef struct apple_double_entry
{
381 u_int32_t type
; /* entry type: see list, 0 invalid */
382 u_int32_t offset
; /* entry data offset from the beginning of the file. */
383 u_int32_t length
; /* entry data length in bytes. */
384 } apple_double_entry_t
;
387 typedef struct apple_double_header
{
388 u_int32_t magic
; /* == ADH_MAGIC */
389 u_int32_t version
; /* format version: 2 = 0x00020000 */
391 u_int16_t numEntries
; /* number of entries which follow */
392 apple_double_entry_t entries
[2]; /* 'finfo' & 'rsrc' always exist */
393 u_int8_t finfo
[FINDERINFOSIZE
]; /* Must start with Finder Info (32 bytes) */
394 u_int8_t pad
[2]; /* get better alignment inside attr_header */
395 } apple_double_header_t
;
397 #define ADHDRSIZE (4+4+16+2)
399 /* Entries are aligned on 4 byte boundaries */
400 typedef struct attr_entry
{
401 u_int32_t offset
; /* file offset to data */
402 u_int32_t length
; /* size of attribute data */
405 u_int8_t name
[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
409 /* Header + entries must fit into 64K */
410 typedef struct attr_header
{
411 apple_double_header_t appledouble
;
412 u_int32_t magic
; /* == ATTR_HDR_MAGIC */
413 u_int32_t debug_tag
; /* for debugging == file id of owning file */
414 u_int32_t total_size
; /* total size of attribute header + entries + data */
415 u_int32_t data_start
; /* file offset to attribute data area */
416 u_int32_t data_length
; /* length of attribute data area */
417 u_int32_t reserved
[3];
423 /* Empty Resource Fork Header */
424 typedef struct rsrcfork_header
{
425 u_int32_t fh_DataOffset
;
426 u_int32_t fh_MapOffset
;
427 u_int32_t fh_DataLength
;
428 u_int32_t fh_MapLength
;
429 u_int8_t systemData
[112];
430 u_int8_t appData
[128];
431 u_int32_t mh_DataOffset
;
432 u_int32_t mh_MapOffset
;
433 u_int32_t mh_DataLength
;
434 u_int32_t mh_MapLength
;
438 u_int8_t mh_InMemoryAttr
;
444 #define RF_FIRST_RESOURCE 256
445 #define RF_NULL_MAP_LENGTH 30
446 #define RF_EMPTY_TAG "This resource fork intentionally left blank "
448 #pragma options align=reset
450 /* Runtime information about the attribute file. */
451 typedef struct attr_info
{
452 vfs_context_t context
;
457 size_t rawsize
; /* raw size of AppleDouble file */
458 apple_double_header_t
*filehdr
;
459 apple_double_entry_t
*finderinfo
;
460 apple_double_entry_t
*rsrcfork
;
461 attr_header_t
*attrhdr
;
462 attr_entry_t
*attr_entry
;
464 u_int8_t emptyfinderinfo
;
468 #define ATTR_SETTING 1
470 #define ATTR_ALIGN 3L /* Use four-byte alignment */
472 #define ATTR_ENTRY_LENGTH(namelen) \
473 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
475 #define ATTR_NEXT(ae) \
476 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
478 #define ATTR_VALID(ae, ai) \
479 ((u_int8_t *)ATTR_NEXT(ae) <= ((ai).rawdata + (ai).rawsize))
482 #define SWAP16(x) NXSwapBigShortToHost((x))
483 #define SWAP32(x) NXSwapBigIntToHost((x))
484 #define SWAP64(x) NXSwapBigLongLongToHost((x))
487 static u_int32_t emptyfinfo
[8] = {0};
491 * Local support routines
493 static void close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
);
495 static int open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
);
497 static int create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
);
499 static int remove_xattrfile(vnode_t xvp
, vfs_context_t context
);
501 static int get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
);
503 static void rel_xattrinfo(attr_info_t
*ainfop
);
505 static int write_xattrinfo(attr_info_t
*ainfop
);
507 static void init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
);
509 static int lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
);
511 static int unlock_xattrfile(vnode_t xvp
, vfs_context_t context
);
514 #if BYTE_ORDER == LITTLE_ENDIAN
515 static void swap_adhdr(apple_double_header_t
*adh
);
516 static void swap_attrhdr(attr_header_t
*ah
);
519 #define swap_adhdr(x)
520 #define swap_attrhdr(x)
523 static int validate_attrhdr(attr_header_t
*ah
, size_t bufsize
);
524 static int shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
525 static int shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
);
529 * Retrieve the data of an extended attribute.
532 default_getxattr(vnode_t vp
, const char *name
, uio_t uio
, size_t *size
,
533 __unused
int options
, vfs_context_t context
)
537 attr_header_t
*header
;
548 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
551 * Open the file locked (shared) since the Carbon
552 * File Manager may have the Apple Double file open
553 * and could be changing the resource fork.
555 fileflags
|= O_SHLOCK
;
560 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
563 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
564 close_xattrfile(xvp
, fileflags
, context
);
568 /* Get the Finder Info. */
569 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
571 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
573 } else if (uio
== NULL
) {
574 *size
= FINDERINFOSIZE
;
576 } else if (uio_offset(uio
) != 0) {
578 } else if (uio_resid(uio
) < FINDERINFOSIZE
) {
581 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
582 error
= uiomove((caddr_t
)attrdata
, FINDERINFOSIZE
, uio
);
587 /* Read the Resource Fork. */
589 if (!vnode_isreg(vp
)) {
591 } else if (ainfo
.rsrcfork
== NULL
) {
593 } else if (uio
== NULL
) {
594 *size
= (size_t)ainfo
.rsrcfork
->length
;
596 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
597 error
= VNOP_READ(xvp
, uio
, 0, context
);
599 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
604 if (ainfo
.attrhdr
== NULL
|| ainfo
.attr_entry
== NULL
) {
608 if (uio_offset(uio
) != 0) {
613 namelen
= strlen(name
) + 1;
614 header
= ainfo
.attrhdr
;
615 entry
= ainfo
.attr_entry
;
617 * Search for attribute name in the header.
619 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
620 if (strncmp(entry
->name
, name
, namelen
) == 0) {
621 datalen
= (size_t)entry
->length
;
627 if (uio_resid(uio
) < datalen
) {
631 if (entry
->offset
+ datalen
< ATTR_MAX_HDR_SIZE
) {
632 attrdata
= ((u_int8_t
*)header
+ entry
->offset
);
633 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
635 uio_setoffset(uio
, entry
->offset
);
636 error
= VNOP_READ(xvp
, uio
, 0, context
);
637 uio_setoffset(uio
, 0);
641 entry
= ATTR_NEXT(entry
);
644 rel_xattrinfo(&ainfo
);
645 close_xattrfile(xvp
, fileflags
, context
);
651 * Set the data of an extended attribute.
654 default_setxattr(vnode_t vp
, const char *name
, uio_t uio
, int options
, vfs_context_t context
)
658 attr_header_t
*header
;
660 attr_entry_t
*lastentry
;
664 size_t datafreespace
;
672 datalen
= uio_resid(uio
);
673 namelen
= strlen(name
) + 1;
674 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
676 if (datalen
> ATTR_MAX_SIZE
) {
677 return (E2BIG
); /* EINVAL instead ? */
681 * Open the file locked since setting an attribute
682 * can change the layout of the Apple Double file.
684 fileflags
= FREAD
| FWRITE
| O_EXLOCK
;
685 if ((error
= open_xattrfile(vp
, O_CREAT
| fileflags
, &xvp
, context
))) {
688 if ((error
= get_xattrinfo(xvp
, ATTR_SETTING
, &ainfo
, context
))) {
689 close_xattrfile(xvp
, fileflags
, context
);
693 /* Set the Finder Info. */
694 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
695 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
696 /* attr exists and "create" was specified? */
697 if (options
& XATTR_CREATE
) {
702 /* attr doesn't exists and "replace" was specified? */
703 if (options
& XATTR_REPLACE
) {
708 if (uio_offset(uio
) != 0 || datalen
!= FINDERINFOSIZE
) {
712 if (ainfo
.finderinfo
) {
713 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
714 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
717 ainfo
.iosize
= sizeof(attr_header_t
);
718 error
= write_xattrinfo(&ainfo
);
725 /* Write the Resource Fork. */
726 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
729 if (!vnode_isreg(vp
)) {
733 if (ainfo
.rsrcfork
&& ainfo
.rsrcfork
->length
) {
734 /* attr exists and "create" was specified? */
735 if (options
& XATTR_CREATE
) {
740 /* attr doesn't exists and "replace" was specified? */
741 if (options
& XATTR_REPLACE
) {
746 endoffset
= uio_resid(uio
) + uio_offset(uio
); /* new size */
747 uio_setoffset(uio
, uio_offset(uio
) + ainfo
.rsrcfork
->offset
);
748 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
751 uio_setoffset(uio
, uio_offset(uio
) - ainfo
.rsrcfork
->offset
);
752 if (endoffset
> ainfo
.rsrcfork
->length
) {
753 ainfo
.rsrcfork
->length
= endoffset
;
754 ainfo
.iosize
= sizeof(attr_header_t
);
755 error
= write_xattrinfo(&ainfo
);
761 if (ainfo
.attrhdr
== NULL
) {
765 header
= ainfo
.attrhdr
;
766 entry
= ainfo
.attr_entry
;
768 /* Check if data area crosses the maximum header size. */
769 if ((header
->data_start
+ header
->data_length
+ entrylen
+ datalen
) > ATTR_MAX_HDR_SIZE
)
770 splitdata
= 1; /* do data I/O separately */
775 * See if attribute already exists.
777 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
778 if (strncmp(entry
->name
, name
, namelen
) == 0) {
782 entry
= ATTR_NEXT(entry
);
786 if (options
& XATTR_CREATE
) {
790 if (datalen
== entry
->length
) {
792 uio_setoffset(uio
, entry
->offset
);
793 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
794 uio_setoffset(uio
, 0);
796 printf("setxattr: VNOP_WRITE error %d\n", error
);
799 attrdata
= (u_int8_t
*)header
+ entry
->offset
;
800 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
803 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
804 error
= write_xattrinfo(&ainfo
);
806 printf("setxattr: write_xattrinfo error %d\n", error
);
812 * Brute force approach - just remove old entry and set new entry.
815 rel_xattrinfo(&ainfo
);
816 close_xattrfile(xvp
, fileflags
, context
);
817 error
= default_removexattr(vp
, name
, options
, context
);
821 goto start
; /* start over */
826 if (options
& XATTR_REPLACE
) {
827 error
= ENOATTR
; /* nothing there to replace */
830 /* Check if header size limit has been reached. */
831 if ((header
->data_start
+ entrylen
) > ATTR_MAX_HDR_SIZE
) {
836 datafreespace
= header
->total_size
- (header
->data_start
+ header
->data_length
);
838 /* Check if we need more space. */
839 if ((datalen
+ entrylen
) > datafreespace
) {
842 growsize
= roundup((datalen
+ entrylen
) - datafreespace
, ATTR_BUF_SIZE
);
844 /* Clip roundup size when we can still fit in ATTR_MAX_HDR_SIZE. */
845 if (!splitdata
&& (header
->total_size
+ growsize
) > ATTR_MAX_HDR_SIZE
) {
846 growsize
= ATTR_MAX_HDR_SIZE
- header
->total_size
;
849 ainfo
.filesize
+= growsize
;
850 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
852 printf("setxattr: VNOP_TRUNCATE error %d\n", error
);
858 * Move the resource fork out of the way.
860 if (ainfo
.rsrcfork
) {
861 if (ainfo
.rsrcfork
->length
!= 0) {
863 ainfo
.rsrcfork
->offset
,
864 ainfo
.rsrcfork
->length
,
867 ainfo
.rsrcfork
->offset
+= growsize
;
869 ainfo
.finderinfo
->length
+= growsize
;
870 header
->total_size
+= growsize
;
873 /* Make space for a new entry. */
880 bcopy((u_int8_t
*)header
+ header
->data_start
,
881 (u_int8_t
*)header
+ header
->data_start
+ entrylen
,
882 header
->data_length
);
884 header
->data_start
+= entrylen
;
886 /* Fix up entry data offsets. */
888 for (entry
= ainfo
.attr_entry
; entry
!= lastentry
&& ATTR_VALID(entry
, ainfo
); entry
= ATTR_NEXT(entry
)) {
889 entry
->offset
+= entrylen
;
893 * If the attribute data area is entirely within
894 * the header buffer, then just update the buffer,
895 * otherwise we'll write it separately to the file.
900 /* Write new attribute data after the end of existing data. */
901 offset
= header
->data_start
+ header
->data_length
;
902 uio_setoffset(uio
, offset
);
903 error
= VNOP_WRITE(xvp
, uio
, 0, context
);
904 uio_setoffset(uio
, 0);
906 printf("setxattr: VNOP_WRITE error %d\n", error
);
910 attrdata
= (u_int8_t
*)header
+ header
->data_start
+ header
->data_length
;
912 error
= uiomove((caddr_t
)attrdata
, datalen
, uio
);
914 printf("setxattr: uiomove error %d\n", error
);
919 /* Create the attribute entry. */
920 lastentry
->length
= datalen
;
921 lastentry
->offset
= header
->data_start
+ header
->data_length
;
922 lastentry
->namelen
= namelen
;
923 lastentry
->flags
= 0;
924 bcopy(name
, &lastentry
->name
[0], namelen
);
926 /* Update the attributes header. */
928 header
->data_length
+= datalen
;
931 /* Only write the entries, since the data was written separately. */
932 ainfo
.iosize
= ainfo
.attrhdr
->data_start
;
934 /* The entry and data are both in the header; write them together. */
935 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
937 error
= write_xattrinfo(&ainfo
);
939 printf("setxattr: write_xattrinfo error %d\n", error
);
943 rel_xattrinfo(&ainfo
);
944 close_xattrfile(xvp
, fileflags
, context
);
946 /* Touch the change time if we changed an attribute. */
948 struct vnode_attr va
;
950 /* Re-write the mtime to cause a ctime change. */
952 VATTR_WANTED(&va
, va_modify_time
);
953 if (vnode_getattr(vp
, &va
, context
) == 0) {
955 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
956 (void) vnode_setattr(vp
, &va
, context
);
964 * Remove an extended attribute.
967 default_removexattr(vnode_t vp
, const char *name
, __unused
int options
, vfs_context_t context
)
971 attr_header_t
*header
;
973 attr_entry_t
*oldslot
;
979 int found
= 0, lastone
= 0;
987 fileflags
= FREAD
| FWRITE
;
988 if (bcmp(name
, XATTR_RESOURCEFORK_NAME
, sizeof(XATTR_RESOURCEFORK_NAME
)) == 0) {
991 * Open the file locked (exclusive) since the Carbon
992 * File Manager may have the Apple Double file open
993 * and could be changing the resource fork.
995 fileflags
|= O_EXLOCK
;
1000 if ((error
= open_xattrfile(vp
, fileflags
, &xvp
, context
))) {
1003 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1004 close_xattrfile(xvp
, fileflags
, context
);
1008 attrcount
+= ainfo
.attrhdr
->num_attrs
;
1011 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
)
1014 /* Clear the Finder Info. */
1015 if (bcmp(name
, XATTR_FINDERINFO_NAME
, sizeof(XATTR_FINDERINFO_NAME
)) == 0) {
1016 if (ainfo
.finderinfo
== NULL
|| ainfo
.emptyfinderinfo
) {
1020 /* On removal of last attribute the ._ file is removed. */
1021 if (--attrcount
== 0)
1023 attrdata
= (u_int8_t
*)ainfo
.filehdr
+ ainfo
.finderinfo
->offset
;
1024 bzero((caddr_t
)attrdata
, FINDERINFOSIZE
);
1025 ainfo
.iosize
= sizeof(attr_header_t
);
1026 error
= write_xattrinfo(&ainfo
);
1030 /* Clear the Resource Fork. */
1032 if (!vnode_isreg(vp
)) {
1036 if (ainfo
.rsrcfork
== NULL
|| ainfo
.rsrcfork
->length
== 0) {
1040 /* On removal of last attribute the ._ file is removed. */
1041 if (--attrcount
== 0)
1045 * If the resource fork isn't the last AppleDouble
1046 * entry then the space needs to be reclaimed by
1047 * shifting the entries after the resource fork.
1049 if ((ainfo
.rsrcfork
->offset
+ ainfo
.rsrcfork
->length
) == ainfo
.filesize
) {
1050 ainfo
.filesize
-= ainfo
.rsrcfork
->length
;
1051 error
= vnode_setsize(xvp
, ainfo
.filesize
, 0, context
);
1054 ainfo
.rsrcfork
->length
= 0;
1055 ainfo
.iosize
= sizeof(attr_header_t
);
1056 error
= write_xattrinfo(&ainfo
);
1061 if (ainfo
.attrhdr
== NULL
) {
1065 namelen
= strlen(name
) + 1;
1066 header
= ainfo
.attrhdr
;
1067 entry
= ainfo
.attr_entry
;
1070 * See if this attribute exists.
1072 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1073 if (strncmp(entry
->name
, name
, namelen
) == 0) {
1075 if ((i
+1) == header
->num_attrs
)
1079 entry
= ATTR_NEXT(entry
);
1085 /* On removal of last attribute the ._ file is removed. */
1086 if (--attrcount
== 0)
1089 datalen
= entry
->length
;
1090 dataoff
= entry
->offset
;
1091 entrylen
= ATTR_ENTRY_LENGTH(namelen
);
1092 if ((header
->data_start
+ header
->data_length
) > ATTR_MAX_HDR_SIZE
)
1097 /* Remove the attribute entry. */
1099 bcopy((u_int8_t
*)entry
+ entrylen
, (u_int8_t
*)entry
,
1100 ((size_t)header
+ header
->data_start
) - ((size_t)entry
+ entrylen
));
1103 /* Adjust the attribute data. */
1107 dataoff
- header
->data_start
,
1113 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
),
1117 /* XXX write zeros to freed space ? */
1118 ainfo
.iosize
= ainfo
.attrhdr
->data_start
- entrylen
;
1122 bcopy((u_int8_t
*)header
+ header
->data_start
,
1123 (u_int8_t
*)header
+ header
->data_start
- entrylen
,
1124 dataoff
- header
->data_start
);
1126 bcopy((u_int8_t
*)header
+ dataoff
+ datalen
,
1127 (u_int8_t
*)header
+ dataoff
- entrylen
,
1128 (header
->data_start
+ header
->data_length
) - (dataoff
+ datalen
));
1130 bzero (((u_int8_t
*)header
+ header
->data_start
+ header
->data_length
) - (datalen
+ entrylen
), (datalen
+ entrylen
));
1131 ainfo
.iosize
= ainfo
.attrhdr
->data_start
+ ainfo
.attrhdr
->data_length
;
1134 /* Adjust the header values and entry offsets. */
1135 header
->num_attrs
--;
1136 header
->data_start
-= entrylen
;
1137 header
->data_length
-= datalen
;
1140 entry
= ainfo
.attr_entry
;
1141 for (i
= 0; i
< header
->num_attrs
&& ATTR_VALID(entry
, ainfo
); i
++) {
1142 entry
->offset
-= entrylen
;
1143 if (entry
>= oldslot
)
1144 entry
->offset
-= datalen
;
1145 entry
= ATTR_NEXT(entry
);
1147 error
= write_xattrinfo(&ainfo
);
1149 printf("removexattr: write_xattrinfo error %d\n", error
);
1152 rel_xattrinfo(&ainfo
);
1154 /* When there are no more attributes remove the ._ file. */
1155 if (attrcount
== 0) {
1156 if (fileflags
& O_EXLOCK
)
1157 (void) unlock_xattrfile(xvp
, context
);
1158 VNOP_CLOSE(xvp
, fileflags
, context
);
1160 error
= remove_xattrfile(xvp
, context
);
1163 close_xattrfile(xvp
, fileflags
, context
);
1165 /* Touch the change time if we changed an attribute. */
1167 struct vnode_attr va
;
1169 /* Re-write the mtime to cause a ctime change. */
1171 VATTR_WANTED(&va
, va_modify_time
);
1172 if (vnode_getattr(vp
, &va
, context
) == 0) {
1174 VATTR_SET(&va
, va_modify_time
, va
.va_modify_time
);
1175 (void) vnode_setattr(vp
, &va
, context
);
1184 * Retrieve the list of extended attribute names.
1187 default_listxattr(vnode_t vp
, uio_t uio
, size_t *size
, __unused
int options
, vfs_context_t context
)
1191 attr_entry_t
*entry
;
1196 * We do not zero "*size" here as we don't want to stomp a size set when
1197 * VNOP_LISTXATTR processed any native EAs. That size is initially zeroed by the
1198 * system call layer, up in listxattr or flistxattr.
1201 if ((error
= open_xattrfile(vp
, FREAD
, &xvp
, context
))) {
1202 if (error
== ENOATTR
)
1206 if ((error
= get_xattrinfo(xvp
, 0, &ainfo
, context
))) {
1207 close_xattrfile(xvp
, FREAD
, context
);
1211 /* Check for Finder Info. */
1212 if (ainfo
.finderinfo
&& !ainfo
.emptyfinderinfo
) {
1214 *size
+= sizeof(XATTR_FINDERINFO_NAME
);
1215 } else if (uio_resid(uio
) < sizeof(XATTR_FINDERINFO_NAME
)) {
1219 error
= uiomove((caddr_t
)XATTR_FINDERINFO_NAME
,
1220 sizeof(XATTR_FINDERINFO_NAME
), uio
);
1228 /* Check for Resource Fork. */
1229 if (vnode_isreg(vp
) && ainfo
.rsrcfork
) {
1231 *size
+= sizeof(XATTR_RESOURCEFORK_NAME
);
1232 } else if (uio_resid(uio
) < sizeof(XATTR_RESOURCEFORK_NAME
)) {
1236 error
= uiomove((caddr_t
)XATTR_RESOURCEFORK_NAME
,
1237 sizeof(XATTR_RESOURCEFORK_NAME
), uio
);
1245 /* Check for attributes. */
1246 if (ainfo
.attrhdr
) {
1247 count
= ainfo
.attrhdr
->num_attrs
;
1248 for (i
= 0, entry
= ainfo
.attr_entry
; i
< count
&& ATTR_VALID(entry
, ainfo
); i
++) {
1249 if (xattr_protected(entry
->name
) ||
1250 xattr_validatename(entry
->name
) != 0) {
1251 entry
= ATTR_NEXT(entry
);
1255 *size
+= entry
->namelen
;
1256 entry
= ATTR_NEXT(entry
);
1259 if (uio_resid(uio
) < entry
->namelen
) {
1263 error
= uiomove((caddr_t
) entry
->name
, entry
->namelen
, uio
);
1265 if (error
!= EFAULT
)
1269 entry
= ATTR_NEXT(entry
);
1273 rel_xattrinfo(&ainfo
);
1274 close_xattrfile(xvp
, FREAD
, context
);
1280 open_xattrfile(vnode_t vp
, int fileflags
, vnode_t
*xvpp
, vfs_context_t context
)
1282 vnode_t xvp
= NULLVP
;
1283 vnode_t dvp
= NULLVP
;
1284 struct vnode_attr va
;
1285 struct nameidata nd
;
1287 char *filename
= NULL
;
1288 char *basename
= NULL
;
1294 if (vnode_isvroot(vp
) && vnode_isdir(vp
)) {
1296 * For the root directory use "._." to hold the attributes.
1298 filename
= &smallname
[0];
1299 sprintf(filename
, "%s%s", ATTR_FILE_PREFIX
, ".");
1300 dvp
= vp
; /* the "._." file resides in the root dir */
1303 if ( (dvp
= vnode_getparent(vp
)) == NULLVP
) {
1307 if ( (basename
= vnode_getname(vp
)) == NULL
) {
1312 /* "._" Attribute files cannot have attributes */
1313 if (vp
->v_type
== VREG
&& strlen(basename
) > 2 &&
1314 basename
[0] == '.' && basename
[1] == '_') {
1318 filename
= &smallname
[0];
1319 len
= snprintf(filename
, sizeof(smallname
), "%s%s", ATTR_FILE_PREFIX
, basename
);
1320 if (len
>= sizeof(smallname
)) {
1321 len
++; /* snprintf result doesn't include '\0' */
1322 MALLOC(filename
, char *, len
, M_TEMP
, M_WAITOK
);
1323 len
= snprintf(filename
, len
, "%s%s", ATTR_FILE_PREFIX
, basename
);
1326 * Note that the lookup here does not authorize. Since we are looking
1327 * up in the same directory that we already have the file vnode in,
1328 * we must have been given the file vnode legitimately. Read/write
1329 * access has already been authorized in layers above for calls from
1330 * userspace, and the authorization code using this path to read
1331 * file security from the EA must always get access
1334 NDINIT(&nd
, LOOKUP
, LOCKLEAF
| NOFOLLOW
| USEDVP
| DONOTAUTH
, UIO_SYSSPACE
,
1335 CAST_USER_ADDR_T(filename
), context
);
1338 if (fileflags
& O_CREAT
) {
1339 nd
.ni_cnd
.cn_nameiop
= CREATE
;
1341 nd
.ni_cnd
.cn_flags
|= LOCKPARENT
;
1343 if ( (error
= namei(&nd
))) {
1348 if ( (xvp
= nd
.ni_vp
) == NULLVP
) {
1354 * Pick up uid/gid/mode from target file.
1357 VATTR_WANTED(&va
, va_uid
);
1358 VATTR_WANTED(&va
, va_gid
);
1359 VATTR_WANTED(&va
, va_mode
);
1360 if (VNOP_GETATTR(vp
, &va
, context
) == 0 &&
1361 VATTR_IS_SUPPORTED(&va
, va_uid
) &&
1362 VATTR_IS_SUPPORTED(&va
, va_gid
) &&
1363 VATTR_IS_SUPPORTED(&va
, va_mode
)) {
1366 umode
= va
.va_mode
& (S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
|S_IWOTH
);
1367 } else /* fallback values */ {
1368 uid
= KAUTH_UID_NONE
;
1369 gid
= KAUTH_GID_NONE
;
1370 umode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
;
1374 VATTR_SET(&va
, va_type
, VREG
);
1375 VATTR_SET(&va
, va_mode
, umode
);
1376 if (uid
!= KAUTH_UID_NONE
)
1377 VATTR_SET(&va
, va_uid
, uid
);
1378 if (gid
!= KAUTH_GID_NONE
)
1379 VATTR_SET(&va
, va_gid
, gid
);
1381 error
= vn_create(dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, &va
,
1382 VN_CREATE_NOAUTH
| VN_CREATE_NOINHERIT
,
1389 vnode_put(dvp
); /* drop iocount from LOCKPARENT request above */
1394 if ((error
= namei(&nd
))) {
1404 if (xvp
->v_type
!= VREG
) {
1409 * Owners must match.
1412 VATTR_WANTED(&va
, va_uid
);
1413 if (VNOP_GETATTR(vp
, &va
, context
) == 0 && VATTR_IS_SUPPORTED(&va
, va_uid
)) {
1414 uid_t owner
= va
.va_uid
;
1417 VATTR_WANTED(&va
, va_uid
);
1418 if (VNOP_GETATTR(xvp
, &va
, context
) == 0 && (owner
!= va
.va_uid
)) {
1419 error
= ENOATTR
; /* don't use this "._" file */
1424 if ( (error
= VNOP_OPEN(xvp
, fileflags
, context
))) {
1430 if ((error
= vnode_ref(xvp
))) {
1435 /* If create was requested, make sure file header exists. */
1436 if (fileflags
& O_CREAT
) {
1438 VATTR_WANTED(&va
, va_data_size
);
1439 VATTR_WANTED(&va
, va_fileid
);
1440 VATTR_WANTED(&va
, va_nlink
);
1441 if ( (error
= vnode_getattr(xvp
, &va
, context
)) != 0) {
1446 /* If the file is empty then add a default header. */
1447 if (va
.va_data_size
== 0) {
1448 /* Don't adopt hard-linked "._" files. */
1449 if (VATTR_IS_SUPPORTED(&va
, va_nlink
) && va
.va_nlink
> 1) {
1453 if ( (error
= create_xattrfile(xvp
, (u_int32_t
)va
.va_fileid
, context
)))
1457 /* Apply file locking if requested. */
1458 if (fileflags
& (O_EXLOCK
| O_SHLOCK
)) {
1461 locktype
= (fileflags
& O_EXLOCK
) ? F_WRLCK
: F_RDLCK
;
1462 error
= lock_xattrfile(xvp
, locktype
, context
);
1465 if (dvp
&& (dvp
!= vp
)) {
1469 vnode_putname(basename
);
1471 if (filename
&& filename
!= &smallname
[0]) {
1472 FREE(filename
, M_TEMP
);
1475 if (xvp
!= NULLVP
) {
1477 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
1480 (void) vnode_rele(xvp
);
1482 (void) vnode_put(xvp
);
1485 if ((error
== ENOATTR
) && (fileflags
& O_CREAT
)) {
1489 *xvpp
= xvp
; /* return a referenced vnode */
1494 close_xattrfile(vnode_t xvp
, int fileflags
, vfs_context_t context
)
1496 // if (fileflags & FWRITE)
1497 // (void) VNOP_FSYNC(xvp, MNT_WAIT, context);
1499 if (fileflags
& (O_EXLOCK
| O_SHLOCK
))
1500 (void) unlock_xattrfile(xvp
, context
);
1502 (void) VNOP_CLOSE(xvp
, fileflags
, context
);
1503 (void) vnode_rele(xvp
);
1504 (void) vnode_put(xvp
);
1508 remove_xattrfile(vnode_t xvp
, vfs_context_t context
)
1511 struct nameidata nd
;
1516 path
= get_pathbuff();
1517 pathlen
= MAXPATHLEN
;
1518 vn_getpath(xvp
, path
, &pathlen
);
1520 NDINIT(&nd
, DELETE
, LOCKPARENT
| NOFOLLOW
| DONOTAUTH
,
1521 UIO_SYSSPACE
, CAST_USER_ADDR_T(path
), context
);
1523 release_pathbuff(path
);
1530 error
= VNOP_REMOVE(dvp
, xvp
, &nd
.ni_cnd
, 0, context
);
1539 get_xattrinfo(vnode_t xvp
, int setting
, attr_info_t
*ainfop
, vfs_context_t context
)
1542 void * buffer
= NULL
;
1543 apple_double_header_t
*filehdr
;
1544 attr_header_t
*attrhdr
;
1545 struct vnode_attr va
;
1550 bzero(ainfop
, sizeof(attr_info_t
));
1551 ainfop
->filevp
= xvp
;
1552 ainfop
->context
= context
;
1554 VATTR_WANTED(&va
, va_data_size
);
1555 VATTR_WANTED(&va
, va_fileid
);
1556 if ((error
= vnode_getattr(xvp
, &va
, context
))) {
1559 ainfop
->filesize
= va
.va_data_size
;
1561 /* When setting attributes, allow room for the header to grow. */
1563 iosize
= ATTR_MAX_HDR_SIZE
;
1565 iosize
= MIN(ATTR_MAX_HDR_SIZE
, ainfop
->filesize
);
1571 ainfop
->iosize
= iosize
;
1572 MALLOC(buffer
, void *, iosize
, M_TEMP
, M_WAITOK
);
1573 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_READ
);
1574 uio_addiov(auio
, (uintptr_t)buffer
, iosize
);
1576 /* Read the file header. */
1577 error
= VNOP_READ(xvp
, auio
, 0, context
);
1581 ainfop
->rawsize
= iosize
- uio_resid(auio
);
1582 ainfop
->rawdata
= (u_int8_t
*)buffer
;
1584 filehdr
= (apple_double_header_t
*)buffer
;
1586 /* Check for Apple Double file. */
1587 if (SWAP32(filehdr
->magic
) != ADH_MAGIC
||
1588 SWAP32(filehdr
->version
) != ADH_VERSION
||
1589 SWAP16(filehdr
->numEntries
) < 1 ||
1590 SWAP16(filehdr
->numEntries
) > 15) {
1594 if (ADHDRSIZE
+ (SWAP16(filehdr
->numEntries
) * sizeof(apple_double_entry_t
)) > ainfop
->rawsize
) {
1599 swap_adhdr(filehdr
);
1600 ainfop
->filehdr
= filehdr
; /* valid AppleDouble header */
1601 /* rel_xattrinfo is responsible for freeing the header buffer */
1604 /* Check the AppleDouble entries. */
1605 for (i
= 0; i
< filehdr
->numEntries
; ++i
) {
1606 if (filehdr
->entries
[i
].type
== AD_FINDERINFO
&&
1607 filehdr
->entries
[i
].length
> 0) {
1608 ainfop
->finderinfo
= &filehdr
->entries
[i
];
1609 attrhdr
= (attr_header_t
*)filehdr
;
1611 if (bcmp((u_int8_t
*)ainfop
->filehdr
+ ainfop
->finderinfo
->offset
,
1612 emptyfinfo
, sizeof(emptyfinfo
)) == 0) {
1613 ainfop
->emptyfinderinfo
= 1;
1619 /* See if we need to convert this AppleDouble file. */
1620 if (filehdr
->entries
[0].length
== FINDERINFOSIZE
) {
1625 filehdr
->entries
[1].type
!= AD_RESOURCE
||
1626 filehdr
->numEntries
> 2) {
1627 continue; /* not expected layout */
1629 delta
= ATTR_BUF_SIZE
- (filehdr
->entries
[0].offset
+ FINDERINFOSIZE
);
1630 if (filehdr
->entries
[1].length
) {
1631 /* Make some room. */
1632 shift_data_down(xvp
,
1633 filehdr
->entries
[1].offset
,
1634 filehdr
->entries
[1].length
,
1636 writesize
= sizeof(attr_header_t
);
1638 rsrcfork_header_t
*rsrcforkhdr
;
1640 vnode_setsize(xvp
, filehdr
->entries
[1].offset
+ delta
, 0, context
);
1642 /* Steal some space for an empty RF header. */
1643 delta
-= sizeof(rsrcfork_header_t
);
1645 bzero(&attrhdr
->appledouble
.pad
[0], delta
);
1646 rsrcforkhdr
= (rsrcfork_header_t
*)((char *)filehdr
+ filehdr
->entries
[1].offset
+ delta
);
1648 /* Fill in Empty Resource Fork Header. */
1649 init_empty_resource_fork(rsrcforkhdr
);
1651 filehdr
->entries
[1].length
= sizeof(rsrcfork_header_t
);
1652 writesize
= ATTR_BUF_SIZE
;
1654 filehdr
->entries
[0].length
+= delta
;
1655 filehdr
->entries
[1].offset
+= delta
;
1657 /* Fill in Attribute Header. */
1658 attrhdr
->magic
= ATTR_HDR_MAGIC
;
1659 attrhdr
->debug_tag
= (u_int32_t
)va
.va_fileid
;
1660 attrhdr
->total_size
= filehdr
->entries
[1].offset
;
1661 attrhdr
->data_start
= sizeof(attr_header_t
);
1662 attrhdr
->data_length
= 0;
1663 attrhdr
->reserved
[0] = 0;
1664 attrhdr
->reserved
[1] = 0;
1665 attrhdr
->reserved
[2] = 0;
1667 attrhdr
->num_attrs
= 0;
1669 /* Push out new header */
1670 uio_reset(auio
, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1671 uio_addiov(auio
, (uintptr_t)filehdr
, writesize
);
1673 swap_adhdr(filehdr
);
1674 swap_attrhdr(attrhdr
);
1675 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
1676 swap_adhdr(filehdr
);
1677 /* The attribute header gets swapped below. */
1679 if (SWAP32 (attrhdr
->magic
) != ATTR_HDR_MAGIC
||
1680 validate_attrhdr(attrhdr
, ainfop
->rawsize
) != 0) {
1681 printf("get_xattrinfo: invalid attribute header\n");
1684 swap_attrhdr(attrhdr
);
1685 ainfop
->attrhdr
= attrhdr
; /* valid attribute header */
1686 ainfop
->attr_entry
= (attr_entry_t
*)&attrhdr
[1];
1689 if (filehdr
->entries
[i
].type
== AD_RESOURCE
&&
1690 (filehdr
->entries
[i
].length
> sizeof(rsrcfork_header_t
) || setting
)) {
1691 ainfop
->rsrcfork
= &filehdr
->entries
[i
];
1692 if (i
!= (filehdr
->numEntries
- 1)) {
1693 printf("get_xattrinfo: resource fork not last entry\n");
1694 ainfop
->readonly
= 1;
1704 FREE(buffer
, M_TEMP
);
1710 create_xattrfile(vnode_t xvp
, u_int32_t fileid
, vfs_context_t context
)
1713 rsrcfork_header_t
*rsrcforkhdr
;
1719 MALLOC(buffer
, void *, ATTR_BUF_SIZE
, M_TEMP
, M_WAITOK
);
1720 bzero(buffer
, ATTR_BUF_SIZE
);
1722 xah
= (attr_header_t
*)buffer
;
1723 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1724 uio_addiov(auio
, (uintptr_t)buffer
, ATTR_BUF_SIZE
);
1725 rsrcforksize
= sizeof(rsrcfork_header_t
);
1726 rsrcforkhdr
= (rsrcfork_header_t
*) ((char *)buffer
+ ATTR_BUF_SIZE
- rsrcforksize
);
1728 /* Fill in Apple Double Header. */
1729 xah
->appledouble
.magic
= SWAP32 (ADH_MAGIC
);
1730 xah
->appledouble
.version
= SWAP32 (ADH_VERSION
);
1731 xah
->appledouble
.numEntries
= SWAP16 (2);
1732 xah
->appledouble
.entries
[0].type
= SWAP32 (AD_FINDERINFO
);
1733 xah
->appledouble
.entries
[0].offset
= SWAP32 (offsetof(apple_double_header_t
, finfo
));
1734 xah
->appledouble
.entries
[0].length
= SWAP32 (ATTR_BUF_SIZE
- offsetof(apple_double_header_t
, finfo
) - rsrcforksize
);
1735 xah
->appledouble
.entries
[1].type
= SWAP32 (AD_RESOURCE
);
1736 xah
->appledouble
.entries
[1].offset
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
1737 xah
->appledouble
.entries
[1].length
= SWAP32 (rsrcforksize
);
1738 bcopy(ADH_MACOSX
, xah
->appledouble
.filler
, sizeof(xah
->appledouble
.filler
));
1740 /* Fill in Attribute Header. */
1741 xah
->magic
= SWAP32 (ATTR_HDR_MAGIC
);
1742 xah
->debug_tag
= SWAP32 (fileid
);
1743 xah
->total_size
= SWAP32 (ATTR_BUF_SIZE
- rsrcforksize
);
1744 xah
->data_start
= SWAP32 (sizeof(attr_header_t
));
1746 /* Fill in Empty Resource Fork Header. */
1747 init_empty_resource_fork(rsrcforkhdr
);
1750 error
= VNOP_WRITE(xvp
, auio
, 0, context
);
1753 FREE(buffer
, M_TEMP
);
1759 init_empty_resource_fork(rsrcfork_header_t
* rsrcforkhdr
)
1761 bzero(rsrcforkhdr
, sizeof(rsrcfork_header_t
));
1762 rsrcforkhdr
->fh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
1763 rsrcforkhdr
->fh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
1764 rsrcforkhdr
->fh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
1765 rsrcforkhdr
->mh_DataOffset
= SWAP32 (RF_FIRST_RESOURCE
);
1766 rsrcforkhdr
->mh_MapOffset
= SWAP32 (RF_FIRST_RESOURCE
);
1767 rsrcforkhdr
->mh_MapLength
= SWAP32 (RF_NULL_MAP_LENGTH
);
1768 rsrcforkhdr
->mh_Types
= SWAP16 (RF_NULL_MAP_LENGTH
- 2 );
1769 rsrcforkhdr
->mh_Names
= SWAP16 (RF_NULL_MAP_LENGTH
);
1770 rsrcforkhdr
->typeCount
= SWAP16 (-1);
1771 bcopy(RF_EMPTY_TAG
, rsrcforkhdr
->systemData
, sizeof(RF_EMPTY_TAG
));
1775 rel_xattrinfo(attr_info_t
*ainfop
)
1777 FREE(ainfop
->filehdr
, M_TEMP
);
1778 bzero(ainfop
, sizeof(attr_info_t
));
1782 write_xattrinfo(attr_info_t
*ainfop
)
1787 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1788 uio_addiov(auio
, (uintptr_t)ainfop
->filehdr
, ainfop
->iosize
);
1790 swap_adhdr(ainfop
->filehdr
);
1791 swap_attrhdr(ainfop
->attrhdr
);
1793 error
= VNOP_WRITE(ainfop
->filevp
, auio
, 0, ainfop
->context
);
1795 swap_adhdr(ainfop
->filehdr
);
1796 swap_attrhdr(ainfop
->attrhdr
);
1800 #if BYTE_ORDER == LITTLE_ENDIAN
1802 * Endian swap apple double header
1805 swap_adhdr(apple_double_header_t
*adh
)
1810 count
= (adh
->magic
== ADH_MAGIC
) ? adh
->numEntries
: SWAP16(adh
->numEntries
);
1812 adh
->magic
= SWAP32 (adh
->magic
);
1813 adh
->version
= SWAP32 (adh
->version
);
1814 adh
->numEntries
= SWAP16 (adh
->numEntries
);
1816 for (i
= 0; i
< count
; i
++) {
1817 adh
->entries
[i
].type
= SWAP32 (adh
->entries
[i
].type
);
1818 adh
->entries
[i
].offset
= SWAP32 (adh
->entries
[i
].offset
);
1819 adh
->entries
[i
].length
= SWAP32 (adh
->entries
[i
].length
);
1824 * Endian swap extended attributes header
1827 swap_attrhdr(attr_header_t
*ah
)
1833 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
1835 ah
->magic
= SWAP32 (ah
->magic
);
1836 ah
->debug_tag
= SWAP32 (ah
->debug_tag
);
1837 ah
->total_size
= SWAP32 (ah
->total_size
);
1838 ah
->data_start
= SWAP32 (ah
->data_start
);
1839 ah
->data_length
= SWAP32 (ah
->data_length
);
1840 ah
->flags
= SWAP16 (ah
->flags
);
1841 ah
->num_attrs
= SWAP16 (ah
->num_attrs
);
1843 ae
= (attr_entry_t
*)(&ah
[1]);
1844 for (i
= 0; i
< count
; i
++, ae
= ATTR_NEXT(ae
)) {
1845 ae
->offset
= SWAP32 (ae
->offset
);
1846 ae
->length
= SWAP32 (ae
->length
);
1847 ae
->flags
= SWAP16 (ae
->flags
);
1853 * Validate attributes header contents
1856 validate_attrhdr(attr_header_t
*ah
, size_t bufsize
)
1866 bufend
= (u_int8_t
*)ah
+ bufsize
;
1867 count
= (ah
->magic
== ATTR_HDR_MAGIC
) ? ah
->num_attrs
: SWAP16(ah
->num_attrs
);
1869 ae
= (attr_entry_t
*)(&ah
[1]);
1870 for (i
= 0; i
< count
&& (u_int8_t
*)ae
< bufend
; i
++, ae
= ATTR_NEXT(ae
)) {
1872 return (i
< count
? EINVAL
: 0);
1876 // "start" & "end" are byte offsets in the file.
1877 // "to" is the byte offset we want to move the
1878 // data to. "to" should be > "start".
1880 // we do the copy backwards to avoid problems if
1881 // there's an overlap.
1884 shift_data_down(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
1887 size_t chunk
, orig_chunk
;
1890 ucred_t ucred
= vfs_context_ucred(context
);
1891 proc_t p
= vfs_context_proc(context
);
1893 if (delta
== 0 || len
== 0) {
1903 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
1907 for(pos
=start
+len
-chunk
; pos
>= start
; pos
-=chunk
) {
1908 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1910 printf("xattr:shift_data: error reading data @ %lld (read %d of %d) (%d)\n",
1911 pos
, ret
, chunk
, ret
);
1915 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
+ delta
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1917 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %d) (%d)\n",
1918 pos
+delta
, ret
, chunk
, ret
);
1922 if ((pos
- chunk
) < start
) {
1923 chunk
= pos
- start
;
1925 if (chunk
== 0) { // we're all done
1930 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
1937 shift_data_up(vnode_t xvp
, off_t start
, size_t len
, off_t delta
, vfs_context_t context
)
1940 size_t chunk
, orig_chunk
;
1944 ucred_t ucred
= vfs_context_ucred(context
);
1945 proc_t p
= vfs_context_proc(context
);
1947 if (delta
== 0 || len
== 0) {
1958 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buff
, chunk
)) {
1962 for(pos
= start
; pos
< end
; pos
+= chunk
) {
1963 ret
= vn_rdwr(UIO_READ
, xvp
, buff
, chunk
, pos
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1965 printf("xattr:shift_data: error reading data @ %lld (read %d of %d) (%d)\n",
1966 pos
, ret
, chunk
, ret
);
1970 ret
= vn_rdwr(UIO_WRITE
, xvp
, buff
, chunk
, pos
- delta
, UIO_SYSSPACE
, IO_NODELOCKED
, ucred
, &iolen
, p
);
1972 printf("xattr:shift_data: error writing data @ %lld (wrote %d of %d) (%d)\n",
1973 pos
+delta
, ret
, chunk
, ret
);
1977 if ((pos
+ chunk
) > end
) {
1980 if (chunk
== 0) { // we're all done
1985 kmem_free(kernel_map
, (vm_offset_t
)buff
, orig_chunk
);
1991 lock_xattrfile(vnode_t xvp
, short locktype
, vfs_context_t context
)
1995 lf
.l_whence
= SEEK_SET
;
1998 lf
.l_type
= locktype
; /* F_WRLCK or F_RDLCK */
1999 /* Note: id is just a kernel address that's not a proc */
2000 return VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_SETLK
, &lf
, F_FLOCK
, context
);
2004 unlock_xattrfile(vnode_t xvp
, vfs_context_t context
)
2008 lf
.l_whence
= SEEK_SET
;
2011 lf
.l_type
= F_UNLCK
;
2012 /* Note: id is just a kernel address that's not a proc */
2013 return VNOP_ADVLOCK(xvp
, (caddr_t
)xvp
, F_UNLCK
, &lf
, F_FLOCK
, context
);