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
56static void packnameattr(struct attrblock *abp, struct vnode *vp,
57			const u_int8_t *name, int namelen);
58
59static 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
63static void packfileattr(struct attrblock *abp, struct hfsmount *hfsmp,
64			struct cat_attr *cattrp, struct cat_fork *datafork,
65			struct cat_fork *rsrcfork);
66
67static void packdirattr(struct attrblock *abp, struct hfsmount *hfsmp,
68			struct vnode *vp, struct cat_desc * descp,
69			struct cat_attr * cattrp);
70
71static 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__
84int
85hfs_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
370exit1:
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
376exit2:
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__
402void
403hfs_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
425static char*
426mountpointname(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
453static void
454packnameattr(
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
511static void
512packcommonattr(
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
740static void
741packdirattr(
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
788static void
789packfileattr(
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__
857int
858hfs_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 */
945static u_int32_t
946hfs_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__
970unsigned long
971DerivePermissionSummary(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
1004Exit:
1005	return (permissions);
1006}
1007
1008