2 * Copyright (c) 2000-2007 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 Computer, 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>
42 #include <sys/mount_internal.h>
43 #include <sys/kauth.h>
45 #include <kern/locks.h>
48 #include "hfs_cnode.h"
49 #include "hfs_mount.h"
51 #include "hfs_attrlist.h"
52 #include "hfs_btreeio.h"
54 /* Packing routines: */
56 static void packnameattr(struct attrblock
*abp
, struct vnode
*vp
,
57 const u_int8_t
*name
, int namelen
);
59 static void packcommonattr(struct attrblock
*abp
, struct hfsmount
*hfsmp
,
60 struct vnode
*vp
, struct cat_desc
* cdp
,
61 struct cat_attr
* cap
, struct proc
*p
);
63 static void packfileattr(struct attrblock
*abp
, struct hfsmount
*hfsmp
,
64 struct cat_attr
*cattrp
, struct cat_fork
*datafork
,
65 struct cat_fork
*rsrcfork
);
67 static void packdirattr(struct attrblock
*abp
, struct hfsmount
*hfsmp
,
68 struct vnode
*vp
, struct cat_desc
* descp
,
69 struct cat_attr
* cattrp
);
71 static u_int32_t
hfs_real_user_access(vnode_t vp
, vfs_context_t ctx
);
74 * readdirattr operation will return attributes for the items in the
75 * directory specified.
77 * It does not do . and .. entries. The problem is if you are at the root of the
78 * hfs directory and go to .. you could be crossing a mountpoint into a
79 * different (ufs) file system. The attributes that apply for it may not
80 * apply for the file system you are doing the readdirattr on. To make life
81 * simpler, this call will only return entries in its directory, hfs like.
85 hfs_vnop_readdirattr(ap
)
86 struct vnop_readdirattr_args
/* {
88 struct attrlist *a_alist;
94 u_long *a_actualcount;
95 vfs_context_t a_context;
98 struct vnode
*dvp
= ap
->a_vp
;
100 struct hfsmount
* hfsmp
;
101 struct attrlist
*alist
= ap
->a_alist
;
102 uio_t uio
= ap
->a_uio
;
103 int maxcount
= ap
->a_maxcount
;
104 struct proc
*p
= vfs_context_proc(ap
->a_context
);
105 u_int32_t fixedblocksize
;
106 u_int32_t maxattrblocksize
;
107 u_int32_t currattrbufsize
;
108 void *attrbufptr
= NULL
;
111 struct attrblock attrblk
;
114 int i
, dir_entries
= 0;
115 struct cat_desc
*lastdescp
= NULL
;
116 struct cat_entrylist
*ce_list
= NULL
;
117 directoryhint_t
*dirhint
= NULL
;
122 *(ap
->a_actualcount
) = 0;
123 *(ap
->a_eofflag
) = 0;
125 /* Check for invalid options and buffer space. */
126 if (((ap
->a_options
& ~(FSOPT_NOINMEMUPDATE
| FSOPT_NOFOLLOW
)) != 0) ||
127 (uio_resid(uio
) <= 0) || (uio_iovcnt(uio
) > 1) || (maxcount
<= 0)) {
131 * Reject requests for unsupported attributes.
133 if ((alist
->bitmapcount
!= ATTR_BIT_MAP_COUNT
) ||
134 (alist
->commonattr
& ~HFS_ATTR_CMN_VALID
) ||
135 (alist
->volattr
!= 0) ||
136 (alist
->dirattr
& ~HFS_ATTR_DIR_VALID
) ||
137 (alist
->fileattr
& ~HFS_ATTR_FILE_VALID
) ||
138 (alist
->forkattr
!= 0)) {
142 * Take an exclusive directory lock since we manipulate the directory hints
144 if ((error
= hfs_lock(VTOC(dvp
), HFS_EXCLUSIVE_LOCK
))) {
150 dir_entries
= dcp
->c_entries
;
152 /* Extract directory index and tag (sequence number) from uio_offset */
153 index
= uio_offset(uio
) & HFS_INDEX_MASK
;
154 tag
= uio_offset(uio
) & ~HFS_INDEX_MASK
;
155 if ((index
+ 1) > dir_entries
) {
156 *(ap
->a_eofflag
) = 1;
161 /* Get a buffer to hold packed attributes. */
162 fixedblocksize
= (sizeof(u_int32_t
) + hfs_attrblksize(alist
)); /* 4 bytes for length */
163 maxattrblocksize
= fixedblocksize
;
164 if (alist
->commonattr
& ATTR_CMN_NAME
)
165 maxattrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
166 MALLOC(attrbufptr
, void *, maxattrblocksize
, M_TEMP
, M_WAITOK
);
167 attrptr
= attrbufptr
;
168 varptr
= (char *)attrbufptr
+ fixedblocksize
; /* Point to variable-length storage */
170 /* Get a detached directory hint (cnode must be locked exclusive) */
171 dirhint
= hfs_getdirhint(dcp
, ((index
- 1) & HFS_INDEX_MASK
) | tag
, TRUE
);
173 /* Hide tag from catalog layer. */
174 dirhint
->dh_index
&= HFS_INDEX_MASK
;
175 if (dirhint
->dh_index
== HFS_INDEX_MASK
) {
176 dirhint
->dh_index
= -1;
180 * Obtain a list of catalog entries and pack their attributes until
181 * the output buffer is full or maxcount entries have been packed.
185 * Constrain our list size.
187 maxentries
= uio_resid(uio
) / (fixedblocksize
+ HFS_AVERAGE_NAME_SIZE
);
188 maxentries
= min(maxentries
, dcp
->c_entries
- index
);
189 maxentries
= min(maxentries
, maxcount
);
190 maxentries
= min(maxentries
, MAXCATENTRIES
);
191 if (maxentries
< 1) {
196 /* Initialize a catalog entry list. */
197 MALLOC(ce_list
, struct cat_entrylist
*, CE_LIST_SIZE(maxentries
), M_TEMP
, M_WAITOK
);
198 bzero(ce_list
, CE_LIST_SIZE(maxentries
));
199 ce_list
->maxentries
= maxentries
;
202 * Populate the ce_list from the catalog file.
204 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
206 error
= cat_getentriesattr(hfsmp
, dirhint
, ce_list
);
207 /* Don't forget to release the descriptors later! */
209 hfs_systemfile_unlock(hfsmp
, lockflags
);
211 if (error
== ENOENT
) {
212 *(ap
->a_eofflag
) = TRUE
;
220 * Drop the directory lock so we don't deadlock when we:
221 * - acquire a child cnode lock
222 * - make calls to vnode_authorize()
223 * - make calls to kauth_cred_ismember_gid()
228 /* Process the catalog entries. */
229 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
230 struct cnode
*cp
= NULL
;
231 struct vnode
*vp
= NULL
;
232 struct cat_desc
* cdescp
;
233 struct cat_attr
* cattrp
;
234 struct cat_fork c_datafork
;
235 struct cat_fork c_rsrcfork
;
237 bzero(&c_datafork
, sizeof(c_datafork
));
238 bzero(&c_rsrcfork
, sizeof(c_rsrcfork
));
239 cdescp
= &ce_list
->entry
[i
].ce_desc
;
240 cattrp
= &ce_list
->entry
[i
].ce_attr
;
241 c_datafork
.cf_size
= ce_list
->entry
[i
].ce_datasize
;
242 c_datafork
.cf_blocks
= ce_list
->entry
[i
].ce_datablks
;
243 c_rsrcfork
.cf_size
= ce_list
->entry
[i
].ce_rsrcsize
;
244 c_rsrcfork
.cf_blocks
= ce_list
->entry
[i
].ce_rsrcblks
;
246 if ((alist
->commonattr
& ATTR_CMN_USERACCESS
) &&
247 (cattrp
->ca_recflags
& kHFSHasSecurityMask
)) {
249 * Obtain vnode for our vnode_authorize() calls.
251 if (hfs_vget(hfsmp
, cattrp
->ca_fileid
, &vp
, 0) != 0) {
254 } else if (!(ap
->a_options
& FSOPT_NOINMEMUPDATE
)) {
255 /* Get in-memory cnode data (if any). */
256 vp
= hfs_chash_getvnode(hfsmp
->hfs_raw_dev
, cattrp
->ca_fileid
, 0, 0);
260 /* Only use cnode's decriptor for non-hardlinks */
261 if (!(cp
->c_flag
& C_HARDLINK
))
262 cdescp
= &cp
->c_desc
;
263 cattrp
= &cp
->c_attr
;
264 if (cp
->c_datafork
) {
265 c_datafork
.cf_size
= cp
->c_datafork
->ff_size
;
266 c_datafork
.cf_blocks
= cp
->c_datafork
->ff_blocks
;
268 if (cp
->c_rsrcfork
) {
269 c_rsrcfork
.cf_size
= cp
->c_rsrcfork
->ff_size
;
270 c_rsrcfork
.cf_blocks
= cp
->c_rsrcfork
->ff_blocks
;
272 /* All done with cnode. */
277 *((u_int32_t
*)attrptr
) = 0;
278 attrptr
= ((u_int32_t
*)attrptr
) + 1;
279 attrblk
.ab_attrlist
= alist
;
280 attrblk
.ab_attrbufpp
= &attrptr
;
281 attrblk
.ab_varbufpp
= &varptr
;
282 attrblk
.ab_flags
= 0;
283 attrblk
.ab_blocksize
= maxattrblocksize
;
284 attrblk
.ab_context
= ap
->a_context
;
286 /* Pack catalog entries into attribute buffer. */
287 hfs_packattrblk(&attrblk
, hfsmp
, vp
, cdescp
, cattrp
, &c_datafork
, &c_rsrcfork
, p
);
288 currattrbufsize
= ((char *)varptr
- (char *)attrbufptr
);
290 /* All done with vnode. */
296 /* Make sure there's enough buffer space remaining. */
297 // LP64todo - fix this!
298 if (uio_resid(uio
) < 0 || currattrbufsize
> (u_int32_t
)uio_resid(uio
)) {
301 *((u_int32_t
*)attrbufptr
) = currattrbufsize
;
302 error
= uiomove((caddr_t
)attrbufptr
, currattrbufsize
, uio
);
303 if (error
!= E_NONE
) {
306 attrptr
= attrbufptr
;
307 /* Point to variable-length storage */
308 varptr
= (char *)attrbufptr
+ fixedblocksize
;
309 /* Save the last valid catalog entry */
310 lastdescp
= &ce_list
->entry
[i
].ce_desc
;
312 *ap
->a_actualcount
+= 1;
314 /* Termination checks */
315 if ((--maxcount
<= 0) ||
316 // LP64todo - fix this!
317 uio_resid(uio
) < 0 ||
318 ((u_int32_t
)uio_resid(uio
) < (fixedblocksize
+ HFS_AVERAGE_NAME_SIZE
)) ||
319 (index
>= dir_entries
)) {
323 } /* for each catalog entry */
325 /* For small directories, check if we're all done. */
326 if (*ap
->a_actualcount
== (u_long
)dir_entries
) {
327 *(ap
->a_eofflag
) = TRUE
;
330 /* If we skipped catalog entries for reserved files that should
331 * not be listed in namespace, update the index accordingly.
333 if (ce_list
->skipentries
) {
334 index
+= ce_list
->skipentries
;
335 ce_list
->skipentries
= 0;
338 /* If there are more entries then save the last name. */
339 if (index
< dir_entries
340 && !(*(ap
->a_eofflag
))
341 && lastdescp
!= NULL
) {
343 /* Remember last entry */
344 if ((dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) &&
345 (dirhint
->dh_desc
.cd_nameptr
!= NULL
)) {
346 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
347 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
349 dirhint
->dh_desc
.cd_namelen
= lastdescp
->cd_namelen
;
350 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)
351 vfs_addname((const char *)lastdescp
->cd_nameptr
, lastdescp
->cd_namelen
, 0, 0);
352 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
353 dirhint
->dh_index
= index
- 1;
354 dirhint
->dh_desc
.cd_cnid
= lastdescp
->cd_cnid
;
355 dirhint
->dh_desc
.cd_hint
= lastdescp
->cd_hint
;
356 dirhint
->dh_desc
.cd_encoding
= lastdescp
->cd_encoding
;
358 /* No more entries. */
359 *(ap
->a_eofflag
) = TRUE
;
362 /* All done with the catalog descriptors. */
363 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
)
364 cat_releasedesc(&ce_list
->entry
[i
].ce_desc
);
365 ce_list
->realentries
= 0;
367 (void) hfs_lock(VTOC(dvp
), HFS_FORCE_LOCK
);
371 /* Pack directory index and tag into uio_offset. */
372 while (tag
== 0) tag
= (++dcp
->c_dirhinttag
) << HFS_INDEX_BITS
;
373 uio_setoffset(uio
, index
| tag
);
374 dirhint
->dh_index
|= tag
;
377 *ap
->a_newstate
= dcp
->c_dirchangecnt
;
379 /* Drop directory hint on error or if there are no more entries */
381 if ((error
!= 0) || (index
>= dir_entries
) || *(ap
->a_eofflag
))
382 hfs_reldirhint(dcp
, dirhint
);
384 hfs_insertdirhint(dcp
, dirhint
);
387 FREE(attrbufptr
, M_TEMP
);
389 FREE(ce_list
, M_TEMP
);
396 /*==================== Attribute list support routines ====================*/
399 * Pack cnode attributes into an attribute block.
403 hfs_packattrblk(struct attrblock
*abp
,
404 struct hfsmount
*hfsmp
,
406 struct cat_desc
*descp
,
407 struct cat_attr
*attrp
,
408 struct cat_fork
*datafork
,
409 struct cat_fork
*rsrcfork
,
412 struct attrlist
*attrlistp
= abp
->ab_attrlist
;
414 if (attrlistp
->commonattr
)
415 packcommonattr(abp
, hfsmp
, vp
, descp
, attrp
, p
);
417 if (attrlistp
->dirattr
&& S_ISDIR(attrp
->ca_mode
))
418 packdirattr(abp
, hfsmp
, vp
, descp
,attrp
);
420 if (attrlistp
->fileattr
&& !S_ISDIR(attrp
->ca_mode
))
421 packfileattr(abp
, hfsmp
, attrp
, datafork
, rsrcfork
);
426 mountpointname(struct mount
*mp
)
428 size_t namelength
= strlen(mp
->mnt_vfsstat
.f_mntonname
);
436 * Look backwards through the name string, looking for
437 * the first slash encountered (which must precede the
438 * last part of the pathname).
440 for (c
= mp
->mnt_vfsstat
.f_mntonname
+ namelength
- 1;
441 namelength
> 0; --c
, --namelength
) {
444 } else if (foundchars
) {
449 return (mp
->mnt_vfsstat
.f_mntonname
);
455 struct attrblock
*abp
,
457 const u_int8_t
*name
,
461 struct attrreference
* attr_refptr
;
464 u_int32_t attrlength
;
467 /* A cnode's name may be incorrect for the root of a mounted
468 * filesystem (it can be mounted on a different directory name
469 * than the name of the volume, such as "blah-1"). So for the
470 * root directory, it's best to return the last element of the
471 location where the volume's mounted:
473 if ((vp
!= NULL
) && vnode_isvroot(vp
) &&
474 (mpname
= mountpointname(vnode_mount(vp
)))) {
475 mpnamelen
= strlen(mpname
);
477 /* Trim off any trailing slashes: */
478 while ((mpnamelen
> 0) && (mpname
[mpnamelen
-1] == '/'))
481 /* If there's anything left, use it instead of the volume's name */
483 name
= (u_int8_t
*)mpname
;
492 varbufptr
= *abp
->ab_varbufpp
;
493 attr_refptr
= (struct attrreference
*)(*abp
->ab_attrbufpp
);
495 attrlength
= namelen
+ 1;
496 attr_refptr
->attr_dataoffset
= (char *)varbufptr
- (char *)attr_refptr
;
497 attr_refptr
->attr_length
= attrlength
;
498 (void) strncpy((char *)varbufptr
, (const char *) name
, attrlength
);
500 * Advance beyond the space just allocated and
501 * round up to the next 4-byte boundary:
503 varbufptr
= ((char *)varbufptr
) + attrlength
+ ((4 - (attrlength
& 3)) & 3);
506 *abp
->ab_attrbufpp
= attr_refptr
;
507 *abp
->ab_varbufpp
= varbufptr
;
513 struct attrblock
*abp
,
514 struct hfsmount
*hfsmp
,
516 struct cat_desc
* cdp
,
517 struct cat_attr
* cap
,
520 attrgroup_t attr
= abp
->ab_attrlist
->commonattr
;
521 struct mount
*mp
= HFSTOVFS(hfsmp
);
522 void *attrbufptr
= *abp
->ab_attrbufpp
;
523 void *varbufptr
= *abp
->ab_varbufpp
;
524 boolean_t is_64_bit
= proc_is64bit(p
);
528 if (attr
& (ATTR_CMN_OWNERID
| ATTR_CMN_GRPID
)) {
529 cuid
= kauth_cred_getuid(proc_ucred(p
));
533 if (ATTR_CMN_NAME
& attr
) {
534 packnameattr(abp
, vp
, cdp
->cd_nameptr
, cdp
->cd_namelen
);
535 attrbufptr
= *abp
->ab_attrbufpp
;
536 varbufptr
= *abp
->ab_varbufpp
;
538 if (ATTR_CMN_DEVID
& attr
) {
539 *((dev_t
*)attrbufptr
) = hfsmp
->hfs_raw_dev
;
540 attrbufptr
= ((dev_t
*)attrbufptr
) + 1;
542 if (ATTR_CMN_FSID
& attr
) {
545 fsid
.val
[0] = (long)hfsmp
->hfs_raw_dev
;
546 fsid
.val
[1] = (long)vfs_typenum(mp
);
547 *((fsid_t
*)attrbufptr
) = fsid
;
548 attrbufptr
= ((fsid_t
*)attrbufptr
) + 1;
550 if (ATTR_CMN_OBJTYPE
& attr
) {
551 *((fsobj_type_t
*)attrbufptr
) = IFTOVT(cap
->ca_mode
);
552 attrbufptr
= ((fsobj_type_t
*)attrbufptr
) + 1;
554 if (ATTR_CMN_OBJTAG
& attr
) {
555 *((fsobj_tag_t
*)attrbufptr
) = VT_HFS
;
556 attrbufptr
= ((fsobj_tag_t
*)attrbufptr
) + 1;
559 * Exporting file IDs from HFS Plus:
561 * For "normal" files the c_fileid is the same value as the
562 * c_cnid. But for hard link files, they are different - the
563 * c_cnid belongs to the active directory entry (ie the link)
564 * and the c_fileid is for the actual inode (ie the data file).
566 * The stat call (getattr) will always return the c_fileid
567 * and Carbon APIs, which are hardlink-ignorant, will always
568 * receive the c_cnid (from getattrlist).
570 if (ATTR_CMN_OBJID
& attr
) {
571 ((fsobj_id_t
*)attrbufptr
)->fid_objno
= cdp
->cd_cnid
;
572 ((fsobj_id_t
*)attrbufptr
)->fid_generation
= 0;
573 attrbufptr
= ((fsobj_id_t
*)attrbufptr
) + 1;
575 if (ATTR_CMN_OBJPERMANENTID
& attr
) {
576 ((fsobj_id_t
*)attrbufptr
)->fid_objno
= cdp
->cd_cnid
;
577 ((fsobj_id_t
*)attrbufptr
)->fid_generation
= 0;
578 attrbufptr
= ((fsobj_id_t
*)attrbufptr
) + 1;
580 if (ATTR_CMN_PAROBJID
& attr
) {
581 ((fsobj_id_t
*)attrbufptr
)->fid_objno
= cdp
->cd_parentcnid
;
582 ((fsobj_id_t
*)attrbufptr
)->fid_generation
= 0;
583 attrbufptr
= ((fsobj_id_t
*)attrbufptr
) + 1;
585 if (ATTR_CMN_SCRIPT
& attr
) {
586 *((text_encoding_t
*)attrbufptr
) = cdp
->cd_encoding
;
587 attrbufptr
= ((text_encoding_t
*)attrbufptr
) + 1;
589 if (ATTR_CMN_CRTIME
& attr
) {
591 ((struct user_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_itime
;
592 ((struct user_timespec
*)attrbufptr
)->tv_nsec
= 0;
593 attrbufptr
= ((struct user_timespec
*)attrbufptr
) + 1;
596 ((struct timespec
*)attrbufptr
)->tv_sec
= cap
->ca_itime
;
597 ((struct timespec
*)attrbufptr
)->tv_nsec
= 0;
598 attrbufptr
= ((struct timespec
*)attrbufptr
) + 1;
601 if (ATTR_CMN_MODTIME
& attr
) {
603 ((struct user_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_mtime
;
604 ((struct user_timespec
*)attrbufptr
)->tv_nsec
= 0;
605 attrbufptr
= ((struct user_timespec
*)attrbufptr
) + 1;
608 ((struct timespec
*)attrbufptr
)->tv_sec
= cap
->ca_mtime
;
609 ((struct timespec
*)attrbufptr
)->tv_nsec
= 0;
610 attrbufptr
= ((struct timespec
*)attrbufptr
) + 1;
613 if (ATTR_CMN_CHGTIME
& attr
) {
615 ((struct user_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_ctime
;
616 ((struct user_timespec
*)attrbufptr
)->tv_nsec
= 0;
617 attrbufptr
= ((struct user_timespec
*)attrbufptr
) + 1;
620 ((struct timespec
*)attrbufptr
)->tv_sec
= cap
->ca_ctime
;
621 ((struct timespec
*)attrbufptr
)->tv_nsec
= 0;
622 attrbufptr
= ((struct timespec
*)attrbufptr
) + 1;
625 if (ATTR_CMN_ACCTIME
& attr
) {
627 ((struct user_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_atime
;
628 ((struct user_timespec
*)attrbufptr
)->tv_nsec
= 0;
629 attrbufptr
= ((struct user_timespec
*)attrbufptr
) + 1;
632 ((struct timespec
*)attrbufptr
)->tv_sec
= cap
->ca_atime
;
633 ((struct timespec
*)attrbufptr
)->tv_nsec
= 0;
634 attrbufptr
= ((struct timespec
*)attrbufptr
) + 1;
637 if (ATTR_CMN_BKUPTIME
& attr
) {
639 ((struct user_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_btime
;
640 ((struct user_timespec
*)attrbufptr
)->tv_nsec
= 0;
641 attrbufptr
= ((struct user_timespec
*)attrbufptr
) + 1;
644 ((struct timespec
*)attrbufptr
)->tv_sec
= cap
->ca_btime
;
645 ((struct timespec
*)attrbufptr
)->tv_nsec
= 0;
646 attrbufptr
= ((struct timespec
*)attrbufptr
) + 1;
649 if (ATTR_CMN_FNDRINFO
& attr
) {
650 bcopy(&cap
->ca_finderinfo
, attrbufptr
, sizeof(u_int8_t
) * 32);
651 /* Don't expose a symlink's private type/creator. */
652 if (S_ISLNK(cap
->ca_mode
)) {
653 struct FndrFileInfo
*fip
;
655 fip
= (struct FndrFileInfo
*)attrbufptr
;
659 attrbufptr
= (char *)attrbufptr
+ sizeof(u_int8_t
) * 32;
661 if (ATTR_CMN_OWNERID
& attr
) {
662 uid_t nuid
= cap
->ca_uid
;
665 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
)
667 else if (nuid
== UNKNOWNUID
)
671 *((uid_t
*)attrbufptr
) = nuid
;
672 attrbufptr
= ((uid_t
*)attrbufptr
) + 1;
674 if (ATTR_CMN_GRPID
& attr
) {
675 gid_t ngid
= cap
->ca_gid
;
678 gid_t cgid
= kauth_cred_getgid(proc_ucred(p
));
679 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
)
681 else if (ngid
== UNKNOWNUID
)
685 *((gid_t
*)attrbufptr
) = ngid
;
686 attrbufptr
= ((gid_t
*)attrbufptr
) + 1;
688 if (ATTR_CMN_ACCESSMASK
& attr
) {
690 * [2856576] Since we are dynamically changing the owner, also
691 * effectively turn off the set-user-id and set-group-id bits,
692 * just like chmod(2) would when changing ownership. This prevents
693 * a security hole where set-user-id programs run as whoever is
694 * logged on (or root if nobody is logged in yet!)
696 *((u_int32_t
*)attrbufptr
) = (cap
->ca_uid
== UNKNOWNUID
) ?
697 cap
->ca_mode
& ~(S_ISUID
| S_ISGID
) : cap
->ca_mode
;
698 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
700 if (ATTR_CMN_FLAGS
& attr
) {
701 *((u_int32_t
*)attrbufptr
) = cap
->ca_flags
;
702 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
704 if (ATTR_CMN_USERACCESS
& attr
) {
705 u_int32_t user_access
;
707 /* Take the long path when we have an ACL */
708 if ((vp
!= NULLVP
) && (cap
->ca_recflags
& kHFSHasSecurityMask
)) {
709 user_access
= hfs_real_user_access(vp
, abp
->ab_context
);
711 user_access
= DerivePermissionSummary(cap
->ca_uid
, cap
->ca_gid
,
712 cap
->ca_mode
, mp
, proc_ucred(current_proc()), 0);
714 /* Also consider READ-ONLY file system. */
715 if (vfs_flags(mp
) & MNT_RDONLY
) {
716 user_access
&= ~W_OK
;
718 /* Locked objects are not writable either */
719 if ((cap
->ca_flags
& UF_IMMUTABLE
) && (vfs_context_suser(abp
->ab_context
) != 0))
720 user_access
&= ~W_OK
;
721 if ((cap
->ca_flags
& SF_IMMUTABLE
) && (vfs_context_suser(abp
->ab_context
) == 0))
722 user_access
&= ~W_OK
;
724 *((u_int32_t
*)attrbufptr
) = user_access
;
725 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
727 if (ATTR_CMN_FILEID
& attr
) {
728 *((u_int64_t
*)attrbufptr
) = cap
->ca_fileid
;
729 attrbufptr
= ((u_int64_t
*)attrbufptr
) + 1;
731 if (ATTR_CMN_PARENTID
& attr
) {
732 *((u_int64_t
*)attrbufptr
) = cdp
->cd_parentcnid
;
733 attrbufptr
= ((u_int64_t
*)attrbufptr
) + 1;
736 *abp
->ab_attrbufpp
= attrbufptr
;
737 *abp
->ab_varbufpp
= varbufptr
;
742 struct attrblock
*abp
,
743 struct hfsmount
*hfsmp
,
745 struct cat_desc
* descp
,
746 struct cat_attr
* cattrp
)
748 attrgroup_t attr
= abp
->ab_attrlist
->dirattr
;
749 void *attrbufptr
= *abp
->ab_attrbufpp
;
753 * The DIR_LINKCOUNT is the count of real directory hard links.
754 * (i.e. its not the sum of the implied "." and ".." references
755 * typically used in stat's st_nlink field)
757 if (ATTR_DIR_LINKCOUNT
& attr
) {
758 *((u_int32_t
*)attrbufptr
) = cattrp
->ca_linkcount
;
759 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
761 if (ATTR_DIR_ENTRYCOUNT
& attr
) {
762 entries
= cattrp
->ca_entries
;
764 if (descp
->cd_parentcnid
== kHFSRootParentID
) {
765 if (hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
!= 0)
766 --entries
; /* hide private dir */
767 if (hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
!= 0)
768 --entries
; /* hide private dir */
770 ((hfsmp
->vcbAtrb
& kHFSVolumeJournaledMask
) &&
771 (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))
772 entries
-= 2; /* hide the journal files */
775 *((u_int32_t
*)attrbufptr
) = entries
;
776 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
778 if (ATTR_DIR_MOUNTSTATUS
& attr
) {
779 if (vp
!= NULL
&& vnode_mountedhere(vp
) != NULL
)
780 *((u_int32_t
*)attrbufptr
) = DIR_MNTSTATUS_MNTPOINT
;
782 *((u_int32_t
*)attrbufptr
) = 0;
783 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
785 *abp
->ab_attrbufpp
= attrbufptr
;
790 struct attrblock
*abp
,
791 struct hfsmount
*hfsmp
,
792 struct cat_attr
*cattrp
,
793 struct cat_fork
*datafork
,
794 struct cat_fork
*rsrcfork
)
796 attrgroup_t attr
= abp
->ab_attrlist
->fileattr
;
797 void *attrbufptr
= *abp
->ab_attrbufpp
;
798 void *varbufptr
= *abp
->ab_varbufpp
;
799 u_int32_t allocblksize
;
801 allocblksize
= HFSTOVCB(hfsmp
)->blockSize
;
803 if (ATTR_FILE_LINKCOUNT
& attr
) {
804 *((u_int32_t
*)attrbufptr
) = cattrp
->ca_linkcount
;
805 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
807 if (ATTR_FILE_TOTALSIZE
& attr
) {
808 *((off_t
*)attrbufptr
) = datafork
->cf_size
+ rsrcfork
->cf_size
;
809 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
811 if (ATTR_FILE_ALLOCSIZE
& attr
) {
812 *((off_t
*)attrbufptr
) =
813 (off_t
)cattrp
->ca_blocks
* (off_t
)allocblksize
;
814 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
816 if (ATTR_FILE_IOBLOCKSIZE
& attr
) {
817 *((u_int32_t
*)attrbufptr
) = hfsmp
->hfs_logBlockSize
;
818 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
820 if (ATTR_FILE_CLUMPSIZE
& attr
) {
821 *((u_int32_t
*)attrbufptr
) = hfsmp
->vcbClpSiz
;
822 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
824 if (ATTR_FILE_DEVTYPE
& attr
) {
825 if (S_ISBLK(cattrp
->ca_mode
) || S_ISCHR(cattrp
->ca_mode
))
826 *((u_int32_t
*)attrbufptr
) = (u_int32_t
)cattrp
->ca_rdev
;
828 *((u_int32_t
*)attrbufptr
) = 0;
829 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
831 if (ATTR_FILE_DATALENGTH
& attr
) {
832 *((off_t
*)attrbufptr
) = datafork
->cf_size
;
833 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
835 if (ATTR_FILE_DATAALLOCSIZE
& attr
) {
836 *((off_t
*)attrbufptr
) =
837 (off_t
)datafork
->cf_blocks
* (off_t
)allocblksize
;
838 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
840 if (ATTR_FILE_RSRCLENGTH
& attr
) {
841 *((off_t
*)attrbufptr
) = rsrcfork
->cf_size
;
842 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
844 if (ATTR_FILE_RSRCALLOCSIZE
& attr
) {
845 *((off_t
*)attrbufptr
) =
846 (off_t
)rsrcfork
->cf_blocks
* (off_t
)allocblksize
;
847 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
849 *abp
->ab_attrbufpp
= attrbufptr
;
850 *abp
->ab_varbufpp
= varbufptr
;
854 * Calculate the total size of an attribute block.
858 hfs_attrblksize(struct attrlist
*attrlist
)
863 boolean_t is_64_bit
= proc_is64bit(current_proc());
866 sizeof_timespec
= sizeof(struct user_timespec
);
868 sizeof_timespec
= sizeof(struct timespec
);
870 DBG_ASSERT((attrlist
->commonattr
& ~ATTR_CMN_VALIDMASK
) == 0);
872 DBG_ASSERT((attrlist
->volattr
& ~ATTR_VOL_VALIDMASK
) == 0);
874 DBG_ASSERT((attrlist
->dirattr
& ~ATTR_DIR_VALIDMASK
) == 0);
876 DBG_ASSERT((attrlist
->fileattr
& ~ATTR_FILE_VALIDMASK
) == 0);
878 DBG_ASSERT((attrlist
->forkattr
& ~ATTR_FORK_VALIDMASK
) == 0);
882 if ((a
= attrlist
->commonattr
) != 0) {
883 if (a
& ATTR_CMN_NAME
) size
+= sizeof(struct attrreference
);
884 if (a
& ATTR_CMN_DEVID
) size
+= sizeof(dev_t
);
885 if (a
& ATTR_CMN_FSID
) size
+= sizeof(fsid_t
);
886 if (a
& ATTR_CMN_OBJTYPE
) size
+= sizeof(fsobj_type_t
);
887 if (a
& ATTR_CMN_OBJTAG
) size
+= sizeof(fsobj_tag_t
);
888 if (a
& ATTR_CMN_OBJID
) size
+= sizeof(fsobj_id_t
);
889 if (a
& ATTR_CMN_OBJPERMANENTID
) size
+= sizeof(fsobj_id_t
);
890 if (a
& ATTR_CMN_PAROBJID
) size
+= sizeof(fsobj_id_t
);
891 if (a
& ATTR_CMN_SCRIPT
) size
+= sizeof(text_encoding_t
);
892 if (a
& ATTR_CMN_CRTIME
) size
+= sizeof_timespec
;
893 if (a
& ATTR_CMN_MODTIME
) size
+= sizeof_timespec
;
894 if (a
& ATTR_CMN_CHGTIME
) size
+= sizeof_timespec
;
895 if (a
& ATTR_CMN_ACCTIME
) size
+= sizeof_timespec
;
896 if (a
& ATTR_CMN_BKUPTIME
) size
+= sizeof_timespec
;
897 if (a
& ATTR_CMN_FNDRINFO
) size
+= 32 * sizeof(u_int8_t
);
898 if (a
& ATTR_CMN_OWNERID
) size
+= sizeof(uid_t
);
899 if (a
& ATTR_CMN_GRPID
) size
+= sizeof(gid_t
);
900 if (a
& ATTR_CMN_ACCESSMASK
) size
+= sizeof(u_int32_t
);
901 if (a
& ATTR_CMN_FLAGS
) size
+= sizeof(u_int32_t
);
902 if (a
& ATTR_CMN_USERACCESS
) size
+= sizeof(u_int32_t
);
903 if (a
& ATTR_CMN_FILEID
) size
+= sizeof(u_int64_t
);
904 if (a
& ATTR_CMN_PARENTID
) size
+= sizeof(u_int64_t
);
906 if ((a
= attrlist
->dirattr
) != 0) {
907 if (a
& ATTR_DIR_LINKCOUNT
) size
+= sizeof(u_int32_t
);
908 if (a
& ATTR_DIR_ENTRYCOUNT
) size
+= sizeof(u_int32_t
);
909 if (a
& ATTR_DIR_MOUNTSTATUS
) size
+= sizeof(u_int32_t
);
911 if ((a
= attrlist
->fileattr
) != 0) {
912 if (a
& ATTR_FILE_LINKCOUNT
) size
+= sizeof(u_int32_t
);
913 if (a
& ATTR_FILE_TOTALSIZE
) size
+= sizeof(off_t
);
914 if (a
& ATTR_FILE_ALLOCSIZE
) size
+= sizeof(off_t
);
915 if (a
& ATTR_FILE_IOBLOCKSIZE
) size
+= sizeof(u_int32_t
);
916 if (a
& ATTR_FILE_CLUMPSIZE
) size
+= sizeof(u_int32_t
);
917 if (a
& ATTR_FILE_DEVTYPE
) size
+= sizeof(u_int32_t
);
918 if (a
& ATTR_FILE_DATALENGTH
) size
+= sizeof(off_t
);
919 if (a
& ATTR_FILE_DATAALLOCSIZE
) size
+= sizeof(off_t
);
920 if (a
& ATTR_FILE_RSRCLENGTH
) size
+= sizeof(off_t
);
921 if (a
& ATTR_FILE_RSRCALLOCSIZE
) size
+= sizeof(off_t
);
927 #define KAUTH_DIR_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | \
928 KAUTH_VNODE_ADD_SUBDIRECTORY | \
929 KAUTH_VNODE_DELETE_CHILD)
931 #define KAUTH_DIR_READ_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY)
933 #define KAUTH_DIR_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH)
935 #define KAUTH_FILE_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA)
937 #define KAUTH_FILE_READRIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA)
939 #define KAUTH_FILE_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE)
943 * Compute the same [expensive] user_access value as getattrlist does
946 hfs_real_user_access(vnode_t vp
, vfs_context_t ctx
)
948 u_int32_t user_access
= 0;
950 if (vnode_isdir(vp
)) {
951 if (vnode_authorize(vp
, NULLVP
, KAUTH_DIR_WRITE_RIGHTS
, ctx
) == 0)
953 if (vnode_authorize(vp
, NULLVP
, KAUTH_DIR_READ_RIGHTS
, ctx
) == 0)
955 if (vnode_authorize(vp
, NULLVP
, KAUTH_DIR_EXECUTE_RIGHTS
, ctx
) == 0)
958 if (vnode_authorize(vp
, NULLVP
, KAUTH_FILE_WRITE_RIGHTS
, ctx
) == 0)
960 if (vnode_authorize(vp
, NULLVP
, KAUTH_FILE_READRIGHTS
, ctx
) == 0)
962 if (vnode_authorize(vp
, NULLVP
, KAUTH_FILE_EXECUTE_RIGHTS
, ctx
) == 0)
965 return (user_access
);
971 DerivePermissionSummary(uid_t obj_uid
, gid_t obj_gid
, mode_t obj_mode
,
972 struct mount
*mp
, kauth_cred_t cred
, __unused
struct proc
*p
)
974 unsigned long permissions
;
976 if (obj_uid
== UNKNOWNUID
)
977 obj_uid
= kauth_cred_getuid(cred
);
979 /* User id 0 (root) always gets access. */
980 if (!suser(cred
, NULL
)) {
981 permissions
= R_OK
| W_OK
| X_OK
;
985 /* Otherwise, check the owner. */
986 if (hfs_owner_rights(VFSTOHFS(mp
), obj_uid
, cred
, NULL
, false) == 0) {
987 permissions
= ((unsigned long)obj_mode
& S_IRWXU
) >> 6;
991 /* Otherwise, check the groups. */
992 if (! (((unsigned int)vfs_flags(mp
)) & MNT_UNKNOWNPERMISSIONS
)) {
995 if (kauth_cred_ismember_gid(cred
, obj_gid
, &is_member
) == 0 && is_member
) {
996 permissions
= ((unsigned long)obj_mode
& S_IRWXG
) >> 3;
1001 /* Otherwise, settle for 'others' access. */
1002 permissions
= (unsigned long)obj_mode
& S_IRWXO
;
1005 return (permissions
);