]> git.saurik.com Git - apple/hfs.git/blob - core/hfs_attrlist.c
hfs-407.30.1.tar.gz
[apple/hfs.git] / core / hfs_attrlist.c
1 /*
2 * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /*
30 * hfs_attrlist.c - HFS attribute list processing
31 *
32 * Copyright (c) 1998-2002, Apple Inc. All Rights Reserved.
33 */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/attr.h>
40 #include <sys/stat.h>
41 #include <sys/unistd.h>
42 #include <sys/vm.h>
43 #include <sys/kauth.h>
44 #include <sys/fsctl.h>
45
46 #include <kern/locks.h>
47
48 #include "hfs.h"
49 #include "hfs_cnode.h"
50 #include "hfs_mount.h"
51 #include "hfs_dbg.h"
52 #include "hfs_attrlist.h"
53 #include "hfs_btreeio.h"
54 #include "hfs_cprotect.h"
55
56 /* Packing routines: */
57
58 static void packnameattr(struct attrblock *abp, struct vnode *vp,
59 const u_int8_t *name, int namelen);
60
61 static void packcommonattr(struct attrblock *abp, struct hfsmount *hfsmp,
62 struct vnode *vp, struct cat_desc * cdp,
63 struct cat_attr * cap, struct vfs_context *ctx);
64
65 static void packfileattr(struct attrblock *abp, struct hfsmount *hfsmp,
66 struct cat_attr *cattrp, struct cat_fork *datafork,
67 struct cat_fork *rsrcfork, struct vnode *vp);
68
69 static void packdirattr(struct attrblock *abp, struct hfsmount *hfsmp,
70 struct vnode *vp, struct cat_desc * descp,
71 struct cat_attr * cattrp);
72
73 static u_int32_t hfs_real_user_access(vnode_t vp, vfs_context_t ctx);
74
75 static void get_vattr_data_for_attrs(struct attrlist *, struct vnode_attr *,
76 struct hfsmount *, struct vnode *, struct cat_desc *, struct cat_attr *,
77 struct cat_fork *, struct cat_fork *, vfs_context_t);
78
79 static void vattr_data_for_common_attrs(struct attrlist *, struct vnode_attr *,
80 struct hfsmount *, struct vnode *, struct cat_desc *, struct cat_attr *,
81 vfs_context_t);
82
83 static void vattr_data_for_dir_attrs(struct attrlist *, struct vnode_attr *,
84 struct hfsmount *, struct vnode *, struct cat_desc *, struct cat_attr *);
85
86 static void vattr_data_for_file_attrs(struct attrlist *, struct vnode_attr *,
87 struct hfsmount *, struct cat_attr *, struct cat_fork *, struct cat_fork *,
88 struct vnode *vp);
89
90 static int hfs_readdirattr_internal(struct vnode *, struct attrlist *,
91 struct vnode_attr *, uio_t, uint64_t, int, uint32_t *, int *, int *,
92 vfs_context_t);
93
94 /*
95 * readdirattr operation will return attributes for the items in the
96 * directory specified.
97 *
98 * It does not do . and .. entries. The problem is if you are at the root of the
99 * hfs directory and go to .. you could be crossing a mountpoint into a
100 * different (ufs) file system. The attributes that apply for it may not
101 * apply for the file system you are doing the readdirattr on. To make life
102 * simpler, this call will only return entries in its directory, hfs like.
103 */
104 int
105 hfs_vnop_readdirattr(ap)
106 struct vnop_readdirattr_args /* {
107 struct vnode *a_vp;
108 struct attrlist *a_alist;
109 struct uio *a_uio;
110 u_long a_maxcount;
111 u_long a_options;
112 u_long *a_newstate;
113 int *a_eofflag;
114 u_long *a_actualcount;
115 vfs_context_t a_context;
116 } */ *ap;
117 {
118 int error;
119 struct attrlist *alist = ap->a_alist;
120
121 /* Check for invalid options and buffer space. */
122 if (((ap->a_options & ~(FSOPT_NOINMEMUPDATE | FSOPT_NOFOLLOW)) != 0) ||
123 (ap->a_maxcount <= 0)) {
124 return (EINVAL);
125 }
126 /*
127 * Reject requests for unsupported attributes.
128 */
129 if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
130 (alist->commonattr & ~HFS_ATTR_CMN_VALID) ||
131 (alist->volattr != 0) ||
132 (alist->dirattr & ~HFS_ATTR_DIR_VALID) ||
133 (alist->fileattr & ~HFS_ATTR_FILE_VALID) ||
134 (alist->forkattr != 0)) {
135 return (EINVAL);
136 }
137
138 error = hfs_readdirattr_internal(ap->a_vp, alist, NULL, ap->a_uio,
139 (uint64_t)ap->a_options, ap->a_maxcount, ap->a_newstate,
140 ap->a_eofflag, (int *)ap->a_actualcount, ap->a_context);
141
142 return (error);
143 }
144
145
146 /*
147 * getattrlistbulk, like readdirattr, will return attributes for the items in
148 * the directory specified.
149 *
150 * It does not do . and .. entries. The problem is if you are at the root of the
151 * hfs directory and go to .. you could be crossing a mountpoint into a
152 * different (ufs) file system. The attributes that apply for it may not
153 * apply for the file system you are doing the readdirattr on. To make life
154 * simpler, this call will only return entries in its directory, hfs like.
155 */
156 int
157 hfs_vnop_getattrlistbulk(ap)
158 struct vnop_getattrlistbulk_args /* {
159 struct vnodeop_desc *a_desc;
160 vnode_t a_vp;
161 struct attrlist *a_alist;
162 struct vnode_attr *a_vap;
163 struct uio *a_uio;
164 void *a_private;
165 uint64_t a_options;
166 int32_t *a_eofflag;
167 int32_t *a_actualcount;
168 vfs_context_t a_context;
169 } */ *ap;
170 {
171 int error = 0;
172
173 error = hfs_readdirattr_internal(ap->a_vp, ap->a_alist, ap->a_vap,
174 ap->a_uio, (uint64_t)ap->a_options, 0, NULL, ap->a_eofflag,
175 (int *)ap->a_actualcount, ap->a_context);
176
177 return (error);
178 }
179
180 /*
181 * Common function for both hfs_vnop_readdirattr and hfs_vnop_getattrlistbulk.
182 * This either fills in a vnode_attr structure or fills in an attrbute buffer
183 * Currently the difference in behaviour required for the two vnops is keyed
184 * on whether the passed in vnode_attr pointer is null or not. If the pointer
185 * is null we fill in buffer passed and if it is not null we fill in the fields
186 * of the vnode_attr structure.
187 */
188 int
189 hfs_readdirattr_internal(struct vnode *dvp, struct attrlist *alist,
190 struct vnode_attr *vap, uio_t uio, uint64_t options, int maxcount,
191 uint32_t *newstate, int *eofflag, int *actualcount, vfs_context_t ctx)
192 {
193 struct cnode *dcp;
194 struct hfsmount * hfsmp;
195 u_int32_t fixedblocksize;
196 u_int32_t maxattrblocksize = 0;
197 u_int32_t currattrbufsize;
198 void *attrbufptr = NULL;
199 void *attrptr = NULL;
200 void *varptr = NULL;
201 caddr_t namebuf = NULL;
202 struct attrblock attrblk;
203 int error = 0;
204 int index = 0;
205 int i = 0;
206 struct cat_desc *lastdescp = NULL;
207 struct cat_entrylist *ce_list = NULL;
208 directoryhint_t *dirhint = NULL;
209 unsigned int tag;
210 int maxentries = 0;
211 int lockflags;
212 u_int32_t dirchg = 0;
213 int reachedeof = 0;
214 int internal_actualcount;
215 int internal_eofflag;
216
217 /* Lets makse sure we have something assign to actualcount always, min change required */
218 if (actualcount == NULL) {
219 actualcount = &internal_actualcount;
220 }
221 /* Lets makse sure we have something assign to eofflag always, min change required */
222 if (eofflag == NULL) {
223 eofflag = &internal_eofflag;
224 }
225
226 *(actualcount) = 0;
227 *(eofflag) = 0;
228
229 if ((uio_resid(uio) <= 0) || (uio_iovcnt(uio) > 1))
230 return (EINVAL);
231
232 if (VTOC(dvp)->c_bsdflags & UF_COMPRESSED) {
233 int compressed = hfs_file_is_compressed(VTOC(dvp), 0); /* 0 == take the cnode lock */
234
235 if (!compressed) {
236 error = check_for_dataless_file(dvp, NAMESPACE_HANDLER_READ_OP);
237 if (error) {
238 return error;
239 }
240 }
241 }
242
243 /*
244 * Take an exclusive directory lock since we manipulate the directory hints
245 */
246 if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
247 return (error);
248 }
249 dcp = VTOC(dvp);
250 hfsmp = VTOHFS(dvp);
251
252 dirchg = dcp->c_dirchangecnt;
253
254 /* Extract directory index and tag (sequence number) from uio_offset */
255 index = uio_offset(uio) & HFS_INDEX_MASK;
256 tag = uio_offset(uio) & ~HFS_INDEX_MASK;
257
258 /*
259 * We can't just use the valence as an optimization to avoid
260 * going to the catalog. It might be wrong (== 0), and that would
261 * cause us to avoid iterating the directory when it might actually have
262 * contents. Instead, use the catalog to tell us when we've hit EOF
263 * for this directory
264 */
265
266 /* Get a buffer to hold packed attributes. */
267 fixedblocksize = (sizeof(u_int32_t) + hfs_attrblksize(alist)); /* 4 bytes for length */
268
269 if (!vap) {
270 maxattrblocksize = fixedblocksize;
271 if (alist->commonattr & ATTR_CMN_NAME)
272 maxattrblocksize += kHFSPlusMaxFileNameBytes + 1;
273
274 attrbufptr = hfs_malloc(maxattrblocksize);
275 attrptr = attrbufptr;
276 varptr = (char *)attrbufptr + fixedblocksize; /* Point to variable-length storage */
277 } else {
278 if ((alist->commonattr & ATTR_CMN_NAME) && !vap->va_name) {
279 namebuf = hfs_malloc(MAXPATHLEN);
280 if (!namebuf) {
281 error = ENOMEM;
282 goto exit2;
283 }
284 vap->va_name = namebuf;
285 }
286 }
287 /* Get a detached directory hint (cnode must be locked exclusive) */
288 dirhint = hfs_getdirhint(dcp, ((index - 1) & HFS_INDEX_MASK) | tag, TRUE);
289
290 /* Hide tag from catalog layer. */
291 dirhint->dh_index &= HFS_INDEX_MASK;
292 if (dirhint->dh_index == HFS_INDEX_MASK) {
293 dirhint->dh_index = -1;
294 }
295
296 /*
297 * Obtain a list of catalog entries and pack their attributes until
298 * the output buffer is full or maxcount entries have been packed.
299 */
300
301 /*
302 * Constrain our list size.
303 */
304 maxentries = uio_resid(uio) / (fixedblocksize + HFS_AVERAGE_NAME_SIZE);
305 /* There is maxcount for the bulk vnop */
306 if (!vap)
307 maxentries = min(maxentries, maxcount);
308 maxentries = min(maxentries, MAXCATENTRIES);
309 if (maxentries < 1) {
310 error = EINVAL;
311 goto exit2;
312 }
313
314 /* Initialize a catalog entry list. */
315 ce_list = hfs_mallocz(CE_LIST_SIZE(maxentries));
316 ce_list->maxentries = maxentries;
317
318 /*
319 * Populate the ce_list from the catalog file.
320 */
321 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
322
323 error = cat_getentriesattr(hfsmp, dirhint, ce_list, &reachedeof);
324 /* Don't forget to release the descriptors later! */
325
326 hfs_systemfile_unlock(hfsmp, lockflags);
327
328 if ((error == ENOENT) || (reachedeof != 0)) {
329 *(eofflag) = TRUE;
330 error = 0;
331 }
332 if (error) {
333 goto exit1;
334 }
335
336 dcp->c_touch_acctime = TRUE;
337
338 /*
339 * Check for a FS corruption in the valence. We're holding the cnode lock
340 * exclusive since we need to serialize the directory hints, so if we found
341 * that the valence reported 0, but we actually found some items here, then
342 * silently minimally self-heal and bump the valence to 1.
343 */
344 if ((dcp->c_entries == 0) && (ce_list->realentries > 0)) {
345 dcp->c_entries++;
346 dcp->c_flag |= C_MODIFIED;
347 printf("%s : repairing valence to non-zero!\n", __FUNCTION__);
348 /* force an update on dcp while we're still holding the lock. */
349 hfs_update(dvp, 0);
350 }
351
352 /*
353 * Drop the directory lock so we don't deadlock when we:
354 * - acquire a child cnode lock
355 * - make calls to vnode_authorize()
356 * - make calls to kauth_cred_ismember_gid()
357 */
358 hfs_unlock(dcp);
359 dcp = NULL;
360
361 /* Process the catalog entries. */
362 for (i = 0; i < (int)ce_list->realentries; ++i) {
363 struct cnode *cp = NULL;
364 struct vnode *vp = NULL;
365 struct cat_desc * cdescp;
366 struct cat_attr * cattrp;
367 struct cat_fork c_datafork;
368 struct cat_fork c_rsrcfork;
369
370 bzero(&c_datafork, sizeof(c_datafork));
371 bzero(&c_rsrcfork, sizeof(c_rsrcfork));
372 cdescp = &ce_list->entry[i].ce_desc;
373 cattrp = &ce_list->entry[i].ce_attr;
374 c_datafork.cf_size = ce_list->entry[i].ce_datasize;
375 c_datafork.cf_blocks = ce_list->entry[i].ce_datablks;
376 c_rsrcfork.cf_size = ce_list->entry[i].ce_rsrcsize;
377 c_rsrcfork.cf_blocks = ce_list->entry[i].ce_rsrcblks;
378
379 if (((alist->commonattr & ATTR_CMN_USERACCESS) &&
380 (cattrp->ca_recflags & kHFSHasSecurityMask))
381 #if CONFIG_PROTECT
382 ||
383 ((alist->commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) && (vap))
384 #endif
385 ) {
386 /*
387 * Obtain vnode for our vnode_authorize() calls.
388 */
389 if (hfs_vget(hfsmp, cattrp->ca_fileid, &vp, 0, 0) != 0) {
390 vp = NULL;
391 }
392 } else if (vap || !(options & FSOPT_NOINMEMUPDATE)) {
393 /* Get in-memory cnode data (if any). */
394 vp = hfs_chash_getvnode(hfsmp, cattrp->ca_fileid, 0, 0, 0);
395 }
396 if (vp != NULL) {
397 cp = VTOC(vp);
398 /* Only use cnode's decriptor for non-hardlinks */
399 if (!(cp->c_flag & C_HARDLINK))
400 cdescp = &cp->c_desc;
401 cattrp = &cp->c_attr;
402 if (cp->c_datafork) {
403 c_datafork.cf_size = cp->c_datafork->ff_size;
404 c_datafork.cf_blocks = cp->c_datafork->ff_blocks;
405 }
406 if (cp->c_rsrcfork) {
407 c_rsrcfork.cf_size = cp->c_rsrcfork->ff_size;
408 c_rsrcfork.cf_blocks = cp->c_rsrcfork->ff_blocks;
409 }
410 /* All done with cnode. */
411 hfs_unlock(cp);
412 cp = NULL;
413 }
414
415 if (!vap) {
416 *((u_int32_t *)attrptr) = 0;
417 attrptr = ((u_int32_t *)attrptr) + 1;
418 attrblk.ab_attrlist = alist;
419 attrblk.ab_attrbufpp = &attrptr;
420 attrblk.ab_varbufpp = &varptr;
421 attrblk.ab_flags = 0;
422 attrblk.ab_blocksize = maxattrblocksize;
423 attrblk.ab_context = ctx;
424
425 /* Pack catalog entries into attribute buffer. */
426 hfs_packattrblk(&attrblk, hfsmp, vp, cdescp, cattrp, &c_datafork, &c_rsrcfork, ctx);
427 currattrbufsize = ((char *)varptr - (char *)attrbufptr);
428
429 /* All done with vnode. */
430 if (vp != NULL) {
431 vnode_put(vp);
432 vp = NULL;
433 }
434
435 /* Make sure there's enough buffer space remaining. */
436 // LP64todo - fix this!
437 if (uio_resid(uio) < 0 ||
438 currattrbufsize > (u_int32_t)uio_resid(uio)) {
439 break;
440 } else {
441 *((u_int32_t *)attrbufptr) = currattrbufsize;
442 error = uiomove((caddr_t)attrbufptr, currattrbufsize, uio);
443 if (error != E_NONE) {
444 break;
445 }
446 attrptr = attrbufptr;
447 /* Point to variable-length storage */
448 varptr = (char *)attrbufptr + fixedblocksize;
449 /* Save the last valid catalog entry */
450 lastdescp = &ce_list->entry[i].ce_desc;
451 index++;
452 *actualcount += 1;
453
454 /* Termination checks */
455 if ((--maxcount <= 0) ||
456 // LP64todo - fix this!
457 uio_resid(uio) < 0 ||
458 ((u_int32_t)uio_resid(uio) < (fixedblocksize + HFS_AVERAGE_NAME_SIZE))){
459 break;
460 }
461 }
462 } else {
463 size_t orig_resid = (size_t)uio_resid(uio);
464 size_t resid;
465
466 get_vattr_data_for_attrs(alist, vap, hfsmp, vp, cdescp,
467 cattrp, &c_datafork, &c_rsrcfork, ctx);
468
469 #if CONFIG_PROTECT
470 if ((alist->commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) &&
471 vp) {
472 cp_key_class_t class;
473
474 if (!cp_vnode_getclass(vp, &class)) {
475 VATTR_RETURN(vap, va_dataprotect_class,
476 (uint32_t)class);
477 }
478 }
479 #endif
480 error = vfs_attr_pack(vp, uio, alist, options, vap,
481 NULL, ctx);
482
483 /* All done with vnode. */
484 if (vp) {
485 vnode_put(vp);
486 vp = NULL;
487 }
488
489 resid = uio_resid(uio);
490
491 /* Was this entry succesful ? */
492 if (error || resid == orig_resid)
493 break;
494
495 /* Save the last valid catalog entry */
496 lastdescp = &ce_list->entry[i].ce_desc;
497 index++;
498 *actualcount += 1;
499
500 /* Do we have the bare minimum for the next entry ? */
501 if (resid < sizeof(uint32_t))
502 break;
503 }
504 } /* for each catalog entry */
505
506 /*
507 * If we couldn't fit all the entries requested in the user's buffer,
508 * it's not EOF.
509 */
510 if (*eofflag && (*actualcount < (int)ce_list->realentries))
511 *eofflag = 0;
512
513 /* If we skipped catalog entries for reserved files that should
514 * not be listed in namespace, update the index accordingly.
515 */
516 if (ce_list->skipentries) {
517 index += ce_list->skipentries;
518 ce_list->skipentries = 0;
519 }
520
521 /*
522 * If there are more entries then save the last name.
523 * Key this behavior based on whether or not we observed EOFFLAG.
524 *
525 * Do not use the valence as a way to determine if we hit EOF, since
526 * it can be wrong. Use the catalog's output only.
527 */
528 if ((*(eofflag) == 0) && (lastdescp != NULL)) {
529
530 /* Remember last entry */
531 if ((dirhint->dh_desc.cd_flags & CD_HASBUF) &&
532 (dirhint->dh_desc.cd_nameptr != NULL)) {
533 dirhint->dh_desc.cd_flags &= ~CD_HASBUF;
534 vfs_removename((const char *)dirhint->dh_desc.cd_nameptr);
535 }
536 if (lastdescp->cd_nameptr != NULL) {
537 dirhint->dh_desc.cd_namelen = lastdescp->cd_namelen;
538 dirhint->dh_desc.cd_nameptr = (const u_int8_t *)
539 vfs_addname((const char *)lastdescp->cd_nameptr, lastdescp->cd_namelen, 0, 0);
540 dirhint->dh_desc.cd_flags |= CD_HASBUF;
541 } else {
542 dirhint->dh_desc.cd_namelen = 0;
543 dirhint->dh_desc.cd_nameptr = NULL;
544 }
545 dirhint->dh_index = index - 1;
546 dirhint->dh_desc.cd_cnid = lastdescp->cd_cnid;
547 dirhint->dh_desc.cd_hint = lastdescp->cd_hint;
548 dirhint->dh_desc.cd_encoding = lastdescp->cd_encoding;
549 }
550
551 /* All done with the catalog descriptors. */
552 for (i = 0; i < (int)ce_list->realentries; ++i)
553 cat_releasedesc(&ce_list->entry[i].ce_desc);
554 ce_list->realentries = 0;
555
556 (void) hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
557 dcp = VTOC(dvp);
558
559 exit1:
560 /* Pack directory index and tag into uio_offset. */
561 while (tag == 0) tag = (++dcp->c_dirhinttag) << HFS_INDEX_BITS;
562 uio_setoffset(uio, index | tag);
563 dirhint->dh_index |= tag;
564
565 exit2:
566 if (newstate)
567 *newstate = dirchg;
568
569 /*
570 * Drop directory hint on error or if there are no more entries,
571 * only if EOF was seen.
572 */
573 if (dirhint) {
574 if ((error != 0) || *(eofflag))
575 hfs_reldirhint(dcp, dirhint);
576 else
577 hfs_insertdirhint(dcp, dirhint);
578 }
579 if (namebuf) {
580 hfs_free(namebuf, MAXPATHLEN);
581 vap->va_name = NULL;
582 }
583 if (attrbufptr)
584 hfs_free(attrbufptr, maxattrblocksize);
585 if (ce_list)
586 hfs_free(ce_list, CE_LIST_SIZE(maxentries));
587
588 if (vap && *actualcount && error)
589 error = 0;
590
591 hfs_unlock(dcp);
592 return (error);
593 }
594
595
596 /*==================== Attribute list support routines ====================*/
597
598 /*
599 * Pack cnode attributes into an attribute block.
600 */
601 void
602 hfs_packattrblk(struct attrblock *abp,
603 struct hfsmount *hfsmp,
604 struct vnode *vp,
605 struct cat_desc *descp,
606 struct cat_attr *attrp,
607 struct cat_fork *datafork,
608 struct cat_fork *rsrcfork,
609 struct vfs_context *ctx)
610 {
611 struct attrlist *attrlistp = abp->ab_attrlist;
612
613 if (attrlistp->commonattr)
614 packcommonattr(abp, hfsmp, vp, descp, attrp, ctx);
615
616 if (attrlistp->dirattr && S_ISDIR(attrp->ca_mode))
617 packdirattr(abp, hfsmp, vp, descp,attrp);
618
619 if (attrlistp->fileattr && !S_ISDIR(attrp->ca_mode))
620 packfileattr(abp, hfsmp, attrp, datafork, rsrcfork, vp);
621 }
622
623 static char*
624 mountpointname(struct mount *mp)
625 {
626 struct vfsstatfs *vsfs = vfs_statfs(mp);
627
628 size_t namelength = strlen(vsfs->f_mntonname);
629 int foundchars = 0;
630 char *c;
631
632 if (namelength == 0)
633 return (NULL);
634
635 /*
636 * Look backwards through the name string, looking for
637 * the first slash encountered (which must precede the
638 * last part of the pathname).
639 */
640 for (c = vsfs->f_mntonname + namelength - 1;
641 namelength > 0; --c, --namelength) {
642 if (*c != '/') {
643 foundchars = 1;
644 } else if (foundchars) {
645 return (c + 1);
646 }
647 }
648
649 return vsfs->f_mntonname;
650 }
651
652
653 static void
654 packnameattr(
655 struct attrblock *abp,
656 struct vnode *vp,
657 const u_int8_t *name,
658 int namelen)
659 {
660 void *varbufptr;
661 struct attrreference * attr_refptr;
662 char *mpname;
663 size_t mpnamelen;
664 u_int32_t attrlength;
665 u_int8_t empty = 0;
666
667 /* A cnode's name may be incorrect for the root of a mounted
668 * filesystem (it can be mounted on a different directory name
669 * than the name of the volume, such as "blah-1"). So for the
670 * root directory, it's best to return the last element of the
671 location where the volume's mounted:
672 */
673 if ((vp != NULL) && vnode_isvroot(vp) &&
674 (mpname = mountpointname(vnode_mount(vp)))) {
675 mpnamelen = strlen(mpname);
676
677 /* Trim off any trailing slashes: */
678 while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/'))
679 --mpnamelen;
680
681 /* If there's anything left, use it instead of the volume's name */
682 if (mpnamelen > 0) {
683 name = (u_int8_t *)mpname;
684 namelen = mpnamelen;
685 }
686 }
687 if (name == NULL) {
688 name = &empty;
689 namelen = 0;
690 }
691
692 varbufptr = *abp->ab_varbufpp;
693 attr_refptr = (struct attrreference *)(*abp->ab_attrbufpp);
694
695 attrlength = namelen + 1;
696 attr_refptr->attr_dataoffset = (char *)varbufptr - (char *)attr_refptr;
697 attr_refptr->attr_length = attrlength;
698 (void) strncpy((char *)varbufptr, (const char *) name, attrlength);
699 /*
700 * Advance beyond the space just allocated and
701 * round up to the next 4-byte boundary:
702 */
703 varbufptr = ((char *)varbufptr) + attrlength + ((4 - (attrlength & 3)) & 3);
704 ++attr_refptr;
705
706 *abp->ab_attrbufpp = attr_refptr;
707 *abp->ab_varbufpp = varbufptr;
708 }
709
710 static void
711 packcommonattr(
712 struct attrblock *abp,
713 struct hfsmount *hfsmp,
714 struct vnode *vp,
715 struct cat_desc * cdp,
716 struct cat_attr * cap,
717 struct vfs_context * ctx)
718 {
719 attrgroup_t attr = abp->ab_attrlist->commonattr;
720 struct mount *mp = HFSTOVFS(hfsmp);
721 void *attrbufptr = *abp->ab_attrbufpp;
722 void *varbufptr = *abp->ab_varbufpp;
723 boolean_t is_64_bit = proc_is64bit(vfs_context_proc(ctx));
724 uid_t cuid = 1;
725 int isroot = 0;
726
727 if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) {
728 cuid = kauth_cred_getuid(vfs_context_ucred(ctx));
729 isroot = cuid == 0;
730 }
731
732 if (ATTR_CMN_NAME & attr) {
733 packnameattr(abp, vp, cdp->cd_nameptr, cdp->cd_namelen);
734 attrbufptr = *abp->ab_attrbufpp;
735 varbufptr = *abp->ab_varbufpp;
736 }
737 if (ATTR_CMN_DEVID & attr) {
738 *((dev_t *)attrbufptr) = hfsmp->hfs_raw_dev;
739 attrbufptr = ((dev_t *)attrbufptr) + 1;
740 }
741 if (ATTR_CMN_FSID & attr) {
742 fsid_t fsid;
743
744 fsid.val[0] = hfsmp->hfs_raw_dev;
745 fsid.val[1] = vfs_typenum(mp);
746 *((fsid_t *)attrbufptr) = fsid;
747 attrbufptr = ((fsid_t *)attrbufptr) + 1;
748 }
749 if (ATTR_CMN_OBJTYPE & attr) {
750 *((fsobj_type_t *)attrbufptr) = IFTOVT(cap->ca_mode);
751 attrbufptr = ((fsobj_type_t *)attrbufptr) + 1;
752 }
753 if (ATTR_CMN_OBJTAG & attr) {
754 *((fsobj_tag_t *)attrbufptr) = VT_HFS;
755 attrbufptr = ((fsobj_tag_t *)attrbufptr) + 1;
756 }
757 /*
758 * Exporting file IDs from HFS Plus:
759 *
760 * For "normal" files the c_fileid is the same value as the
761 * c_cnid. But for hard link files, they are different - the
762 * c_cnid belongs to the active directory entry (ie the link)
763 * and the c_fileid is for the actual inode (ie the data file).
764 *
765 * The stat call (getattr) will always return the c_fileid
766 * and Carbon APIs, which are hardlink-ignorant, will always
767 * receive the c_cnid (from getattrlist).
768 */
769 if (ATTR_CMN_OBJID & attr) {
770 ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid;
771 ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
772 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
773 }
774 if (ATTR_CMN_OBJPERMANENTID & attr) {
775 ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid;
776 ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
777 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
778 }
779 if (ATTR_CMN_PAROBJID & attr) {
780 ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_parentcnid;
781 ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
782 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
783 }
784 if (ATTR_CMN_SCRIPT & attr) {
785 *((text_encoding_t *)attrbufptr) = cdp->cd_encoding;
786 attrbufptr = ((text_encoding_t *)attrbufptr) + 1;
787 }
788 if (ATTR_CMN_CRTIME & attr) {
789 if (is_64_bit) {
790 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_itime;
791 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
792 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
793 }
794 else {
795 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_itime;
796 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
797 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
798 }
799 }
800 if (ATTR_CMN_MODTIME & attr) {
801 if (is_64_bit) {
802 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_mtime;
803 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
804 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
805 }
806 else {
807 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_mtime;
808 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
809 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
810 }
811 }
812 if (ATTR_CMN_CHGTIME & attr) {
813 if (is_64_bit) {
814 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_ctime;
815 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
816 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
817 }
818 else {
819 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_ctime;
820 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
821 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
822 }
823 }
824 if (ATTR_CMN_ACCTIME & attr) {
825 if (is_64_bit) {
826 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_atime;
827 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
828 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
829 }
830 else {
831 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_atime;
832 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
833 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
834 }
835 }
836 if (ATTR_CMN_BKUPTIME & attr) {
837 if (is_64_bit) {
838 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_btime;
839 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
840 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
841 }
842 else {
843 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_btime;
844 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
845 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
846 }
847 }
848 if (ATTR_CMN_FNDRINFO & attr) {
849 u_int8_t *finfo = NULL;
850 bcopy(&cap->ca_finderinfo, attrbufptr, sizeof(u_int8_t) * 32);
851 finfo = (u_int8_t*)attrbufptr;
852
853 /* Don't expose a symlink's private type/creator. */
854 if (S_ISLNK(cap->ca_mode)) {
855 struct FndrFileInfo *fip;
856
857 fip = (struct FndrFileInfo *)attrbufptr;
858 fip->fdType = 0;
859 fip->fdCreator = 0;
860 }
861
862 /* advance 16 bytes into the attrbuf */
863 finfo = finfo + 16;
864
865 /* also don't expose the date_added or write_gen_counter fields */
866 if (S_ISREG(cap->ca_mode) || S_ISLNK(cap->ca_mode)) {
867 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
868 extinfo->document_id = 0;
869 extinfo->date_added = 0;
870 extinfo->write_gen_counter = 0;
871 }
872 else if (S_ISDIR(cap->ca_mode)) {
873 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
874 extinfo->document_id = 0;
875 extinfo->date_added = 0;
876 extinfo->write_gen_counter = 0;
877 }
878
879 attrbufptr = (char *)attrbufptr + sizeof(u_int8_t) * 32;
880 }
881 if (ATTR_CMN_OWNERID & attr) {
882 uid_t nuid = cap->ca_uid;
883
884 if (!isroot) {
885 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS)
886 nuid = cuid;
887 else if (nuid == UNKNOWNUID)
888 nuid = cuid;
889 }
890
891 *((uid_t *)attrbufptr) = nuid;
892 attrbufptr = ((uid_t *)attrbufptr) + 1;
893 }
894 if (ATTR_CMN_GRPID & attr) {
895 gid_t ngid = cap->ca_gid;
896
897 if (!isroot) {
898 gid_t cgid = kauth_cred_getgid(vfs_context_ucred(ctx));
899 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS)
900 ngid = cgid;
901 else if (ngid == UNKNOWNUID)
902 ngid = cgid;
903 }
904
905 *((gid_t *)attrbufptr) = ngid;
906 attrbufptr = ((gid_t *)attrbufptr) + 1;
907 }
908 if (ATTR_CMN_ACCESSMASK & attr) {
909 /*
910 * [2856576] Since we are dynamically changing the owner, also
911 * effectively turn off the set-user-id and set-group-id bits,
912 * just like chmod(2) would when changing ownership. This prevents
913 * a security hole where set-user-id programs run as whoever is
914 * logged on (or root if nobody is logged in yet!)
915 */
916 *((u_int32_t *)attrbufptr) = (cap->ca_uid == UNKNOWNUID) ?
917 cap->ca_mode & ~(S_ISUID | S_ISGID) : cap->ca_mode;
918 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
919 }
920 if (ATTR_CMN_FLAGS & attr) {
921 *((u_int32_t *)attrbufptr) = cap->ca_flags;
922 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
923 }
924 if (ATTR_CMN_USERACCESS & attr) {
925 u_int32_t user_access;
926
927 /* Take the long path when we have an ACL */
928 if ((vp != NULLVP) && (cap->ca_recflags & kHFSHasSecurityMask)) {
929 user_access = hfs_real_user_access(vp, abp->ab_context);
930 } else {
931 user_access = DerivePermissionSummary(cap->ca_uid, cap->ca_gid,
932 cap->ca_mode, mp, vfs_context_ucred(ctx), 0);
933 }
934 /* Also consider READ-ONLY file system. */
935 if (vfs_flags(mp) & MNT_RDONLY) {
936 user_access &= ~W_OK;
937 }
938 /* Locked objects are not writable either */
939 if ((cap->ca_flags & UF_IMMUTABLE) && (vfs_context_suser(abp->ab_context) != 0))
940 user_access &= ~W_OK;
941 if ((cap->ca_flags & SF_IMMUTABLE) && (vfs_context_suser(abp->ab_context) == 0))
942 user_access &= ~W_OK;
943
944 *((u_int32_t *)attrbufptr) = user_access;
945 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
946 }
947 if (ATTR_CMN_FILEID & attr) {
948 *((u_int64_t *)attrbufptr) = cap->ca_fileid;
949 attrbufptr = ((u_int64_t *)attrbufptr) + 1;
950 }
951 if (ATTR_CMN_PARENTID & attr) {
952 *((u_int64_t *)attrbufptr) = cdp->cd_parentcnid;
953 attrbufptr = ((u_int64_t *)attrbufptr) + 1;
954 }
955
956 *abp->ab_attrbufpp = attrbufptr;
957 *abp->ab_varbufpp = varbufptr;
958 }
959
960 static void
961 packdirattr(
962 struct attrblock *abp,
963 struct hfsmount *hfsmp,
964 struct vnode *vp,
965 struct cat_desc * descp,
966 struct cat_attr * cattrp)
967 {
968 attrgroup_t attr = abp->ab_attrlist->dirattr;
969 void *attrbufptr = *abp->ab_attrbufpp;
970 u_int32_t entries;
971
972 /*
973 * The DIR_LINKCOUNT is the count of real directory hard links.
974 * (i.e. its not the sum of the implied "." and ".." references
975 * typically used in stat's st_nlink field)
976 */
977 if (ATTR_DIR_LINKCOUNT & attr) {
978 *((u_int32_t *)attrbufptr) = cattrp->ca_linkcount;
979 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
980 }
981 if (ATTR_DIR_ENTRYCOUNT & attr) {
982 entries = cattrp->ca_entries;
983
984 if (descp->cd_parentcnid == kHFSRootParentID) {
985 if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0)
986 --entries; /* hide private dir */
987 if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0)
988 --entries; /* hide private dir */
989 if (hfsmp->jnl ||
990 ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) &&
991 (hfsmp->hfs_flags & HFS_READ_ONLY)))
992 entries -= 2; /* hide the journal files */
993 }
994
995 *((u_int32_t *)attrbufptr) = entries;
996 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
997 }
998 if (ATTR_DIR_MOUNTSTATUS & attr) {
999 if (vp != NULL && vnode_mountedhere(vp) != NULL)
1000 *((u_int32_t *)attrbufptr) = DIR_MNTSTATUS_MNTPOINT;
1001 else
1002 *((u_int32_t *)attrbufptr) = 0;
1003 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
1004 }
1005 *abp->ab_attrbufpp = attrbufptr;
1006 }
1007
1008 static void
1009 packfileattr(
1010 struct attrblock *abp,
1011 struct hfsmount *hfsmp,
1012 struct cat_attr *cattrp,
1013 struct cat_fork *datafork,
1014 struct cat_fork *rsrcfork,
1015 struct vnode *vp)
1016 {
1017 #if !HFS_COMPRESSION
1018 #pragma unused(vp)
1019 #endif
1020 attrgroup_t attr = abp->ab_attrlist->fileattr;
1021 void *attrbufptr = *abp->ab_attrbufpp;
1022 void *varbufptr = *abp->ab_varbufpp;
1023 u_int32_t allocblksize;
1024
1025 allocblksize = HFSTOVCB(hfsmp)->blockSize;
1026
1027 off_t datasize = datafork->cf_size;
1028 off_t totalsize = datasize + rsrcfork->cf_size;
1029 #if HFS_COMPRESSION
1030 int handle_compressed;
1031 handle_compressed = (cattrp->ca_flags & UF_COMPRESSED);// && hfs_file_is_compressed(VTOC(vp), 1);
1032
1033 if (handle_compressed) {
1034 if (attr & (ATTR_FILE_DATALENGTH|ATTR_FILE_TOTALSIZE)) {
1035 if ( 0 == hfs_uncompressed_size_of_compressed_file(hfsmp, vp, cattrp->ca_fileid, &datasize, 1) ) { /* 1 == don't take the cnode lock */
1036 /* total size of a compressed file is just the data size */
1037 totalsize = datasize;
1038 }
1039 }
1040 }
1041 #endif
1042
1043 if (ATTR_FILE_LINKCOUNT & attr) {
1044 *((u_int32_t *)attrbufptr) = cattrp->ca_linkcount;
1045 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
1046 }
1047 if (ATTR_FILE_TOTALSIZE & attr) {
1048 *((off_t *)attrbufptr) = totalsize;
1049 attrbufptr = ((off_t *)attrbufptr) + 1;
1050 }
1051 if (ATTR_FILE_ALLOCSIZE & attr) {
1052 *((off_t *)attrbufptr) =
1053 (off_t)cattrp->ca_blocks * (off_t)allocblksize;
1054 attrbufptr = ((off_t *)attrbufptr) + 1;
1055 }
1056 if (ATTR_FILE_IOBLOCKSIZE & attr) {
1057 *((u_int32_t *)attrbufptr) = hfsmp->hfs_logBlockSize;
1058 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
1059 }
1060 if (ATTR_FILE_CLUMPSIZE & attr) {
1061 *((u_int32_t *)attrbufptr) = hfsmp->vcbClpSiz;
1062 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
1063 }
1064 if (ATTR_FILE_DEVTYPE & attr) {
1065 if (S_ISBLK(cattrp->ca_mode) || S_ISCHR(cattrp->ca_mode))
1066 *((u_int32_t *)attrbufptr) = (u_int32_t)cattrp->ca_rdev;
1067 else
1068 *((u_int32_t *)attrbufptr) = 0;
1069 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
1070 }
1071
1072 if (ATTR_FILE_DATALENGTH & attr) {
1073 *((off_t *)attrbufptr) = datasize;
1074 attrbufptr = ((off_t *)attrbufptr) + 1;
1075 }
1076
1077 #if HFS_COMPRESSION
1078 /* fake the data fork size on a decmpfs compressed file to reflect the
1079 * uncompressed size. This ensures proper reading and copying of these files.
1080 * NOTE: we may need to get the vnode here because the vnode parameter
1081 * passed by hfs_vnop_readdirattr() may be null.
1082 */
1083
1084 if ( handle_compressed ) {
1085 if (attr & ATTR_FILE_DATAALLOCSIZE) {
1086 *((off_t *)attrbufptr) = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize;
1087 attrbufptr = ((off_t *)attrbufptr) + 1;
1088 }
1089 if (attr & ATTR_FILE_RSRCLENGTH) {
1090 *((off_t *)attrbufptr) = 0;
1091 attrbufptr = ((off_t *)attrbufptr) + 1;
1092 }
1093 if (attr & ATTR_FILE_RSRCALLOCSIZE) {
1094 *((off_t *)attrbufptr) = 0;
1095 attrbufptr = ((off_t *)attrbufptr) + 1;
1096 }
1097 }
1098 else
1099 #endif
1100 {
1101 if (ATTR_FILE_DATAALLOCSIZE & attr) {
1102 *((off_t *)attrbufptr) = (off_t)datafork->cf_blocks * (off_t)allocblksize;
1103 attrbufptr = ((off_t *)attrbufptr) + 1;
1104 }
1105 if (ATTR_FILE_RSRCLENGTH & attr) {
1106 *((off_t *)attrbufptr) = rsrcfork->cf_size;
1107 attrbufptr = ((off_t *)attrbufptr) + 1;
1108 }
1109 if (ATTR_FILE_RSRCALLOCSIZE & attr) {
1110 *((off_t *)attrbufptr) = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize;
1111 attrbufptr = ((off_t *)attrbufptr) + 1;
1112 }
1113 }
1114 *abp->ab_attrbufpp = attrbufptr;
1115 *abp->ab_varbufpp = varbufptr;
1116 }
1117
1118 /*
1119 * Calculate the total size of an attribute block.
1120 */
1121 int
1122 hfs_attrblksize(struct attrlist *attrlist)
1123 {
1124 int size;
1125 attrgroup_t a;
1126 int sizeof_timespec;
1127 boolean_t is_64_bit = proc_is64bit(current_proc());
1128
1129 if (is_64_bit)
1130 sizeof_timespec = sizeof(struct user64_timespec);
1131 else
1132 sizeof_timespec = sizeof(struct user32_timespec);
1133
1134 hfs_assert((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0);
1135
1136 hfs_assert((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0);
1137
1138 hfs_assert((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0);
1139
1140 hfs_assert((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0);
1141
1142 // disable this because it will break the simulator/build machines each
1143 // time a new _CMNEXT_ bit is added
1144 // hfs_assert(((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0) ||
1145 // ((attrlist->forkattr & ~ATTR_CMNEXT_VALIDMASK) == 0));
1146
1147 size = 0;
1148
1149 if ((a = attrlist->commonattr) != 0) {
1150 if (a & ATTR_CMN_NAME) size += sizeof(struct attrreference);
1151 if (a & ATTR_CMN_DEVID) size += sizeof(dev_t);
1152 if (a & ATTR_CMN_FSID) size += sizeof(fsid_t);
1153 if (a & ATTR_CMN_OBJTYPE) size += sizeof(fsobj_type_t);
1154 if (a & ATTR_CMN_OBJTAG) size += sizeof(fsobj_tag_t);
1155 if (a & ATTR_CMN_OBJID) size += sizeof(fsobj_id_t);
1156 if (a & ATTR_CMN_OBJPERMANENTID) size += sizeof(fsobj_id_t);
1157 if (a & ATTR_CMN_PAROBJID) size += sizeof(fsobj_id_t);
1158 if (a & ATTR_CMN_SCRIPT) size += sizeof(text_encoding_t);
1159 if (a & ATTR_CMN_CRTIME) size += sizeof_timespec;
1160 if (a & ATTR_CMN_MODTIME) size += sizeof_timespec;
1161 if (a & ATTR_CMN_CHGTIME) size += sizeof_timespec;
1162 if (a & ATTR_CMN_ACCTIME) size += sizeof_timespec;
1163 if (a & ATTR_CMN_BKUPTIME) size += sizeof_timespec;
1164 if (a & ATTR_CMN_FNDRINFO) size += 32 * sizeof(u_int8_t);
1165 if (a & ATTR_CMN_OWNERID) size += sizeof(uid_t);
1166 if (a & ATTR_CMN_GRPID) size += sizeof(gid_t);
1167 if (a & ATTR_CMN_ACCESSMASK) size += sizeof(u_int32_t);
1168 if (a & ATTR_CMN_FLAGS) size += sizeof(u_int32_t);
1169 if (a & ATTR_CMN_USERACCESS) size += sizeof(u_int32_t);
1170 if (a & ATTR_CMN_FILEID) size += sizeof(u_int64_t);
1171 if (a & ATTR_CMN_PARENTID) size += sizeof(u_int64_t);
1172 }
1173 if ((a = attrlist->dirattr) != 0) {
1174 if (a & ATTR_DIR_LINKCOUNT) size += sizeof(u_int32_t);
1175 if (a & ATTR_DIR_ENTRYCOUNT) size += sizeof(u_int32_t);
1176 if (a & ATTR_DIR_MOUNTSTATUS) size += sizeof(u_int32_t);
1177 }
1178 if ((a = attrlist->fileattr) != 0) {
1179 if (a & ATTR_FILE_LINKCOUNT) size += sizeof(u_int32_t);
1180 if (a & ATTR_FILE_TOTALSIZE) size += sizeof(off_t);
1181 if (a & ATTR_FILE_ALLOCSIZE) size += sizeof(off_t);
1182 if (a & ATTR_FILE_IOBLOCKSIZE) size += sizeof(u_int32_t);
1183 if (a & ATTR_FILE_CLUMPSIZE) size += sizeof(u_int32_t);
1184 if (a & ATTR_FILE_DEVTYPE) size += sizeof(u_int32_t);
1185 if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t);
1186 if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t);
1187 if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t);
1188 if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t);
1189 }
1190
1191 return (size);
1192 }
1193
1194 #define KAUTH_DIR_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | \
1195 KAUTH_VNODE_ADD_SUBDIRECTORY | \
1196 KAUTH_VNODE_DELETE_CHILD)
1197
1198 #define KAUTH_DIR_READ_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY)
1199
1200 #define KAUTH_DIR_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH)
1201
1202 #define KAUTH_FILE_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA)
1203
1204 #define KAUTH_FILE_READRIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA)
1205
1206 #define KAUTH_FILE_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE)
1207
1208
1209 /*
1210 * Compute the same [expensive] user_access value as getattrlist does
1211 */
1212 static u_int32_t
1213 hfs_real_user_access(vnode_t vp, vfs_context_t ctx)
1214 {
1215 u_int32_t user_access = 0;
1216
1217 if (vnode_isdir(vp)) {
1218 if (vnode_authorize(vp, NULLVP, KAUTH_DIR_WRITE_RIGHTS, ctx) == 0)
1219 user_access |= W_OK;
1220 if (vnode_authorize(vp, NULLVP, KAUTH_DIR_READ_RIGHTS, ctx) == 0)
1221 user_access |= R_OK;
1222 if (vnode_authorize(vp, NULLVP, KAUTH_DIR_EXECUTE_RIGHTS, ctx) == 0)
1223 user_access |= X_OK;
1224 } else {
1225 if (vnode_authorize(vp, NULLVP, KAUTH_FILE_WRITE_RIGHTS, ctx) == 0)
1226 user_access |= W_OK;
1227 if (vnode_authorize(vp, NULLVP, KAUTH_FILE_READRIGHTS, ctx) == 0)
1228 user_access |= R_OK;
1229 if (vnode_authorize(vp, NULLVP, KAUTH_FILE_EXECUTE_RIGHTS, ctx) == 0)
1230 user_access |= X_OK;
1231 }
1232 return (user_access);
1233 }
1234
1235
1236 u_int32_t
1237 DerivePermissionSummary(uid_t obj_uid, gid_t obj_gid, mode_t obj_mode,
1238 struct mount *mp, kauth_cred_t cred, __unused struct proc *p)
1239 {
1240 u_int32_t permissions;
1241
1242 if (obj_uid == UNKNOWNUID)
1243 obj_uid = kauth_cred_getuid(cred);
1244
1245 /* User id 0 (root) always gets access. */
1246 if (!suser(cred, NULL)) {
1247 permissions = R_OK | W_OK | X_OK;
1248 goto Exit;
1249 };
1250
1251 /* Otherwise, check the owner. */
1252 if (hfs_owner_rights(VFSTOHFS(mp), obj_uid, cred, NULL, false) == 0) {
1253 permissions = ((u_int32_t)obj_mode & S_IRWXU) >> 6;
1254 goto Exit;
1255 }
1256
1257 /* Otherwise, check the groups. */
1258 if (! (((unsigned int)vfs_flags(mp)) & MNT_UNKNOWNPERMISSIONS)) {
1259 int is_member;
1260
1261 if (kauth_cred_ismember_gid(cred, obj_gid, &is_member) == 0 && is_member) {
1262 permissions = ((u_int32_t)obj_mode & S_IRWXG) >> 3;
1263 goto Exit;
1264 }
1265 }
1266
1267 /* Otherwise, settle for 'others' access. */
1268 permissions = (u_int32_t)obj_mode & S_IRWXO;
1269
1270 Exit:
1271 return (permissions);
1272 }
1273
1274
1275 /*
1276 * ===========================================================================
1277 * Support functions for filling up a vnode_attr structure based on attributes
1278 * requested.
1279 * ===========================================================================
1280 */
1281 void
1282 get_vattr_data_for_attrs(struct attrlist *alp, struct vnode_attr *vap,
1283 struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc *descp,
1284 struct cat_attr *atrp, struct cat_fork *datafork, struct cat_fork *rsrcfork,
1285 vfs_context_t ctx)
1286 {
1287 if (alp->commonattr || alp->forkattr) {
1288 vattr_data_for_common_attrs(alp, vap, hfsmp, vp, descp, atrp,
1289 ctx);
1290 }
1291
1292 if (alp->dirattr && S_ISDIR(atrp->ca_mode))
1293 vattr_data_for_dir_attrs(alp, vap, hfsmp, vp, descp, atrp);
1294
1295 if (alp->fileattr && !S_ISDIR(atrp->ca_mode)) {
1296 vattr_data_for_file_attrs(alp, vap, hfsmp, atrp, datafork,
1297 rsrcfork, vp);
1298 }
1299 }
1300
1301 static void
1302 copy_name_attr(struct vnode_attr *vap, struct vnode *vp, const u_int8_t *name,
1303 int namelen)
1304 {
1305 char *mpname;
1306 size_t mpnamelen;
1307 u_int32_t attrlength;
1308 u_int8_t empty = 0;
1309
1310 /* A cnode's name may be incorrect for the root of a mounted
1311 * filesystem (it can be mounted on a different directory name
1312 * than the name of the volume, such as "blah-1"). So for the
1313 * root directory, it's best to return the last element of the
1314 location where the volume's mounted:
1315 */
1316 if ((vp != NULL) && vnode_isvroot(vp) &&
1317 (mpname = mountpointname(vnode_mount(vp)))) {
1318 mpnamelen = strlen(mpname);
1319
1320 /* Trim off any trailing slashes: */
1321 while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/'))
1322 --mpnamelen;
1323
1324 /* If there's anything left, use it instead of the volume's name */
1325 if (mpnamelen > 0) {
1326 name = (u_int8_t *)mpname;
1327 namelen = mpnamelen;
1328 }
1329 }
1330
1331 if (name == NULL) {
1332 name = &empty;
1333 namelen = 0;
1334 }
1335
1336 attrlength = namelen + 1;
1337 (void) strncpy((char *)vap->va_name, (const char *) name, attrlength);
1338 /*
1339 * round upto 8 and zero out the rounded up bytes.
1340 */
1341 attrlength = min(kHFSPlusMaxFileNameBytes, ((attrlength + 7) & ~0x07));
1342 bzero(vap->va_name + attrlength, kHFSPlusMaxFileNameBytes - attrlength);
1343 }
1344
1345 static void
1346 vattr_data_for_common_attrs( struct attrlist *alp, struct vnode_attr *vap,
1347 struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc *cdp,
1348 struct cat_attr *cap, vfs_context_t ctx)
1349 {
1350 attrgroup_t attr = alp->commonattr;
1351 struct mount *mp = HFSTOVFS(hfsmp);
1352 uid_t cuid = 1;
1353 int isroot = 0;
1354
1355 if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) {
1356 cuid = kauth_cred_getuid(vfs_context_ucred(ctx));
1357 isroot = cuid == 0;
1358 }
1359
1360 if (ATTR_CMN_NAME & attr) {
1361 if (vap->va_name) {
1362 copy_name_attr(vap, vp, cdp->cd_nameptr,
1363 cdp->cd_namelen);
1364 VATTR_SET_SUPPORTED(vap, va_name);
1365 } else {
1366 VATTR_CLEAR_SUPPORTED(vap, va_name);
1367 }
1368 }
1369
1370 if (ATTR_CMN_DEVID & attr) {
1371 vap->va_devid = hfsmp->hfs_raw_dev;
1372 VATTR_SET_SUPPORTED(vap, va_devid);
1373 }
1374
1375 if (ATTR_CMN_FSID & attr) {
1376 vap->va_fsid64.val[0] = hfsmp->hfs_raw_dev;
1377 vap->va_fsid64.val[1] = vfs_typenum(mp);
1378 VATTR_SET_SUPPORTED(vap, va_fsid64);
1379 }
1380 /*
1381 * We always provide the objtype even if not asked because VFS helper
1382 * functions depend on knowing the object's type.
1383 */
1384 vap->va_objtype = IFTOVT(cap->ca_mode);
1385 VATTR_SET_SUPPORTED(vap, va_objtype);
1386
1387 if (ATTR_CMN_OBJTAG & attr) {
1388 vap->va_objtag = VT_HFS;
1389 VATTR_SET_SUPPORTED(vap, va_objtag);
1390 }
1391 /*
1392 * Exporting file IDs from HFS Plus:
1393 *
1394 * For "normal" files the c_fileid is the same value as the
1395 * c_cnid. But for hard link files, they are different - the
1396 * c_cnid belongs to the active directory entry (ie the link)
1397 * and the c_fileid is for the actual inode (ie the data file).
1398 *
1399 * The stat call (getattr) will always return the c_fileid
1400 * and Carbon APIs, which are hardlink-ignorant, will always
1401 * receive the c_cnid (from getattrlist).
1402 *
1403 * Forkattrs are now repurposed for Common Extended Attributes.
1404 */
1405 if ((ATTR_CMN_OBJID & attr) || (ATTR_CMN_OBJPERMANENTID & attr) ||
1406 alp->forkattr & ATTR_CMNEXT_LINKID) {
1407 vap->va_linkid = cdp->cd_cnid;
1408 VATTR_SET_SUPPORTED(vap, va_linkid);
1409 }
1410
1411 if (ATTR_CMN_PAROBJID & attr) {
1412 vap->va_parentid = cdp->cd_parentcnid;
1413 VATTR_SET_SUPPORTED(vap, va_parentid);
1414 }
1415
1416 if (ATTR_CMN_SCRIPT & attr) {
1417 vap->va_encoding = cdp->cd_encoding;
1418 VATTR_SET_SUPPORTED(vap, va_encoding);
1419 }
1420
1421 if (ATTR_CMN_CRTIME & attr) {
1422 vap->va_create_time.tv_sec = cap->ca_itime;
1423 vap->va_create_time.tv_nsec = 0;
1424 VATTR_SET_SUPPORTED(vap, va_create_time);
1425 }
1426
1427 if (ATTR_CMN_MODTIME & attr) {
1428 vap->va_modify_time.tv_sec = cap->ca_mtime;
1429 vap->va_modify_time.tv_nsec = 0;
1430 VATTR_SET_SUPPORTED(vap, va_modify_time);
1431 }
1432
1433 if (ATTR_CMN_CHGTIME & attr) {
1434 vap->va_change_time.tv_sec = cap->ca_ctime;
1435 vap->va_change_time.tv_nsec = 0;
1436 VATTR_SET_SUPPORTED(vap, va_change_time);
1437 }
1438
1439 if (ATTR_CMN_ACCTIME & attr) {
1440 vap->va_access_time.tv_sec = cap->ca_atime;
1441 vap->va_access_time.tv_nsec = 0;
1442 VATTR_SET_SUPPORTED(vap, va_access_time);
1443 }
1444
1445 if (ATTR_CMN_BKUPTIME & attr) {
1446 vap->va_backup_time.tv_sec = cap->ca_btime;
1447 vap->va_backup_time.tv_nsec = 0;
1448 VATTR_SET_SUPPORTED(vap, va_backup_time);
1449 }
1450
1451 if (ATTR_CMN_FNDRINFO & attr) {
1452 u_int8_t *finfo = NULL;
1453
1454 bcopy(&cap->ca_finderinfo, &vap->va_finderinfo[0],
1455 sizeof(u_int8_t) * 32);
1456 finfo = (u_int8_t*)(&vap->va_finderinfo[0]);
1457
1458 /* Don't expose a symlink's private type/creator. */
1459 if (S_ISLNK(cap->ca_mode)) {
1460 struct FndrFileInfo *fip;
1461
1462 fip = (struct FndrFileInfo *)finfo;
1463 fip->fdType = 0;
1464 fip->fdCreator = 0;
1465 }
1466
1467 /* advance 16 bytes into the attrbuf */
1468 finfo = finfo + 16;
1469
1470 /* also don't expose the date_added or write_gen_counter fields */
1471 if (S_ISREG(cap->ca_mode) || S_ISLNK(cap->ca_mode)) {
1472 struct FndrExtendedFileInfo *extinfo =
1473 (struct FndrExtendedFileInfo *)finfo;
1474 extinfo->document_id = 0;
1475 extinfo->date_added = 0;
1476 extinfo->write_gen_counter = 0;
1477 } else if (S_ISDIR(cap->ca_mode)) {
1478 struct FndrExtendedDirInfo *extinfo =
1479 (struct FndrExtendedDirInfo *)finfo;
1480 extinfo->document_id = 0;
1481 extinfo->date_added = 0;
1482 extinfo->write_gen_counter = 0;
1483 }
1484
1485 VATTR_SET_SUPPORTED(vap, va_finderinfo);
1486 }
1487
1488 if (ATTR_CMN_OWNERID & attr) {
1489 uid_t nuid = cap->ca_uid;
1490
1491 if (!isroot) {
1492 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS)
1493 nuid = cuid;
1494 else if (nuid == UNKNOWNUID)
1495 nuid = cuid;
1496 }
1497
1498 vap->va_uid = nuid;
1499 VATTR_SET_SUPPORTED(vap, va_uid);
1500 }
1501
1502 if (ATTR_CMN_GRPID & attr) {
1503 gid_t ngid = cap->ca_gid;
1504
1505 if (!isroot) {
1506 gid_t cgid = kauth_cred_getgid(vfs_context_ucred(ctx));
1507 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS)
1508 ngid = cgid;
1509 else if (ngid == UNKNOWNUID)
1510 ngid = cgid;
1511 }
1512
1513 vap->va_gid = ngid;
1514 VATTR_SET_SUPPORTED(vap, va_gid);
1515 }
1516
1517 if (ATTR_CMN_ACCESSMASK & attr) {
1518 uint32_t nmode;
1519 /*
1520 * [2856576] Since we are dynamically changing the owner, also
1521 * effectively turn off the set-user-id and set-group-id bits,
1522 * just like chmod(2) would when changing ownership. This prevents
1523 * a security hole where set-user-id programs run as whoever is
1524 * logged on (or root if nobody is logged in yet!)
1525 */
1526 nmode = (cap->ca_uid == UNKNOWNUID) ?
1527 cap->ca_mode & ~(S_ISUID | S_ISGID) : cap->ca_mode;
1528
1529 vap->va_mode = nmode;
1530 VATTR_SET_SUPPORTED(vap, va_mode);
1531 }
1532
1533 if (ATTR_CMN_FLAGS & attr) {
1534 vap->va_flags = cap->ca_flags;
1535 VATTR_SET_SUPPORTED(vap, va_flags);
1536 }
1537
1538 if (ATTR_CMN_GEN_COUNT & attr) {
1539 vap->va_write_gencount = hfs_get_gencount_from_blob(
1540 (const uint8_t *)cap->ca_finderinfo, cap->ca_mode);
1541 VATTR_SET_SUPPORTED(vap, va_write_gencount);
1542 }
1543
1544 if (ATTR_CMN_DOCUMENT_ID & attr) {
1545 vap->va_document_id = hfs_get_document_id_from_blob(
1546 (const uint8_t *)cap->ca_finderinfo, cap->ca_mode);
1547 VATTR_SET_SUPPORTED(vap, va_document_id);
1548 }
1549
1550 if (ATTR_CMN_USERACCESS & attr) {
1551 u_int32_t user_access;
1552
1553 /* Take the long path when we have an ACL */
1554 if ((vp != NULLVP) && (cap->ca_recflags & kHFSHasSecurityMask)) {
1555 user_access = hfs_real_user_access(vp, ctx);
1556 } else {
1557 user_access = DerivePermissionSummary(cap->ca_uid, cap->ca_gid,
1558 cap->ca_mode, mp, vfs_context_ucred(ctx), 0);
1559 }
1560 /* Also consider READ-ONLY file system. */
1561 if (vfs_flags(mp) & MNT_RDONLY) {
1562 user_access &= ~W_OK;
1563 }
1564 /* Locked objects are not writable either */
1565 if ((cap->ca_flags & UF_IMMUTABLE) && (vfs_context_suser(ctx) != 0))
1566 user_access &= ~W_OK;
1567 if ((cap->ca_flags & SF_IMMUTABLE) && (vfs_context_suser(ctx) == 0))
1568 user_access &= ~W_OK;
1569
1570 vap->va_user_access = user_access;
1571 VATTR_SET_SUPPORTED(vap, va_user_access);
1572 }
1573
1574 /*
1575 * Right now the best we can do is tell if we *don't* have extended
1576 * security (like hfs_vnop_getattr).
1577 */
1578 if (ATTR_CMN_EXTENDED_SECURITY & attr) {
1579 if (!(cap->ca_recflags & kHFSHasSecurityMask)) {
1580 vap->va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
1581 VATTR_SET_SUPPORTED(vap, va_acl);
1582 }
1583 }
1584
1585 if (ATTR_CMN_FILEID & attr) {
1586 vap->va_fileid = cap->ca_fileid;
1587 VATTR_SET_SUPPORTED(vap, va_fileid);
1588 }
1589
1590 if (ATTR_CMN_PARENTID & attr) {
1591 vap->va_parentid = cdp->cd_parentcnid;
1592 VATTR_SET_SUPPORTED(vap, va_parentid);
1593 }
1594
1595 if (ATTR_CMN_ADDEDTIME & attr) {
1596 if (cap->ca_recflags & kHFSHasDateAddedMask) {
1597 vap->va_addedtime.tv_sec = hfs_get_dateadded_from_blob(
1598 (const uint8_t *)cap->ca_finderinfo, cap->ca_mode);
1599 vap->va_addedtime.tv_nsec = 0;
1600 VATTR_SET_SUPPORTED(vap, va_addedtime);
1601 }
1602 }
1603 }
1604
1605 static void
1606 vattr_data_for_dir_attrs(struct attrlist *alp, struct vnode_attr *vap,
1607 struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc * descp,
1608 struct cat_attr * cattrp)
1609 {
1610 attrgroup_t attr = alp->dirattr;
1611 u_int32_t entries;
1612
1613 /*
1614 * The DIR_LINKCOUNT is the count of real directory hard links.
1615 * (i.e. its not the sum of the implied "." and ".." references
1616 * typically used in stat's st_nlink field)
1617 */
1618 if (ATTR_DIR_LINKCOUNT & attr) {
1619 vap->va_dirlinkcount = cattrp->ca_linkcount;
1620 VATTR_SET_SUPPORTED(vap, va_dirlinkcount);
1621 }
1622 if (ATTR_DIR_ENTRYCOUNT & attr) {
1623 entries = cattrp->ca_entries;
1624
1625 if (descp->cd_parentcnid == kHFSRootParentID) {
1626 if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0)
1627 --entries; /* hide private dir */
1628 if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0)
1629 --entries; /* hide private dir */
1630 if (hfsmp->jnl ||
1631 ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) &&
1632 (hfsmp->hfs_flags & HFS_READ_ONLY)))
1633 entries -= 2; /* hide the journal files */
1634 }
1635
1636 vap->va_nchildren = entries;
1637 VATTR_SET_SUPPORTED(vap, va_nchildren);
1638 }
1639
1640 if (ATTR_DIR_MOUNTSTATUS & attr) {
1641 /*
1642 * There is not vnode_attr for mount point status.
1643 * XXX. Should there be ?
1644 */
1645 u_int32_t mstatus = 0;
1646
1647 if (vp != NULL && vnode_mountedhere(vp) != NULL)
1648 mstatus = DIR_MNTSTATUS_MNTPOINT;
1649 }
1650 }
1651
1652 static void
1653 vattr_data_for_file_attrs(struct attrlist *alp, struct vnode_attr *vap,
1654 struct hfsmount *hfsmp, struct cat_attr *cattrp, struct cat_fork *datafork,
1655 struct cat_fork *rsrcfork, struct vnode *vp)
1656 {
1657 #if !HFS_COMPRESSION
1658 #pragma unused(vp)
1659 #endif
1660 attrgroup_t attr = alp->fileattr;
1661 off_t da_size, rsrc_len, rsrc_alloc;
1662 u_int32_t allocblksize;
1663
1664 allocblksize = HFSTOVCB(hfsmp)->blockSize;
1665
1666 off_t datasize = datafork->cf_size;
1667 off_t totalsize = datasize + rsrcfork->cf_size;
1668 #if HFS_COMPRESSION
1669 int handle_compressed;
1670 handle_compressed = (cattrp->ca_flags & UF_COMPRESSED);// && hfs_file_is_compressed(VTOC(vp), 1);
1671
1672 if (handle_compressed) {
1673 if (attr & (ATTR_FILE_DATALENGTH|ATTR_FILE_TOTALSIZE)) {
1674 if ( 0 == hfs_uncompressed_size_of_compressed_file(hfsmp, vp, cattrp->ca_fileid, &datasize, 1) ) { /* 1 == don't take the cnode lock */
1675 /* total size of a compressed file is just the data size */
1676 totalsize = datasize;
1677 }
1678 }
1679 }
1680 #endif
1681
1682 if (ATTR_FILE_LINKCOUNT & attr) {
1683 vap->va_nlink = cattrp->ca_linkcount;
1684 VATTR_SET_SUPPORTED(vap, va_nlink);
1685 }
1686 if (ATTR_FILE_TOTALSIZE & attr) {
1687 VATTR_RETURN(vap, va_total_size, totalsize);
1688 }
1689 if (ATTR_FILE_ALLOCSIZE & attr) {
1690 VATTR_RETURN(vap, va_total_alloc,
1691 (off_t)cattrp->ca_blocks * (off_t)allocblksize );
1692 }
1693 if (ATTR_FILE_IOBLOCKSIZE & attr) {
1694 VATTR_RETURN(vap, va_iosize, hfsmp->hfs_logBlockSize);
1695 }
1696
1697 /* ATTR_FILE_CLUMPSIZE is obsolete */
1698
1699 if (ATTR_FILE_DEVTYPE & attr) {
1700 dev_t dev = 0;
1701
1702 if (S_ISBLK(cattrp->ca_mode) || S_ISCHR(cattrp->ca_mode))
1703 dev = (u_int32_t)cattrp->ca_rdev;
1704
1705 VATTR_RETURN(vap, va_rdev, dev);
1706 }
1707
1708 if (ATTR_FILE_DATALENGTH & attr) {
1709 VATTR_RETURN(vap, va_data_size, datasize);
1710 }
1711 #if HFS_COMPRESSION
1712 /* fake the data fork size on a decmpfs compressed file to reflect the
1713 * uncompressed size. This ensures proper reading and copying of these
1714 * files.
1715 * NOTE: we may need to get the vnode here because the vnode parameter
1716 * passed by hfs_vnop_readdirattr() may be null.
1717 */
1718
1719 if (handle_compressed) {
1720 da_size = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize;
1721 rsrc_len = 0;
1722 rsrc_alloc = 0;
1723 }
1724 else
1725 #endif
1726 {
1727 da_size = (off_t)datafork->cf_blocks * (off_t)allocblksize;
1728 rsrc_len = rsrcfork->cf_size;
1729 rsrc_alloc = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize;
1730 }
1731
1732 if (ATTR_FILE_DATAALLOCSIZE & attr) {
1733 VATTR_RETURN(vap, va_data_alloc, da_size);
1734 }
1735
1736 if (ATTR_FILE_RSRCLENGTH & attr) {
1737 VATTR_RETURN(vap, va_rsrc_length, rsrc_len);
1738 }
1739
1740 if (ATTR_FILE_RSRCALLOCSIZE & attr) {
1741 VATTR_RETURN(vap, va_rsrc_alloc, rsrc_alloc);
1742 }
1743 }