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