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