]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_attrlist.c
xnu-2422.100.13.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
A
73
74/*
75 * readdirattr operation will return attributes for the items in the
76 * directory specified.
77 *
78 * It does not do . and .. entries. The problem is if you are at the root of the
79 * hfs directory and go to .. you could be crossing a mountpoint into a
80 * different (ufs) file system. The attributes that apply for it may not
81 * apply for the file system you are doing the readdirattr on. To make life
82 * simpler, this call will only return entries in its directory, hfs like.
9bccf70c 83 */
9bccf70c 84int
91447636
A
85hfs_vnop_readdirattr(ap)
86 struct vnop_readdirattr_args /* {
9bccf70c
A
87 struct vnode *a_vp;
88 struct attrlist *a_alist;
89 struct uio *a_uio;
90 u_long a_maxcount;
91 u_long a_options;
92 u_long *a_newstate;
93 int *a_eofflag;
94 u_long *a_actualcount;
91447636 95 vfs_context_t a_context;
9bccf70c
A
96 } */ *ap;
97{
98 struct vnode *dvp = ap->a_vp;
91447636
A
99 struct cnode *dcp;
100 struct hfsmount * hfsmp;
9bccf70c 101 struct attrlist *alist = ap->a_alist;
91447636 102 uio_t uio = ap->a_uio;
9bccf70c 103 int maxcount = ap->a_maxcount;
2d21ac55
A
104 u_int32_t fixedblocksize;
105 u_int32_t maxattrblocksize;
106 u_int32_t currattrbufsize;
9bccf70c
A
107 void *attrbufptr = NULL;
108 void *attrptr;
109 void *varptr;
110 struct attrblock attrblk;
111 int error = 0;
2d21ac55
A
112 int index = 0;
113 int i, dir_entries = 0;
9bccf70c 114 struct cat_desc *lastdescp = NULL;
9bccf70c 115 struct cat_entrylist *ce_list = NULL;
91447636
A
116 directoryhint_t *dirhint = NULL;
117 unsigned int tag;
2d21ac55
A
118 int maxentries;
119 int lockflags;
b0d623f7 120 u_int32_t dirchg = 0;
b4c24cb9 121
9bccf70c
A
122 *(ap->a_actualcount) = 0;
123 *(ap->a_eofflag) = 0;
9bccf70c
A
124
125 /* Check for invalid options and buffer space. */
2d21ac55
A
126 if (((ap->a_options & ~(FSOPT_NOINMEMUPDATE | FSOPT_NOFOLLOW)) != 0) ||
127 (uio_resid(uio) <= 0) || (uio_iovcnt(uio) > 1) || (maxcount <= 0)) {
9bccf70c 128 return (EINVAL);
2d21ac55
A
129 }
130 /*
131 * Reject requests for unsupported attributes.
132 */
9bccf70c 133 if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
2d21ac55 134 (alist->commonattr & ~HFS_ATTR_CMN_VALID) ||
9bccf70c 135 (alist->volattr != 0) ||
2d21ac55
A
136 (alist->dirattr & ~HFS_ATTR_DIR_VALID) ||
137 (alist->fileattr & ~HFS_ATTR_FILE_VALID) ||
138 (alist->forkattr != 0)) {
9bccf70c 139 return (EINVAL);
2d21ac55 140 }
6d2010ae 141
316670eb 142 if (VTOC(dvp)->c_bsdflags & UF_COMPRESSED) {
6d2010ae
A
143 int compressed = hfs_file_is_compressed(VTOC(dvp), 0); /* 0 == take the cnode lock */
144
145 if (!compressed) {
146 error = check_for_dataless_file(dvp, NAMESPACE_HANDLER_READ_OP);
147 if (error) {
148 return error;
149 }
150 }
151 }
152
153
2d21ac55
A
154 /*
155 * Take an exclusive directory lock since we manipulate the directory hints
156 */
39236c6e 157 if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
91447636 158 return (error);
2d21ac55 159 }
91447636
A
160 dcp = VTOC(dvp);
161 hfsmp = VTOHFS(dvp);
162
91447636 163 dir_entries = dcp->c_entries;
b0d623f7 164 dirchg = dcp->c_dirchangecnt;
9bccf70c 165
2d21ac55 166 /* Extract directory index and tag (sequence number) from uio_offset */
91447636
A
167 index = uio_offset(uio) & HFS_INDEX_MASK;
168 tag = uio_offset(uio) & ~HFS_INDEX_MASK;
b4c24cb9 169 if ((index + 1) > dir_entries) {
9bccf70c
A
170 *(ap->a_eofflag) = 1;
171 error = 0;
2d21ac55 172 goto exit2;
9bccf70c
A
173 }
174
175 /* Get a buffer to hold packed attributes. */
2d21ac55 176 fixedblocksize = (sizeof(u_int32_t) + hfs_attrblksize(alist)); /* 4 bytes for length */
9bccf70c
A
177 maxattrblocksize = fixedblocksize;
178 if (alist->commonattr & ATTR_CMN_NAME)
179 maxattrblocksize += kHFSPlusMaxFileNameBytes + 1;
180 MALLOC(attrbufptr, void *, maxattrblocksize, M_TEMP, M_WAITOK);
b0d623f7
A
181 if (attrbufptr == NULL) {
182 error = ENOMEM;
183 goto exit2;
184 }
9bccf70c
A
185 attrptr = attrbufptr;
186 varptr = (char *)attrbufptr + fixedblocksize; /* Point to variable-length storage */
187
2d21ac55
A
188 /* Get a detached directory hint (cnode must be locked exclusive) */
189 dirhint = hfs_getdirhint(dcp, ((index - 1) & HFS_INDEX_MASK) | tag, TRUE);
91447636
A
190
191 /* Hide tag from catalog layer. */
192 dirhint->dh_index &= HFS_INDEX_MASK;
193 if (dirhint->dh_index == HFS_INDEX_MASK) {
194 dirhint->dh_index = -1;
195 }
196
197 /*
2d21ac55
A
198 * Obtain a list of catalog entries and pack their attributes until
199 * the output buffer is full or maxcount entries have been packed.
200 */
201
202 /*
203 * Constrain our list size.
91447636 204 */
2d21ac55 205 maxentries = uio_resid(uio) / (fixedblocksize + HFS_AVERAGE_NAME_SIZE);
2d21ac55
A
206 maxentries = min(maxentries, maxcount);
207 maxentries = min(maxentries, MAXCATENTRIES);
208 if (maxentries < 1) {
209 error = EINVAL;
210 goto exit2;
91447636 211 }
2d21ac55
A
212
213 /* Initialize a catalog entry list. */
214 MALLOC(ce_list, struct cat_entrylist *, CE_LIST_SIZE(maxentries), M_TEMP, M_WAITOK);
b0d623f7
A
215 if (ce_list == NULL) {
216 error = ENOMEM;
217 goto exit2;
218 }
2d21ac55
A
219 bzero(ce_list, CE_LIST_SIZE(maxentries));
220 ce_list->maxentries = maxentries;
221
9bccf70c 222 /*
2d21ac55 223 * Populate the ce_list from the catalog file.
9bccf70c 224 */
2d21ac55
A
225 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
226
227 error = cat_getentriesattr(hfsmp, dirhint, ce_list);
228 /* Don't forget to release the descriptors later! */
229
230 hfs_systemfile_unlock(hfsmp, lockflags);
231
232 if (error == ENOENT) {
233 *(ap->a_eofflag) = TRUE;
234 error = 0;
235 }
236 if (error) {
237 goto exit1;
238 }
239
240 /*
241 * Drop the directory lock so we don't deadlock when we:
242 * - acquire a child cnode lock
243 * - make calls to vnode_authorize()
244 * - make calls to kauth_cred_ismember_gid()
245 */
246 hfs_unlock(dcp);
247 dcp = NULL;
248
249 /* Process the catalog entries. */
250 for (i = 0; i < (int)ce_list->realentries; ++i) {
251 struct cnode *cp = NULL;
252 struct vnode *vp = NULL;
253 struct cat_desc * cdescp;
254 struct cat_attr * cattrp;
255 struct cat_fork c_datafork;
256 struct cat_fork c_rsrcfork;
257
258 bzero(&c_datafork, sizeof(c_datafork));
259 bzero(&c_rsrcfork, sizeof(c_rsrcfork));
260 cdescp = &ce_list->entry[i].ce_desc;
261 cattrp = &ce_list->entry[i].ce_attr;
262 c_datafork.cf_size = ce_list->entry[i].ce_datasize;
263 c_datafork.cf_blocks = ce_list->entry[i].ce_datablks;
264 c_rsrcfork.cf_size = ce_list->entry[i].ce_rsrcsize;
265 c_rsrcfork.cf_blocks = ce_list->entry[i].ce_rsrcblks;
266
267 if ((alist->commonattr & ATTR_CMN_USERACCESS) &&
268 (cattrp->ca_recflags & kHFSHasSecurityMask)) {
9bccf70c 269 /*
2d21ac55 270 * Obtain vnode for our vnode_authorize() calls.
9bccf70c 271 */
6d2010ae 272 if (hfs_vget(hfsmp, cattrp->ca_fileid, &vp, 0, 0) != 0) {
9bccf70c 273 vp = NULL;
9bccf70c 274 }
2d21ac55
A
275 } else if (!(ap->a_options & FSOPT_NOINMEMUPDATE)) {
276 /* Get in-memory cnode data (if any). */
6d2010ae 277 vp = hfs_chash_getvnode(hfsmp, cattrp->ca_fileid, 0, 0, 0);
2d21ac55
A
278 }
279 if (vp != NULL) {
280 cp = VTOC(vp);
281 /* Only use cnode's decriptor for non-hardlinks */
282 if (!(cp->c_flag & C_HARDLINK))
283 cdescp = &cp->c_desc;
284 cattrp = &cp->c_attr;
285 if (cp->c_datafork) {
286 c_datafork.cf_size = cp->c_datafork->ff_size;
287 c_datafork.cf_blocks = cp->c_datafork->ff_blocks;
288 }
289 if (cp->c_rsrcfork) {
290 c_rsrcfork.cf_size = cp->c_rsrcfork->ff_size;
291 c_rsrcfork.cf_blocks = cp->c_rsrcfork->ff_blocks;
9bccf70c 292 }
2d21ac55
A
293 /* All done with cnode. */
294 hfs_unlock(cp);
295 cp = NULL;
296 }
9bccf70c 297
2d21ac55
A
298 *((u_int32_t *)attrptr) = 0;
299 attrptr = ((u_int32_t *)attrptr) + 1;
300 attrblk.ab_attrlist = alist;
301 attrblk.ab_attrbufpp = &attrptr;
302 attrblk.ab_varbufpp = &varptr;
303 attrblk.ab_flags = 0;
304 attrblk.ab_blocksize = maxattrblocksize;
305 attrblk.ab_context = ap->a_context;
306
307 /* Pack catalog entries into attribute buffer. */
b0d623f7 308 hfs_packattrblk(&attrblk, hfsmp, vp, cdescp, cattrp, &c_datafork, &c_rsrcfork, ap->a_context);
2d21ac55
A
309 currattrbufsize = ((char *)varptr - (char *)attrbufptr);
310
311 /* All done with vnode. */
312 if (vp != NULL) {
313 vnode_put(vp);
314 vp = NULL;
315 }
91447636 316
2d21ac55
A
317 /* Make sure there's enough buffer space remaining. */
318 // LP64todo - fix this!
319 if (uio_resid(uio) < 0 || currattrbufsize > (u_int32_t)uio_resid(uio)) {
320 break;
321 } else {
322 *((u_int32_t *)attrbufptr) = currattrbufsize;
323 error = uiomove((caddr_t)attrbufptr, currattrbufsize, uio);
324 if (error != E_NONE) {
325 break;
326 }
327 attrptr = attrbufptr;
328 /* Point to variable-length storage */
329 varptr = (char *)attrbufptr + fixedblocksize;
330 /* Save the last valid catalog entry */
331 lastdescp = &ce_list->entry[i].ce_desc;
332 index++;
333 *ap->a_actualcount += 1;
334
335 /* Termination checks */
336 if ((--maxcount <= 0) ||
337 // LP64todo - fix this!
338 uio_resid(uio) < 0 ||
b0d623f7 339 ((u_int32_t)uio_resid(uio) < (fixedblocksize + HFS_AVERAGE_NAME_SIZE))){
2d21ac55 340 break;
9bccf70c
A
341 }
342 }
2d21ac55 343 } /* for each catalog entry */
9bccf70c 344
2d21ac55
A
345 /* For small directories, check if we're all done. */
346 if (*ap->a_actualcount == (u_long)dir_entries) {
347 *(ap->a_eofflag) = TRUE;
348 }
9bccf70c 349
2d21ac55
A
350 /* If we skipped catalog entries for reserved files that should
351 * not be listed in namespace, update the index accordingly.
352 */
353 if (ce_list->skipentries) {
354 index += ce_list->skipentries;
355 ce_list->skipentries = 0;
356 }
9bccf70c 357
2d21ac55
A
358 /* If there are more entries then save the last name. */
359 if (index < dir_entries
360 && !(*(ap->a_eofflag))
361 && lastdescp != NULL) {
362
363 /* Remember last entry */
364 if ((dirhint->dh_desc.cd_flags & CD_HASBUF) &&
365 (dirhint->dh_desc.cd_nameptr != NULL)) {
366 dirhint->dh_desc.cd_flags &= ~CD_HASBUF;
367 vfs_removename((const char *)dirhint->dh_desc.cd_nameptr);
368 }
369 dirhint->dh_desc.cd_namelen = lastdescp->cd_namelen;
370 dirhint->dh_desc.cd_nameptr = (const u_int8_t *)
371 vfs_addname((const char *)lastdescp->cd_nameptr, lastdescp->cd_namelen, 0, 0);
372 dirhint->dh_desc.cd_flags |= CD_HASBUF;
373 dirhint->dh_index = index - 1;
374 dirhint->dh_desc.cd_cnid = lastdescp->cd_cnid;
375 dirhint->dh_desc.cd_hint = lastdescp->cd_hint;
376 dirhint->dh_desc.cd_encoding = lastdescp->cd_encoding;
377 } else {
378 /* No more entries. */
379 *(ap->a_eofflag) = TRUE;
91447636 380 }
2d21ac55
A
381
382 /* All done with the catalog descriptors. */
383 for (i = 0; i < (int)ce_list->realentries; ++i)
384 cat_releasedesc(&ce_list->entry[i].ce_desc);
385 ce_list->realentries = 0;
386
39236c6e 387 (void) hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
2d21ac55 388 dcp = VTOC(dvp);
9bccf70c 389
2d21ac55
A
390exit1:
391 /* Pack directory index and tag into uio_offset. */
91447636
A
392 while (tag == 0) tag = (++dcp->c_dirhinttag) << HFS_INDEX_BITS;
393 uio_setoffset(uio, index | tag);
394 dirhint->dh_index |= tag;
9bccf70c 395
2d21ac55 396exit2:
b0d623f7 397 *ap->a_newstate = dirchg;
2d21ac55 398
91447636 399 /* Drop directory hint on error or if there are no more entries */
2d21ac55
A
400 if (dirhint) {
401 if ((error != 0) || (index >= dir_entries) || *(ap->a_eofflag))
402 hfs_reldirhint(dcp, dirhint);
403 else
404 hfs_insertdirhint(dcp, dirhint);
91447636 405 }
9bccf70c
A
406 if (attrbufptr)
407 FREE(attrbufptr, M_TEMP);
408 if (ce_list)
409 FREE(ce_list, M_TEMP);
2d21ac55 410
91447636 411 hfs_unlock(dcp);
9bccf70c
A
412 return (error);
413}
414
415
416/*==================== Attribute list support routines ====================*/
417
418/*
419 * Pack cnode attributes into an attribute block.
420 */
6d2010ae 421__private_extern__
9bccf70c
A
422void
423hfs_packattrblk(struct attrblock *abp,
424 struct hfsmount *hfsmp,
425 struct vnode *vp,
426 struct cat_desc *descp,
427 struct cat_attr *attrp,
428 struct cat_fork *datafork,
55e303ae 429 struct cat_fork *rsrcfork,
b0d623f7 430 struct vfs_context *ctx)
2d21ac55
A
431{
432 struct attrlist *attrlistp = abp->ab_attrlist;
91447636 433
2d21ac55 434 if (attrlistp->commonattr)
b0d623f7 435 packcommonattr(abp, hfsmp, vp, descp, attrp, ctx);
9bccf70c 436
2d21ac55
A
437 if (attrlistp->dirattr && S_ISDIR(attrp->ca_mode))
438 packdirattr(abp, hfsmp, vp, descp,attrp);
439
440 if (attrlistp->fileattr && !S_ISDIR(attrp->ca_mode))
b0d623f7 441 packfileattr(abp, hfsmp, attrp, datafork, rsrcfork, vp);
9bccf70c
A
442}
443
444
2d21ac55
A
445static char*
446mountpointname(struct mount *mp)
9bccf70c 447{
2d21ac55
A
448 size_t namelength = strlen(mp->mnt_vfsstat.f_mntonname);
449 int foundchars = 0;
450 char *c;
451
452 if (namelength == 0)
453 return (NULL);
454
455 /*
456 * Look backwards through the name string, looking for
457 * the first slash encountered (which must precede the
458 * last part of the pathname).
459 */
460 for (c = mp->mnt_vfsstat.f_mntonname + namelength - 1;
461 namelength > 0; --c, --namelength) {
462 if (*c != '/') {
463 foundchars = 1;
464 } else if (foundchars) {
465 return (c + 1);
466 }
467 }
468
469 return (mp->mnt_vfsstat.f_mntonname);
470}
9bccf70c 471
9bccf70c 472
2d21ac55
A
473static void
474packnameattr(
475 struct attrblock *abp,
476 struct vnode *vp,
477 const u_int8_t *name,
478 int namelen)
479{
480 void *varbufptr;
481 struct attrreference * attr_refptr;
482 char *mpname;
483 size_t mpnamelen;
484 u_int32_t attrlength;
485 u_int8_t empty = 0;
9bccf70c 486
2d21ac55
A
487 /* A cnode's name may be incorrect for the root of a mounted
488 * filesystem (it can be mounted on a different directory name
489 * than the name of the volume, such as "blah-1"). So for the
490 * root directory, it's best to return the last element of the
491 location where the volume's mounted:
492 */
493 if ((vp != NULL) && vnode_isvroot(vp) &&
494 (mpname = mountpointname(vnode_mount(vp)))) {
495 mpnamelen = strlen(mpname);
496
497 /* Trim off any trailing slashes: */
498 while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/'))
499 --mpnamelen;
500
501 /* If there's anything left, use it instead of the volume's name */
502 if (mpnamelen > 0) {
503 name = (u_int8_t *)mpname;
504 namelen = mpnamelen;
9bccf70c 505 }
9bccf70c 506 }
2d21ac55
A
507 if (name == NULL) {
508 name = &empty;
509 namelen = 0;
9bccf70c 510 }
2d21ac55
A
511
512 varbufptr = *abp->ab_varbufpp;
513 attr_refptr = (struct attrreference *)(*abp->ab_attrbufpp);
514
515 attrlength = namelen + 1;
516 attr_refptr->attr_dataoffset = (char *)varbufptr - (char *)attr_refptr;
517 attr_refptr->attr_length = attrlength;
518 (void) strncpy((char *)varbufptr, (const char *) name, attrlength);
519 /*
520 * Advance beyond the space just allocated and
521 * round up to the next 4-byte boundary:
522 */
523 varbufptr = ((char *)varbufptr) + attrlength + ((4 - (attrlength & 3)) & 3);
524 ++attr_refptr;
525
526 *abp->ab_attrbufpp = attr_refptr;
9bccf70c
A
527 *abp->ab_varbufpp = varbufptr;
528}
529
530
531static void
532packcommonattr(
533 struct attrblock *abp,
534 struct hfsmount *hfsmp,
535 struct vnode *vp,
536 struct cat_desc * cdp,
55e303ae 537 struct cat_attr * cap,
b0d623f7 538 struct vfs_context * ctx)
9bccf70c
A
539{
540 attrgroup_t attr = abp->ab_attrlist->commonattr;
541 struct mount *mp = HFSTOVFS(hfsmp);
542 void *attrbufptr = *abp->ab_attrbufpp;
543 void *varbufptr = *abp->ab_varbufpp;
b0d623f7 544 boolean_t is_64_bit = proc_is64bit(vfs_context_proc(ctx));
2d21ac55
A
545 uid_t cuid = 1;
546 int isroot = 0;
547
548 if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) {
b0d623f7 549 cuid = kauth_cred_getuid(vfs_context_ucred(ctx));
2d21ac55
A
550 isroot = cuid == 0;
551 }
9bccf70c
A
552
553 if (ATTR_CMN_NAME & attr) {
91447636 554 packnameattr(abp, vp, cdp->cd_nameptr, cdp->cd_namelen);
9bccf70c
A
555 attrbufptr = *abp->ab_attrbufpp;
556 varbufptr = *abp->ab_varbufpp;
557 }
558 if (ATTR_CMN_DEVID & attr) {
2d21ac55
A
559 *((dev_t *)attrbufptr) = hfsmp->hfs_raw_dev;
560 attrbufptr = ((dev_t *)attrbufptr) + 1;
9bccf70c
A
561 }
562 if (ATTR_CMN_FSID & attr) {
91447636
A
563 fsid_t fsid;
564
39236c6e
A
565 fsid.val[0] = hfsmp->hfs_raw_dev;
566 fsid.val[1] = vfs_typenum(mp);
91447636 567 *((fsid_t *)attrbufptr) = fsid;
2d21ac55 568 attrbufptr = ((fsid_t *)attrbufptr) + 1;
9bccf70c
A
569 }
570 if (ATTR_CMN_OBJTYPE & attr) {
2d21ac55
A
571 *((fsobj_type_t *)attrbufptr) = IFTOVT(cap->ca_mode);
572 attrbufptr = ((fsobj_type_t *)attrbufptr) + 1;
9bccf70c
A
573 }
574 if (ATTR_CMN_OBJTAG & attr) {
2d21ac55
A
575 *((fsobj_tag_t *)attrbufptr) = VT_HFS;
576 attrbufptr = ((fsobj_tag_t *)attrbufptr) + 1;
9bccf70c
A
577 }
578 /*
579 * Exporting file IDs from HFS Plus:
580 *
581 * For "normal" files the c_fileid is the same value as the
582 * c_cnid. But for hard link files, they are different - the
583 * c_cnid belongs to the active directory entry (ie the link)
584 * and the c_fileid is for the actual inode (ie the data file).
585 *
586 * The stat call (getattr) will always return the c_fileid
587 * and Carbon APIs, which are hardlink-ignorant, will always
588 * receive the c_cnid (from getattrlist).
589 */
2d21ac55 590 if (ATTR_CMN_OBJID & attr) {
9bccf70c
A
591 ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid;
592 ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
2d21ac55 593 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
9bccf70c
A
594 }
595 if (ATTR_CMN_OBJPERMANENTID & attr) {
596 ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid;
597 ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
2d21ac55 598 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
9bccf70c
A
599 }
600 if (ATTR_CMN_PAROBJID & attr) {
601 ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_parentcnid;
602 ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
2d21ac55 603 attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
9bccf70c
A
604 }
605 if (ATTR_CMN_SCRIPT & attr) {
2d21ac55
A
606 *((text_encoding_t *)attrbufptr) = cdp->cd_encoding;
607 attrbufptr = ((text_encoding_t *)attrbufptr) + 1;
9bccf70c
A
608 }
609 if (ATTR_CMN_CRTIME & attr) {
91447636 610 if (is_64_bit) {
b0d623f7
A
611 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_itime;
612 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
613 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
91447636
A
614 }
615 else {
b0d623f7
A
616 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_itime;
617 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
618 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
91447636 619 }
9bccf70c
A
620 }
621 if (ATTR_CMN_MODTIME & attr) {
91447636 622 if (is_64_bit) {
b0d623f7
A
623 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_mtime;
624 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
625 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
91447636
A
626 }
627 else {
b0d623f7
A
628 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_mtime;
629 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
630 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
91447636 631 }
9bccf70c
A
632 }
633 if (ATTR_CMN_CHGTIME & attr) {
91447636 634 if (is_64_bit) {
b0d623f7
A
635 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_ctime;
636 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
637 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
91447636
A
638 }
639 else {
b0d623f7
A
640 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_ctime;
641 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
642 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
91447636 643 }
9bccf70c
A
644 }
645 if (ATTR_CMN_ACCTIME & attr) {
91447636 646 if (is_64_bit) {
b0d623f7
A
647 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_atime;
648 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
649 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
91447636
A
650 }
651 else {
b0d623f7
A
652 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_atime;
653 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
654 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
91447636 655 }
9bccf70c
A
656 }
657 if (ATTR_CMN_BKUPTIME & attr) {
91447636 658 if (is_64_bit) {
b0d623f7
A
659 ((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_btime;
660 ((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
661 attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
91447636
A
662 }
663 else {
b0d623f7
A
664 ((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_btime;
665 ((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
666 attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
91447636 667 }
9bccf70c
A
668 }
669 if (ATTR_CMN_FNDRINFO & attr) {
6d2010ae 670 u_int8_t *finfo = NULL;
9bccf70c 671 bcopy(&cap->ca_finderinfo, attrbufptr, sizeof(u_int8_t) * 32);
6d2010ae
A
672 finfo = (u_int8_t*)attrbufptr;
673
2d21ac55
A
674 /* Don't expose a symlink's private type/creator. */
675 if (S_ISLNK(cap->ca_mode)) {
676 struct FndrFileInfo *fip;
677
678 fip = (struct FndrFileInfo *)attrbufptr;
679 fip->fdType = 0;
680 fip->fdCreator = 0;
681 }
6d2010ae
A
682
683 /* advance 16 bytes into the attrbuf */
684 finfo = finfo + 16;
39236c6e
A
685
686 /* also don't expose the date_added or write_gen_counter fields */
687 if (S_ISREG(cap->ca_mode) || S_ISLNK(cap->ca_mode)) {
6d2010ae 688 struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
22ba694c 689 extinfo->document_id = 0;
6d2010ae 690 extinfo->date_added = 0;
39236c6e 691 extinfo->write_gen_counter = 0;
6d2010ae
A
692 }
693 else if (S_ISDIR(cap->ca_mode)) {
694 struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
22ba694c 695 extinfo->document_id = 0;
6d2010ae 696 extinfo->date_added = 0;
22ba694c 697 extinfo->write_gen_counter = 0;
6d2010ae
A
698 }
699
2d21ac55 700 attrbufptr = (char *)attrbufptr + sizeof(u_int8_t) * 32;
9bccf70c
A
701 }
702 if (ATTR_CMN_OWNERID & attr) {
2d21ac55
A
703 uid_t nuid = cap->ca_uid;
704
705 if (!isroot) {
706 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS)
707 nuid = cuid;
708 else if (nuid == UNKNOWNUID)
709 nuid = cuid;
710 }
711
712 *((uid_t *)attrbufptr) = nuid;
713 attrbufptr = ((uid_t *)attrbufptr) + 1;
9bccf70c
A
714 }
715 if (ATTR_CMN_GRPID & attr) {
2d21ac55
A
716 gid_t ngid = cap->ca_gid;
717
718 if (!isroot) {
b0d623f7 719 gid_t cgid = kauth_cred_getgid(vfs_context_ucred(ctx));
2d21ac55
A
720 if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS)
721 ngid = cgid;
722 else if (ngid == UNKNOWNUID)
723 ngid = cgid;
724 }
725
726 *((gid_t *)attrbufptr) = ngid;
727 attrbufptr = ((gid_t *)attrbufptr) + 1;
9bccf70c
A
728 }
729 if (ATTR_CMN_ACCESSMASK & attr) {
730 /*
731 * [2856576] Since we are dynamically changing the owner, also
732 * effectively turn off the set-user-id and set-group-id bits,
733 * just like chmod(2) would when changing ownership. This prevents
734 * a security hole where set-user-id programs run as whoever is
735 * logged on (or root if nobody is logged in yet!)
736 */
2d21ac55
A
737 *((u_int32_t *)attrbufptr) = (cap->ca_uid == UNKNOWNUID) ?
738 cap->ca_mode & ~(S_ISUID | S_ISGID) : cap->ca_mode;
739 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
740 }
741 if (ATTR_CMN_FLAGS & attr) {
2d21ac55
A
742 *((u_int32_t *)attrbufptr) = cap->ca_flags;
743 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c 744 }
22ba694c 745
9bccf70c 746 if (ATTR_CMN_USERACCESS & attr) {
2d21ac55
A
747 u_int32_t user_access;
748
749 /* Take the long path when we have an ACL */
750 if ((vp != NULLVP) && (cap->ca_recflags & kHFSHasSecurityMask)) {
751 user_access = hfs_real_user_access(vp, abp->ab_context);
752 } else {
753 user_access = DerivePermissionSummary(cap->ca_uid, cap->ca_gid,
39236c6e 754 cap->ca_mode, mp, vfs_context_ucred(ctx), 0);
2d21ac55
A
755 }
756 /* Also consider READ-ONLY file system. */
757 if (vfs_flags(mp) & MNT_RDONLY) {
758 user_access &= ~W_OK;
759 }
760 /* Locked objects are not writable either */
761 if ((cap->ca_flags & UF_IMMUTABLE) && (vfs_context_suser(abp->ab_context) != 0))
762 user_access &= ~W_OK;
763 if ((cap->ca_flags & SF_IMMUTABLE) && (vfs_context_suser(abp->ab_context) == 0))
764 user_access &= ~W_OK;
765
766 *((u_int32_t *)attrbufptr) = user_access;
767 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
768 }
769 if (ATTR_CMN_FILEID & attr) {
770 *((u_int64_t *)attrbufptr) = cap->ca_fileid;
771 attrbufptr = ((u_int64_t *)attrbufptr) + 1;
772 }
773 if (ATTR_CMN_PARENTID & attr) {
774 *((u_int64_t *)attrbufptr) = cdp->cd_parentcnid;
775 attrbufptr = ((u_int64_t *)attrbufptr) + 1;
9bccf70c
A
776 }
777
778 *abp->ab_attrbufpp = attrbufptr;
779 *abp->ab_varbufpp = varbufptr;
780}
781
782static void
783packdirattr(
784 struct attrblock *abp,
785 struct hfsmount *hfsmp,
786 struct vnode *vp,
787 struct cat_desc * descp,
91447636 788 struct cat_attr * cattrp)
9bccf70c
A
789{
790 attrgroup_t attr = abp->ab_attrlist->dirattr;
791 void *attrbufptr = *abp->ab_attrbufpp;
2d21ac55
A
792 u_int32_t entries;
793
794 /*
795 * The DIR_LINKCOUNT is the count of real directory hard links.
796 * (i.e. its not the sum of the implied "." and ".." references
797 * typically used in stat's st_nlink field)
798 */
799 if (ATTR_DIR_LINKCOUNT & attr) {
800 *((u_int32_t *)attrbufptr) = cattrp->ca_linkcount;
801 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
802 }
9bccf70c 803 if (ATTR_DIR_ENTRYCOUNT & attr) {
2d21ac55 804 entries = cattrp->ca_entries;
9bccf70c 805
91447636 806 if (descp->cd_parentcnid == kHFSRootParentID) {
2d21ac55
A
807 if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0)
808 --entries; /* hide private dir */
809 if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0)
b4c24cb9 810 --entries; /* hide private dir */
2d21ac55
A
811 if (hfsmp->jnl ||
812 ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) &&
813 (hfsmp->hfs_flags & HFS_READ_ONLY)))
b4c24cb9
A
814 entries -= 2; /* hide the journal files */
815 }
9bccf70c 816
2d21ac55
A
817 *((u_int32_t *)attrbufptr) = entries;
818 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
819 }
820 if (ATTR_DIR_MOUNTSTATUS & attr) {
91447636 821 if (vp != NULL && vnode_mountedhere(vp) != NULL)
2d21ac55 822 *((u_int32_t *)attrbufptr) = DIR_MNTSTATUS_MNTPOINT;
9bccf70c 823 else
2d21ac55
A
824 *((u_int32_t *)attrbufptr) = 0;
825 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
826 }
827 *abp->ab_attrbufpp = attrbufptr;
828}
829
830static void
831packfileattr(
832 struct attrblock *abp,
833 struct hfsmount *hfsmp,
834 struct cat_attr *cattrp,
835 struct cat_fork *datafork,
b0d623f7
A
836 struct cat_fork *rsrcfork,
837 struct vnode *vp)
9bccf70c 838{
b0d623f7
A
839#if !HFS_COMPRESSION
840#pragma unused(vp)
841#endif
9bccf70c
A
842 attrgroup_t attr = abp->ab_attrlist->fileattr;
843 void *attrbufptr = *abp->ab_attrbufpp;
844 void *varbufptr = *abp->ab_varbufpp;
2d21ac55 845 u_int32_t allocblksize;
9bccf70c
A
846
847 allocblksize = HFSTOVCB(hfsmp)->blockSize;
848
b0d623f7
A
849 off_t datasize = datafork->cf_size;
850 off_t totalsize = datasize + rsrcfork->cf_size;
851#if HFS_COMPRESSION
6d2010ae
A
852 int handle_compressed;
853 handle_compressed = (cattrp->ca_flags & UF_COMPRESSED);// && hfs_file_is_compressed(VTOC(vp), 1);
854
855 if (handle_compressed) {
b0d623f7
A
856 if (attr & (ATTR_FILE_DATALENGTH|ATTR_FILE_TOTALSIZE)) {
857 if ( 0 == hfs_uncompressed_size_of_compressed_file(hfsmp, vp, cattrp->ca_fileid, &datasize, 1) ) { /* 1 == don't take the cnode lock */
858 /* total size of a compressed file is just the data size */
859 totalsize = datasize;
860 }
861 }
862 }
863#endif
864
9bccf70c 865 if (ATTR_FILE_LINKCOUNT & attr) {
2d21ac55
A
866 *((u_int32_t *)attrbufptr) = cattrp->ca_linkcount;
867 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
868 }
869 if (ATTR_FILE_TOTALSIZE & attr) {
b0d623f7 870 *((off_t *)attrbufptr) = totalsize;
2d21ac55 871 attrbufptr = ((off_t *)attrbufptr) + 1;
9bccf70c
A
872 }
873 if (ATTR_FILE_ALLOCSIZE & attr) {
2d21ac55 874 *((off_t *)attrbufptr) =
9bccf70c 875 (off_t)cattrp->ca_blocks * (off_t)allocblksize;
2d21ac55 876 attrbufptr = ((off_t *)attrbufptr) + 1;
9bccf70c
A
877 }
878 if (ATTR_FILE_IOBLOCKSIZE & attr) {
2d21ac55
A
879 *((u_int32_t *)attrbufptr) = hfsmp->hfs_logBlockSize;
880 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
881 }
882 if (ATTR_FILE_CLUMPSIZE & attr) {
2d21ac55
A
883 *((u_int32_t *)attrbufptr) = hfsmp->vcbClpSiz;
884 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c
A
885 }
886 if (ATTR_FILE_DEVTYPE & attr) {
887 if (S_ISBLK(cattrp->ca_mode) || S_ISCHR(cattrp->ca_mode))
2d21ac55 888 *((u_int32_t *)attrbufptr) = (u_int32_t)cattrp->ca_rdev;
9bccf70c 889 else
2d21ac55
A
890 *((u_int32_t *)attrbufptr) = 0;
891 attrbufptr = ((u_int32_t *)attrbufptr) + 1;
9bccf70c 892 }
b0d623f7 893
9bccf70c 894 if (ATTR_FILE_DATALENGTH & attr) {
b0d623f7 895 *((off_t *)attrbufptr) = datasize;
2d21ac55 896 attrbufptr = ((off_t *)attrbufptr) + 1;
9bccf70c 897 }
b0d623f7
A
898
899#if HFS_COMPRESSION
900 /* fake the data fork size on a decmpfs compressed file to reflect the
901 * uncompressed size. This ensures proper reading and copying of these files.
902 * NOTE: we may need to get the vnode here because the vnode parameter
903 * passed by hfs_vnop_readdirattr() may be null.
904 */
905
6d2010ae 906 if ( handle_compressed ) {
b0d623f7
A
907 if (attr & ATTR_FILE_DATAALLOCSIZE) {
908 *((off_t *)attrbufptr) = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize;
909 attrbufptr = ((off_t *)attrbufptr) + 1;
910 }
911 if (attr & ATTR_FILE_RSRCLENGTH) {
912 *((off_t *)attrbufptr) = 0;
913 attrbufptr = ((off_t *)attrbufptr) + 1;
914 }
915 if (attr & ATTR_FILE_RSRCALLOCSIZE) {
916 *((off_t *)attrbufptr) = 0;
917 attrbufptr = ((off_t *)attrbufptr) + 1;
918 }
9bccf70c 919 }
b0d623f7
A
920 else
921#endif
922 {
923 if (ATTR_FILE_DATAALLOCSIZE & attr) {
924 *((off_t *)attrbufptr) = (off_t)datafork->cf_blocks * (off_t)allocblksize;
925 attrbufptr = ((off_t *)attrbufptr) + 1;
926 }
927 if (ATTR_FILE_RSRCLENGTH & attr) {
928 *((off_t *)attrbufptr) = rsrcfork->cf_size;
929 attrbufptr = ((off_t *)attrbufptr) + 1;
930 }
931 if (ATTR_FILE_RSRCALLOCSIZE & attr) {
932 *((off_t *)attrbufptr) = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize;
933 attrbufptr = ((off_t *)attrbufptr) + 1;
934 }
9bccf70c
A
935 }
936 *abp->ab_attrbufpp = attrbufptr;
937 *abp->ab_varbufpp = varbufptr;
938}
939
9bccf70c
A
940/*
941 * Calculate the total size of an attribute block.
942 */
6d2010ae 943__private_extern__
9bccf70c
A
944int
945hfs_attrblksize(struct attrlist *attrlist)
946{
947 int size;
948 attrgroup_t a;
91447636
A
949 int sizeof_timespec;
950 boolean_t is_64_bit = proc_is64bit(current_proc());
9bccf70c 951
91447636 952 if (is_64_bit)
b0d623f7 953 sizeof_timespec = sizeof(struct user64_timespec);
91447636 954 else
b0d623f7 955 sizeof_timespec = sizeof(struct user32_timespec);
91447636 956
9bccf70c
A
957 DBG_ASSERT((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0);
958
9bccf70c
A
959 DBG_ASSERT((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0);
960
9bccf70c
A
961 DBG_ASSERT((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0);
962
9bccf70c
A
963 DBG_ASSERT((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0);
964
9bccf70c
A
965 DBG_ASSERT((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0);
966
967 size = 0;
968
969 if ((a = attrlist->commonattr) != 0) {
2d21ac55 970 if (a & ATTR_CMN_NAME) size += sizeof(struct attrreference);
9bccf70c
A
971 if (a & ATTR_CMN_DEVID) size += sizeof(dev_t);
972 if (a & ATTR_CMN_FSID) size += sizeof(fsid_t);
973 if (a & ATTR_CMN_OBJTYPE) size += sizeof(fsobj_type_t);
974 if (a & ATTR_CMN_OBJTAG) size += sizeof(fsobj_tag_t);
975 if (a & ATTR_CMN_OBJID) size += sizeof(fsobj_id_t);
976 if (a & ATTR_CMN_OBJPERMANENTID) size += sizeof(fsobj_id_t);
977 if (a & ATTR_CMN_PAROBJID) size += sizeof(fsobj_id_t);
978 if (a & ATTR_CMN_SCRIPT) size += sizeof(text_encoding_t);
2d21ac55
A
979 if (a & ATTR_CMN_CRTIME) size += sizeof_timespec;
980 if (a & ATTR_CMN_MODTIME) size += sizeof_timespec;
981 if (a & ATTR_CMN_CHGTIME) size += sizeof_timespec;
982 if (a & ATTR_CMN_ACCTIME) size += sizeof_timespec;
983 if (a & ATTR_CMN_BKUPTIME) size += sizeof_timespec;
9bccf70c
A
984 if (a & ATTR_CMN_FNDRINFO) size += 32 * sizeof(u_int8_t);
985 if (a & ATTR_CMN_OWNERID) size += sizeof(uid_t);
986 if (a & ATTR_CMN_GRPID) size += sizeof(gid_t);
2d21ac55
A
987 if (a & ATTR_CMN_ACCESSMASK) size += sizeof(u_int32_t);
988 if (a & ATTR_CMN_FLAGS) size += sizeof(u_int32_t);
989 if (a & ATTR_CMN_USERACCESS) size += sizeof(u_int32_t);
990 if (a & ATTR_CMN_FILEID) size += sizeof(u_int64_t);
991 if (a & ATTR_CMN_PARENTID) size += sizeof(u_int64_t);
992 }
9bccf70c 993 if ((a = attrlist->dirattr) != 0) {
2d21ac55
A
994 if (a & ATTR_DIR_LINKCOUNT) size += sizeof(u_int32_t);
995 if (a & ATTR_DIR_ENTRYCOUNT) size += sizeof(u_int32_t);
996 if (a & ATTR_DIR_MOUNTSTATUS) size += sizeof(u_int32_t);
997 }
9bccf70c 998 if ((a = attrlist->fileattr) != 0) {
2d21ac55 999 if (a & ATTR_FILE_LINKCOUNT) size += sizeof(u_int32_t);
9bccf70c
A
1000 if (a & ATTR_FILE_TOTALSIZE) size += sizeof(off_t);
1001 if (a & ATTR_FILE_ALLOCSIZE) size += sizeof(off_t);
2d21ac55
A
1002 if (a & ATTR_FILE_IOBLOCKSIZE) size += sizeof(u_int32_t);
1003 if (a & ATTR_FILE_CLUMPSIZE) size += sizeof(u_int32_t);
1004 if (a & ATTR_FILE_DEVTYPE) size += sizeof(u_int32_t);
9bccf70c
A
1005 if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t);
1006 if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t);
9bccf70c
A
1007 if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t);
1008 if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t);
2d21ac55 1009 }
9bccf70c 1010
2d21ac55 1011 return (size);
9bccf70c
A
1012}
1013
2d21ac55
A
1014#define KAUTH_DIR_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | \
1015 KAUTH_VNODE_ADD_SUBDIRECTORY | \
1016 KAUTH_VNODE_DELETE_CHILD)
1017
1018#define KAUTH_DIR_READ_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY)
1019
1020#define KAUTH_DIR_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH)
1021
1022#define KAUTH_FILE_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA)
1023
1024#define KAUTH_FILE_READRIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA)
1025
1026#define KAUTH_FILE_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE)
1027
1028
1029/*
1030 * Compute the same [expensive] user_access value as getattrlist does
1031 */
1032static u_int32_t
1033hfs_real_user_access(vnode_t vp, vfs_context_t ctx)
1034{
1035 u_int32_t user_access = 0;
1036
1037 if (vnode_isdir(vp)) {
1038 if (vnode_authorize(vp, NULLVP, KAUTH_DIR_WRITE_RIGHTS, ctx) == 0)
1039 user_access |= W_OK;
1040 if (vnode_authorize(vp, NULLVP, KAUTH_DIR_READ_RIGHTS, ctx) == 0)
1041 user_access |= R_OK;
1042 if (vnode_authorize(vp, NULLVP, KAUTH_DIR_EXECUTE_RIGHTS, ctx) == 0)
1043 user_access |= X_OK;
1044 } else {
1045 if (vnode_authorize(vp, NULLVP, KAUTH_FILE_WRITE_RIGHTS, ctx) == 0)
1046 user_access |= W_OK;
1047 if (vnode_authorize(vp, NULLVP, KAUTH_FILE_READRIGHTS, ctx) == 0)
1048 user_access |= R_OK;
1049 if (vnode_authorize(vp, NULLVP, KAUTH_FILE_EXECUTE_RIGHTS, ctx) == 0)
1050 user_access |= X_OK;
1051 }
1052 return (user_access);
1053}
1054
9bccf70c 1055
b0d623f7 1056u_int32_t
9bccf70c 1057DerivePermissionSummary(uid_t obj_uid, gid_t obj_gid, mode_t obj_mode,
2d21ac55 1058 struct mount *mp, kauth_cred_t cred, __unused struct proc *p)
9bccf70c 1059{
b0d623f7 1060 u_int32_t permissions;
9bccf70c
A
1061
1062 if (obj_uid == UNKNOWNUID)
2d21ac55 1063 obj_uid = kauth_cred_getuid(cred);
9bccf70c
A
1064
1065 /* User id 0 (root) always gets access. */
91447636 1066 if (!suser(cred, NULL)) {
9bccf70c
A
1067 permissions = R_OK | W_OK | X_OK;
1068 goto Exit;
1069 };
1070
1071 /* Otherwise, check the owner. */
2d21ac55 1072 if (hfs_owner_rights(VFSTOHFS(mp), obj_uid, cred, NULL, false) == 0) {
b0d623f7 1073 permissions = ((u_int32_t)obj_mode & S_IRWXU) >> 6;
9bccf70c
A
1074 goto Exit;
1075 }
1076
1077 /* Otherwise, check the groups. */
91447636
A
1078 if (! (((unsigned int)vfs_flags(mp)) & MNT_UNKNOWNPERMISSIONS)) {
1079 int is_member;
1080
1081 if (kauth_cred_ismember_gid(cred, obj_gid, &is_member) == 0 && is_member) {
b0d623f7 1082 permissions = ((u_int32_t)obj_mode & S_IRWXG) >> 3;
91447636 1083 goto Exit;
9bccf70c
A
1084 }
1085 }
1086
1087 /* Otherwise, settle for 'others' access. */
b0d623f7 1088 permissions = (u_int32_t)obj_mode & S_IRWXO;
9bccf70c
A
1089
1090Exit:
1091 return (permissions);
1092}
1093