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>
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"
55 /* Packing routines: */
57 static void packnameattr(struct attrblock
*abp
, struct vnode
*vp
,
58 const u_int8_t
*name
, int namelen
);
60 static void packcommonattr(struct attrblock
*abp
, struct hfsmount
*hfsmp
,
61 struct vnode
*vp
, struct cat_desc
* cdp
,
62 struct cat_attr
* cap
, struct vfs_context
*ctx
);
64 static void packfileattr(struct attrblock
*abp
, struct hfsmount
*hfsmp
,
65 struct cat_attr
*cattrp
, struct cat_fork
*datafork
,
66 struct cat_fork
*rsrcfork
, struct vnode
*vp
);
68 static void packdirattr(struct attrblock
*abp
, struct hfsmount
*hfsmp
,
69 struct vnode
*vp
, struct cat_desc
* descp
,
70 struct cat_attr
* cattrp
);
72 static u_int32_t
hfs_real_user_access(vnode_t vp
, vfs_context_t ctx
);
75 * readdirattr operation will return attributes for the items in the
76 * directory specified.
78 * It does not do . and .. entries. The problem is if you are at the root of the
79 * hfs directory and go to .. you could be crossing a mountpoint into a
80 * different (ufs) file system. The attributes that apply for it may not
81 * apply for the file system you are doing the readdirattr on. To make life
82 * 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 if (VTOC(dvp
)->c_bsdflags
& UF_COMPRESSED
) {
143 int compressed
= hfs_file_is_compressed(VTOC(dvp
), 0); /* 0 == take the cnode lock */
146 error
= check_for_dataless_file(dvp
, NAMESPACE_HANDLER_READ_OP
);
155 * Take an exclusive directory lock since we manipulate the directory hints
157 if ((error
= hfs_lock(VTOC(dvp
), HFS_EXCLUSIVE_LOCK
))) {
163 dir_entries
= dcp
->c_entries
;
164 dirchg
= dcp
->c_dirchangecnt
;
166 /* Extract directory index and tag (sequence number) from uio_offset */
167 index
= uio_offset(uio
) & HFS_INDEX_MASK
;
168 tag
= uio_offset(uio
) & ~HFS_INDEX_MASK
;
169 if ((index
+ 1) > dir_entries
) {
170 *(ap
->a_eofflag
) = 1;
175 /* Get a buffer to hold packed attributes. */
176 fixedblocksize
= (sizeof(u_int32_t
) + hfs_attrblksize(alist
)); /* 4 bytes for length */
177 maxattrblocksize
= fixedblocksize
;
178 if (alist
->commonattr
& ATTR_CMN_NAME
)
179 maxattrblocksize
+= kHFSPlusMaxFileNameBytes
+ 1;
180 MALLOC(attrbufptr
, void *, maxattrblocksize
, M_TEMP
, M_WAITOK
);
181 if (attrbufptr
== NULL
) {
185 attrptr
= attrbufptr
;
186 varptr
= (char *)attrbufptr
+ fixedblocksize
; /* Point to variable-length storage */
188 /* Get a detached directory hint (cnode must be locked exclusive) */
189 dirhint
= hfs_getdirhint(dcp
, ((index
- 1) & HFS_INDEX_MASK
) | tag
, TRUE
);
191 /* Hide tag from catalog layer. */
192 dirhint
->dh_index
&= HFS_INDEX_MASK
;
193 if (dirhint
->dh_index
== HFS_INDEX_MASK
) {
194 dirhint
->dh_index
= -1;
198 * Obtain a list of catalog entries and pack their attributes until
199 * the output buffer is full or maxcount entries have been packed.
203 * Constrain our list size.
205 maxentries
= uio_resid(uio
) / (fixedblocksize
+ HFS_AVERAGE_NAME_SIZE
);
206 maxentries
= min(maxentries
, maxcount
);
207 maxentries
= min(maxentries
, MAXCATENTRIES
);
208 if (maxentries
< 1) {
213 /* Initialize a catalog entry list. */
214 MALLOC(ce_list
, struct cat_entrylist
*, CE_LIST_SIZE(maxentries
), M_TEMP
, M_WAITOK
);
215 if (ce_list
== NULL
) {
219 bzero(ce_list
, CE_LIST_SIZE(maxentries
));
220 ce_list
->maxentries
= maxentries
;
223 * Populate the ce_list from the catalog file.
225 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
227 error
= cat_getentriesattr(hfsmp
, dirhint
, ce_list
);
228 /* Don't forget to release the descriptors later! */
230 hfs_systemfile_unlock(hfsmp
, lockflags
);
232 if (error
== ENOENT
) {
233 *(ap
->a_eofflag
) = TRUE
;
241 * Drop the directory lock so we don't deadlock when we:
242 * - acquire a child cnode lock
243 * - make calls to vnode_authorize()
244 * - make calls to kauth_cred_ismember_gid()
249 /* Process the catalog entries. */
250 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
) {
251 struct cnode
*cp
= NULL
;
252 struct vnode
*vp
= NULL
;
253 struct cat_desc
* cdescp
;
254 struct cat_attr
* cattrp
;
255 struct cat_fork c_datafork
;
256 struct cat_fork c_rsrcfork
;
258 bzero(&c_datafork
, sizeof(c_datafork
));
259 bzero(&c_rsrcfork
, sizeof(c_rsrcfork
));
260 cdescp
= &ce_list
->entry
[i
].ce_desc
;
261 cattrp
= &ce_list
->entry
[i
].ce_attr
;
262 c_datafork
.cf_size
= ce_list
->entry
[i
].ce_datasize
;
263 c_datafork
.cf_blocks
= ce_list
->entry
[i
].ce_datablks
;
264 c_rsrcfork
.cf_size
= ce_list
->entry
[i
].ce_rsrcsize
;
265 c_rsrcfork
.cf_blocks
= ce_list
->entry
[i
].ce_rsrcblks
;
267 if ((alist
->commonattr
& ATTR_CMN_USERACCESS
) &&
268 (cattrp
->ca_recflags
& kHFSHasSecurityMask
)) {
270 * Obtain vnode for our vnode_authorize() calls.
272 if (hfs_vget(hfsmp
, cattrp
->ca_fileid
, &vp
, 0, 0) != 0) {
275 } else if (!(ap
->a_options
& FSOPT_NOINMEMUPDATE
)) {
276 /* Get in-memory cnode data (if any). */
277 vp
= hfs_chash_getvnode(hfsmp
, cattrp
->ca_fileid
, 0, 0, 0);
281 /* Only use cnode's decriptor for non-hardlinks */
282 if (!(cp
->c_flag
& C_HARDLINK
))
283 cdescp
= &cp
->c_desc
;
284 cattrp
= &cp
->c_attr
;
285 if (cp
->c_datafork
) {
286 c_datafork
.cf_size
= cp
->c_datafork
->ff_size
;
287 c_datafork
.cf_blocks
= cp
->c_datafork
->ff_blocks
;
289 if (cp
->c_rsrcfork
) {
290 c_rsrcfork
.cf_size
= cp
->c_rsrcfork
->ff_size
;
291 c_rsrcfork
.cf_blocks
= cp
->c_rsrcfork
->ff_blocks
;
293 /* All done with cnode. */
298 *((u_int32_t
*)attrptr
) = 0;
299 attrptr
= ((u_int32_t
*)attrptr
) + 1;
300 attrblk
.ab_attrlist
= alist
;
301 attrblk
.ab_attrbufpp
= &attrptr
;
302 attrblk
.ab_varbufpp
= &varptr
;
303 attrblk
.ab_flags
= 0;
304 attrblk
.ab_blocksize
= maxattrblocksize
;
305 attrblk
.ab_context
= ap
->a_context
;
307 /* Pack catalog entries into attribute buffer. */
308 hfs_packattrblk(&attrblk
, hfsmp
, vp
, cdescp
, cattrp
, &c_datafork
, &c_rsrcfork
, ap
->a_context
);
309 currattrbufsize
= ((char *)varptr
- (char *)attrbufptr
);
311 /* All done with vnode. */
317 /* Make sure there's enough buffer space remaining. */
318 // LP64todo - fix this!
319 if (uio_resid(uio
) < 0 || currattrbufsize
> (u_int32_t
)uio_resid(uio
)) {
322 *((u_int32_t
*)attrbufptr
) = currattrbufsize
;
323 error
= uiomove((caddr_t
)attrbufptr
, currattrbufsize
, uio
);
324 if (error
!= E_NONE
) {
327 attrptr
= attrbufptr
;
328 /* Point to variable-length storage */
329 varptr
= (char *)attrbufptr
+ fixedblocksize
;
330 /* Save the last valid catalog entry */
331 lastdescp
= &ce_list
->entry
[i
].ce_desc
;
333 *ap
->a_actualcount
+= 1;
335 /* Termination checks */
336 if ((--maxcount
<= 0) ||
337 // LP64todo - fix this!
338 uio_resid(uio
) < 0 ||
339 ((u_int32_t
)uio_resid(uio
) < (fixedblocksize
+ HFS_AVERAGE_NAME_SIZE
))){
343 } /* for each catalog entry */
345 /* For small directories, check if we're all done. */
346 if (*ap
->a_actualcount
== (u_long
)dir_entries
) {
347 *(ap
->a_eofflag
) = TRUE
;
350 /* If we skipped catalog entries for reserved files that should
351 * not be listed in namespace, update the index accordingly.
353 if (ce_list
->skipentries
) {
354 index
+= ce_list
->skipentries
;
355 ce_list
->skipentries
= 0;
358 /* If there are more entries then save the last name. */
359 if (index
< dir_entries
360 && !(*(ap
->a_eofflag
))
361 && lastdescp
!= NULL
) {
363 /* Remember last entry */
364 if ((dirhint
->dh_desc
.cd_flags
& CD_HASBUF
) &&
365 (dirhint
->dh_desc
.cd_nameptr
!= NULL
)) {
366 dirhint
->dh_desc
.cd_flags
&= ~CD_HASBUF
;
367 vfs_removename((const char *)dirhint
->dh_desc
.cd_nameptr
);
369 dirhint
->dh_desc
.cd_namelen
= lastdescp
->cd_namelen
;
370 dirhint
->dh_desc
.cd_nameptr
= (const u_int8_t
*)
371 vfs_addname((const char *)lastdescp
->cd_nameptr
, lastdescp
->cd_namelen
, 0, 0);
372 dirhint
->dh_desc
.cd_flags
|= CD_HASBUF
;
373 dirhint
->dh_index
= index
- 1;
374 dirhint
->dh_desc
.cd_cnid
= lastdescp
->cd_cnid
;
375 dirhint
->dh_desc
.cd_hint
= lastdescp
->cd_hint
;
376 dirhint
->dh_desc
.cd_encoding
= lastdescp
->cd_encoding
;
378 /* No more entries. */
379 *(ap
->a_eofflag
) = TRUE
;
382 /* All done with the catalog descriptors. */
383 for (i
= 0; i
< (int)ce_list
->realentries
; ++i
)
384 cat_releasedesc(&ce_list
->entry
[i
].ce_desc
);
385 ce_list
->realentries
= 0;
387 (void) hfs_lock(VTOC(dvp
), HFS_FORCE_LOCK
);
391 /* Pack directory index and tag into uio_offset. */
392 while (tag
== 0) tag
= (++dcp
->c_dirhinttag
) << HFS_INDEX_BITS
;
393 uio_setoffset(uio
, index
| tag
);
394 dirhint
->dh_index
|= tag
;
397 *ap
->a_newstate
= dirchg
;
399 /* Drop directory hint on error or if there are no more entries */
401 if ((error
!= 0) || (index
>= dir_entries
) || *(ap
->a_eofflag
))
402 hfs_reldirhint(dcp
, dirhint
);
404 hfs_insertdirhint(dcp
, dirhint
);
407 FREE(attrbufptr
, M_TEMP
);
409 FREE(ce_list
, M_TEMP
);
416 /*==================== Attribute list support routines ====================*/
419 * Pack cnode attributes into an attribute block.
423 hfs_packattrblk(struct attrblock
*abp
,
424 struct hfsmount
*hfsmp
,
426 struct cat_desc
*descp
,
427 struct cat_attr
*attrp
,
428 struct cat_fork
*datafork
,
429 struct cat_fork
*rsrcfork
,
430 struct vfs_context
*ctx
)
432 struct attrlist
*attrlistp
= abp
->ab_attrlist
;
434 if (attrlistp
->commonattr
)
435 packcommonattr(abp
, hfsmp
, vp
, descp
, attrp
, ctx
);
437 if (attrlistp
->dirattr
&& S_ISDIR(attrp
->ca_mode
))
438 packdirattr(abp
, hfsmp
, vp
, descp
,attrp
);
440 if (attrlistp
->fileattr
&& !S_ISDIR(attrp
->ca_mode
))
441 packfileattr(abp
, hfsmp
, attrp
, datafork
, rsrcfork
, vp
);
446 mountpointname(struct mount
*mp
)
448 size_t namelength
= strlen(mp
->mnt_vfsstat
.f_mntonname
);
456 * Look backwards through the name string, looking for
457 * the first slash encountered (which must precede the
458 * last part of the pathname).
460 for (c
= mp
->mnt_vfsstat
.f_mntonname
+ namelength
- 1;
461 namelength
> 0; --c
, --namelength
) {
464 } else if (foundchars
) {
469 return (mp
->mnt_vfsstat
.f_mntonname
);
475 struct attrblock
*abp
,
477 const u_int8_t
*name
,
481 struct attrreference
* attr_refptr
;
484 u_int32_t attrlength
;
487 /* A cnode's name may be incorrect for the root of a mounted
488 * filesystem (it can be mounted on a different directory name
489 * than the name of the volume, such as "blah-1"). So for the
490 * root directory, it's best to return the last element of the
491 location where the volume's mounted:
493 if ((vp
!= NULL
) && vnode_isvroot(vp
) &&
494 (mpname
= mountpointname(vnode_mount(vp
)))) {
495 mpnamelen
= strlen(mpname
);
497 /* Trim off any trailing slashes: */
498 while ((mpnamelen
> 0) && (mpname
[mpnamelen
-1] == '/'))
501 /* If there's anything left, use it instead of the volume's name */
503 name
= (u_int8_t
*)mpname
;
512 varbufptr
= *abp
->ab_varbufpp
;
513 attr_refptr
= (struct attrreference
*)(*abp
->ab_attrbufpp
);
515 attrlength
= namelen
+ 1;
516 attr_refptr
->attr_dataoffset
= (char *)varbufptr
- (char *)attr_refptr
;
517 attr_refptr
->attr_length
= attrlength
;
518 (void) strncpy((char *)varbufptr
, (const char *) name
, attrlength
);
520 * Advance beyond the space just allocated and
521 * round up to the next 4-byte boundary:
523 varbufptr
= ((char *)varbufptr
) + attrlength
+ ((4 - (attrlength
& 3)) & 3);
526 *abp
->ab_attrbufpp
= attr_refptr
;
527 *abp
->ab_varbufpp
= varbufptr
;
533 struct attrblock
*abp
,
534 struct hfsmount
*hfsmp
,
536 struct cat_desc
* cdp
,
537 struct cat_attr
* cap
,
538 struct vfs_context
* ctx
)
540 attrgroup_t attr
= abp
->ab_attrlist
->commonattr
;
541 struct mount
*mp
= HFSTOVFS(hfsmp
);
542 void *attrbufptr
= *abp
->ab_attrbufpp
;
543 void *varbufptr
= *abp
->ab_varbufpp
;
544 boolean_t is_64_bit
= proc_is64bit(vfs_context_proc(ctx
));
548 if (attr
& (ATTR_CMN_OWNERID
| ATTR_CMN_GRPID
)) {
549 cuid
= kauth_cred_getuid(vfs_context_ucred(ctx
));
553 if (ATTR_CMN_NAME
& attr
) {
554 packnameattr(abp
, vp
, cdp
->cd_nameptr
, cdp
->cd_namelen
);
555 attrbufptr
= *abp
->ab_attrbufpp
;
556 varbufptr
= *abp
->ab_varbufpp
;
558 if (ATTR_CMN_DEVID
& attr
) {
559 *((dev_t
*)attrbufptr
) = hfsmp
->hfs_raw_dev
;
560 attrbufptr
= ((dev_t
*)attrbufptr
) + 1;
562 if (ATTR_CMN_FSID
& attr
) {
565 fsid
.val
[0] = (long)hfsmp
->hfs_raw_dev
;
566 fsid
.val
[1] = (long)vfs_typenum(mp
);
567 *((fsid_t
*)attrbufptr
) = fsid
;
568 attrbufptr
= ((fsid_t
*)attrbufptr
) + 1;
570 if (ATTR_CMN_OBJTYPE
& attr
) {
571 *((fsobj_type_t
*)attrbufptr
) = IFTOVT(cap
->ca_mode
);
572 attrbufptr
= ((fsobj_type_t
*)attrbufptr
) + 1;
574 if (ATTR_CMN_OBJTAG
& attr
) {
575 *((fsobj_tag_t
*)attrbufptr
) = VT_HFS
;
576 attrbufptr
= ((fsobj_tag_t
*)attrbufptr
) + 1;
579 * Exporting file IDs from HFS Plus:
581 * For "normal" files the c_fileid is the same value as the
582 * c_cnid. But for hard link files, they are different - the
583 * c_cnid belongs to the active directory entry (ie the link)
584 * and the c_fileid is for the actual inode (ie the data file).
586 * The stat call (getattr) will always return the c_fileid
587 * and Carbon APIs, which are hardlink-ignorant, will always
588 * receive the c_cnid (from getattrlist).
590 if (ATTR_CMN_OBJID
& attr
) {
591 ((fsobj_id_t
*)attrbufptr
)->fid_objno
= cdp
->cd_cnid
;
592 ((fsobj_id_t
*)attrbufptr
)->fid_generation
= 0;
593 attrbufptr
= ((fsobj_id_t
*)attrbufptr
) + 1;
595 if (ATTR_CMN_OBJPERMANENTID
& attr
) {
596 ((fsobj_id_t
*)attrbufptr
)->fid_objno
= cdp
->cd_cnid
;
597 ((fsobj_id_t
*)attrbufptr
)->fid_generation
= 0;
598 attrbufptr
= ((fsobj_id_t
*)attrbufptr
) + 1;
600 if (ATTR_CMN_PAROBJID
& attr
) {
601 ((fsobj_id_t
*)attrbufptr
)->fid_objno
= cdp
->cd_parentcnid
;
602 ((fsobj_id_t
*)attrbufptr
)->fid_generation
= 0;
603 attrbufptr
= ((fsobj_id_t
*)attrbufptr
) + 1;
605 if (ATTR_CMN_SCRIPT
& attr
) {
606 *((text_encoding_t
*)attrbufptr
) = cdp
->cd_encoding
;
607 attrbufptr
= ((text_encoding_t
*)attrbufptr
) + 1;
609 if (ATTR_CMN_CRTIME
& attr
) {
611 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_itime
;
612 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
613 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
616 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_itime
;
617 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
618 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
621 if (ATTR_CMN_MODTIME
& attr
) {
623 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_mtime
;
624 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
625 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
628 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_mtime
;
629 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
630 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
633 if (ATTR_CMN_CHGTIME
& attr
) {
635 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_ctime
;
636 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
637 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
640 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_ctime
;
641 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
642 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
645 if (ATTR_CMN_ACCTIME
& attr
) {
647 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_atime
;
648 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
649 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
652 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_atime
;
653 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
654 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
657 if (ATTR_CMN_BKUPTIME
& attr
) {
659 ((struct user64_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_btime
;
660 ((struct user64_timespec
*)attrbufptr
)->tv_nsec
= 0;
661 attrbufptr
= ((struct user64_timespec
*)attrbufptr
) + 1;
664 ((struct user32_timespec
*)attrbufptr
)->tv_sec
= cap
->ca_btime
;
665 ((struct user32_timespec
*)attrbufptr
)->tv_nsec
= 0;
666 attrbufptr
= ((struct user32_timespec
*)attrbufptr
) + 1;
669 if (ATTR_CMN_FNDRINFO
& attr
) {
670 u_int8_t
*finfo
= NULL
;
671 bcopy(&cap
->ca_finderinfo
, attrbufptr
, sizeof(u_int8_t
) * 32);
672 finfo
= (u_int8_t
*)attrbufptr
;
674 /* Don't expose a symlink's private type/creator. */
675 if (S_ISLNK(cap
->ca_mode
)) {
676 struct FndrFileInfo
*fip
;
678 fip
= (struct FndrFileInfo
*)attrbufptr
;
683 /* advance 16 bytes into the attrbuf */
685 if (S_ISREG(cap
->ca_mode
)) {
686 struct FndrExtendedFileInfo
*extinfo
= (struct FndrExtendedFileInfo
*)finfo
;
687 extinfo
->date_added
= 0;
689 else if (S_ISDIR(cap
->ca_mode
)) {
690 struct FndrExtendedDirInfo
*extinfo
= (struct FndrExtendedDirInfo
*)finfo
;
691 extinfo
->date_added
= 0;
694 attrbufptr
= (char *)attrbufptr
+ sizeof(u_int8_t
) * 32;
696 if (ATTR_CMN_OWNERID
& attr
) {
697 uid_t nuid
= cap
->ca_uid
;
700 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
)
702 else if (nuid
== UNKNOWNUID
)
706 *((uid_t
*)attrbufptr
) = nuid
;
707 attrbufptr
= ((uid_t
*)attrbufptr
) + 1;
709 if (ATTR_CMN_GRPID
& attr
) {
710 gid_t ngid
= cap
->ca_gid
;
713 gid_t cgid
= kauth_cred_getgid(vfs_context_ucred(ctx
));
714 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp
))) & MNT_UNKNOWNPERMISSIONS
)
716 else if (ngid
== UNKNOWNUID
)
720 *((gid_t
*)attrbufptr
) = ngid
;
721 attrbufptr
= ((gid_t
*)attrbufptr
) + 1;
723 if (ATTR_CMN_ACCESSMASK
& attr
) {
725 * [2856576] Since we are dynamically changing the owner, also
726 * effectively turn off the set-user-id and set-group-id bits,
727 * just like chmod(2) would when changing ownership. This prevents
728 * a security hole where set-user-id programs run as whoever is
729 * logged on (or root if nobody is logged in yet!)
731 *((u_int32_t
*)attrbufptr
) = (cap
->ca_uid
== UNKNOWNUID
) ?
732 cap
->ca_mode
& ~(S_ISUID
| S_ISGID
) : cap
->ca_mode
;
733 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
735 if (ATTR_CMN_FLAGS
& attr
) {
736 *((u_int32_t
*)attrbufptr
) = cap
->ca_flags
;
737 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
739 if (ATTR_CMN_USERACCESS
& attr
) {
740 u_int32_t user_access
;
742 /* Take the long path when we have an ACL */
743 if ((vp
!= NULLVP
) && (cap
->ca_recflags
& kHFSHasSecurityMask
)) {
744 user_access
= hfs_real_user_access(vp
, abp
->ab_context
);
746 user_access
= DerivePermissionSummary(cap
->ca_uid
, cap
->ca_gid
,
747 cap
->ca_mode
, mp
, proc_ucred(current_proc()), 0);
749 /* Also consider READ-ONLY file system. */
750 if (vfs_flags(mp
) & MNT_RDONLY
) {
751 user_access
&= ~W_OK
;
753 /* Locked objects are not writable either */
754 if ((cap
->ca_flags
& UF_IMMUTABLE
) && (vfs_context_suser(abp
->ab_context
) != 0))
755 user_access
&= ~W_OK
;
756 if ((cap
->ca_flags
& SF_IMMUTABLE
) && (vfs_context_suser(abp
->ab_context
) == 0))
757 user_access
&= ~W_OK
;
759 *((u_int32_t
*)attrbufptr
) = user_access
;
760 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
762 if (ATTR_CMN_FILEID
& attr
) {
763 *((u_int64_t
*)attrbufptr
) = cap
->ca_fileid
;
764 attrbufptr
= ((u_int64_t
*)attrbufptr
) + 1;
766 if (ATTR_CMN_PARENTID
& attr
) {
767 *((u_int64_t
*)attrbufptr
) = cdp
->cd_parentcnid
;
768 attrbufptr
= ((u_int64_t
*)attrbufptr
) + 1;
771 *abp
->ab_attrbufpp
= attrbufptr
;
772 *abp
->ab_varbufpp
= varbufptr
;
777 struct attrblock
*abp
,
778 struct hfsmount
*hfsmp
,
780 struct cat_desc
* descp
,
781 struct cat_attr
* cattrp
)
783 attrgroup_t attr
= abp
->ab_attrlist
->dirattr
;
784 void *attrbufptr
= *abp
->ab_attrbufpp
;
788 * The DIR_LINKCOUNT is the count of real directory hard links.
789 * (i.e. its not the sum of the implied "." and ".." references
790 * typically used in stat's st_nlink field)
792 if (ATTR_DIR_LINKCOUNT
& attr
) {
793 *((u_int32_t
*)attrbufptr
) = cattrp
->ca_linkcount
;
794 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
796 if (ATTR_DIR_ENTRYCOUNT
& attr
) {
797 entries
= cattrp
->ca_entries
;
799 if (descp
->cd_parentcnid
== kHFSRootParentID
) {
800 if (hfsmp
->hfs_private_desc
[FILE_HARDLINKS
].cd_cnid
!= 0)
801 --entries
; /* hide private dir */
802 if (hfsmp
->hfs_private_desc
[DIR_HARDLINKS
].cd_cnid
!= 0)
803 --entries
; /* hide private dir */
805 ((hfsmp
->vcbAtrb
& kHFSVolumeJournaledMask
) &&
806 (hfsmp
->hfs_flags
& HFS_READ_ONLY
)))
807 entries
-= 2; /* hide the journal files */
810 *((u_int32_t
*)attrbufptr
) = entries
;
811 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
813 if (ATTR_DIR_MOUNTSTATUS
& attr
) {
814 if (vp
!= NULL
&& vnode_mountedhere(vp
) != NULL
)
815 *((u_int32_t
*)attrbufptr
) = DIR_MNTSTATUS_MNTPOINT
;
817 *((u_int32_t
*)attrbufptr
) = 0;
818 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
820 *abp
->ab_attrbufpp
= attrbufptr
;
825 struct attrblock
*abp
,
826 struct hfsmount
*hfsmp
,
827 struct cat_attr
*cattrp
,
828 struct cat_fork
*datafork
,
829 struct cat_fork
*rsrcfork
,
835 attrgroup_t attr
= abp
->ab_attrlist
->fileattr
;
836 void *attrbufptr
= *abp
->ab_attrbufpp
;
837 void *varbufptr
= *abp
->ab_varbufpp
;
838 u_int32_t allocblksize
;
840 allocblksize
= HFSTOVCB(hfsmp
)->blockSize
;
842 off_t datasize
= datafork
->cf_size
;
843 off_t totalsize
= datasize
+ rsrcfork
->cf_size
;
845 int handle_compressed
;
846 handle_compressed
= (cattrp
->ca_flags
& UF_COMPRESSED
);// && hfs_file_is_compressed(VTOC(vp), 1);
848 if (handle_compressed
) {
849 if (attr
& (ATTR_FILE_DATALENGTH
|ATTR_FILE_TOTALSIZE
)) {
850 if ( 0 == hfs_uncompressed_size_of_compressed_file(hfsmp
, vp
, cattrp
->ca_fileid
, &datasize
, 1) ) { /* 1 == don't take the cnode lock */
851 /* total size of a compressed file is just the data size */
852 totalsize
= datasize
;
858 if (ATTR_FILE_LINKCOUNT
& attr
) {
859 *((u_int32_t
*)attrbufptr
) = cattrp
->ca_linkcount
;
860 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
862 if (ATTR_FILE_TOTALSIZE
& attr
) {
863 *((off_t
*)attrbufptr
) = totalsize
;
864 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
866 if (ATTR_FILE_ALLOCSIZE
& attr
) {
867 *((off_t
*)attrbufptr
) =
868 (off_t
)cattrp
->ca_blocks
* (off_t
)allocblksize
;
869 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
871 if (ATTR_FILE_IOBLOCKSIZE
& attr
) {
872 *((u_int32_t
*)attrbufptr
) = hfsmp
->hfs_logBlockSize
;
873 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
875 if (ATTR_FILE_CLUMPSIZE
& attr
) {
876 *((u_int32_t
*)attrbufptr
) = hfsmp
->vcbClpSiz
;
877 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
879 if (ATTR_FILE_DEVTYPE
& attr
) {
880 if (S_ISBLK(cattrp
->ca_mode
) || S_ISCHR(cattrp
->ca_mode
))
881 *((u_int32_t
*)attrbufptr
) = (u_int32_t
)cattrp
->ca_rdev
;
883 *((u_int32_t
*)attrbufptr
) = 0;
884 attrbufptr
= ((u_int32_t
*)attrbufptr
) + 1;
887 if (ATTR_FILE_DATALENGTH
& attr
) {
888 *((off_t
*)attrbufptr
) = datasize
;
889 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
893 /* fake the data fork size on a decmpfs compressed file to reflect the
894 * uncompressed size. This ensures proper reading and copying of these files.
895 * NOTE: we may need to get the vnode here because the vnode parameter
896 * passed by hfs_vnop_readdirattr() may be null.
899 if ( handle_compressed
) {
900 if (attr
& ATTR_FILE_DATAALLOCSIZE
) {
901 *((off_t
*)attrbufptr
) = (off_t
)rsrcfork
->cf_blocks
* (off_t
)allocblksize
;
902 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
904 if (attr
& ATTR_FILE_RSRCLENGTH
) {
905 *((off_t
*)attrbufptr
) = 0;
906 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
908 if (attr
& ATTR_FILE_RSRCALLOCSIZE
) {
909 *((off_t
*)attrbufptr
) = 0;
910 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
916 if (ATTR_FILE_DATAALLOCSIZE
& attr
) {
917 *((off_t
*)attrbufptr
) = (off_t
)datafork
->cf_blocks
* (off_t
)allocblksize
;
918 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
920 if (ATTR_FILE_RSRCLENGTH
& attr
) {
921 *((off_t
*)attrbufptr
) = rsrcfork
->cf_size
;
922 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
924 if (ATTR_FILE_RSRCALLOCSIZE
& attr
) {
925 *((off_t
*)attrbufptr
) = (off_t
)rsrcfork
->cf_blocks
* (off_t
)allocblksize
;
926 attrbufptr
= ((off_t
*)attrbufptr
) + 1;
929 *abp
->ab_attrbufpp
= attrbufptr
;
930 *abp
->ab_varbufpp
= varbufptr
;
934 * Calculate the total size of an attribute block.
938 hfs_attrblksize(struct attrlist
*attrlist
)
943 boolean_t is_64_bit
= proc_is64bit(current_proc());
946 sizeof_timespec
= sizeof(struct user64_timespec
);
948 sizeof_timespec
= sizeof(struct user32_timespec
);
950 DBG_ASSERT((attrlist
->commonattr
& ~ATTR_CMN_VALIDMASK
) == 0);
952 DBG_ASSERT((attrlist
->volattr
& ~ATTR_VOL_VALIDMASK
) == 0);
954 DBG_ASSERT((attrlist
->dirattr
& ~ATTR_DIR_VALIDMASK
) == 0);
956 DBG_ASSERT((attrlist
->fileattr
& ~ATTR_FILE_VALIDMASK
) == 0);
958 DBG_ASSERT((attrlist
->forkattr
& ~ATTR_FORK_VALIDMASK
) == 0);
962 if ((a
= attrlist
->commonattr
) != 0) {
963 if (a
& ATTR_CMN_NAME
) size
+= sizeof(struct attrreference
);
964 if (a
& ATTR_CMN_DEVID
) size
+= sizeof(dev_t
);
965 if (a
& ATTR_CMN_FSID
) size
+= sizeof(fsid_t
);
966 if (a
& ATTR_CMN_OBJTYPE
) size
+= sizeof(fsobj_type_t
);
967 if (a
& ATTR_CMN_OBJTAG
) size
+= sizeof(fsobj_tag_t
);
968 if (a
& ATTR_CMN_OBJID
) size
+= sizeof(fsobj_id_t
);
969 if (a
& ATTR_CMN_OBJPERMANENTID
) size
+= sizeof(fsobj_id_t
);
970 if (a
& ATTR_CMN_PAROBJID
) size
+= sizeof(fsobj_id_t
);
971 if (a
& ATTR_CMN_SCRIPT
) size
+= sizeof(text_encoding_t
);
972 if (a
& ATTR_CMN_CRTIME
) size
+= sizeof_timespec
;
973 if (a
& ATTR_CMN_MODTIME
) size
+= sizeof_timespec
;
974 if (a
& ATTR_CMN_CHGTIME
) size
+= sizeof_timespec
;
975 if (a
& ATTR_CMN_ACCTIME
) size
+= sizeof_timespec
;
976 if (a
& ATTR_CMN_BKUPTIME
) size
+= sizeof_timespec
;
977 if (a
& ATTR_CMN_FNDRINFO
) size
+= 32 * sizeof(u_int8_t
);
978 if (a
& ATTR_CMN_OWNERID
) size
+= sizeof(uid_t
);
979 if (a
& ATTR_CMN_GRPID
) size
+= sizeof(gid_t
);
980 if (a
& ATTR_CMN_ACCESSMASK
) size
+= sizeof(u_int32_t
);
981 if (a
& ATTR_CMN_FLAGS
) size
+= sizeof(u_int32_t
);
982 if (a
& ATTR_CMN_USERACCESS
) size
+= sizeof(u_int32_t
);
983 if (a
& ATTR_CMN_FILEID
) size
+= sizeof(u_int64_t
);
984 if (a
& ATTR_CMN_PARENTID
) size
+= sizeof(u_int64_t
);
986 if ((a
= attrlist
->dirattr
) != 0) {
987 if (a
& ATTR_DIR_LINKCOUNT
) size
+= sizeof(u_int32_t
);
988 if (a
& ATTR_DIR_ENTRYCOUNT
) size
+= sizeof(u_int32_t
);
989 if (a
& ATTR_DIR_MOUNTSTATUS
) size
+= sizeof(u_int32_t
);
991 if ((a
= attrlist
->fileattr
) != 0) {
992 if (a
& ATTR_FILE_LINKCOUNT
) size
+= sizeof(u_int32_t
);
993 if (a
& ATTR_FILE_TOTALSIZE
) size
+= sizeof(off_t
);
994 if (a
& ATTR_FILE_ALLOCSIZE
) size
+= sizeof(off_t
);
995 if (a
& ATTR_FILE_IOBLOCKSIZE
) size
+= sizeof(u_int32_t
);
996 if (a
& ATTR_FILE_CLUMPSIZE
) size
+= sizeof(u_int32_t
);
997 if (a
& ATTR_FILE_DEVTYPE
) size
+= sizeof(u_int32_t
);
998 if (a
& ATTR_FILE_DATALENGTH
) size
+= sizeof(off_t
);
999 if (a
& ATTR_FILE_DATAALLOCSIZE
) size
+= sizeof(off_t
);
1000 if (a
& ATTR_FILE_RSRCLENGTH
) size
+= sizeof(off_t
);
1001 if (a
& ATTR_FILE_RSRCALLOCSIZE
) size
+= sizeof(off_t
);
1007 #define KAUTH_DIR_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | \
1008 KAUTH_VNODE_ADD_SUBDIRECTORY | \
1009 KAUTH_VNODE_DELETE_CHILD)
1011 #define KAUTH_DIR_READ_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY)
1013 #define KAUTH_DIR_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH)
1015 #define KAUTH_FILE_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA)
1017 #define KAUTH_FILE_READRIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA)
1019 #define KAUTH_FILE_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE)
1023 * Compute the same [expensive] user_access value as getattrlist does
1026 hfs_real_user_access(vnode_t vp
, vfs_context_t ctx
)
1028 u_int32_t user_access
= 0;
1030 if (vnode_isdir(vp
)) {
1031 if (vnode_authorize(vp
, NULLVP
, KAUTH_DIR_WRITE_RIGHTS
, ctx
) == 0)
1032 user_access
|= W_OK
;
1033 if (vnode_authorize(vp
, NULLVP
, KAUTH_DIR_READ_RIGHTS
, ctx
) == 0)
1034 user_access
|= R_OK
;
1035 if (vnode_authorize(vp
, NULLVP
, KAUTH_DIR_EXECUTE_RIGHTS
, ctx
) == 0)
1036 user_access
|= X_OK
;
1038 if (vnode_authorize(vp
, NULLVP
, KAUTH_FILE_WRITE_RIGHTS
, ctx
) == 0)
1039 user_access
|= W_OK
;
1040 if (vnode_authorize(vp
, NULLVP
, KAUTH_FILE_READRIGHTS
, ctx
) == 0)
1041 user_access
|= R_OK
;
1042 if (vnode_authorize(vp
, NULLVP
, KAUTH_FILE_EXECUTE_RIGHTS
, ctx
) == 0)
1043 user_access
|= X_OK
;
1045 return (user_access
);
1050 DerivePermissionSummary(uid_t obj_uid
, gid_t obj_gid
, mode_t obj_mode
,
1051 struct mount
*mp
, kauth_cred_t cred
, __unused
struct proc
*p
)
1053 u_int32_t permissions
;
1055 if (obj_uid
== UNKNOWNUID
)
1056 obj_uid
= kauth_cred_getuid(cred
);
1058 /* User id 0 (root) always gets access. */
1059 if (!suser(cred
, NULL
)) {
1060 permissions
= R_OK
| W_OK
| X_OK
;
1064 /* Otherwise, check the owner. */
1065 if (hfs_owner_rights(VFSTOHFS(mp
), obj_uid
, cred
, NULL
, false) == 0) {
1066 permissions
= ((u_int32_t
)obj_mode
& S_IRWXU
) >> 6;
1070 /* Otherwise, check the groups. */
1071 if (! (((unsigned int)vfs_flags(mp
)) & MNT_UNKNOWNPERMISSIONS
)) {
1074 if (kauth_cred_ismember_gid(cred
, obj_gid
, &is_member
) == 0 && is_member
) {
1075 permissions
= ((u_int32_t
)obj_mode
& S_IRWXG
) >> 3;
1080 /* Otherwise, settle for 'others' access. */
1081 permissions
= (u_int32_t
)obj_mode
& S_IRWXO
;
1084 return (permissions
);