2 * Copyright (c) 2000-2008 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 vfs_context
*ctx
);
63 static void packfileattr(struct attrblock
*abp
, struct hfsmount
*hfsmp
,
64 struct cat_attr
*cattrp
, struct cat_fork
*datafork
,
65 struct cat_fork
*rsrcfork
, struct vnode
*vp
);
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 u_int32_t fixedblocksize
;
105 u_int32_t maxattrblocksize
;
106 u_int32_t currattrbufsize
;
107 void *attrbufptr
= NULL
;
110 struct attrblock attrblk
;
113 int i
, dir_entries
= 0;
114 struct cat_desc
*lastdescp
= NULL
;
115 struct cat_entrylist
*ce_list
= NULL
;
116 directoryhint_t
*dirhint
= NULL
;
120 u_int32_t dirchg
= 0;
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
;
151 dirchg
= dcp
->c_dirchangecnt
;
153 /* Extract directory index and tag (sequence number) from uio_offset */
154 index
= uio_offset(uio
) & HFS_INDEX_MASK
;
155 tag
= uio_offset(uio
) & ~HFS_INDEX_MASK
;
156 if ((index
+ 1) > dir_entries
) {
157 *(ap
->a_eofflag
) = 1;
162 /* Get a buffer to hold packed attributes. */
163 fixedblocksize
= (sizeof(u_int32_t
) + hfs_attrblksize(alist
)); /* 4 bytes for length */
164 maxattrblocksize
= fixedblocksize
;
165 if (alist
->commonattr
& ATTR_CMN_NAME
)
166 maxattrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
167 MALLOC(attrbufptr
, void *, maxattrblocksize
, M_TEMP
, M_WAITOK
);
168 if (attrbufptr
== NULL
) {
172 attrptr
= attrbufptr
;
173 varptr
= (char *)attrbufptr
+ fixedblocksize
; /* Point to variable-length storage */
175 /* Get a detached directory hint (cnode must be locked exclusive) */
176 dirhint
= hfs_getdirhint(dcp
, ((index
- 1) & HFS_INDEX_MASK
) | tag
, TRUE
);
178 /* Hide tag from catalog layer. */
179 dirhint
->dh_index
&= HFS_INDEX_MASK
;
180 if (dirhint
->dh_index
== HFS_INDEX_MASK
) {
181 dirhint
->dh_index
= -1;
185 * Obtain a list of catalog entries and pack their attributes until
186 * the output buffer is full or maxcount entries have been packed.
190 * Constrain our list size.
192 maxentries
= uio_resid(uio
) / (fixedblocksize
+ HFS_AVERAGE_NAME_SIZE
);
193 maxentries
= min(maxentries
, maxcount
);
194 maxentries
= min(maxentries
, MAXCATENTRIES
);
195 if (maxentries
< 1) {
200 /* Initialize a catalog entry list. */
201 MALLOC(ce_list
, struct cat_entrylist
*, CE_LIST_SIZE(maxentries
), M_TEMP
, M_WAITOK
);
202 if (ce_list
== NULL
) {
206 bzero(ce_list
, CE_LIST_SIZE(maxentries
));
207 ce_list
->maxentries
= maxentries
;
210 * Populate the ce_list from the catalog file.
212 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
214 error
= cat_getentriesattr(hfsmp
, dirhint
, ce_list
);
215 /* Don't forget to release the descriptors later! */
217 hfs_systemfile_unlock(hfsmp
, lockflags
);
219 if (error
== ENOENT
) {
220 *(ap
->a_eofflag
) = TRUE
;
228 * Drop the directory lock so we don't deadlock when we:
229 * - acquire a child cnode lock
230 * - make calls to vnode_authorize()
231 * - make calls to kauth_cred_ismember_gid()
236 /* Process the catalog entries. */
237 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
238 struct cnode
*cp
= NULL
;
239 struct vnode
*vp
= NULL
;
240 struct cat_desc
* cdescp
;
241 struct cat_attr
* cattrp
;
242 struct cat_fork c_datafork
;
243 struct cat_fork c_rsrcfork
;
245 bzero(&c_datafork
, sizeof(c_datafork
));
246 bzero(&c_rsrcfork
, sizeof(c_rsrcfork
));
247 cdescp
= &ce_list
->entry
[i
].ce_desc
;
248 cattrp
= &ce_list
->entry
[i
].ce_attr
;
249 c_datafork
.cf_size
= ce_list
->entry
[i
].ce_datasize
;
250 c_datafork
.cf_blocks
= ce_list
->entry
[i
].ce_datablks
;
251 c_rsrcfork
.cf_size
= ce_list
->entry
[i
].ce_rsrcsize
;
252 c_rsrcfork
.cf_blocks
= ce_list
->entry
[i
].ce_rsrcblks
;
254 if ((alist
->commonattr
& ATTR_CMN_USERACCESS
) &&
255 (cattrp
->ca_recflags
& kHFSHasSecurityMask
)) {
257 * Obtain vnode for our vnode_authorize() calls.
259 if (hfs_vget(hfsmp
, cattrp
->ca_fileid
, &vp
, 0) != 0) {
262 } else if (!(ap
->a_options
& FSOPT_NOINMEMUPDATE
)) {
263 /* Get in-memory cnode data (if any). */
264 vp
= hfs_chash_getvnode(hfsmp
, cattrp
->ca_fileid
, 0, 0);
268 /* Only use cnode's decriptor for non-hardlinks */
269 if (!(cp
->c_flag
& C_HARDLINK
))
270 cdescp
= &cp
->c_desc
;
271 cattrp
= &cp
->c_attr
;
272 if (cp
->c_datafork
) {
273 c_datafork
.cf_size
= cp
->c_datafork
->ff_size
;
274 c_datafork
.cf_blocks
= cp
->c_datafork
->ff_blocks
;
276 if (cp
->c_rsrcfork
) {
277 c_rsrcfork
.cf_size
= cp
->c_rsrcfork
->ff_size
;
278 c_rsrcfork
.cf_blocks
= cp
->c_rsrcfork
->ff_blocks
;
280 /* All done with cnode. */
285 *((u_int32_t
*)attrptr
) = 0;
286 attrptr
= ((u_int32_t
*)attrptr
) + 1;
287 attrblk
.ab_attrlist
= alist
;
288 attrblk
.ab_attrbufpp
= &attrptr
;
289 attrblk
.ab_varbufpp
= &varptr
;
290 attrblk
.ab_flags
= 0;
291 attrblk
.ab_blocksize
= maxattrblocksize
;
292 attrblk
.ab_context
= ap
->a_context
;
294 /* Pack catalog entries into attribute buffer. */
295 hfs_packattrblk(&attrblk
, hfsmp
, vp
, cdescp
, cattrp
, &c_datafork
, &c_rsrcfork
, ap
->a_context
);
296 currattrbufsize
= ((char *)varptr
- (char *)attrbufptr
);
298 /* All done with vnode. */
304 /* Make sure there's enough buffer space remaining. */
305 // LP64todo - fix this!
306 if (uio_resid(uio
) < 0 || currattrbufsize
> (u_int32_t
)uio_resid(uio
)) {
309 *((u_int32_t
*)attrbufptr
) = currattrbufsize
;
310 error
= uiomove((caddr_t
)attrbufptr
, currattrbufsize
, uio
);
311 if (error
!= E_NONE
) {
314 attrptr
= attrbufptr
;
315 /* Point to variable-length storage */
316 varptr
= (char *)attrbufptr
+ fixedblocksize
;
317 /* Save the last valid catalog entry */
318 lastdescp
= &ce_list
->entry
[i
].ce_desc
;
320 *ap
->a_actualcount
+= 1;
322 /* Termination checks */
323 if ((--maxcount
<= 0) ||
324 // LP64todo - fix this!
325 uio_resid(uio
) < 0 ||
326 ((u_int32_t
)uio_resid(uio
) < (fixedblocksize
+ HFS_AVERAGE_NAME_SIZE
))){
330 } /* for each catalog entry */
332 /* For small directories, check if we're all done. */
333 if (*ap
->a_actualcount
== (u_long
)dir_entries
) {
334 *(ap
->a_eofflag
) = TRUE
;
337 /* If we skipped catalog entries for reserved files that should
338 * not be listed in namespace, update the index accordingly.
340 if (ce_list
->skipentries
) {
341 index
+= ce_list
->skipentries
;
342 ce_list
->skipentries
= 0;
345 /* If there are more entries then save the last name. */
346 if (index
< dir_entries
347 && !(*(ap
->a_eofflag
))
348 && lastdescp
!= NULL
) {
350 /* Remember last entry */
351 if ((dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) &&
352 (dirhint
->dh_desc
.cd_nameptr
!= NULL
)) {
353 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
354 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
356 dirhint
->dh_desc
.cd_namelen
= lastdescp
->cd_namelen
;
357 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)
358 vfs_addname((const char *)lastdescp
->cd_nameptr
, lastdescp
->cd_namelen
, 0, 0);
359 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
360 dirhint
->dh_index
= index
- 1;
361 dirhint
->dh_desc
.cd_cnid
= lastdescp
->cd_cnid
;
362 dirhint
->dh_desc
.cd_hint
= lastdescp
->cd_hint
;
363 dirhint
->dh_desc
.cd_encoding
= lastdescp
->cd_encoding
;
365 /* No more entries. */
366 *(ap
->a_eofflag
) = TRUE
;
369 /* All done with the catalog descriptors. */
370 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
)
371 cat_releasedesc(&ce_list
->entry
[i
].ce_desc
);
372 ce_list
->realentries
= 0;
374 (void) hfs_lock(VTOC(dvp
), HFS_FORCE_LOCK
);
378 /* Pack directory index and tag into uio_offset. */
379 while (tag
== 0) tag
= (++dcp
->c_dirhinttag
) << HFS_INDEX_BITS
;
380 uio_setoffset(uio
, index
| tag
);
381 dirhint
->dh_index
|= tag
;
384 *ap
->a_newstate
= dirchg
;
386 /* Drop directory hint on error or if there are no more entries */
388 if ((error
!= 0) || (index
>= dir_entries
) || *(ap
->a_eofflag
))
389 hfs_reldirhint(dcp
, dirhint
);
391 hfs_insertdirhint(dcp
, dirhint
);
394 FREE(attrbufptr
, M_TEMP
);
396 FREE(ce_list
, M_TEMP
);
403 /*==================== Attribute list support routines ====================*/
406 * Pack cnode attributes into an attribute block.
410 hfs_packattrblk(struct attrblock
*abp
,
411 struct hfsmount
*hfsmp
,
413 struct cat_desc
*descp
,
414 struct cat_attr
*attrp
,
415 struct cat_fork
*datafork
,
416 struct cat_fork
*rsrcfork
,
417 struct vfs_context
*ctx
)
419 struct attrlist
*attrlistp
= abp
->ab_attrlist
;
421 if (attrlistp
->commonattr
)
422 packcommonattr(abp
, hfsmp
, vp
, descp
, attrp
, ctx
);
424 if (attrlistp
->dirattr
&& S_ISDIR(attrp
->ca_mode
))
425 packdirattr(abp
, hfsmp
, vp
, descp
,attrp
);
427 if (attrlistp
->fileattr
&& !S_ISDIR(attrp
->ca_mode
))
428 packfileattr(abp
, hfsmp
, attrp
, datafork
, rsrcfork
, vp
);
433 mountpointname(struct mount
*mp
)
435 size_t namelength
= strlen(mp
->mnt_vfsstat
.f_mntonname
);
443 * Look backwards through the name string, looking for
444 * the first slash encountered (which must precede the
445 * last part of the pathname).
447 for (c
= mp
->mnt_vfsstat
.f_mntonname
+ namelength
- 1;
448 namelength
> 0; --c
, --namelength
) {
451 } else if (foundchars
) {
456 return (mp
->mnt_vfsstat
.f_mntonname
);
462 struct attrblock
*abp
,
464 const u_int8_t
*name
,
468 struct attrreference
* attr_refptr
;
471 u_int32_t attrlength
;
474 /* A cnode's name may be incorrect for the root of a mounted
475 * filesystem (it can be mounted on a different directory name
476 * than the name of the volume, such as "blah-1"). So for the
477 * root directory, it's best to return the last element of the
478 location where the volume's mounted:
480 if ((vp
!= NULL
) && vnode_isvroot(vp
) &&
481 (mpname
= mountpointname(vnode_mount(vp
)))) {
482 mpnamelen
= strlen(mpname
);
484 /* Trim off any trailing slashes: */
485 while ((mpnamelen
> 0) && (mpname
[mpnamelen
-1] == '/'))
488 /* If there's anything left, use it instead of the volume's name */
490 name
= (u_int8_t
*)mpname
;
499 varbufptr
= *abp
->ab_varbufpp
;
500 attr_refptr
= (struct attrreference
*)(*abp
->ab_attrbufpp
);
502 attrlength
= namelen
+ 1;
503 attr_refptr
->attr_dataoffset
= (char *)varbufptr
- (char *)attr_refptr
;
504 attr_refptr
->attr_length
= attrlength
;
505 (void) strncpy((char *)varbufptr
, (const char *) name
, attrlength
);
507 * Advance beyond the space just allocated and
508 * round up to the next 4-byte boundary:
510 varbufptr
= ((char *)varbufptr
) + attrlength
+ ((4 - (attrlength
& 3)) & 3);
513 *abp
->ab_attrbufpp
= attr_refptr
;
514 *abp
->ab_varbufpp
= varbufptr
;
520 struct attrblock
*abp
,
521 struct hfsmount
*hfsmp
,
523 struct cat_desc
* cdp
,
524 struct cat_attr
* cap
,
525 struct vfs_context
* ctx
)
527 attrgroup_t attr
= abp
->ab_attrlist
->commonattr
;
528 struct mount
*mp
= HFSTOVFS(hfsmp
);
529 void *attrbufptr
= *abp
->ab_attrbufpp
;
530 void *varbufptr
= *abp
->ab_varbufpp
;
531 boolean_t is_64_bit
= proc_is64bit(vfs_context_proc(ctx
));
535 if (attr
& (ATTR_CMN_OWNERID
| ATTR_CMN_GRPID
)) {
536 cuid
= kauth_cred_getuid(vfs_context_ucred(ctx
));
540 if (ATTR_CMN_NAME
& attr
) {
541 packnameattr(abp
, vp
, cdp
->cd_nameptr
, cdp
->cd_namelen
);
542 attrbufptr
= *abp
->ab_attrbufpp
;
543 varbufptr
= *abp
->ab_varbufpp
;
545 if (ATTR_CMN_DEVID
& attr
) {
546 *((dev_t
*)attrbufptr
) = hfsmp
->hfs_raw_dev
;
547 attrbufptr
= ((dev_t
*)attrbufptr
) + 1;
549 if (ATTR_CMN_FSID
& attr
) {
552 fsid
.val
[0] = (long)hfsmp
->hfs_raw_dev
;
553 fsid
.val
[1] = (long)vfs_typenum(mp
);
554 *((fsid_t
*)attrbufptr
) = fsid
;
555 attrbufptr
= ((fsid_t
*)attrbufptr
) + 1;
557 if (ATTR_CMN_OBJTYPE
& attr
) {
558 *((fsobj_type_t
*)attrbufptr
) = IFTOVT(cap
->ca_mode
);
559 attrbufptr
= ((fsobj_type_t
*)attrbufptr
) + 1;
561 if (ATTR_CMN_OBJTAG
& attr
) {
562 *((fsobj_tag_t
*)attrbufptr
) = VT_HFS
;
563 attrbufptr
= ((fsobj_tag_t
*)attrbufptr
) + 1;
566 * Exporting file IDs from HFS Plus:
568 * For "normal" files the c_fileid is the same value as the
569 * c_cnid. But for hard link files, they are different - the
570 * c_cnid belongs to the active directory entry (ie the link)
571 * and the c_fileid is for the actual inode (ie the data file).
573 * The stat call (getattr) will always return the c_fileid
574 * and Carbon APIs, which are hardlink-ignorant, will always
575 * receive the c_cnid (from getattrlist).
577 if (ATTR_CMN_OBJID
& attr
) {
578 ((fsobj_id_t
*)attrbufptr
)->fid_objno
= cdp
->cd_cnid
;
579 ((fsobj_id_t
*)attrbufptr
)->fid_generation
= 0;
580 attrbufptr
= ((fsobj_id_t
*)attrbufptr
) + 1;
582 if (ATTR_CMN_OBJPERMANENTID
& attr
) {
583 ((fsobj_id_t
*)attrbufptr
)->fid_objno
= cdp
->cd_cnid
;
584 ((fsobj_id_t
*)attrbufptr
)->fid_generation
= 0;
585 attrbufptr
= ((fsobj_id_t
*)attrbufptr
) + 1;
587 if (ATTR_CMN_PAROBJID
& attr
) {
588 ((fsobj_id_t
*)attrbufptr
)->fid_objno
= cdp
->cd_parentcnid
;
589 ((fsobj_id_t
*)attrbufptr
)->fid_generation
= 0;
590 attrbufptr
= ((fsobj_id_t
*)attrbufptr
) + 1;
592 if (ATTR_CMN_SCRIPT
& attr
) {
593 *((text_encoding_t
*)attrbufptr
) = cdp
->cd_encoding
;
594 attrbufptr
= ((text_encoding_t
*)attrbufptr
) + 1;
596 if (ATTR_CMN_CRTIME
& attr
) {
598 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_itime
;
599 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
600 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
603 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_itime
;
604 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
605 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
608 if (ATTR_CMN_MODTIME
& attr
) {
610 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_mtime
;
611 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
612 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
615 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_mtime
;
616 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
617 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
620 if (ATTR_CMN_CHGTIME
& attr
) {
622 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_ctime
;
623 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
624 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
627 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_ctime
;
628 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
629 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
632 if (ATTR_CMN_ACCTIME
& attr
) {
634 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_atime
;
635 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
636 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
639 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_atime
;
640 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
641 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
644 if (ATTR_CMN_BKUPTIME
& attr
) {
646 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_btime
;
647 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
648 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
651 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_btime
;
652 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
653 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
656 if (ATTR_CMN_FNDRINFO
& attr
) {
657 bcopy(&cap
->ca_finderinfo
, attrbufptr
, sizeof(u_int8_t
) * 32);
658 /* Don't expose a symlink's private type/creator. */
659 if (S_ISLNK(cap
->ca_mode
)) {
660 struct FndrFileInfo
*fip
;
662 fip
= (struct FndrFileInfo
*)attrbufptr
;
666 attrbufptr
= (char *)attrbufptr
+ sizeof(u_int8_t
) * 32;
668 if (ATTR_CMN_OWNERID
& attr
) {
669 uid_t nuid
= cap
->ca_uid
;
672 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
)
674 else if (nuid
== UNKNOWNUID
)
678 *((uid_t
*)attrbufptr
) = nuid
;
679 attrbufptr
= ((uid_t
*)attrbufptr
) + 1;
681 if (ATTR_CMN_GRPID
& attr
) {
682 gid_t ngid
= cap
->ca_gid
;
685 gid_t cgid
= kauth_cred_getgid(vfs_context_ucred(ctx
));
686 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
)
688 else if (ngid
== UNKNOWNUID
)
692 *((gid_t
*)attrbufptr
) = ngid
;
693 attrbufptr
= ((gid_t
*)attrbufptr
) + 1;
695 if (ATTR_CMN_ACCESSMASK
& attr
) {
697 * [2856576] Since we are dynamically changing the owner, also
698 * effectively turn off the set-user-id and set-group-id bits,
699 * just like chmod(2) would when changing ownership. This prevents
700 * a security hole where set-user-id programs run as whoever is
701 * logged on (or root if nobody is logged in yet!)
703 *((u_int32_t
*)attrbufptr
) = (cap
->ca_uid
== UNKNOWNUID
) ?
704 cap
->ca_mode
& ~(S_ISUID
| S_ISGID
) : cap
->ca_mode
;
705 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
707 if (ATTR_CMN_FLAGS
& attr
) {
708 *((u_int32_t
*)attrbufptr
) = cap
->ca_flags
;
709 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
711 if (ATTR_CMN_USERACCESS
& attr
) {
712 u_int32_t user_access
;
714 /* Take the long path when we have an ACL */
715 if ((vp
!= NULLVP
) && (cap
->ca_recflags
& kHFSHasSecurityMask
)) {
716 user_access
= hfs_real_user_access(vp
, abp
->ab_context
);
718 user_access
= DerivePermissionSummary(cap
->ca_uid
, cap
->ca_gid
,
719 cap
->ca_mode
, mp
, proc_ucred(current_proc()), 0);
721 /* Also consider READ-ONLY file system. */
722 if (vfs_flags(mp
) & MNT_RDONLY
) {
723 user_access
&= ~W_OK
;
725 /* Locked objects are not writable either */
726 if ((cap
->ca_flags
& UF_IMMUTABLE
) && (vfs_context_suser(abp
->ab_context
) != 0))
727 user_access
&= ~W_OK
;
728 if ((cap
->ca_flags
& SF_IMMUTABLE
) && (vfs_context_suser(abp
->ab_context
) == 0))
729 user_access
&= ~W_OK
;
731 *((u_int32_t
*)attrbufptr
) = user_access
;
732 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
734 if (ATTR_CMN_FILEID
& attr
) {
735 *((u_int64_t
*)attrbufptr
) = cap
->ca_fileid
;
736 attrbufptr
= ((u_int64_t
*)attrbufptr
) + 1;
738 if (ATTR_CMN_PARENTID
& attr
) {
739 *((u_int64_t
*)attrbufptr
) = cdp
->cd_parentcnid
;
740 attrbufptr
= ((u_int64_t
*)attrbufptr
) + 1;
743 *abp
->ab_attrbufpp
= attrbufptr
;
744 *abp
->ab_varbufpp
= varbufptr
;
749 struct attrblock
*abp
,
750 struct hfsmount
*hfsmp
,
752 struct cat_desc
* descp
,
753 struct cat_attr
* cattrp
)
755 attrgroup_t attr
= abp
->ab_attrlist
->dirattr
;
756 void *attrbufptr
= *abp
->ab_attrbufpp
;
760 * The DIR_LINKCOUNT is the count of real directory hard links.
761 * (i.e. its not the sum of the implied "." and ".." references
762 * typically used in stat's st_nlink field)
764 if (ATTR_DIR_LINKCOUNT
& attr
) {
765 *((u_int32_t
*)attrbufptr
) = cattrp
->ca_linkcount
;
766 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
768 if (ATTR_DIR_ENTRYCOUNT
& attr
) {
769 entries
= cattrp
->ca_entries
;
771 if (descp
->cd_parentcnid
== kHFSRootParentID
) {
772 if (hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
!= 0)
773 --entries
; /* hide private dir */
774 if (hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
!= 0)
775 --entries
; /* hide private dir */
777 ((hfsmp
->vcbAtrb
& kHFSVolumeJournaledMask
) &&
778 (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))
779 entries
-= 2; /* hide the journal files */
782 *((u_int32_t
*)attrbufptr
) = entries
;
783 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
785 if (ATTR_DIR_MOUNTSTATUS
& attr
) {
786 if (vp
!= NULL
&& vnode_mountedhere(vp
) != NULL
)
787 *((u_int32_t
*)attrbufptr
) = DIR_MNTSTATUS_MNTPOINT
;
789 *((u_int32_t
*)attrbufptr
) = 0;
790 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
792 *abp
->ab_attrbufpp
= attrbufptr
;
797 struct attrblock
*abp
,
798 struct hfsmount
*hfsmp
,
799 struct cat_attr
*cattrp
,
800 struct cat_fork
*datafork
,
801 struct cat_fork
*rsrcfork
,
807 attrgroup_t attr
= abp
->ab_attrlist
->fileattr
;
808 void *attrbufptr
= *abp
->ab_attrbufpp
;
809 void *varbufptr
= *abp
->ab_varbufpp
;
810 u_int32_t allocblksize
;
812 allocblksize
= HFSTOVCB(hfsmp
)->blockSize
;
814 off_t datasize
= datafork
->cf_size
;
815 off_t totalsize
= datasize
+ rsrcfork
->cf_size
;
817 if ( cattrp
->ca_flags
& UF_COMPRESSED
) {
818 if (attr
& (ATTR_FILE_DATALENGTH
|ATTR_FILE_TOTALSIZE
)) {
819 if ( 0 == hfs_uncompressed_size_of_compressed_file(hfsmp
, vp
, cattrp
->ca_fileid
, &datasize
, 1) ) { /* 1 == don't take the cnode lock */
820 /* total size of a compressed file is just the data size */
821 totalsize
= datasize
;
827 if (ATTR_FILE_LINKCOUNT
& attr
) {
828 *((u_int32_t
*)attrbufptr
) = cattrp
->ca_linkcount
;
829 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
831 if (ATTR_FILE_TOTALSIZE
& attr
) {
832 *((off_t
*)attrbufptr
) = totalsize
;
833 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
835 if (ATTR_FILE_ALLOCSIZE
& attr
) {
836 *((off_t
*)attrbufptr
) =
837 (off_t
)cattrp
->ca_blocks
* (off_t
)allocblksize
;
838 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
840 if (ATTR_FILE_IOBLOCKSIZE
& attr
) {
841 *((u_int32_t
*)attrbufptr
) = hfsmp
->hfs_logBlockSize
;
842 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
844 if (ATTR_FILE_CLUMPSIZE
& attr
) {
845 *((u_int32_t
*)attrbufptr
) = hfsmp
->vcbClpSiz
;
846 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
848 if (ATTR_FILE_DEVTYPE
& attr
) {
849 if (S_ISBLK(cattrp
->ca_mode
) || S_ISCHR(cattrp
->ca_mode
))
850 *((u_int32_t
*)attrbufptr
) = (u_int32_t
)cattrp
->ca_rdev
;
852 *((u_int32_t
*)attrbufptr
) = 0;
853 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
856 if (ATTR_FILE_DATALENGTH
& attr
) {
857 *((off_t
*)attrbufptr
) = datasize
;
858 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
862 /* fake the data fork size on a decmpfs compressed file to reflect the
863 * uncompressed size. This ensures proper reading and copying of these files.
864 * NOTE: we may need to get the vnode here because the vnode parameter
865 * passed by hfs_vnop_readdirattr() may be null.
868 if ( cattrp
->ca_flags
& UF_COMPRESSED
) {
869 if (attr
& ATTR_FILE_DATAALLOCSIZE
) {
870 *((off_t
*)attrbufptr
) = (off_t
)rsrcfork
->cf_blocks
* (off_t
)allocblksize
;
871 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
873 if (attr
& ATTR_FILE_RSRCLENGTH
) {
874 *((off_t
*)attrbufptr
) = 0;
875 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
877 if (attr
& ATTR_FILE_RSRCALLOCSIZE
) {
878 *((off_t
*)attrbufptr
) = 0;
879 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
885 if (ATTR_FILE_DATAALLOCSIZE
& attr
) {
886 *((off_t
*)attrbufptr
) = (off_t
)datafork
->cf_blocks
* (off_t
)allocblksize
;
887 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
889 if (ATTR_FILE_RSRCLENGTH
& attr
) {
890 *((off_t
*)attrbufptr
) = rsrcfork
->cf_size
;
891 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
893 if (ATTR_FILE_RSRCALLOCSIZE
& attr
) {
894 *((off_t
*)attrbufptr
) = (off_t
)rsrcfork
->cf_blocks
* (off_t
)allocblksize
;
895 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
898 *abp
->ab_attrbufpp
= attrbufptr
;
899 *abp
->ab_varbufpp
= varbufptr
;
903 * Calculate the total size of an attribute block.
907 hfs_attrblksize(struct attrlist
*attrlist
)
912 boolean_t is_64_bit
= proc_is64bit(current_proc());
915 sizeof_timespec
= sizeof(struct user64_timespec
);
917 sizeof_timespec
= sizeof(struct user32_timespec
);
919 DBG_ASSERT((attrlist
->commonattr
& ~ATTR_CMN_VALIDMASK
) == 0);
921 DBG_ASSERT((attrlist
->volattr
& ~ATTR_VOL_VALIDMASK
) == 0);
923 DBG_ASSERT((attrlist
->dirattr
& ~ATTR_DIR_VALIDMASK
) == 0);
925 DBG_ASSERT((attrlist
->fileattr
& ~ATTR_FILE_VALIDMASK
) == 0);
927 DBG_ASSERT((attrlist
->forkattr
& ~ATTR_FORK_VALIDMASK
) == 0);
931 if ((a
= attrlist
->commonattr
) != 0) {
932 if (a
& ATTR_CMN_NAME
) size
+= sizeof(struct attrreference
);
933 if (a
& ATTR_CMN_DEVID
) size
+= sizeof(dev_t
);
934 if (a
& ATTR_CMN_FSID
) size
+= sizeof(fsid_t
);
935 if (a
& ATTR_CMN_OBJTYPE
) size
+= sizeof(fsobj_type_t
);
936 if (a
& ATTR_CMN_OBJTAG
) size
+= sizeof(fsobj_tag_t
);
937 if (a
& ATTR_CMN_OBJID
) size
+= sizeof(fsobj_id_t
);
938 if (a
& ATTR_CMN_OBJPERMANENTID
) size
+= sizeof(fsobj_id_t
);
939 if (a
& ATTR_CMN_PAROBJID
) size
+= sizeof(fsobj_id_t
);
940 if (a
& ATTR_CMN_SCRIPT
) size
+= sizeof(text_encoding_t
);
941 if (a
& ATTR_CMN_CRTIME
) size
+= sizeof_timespec
;
942 if (a
& ATTR_CMN_MODTIME
) size
+= sizeof_timespec
;
943 if (a
& ATTR_CMN_CHGTIME
) size
+= sizeof_timespec
;
944 if (a
& ATTR_CMN_ACCTIME
) size
+= sizeof_timespec
;
945 if (a
& ATTR_CMN_BKUPTIME
) size
+= sizeof_timespec
;
946 if (a
& ATTR_CMN_FNDRINFO
) size
+= 32 * sizeof(u_int8_t
);
947 if (a
& ATTR_CMN_OWNERID
) size
+= sizeof(uid_t
);
948 if (a
& ATTR_CMN_GRPID
) size
+= sizeof(gid_t
);
949 if (a
& ATTR_CMN_ACCESSMASK
) size
+= sizeof(u_int32_t
);
950 if (a
& ATTR_CMN_FLAGS
) size
+= sizeof(u_int32_t
);
951 if (a
& ATTR_CMN_USERACCESS
) size
+= sizeof(u_int32_t
);
952 if (a
& ATTR_CMN_FILEID
) size
+= sizeof(u_int64_t
);
953 if (a
& ATTR_CMN_PARENTID
) size
+= sizeof(u_int64_t
);
955 if ((a
= attrlist
->dirattr
) != 0) {
956 if (a
& ATTR_DIR_LINKCOUNT
) size
+= sizeof(u_int32_t
);
957 if (a
& ATTR_DIR_ENTRYCOUNT
) size
+= sizeof(u_int32_t
);
958 if (a
& ATTR_DIR_MOUNTSTATUS
) size
+= sizeof(u_int32_t
);
960 if ((a
= attrlist
->fileattr
) != 0) {
961 if (a
& ATTR_FILE_LINKCOUNT
) size
+= sizeof(u_int32_t
);
962 if (a
& ATTR_FILE_TOTALSIZE
) size
+= sizeof(off_t
);
963 if (a
& ATTR_FILE_ALLOCSIZE
) size
+= sizeof(off_t
);
964 if (a
& ATTR_FILE_IOBLOCKSIZE
) size
+= sizeof(u_int32_t
);
965 if (a
& ATTR_FILE_CLUMPSIZE
) size
+= sizeof(u_int32_t
);
966 if (a
& ATTR_FILE_DEVTYPE
) size
+= sizeof(u_int32_t
);
967 if (a
& ATTR_FILE_DATALENGTH
) size
+= sizeof(off_t
);
968 if (a
& ATTR_FILE_DATAALLOCSIZE
) size
+= sizeof(off_t
);
969 if (a
& ATTR_FILE_RSRCLENGTH
) size
+= sizeof(off_t
);
970 if (a
& ATTR_FILE_RSRCALLOCSIZE
) size
+= sizeof(off_t
);
976 #define KAUTH_DIR_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | \
977 KAUTH_VNODE_ADD_SUBDIRECTORY | \
978 KAUTH_VNODE_DELETE_CHILD)
980 #define KAUTH_DIR_READ_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY)
982 #define KAUTH_DIR_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH)
984 #define KAUTH_FILE_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA)
986 #define KAUTH_FILE_READRIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA)
988 #define KAUTH_FILE_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE)
992 * Compute the same [expensive] user_access value as getattrlist does
995 hfs_real_user_access(vnode_t vp
, vfs_context_t ctx
)
997 u_int32_t user_access
= 0;
999 if (vnode_isdir(vp
)) {
1000 if (vnode_authorize(vp
, NULLVP
, KAUTH_DIR_WRITE_RIGHTS
, ctx
) == 0)
1001 user_access
|= W_OK
;
1002 if (vnode_authorize(vp
, NULLVP
, KAUTH_DIR_READ_RIGHTS
, ctx
) == 0)
1003 user_access
|= R_OK
;
1004 if (vnode_authorize(vp
, NULLVP
, KAUTH_DIR_EXECUTE_RIGHTS
, ctx
) == 0)
1005 user_access
|= X_OK
;
1007 if (vnode_authorize(vp
, NULLVP
, KAUTH_FILE_WRITE_RIGHTS
, ctx
) == 0)
1008 user_access
|= W_OK
;
1009 if (vnode_authorize(vp
, NULLVP
, KAUTH_FILE_READRIGHTS
, ctx
) == 0)
1010 user_access
|= R_OK
;
1011 if (vnode_authorize(vp
, NULLVP
, KAUTH_FILE_EXECUTE_RIGHTS
, ctx
) == 0)
1012 user_access
|= X_OK
;
1014 return (user_access
);
1020 DerivePermissionSummary(uid_t obj_uid
, gid_t obj_gid
, mode_t obj_mode
,
1021 struct mount
*mp
, kauth_cred_t cred
, __unused
struct proc
*p
)
1023 u_int32_t permissions
;
1025 if (obj_uid
== UNKNOWNUID
)
1026 obj_uid
= kauth_cred_getuid(cred
);
1028 /* User id 0 (root) always gets access. */
1029 if (!suser(cred
, NULL
)) {
1030 permissions
= R_OK
| W_OK
| X_OK
;
1034 /* Otherwise, check the owner. */
1035 if (hfs_owner_rights(VFSTOHFS(mp
), obj_uid
, cred
, NULL
, false) == 0) {
1036 permissions
= ((u_int32_t
)obj_mode
& S_IRWXU
) >> 6;
1040 /* Otherwise, check the groups. */
1041 if (! (((unsigned int)vfs_flags(mp
)) & MNT_UNKNOWNPERMISSIONS
)) {
1044 if (kauth_cred_ismember_gid(cred
, obj_gid
, &is_member
) == 0 && is_member
) {
1045 permissions
= ((u_int32_t
)obj_mode
& S_IRWXG
) >> 3;
1050 /* Otherwise, settle for 'others' access. */
1051 permissions
= (u_int32_t
)obj_mode
& S_IRWXO
;
1054 return (permissions
);