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