1/*
2 * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29/*
30 * hfs_attrlist.c - HFS attribute list processing
31 *
32 * Copyright (c) 1998-2002, Apple Computer, Inc.  All Rights Reserved.
33 */
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39#include <sys/attr.h>
40#include <sys/stat.h>
41#include <sys/unistd.h>
42#include <sys/mount_internal.h>
43#include <sys/kauth.h>
44#include <sys/fsctl.h>
45
46#include <kern/locks.h>
47
48#include "hfs.h"
49#include "hfs_cnode.h"
50#include "hfs_mount.h"
51#include "hfs_dbg.h"
52#include "hfs_attrlist.h"
53#include "hfs_btreeio.h"
54
55/* Packing routines: */
56
57static void packnameattr(struct attrblock *abp, struct vnode *vp,
58			const u_int8_t *name, int namelen);
59
60static void packcommonattr(struct attrblock *abp, struct hfsmount *hfsmp,
61			struct vnode *vp, struct cat_desc * cdp,
62			struct cat_attr * cap, struct vfs_context *ctx);
63
64static void packfileattr(struct attrblock *abp, struct hfsmount *hfsmp,
65			struct cat_attr *cattrp, struct cat_fork *datafork,
66			struct cat_fork *rsrcfork, struct vnode *vp);
67
68static void packdirattr(struct attrblock *abp, struct hfsmount *hfsmp,
69			struct vnode *vp, struct cat_desc * descp,
70			struct cat_attr * cattrp);
71
72static u_int32_t hfs_real_user_access(vnode_t vp, vfs_context_t ctx);
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.
83 */
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	u_int32_t fixedblocksize;
105	u_int32_t maxattrblocksize;
106	u_int32_t currattrbufsize;
107	void *attrbufptr = NULL;
108	void *attrptr;
109	void *varptr;
110	struct attrblock attrblk;
111	int error = 0;
112	int index = 0;
113	int i, dir_entries = 0;
114	struct cat_desc *lastdescp = NULL;
115	struct cat_entrylist *ce_list = NULL;
116	directoryhint_t *dirhint = NULL;
117	unsigned int tag;
118	int maxentries;
119	int lockflags;
120	u_int32_t dirchg = 0;
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	if (VTOC(dvp)->c_bsdflags & UF_COMPRESSED) {
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
154	/*
155	 * Take an exclusive directory lock since we manipulate the directory hints
156	 */
157	if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
158		return (error);
159	}
160	dcp = VTOC(dvp);
161	hfsmp = VTOHFS(dvp);
162
163	dir_entries = dcp->c_entries;
164	dirchg = dcp->c_dirchangecnt;
165
166	/* Extract directory index and tag (sequence number) from uio_offset */
167	index = uio_offset(uio) & HFS_INDEX_MASK;
168	tag = uio_offset(uio) & ~HFS_INDEX_MASK;
169	if ((index + 1) > dir_entries) {
170		*(ap->a_eofflag) = 1;
171		error = 0;
172		goto exit2;
173	}
174
175	/* Get a buffer to hold packed attributes. */
176	fixedblocksize = (sizeof(u_int32_t) + hfs_attrblksize(alist)); /* 4 bytes for length */
177	maxattrblocksize = fixedblocksize;
178	if (alist->commonattr & ATTR_CMN_NAME)
179		maxattrblocksize += kHFSPlusMaxFileNameBytes + 1;
180	MALLOC(attrbufptr, void *, maxattrblocksize, M_TEMP, M_WAITOK);
181	if (attrbufptr == NULL) {
182		error = ENOMEM;
183		goto exit2;
184	}
185	attrptr = attrbufptr;
186	varptr = (char *)attrbufptr + fixedblocksize;  /* Point to variable-length storage */
187
188	/* Get a detached directory hint (cnode must be locked exclusive) */
189	dirhint = hfs_getdirhint(dcp, ((index - 1) & HFS_INDEX_MASK) | tag, TRUE);
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	/*
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.
204	 */
205	maxentries = uio_resid(uio) / (fixedblocksize + HFS_AVERAGE_NAME_SIZE);
206	maxentries = min(maxentries, maxcount);
207	maxentries = min(maxentries, MAXCATENTRIES);
208	if (maxentries < 1) {
209		error = EINVAL;
210		goto exit2;
211	}
212
213	/* Initialize a catalog entry list. */
214	MALLOC(ce_list, struct cat_entrylist *, CE_LIST_SIZE(maxentries), M_TEMP, M_WAITOK);
215	if (ce_list == NULL) {
216		error = ENOMEM;
217		goto exit2;
218	}
219	bzero(ce_list, CE_LIST_SIZE(maxentries));
220	ce_list->maxentries = maxentries;
221
222	/*
223	 * Populate the ce_list from the catalog file.
224	 */
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)) {
269			/*
270			 * Obtain vnode for our vnode_authorize() calls.
271			 */
272			if (hfs_vget(hfsmp, cattrp->ca_fileid, &vp, 0, 0) != 0) {
273				vp = NULL;
274			}
275		} else if (!(ap->a_options & FSOPT_NOINMEMUPDATE)) {
276			/* Get in-memory cnode data (if any). */
277			vp = hfs_chash_getvnode(hfsmp, cattrp->ca_fileid, 0, 0, 0);
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;
292			}
293			/* All done with cnode. */
294			hfs_unlock(cp);
295			cp = NULL;
296		}
297
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. */
308		hfs_packattrblk(&attrblk, hfsmp, vp, cdescp, cattrp, &c_datafork, &c_rsrcfork, ap->a_context);
309		currattrbufsize = ((char *)varptr - (char *)attrbufptr);
310
311		/* All done with vnode. */
312		if (vp != NULL) {
313			vnode_put(vp);
314			vp = NULL;
315		}
316
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 ||
339			    ((u_int32_t)uio_resid(uio) < (fixedblocksize + HFS_AVERAGE_NAME_SIZE))){
340				break;
341			}
342		}
343	} /* for each catalog entry */
344
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	}
349
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	}
357
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;
380	}
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
387	(void) hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
388	dcp = VTOC(dvp);
389
390exit1:
391	/* Pack directory index and tag into uio_offset. */
392	while (tag == 0) tag = (++dcp->c_dirhinttag) << HFS_INDEX_BITS;
393	uio_setoffset(uio, index | tag);
394	dirhint->dh_index |= tag;
395
396exit2:
397	*ap->a_newstate = dirchg;
398
399	/* Drop directory hint on error or if there are no more entries */
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);
405	}
406	if (attrbufptr)
407		FREE(attrbufptr, M_TEMP);
408	if (ce_list)
409		FREE(ce_list, M_TEMP);
410
411	hfs_unlock(dcp);
412	return (error);
413}
414
415
416/*==================== Attribute list support routines ====================*/
417
418/*
419 * Pack cnode attributes into an attribute block.
420 */
421__private_extern__
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,
429		struct cat_fork *rsrcfork,
430		struct vfs_context *ctx)
431{
432	struct attrlist *attrlistp = abp->ab_attrlist;
433
434	if (attrlistp->commonattr)
435		packcommonattr(abp, hfsmp, vp, descp, attrp, ctx);
436
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))
441		packfileattr(abp, hfsmp, attrp, datafork, rsrcfork, vp);
442}
443
444
445static char*
446mountpointname(struct mount *mp)
447{
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}
471
472
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;
486
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;
505		}
506	}
507	if (name == NULL) {
508		name = &empty;
509		namelen = 0;
510	}
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;
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,
537	struct cat_attr * cap,
538	struct vfs_context * ctx)
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;
544	boolean_t is_64_bit = proc_is64bit(vfs_context_proc(ctx));
545	uid_t cuid = 1;
546	int isroot = 0;
547
548	if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) {
549		cuid = kauth_cred_getuid(vfs_context_ucred(ctx));
550		isroot = cuid == 0;
551	}
552
553	if (ATTR_CMN_NAME & attr) {
554		packnameattr(abp, vp, cdp->cd_nameptr, cdp->cd_namelen);
555		attrbufptr = *abp->ab_attrbufpp;
556		varbufptr = *abp->ab_varbufpp;
557	}
558	if (ATTR_CMN_DEVID & attr) {
559		*((dev_t *)attrbufptr) = hfsmp->hfs_raw_dev;
560		attrbufptr = ((dev_t *)attrbufptr) + 1;
561	}
562	if (ATTR_CMN_FSID & attr) {
563		fsid_t fsid;
564
565		fsid.val[0] = hfsmp->hfs_raw_dev;
566		fsid.val[1] = vfs_typenum(mp);
567		*((fsid_t *)attrbufptr) = fsid;
568		attrbufptr = ((fsid_t *)attrbufptr) + 1;
569	}
570	if (ATTR_CMN_OBJTYPE & attr) {
571		*((fsobj_type_t *)attrbufptr) = IFTOVT(cap->ca_mode);
572		attrbufptr = ((fsobj_type_t *)attrbufptr) + 1;
573	}
574	if (ATTR_CMN_OBJTAG & attr) {
575		*((fsobj_tag_t *)attrbufptr) = VT_HFS;
576		attrbufptr = ((fsobj_tag_t *)attrbufptr) + 1;
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	 */
590	if (ATTR_CMN_OBJID & attr) {
591		((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid;
592		((fsobj_id_t *)attrbufptr)->fid_generation = 0;
593		attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
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;
598		attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
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;
603		attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
604	}
605	if (ATTR_CMN_SCRIPT & attr) {
606		*((text_encoding_t *)attrbufptr) = cdp->cd_encoding;
607		attrbufptr = ((text_encoding_t *)attrbufptr) + 1;
608	}
609	if (ATTR_CMN_CRTIME & attr) {
610	    if (is_64_bit) {
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;
614	    }
615	    else {
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;
619	    }
620	}
621	if (ATTR_CMN_MODTIME & attr) {
622	    if (is_64_bit) {
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;
626	    }
627	    else {
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;
631	    }
632	}
633	if (ATTR_CMN_CHGTIME & attr) {
634	    if (is_64_bit) {
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;
638	    }
639	    else {
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;
643	    }
644	}
645	if (ATTR_CMN_ACCTIME & attr) {
646	    if (is_64_bit) {
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;
650	    }
651	    else {
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;
655	    }
656	}
657	if (ATTR_CMN_BKUPTIME & attr) {
658	    if (is_64_bit) {
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;
662	    }
663	    else {
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;
667	    }
668	}
669	if (ATTR_CMN_FNDRINFO & attr) {
670		u_int8_t *finfo = NULL;
671		bcopy(&cap->ca_finderinfo, attrbufptr, sizeof(u_int8_t) * 32);
672		finfo = (u_int8_t*)attrbufptr;
673
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		}
682
683		/* advance 16 bytes into the attrbuf */
684		finfo = finfo + 16;
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)) {
688			struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
689			extinfo->document_id = 0;
690			extinfo->date_added = 0;
691			extinfo->write_gen_counter = 0;
692		}
693		else if (S_ISDIR(cap->ca_mode)) {
694			struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
695			extinfo->document_id = 0;
696			extinfo->date_added = 0;
697			extinfo->write_gen_counter = 0;
698		}
699
700		attrbufptr = (char *)attrbufptr + sizeof(u_int8_t) * 32;
701	}
702	if (ATTR_CMN_OWNERID & attr) {
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;
714	}
715	if (ATTR_CMN_GRPID & attr) {
716		gid_t ngid = cap->ca_gid;
717
718		if (!isroot) {
719			gid_t cgid = kauth_cred_getgid(vfs_context_ucred(ctx));
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;
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		 */
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;
740	}
741	if (ATTR_CMN_FLAGS & attr) {
742		*((u_int32_t *)attrbufptr) = cap->ca_flags;
743		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
744	}
745
746	if (ATTR_CMN_USERACCESS & attr) {
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,
754			                  cap->ca_mode, mp, vfs_context_ucred(ctx), 0);
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;
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,
788	struct cat_attr * cattrp)
789{
790	attrgroup_t attr = abp->ab_attrlist->dirattr;
791	void *attrbufptr = *abp->ab_attrbufpp;
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	}
803	if (ATTR_DIR_ENTRYCOUNT & attr) {
804		entries = cattrp->ca_entries;
805
806		if (descp->cd_parentcnid == kHFSRootParentID) {
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)
810				--entries;	    /* hide private dir */
811			if (hfsmp->jnl ||
812			    ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) &&
813			     (hfsmp->hfs_flags & HFS_READ_ONLY)))
814				entries -= 2;	/* hide the journal files */
815		}
816
817		*((u_int32_t *)attrbufptr) = entries;
818		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
819	}
820	if (ATTR_DIR_MOUNTSTATUS & attr) {
821		if (vp != NULL && vnode_mountedhere(vp) != NULL)
822			*((u_int32_t *)attrbufptr) = DIR_MNTSTATUS_MNTPOINT;
823		else
824			*((u_int32_t *)attrbufptr) = 0;
825		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
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,
836	struct cat_fork *rsrcfork,
837	struct vnode *vp)
838{
839#if !HFS_COMPRESSION
840#pragma unused(vp)
841#endif
842	attrgroup_t attr = abp->ab_attrlist->fileattr;
843	void *attrbufptr = *abp->ab_attrbufpp;
844	void *varbufptr = *abp->ab_varbufpp;
845	u_int32_t allocblksize;
846
847	allocblksize = HFSTOVCB(hfsmp)->blockSize;
848
849	off_t datasize = datafork->cf_size;
850	off_t totalsize = datasize + rsrcfork->cf_size;
851#if HFS_COMPRESSION
852	int handle_compressed;
853	handle_compressed =  (cattrp->ca_flags & UF_COMPRESSED);// && hfs_file_is_compressed(VTOC(vp), 1);
854
855	if (handle_compressed) {
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
865	if (ATTR_FILE_LINKCOUNT & attr) {
866		*((u_int32_t *)attrbufptr) = cattrp->ca_linkcount;
867		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
868	}
869	if (ATTR_FILE_TOTALSIZE & attr) {
870		*((off_t *)attrbufptr) = totalsize;
871		attrbufptr = ((off_t *)attrbufptr) + 1;
872	}
873	if (ATTR_FILE_ALLOCSIZE & attr) {
874		*((off_t *)attrbufptr) =
875			(off_t)cattrp->ca_blocks * (off_t)allocblksize;
876		attrbufptr = ((off_t *)attrbufptr) + 1;
877	}
878	if (ATTR_FILE_IOBLOCKSIZE & attr) {
879		*((u_int32_t *)attrbufptr) = hfsmp->hfs_logBlockSize;
880		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
881	}
882	if (ATTR_FILE_CLUMPSIZE & attr) {
883		*((u_int32_t *)attrbufptr) = hfsmp->vcbClpSiz;
884		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
885	}
886	if (ATTR_FILE_DEVTYPE & attr) {
887		if (S_ISBLK(cattrp->ca_mode) || S_ISCHR(cattrp->ca_mode))
888			*((u_int32_t *)attrbufptr) = (u_int32_t)cattrp->ca_rdev;
889		else
890			*((u_int32_t *)attrbufptr) = 0;
891		attrbufptr = ((u_int32_t *)attrbufptr) + 1;
892	}
893
894	if (ATTR_FILE_DATALENGTH & attr) {
895		*((off_t *)attrbufptr) = datasize;
896		attrbufptr = ((off_t *)attrbufptr) + 1;
897	}
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
906	if ( handle_compressed ) {
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		}
919	}
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		}
935	}
936	*abp->ab_attrbufpp = attrbufptr;
937	*abp->ab_varbufpp = varbufptr;
938}
939
940/*
941 * Calculate the total size of an attribute block.
942 */
943__private_extern__
944int
945hfs_attrblksize(struct attrlist *attrlist)
946{
947	int size;
948	attrgroup_t a;
949	int sizeof_timespec;
950	boolean_t is_64_bit = proc_is64bit(current_proc());
951
952    if (is_64_bit)
953        sizeof_timespec = sizeof(struct user64_timespec);
954    else
955        sizeof_timespec = sizeof(struct user32_timespec);
956
957	DBG_ASSERT((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0);
958
959	DBG_ASSERT((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0);
960
961	DBG_ASSERT((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0);
962
963	DBG_ASSERT((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0);
964
965	DBG_ASSERT((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0);
966
967	size = 0;
968
969	if ((a = attrlist->commonattr) != 0) {
970		if (a & ATTR_CMN_NAME) size += sizeof(struct attrreference);
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);
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;
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);
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	}
993	if ((a = attrlist->dirattr) != 0) {
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	}
998	if ((a = attrlist->fileattr) != 0) {
999		if (a & ATTR_FILE_LINKCOUNT) size += sizeof(u_int32_t);
1000		if (a & ATTR_FILE_TOTALSIZE) size += sizeof(off_t);
1001		if (a & ATTR_FILE_ALLOCSIZE) size += sizeof(off_t);
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);
1005		if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t);
1006		if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t);
1007		if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t);
1008		if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t);
1009	}
1010
1011	return (size);
1012}
1013
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
1055
1056u_int32_t
1057DerivePermissionSummary(uid_t obj_uid, gid_t obj_gid, mode_t obj_mode,
1058		struct mount *mp, kauth_cred_t cred, __unused struct proc *p)
1059{
1060	u_int32_t permissions;
1061
1062	if (obj_uid == UNKNOWNUID)
1063		obj_uid = kauth_cred_getuid(cred);
1064
1065	/* User id 0 (root) always gets access. */
1066	if (!suser(cred, NULL)) {
1067		permissions = R_OK | W_OK | X_OK;
1068		goto Exit;
1069	};
1070
1071	/* Otherwise, check the owner. */
1072	if (hfs_owner_rights(VFSTOHFS(mp), obj_uid, cred, NULL, false) == 0) {
1073		permissions = ((u_int32_t)obj_mode & S_IRWXU) >> 6;
1074		goto Exit;
1075	}
1076
1077	/* Otherwise, check the groups. */
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) {
1082			permissions = ((u_int32_t)obj_mode & S_IRWXG) >> 3;
1083			goto Exit;
1084		}
1085	}
1086
1087	/* Otherwise, settle for 'others' access. */
1088	permissions = (u_int32_t)obj_mode & S_IRWXO;
1089
1090Exit:
1091	return (permissions);
1092}
1093
1094