]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_attrlist.c
xnu-2782.40.9.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_attrlist.c
CommitLineData
9bccf70c 1/*
22ba694c 2 * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
9bccf70c 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
9bccf70c
A
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>
91447636
A
42#include <sys/mount_internal.h>
43#include <sys/kauth.h>
6d2010ae 44#include <sys/fsctl.h>
91447636
A
45
46#include <kern/locks.h>
9bccf70c
A
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"
2d21ac55 53#include "hfs_btreeio.h"
9bccf70c
A
54
55/* Packing routines: */
56
2d21ac55
A
57static void packnameattr(struct attrblock *abp, struct vnode *vp,
58 const u_int8_t *name, int namelen);
9bccf70c
A
59
60static void packcommonattr(struct attrblock *abp, struct hfsmount *hfsmp,
61 struct vnode *vp, struct cat_desc * cdp,
b0d623f7 62 struct cat_attr * cap, struct vfs_context *ctx);
9bccf70c
A
63
64static void packfileattr(struct attrblock *abp, struct hfsmount *hfsmp,
65 struct cat_attr *cattrp, struct cat_fork *datafork,
b0d623f7 66 struct cat_fork *rsrcfork, struct vnode *vp);
9bccf70c
A
67
68static void packdirattr(struct attrblock *abp, struct hfsmount *hfsmp,
69 struct vnode *vp, struct cat_desc * descp,
91447636 70 struct cat_attr * cattrp);
9bccf70c 71
2d21ac55 72static u_int32_t hfs_real_user_access(vnode_t vp, vfs_context_t ctx);
9bccf70c 73
fe8ab488
A
74static 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
78static 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
82static void vattr_data_for_dir_attrs(struct attrlist *, struct vnode_attr *,
83 struct hfsmount *, struct vnode *, struct cat_desc *, struct cat_attr *);
84
85static 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
89static 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
9bccf70c
A
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.
9bccf70c 102 */
9bccf70c 103int
91447636
A
104hfs_vnop_readdirattr(ap)
105 struct vnop_readdirattr_args /* {
9bccf70c
A
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;
91447636 114 vfs_context_t a_context;
9bccf70c
A
115 } */ *ap;
116{
fe8ab488
A
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 */
155int
156hfs_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 */
187int
188hfs_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{
91447636
A
192 struct cnode *dcp;
193 struct hfsmount * hfsmp;
2d21ac55
A
194 u_int32_t fixedblocksize;
195 u_int32_t maxattrblocksize;
196 u_int32_t currattrbufsize;
9bccf70c 197 void *attrbufptr = NULL;
fe8ab488
A
198 void *attrptr = NULL;
199 void *varptr = NULL;
200 caddr_t namebuf = NULL;
9bccf70c
A
201 struct attrblock attrblk;
202 int error = 0;
2d21ac55 203 int index = 0;
fe8ab488 204 int i = 0;
9bccf70c 205 struct cat_desc *lastdescp = NULL;
9bccf70c 206 struct cat_entrylist *ce_list = NULL;
91447636
A
207 directoryhint_t *dirhint = NULL;
208 unsigned int tag;
2d21ac55
A
209 int maxentries;
210 int lockflags;
b0d623f7 211 u_int32_t dirchg = 0;
fe8ab488 212 int reachedeof = 0;
b4c24cb9 213
fe8ab488
A
214 *(actualcount) = 0;
215 *(eofflag) = 0;
9bccf70c 216
fe8ab488 217 if ((uio_resid(uio) <= 0) || (uio_iovcnt(uio) > 1))
9bccf70c 218 return (EINVAL);
6d2010ae 219
316670eb 220 if (VTOC(dvp)->c_bsdflags & UF_COMPRESSED) {
6d2010ae
A
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
2d21ac55
A
231 /*
232 * Take an exclusive directory lock since we manipulate the directory hints
233 */
39236c6e 234 if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
91447636 235 return (error);
2d21ac55 236 }
91447636
A
237 dcp = VTOC(dvp);
238 hfsmp = VTOHFS(dvp);
239
b0d623f7 240 dirchg = dcp->c_dirchangecnt;
9bccf70c 241
2d21ac55 242 /* Extract directory index and tag (sequence number) from uio_offset */
91447636
A
243 index = uio_offset(uio) & HFS_INDEX_MASK;
244 tag = uio_offset(uio) & ~HFS_INDEX_MASK;
fe8ab488
A
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 */
9bccf70c
A
253
254 /* Get a buffer to hold packed attributes. */
2d21ac55 255 fixedblocksize = (sizeof(u_int32_t) + hfs_attrblksize(alist)); /* 4 bytes for length */
9bccf70c 256
fe8ab488
A
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 }
2d21ac55
A
279 /* Get a detached directory hint (cnode must be locked exclusive) */
280 dirhint = hfs_getdirhint(dcp, ((index - 1) & HFS_INDEX_MASK) | tag, TRUE);
91447636
A
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 /*
2d21ac55
A
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.
91447636 295 */
2d21ac55 296 maxentries = uio_resid(uio) / (fixedblocksize + HFS_AVERAGE_NAME_SIZE);
fe8ab488
A
297 /* There is maxcount for the bulk vnop */
298 if (!vap)
299 maxentries = min(maxentries, maxcount);
2d21ac55
A
300 maxentries = min(maxentries, MAXCATENTRIES);
301 if (maxentries < 1) {
302 error = EINVAL;
303 goto exit2;
91447636 304 }
2d21ac55
A
305
306 /* Initialize a catalog entry list. */
307 MALLOC(ce_list, struct cat_entrylist *, CE_LIST_SIZE(maxentries), M_TEMP, M_WAITOK);
b0d623f7
A
308 if (ce_list == NULL) {
309 error = ENOMEM;
310 goto exit2;
311 }
2d21ac55
A
312 bzero(ce_list, CE_LIST_SIZE(maxentries));
313 ce_list->maxentries = maxentries;
314
9bccf70c 315 /*
2d21ac55 316 * Populate the ce_list from the catalog file.
9bccf70c 317 */
2d21ac55
A
318 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
319
fe8ab488 320 error = cat_getentriesattr(hfsmp, dirhint, ce_list, &reachedeof);
2d21ac55
A
321 /* Don't forget to release the descriptors later! */
322
323 hfs_systemfile_unlock(hfsmp, lockflags);
324
fe8ab488
A
325 if ((error == ENOENT) || (reachedeof != 0)) {
326 *(eofflag) = TRUE;
2d21ac55
A
327 error = 0;
328 }
329 if (error) {
330 goto exit1;
331 }
332
fe8ab488
A
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
2d21ac55
A
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
fe8ab488
A
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 ) {
9bccf70c 381 /*
2d21ac55 382 * Obtain vnode for our vnode_authorize() calls.
9bccf70c 383 */
6d2010ae 384 if (hfs_vget(hfsmp, cattrp->ca_fileid, &vp, 0, 0) != 0) {
9bccf70c 385 vp = NULL;
9bccf70c 386 }
fe8ab488 387 } else if (vap || !(options & FSOPT_NOINMEMUPDATE)) {
2d21ac55 388 /* Get in-memory cnode data (if any). */
6d2010ae 389 vp = hfs_chash_getvnode(hfsmp, cattrp->ca_fileid, 0, 0, 0);
2d21ac55
A
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;
9bccf70c 404 }
2d21ac55
A
405 /* All done with cnode. */
406 hfs_unlock(cp);
407 cp = NULL;
408 }
9bccf70c 409
fe8ab488
A
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 }
91447636 429
fe8ab488
A
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)) {
2d21ac55 434 break;
fe8ab488
A
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 }
2d21ac55 473 }
fe8ab488
A
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
2d21ac55
A
490 /* Save the last valid catalog entry */
491 lastdescp = &ce_list->entry[i].ce_desc;
492 index++;
fe8ab488 493 *actualcount += 1;
2d21ac55 494
fe8ab488
A
495 /* Do we have the bare minimum for the next entry ? */
496 if (resid < sizeof(uint32_t))
2d21ac55 497 break;
9bccf70c 498 }
2d21ac55 499 } /* for each catalog entry */
9bccf70c 500
bb59bff1
A
501 /*
502 * If we couldn't fit all the entries requested in the user's buffer,
503 * it's not EOF.
504 */
505 if (*eofflag && (*actualcount < (int)ce_list->realentries))
506 *eofflag = 0;
507
2d21ac55
A
508 /* If we skipped catalog entries for reserved files that should
509 * not be listed in namespace, update the index accordingly.
510 */
511 if (ce_list->skipentries) {
512 index += ce_list->skipentries;
513 ce_list->skipentries = 0;
514 }
9bccf70c 515
fe8ab488
A
516 /*
517 * If there are more entries then save the last name.
518 * Key this behavior based on whether or not we observed EOFFLAG.
519 *
520 * Do not use the valence as a way to determine if we hit EOF, since
521 * it can be wrong. Use the catalog's output only.
522 */
523 if ((*(eofflag) == 0) && lastdescp != NULL) {
2d21ac55
A
524
525 /* Remember last entry */
526 if ((dirhint->dh_desc.cd_flags & CD_HASBUF) &&
527 (dirhint->dh_desc.cd_nameptr != NULL)) {
528 dirhint->dh_desc.cd_flags &= ~CD_HASBUF;
529 vfs_removename((const char *)dirhint->dh_desc.cd_nameptr);
530 }
531 dirhint->dh_desc.cd_namelen = lastdescp->cd_namelen;
532 dirhint->dh_desc.cd_nameptr = (const u_int8_t *)
533 vfs_addname((const char *)lastdescp->cd_nameptr, lastdescp->cd_namelen, 0, 0);
534 dirhint->dh_desc.cd_flags |= CD_HASBUF;
535 dirhint->dh_index = index - 1;
536 dirhint->dh_desc.cd_cnid = lastdescp->cd_cnid;
537 dirhint->dh_desc.cd_hint = lastdescp->cd_hint;
538 dirhint->dh_desc.cd_encoding = lastdescp->cd_encoding;
fe8ab488
A
539 }
540
2d21ac55
A
541 /* All done with the catalog descriptors. */
542 for (i = 0; i < (int)ce_list->realentries; ++i)
543 cat_releasedesc(&ce_list->entry[i].ce_desc);
544 ce_list->realentries = 0;
545
39236c6e 546 (void) hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
2d21ac55 547 dcp = VTOC(dvp);
9bccf70c 548
2d21ac55
A
549exit1:
550 /* Pack directory index and tag into uio_offset. */
91447636
A
551 while (tag == 0) tag = (++dcp->c_dirhinttag) << HFS_INDEX_BITS;
552 uio_setoffset(uio, index | tag);
553 dirhint->dh_index |= tag;
9bccf70c 554
2d21ac55 555exit2:
fe8ab488
A
556 if (newstate)
557 *newstate = dirchg;
2d21ac55 558
fe8ab488
A
559 /*
560 * Drop directory hint on error or if there are no more entries,
561 * only if EOF was seen.
562 */
2d21ac55 563 if (dirhint) {
fe8ab488 564 if ((error != 0) || *(eofflag))
2d21ac55
A
565 hfs_reldirhint(dcp, dirhint);
566 else
567 hfs_insertdirhint(dcp, dirhint);
91447636 568 }
fe8ab488
A
569 if (namebuf) {
570 FREE(namebuf, M_TEMP);
571 vap->va_name = NULL;
572 }
9bccf70c
A
573 if (attrbufptr)
574 FREE(attrbufptr, M_TEMP);
575 if (ce_list)
576 FREE(ce_list, M_TEMP);
2d21ac55 577
fe8ab488
A
578 if (vap && *actualcount && error)
579 error = 0;
580
91447636 581 hfs_unlock(dcp);
9bccf70c
A
582 return (error);
583}
584
585
586/*==================== Attribute list support routines ====================*/
587
588/*
589 * Pack cnode attributes into an attribute block.
590 */
6d2010ae 591__private_extern__
9bccf70c
A
592void
593hfs_packattrblk(struct attrblock *abp,
594 struct hfsmount *hfsmp,
595 struct vnode *vp,
596 struct cat_desc *descp,
597 struct cat_attr *attrp,
598 struct cat_fork *datafork,
55e303ae 599 struct cat_fork *rsrcfork,
b0d623f7 600 struct vfs_context *ctx)
2d21ac55
A
601{
602 struct attrlist *attrlistp = abp->ab_attrlist;
91447636 603
2d21ac55 604 if (attrlistp->commonattr)
b0d623f7 605 packcommonattr(abp, hfsmp, vp, descp, attrp, ctx);
9bccf70c 606
2d21ac55
A
607 if (attrlistp->dirattr && S_ISDIR(attrp->ca_mode))
608 packdirattr(abp, hfsmp, vp, descp,attrp);
609
610 if (attrlistp->fileattr && !S_ISDIR(attrp->ca_mode))
b0d623f7 611 packfileattr(abp, hfsmp, attrp, datafork, rsrcfork, vp);
9bccf70c
A
612}
613
614
2d21ac55
A
615static char*
616mountpointname(struct mount *mp)
9bccf70c 617{
2d21ac55
A
618 size_t namelength = strlen(mp->mnt_vfsstat.f_mntonname);
619 int foundchars = 0;
620 char *c;
621
622 if (namelength == 0)
623 return (NULL);
624
625 /*
626 * Look backwards through the name string, looking for
627 * the first slash encountered (which must precede the
628 * last part of the pathname).
629 */
630 for (c = mp->mnt_vfsstat.f_mntonname + namelength - 1;
631 namelength > 0; --c, --namelength) {
632 if (*c != '/') {
633 foundchars = 1;
634 } else if (foundchars) {
635 return (c + 1);
636 }
637 }
638
639 return (mp->mnt_vfsstat.f_mntonname);
640}
9bccf70c 641
9bccf70c 642
2d21ac55
A
643static void
644packnameattr(
645 struct attrblock *abp,
646 struct vnode *vp,
647 const u_int8_t *name,
648 int namelen)
649{
650 void *varbufptr;
651 struct attrreference * attr_refptr;
652 char *mpname;
653 size_t mpnamelen;
654 u_int32_t attrlength;
655 u_int8_t empty = 0;
9bccf70c 656
2d21ac55
A
657 /* A cnode's name may be incorrect for the root of a mounted
658 * filesystem (it can be mounted on a different directory name
659 * than the name of the volume, such as "blah-1"). So for the
660 * root directory, it's best to return the last element of the
661 location where the volume's mounted:
662 */
663 if ((vp != NULL) && vnode_isvroot(vp) &&
664 (mpname = mountpointname(vnode_mount(vp)))) {
665 mpnamelen = strlen(mpname);
666
667 /* Trim off any trailing slashes: */
668 while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/'))
669 --mpnamelen;
670
671 /* If there's anything left, use it instead of the volume's name */
672 if (mpnamelen > 0) {
673 name = (u_int8_t *)mpname;
674 namelen = mpnamelen;
9bccf70c 675 }
9bccf70c 676 }
2d21ac55
A
677 if (name == NULL) {
678 name = &empty;
679 namelen = 0;
9bccf70c 680 }
2d21ac55
A
681
682 varbufptr = *abp->ab_varbufpp;
683 attr_refptr = (struct attrreference *)(*abp->ab_attrbufpp);
684
685 attrlength = namelen + 1;
686 attr_refptr->attr_dataoffset = (char *)varbufptr - (char *)attr_refptr;
687 attr_refptr->attr_length = attrlength;
688 (void) strncpy((char *)varbufptr, (const char *) name, attrlength);
689 /*
690 * Advance beyond the space just allocated and
691 * round up to the next 4-byte boundary:
692 */
693 varbufptr = ((char *)varbufptr) + attrlength + ((4 - (attrlength & 3)) & 3);
694 ++attr_refptr;
695
696 *abp->ab_attrbufpp = attr_refptr;
9bccf70c
A
697 *abp->ab_varbufpp = varbufptr;
698}
699
9bccf70c
A
700static void
701packcommonattr(
702 struct attrblock *abp,
703 struct hfsmount *hfsmp,
704 struct vnode *vp,
705 struct cat_desc * cdp,
55e303ae 706 struct cat_attr * cap,
b0d623f7 707 struct vfs_context * ctx)
9bccf70c
A
708{
709 attrgroup_t attr = abp->ab_attrlist->commonattr;
710 struct mount *mp = HFSTOVFS(hfsmp);
711 void *attrbufptr = *abp->ab_attrbufpp;
712 void *varbufptr = *abp->ab_varbufpp;
b0d623f7 713 boolean_t is_64_bit = proc_is64bit(vfs_context_proc(ctx));
2d21ac55
A
714 uid_t cuid = 1;
715 int isroot = 0;
716
717 if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) {
b0d623f7 718 cuid = kauth_cred_getuid(vfs_context_ucred(ctx));
2d21ac55
A
719 isroot = cuid == 0;
720 }
9bccf70c
A
721
722 if (ATTR_CMN_NAME & attr) {
91447636 723 packnameattr(abp, vp, cdp->cd_nameptr, cdp->cd_namelen);
9bccf70c
A
724 attrbufptr = *abp->ab_attrbufpp;
725 varbufptr = *abp->ab_varbufpp;
726 }
727 if (ATTR_CMN_DEVID & attr) {
2d21ac55
A
728 *((dev_t *)attrbufptr) = hfsmp->hfs_raw_dev;
729 attrbufptr = ((dev_t *)attrbufptr) + 1;
9bccf70c
A
730 }
731 if (ATTR_CMN_FSID & attr) {
91447636
A
732 fsid_t fsid;
733
39236c6e
A
734 fsid.val[0] = hfsmp->hfs_raw_dev;
735 fsid.val[1] = vfs_typenum(mp);
91447636 736 *((fsid_t *)attrbufptr) = fsid;
2d21ac55 737 attrbufptr = ((fsid_t *)attrbufptr) + 1;
9bccf70c
A
738 }
739 if (ATTR_CMN_OBJTYPE & attr) {
2d21ac55
A
740 *((fsobj_type_t *)attrbufptr) = IFTOVT(cap->ca_mode);
741 attrbufptr = ((fsobj_type_t *)attrbufptr) + 1;
9bccf70c
A
742 }
743 if (ATTR_CMN_OBJTAG & attr) {
2d21ac55
A
744 *((fsobj_tag_t *)attrbufptr) = VT_HFS;
745 attrbufptr = ((fsobj_tag_t *)attrbufptr) + 1;
9bccf70c
A
746 }
747 /*
748 * Exporting file IDs from HFS Plus:
749 *
750 * For "normal" files the c_fileid is the same value as the
751 * c_cnid. But for hard link files, they are different - the
752 * c_cnid belongs to the active directory entry (ie the link)
753 * and the c_fileid is for the actual inode (ie the data file).
754 *
755 * The stat call (getattr) will always return the c_fileid
756 * and Carbon APIs, which are hardlink-ignorant, will always
757 * receive the c_cnid (from getattrlist).
758 */
2d21ac55 759 if (ATTR_CMN_OBJID & attr) {
9bccf70c
A
760 ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid;
761 ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
2d21ac55 762 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
9bccf70c
A
763 }
764 if (ATTR_CMN_OBJPERMANENTID & attr) {
765 ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid;
766 ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
2d21ac55 767 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
9bccf70c
A
768 }
769 if (ATTR_CMN_PAROBJID & attr) {
770 ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_parentcnid;
771 ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
2d21ac55 772 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
9bccf70c
A
773 }
774 if (ATTR_CMN_SCRIPT & attr) {
2d21ac55
A
775 *((text_encoding_t *)attrbufptr) = cdp->cd_encoding;
776 attrbufptr = ((text_encoding_t *)attrbufptr) + 1;
9bccf70c
A
777 }
778 if (ATTR_CMN_CRTIME & attr) {
91447636 779 if (is_64_bit) {
b0d623f7
A
780 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_itime;
781 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
782 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
91447636
A
783 }
784 else {
b0d623f7
A
785 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_itime;
786 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
787 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
91447636 788 }
9bccf70c
A
789 }
790 if (ATTR_CMN_MODTIME & attr) {
91447636 791 if (is_64_bit) {
b0d623f7
A
792 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_mtime;
793 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
794 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
91447636
A
795 }
796 else {
b0d623f7
A
797 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_mtime;
798 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
799 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
91447636 800 }
9bccf70c
A
801 }
802 if (ATTR_CMN_CHGTIME & attr) {
91447636 803 if (is_64_bit) {
b0d623f7
A
804 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_ctime;
805 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
806 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
91447636
A
807 }
808 else {
b0d623f7
A
809 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_ctime;
810 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
811 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
91447636 812 }
9bccf70c
A
813 }
814 if (ATTR_CMN_ACCTIME & attr) {
91447636 815 if (is_64_bit) {
b0d623f7
A
816 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_atime;
817 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
818 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
91447636
A
819 }
820 else {
b0d623f7
A
821 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_atime;
822 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
823 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
91447636 824 }
9bccf70c
A
825 }
826 if (ATTR_CMN_BKUPTIME & attr) {
91447636 827 if (is_64_bit) {
b0d623f7
A
828 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_btime;
829 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
830 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
91447636
A
831 }
832 else {
b0d623f7
A
833 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_btime;
834 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
835 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
91447636 836 }
9bccf70c
A
837 }
838 if (ATTR_CMN_FNDRINFO & attr) {
6d2010ae 839 u_int8_t *finfo = NULL;
9bccf70c 840 bcopy(&cap->ca_finderinfo, attrbufptr, sizeof(u_int8_t) * 32);
6d2010ae
A
841 finfo = (u_int8_t*)attrbufptr;
842
2d21ac55
A
843 /* Don't expose a symlink's private type/creator. */
844 if (S_ISLNK(cap->ca_mode)) {
845 struct FndrFileInfo *fip;
846
847 fip = (struct FndrFileInfo *)attrbufptr;
848 fip->fdType = 0;
849 fip->fdCreator = 0;
850 }
6d2010ae
A
851
852 /* advance 16 bytes into the attrbuf */
853 finfo = finfo + 16;
39236c6e
A
854
855 /* also don't expose the date_added or write_gen_counter fields */
856 if (S_ISREG(cap->ca_mode) || S_ISLNK(cap->ca_mode)) {
6d2010ae 857 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
22ba694c 858 extinfo->document_id = 0;
6d2010ae 859 extinfo->date_added = 0;
39236c6e 860 extinfo->write_gen_counter = 0;
6d2010ae
A
861 }
862 else if (S_ISDIR(cap->ca_mode)) {
863 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
22ba694c 864 extinfo->document_id = 0;
6d2010ae 865 extinfo->date_added = 0;
22ba694c 866 extinfo->write_gen_counter = 0;
6d2010ae
A
867 }
868
2d21ac55 869 attrbufptr = (char *)attrbufptr + sizeof(u_int8_t) * 32;
9bccf70c
A
870 }
871 if (ATTR_CMN_OWNERID & attr) {
2d21ac55
A
872 uid_t nuid = cap->ca_uid;
873
874 if (!isroot) {
875 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS)
876 nuid = cuid;
877 else if (nuid == UNKNOWNUID)
878 nuid = cuid;
879 }
880
881 *((uid_t *)attrbufptr) = nuid;
882 attrbufptr = ((uid_t *)attrbufptr) + 1;
9bccf70c
A
883 }
884 if (ATTR_CMN_GRPID & attr) {
2d21ac55
A
885 gid_t ngid = cap->ca_gid;
886
887 if (!isroot) {
b0d623f7 888 gid_t cgid = kauth_cred_getgid(vfs_context_ucred(ctx));
2d21ac55
A
889 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS)
890 ngid = cgid;
891 else if (ngid == UNKNOWNUID)
892 ngid = cgid;
893 }
894
895 *((gid_t *)attrbufptr) = ngid;
896 attrbufptr = ((gid_t *)attrbufptr) + 1;
9bccf70c
A
897 }
898 if (ATTR_CMN_ACCESSMASK & attr) {
899 /*
900 * [2856576] Since we are dynamically changing the owner, also
901 * effectively turn off the set-user-id and set-group-id bits,
902 * just like chmod(2) would when changing ownership. This prevents
903 * a security hole where set-user-id programs run as whoever is
904 * logged on (or root if nobody is logged in yet!)
905 */
2d21ac55
A
906 *((u_int32_t *)attrbufptr) = (cap->ca_uid == UNKNOWNUID) ?
907 cap->ca_mode & ~(S_ISUID | S_ISGID) : cap->ca_mode;
908 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
909 }
910 if (ATTR_CMN_FLAGS & attr) {
2d21ac55
A
911 *((u_int32_t *)attrbufptr) = cap->ca_flags;
912 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
913 }
914 if (ATTR_CMN_USERACCESS & attr) {
2d21ac55
A
915 u_int32_t user_access;
916
917 /* Take the long path when we have an ACL */
918 if ((vp != NULLVP) && (cap->ca_recflags & kHFSHasSecurityMask)) {
919 user_access = hfs_real_user_access(vp, abp->ab_context);
920 } else {
921 user_access = DerivePermissionSummary(cap->ca_uid, cap->ca_gid,
39236c6e 922 cap->ca_mode, mp, vfs_context_ucred(ctx), 0);
2d21ac55
A
923 }
924 /* Also consider READ-ONLY file system. */
925 if (vfs_flags(mp) & MNT_RDONLY) {
926 user_access &= ~W_OK;
927 }
928 /* Locked objects are not writable either */
929 if ((cap->ca_flags & UF_IMMUTABLE) && (vfs_context_suser(abp->ab_context) != 0))
930 user_access &= ~W_OK;
931 if ((cap->ca_flags & SF_IMMUTABLE) && (vfs_context_suser(abp->ab_context) == 0))
932 user_access &= ~W_OK;
933
934 *((u_int32_t *)attrbufptr) = user_access;
935 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
936 }
937 if (ATTR_CMN_FILEID & attr) {
938 *((u_int64_t *)attrbufptr) = cap->ca_fileid;
939 attrbufptr = ((u_int64_t *)attrbufptr) + 1;
940 }
941 if (ATTR_CMN_PARENTID & attr) {
942 *((u_int64_t *)attrbufptr) = cdp->cd_parentcnid;
943 attrbufptr = ((u_int64_t *)attrbufptr) + 1;
9bccf70c
A
944 }
945
946 *abp->ab_attrbufpp = attrbufptr;
947 *abp->ab_varbufpp = varbufptr;
948}
949
950static void
951packdirattr(
952 struct attrblock *abp,
953 struct hfsmount *hfsmp,
954 struct vnode *vp,
955 struct cat_desc * descp,
91447636 956 struct cat_attr * cattrp)
9bccf70c
A
957{
958 attrgroup_t attr = abp->ab_attrlist->dirattr;
959 void *attrbufptr = *abp->ab_attrbufpp;
2d21ac55
A
960 u_int32_t entries;
961
962 /*
963 * The DIR_LINKCOUNT is the count of real directory hard links.
964 * (i.e. its not the sum of the implied "." and ".." references
965 * typically used in stat's st_nlink field)
966 */
967 if (ATTR_DIR_LINKCOUNT & attr) {
968 *((u_int32_t *)attrbufptr) = cattrp->ca_linkcount;
969 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
970 }
9bccf70c 971 if (ATTR_DIR_ENTRYCOUNT & attr) {
2d21ac55 972 entries = cattrp->ca_entries;
9bccf70c 973
91447636 974 if (descp->cd_parentcnid == kHFSRootParentID) {
2d21ac55
A
975 if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0)
976 --entries; /* hide private dir */
977 if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0)
b4c24cb9 978 --entries; /* hide private dir */
2d21ac55
A
979 if (hfsmp->jnl ||
980 ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) &&
981 (hfsmp->hfs_flags & HFS_READ_ONLY)))
b4c24cb9
A
982 entries -= 2; /* hide the journal files */
983 }
9bccf70c 984
2d21ac55
A
985 *((u_int32_t *)attrbufptr) = entries;
986 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
987 }
988 if (ATTR_DIR_MOUNTSTATUS & attr) {
91447636 989 if (vp != NULL && vnode_mountedhere(vp) != NULL)
2d21ac55 990 *((u_int32_t *)attrbufptr) = DIR_MNTSTATUS_MNTPOINT;
9bccf70c 991 else
2d21ac55
A
992 *((u_int32_t *)attrbufptr) = 0;
993 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
994 }
995 *abp->ab_attrbufpp = attrbufptr;
996}
997
998static void
999packfileattr(
1000 struct attrblock *abp,
1001 struct hfsmount *hfsmp,
1002 struct cat_attr *cattrp,
1003 struct cat_fork *datafork,
b0d623f7
A
1004 struct cat_fork *rsrcfork,
1005 struct vnode *vp)
9bccf70c 1006{
b0d623f7
A
1007#if !HFS_COMPRESSION
1008#pragma unused(vp)
1009#endif
9bccf70c
A
1010 attrgroup_t attr = abp->ab_attrlist->fileattr;
1011 void *attrbufptr = *abp->ab_attrbufpp;
1012 void *varbufptr = *abp->ab_varbufpp;
2d21ac55 1013 u_int32_t allocblksize;
9bccf70c
A
1014
1015 allocblksize = HFSTOVCB(hfsmp)->blockSize;
1016
b0d623f7
A
1017 off_t datasize = datafork->cf_size;
1018 off_t totalsize = datasize + rsrcfork->cf_size;
1019#if HFS_COMPRESSION
6d2010ae
A
1020 int handle_compressed;
1021 handle_compressed = (cattrp->ca_flags & UF_COMPRESSED);// && hfs_file_is_compressed(VTOC(vp), 1);
1022
1023 if (handle_compressed) {
b0d623f7
A
1024 if (attr & (ATTR_FILE_DATALENGTH|ATTR_FILE_TOTALSIZE)) {
1025 if ( 0 == hfs_uncompressed_size_of_compressed_file(hfsmp, vp, cattrp->ca_fileid, &datasize, 1) ) { /* 1 == don't take the cnode lock */
1026 /* total size of a compressed file is just the data size */
1027 totalsize = datasize;
1028 }
1029 }
1030 }
1031#endif
1032
9bccf70c 1033 if (ATTR_FILE_LINKCOUNT & attr) {
2d21ac55
A
1034 *((u_int32_t *)attrbufptr) = cattrp->ca_linkcount;
1035 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
1036 }
1037 if (ATTR_FILE_TOTALSIZE & attr) {
b0d623f7 1038 *((off_t *)attrbufptr) = totalsize;
2d21ac55 1039 attrbufptr = ((off_t *)attrbufptr) + 1;
9bccf70c
A
1040 }
1041 if (ATTR_FILE_ALLOCSIZE & attr) {
2d21ac55 1042 *((off_t *)attrbufptr) =
9bccf70c 1043 (off_t)cattrp->ca_blocks * (off_t)allocblksize;
2d21ac55 1044 attrbufptr = ((off_t *)attrbufptr) + 1;
9bccf70c
A
1045 }
1046 if (ATTR_FILE_IOBLOCKSIZE & attr) {
2d21ac55
A
1047 *((u_int32_t *)attrbufptr) = hfsmp->hfs_logBlockSize;
1048 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
1049 }
1050 if (ATTR_FILE_CLUMPSIZE & attr) {
2d21ac55
A
1051 *((u_int32_t *)attrbufptr) = hfsmp->vcbClpSiz;
1052 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
1053 }
1054 if (ATTR_FILE_DEVTYPE & attr) {
1055 if (S_ISBLK(cattrp->ca_mode) || S_ISCHR(cattrp->ca_mode))
2d21ac55 1056 *((u_int32_t *)attrbufptr) = (u_int32_t)cattrp->ca_rdev;
9bccf70c 1057 else
2d21ac55
A
1058 *((u_int32_t *)attrbufptr) = 0;
1059 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c 1060 }
b0d623f7 1061
9bccf70c 1062 if (ATTR_FILE_DATALENGTH & attr) {
b0d623f7 1063 *((off_t *)attrbufptr) = datasize;
2d21ac55 1064 attrbufptr = ((off_t *)attrbufptr) + 1;
9bccf70c 1065 }
b0d623f7
A
1066
1067#if HFS_COMPRESSION
1068 /* fake the data fork size on a decmpfs compressed file to reflect the
1069 * uncompressed size. This ensures proper reading and copying of these files.
1070 * NOTE: we may need to get the vnode here because the vnode parameter
1071 * passed by hfs_vnop_readdirattr() may be null.
1072 */
1073
6d2010ae 1074 if ( handle_compressed ) {
b0d623f7
A
1075 if (attr & ATTR_FILE_DATAALLOCSIZE) {
1076 *((off_t *)attrbufptr) = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize;
1077 attrbufptr = ((off_t *)attrbufptr) + 1;
1078 }
1079 if (attr & ATTR_FILE_RSRCLENGTH) {
1080 *((off_t *)attrbufptr) = 0;
1081 attrbufptr = ((off_t *)attrbufptr) + 1;
1082 }
1083 if (attr & ATTR_FILE_RSRCALLOCSIZE) {
1084 *((off_t *)attrbufptr) = 0;
1085 attrbufptr = ((off_t *)attrbufptr) + 1;
1086 }
9bccf70c 1087 }
b0d623f7
A
1088 else
1089#endif
1090 {
1091 if (ATTR_FILE_DATAALLOCSIZE & attr) {
1092 *((off_t *)attrbufptr) = (off_t)datafork->cf_blocks * (off_t)allocblksize;
1093 attrbufptr = ((off_t *)attrbufptr) + 1;
1094 }
1095 if (ATTR_FILE_RSRCLENGTH & attr) {
1096 *((off_t *)attrbufptr) = rsrcfork->cf_size;
1097 attrbufptr = ((off_t *)attrbufptr) + 1;
1098 }
1099 if (ATTR_FILE_RSRCALLOCSIZE & attr) {
1100 *((off_t *)attrbufptr) = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize;
1101 attrbufptr = ((off_t *)attrbufptr) + 1;
1102 }
9bccf70c
A
1103 }
1104 *abp->ab_attrbufpp = attrbufptr;
1105 *abp->ab_varbufpp = varbufptr;
1106}
1107
9bccf70c
A
1108/*
1109 * Calculate the total size of an attribute block.
1110 */
6d2010ae 1111__private_extern__
9bccf70c
A
1112int
1113hfs_attrblksize(struct attrlist *attrlist)
1114{
1115 int size;
1116 attrgroup_t a;
91447636
A
1117 int sizeof_timespec;
1118 boolean_t is_64_bit = proc_is64bit(current_proc());
9bccf70c 1119
91447636 1120 if (is_64_bit)
b0d623f7 1121 sizeof_timespec = sizeof(struct user64_timespec);
91447636 1122 else
b0d623f7 1123 sizeof_timespec = sizeof(struct user32_timespec);
91447636 1124
9bccf70c
A
1125 DBG_ASSERT((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0);
1126
9bccf70c
A
1127 DBG_ASSERT((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0);
1128
9bccf70c
A
1129 DBG_ASSERT((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0);
1130
9bccf70c
A
1131 DBG_ASSERT((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0);
1132
9bccf70c
A
1133 DBG_ASSERT((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0);
1134
1135 size = 0;
1136
1137 if ((a = attrlist->commonattr) != 0) {
2d21ac55 1138 if (a & ATTR_CMN_NAME) size += sizeof(struct attrreference);
9bccf70c
A
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);
2d21ac55
A
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;
9bccf70c
A
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);
2d21ac55
A
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 }
9bccf70c 1161 if ((a = attrlist->dirattr) != 0) {
2d21ac55
A
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 }
9bccf70c 1166 if ((a = attrlist->fileattr) != 0) {
2d21ac55 1167 if (a & ATTR_FILE_LINKCOUNT) size += sizeof(u_int32_t);
9bccf70c
A
1168 if (a & ATTR_FILE_TOTALSIZE) size += sizeof(off_t);
1169 if (a & ATTR_FILE_ALLOCSIZE) size += sizeof(off_t);
2d21ac55
A
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);
9bccf70c
A
1173 if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t);
1174 if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t);
9bccf70c
A
1175 if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t);
1176 if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t);
2d21ac55 1177 }
9bccf70c 1178
2d21ac55 1179 return (size);
9bccf70c
A
1180}
1181
2d21ac55
A
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 */
1200static u_int32_t
1201hfs_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
9bccf70c 1223
b0d623f7 1224u_int32_t
9bccf70c 1225DerivePermissionSummary(uid_t obj_uid, gid_t obj_gid, mode_t obj_mode,
2d21ac55 1226 struct mount *mp, kauth_cred_t cred, __unused struct proc *p)
9bccf70c 1227{
b0d623f7 1228 u_int32_t permissions;
9bccf70c
A
1229
1230 if (obj_uid == UNKNOWNUID)
2d21ac55 1231 obj_uid = kauth_cred_getuid(cred);
9bccf70c
A
1232
1233 /* User id 0 (root) always gets access. */
91447636 1234 if (!suser(cred, NULL)) {
9bccf70c
A
1235 permissions = R_OK | W_OK | X_OK;
1236 goto Exit;
1237 };
1238
1239 /* Otherwise, check the owner. */
2d21ac55 1240 if (hfs_owner_rights(VFSTOHFS(mp), obj_uid, cred, NULL, false) == 0) {
b0d623f7 1241 permissions = ((u_int32_t)obj_mode & S_IRWXU) >> 6;
9bccf70c
A
1242 goto Exit;
1243 }
1244
1245 /* Otherwise, check the groups. */
91447636
A
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) {
b0d623f7 1250 permissions = ((u_int32_t)obj_mode & S_IRWXG) >> 3;
91447636 1251 goto Exit;
9bccf70c
A
1252 }
1253 }
1254
1255 /* Otherwise, settle for 'others' access. */
b0d623f7 1256 permissions = (u_int32_t)obj_mode & S_IRWXO;
9bccf70c
A
1257
1258Exit:
1259 return (permissions);
1260}
1261
fe8ab488
A
1262
1263/*
1264 * ===========================================================================
1265 * Support functions for filling up a vnode_attr structure based on attributes
1266 * requested.
1267 * ===========================================================================
1268 */
1269void
1270get_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
1288static void
1289copy_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
1332static void
1333vattr_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
1590static void
1591vattr_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
1637static void
1638vattr_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}