2 * Copyright (c) 2000-2015 Apple 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@
30 * hfs_attrlist.c - HFS attribute list processing
32 * Copyright (c) 1998-2002, Apple Inc. All Rights Reserved.
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
41 #include <sys/unistd.h>
43 #include <sys/kauth.h>
44 #include <sys/fsctl.h>
46 #include <kern/locks.h>
49 #include "hfs_cnode.h"
50 #include "hfs_mount.h"
52 #include "hfs_attrlist.h"
53 #include "hfs_btreeio.h"
54 #include "hfs_cprotect.h"
56 /* Packing routines: */
58 static void packnameattr(struct attrblock
*abp
, struct vnode
*vp
,
59 const u_int8_t
*name
, int namelen
);
61 static void packcommonattr(struct attrblock
*abp
, struct hfsmount
*hfsmp
,
62 struct vnode
*vp
, struct cat_desc
* cdp
,
63 struct cat_attr
* cap
, struct vfs_context
*ctx
);
65 static void packfileattr(struct attrblock
*abp
, struct hfsmount
*hfsmp
,
66 struct cat_attr
*cattrp
, struct cat_fork
*datafork
,
67 struct cat_fork
*rsrcfork
, struct vnode
*vp
);
69 static void packdirattr(struct attrblock
*abp
, struct hfsmount
*hfsmp
,
70 struct vnode
*vp
, struct cat_desc
* descp
,
71 struct cat_attr
* cattrp
);
73 static u_int32_t
hfs_real_user_access(vnode_t vp
, vfs_context_t ctx
);
75 static void get_vattr_data_for_attrs(struct attrlist
*, struct vnode_attr
*,
76 struct hfsmount
*, struct vnode
*, struct cat_desc
*, struct cat_attr
*,
77 struct cat_fork
*, struct cat_fork
*, vfs_context_t
);
79 static void vattr_data_for_common_attrs(struct attrlist
*, struct vnode_attr
*,
80 struct hfsmount
*, struct vnode
*, struct cat_desc
*, struct cat_attr
*,
83 static void vattr_data_for_dir_attrs(struct attrlist
*, struct vnode_attr
*,
84 struct hfsmount
*, struct vnode
*, struct cat_desc
*, struct cat_attr
*);
86 static void vattr_data_for_file_attrs(struct attrlist
*, struct vnode_attr
*,
87 struct hfsmount
*, struct cat_attr
*, struct cat_fork
*, struct cat_fork
*,
90 static int hfs_readdirattr_internal(struct vnode
*, struct attrlist
*,
91 struct vnode_attr
*, uio_t
, uint64_t, int, uint32_t *, int *, int *,
95 * readdirattr operation will return attributes for the items in the
96 * directory specified.
98 * It does not do . and .. entries. The problem is if you are at the root of the
99 * hfs directory and go to .. you could be crossing a mountpoint into a
100 * different (ufs) file system. The attributes that apply for it may not
101 * apply for the file system you are doing the readdirattr on. To make life
102 * simpler, this call will only return entries in its directory, hfs like.
105 hfs_vnop_readdirattr(ap
)
106 struct vnop_readdirattr_args
/* {
108 struct attrlist *a_alist;
114 u_long *a_actualcount;
115 vfs_context_t a_context;
119 struct attrlist
*alist
= ap
->a_alist
;
121 /* Check for invalid options and buffer space. */
122 if (((ap
->a_options
& ~(FSOPT_NOINMEMUPDATE
| FSOPT_NOFOLLOW
)) != 0) ||
123 (ap
->a_maxcount
<= 0)) {
127 * Reject requests for unsupported attributes.
129 if ((alist
->bitmapcount
!= ATTR_BIT_MAP_COUNT
) ||
130 (alist
->commonattr
& ~HFS_ATTR_CMN_VALID
) ||
131 (alist
->volattr
!= 0) ||
132 (alist
->dirattr
& ~HFS_ATTR_DIR_VALID
) ||
133 (alist
->fileattr
& ~HFS_ATTR_FILE_VALID
) ||
134 (alist
->forkattr
!= 0)) {
138 error
= hfs_readdirattr_internal(ap
->a_vp
, alist
, NULL
, ap
->a_uio
,
139 (uint64_t)ap
->a_options
, ap
->a_maxcount
, ap
->a_newstate
,
140 ap
->a_eofflag
, (int *)ap
->a_actualcount
, ap
->a_context
);
147 * getattrlistbulk, like readdirattr, will return attributes for the items in
148 * the directory specified.
150 * It does not do . and .. entries. The problem is if you are at the root of the
151 * hfs directory and go to .. you could be crossing a mountpoint into a
152 * different (ufs) file system. The attributes that apply for it may not
153 * apply for the file system you are doing the readdirattr on. To make life
154 * simpler, this call will only return entries in its directory, hfs like.
157 hfs_vnop_getattrlistbulk(ap
)
158 struct vnop_getattrlistbulk_args
/* {
159 struct vnodeop_desc *a_desc;
161 struct attrlist *a_alist;
162 struct vnode_attr *a_vap;
167 int32_t *a_actualcount;
168 vfs_context_t a_context;
173 error
= hfs_readdirattr_internal(ap
->a_vp
, ap
->a_alist
, ap
->a_vap
,
174 ap
->a_uio
, (uint64_t)ap
->a_options
, 0, NULL
, ap
->a_eofflag
,
175 (int *)ap
->a_actualcount
, ap
->a_context
);
181 * Common function for both hfs_vnop_readdirattr and hfs_vnop_getattrlistbulk.
182 * This either fills in a vnode_attr structure or fills in an attrbute buffer
183 * Currently the difference in behaviour required for the two vnops is keyed
184 * on whether the passed in vnode_attr pointer is null or not. If the pointer
185 * is null we fill in buffer passed and if it is not null we fill in the fields
186 * of the vnode_attr structure.
189 hfs_readdirattr_internal(struct vnode
*dvp
, struct attrlist
*alist
,
190 struct vnode_attr
*vap
, uio_t uio
, uint64_t options
, int maxcount
,
191 uint32_t *newstate
, int *eofflag
, int *actualcount
, vfs_context_t ctx
)
194 struct hfsmount
* hfsmp
;
195 u_int32_t fixedblocksize
;
196 u_int32_t maxattrblocksize
= 0;
197 u_int32_t currattrbufsize
;
198 void *attrbufptr
= NULL
;
199 void *attrptr
= NULL
;
201 caddr_t namebuf
= NULL
;
202 struct attrblock attrblk
;
206 struct cat_desc
*lastdescp
= NULL
;
207 struct cat_entrylist
*ce_list
= NULL
;
208 directoryhint_t
*dirhint
= NULL
;
212 u_int32_t dirchg
= 0;
214 int internal_actualcount
;
215 int internal_eofflag
;
217 /* Lets makse sure we have something assign to actualcount always, min change required */
218 if (actualcount
== NULL
) {
219 actualcount
= &internal_actualcount
;
221 /* Lets makse sure we have something assign to eofflag always, min change required */
222 if (eofflag
== NULL
) {
223 eofflag
= &internal_eofflag
;
229 if ((uio_resid(uio
) <= 0) || (uio_iovcnt(uio
) > 1))
232 if (VTOC(dvp
)->c_bsdflags
& UF_COMPRESSED
) {
233 int compressed
= hfs_file_is_compressed(VTOC(dvp
), 0); /* 0 == take the cnode lock */
236 error
= check_for_dataless_file(dvp
, NAMESPACE_HANDLER_READ_OP
);
244 * Take an exclusive directory lock since we manipulate the directory hints
246 if ((error
= hfs_lock(VTOC(dvp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
252 dirchg
= dcp
->c_dirchangecnt
;
254 /* Extract directory index and tag (sequence number) from uio_offset */
255 index
= uio_offset(uio
) & HFS_INDEX_MASK
;
256 tag
= uio_offset(uio
) & ~HFS_INDEX_MASK
;
259 * We can't just use the valence as an optimization to avoid
260 * going to the catalog. It might be wrong (== 0), and that would
261 * cause us to avoid iterating the directory when it might actually have
262 * contents. Instead, use the catalog to tell us when we've hit EOF
266 /* Get a buffer to hold packed attributes. */
267 fixedblocksize
= (sizeof(u_int32_t
) + hfs_attrblksize(alist
)); /* 4 bytes for length */
270 maxattrblocksize
= fixedblocksize
;
271 if (alist
->commonattr
& ATTR_CMN_NAME
)
272 maxattrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
274 attrbufptr
= hfs_malloc(maxattrblocksize
);
275 attrptr
= attrbufptr
;
276 varptr
= (char *)attrbufptr
+ fixedblocksize
; /* Point to variable-length storage */
278 if ((alist
->commonattr
& ATTR_CMN_NAME
) && !vap
->va_name
) {
279 namebuf
= hfs_malloc(MAXPATHLEN
);
284 vap
->va_name
= namebuf
;
287 /* Get a detached directory hint (cnode must be locked exclusive) */
288 dirhint
= hfs_getdirhint(dcp
, ((index
- 1) & HFS_INDEX_MASK
) | tag
, TRUE
);
290 /* Hide tag from catalog layer. */
291 dirhint
->dh_index
&= HFS_INDEX_MASK
;
292 if (dirhint
->dh_index
== HFS_INDEX_MASK
) {
293 dirhint
->dh_index
= -1;
297 * Obtain a list of catalog entries and pack their attributes until
298 * the output buffer is full or maxcount entries have been packed.
302 * Constrain our list size.
304 maxentries
= uio_resid(uio
) / (fixedblocksize
+ HFS_AVERAGE_NAME_SIZE
);
305 /* There is maxcount for the bulk vnop */
307 maxentries
= min(maxentries
, maxcount
);
308 maxentries
= min(maxentries
, MAXCATENTRIES
);
309 if (maxentries
< 1) {
314 /* Initialize a catalog entry list. */
315 ce_list
= hfs_mallocz(CE_LIST_SIZE(maxentries
));
316 ce_list
->maxentries
= maxentries
;
319 * Populate the ce_list from the catalog file.
321 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
323 error
= cat_getentriesattr(hfsmp
, dirhint
, ce_list
, &reachedeof
);
324 /* Don't forget to release the descriptors later! */
326 hfs_systemfile_unlock(hfsmp
, lockflags
);
328 if ((error
== ENOENT
) || (reachedeof
!= 0)) {
336 dcp
->c_touch_acctime
= TRUE
;
339 * Check for a FS corruption in the valence. We're holding the cnode lock
340 * exclusive since we need to serialize the directory hints, so if we found
341 * that the valence reported 0, but we actually found some items here, then
342 * silently minimally self-heal and bump the valence to 1.
344 if ((dcp
->c_entries
== 0) && (ce_list
->realentries
> 0)) {
346 dcp
->c_flag
|= C_MODIFIED
;
347 printf("%s : repairing valence to non-zero!\n", __FUNCTION__
);
348 /* force an update on dcp while we're still holding the lock. */
353 * Drop the directory lock so we don't deadlock when we:
354 * - acquire a child cnode lock
355 * - make calls to vnode_authorize()
356 * - make calls to kauth_cred_ismember_gid()
361 /* Process the catalog entries. */
362 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
363 struct cnode
*cp
= NULL
;
364 struct vnode
*vp
= NULL
;
365 struct cat_desc
* cdescp
;
366 struct cat_attr
* cattrp
;
367 struct cat_fork c_datafork
;
368 struct cat_fork c_rsrcfork
;
370 bzero(&c_datafork
, sizeof(c_datafork
));
371 bzero(&c_rsrcfork
, sizeof(c_rsrcfork
));
372 cdescp
= &ce_list
->entry
[i
].ce_desc
;
373 cattrp
= &ce_list
->entry
[i
].ce_attr
;
374 c_datafork
.cf_size
= ce_list
->entry
[i
].ce_datasize
;
375 c_datafork
.cf_blocks
= ce_list
->entry
[i
].ce_datablks
;
376 c_rsrcfork
.cf_size
= ce_list
->entry
[i
].ce_rsrcsize
;
377 c_rsrcfork
.cf_blocks
= ce_list
->entry
[i
].ce_rsrcblks
;
379 if (((alist
->commonattr
& ATTR_CMN_USERACCESS
) &&
380 (cattrp
->ca_recflags
& kHFSHasSecurityMask
))
383 ((alist
->commonattr
& ATTR_CMN_DATA_PROTECT_FLAGS
) && (vap
))
387 * Obtain vnode for our vnode_authorize() calls.
389 if (hfs_vget(hfsmp
, cattrp
->ca_fileid
, &vp
, 0, 0) != 0) {
392 } else if (vap
|| !(options
& FSOPT_NOINMEMUPDATE
)) {
393 /* Get in-memory cnode data (if any). */
394 vp
= hfs_chash_getvnode(hfsmp
, cattrp
->ca_fileid
, 0, 0, 0);
398 /* Only use cnode's decriptor for non-hardlinks */
399 if (!(cp
->c_flag
& C_HARDLINK
))
400 cdescp
= &cp
->c_desc
;
401 cattrp
= &cp
->c_attr
;
402 if (cp
->c_datafork
) {
403 c_datafork
.cf_size
= cp
->c_datafork
->ff_size
;
404 c_datafork
.cf_blocks
= cp
->c_datafork
->ff_blocks
;
406 if (cp
->c_rsrcfork
) {
407 c_rsrcfork
.cf_size
= cp
->c_rsrcfork
->ff_size
;
408 c_rsrcfork
.cf_blocks
= cp
->c_rsrcfork
->ff_blocks
;
410 /* All done with cnode. */
416 *((u_int32_t
*)attrptr
) = 0;
417 attrptr
= ((u_int32_t
*)attrptr
) + 1;
418 attrblk
.ab_attrlist
= alist
;
419 attrblk
.ab_attrbufpp
= &attrptr
;
420 attrblk
.ab_varbufpp
= &varptr
;
421 attrblk
.ab_flags
= 0;
422 attrblk
.ab_blocksize
= maxattrblocksize
;
423 attrblk
.ab_context
= ctx
;
425 /* Pack catalog entries into attribute buffer. */
426 hfs_packattrblk(&attrblk
, hfsmp
, vp
, cdescp
, cattrp
, &c_datafork
, &c_rsrcfork
, ctx
);
427 currattrbufsize
= ((char *)varptr
- (char *)attrbufptr
);
429 /* All done with vnode. */
435 /* Make sure there's enough buffer space remaining. */
436 // LP64todo - fix this!
437 if (uio_resid(uio
) < 0 ||
438 currattrbufsize
> (u_int32_t
)uio_resid(uio
)) {
441 *((u_int32_t
*)attrbufptr
) = currattrbufsize
;
442 error
= uiomove((caddr_t
)attrbufptr
, currattrbufsize
, uio
);
443 if (error
!= E_NONE
) {
446 attrptr
= attrbufptr
;
447 /* Point to variable-length storage */
448 varptr
= (char *)attrbufptr
+ fixedblocksize
;
449 /* Save the last valid catalog entry */
450 lastdescp
= &ce_list
->entry
[i
].ce_desc
;
454 /* Termination checks */
455 if ((--maxcount
<= 0) ||
456 // LP64todo - fix this!
457 uio_resid(uio
) < 0 ||
458 ((u_int32_t
)uio_resid(uio
) < (fixedblocksize
+ HFS_AVERAGE_NAME_SIZE
))){
463 size_t orig_resid
= (size_t)uio_resid(uio
);
466 get_vattr_data_for_attrs(alist
, vap
, hfsmp
, vp
, cdescp
,
467 cattrp
, &c_datafork
, &c_rsrcfork
, ctx
);
470 if ((alist
->commonattr
& ATTR_CMN_DATA_PROTECT_FLAGS
) &&
472 cp_key_class_t
class;
474 if (!cp_vnode_getclass(vp
, &class)) {
475 VATTR_RETURN(vap
, va_dataprotect_class
,
480 error
= vfs_attr_pack(vp
, uio
, alist
, options
, vap
,
483 /* All done with vnode. */
489 resid
= uio_resid(uio
);
491 /* Was this entry succesful ? */
492 if (error
|| resid
== orig_resid
)
495 /* Save the last valid catalog entry */
496 lastdescp
= &ce_list
->entry
[i
].ce_desc
;
500 /* Do we have the bare minimum for the next entry ? */
501 if (resid
< sizeof(uint32_t))
504 } /* for each catalog entry */
507 * If we couldn't fit all the entries requested in the user's buffer,
510 if (*eofflag
&& (*actualcount
< (int)ce_list
->realentries
))
513 /* If we skipped catalog entries for reserved files that should
514 * not be listed in namespace, update the index accordingly.
516 if (ce_list
->skipentries
) {
517 index
+= ce_list
->skipentries
;
518 ce_list
->skipentries
= 0;
522 * If there are more entries then save the last name.
523 * Key this behavior based on whether or not we observed EOFFLAG.
525 * Do not use the valence as a way to determine if we hit EOF, since
526 * it can be wrong. Use the catalog's output only.
528 if ((*(eofflag
) == 0) && (lastdescp
!= NULL
)) {
530 /* Remember last entry */
531 if ((dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) &&
532 (dirhint
->dh_desc
.cd_nameptr
!= NULL
)) {
533 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
534 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
536 if (lastdescp
->cd_nameptr
!= NULL
) {
537 dirhint
->dh_desc
.cd_namelen
= lastdescp
->cd_namelen
;
538 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)
539 vfs_addname((const char *)lastdescp
->cd_nameptr
, lastdescp
->cd_namelen
, 0, 0);
540 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
542 dirhint
->dh_desc
.cd_namelen
= 0;
543 dirhint
->dh_desc
.cd_nameptr
= NULL
;
545 dirhint
->dh_index
= index
- 1;
546 dirhint
->dh_desc
.cd_cnid
= lastdescp
->cd_cnid
;
547 dirhint
->dh_desc
.cd_hint
= lastdescp
->cd_hint
;
548 dirhint
->dh_desc
.cd_encoding
= lastdescp
->cd_encoding
;
551 /* All done with the catalog descriptors. */
552 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
)
553 cat_releasedesc(&ce_list
->entry
[i
].ce_desc
);
554 ce_list
->realentries
= 0;
556 (void) hfs_lock(VTOC(dvp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
);
560 /* Pack directory index and tag into uio_offset. */
561 while (tag
== 0) tag
= (++dcp
->c_dirhinttag
) << HFS_INDEX_BITS
;
562 uio_setoffset(uio
, index
| tag
);
563 dirhint
->dh_index
|= tag
;
570 * Drop directory hint on error or if there are no more entries,
571 * only if EOF was seen.
574 if ((error
!= 0) || *(eofflag
))
575 hfs_reldirhint(dcp
, dirhint
);
577 hfs_insertdirhint(dcp
, dirhint
);
580 hfs_free(namebuf
, MAXPATHLEN
);
584 hfs_free(attrbufptr
, maxattrblocksize
);
586 hfs_free(ce_list
, CE_LIST_SIZE(maxentries
));
588 if (vap
&& *actualcount
&& error
)
596 /*==================== Attribute list support routines ====================*/
599 * Pack cnode attributes into an attribute block.
602 hfs_packattrblk(struct attrblock
*abp
,
603 struct hfsmount
*hfsmp
,
605 struct cat_desc
*descp
,
606 struct cat_attr
*attrp
,
607 struct cat_fork
*datafork
,
608 struct cat_fork
*rsrcfork
,
609 struct vfs_context
*ctx
)
611 struct attrlist
*attrlistp
= abp
->ab_attrlist
;
613 if (attrlistp
->commonattr
)
614 packcommonattr(abp
, hfsmp
, vp
, descp
, attrp
, ctx
);
616 if (attrlistp
->dirattr
&& S_ISDIR(attrp
->ca_mode
))
617 packdirattr(abp
, hfsmp
, vp
, descp
,attrp
);
619 if (attrlistp
->fileattr
&& !S_ISDIR(attrp
->ca_mode
))
620 packfileattr(abp
, hfsmp
, attrp
, datafork
, rsrcfork
, vp
);
624 mountpointname(struct mount
*mp
)
626 struct vfsstatfs
*vsfs
= vfs_statfs(mp
);
628 size_t namelength
= strlen(vsfs
->f_mntonname
);
636 * Look backwards through the name string, looking for
637 * the first slash encountered (which must precede the
638 * last part of the pathname).
640 for (c
= vsfs
->f_mntonname
+ namelength
- 1;
641 namelength
> 0; --c
, --namelength
) {
644 } else if (foundchars
) {
649 return vsfs
->f_mntonname
;
655 struct attrblock
*abp
,
657 const u_int8_t
*name
,
661 struct attrreference
* attr_refptr
;
664 u_int32_t attrlength
;
667 /* A cnode's name may be incorrect for the root of a mounted
668 * filesystem (it can be mounted on a different directory name
669 * than the name of the volume, such as "blah-1"). So for the
670 * root directory, it's best to return the last element of the
671 location where the volume's mounted:
673 if ((vp
!= NULL
) && vnode_isvroot(vp
) &&
674 (mpname
= mountpointname(vnode_mount(vp
)))) {
675 mpnamelen
= strlen(mpname
);
677 /* Trim off any trailing slashes: */
678 while ((mpnamelen
> 0) && (mpname
[mpnamelen
-1] == '/'))
681 /* If there's anything left, use it instead of the volume's name */
683 name
= (u_int8_t
*)mpname
;
692 varbufptr
= *abp
->ab_varbufpp
;
693 attr_refptr
= (struct attrreference
*)(*abp
->ab_attrbufpp
);
695 attrlength
= namelen
+ 1;
696 attr_refptr
->attr_dataoffset
= (char *)varbufptr
- (char *)attr_refptr
;
697 attr_refptr
->attr_length
= attrlength
;
698 (void) strncpy((char *)varbufptr
, (const char *) name
, attrlength
);
700 * Advance beyond the space just allocated and
701 * round up to the next 4-byte boundary:
703 varbufptr
= ((char *)varbufptr
) + attrlength
+ ((4 - (attrlength
& 3)) & 3);
706 *abp
->ab_attrbufpp
= attr_refptr
;
707 *abp
->ab_varbufpp
= varbufptr
;
712 struct attrblock
*abp
,
713 struct hfsmount
*hfsmp
,
715 struct cat_desc
* cdp
,
716 struct cat_attr
* cap
,
717 struct vfs_context
* ctx
)
719 attrgroup_t attr
= abp
->ab_attrlist
->commonattr
;
720 struct mount
*mp
= HFSTOVFS(hfsmp
);
721 void *attrbufptr
= *abp
->ab_attrbufpp
;
722 void *varbufptr
= *abp
->ab_varbufpp
;
723 boolean_t is_64_bit
= proc_is64bit(vfs_context_proc(ctx
));
727 if (attr
& (ATTR_CMN_OWNERID
| ATTR_CMN_GRPID
)) {
728 cuid
= kauth_cred_getuid(vfs_context_ucred(ctx
));
732 if (ATTR_CMN_NAME
& attr
) {
733 packnameattr(abp
, vp
, cdp
->cd_nameptr
, cdp
->cd_namelen
);
734 attrbufptr
= *abp
->ab_attrbufpp
;
735 varbufptr
= *abp
->ab_varbufpp
;
737 if (ATTR_CMN_DEVID
& attr
) {
738 *((dev_t
*)attrbufptr
) = hfsmp
->hfs_raw_dev
;
739 attrbufptr
= ((dev_t
*)attrbufptr
) + 1;
741 if (ATTR_CMN_FSID
& attr
) {
744 fsid
.val
[0] = hfsmp
->hfs_raw_dev
;
745 fsid
.val
[1] = vfs_typenum(mp
);
746 *((fsid_t
*)attrbufptr
) = fsid
;
747 attrbufptr
= ((fsid_t
*)attrbufptr
) + 1;
749 if (ATTR_CMN_OBJTYPE
& attr
) {
750 *((fsobj_type_t
*)attrbufptr
) = IFTOVT(cap
->ca_mode
);
751 attrbufptr
= ((fsobj_type_t
*)attrbufptr
) + 1;
753 if (ATTR_CMN_OBJTAG
& attr
) {
754 *((fsobj_tag_t
*)attrbufptr
) = VT_HFS
;
755 attrbufptr
= ((fsobj_tag_t
*)attrbufptr
) + 1;
758 * Exporting file IDs from HFS Plus:
760 * For "normal" files the c_fileid is the same value as the
761 * c_cnid. But for hard link files, they are different - the
762 * c_cnid belongs to the active directory entry (ie the link)
763 * and the c_fileid is for the actual inode (ie the data file).
765 * The stat call (getattr) will always return the c_fileid
766 * and Carbon APIs, which are hardlink-ignorant, will always
767 * receive the c_cnid (from getattrlist).
769 if (ATTR_CMN_OBJID
& attr
) {
770 ((fsobj_id_t
*)attrbufptr
)->fid_objno
= cdp
->cd_cnid
;
771 ((fsobj_id_t
*)attrbufptr
)->fid_generation
= 0;
772 attrbufptr
= ((fsobj_id_t
*)attrbufptr
) + 1;
774 if (ATTR_CMN_OBJPERMANENTID
& attr
) {
775 ((fsobj_id_t
*)attrbufptr
)->fid_objno
= cdp
->cd_cnid
;
776 ((fsobj_id_t
*)attrbufptr
)->fid_generation
= 0;
777 attrbufptr
= ((fsobj_id_t
*)attrbufptr
) + 1;
779 if (ATTR_CMN_PAROBJID
& attr
) {
780 ((fsobj_id_t
*)attrbufptr
)->fid_objno
= cdp
->cd_parentcnid
;
781 ((fsobj_id_t
*)attrbufptr
)->fid_generation
= 0;
782 attrbufptr
= ((fsobj_id_t
*)attrbufptr
) + 1;
784 if (ATTR_CMN_SCRIPT
& attr
) {
785 *((text_encoding_t
*)attrbufptr
) = cdp
->cd_encoding
;
786 attrbufptr
= ((text_encoding_t
*)attrbufptr
) + 1;
788 if (ATTR_CMN_CRTIME
& attr
) {
790 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_itime
;
791 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
792 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
795 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_itime
;
796 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
797 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
800 if (ATTR_CMN_MODTIME
& attr
) {
802 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_mtime
;
803 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
804 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
807 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_mtime
;
808 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
809 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
812 if (ATTR_CMN_CHGTIME
& attr
) {
814 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_ctime
;
815 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
816 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
819 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_ctime
;
820 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
821 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
824 if (ATTR_CMN_ACCTIME
& attr
) {
826 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_atime
;
827 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
828 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
831 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_atime
;
832 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
833 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
836 if (ATTR_CMN_BKUPTIME
& attr
) {
838 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_btime
;
839 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
840 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
843 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_btime
;
844 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
845 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
848 if (ATTR_CMN_FNDRINFO
& attr
) {
849 u_int8_t
*finfo
= NULL
;
850 bcopy(&cap
->ca_finderinfo
, attrbufptr
, sizeof(u_int8_t
) * 32);
851 finfo
= (u_int8_t
*)attrbufptr
;
853 /* Don't expose a symlink's private type/creator. */
854 if (S_ISLNK(cap
->ca_mode
)) {
855 struct FndrFileInfo
*fip
;
857 fip
= (struct FndrFileInfo
*)attrbufptr
;
862 /* advance 16 bytes into the attrbuf */
865 /* also don't expose the date_added or write_gen_counter fields */
866 if (S_ISREG(cap
->ca_mode
) || S_ISLNK(cap
->ca_mode
)) {
867 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)finfo
;
868 extinfo
->document_id
= 0;
869 extinfo
->date_added
= 0;
870 extinfo
->write_gen_counter
= 0;
872 else if (S_ISDIR(cap
->ca_mode
)) {
873 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)finfo
;
874 extinfo
->document_id
= 0;
875 extinfo
->date_added
= 0;
876 extinfo
->write_gen_counter
= 0;
879 attrbufptr
= (char *)attrbufptr
+ sizeof(u_int8_t
) * 32;
881 if (ATTR_CMN_OWNERID
& attr
) {
882 uid_t nuid
= cap
->ca_uid
;
885 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
)
887 else if (nuid
== UNKNOWNUID
)
891 *((uid_t
*)attrbufptr
) = nuid
;
892 attrbufptr
= ((uid_t
*)attrbufptr
) + 1;
894 if (ATTR_CMN_GRPID
& attr
) {
895 gid_t ngid
= cap
->ca_gid
;
898 gid_t cgid
= kauth_cred_getgid(vfs_context_ucred(ctx
));
899 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
)
901 else if (ngid
== UNKNOWNUID
)
905 *((gid_t
*)attrbufptr
) = ngid
;
906 attrbufptr
= ((gid_t
*)attrbufptr
) + 1;
908 if (ATTR_CMN_ACCESSMASK
& attr
) {
910 * [2856576] Since we are dynamically changing the owner, also
911 * effectively turn off the set-user-id and set-group-id bits,
912 * just like chmod(2) would when changing ownership. This prevents
913 * a security hole where set-user-id programs run as whoever is
914 * logged on (or root if nobody is logged in yet!)
916 *((u_int32_t
*)attrbufptr
) = (cap
->ca_uid
== UNKNOWNUID
) ?
917 cap
->ca_mode
& ~(S_ISUID
| S_ISGID
) : cap
->ca_mode
;
918 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
920 if (ATTR_CMN_FLAGS
& attr
) {
921 *((u_int32_t
*)attrbufptr
) = cap
->ca_flags
;
922 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
924 if (ATTR_CMN_USERACCESS
& attr
) {
925 u_int32_t user_access
;
927 /* Take the long path when we have an ACL */
928 if ((vp
!= NULLVP
) && (cap
->ca_recflags
& kHFSHasSecurityMask
)) {
929 user_access
= hfs_real_user_access(vp
, abp
->ab_context
);
931 user_access
= DerivePermissionSummary(cap
->ca_uid
, cap
->ca_gid
,
932 cap
->ca_mode
, mp
, vfs_context_ucred(ctx
), 0);
934 /* Also consider READ-ONLY file system. */
935 if (vfs_flags(mp
) & MNT_RDONLY
) {
936 user_access
&= ~W_OK
;
938 /* Locked objects are not writable either */
939 if ((cap
->ca_flags
& UF_IMMUTABLE
) && (vfs_context_suser(abp
->ab_context
) != 0))
940 user_access
&= ~W_OK
;
941 if ((cap
->ca_flags
& SF_IMMUTABLE
) && (vfs_context_suser(abp
->ab_context
) == 0))
942 user_access
&= ~W_OK
;
944 *((u_int32_t
*)attrbufptr
) = user_access
;
945 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
947 if (ATTR_CMN_FILEID
& attr
) {
948 *((u_int64_t
*)attrbufptr
) = cap
->ca_fileid
;
949 attrbufptr
= ((u_int64_t
*)attrbufptr
) + 1;
951 if (ATTR_CMN_PARENTID
& attr
) {
952 *((u_int64_t
*)attrbufptr
) = cdp
->cd_parentcnid
;
953 attrbufptr
= ((u_int64_t
*)attrbufptr
) + 1;
956 *abp
->ab_attrbufpp
= attrbufptr
;
957 *abp
->ab_varbufpp
= varbufptr
;
962 struct attrblock
*abp
,
963 struct hfsmount
*hfsmp
,
965 struct cat_desc
* descp
,
966 struct cat_attr
* cattrp
)
968 attrgroup_t attr
= abp
->ab_attrlist
->dirattr
;
969 void *attrbufptr
= *abp
->ab_attrbufpp
;
973 * The DIR_LINKCOUNT is the count of real directory hard links.
974 * (i.e. its not the sum of the implied "." and ".." references
975 * typically used in stat's st_nlink field)
977 if (ATTR_DIR_LINKCOUNT
& attr
) {
978 *((u_int32_t
*)attrbufptr
) = cattrp
->ca_linkcount
;
979 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
981 if (ATTR_DIR_ENTRYCOUNT
& attr
) {
982 entries
= cattrp
->ca_entries
;
984 if (descp
->cd_parentcnid
== kHFSRootParentID
) {
985 if (hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
!= 0)
986 --entries
; /* hide private dir */
987 if (hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
!= 0)
988 --entries
; /* hide private dir */
990 ((hfsmp
->vcbAtrb
& kHFSVolumeJournaledMask
) &&
991 (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))
992 entries
-= 2; /* hide the journal files */
995 *((u_int32_t
*)attrbufptr
) = entries
;
996 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
998 if (ATTR_DIR_MOUNTSTATUS
& attr
) {
999 if (vp
!= NULL
&& vnode_mountedhere(vp
) != NULL
)
1000 *((u_int32_t
*)attrbufptr
) = DIR_MNTSTATUS_MNTPOINT
;
1002 *((u_int32_t
*)attrbufptr
) = 0;
1003 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
1005 *abp
->ab_attrbufpp
= attrbufptr
;
1010 struct attrblock
*abp
,
1011 struct hfsmount
*hfsmp
,
1012 struct cat_attr
*cattrp
,
1013 struct cat_fork
*datafork
,
1014 struct cat_fork
*rsrcfork
,
1017 #if !HFS_COMPRESSION
1020 attrgroup_t attr
= abp
->ab_attrlist
->fileattr
;
1021 void *attrbufptr
= *abp
->ab_attrbufpp
;
1022 void *varbufptr
= *abp
->ab_varbufpp
;
1023 u_int32_t allocblksize
;
1025 allocblksize
= HFSTOVCB(hfsmp
)->blockSize
;
1027 off_t datasize
= datafork
->cf_size
;
1028 off_t totalsize
= datasize
+ rsrcfork
->cf_size
;
1030 int handle_compressed
;
1031 handle_compressed
= (cattrp
->ca_flags
& UF_COMPRESSED
);// && hfs_file_is_compressed(VTOC(vp), 1);
1033 if (handle_compressed
) {
1034 if (attr
& (ATTR_FILE_DATALENGTH
|ATTR_FILE_TOTALSIZE
)) {
1035 if ( 0 == hfs_uncompressed_size_of_compressed_file(hfsmp
, vp
, cattrp
->ca_fileid
, &datasize
, 1) ) { /* 1 == don't take the cnode lock */
1036 /* total size of a compressed file is just the data size */
1037 totalsize
= datasize
;
1043 if (ATTR_FILE_LINKCOUNT
& attr
) {
1044 *((u_int32_t
*)attrbufptr
) = cattrp
->ca_linkcount
;
1045 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
1047 if (ATTR_FILE_TOTALSIZE
& attr
) {
1048 *((off_t
*)attrbufptr
) = totalsize
;
1049 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
1051 if (ATTR_FILE_ALLOCSIZE
& attr
) {
1052 *((off_t
*)attrbufptr
) =
1053 (off_t
)cattrp
->ca_blocks
* (off_t
)allocblksize
;
1054 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
1056 if (ATTR_FILE_IOBLOCKSIZE
& attr
) {
1057 *((u_int32_t
*)attrbufptr
) = hfsmp
->hfs_logBlockSize
;
1058 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
1060 if (ATTR_FILE_CLUMPSIZE
& attr
) {
1061 *((u_int32_t
*)attrbufptr
) = hfsmp
->vcbClpSiz
;
1062 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
1064 if (ATTR_FILE_DEVTYPE
& attr
) {
1065 if (S_ISBLK(cattrp
->ca_mode
) || S_ISCHR(cattrp
->ca_mode
))
1066 *((u_int32_t
*)attrbufptr
) = (u_int32_t
)cattrp
->ca_rdev
;
1068 *((u_int32_t
*)attrbufptr
) = 0;
1069 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
1072 if (ATTR_FILE_DATALENGTH
& attr
) {
1073 *((off_t
*)attrbufptr
) = datasize
;
1074 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
1078 /* fake the data fork size on a decmpfs compressed file to reflect the
1079 * uncompressed size. This ensures proper reading and copying of these files.
1080 * NOTE: we may need to get the vnode here because the vnode parameter
1081 * passed by hfs_vnop_readdirattr() may be null.
1084 if ( handle_compressed
) {
1085 if (attr
& ATTR_FILE_DATAALLOCSIZE
) {
1086 *((off_t
*)attrbufptr
) = (off_t
)rsrcfork
->cf_blocks
* (off_t
)allocblksize
;
1087 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
1089 if (attr
& ATTR_FILE_RSRCLENGTH
) {
1090 *((off_t
*)attrbufptr
) = 0;
1091 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
1093 if (attr
& ATTR_FILE_RSRCALLOCSIZE
) {
1094 *((off_t
*)attrbufptr
) = 0;
1095 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
1101 if (ATTR_FILE_DATAALLOCSIZE
& attr
) {
1102 *((off_t
*)attrbufptr
) = (off_t
)datafork
->cf_blocks
* (off_t
)allocblksize
;
1103 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
1105 if (ATTR_FILE_RSRCLENGTH
& attr
) {
1106 *((off_t
*)attrbufptr
) = rsrcfork
->cf_size
;
1107 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
1109 if (ATTR_FILE_RSRCALLOCSIZE
& attr
) {
1110 *((off_t
*)attrbufptr
) = (off_t
)rsrcfork
->cf_blocks
* (off_t
)allocblksize
;
1111 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
1114 *abp
->ab_attrbufpp
= attrbufptr
;
1115 *abp
->ab_varbufpp
= varbufptr
;
1119 * Calculate the total size of an attribute block.
1122 hfs_attrblksize(struct attrlist
*attrlist
)
1126 int sizeof_timespec
;
1127 boolean_t is_64_bit
= proc_is64bit(current_proc());
1130 sizeof_timespec
= sizeof(struct user64_timespec
);
1132 sizeof_timespec
= sizeof(struct user32_timespec
);
1134 hfs_assert((attrlist
->commonattr
& ~ATTR_CMN_VALIDMASK
) == 0);
1136 hfs_assert((attrlist
->volattr
& ~ATTR_VOL_VALIDMASK
) == 0);
1138 hfs_assert((attrlist
->dirattr
& ~ATTR_DIR_VALIDMASK
) == 0);
1140 hfs_assert((attrlist
->fileattr
& ~ATTR_FILE_VALIDMASK
) == 0);
1142 // disable this because it will break the simulator/build machines each
1143 // time a new _CMNEXT_ bit is added
1144 // hfs_assert(((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0) ||
1145 // ((attrlist->forkattr & ~ATTR_CMNEXT_VALIDMASK) == 0));
1149 if ((a
= attrlist
->commonattr
) != 0) {
1150 if (a
& ATTR_CMN_NAME
) size
+= sizeof(struct attrreference
);
1151 if (a
& ATTR_CMN_DEVID
) size
+= sizeof(dev_t
);
1152 if (a
& ATTR_CMN_FSID
) size
+= sizeof(fsid_t
);
1153 if (a
& ATTR_CMN_OBJTYPE
) size
+= sizeof(fsobj_type_t
);
1154 if (a
& ATTR_CMN_OBJTAG
) size
+= sizeof(fsobj_tag_t
);
1155 if (a
& ATTR_CMN_OBJID
) size
+= sizeof(fsobj_id_t
);
1156 if (a
& ATTR_CMN_OBJPERMANENTID
) size
+= sizeof(fsobj_id_t
);
1157 if (a
& ATTR_CMN_PAROBJID
) size
+= sizeof(fsobj_id_t
);
1158 if (a
& ATTR_CMN_SCRIPT
) size
+= sizeof(text_encoding_t
);
1159 if (a
& ATTR_CMN_CRTIME
) size
+= sizeof_timespec
;
1160 if (a
& ATTR_CMN_MODTIME
) size
+= sizeof_timespec
;
1161 if (a
& ATTR_CMN_CHGTIME
) size
+= sizeof_timespec
;
1162 if (a
& ATTR_CMN_ACCTIME
) size
+= sizeof_timespec
;
1163 if (a
& ATTR_CMN_BKUPTIME
) size
+= sizeof_timespec
;
1164 if (a
& ATTR_CMN_FNDRINFO
) size
+= 32 * sizeof(u_int8_t
);
1165 if (a
& ATTR_CMN_OWNERID
) size
+= sizeof(uid_t
);
1166 if (a
& ATTR_CMN_GRPID
) size
+= sizeof(gid_t
);
1167 if (a
& ATTR_CMN_ACCESSMASK
) size
+= sizeof(u_int32_t
);
1168 if (a
& ATTR_CMN_FLAGS
) size
+= sizeof(u_int32_t
);
1169 if (a
& ATTR_CMN_USERACCESS
) size
+= sizeof(u_int32_t
);
1170 if (a
& ATTR_CMN_FILEID
) size
+= sizeof(u_int64_t
);
1171 if (a
& ATTR_CMN_PARENTID
) size
+= sizeof(u_int64_t
);
1173 if ((a
= attrlist
->dirattr
) != 0) {
1174 if (a
& ATTR_DIR_LINKCOUNT
) size
+= sizeof(u_int32_t
);
1175 if (a
& ATTR_DIR_ENTRYCOUNT
) size
+= sizeof(u_int32_t
);
1176 if (a
& ATTR_DIR_MOUNTSTATUS
) size
+= sizeof(u_int32_t
);
1178 if ((a
= attrlist
->fileattr
) != 0) {
1179 if (a
& ATTR_FILE_LINKCOUNT
) size
+= sizeof(u_int32_t
);
1180 if (a
& ATTR_FILE_TOTALSIZE
) size
+= sizeof(off_t
);
1181 if (a
& ATTR_FILE_ALLOCSIZE
) size
+= sizeof(off_t
);
1182 if (a
& ATTR_FILE_IOBLOCKSIZE
) size
+= sizeof(u_int32_t
);
1183 if (a
& ATTR_FILE_CLUMPSIZE
) size
+= sizeof(u_int32_t
);
1184 if (a
& ATTR_FILE_DEVTYPE
) size
+= sizeof(u_int32_t
);
1185 if (a
& ATTR_FILE_DATALENGTH
) size
+= sizeof(off_t
);
1186 if (a
& ATTR_FILE_DATAALLOCSIZE
) size
+= sizeof(off_t
);
1187 if (a
& ATTR_FILE_RSRCLENGTH
) size
+= sizeof(off_t
);
1188 if (a
& ATTR_FILE_RSRCALLOCSIZE
) size
+= sizeof(off_t
);
1194 #define KAUTH_DIR_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | \
1195 KAUTH_VNODE_ADD_SUBDIRECTORY | \
1196 KAUTH_VNODE_DELETE_CHILD)
1198 #define KAUTH_DIR_READ_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY)
1200 #define KAUTH_DIR_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH)
1202 #define KAUTH_FILE_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA)
1204 #define KAUTH_FILE_READRIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA)
1206 #define KAUTH_FILE_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE)
1210 * Compute the same [expensive] user_access value as getattrlist does
1213 hfs_real_user_access(vnode_t vp
, vfs_context_t ctx
)
1215 u_int32_t user_access
= 0;
1217 if (vnode_isdir(vp
)) {
1218 if (vnode_authorize(vp
, NULLVP
, KAUTH_DIR_WRITE_RIGHTS
, ctx
) == 0)
1219 user_access
|= W_OK
;
1220 if (vnode_authorize(vp
, NULLVP
, KAUTH_DIR_READ_RIGHTS
, ctx
) == 0)
1221 user_access
|= R_OK
;
1222 if (vnode_authorize(vp
, NULLVP
, KAUTH_DIR_EXECUTE_RIGHTS
, ctx
) == 0)
1223 user_access
|= X_OK
;
1225 if (vnode_authorize(vp
, NULLVP
, KAUTH_FILE_WRITE_RIGHTS
, ctx
) == 0)
1226 user_access
|= W_OK
;
1227 if (vnode_authorize(vp
, NULLVP
, KAUTH_FILE_READRIGHTS
, ctx
) == 0)
1228 user_access
|= R_OK
;
1229 if (vnode_authorize(vp
, NULLVP
, KAUTH_FILE_EXECUTE_RIGHTS
, ctx
) == 0)
1230 user_access
|= X_OK
;
1232 return (user_access
);
1237 DerivePermissionSummary(uid_t obj_uid
, gid_t obj_gid
, mode_t obj_mode
,
1238 struct mount
*mp
, kauth_cred_t cred
, __unused
struct proc
*p
)
1240 u_int32_t permissions
;
1242 if (obj_uid
== UNKNOWNUID
)
1243 obj_uid
= kauth_cred_getuid(cred
);
1245 /* User id 0 (root) always gets access. */
1246 if (!suser(cred
, NULL
)) {
1247 permissions
= R_OK
| W_OK
| X_OK
;
1251 /* Otherwise, check the owner. */
1252 if (hfs_owner_rights(VFSTOHFS(mp
), obj_uid
, cred
, NULL
, false) == 0) {
1253 permissions
= ((u_int32_t
)obj_mode
& S_IRWXU
) >> 6;
1257 /* Otherwise, check the groups. */
1258 if (! (((unsigned int)vfs_flags(mp
)) & MNT_UNKNOWNPERMISSIONS
)) {
1261 if (kauth_cred_ismember_gid(cred
, obj_gid
, &is_member
) == 0 && is_member
) {
1262 permissions
= ((u_int32_t
)obj_mode
& S_IRWXG
) >> 3;
1267 /* Otherwise, settle for 'others' access. */
1268 permissions
= (u_int32_t
)obj_mode
& S_IRWXO
;
1271 return (permissions
);
1276 * ===========================================================================
1277 * Support functions for filling up a vnode_attr structure based on attributes
1279 * ===========================================================================
1282 get_vattr_data_for_attrs(struct attrlist
*alp
, struct vnode_attr
*vap
,
1283 struct hfsmount
*hfsmp
, struct vnode
*vp
, struct cat_desc
*descp
,
1284 struct cat_attr
*atrp
, struct cat_fork
*datafork
, struct cat_fork
*rsrcfork
,
1287 if (alp
->commonattr
|| alp
->forkattr
) {
1288 vattr_data_for_common_attrs(alp
, vap
, hfsmp
, vp
, descp
, atrp
,
1292 if (alp
->dirattr
&& S_ISDIR(atrp
->ca_mode
))
1293 vattr_data_for_dir_attrs(alp
, vap
, hfsmp
, vp
, descp
, atrp
);
1295 if (alp
->fileattr
&& !S_ISDIR(atrp
->ca_mode
)) {
1296 vattr_data_for_file_attrs(alp
, vap
, hfsmp
, atrp
, datafork
,
1302 copy_name_attr(struct vnode_attr
*vap
, struct vnode
*vp
, const u_int8_t
*name
,
1307 u_int32_t attrlength
;
1310 /* A cnode's name may be incorrect for the root of a mounted
1311 * filesystem (it can be mounted on a different directory name
1312 * than the name of the volume, such as "blah-1"). So for the
1313 * root directory, it's best to return the last element of the
1314 location where the volume's mounted:
1316 if ((vp
!= NULL
) && vnode_isvroot(vp
) &&
1317 (mpname
= mountpointname(vnode_mount(vp
)))) {
1318 mpnamelen
= strlen(mpname
);
1320 /* Trim off any trailing slashes: */
1321 while ((mpnamelen
> 0) && (mpname
[mpnamelen
-1] == '/'))
1324 /* If there's anything left, use it instead of the volume's name */
1325 if (mpnamelen
> 0) {
1326 name
= (u_int8_t
*)mpname
;
1327 namelen
= mpnamelen
;
1336 attrlength
= namelen
+ 1;
1337 (void) strncpy((char *)vap
->va_name
, (const char *) name
, attrlength
);
1339 * round upto 8 and zero out the rounded up bytes.
1341 attrlength
= min(kHFSPlusMaxFileNameBytes
, ((attrlength
+ 7) & ~0x07));
1342 bzero(vap
->va_name
+ attrlength
, kHFSPlusMaxFileNameBytes
- attrlength
);
1346 vattr_data_for_common_attrs( struct attrlist
*alp
, struct vnode_attr
*vap
,
1347 struct hfsmount
*hfsmp
, struct vnode
*vp
, struct cat_desc
*cdp
,
1348 struct cat_attr
*cap
, vfs_context_t ctx
)
1350 attrgroup_t attr
= alp
->commonattr
;
1351 struct mount
*mp
= HFSTOVFS(hfsmp
);
1355 if (attr
& (ATTR_CMN_OWNERID
| ATTR_CMN_GRPID
)) {
1356 cuid
= kauth_cred_getuid(vfs_context_ucred(ctx
));
1360 if (ATTR_CMN_NAME
& attr
) {
1362 copy_name_attr(vap
, vp
, cdp
->cd_nameptr
,
1364 VATTR_SET_SUPPORTED(vap
, va_name
);
1366 VATTR_CLEAR_SUPPORTED(vap
, va_name
);
1370 if (ATTR_CMN_DEVID
& attr
) {
1371 vap
->va_devid
= hfsmp
->hfs_raw_dev
;
1372 VATTR_SET_SUPPORTED(vap
, va_devid
);
1375 if (ATTR_CMN_FSID
& attr
) {
1376 vap
->va_fsid64
.val
[0] = hfsmp
->hfs_raw_dev
;
1377 vap
->va_fsid64
.val
[1] = vfs_typenum(mp
);
1378 VATTR_SET_SUPPORTED(vap
, va_fsid64
);
1381 * We always provide the objtype even if not asked because VFS helper
1382 * functions depend on knowing the object's type.
1384 vap
->va_objtype
= IFTOVT(cap
->ca_mode
);
1385 VATTR_SET_SUPPORTED(vap
, va_objtype
);
1387 if (ATTR_CMN_OBJTAG
& attr
) {
1388 vap
->va_objtag
= VT_HFS
;
1389 VATTR_SET_SUPPORTED(vap
, va_objtag
);
1392 * Exporting file IDs from HFS Plus:
1394 * For "normal" files the c_fileid is the same value as the
1395 * c_cnid. But for hard link files, they are different - the
1396 * c_cnid belongs to the active directory entry (ie the link)
1397 * and the c_fileid is for the actual inode (ie the data file).
1399 * The stat call (getattr) will always return the c_fileid
1400 * and Carbon APIs, which are hardlink-ignorant, will always
1401 * receive the c_cnid (from getattrlist).
1403 * Forkattrs are now repurposed for Common Extended Attributes.
1405 if ((ATTR_CMN_OBJID
& attr
) || (ATTR_CMN_OBJPERMANENTID
& attr
) ||
1406 alp
->forkattr
& ATTR_CMNEXT_LINKID
) {
1407 vap
->va_linkid
= cdp
->cd_cnid
;
1408 VATTR_SET_SUPPORTED(vap
, va_linkid
);
1411 if (ATTR_CMN_PAROBJID
& attr
) {
1412 vap
->va_parentid
= cdp
->cd_parentcnid
;
1413 VATTR_SET_SUPPORTED(vap
, va_parentid
);
1416 if (ATTR_CMN_SCRIPT
& attr
) {
1417 vap
->va_encoding
= cdp
->cd_encoding
;
1418 VATTR_SET_SUPPORTED(vap
, va_encoding
);
1421 if (ATTR_CMN_CRTIME
& attr
) {
1422 vap
->va_create_time
.tv_sec
= cap
->ca_itime
;
1423 vap
->va_create_time
.tv_nsec
= 0;
1424 VATTR_SET_SUPPORTED(vap
, va_create_time
);
1427 if (ATTR_CMN_MODTIME
& attr
) {
1428 vap
->va_modify_time
.tv_sec
= cap
->ca_mtime
;
1429 vap
->va_modify_time
.tv_nsec
= 0;
1430 VATTR_SET_SUPPORTED(vap
, va_modify_time
);
1433 if (ATTR_CMN_CHGTIME
& attr
) {
1434 vap
->va_change_time
.tv_sec
= cap
->ca_ctime
;
1435 vap
->va_change_time
.tv_nsec
= 0;
1436 VATTR_SET_SUPPORTED(vap
, va_change_time
);
1439 if (ATTR_CMN_ACCTIME
& attr
) {
1440 vap
->va_access_time
.tv_sec
= cap
->ca_atime
;
1441 vap
->va_access_time
.tv_nsec
= 0;
1442 VATTR_SET_SUPPORTED(vap
, va_access_time
);
1445 if (ATTR_CMN_BKUPTIME
& attr
) {
1446 vap
->va_backup_time
.tv_sec
= cap
->ca_btime
;
1447 vap
->va_backup_time
.tv_nsec
= 0;
1448 VATTR_SET_SUPPORTED(vap
, va_backup_time
);
1451 if (ATTR_CMN_FNDRINFO
& attr
) {
1452 u_int8_t
*finfo
= NULL
;
1454 bcopy(&cap
->ca_finderinfo
, &vap
->va_finderinfo
[0],
1455 sizeof(u_int8_t
) * 32);
1456 finfo
= (u_int8_t
*)(&vap
->va_finderinfo
[0]);
1458 /* Don't expose a symlink's private type/creator. */
1459 if (S_ISLNK(cap
->ca_mode
)) {
1460 struct FndrFileInfo
*fip
;
1462 fip
= (struct FndrFileInfo
*)finfo
;
1467 /* advance 16 bytes into the attrbuf */
1470 /* also don't expose the date_added or write_gen_counter fields */
1471 if (S_ISREG(cap
->ca_mode
) || S_ISLNK(cap
->ca_mode
)) {
1472 struct FndrExtendedFileInfo
*extinfo
=
1473 (struct FndrExtendedFileInfo
*)finfo
;
1474 extinfo
->document_id
= 0;
1475 extinfo
->date_added
= 0;
1476 extinfo
->write_gen_counter
= 0;
1477 } else if (S_ISDIR(cap
->ca_mode
)) {
1478 struct FndrExtendedDirInfo
*extinfo
=
1479 (struct FndrExtendedDirInfo
*)finfo
;
1480 extinfo
->document_id
= 0;
1481 extinfo
->date_added
= 0;
1482 extinfo
->write_gen_counter
= 0;
1485 VATTR_SET_SUPPORTED(vap
, va_finderinfo
);
1488 if (ATTR_CMN_OWNERID
& attr
) {
1489 uid_t nuid
= cap
->ca_uid
;
1492 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
)
1494 else if (nuid
== UNKNOWNUID
)
1499 VATTR_SET_SUPPORTED(vap
, va_uid
);
1502 if (ATTR_CMN_GRPID
& attr
) {
1503 gid_t ngid
= cap
->ca_gid
;
1506 gid_t cgid
= kauth_cred_getgid(vfs_context_ucred(ctx
));
1507 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
)
1509 else if (ngid
== UNKNOWNUID
)
1514 VATTR_SET_SUPPORTED(vap
, va_gid
);
1517 if (ATTR_CMN_ACCESSMASK
& attr
) {
1520 * [2856576] Since we are dynamically changing the owner, also
1521 * effectively turn off the set-user-id and set-group-id bits,
1522 * just like chmod(2) would when changing ownership. This prevents
1523 * a security hole where set-user-id programs run as whoever is
1524 * logged on (or root if nobody is logged in yet!)
1526 nmode
= (cap
->ca_uid
== UNKNOWNUID
) ?
1527 cap
->ca_mode
& ~(S_ISUID
| S_ISGID
) : cap
->ca_mode
;
1529 vap
->va_mode
= nmode
;
1530 VATTR_SET_SUPPORTED(vap
, va_mode
);
1533 if (ATTR_CMN_FLAGS
& attr
) {
1534 vap
->va_flags
= cap
->ca_flags
;
1535 VATTR_SET_SUPPORTED(vap
, va_flags
);
1538 if (ATTR_CMN_GEN_COUNT
& attr
) {
1539 vap
->va_write_gencount
= hfs_get_gencount_from_blob(
1540 (const uint8_t *)cap
->ca_finderinfo
, cap
->ca_mode
);
1541 VATTR_SET_SUPPORTED(vap
, va_write_gencount
);
1544 if (ATTR_CMN_DOCUMENT_ID
& attr
) {
1545 vap
->va_document_id
= hfs_get_document_id_from_blob(
1546 (const uint8_t *)cap
->ca_finderinfo
, cap
->ca_mode
);
1547 VATTR_SET_SUPPORTED(vap
, va_document_id
);
1550 if (ATTR_CMN_USERACCESS
& attr
) {
1551 u_int32_t user_access
;
1553 /* Take the long path when we have an ACL */
1554 if ((vp
!= NULLVP
) && (cap
->ca_recflags
& kHFSHasSecurityMask
)) {
1555 user_access
= hfs_real_user_access(vp
, ctx
);
1557 user_access
= DerivePermissionSummary(cap
->ca_uid
, cap
->ca_gid
,
1558 cap
->ca_mode
, mp
, vfs_context_ucred(ctx
), 0);
1560 /* Also consider READ-ONLY file system. */
1561 if (vfs_flags(mp
) & MNT_RDONLY
) {
1562 user_access
&= ~W_OK
;
1564 /* Locked objects are not writable either */
1565 if ((cap
->ca_flags
& UF_IMMUTABLE
) && (vfs_context_suser(ctx
) != 0))
1566 user_access
&= ~W_OK
;
1567 if ((cap
->ca_flags
& SF_IMMUTABLE
) && (vfs_context_suser(ctx
) == 0))
1568 user_access
&= ~W_OK
;
1570 vap
->va_user_access
= user_access
;
1571 VATTR_SET_SUPPORTED(vap
, va_user_access
);
1575 * Right now the best we can do is tell if we *don't* have extended
1576 * security (like hfs_vnop_getattr).
1578 if (ATTR_CMN_EXTENDED_SECURITY
& attr
) {
1579 if (!(cap
->ca_recflags
& kHFSHasSecurityMask
)) {
1580 vap
->va_acl
= (kauth_acl_t
) KAUTH_FILESEC_NONE
;
1581 VATTR_SET_SUPPORTED(vap
, va_acl
);
1585 if (ATTR_CMN_FILEID
& attr
) {
1586 vap
->va_fileid
= cap
->ca_fileid
;
1587 VATTR_SET_SUPPORTED(vap
, va_fileid
);
1590 if (ATTR_CMN_PARENTID
& attr
) {
1591 vap
->va_parentid
= cdp
->cd_parentcnid
;
1592 VATTR_SET_SUPPORTED(vap
, va_parentid
);
1595 if (ATTR_CMN_ADDEDTIME
& attr
) {
1596 if (cap
->ca_recflags
& kHFSHasDateAddedMask
) {
1597 vap
->va_addedtime
.tv_sec
= hfs_get_dateadded_from_blob(
1598 (const uint8_t *)cap
->ca_finderinfo
, cap
->ca_mode
);
1599 vap
->va_addedtime
.tv_nsec
= 0;
1600 VATTR_SET_SUPPORTED(vap
, va_addedtime
);
1606 vattr_data_for_dir_attrs(struct attrlist
*alp
, struct vnode_attr
*vap
,
1607 struct hfsmount
*hfsmp
, struct vnode
*vp
, struct cat_desc
* descp
,
1608 struct cat_attr
* cattrp
)
1610 attrgroup_t attr
= alp
->dirattr
;
1614 * The DIR_LINKCOUNT is the count of real directory hard links.
1615 * (i.e. its not the sum of the implied "." and ".." references
1616 * typically used in stat's st_nlink field)
1618 if (ATTR_DIR_LINKCOUNT
& attr
) {
1619 vap
->va_dirlinkcount
= cattrp
->ca_linkcount
;
1620 VATTR_SET_SUPPORTED(vap
, va_dirlinkcount
);
1622 if (ATTR_DIR_ENTRYCOUNT
& attr
) {
1623 entries
= cattrp
->ca_entries
;
1625 if (descp
->cd_parentcnid
== kHFSRootParentID
) {
1626 if (hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
!= 0)
1627 --entries
; /* hide private dir */
1628 if (hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
!= 0)
1629 --entries
; /* hide private dir */
1631 ((hfsmp
->vcbAtrb
& kHFSVolumeJournaledMask
) &&
1632 (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))
1633 entries
-= 2; /* hide the journal files */
1636 vap
->va_nchildren
= entries
;
1637 VATTR_SET_SUPPORTED(vap
, va_nchildren
);
1640 if (ATTR_DIR_MOUNTSTATUS
& attr
) {
1642 * There is not vnode_attr for mount point status.
1643 * XXX. Should there be ?
1645 u_int32_t mstatus
= 0;
1647 if (vp
!= NULL
&& vnode_mountedhere(vp
) != NULL
)
1648 mstatus
= DIR_MNTSTATUS_MNTPOINT
;
1653 vattr_data_for_file_attrs(struct attrlist
*alp
, struct vnode_attr
*vap
,
1654 struct hfsmount
*hfsmp
, struct cat_attr
*cattrp
, struct cat_fork
*datafork
,
1655 struct cat_fork
*rsrcfork
, struct vnode
*vp
)
1657 #if !HFS_COMPRESSION
1660 attrgroup_t attr
= alp
->fileattr
;
1661 off_t da_size
, rsrc_len
, rsrc_alloc
;
1662 u_int32_t allocblksize
;
1664 allocblksize
= HFSTOVCB(hfsmp
)->blockSize
;
1666 off_t datasize
= datafork
->cf_size
;
1667 off_t totalsize
= datasize
+ rsrcfork
->cf_size
;
1669 int handle_compressed
;
1670 handle_compressed
= (cattrp
->ca_flags
& UF_COMPRESSED
);// && hfs_file_is_compressed(VTOC(vp), 1);
1672 if (handle_compressed
) {
1673 if (attr
& (ATTR_FILE_DATALENGTH
|ATTR_FILE_TOTALSIZE
)) {
1674 if ( 0 == hfs_uncompressed_size_of_compressed_file(hfsmp
, vp
, cattrp
->ca_fileid
, &datasize
, 1) ) { /* 1 == don't take the cnode lock */
1675 /* total size of a compressed file is just the data size */
1676 totalsize
= datasize
;
1682 if (ATTR_FILE_LINKCOUNT
& attr
) {
1683 vap
->va_nlink
= cattrp
->ca_linkcount
;
1684 VATTR_SET_SUPPORTED(vap
, va_nlink
);
1686 if (ATTR_FILE_TOTALSIZE
& attr
) {
1687 VATTR_RETURN(vap
, va_total_size
, totalsize
);
1689 if (ATTR_FILE_ALLOCSIZE
& attr
) {
1690 VATTR_RETURN(vap
, va_total_alloc
,
1691 (off_t
)cattrp
->ca_blocks
* (off_t
)allocblksize
);
1693 if (ATTR_FILE_IOBLOCKSIZE
& attr
) {
1694 VATTR_RETURN(vap
, va_iosize
, hfsmp
->hfs_logBlockSize
);
1697 /* ATTR_FILE_CLUMPSIZE is obsolete */
1699 if (ATTR_FILE_DEVTYPE
& attr
) {
1702 if (S_ISBLK(cattrp
->ca_mode
) || S_ISCHR(cattrp
->ca_mode
))
1703 dev
= (u_int32_t
)cattrp
->ca_rdev
;
1705 VATTR_RETURN(vap
, va_rdev
, dev
);
1708 if (ATTR_FILE_DATALENGTH
& attr
) {
1709 VATTR_RETURN(vap
, va_data_size
, datasize
);
1712 /* fake the data fork size on a decmpfs compressed file to reflect the
1713 * uncompressed size. This ensures proper reading and copying of these
1715 * NOTE: we may need to get the vnode here because the vnode parameter
1716 * passed by hfs_vnop_readdirattr() may be null.
1719 if (handle_compressed
) {
1720 da_size
= (off_t
)rsrcfork
->cf_blocks
* (off_t
)allocblksize
;
1727 da_size
= (off_t
)datafork
->cf_blocks
* (off_t
)allocblksize
;
1728 rsrc_len
= rsrcfork
->cf_size
;
1729 rsrc_alloc
= (off_t
)rsrcfork
->cf_blocks
* (off_t
)allocblksize
;
1732 if (ATTR_FILE_DATAALLOCSIZE
& attr
) {
1733 VATTR_RETURN(vap
, va_data_alloc
, da_size
);
1736 if (ATTR_FILE_RSRCLENGTH
& attr
) {
1737 VATTR_RETURN(vap
, va_rsrc_length
, rsrc_len
);
1740 if (ATTR_FILE_RSRCALLOCSIZE
& attr
) {
1741 VATTR_RETURN(vap
, va_rsrc_alloc
, rsrc_alloc
);