/* * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * hfs_attrlist.c - HFS attribute list processing * * Copyright (c) 1998-2002, Apple Computer, Inc. All Rights Reserved. */ #include #include #include #include #include #include #include #include #include #include #include "hfs.h" #include "hfs_cnode.h" #include "hfs_mount.h" #include "hfs_dbg.h" #include "hfs_attrlist.h" /* Routines that are shared by hfs_setattr: */ extern int hfs_write_access(struct vnode *vp, kauth_cred_t cred, struct proc *p, Boolean considerFlags); extern int hfs_chflags(struct vnode *vp, uint32_t flags, kauth_cred_t cred, struct proc *p); extern int hfs_chmod(struct vnode *vp, int mode, kauth_cred_t cred, struct proc *p); extern int hfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred, struct proc *p); __private_extern__ int hfs_vnop_readdirattr(struct vnop_readdirattr_args *ap); __private_extern__ int hfs_vnop_setattrlist(struct vnop_setattrlist_args *ap); __private_extern__ int hfs_vnop_getattrlist(struct vnop_getattrlist_args *ap); /* Packing routines: */ static void packvolcommonattr(struct attrblock *abp, struct hfsmount *hfsmp, struct vnode *vp, struct proc *p); static void packvolattr(struct attrblock *abp, struct hfsmount *hfsmp, struct vnode *vp); static void packcommonattr(struct attrblock *abp, struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc * cdp, struct cat_attr * cap, struct proc *p); static void packfileattr(struct attrblock *abp, struct hfsmount *hfsmp, struct cat_attr *cattrp, struct cat_fork *datafork, struct cat_fork *rsrcfork); static void packdirattr(struct attrblock *abp, struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc * descp, struct cat_attr * cattrp); #if 0 static int unpackattrblk(struct attrblock *abp, struct vnode *vp); static void unpackcommonattr(struct attrblock *abp, struct vnode *vp); static int unpackvolattr(struct attrblock *abp, struct hfsmount *hfsmp, struct vnode *root_vp); /* * Get a list of attributes. */ __private_extern__ int hfs_vnop_getattrlist(ap) struct vnop_getattrlist_args /* { struct vnode *a_vp; struct attrlist *a_alist struct uio *a_uio; int a_options; vfs_context_t a_context; } */ *ap; { struct vnode *vp = ap->a_vp; struct cnode *cp; struct hfsmount *hfsmp; struct attrlist *alist = ap->a_alist; proc_t p = vfs_context_proc(ap->a_context); int fixedblocksize; int attrblocksize; int attrbufsize; void *attrbufptr = NULL; void *attrptr; void *varptr; struct attrblock attrblk; struct cat_fork *datafp = NULL; struct cat_fork *rsrcfp = NULL; struct cat_fork rsrcfork; int lockflags; int error = 0; if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) || ((alist->commonattr & ~ATTR_CMN_VALIDMASK) != 0) || ((alist->volattr & ~ATTR_VOL_VALIDMASK) != 0) || ((alist->dirattr & ~ATTR_DIR_VALIDMASK) != 0) || ((alist->fileattr & ~ATTR_FILE_VALIDMASK) != 0)) { return (EINVAL); } /* * Requesting volume information requires setting the * ATTR_VOL_INFO bit. Also, volume info requests are * mutually exclusive with all other info requests. */ if ((alist->volattr != 0) && (((alist->volattr & ATTR_VOL_INFO) == 0) || (alist->dirattr != 0) || (alist->fileattr != 0))) { return (EINVAL); } /* Reject requests for unsupported options for now: */ if ((alist->commonattr & (ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST)) || (alist->fileattr & (ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST))) { return (EINVAL); } if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) return (error); cp = VTOC(vp); hfsmp = VTOHFS(vp); /* Requesting volume information requires root vnode */ if ((alist->volattr) && cp->c_fileid != kHFSRootFolderID) { error = EINVAL; goto exit; } /* Asking for data fork attributes from the rsrc fork is not supported */ if (VNODE_IS_RSRC(vp) && (alist->fileattr & ATTR_DATAFORK_MASK)) { error = EINVAL; goto exit; } /* This file no longer exists! */ if (cp->c_flag & (C_NOEXISTS | C_DELETED)) { error = ENOENT; goto exit; } /* This file doesn't have a name! */ if ((cp->c_desc.cd_namelen == 0) && (alist->commonattr & ATTR_CMN_NAME)) { error = ENOENT; goto exit; } /* Update cnode times if needed */ hfs_touchtimes(hfsmp, cp); /* * If a File ID (ATTR_CMN_OBJPERMANENTID) is requested on * an HFS volume we must be sure to create the thread * record before returning it. (yikes) */ if (vnode_isreg(vp) && (alist->commonattr & ATTR_CMN_OBJPERMANENTID) && (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)) { cat_cookie_t cookie; if (hfsmp->hfs_flags & HFS_READ_ONLY) { error = EROFS; goto exit; } if ((error = hfs_write_access(vp, vfs_context_ucred(ap->a_context), p, false)) != 0) { goto exit; } /* * Reserve some space in the Catalog file. */ bzero(&cookie, sizeof(cookie)); error = cat_preflight(hfsmp, CAT_CREATE, &cookie, p); if (error) { goto exit; } lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); error = cat_insertfilethread(hfsmp, &cp->c_desc); hfs_systemfile_unlock(hfsmp, lockflags); cat_postflight(hfsmp, &cookie, p); if (error) goto exit; } bzero(&rsrcfork, sizeof(rsrcfork)); /* Establish known fork data */ if (cp->c_datafork != NULL) { datafp = &cp->c_datafork->ff_data; if ((cp->c_rsrcfork == NULL) && (cp->c_blocks == datafp->cf_blocks)) rsrcfp = &rsrcfork; /* rsrc fork is empty */ } if (cp->c_rsrcfork != NULL) rsrcfp = &cp->c_rsrcfork->ff_data; /* * When resource fork data is requested and its not available * in the cnode and the fork is not empty then it needs to be * fetched from the catalog. */ if ((alist->fileattr & ATTR_RSRCFORK_MASK) && (rsrcfp == NULL)) { lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); /* Get resource fork data */ error = cat_lookup(hfsmp, &cp->c_desc, 1, (struct cat_desc *)0, (struct cat_attr *)0, &rsrcfork, NULL); hfs_systemfile_unlock(hfsmp, lockflags); if (error) goto exit; rsrcfp = &rsrcfork; } fixedblocksize = hfs_attrblksize(alist); attrblocksize = fixedblocksize + (sizeof(uint32_t)); /* uint32_t for length word */ if (alist->commonattr & ATTR_CMN_NAME) attrblocksize += kHFSPlusMaxFileNameBytes + 1; if (alist->volattr & ATTR_VOL_MOUNTPOINT) attrblocksize += PATH_MAX; if (alist->volattr & ATTR_VOL_NAME) attrblocksize += kHFSPlusMaxFileNameBytes + 1; #if 0 if (alist->commonattr & ATTR_CMN_NAMEDATTRLIST) attrblocksize += 0; if (alist->fileattr & ATTR_FILE_FORKLIST) attrblocksize += 0; #endif attrbufsize = MIN(uio_resid(ap->a_uio), attrblocksize); MALLOC(attrbufptr, void *, attrblocksize, M_TEMP, M_WAITOK); attrptr = attrbufptr; *((uint32_t *)attrptr) = 0; /* Set buffer length in case of errors */ ++((uint32_t *)attrptr); /* Reserve space for length field */ varptr = ((char *)attrptr) + fixedblocksize; attrblk.ab_attrlist = alist; attrblk.ab_attrbufpp = &attrptr; attrblk.ab_varbufpp = &varptr; attrblk.ab_flags = 0; attrblk.ab_blocksize = attrblocksize; hfs_packattrblk(&attrblk, hfsmp, vp, &cp->c_desc, &cp->c_attr, datafp, rsrcfp, p); /* Don't copy out more data than was generated */ attrbufsize = MIN((u_int)attrbufsize, (u_int)varptr - (u_int)attrbufptr); /* Set actual buffer length for return to caller */ *((uint32_t *)attrbufptr) = attrbufsize; error = uiomove((caddr_t)attrbufptr, attrbufsize, ap->a_uio); exit: if (attrbufptr) FREE(attrbufptr, M_TEMP); hfs_unlock(cp); return (error); } /* * Set a list of attributes. */ __private_extern__ int hfs_vnop_setattrlist(ap) struct vnop_setattrlist_args /* { struct vnode *a_vp; struct attrlist *a_alist struct uio *a_uio; int a_options; vfs_context_t a_context; } */ *ap; { struct vnode *vp = ap->a_vp; struct cnode *cp; struct hfsmount * hfsmp; struct attrlist *alist = ap->a_alist; kauth_cred_t cred = vfs_context_ucred(ap->a_context); struct proc *p = vfs_context_proc(ap->a_context); int attrblocksize; void *attrbufptr = NULL; void *attrptr; void *varptr = NULL; struct attrblock attrblk; uid_t saved_uid; gid_t saved_gid; mode_t saved_mode; uint32_t saved_flags; int error = 0; hfsmp = VTOHFS(vp); if (hfsmp->hfs_flags & HFS_READ_ONLY) return (EROFS); if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) || ((alist->commonattr & ~ATTR_CMN_SETMASK) != 0) || ((alist->volattr & ~ATTR_VOL_SETMASK) != 0) || ((alist->dirattr & ~ATTR_DIR_SETMASK) != 0) || ((alist->fileattr & ~ATTR_FILE_SETMASK) != 0)) { return (EINVAL); } if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) return (error); cp = VTOC(vp); /* * When setting volume attributes make sure * that ATTR_VOL_INFO is set and that all * the attributes are valid. */ if ((alist->volattr != 0) && (((alist->volattr & ATTR_VOL_INFO) == 0) || (alist->commonattr & ~ATTR_CMN_VOLSETMASK) || (cp->c_fileid != kHFSRootFolderID))) { if ((alist->volattr & ATTR_VOL_INFO) == 0) printf("hfs_setattrlist: you forgot to set ATTR_VOL_INFO bit!\n"); else printf("hfs_setattrlist: you cannot set bits 0x%08X!\n", alist->commonattr & ~ATTR_CMN_VOLSETMASK); error = EINVAL; goto ErrorExit; } if (cp->c_flag & (C_NOEXISTS | C_DELETED)) { error = ENOENT; goto ErrorExit; } // XXXdbg - don't allow modifying the journal or journal_info_block if (hfsmp->jnl && cp->c_datafork) { struct HFSPlusExtentDescriptor *extd; extd = &cp->c_datafork->ff_extents[0]; if (extd->startBlock == HFSTOVCB(hfsmp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) { error = EPERM; goto ErrorExit; } } /* * Ownership of a file is required in one of two classes of calls: * * (a) When setting any ownership-requiring attribute other * than ATTR_CMN_FLAGS, or * (b) When setting ATTR_CMN_FLAGS on a volume that's not * plain HFS (for which no real per-object ownership * information is stored) */ if ((alist->commonattr & (ATTR_OWNERSHIP_SETMASK & ~ATTR_CMN_FLAGS)) || ((alist->commonattr & ATTR_CMN_FLAGS) && (VTOVCB(vp)->vcbSigWord != kHFSSigWord))) { /* * NOTE: The following isn't ENTIRELY complete: even if * you're the superuser you cannot change the flags as * long as SF_IMMUTABLE or SF_APPEND is set and the * securelevel > 0. This is verified in hfs_chflags * which gets invoked to do the actual flags field * change so this check is sufficient for now. */ if ((error = hfs_owner_rights(hfsmp, cp->c_uid, cred, p, true)) != 0) goto ErrorExit; } /* * For any other attributes, check to see if the user has * write access to the cnode in question [unlike vn_access, * ignore IMMUTABLE here]: */ if (((alist->commonattr & ~ATTR_OWNERSHIP_SETMASK) != 0) || (alist->volattr != 0) || (alist->dirattr != 0) || (alist->fileattr != 0)) { if ((error = hfs_write_access(vp, cred, p, false)) != 0) goto ErrorExit; } /* * Allocate the buffer now to minimize the time we might * be blocked holding the catalog lock. */ // LP64todo - fix this attrblocksize = uio_resid(ap->a_uio); if (attrblocksize < hfs_attrblksize(alist)) { error = EINVAL; goto ErrorExit; } MALLOC(attrbufptr, void *, attrblocksize, M_TEMP, M_WAITOK); error = uiomove((caddr_t)attrbufptr, attrblocksize, ap->a_uio); if (error) goto ErrorExit; /* Save original state so changes can be detected. */ saved_uid = cp->c_uid; saved_gid = cp->c_gid; saved_mode = cp->c_mode; saved_flags = cp->c_flags; attrptr = attrbufptr; attrblk.ab_attrlist = alist; attrblk.ab_attrbufpp = &attrptr; attrblk.ab_varbufpp = &varptr; attrblk.ab_flags = 0; attrblk.ab_blocksize = attrblocksize; error = unpackattrblk(&attrblk, vp); if (error) goto ErrorExit; /* If unpacking changed the owner/group then call hfs_chown() */ if ((saved_uid != cp->c_uid) || (saved_gid != cp->c_gid)) { uid_t uid; gid_t gid; uid = cp->c_uid; cp->c_uid = saved_uid; gid = cp->c_gid; cp->c_gid = saved_gid; if ((error = hfs_chown(vp, uid, gid, cred, p))) goto ErrorExit; } /* If unpacking changed the mode then call hfs_chmod() */ if (saved_mode != cp->c_mode) { mode_t mode; mode = cp->c_mode; cp->c_mode = saved_mode; if ((error = hfs_chmod(vp, mode, cred, p))) goto ErrorExit; } /* If unpacking changed the flags then call hfs_chflags() */ if (saved_flags !=cp->c_flags) { uint32_t flags; flags = cp->c_flags; cp->c_flags = saved_flags; if ((error = hfs_chflags(vp, flags, cred, p))) goto ErrorExit; } /* * If any cnode attributes changed then do an update. */ if (alist->volattr == 0) { cp->c_flag |= C_MODIFIED; if ((error = hfs_update(vp, TRUE))) { goto ErrorExit; } } /* Volume Rename */ if (alist->volattr & ATTR_VOL_NAME) { ExtendedVCB *vcb = VTOVCB(vp); if (vcb->vcbVN[0] == 0) { /* * Ignore attempts to rename a volume to a zero-length name: * restore the original name from the cnode. */ copystr(cp->c_desc.cd_nameptr, vcb->vcbVN, sizeof(vcb->vcbVN), NULL); } else { struct cat_desc to_desc; struct cat_desc todir_desc; struct cat_desc new_desc; cat_cookie_t cookie; int catreserve = 0; int catlocked = 0; int started_tr = 0; int lockflags; bzero(&to_desc, sizeof(to_desc)); bzero(&todir_desc, sizeof(todir_desc)); bzero(&new_desc, sizeof(new_desc)); bzero(&cookie, sizeof(cookie)); todir_desc.cd_parentcnid = kHFSRootParentID; todir_desc.cd_cnid = kHFSRootFolderID; todir_desc.cd_flags = CD_ISDIR; to_desc.cd_nameptr = vcb->vcbVN; to_desc.cd_namelen = strlen(vcb->vcbVN); to_desc.cd_parentcnid = kHFSRootParentID; to_desc.cd_cnid = cp->c_cnid; to_desc.cd_flags = CD_ISDIR; if ((error = hfs_start_transaction(hfsmp) != 0)) { goto rename_out; } started_tr = 1; /* * Reserve some space in the Catalog file. */ error = cat_preflight(hfsmp, CAT_RENAME, &cookie, p); if (error) { goto rename_out; } catreserve = 1; lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); catlocked = 1; error = cat_rename(hfsmp, &cp->c_desc, &todir_desc, &to_desc, &new_desc); rename_out: if (catlocked) { hfs_systemfile_unlock(hfsmp, lockflags); } if (catreserve) { cat_postflight(hfsmp, &cookie, p); } (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0); if (started_tr) { hfs_end_transaction(hfsmp); } if (error) { /* Restore the old name in the VCB */ copystr(cp->c_desc.cd_nameptr, vcb->vcbVN, sizeof(vcb->vcbVN), NULL); vcb->vcbFlags |= 0xFF00; goto ErrorExit; } /* Release old allocated name buffer */ if (cp->c_desc.cd_flags & CD_HASBUF) { char *name = cp->c_desc.cd_nameptr; cp->c_desc.cd_nameptr = 0; cp->c_desc.cd_namelen = 0; cp->c_desc.cd_flags &= ~CD_HASBUF; vfs_removename(name); } /* Update cnode's catalog descriptor */ replace_desc(cp, &new_desc); vcb->volumeNameEncodingHint = new_desc.cd_encoding; cp->c_touch_chgtime = TRUE; } } /* * When the volume name changes or the volume's finder info * changes then force them to disk immediately. */ if ((alist->volattr & ATTR_VOL_INFO) && ((alist->volattr & ATTR_VOL_NAME) || (alist->commonattr & ATTR_CMN_FNDRINFO))) { (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0); } ErrorExit: if (attrbufptr) FREE(attrbufptr, M_TEMP); hfs_unlock(cp); return (error); } #endif /* * readdirattr operation will return attributes for the items in the * directory specified. * * It does not do . and .. entries. The problem is if you are at the root of the * hfs directory and go to .. you could be crossing a mountpoint into a * different (ufs) file system. The attributes that apply for it may not * apply for the file system you are doing the readdirattr on. To make life * simpler, this call will only return entries in its directory, hfs like. * TO DO LATER: * 1. more than one for uiovcnt support. * 2. put knohint (hints) in state for next call in * 3. credentials checking when rest of hfs does it. * 4. Do return permissions concatenation ??? */ /* # #% readdirattr vp L L L # vnop_readdirattr { IN struct vnode *vp; IN struct attrlist *alist; INOUT struct uio *uio; IN u_long maxcount: IN u_long options; OUT u_long *newstate; OUT int *eofflag; OUT u_long *actualCount; OUT u_long **cookies; IN kauth_cred_t cred; }; */ __private_extern__ int hfs_vnop_readdirattr(ap) struct vnop_readdirattr_args /* { struct vnode *a_vp; struct attrlist *a_alist; struct uio *a_uio; u_long a_maxcount; u_long a_options; u_long *a_newstate; int *a_eofflag; u_long *a_actualcount; vfs_context_t a_context; } */ *ap; { struct vnode *dvp = ap->a_vp; struct cnode *dcp; struct hfsmount * hfsmp; struct attrlist *alist = ap->a_alist; uio_t uio = ap->a_uio; int maxcount = ap->a_maxcount; struct proc *p = vfs_context_proc(ap->a_context); uint32_t fixedblocksize; uint32_t maxattrblocksize; uint32_t currattrbufsize; void *attrbufptr = NULL; void *attrptr; void *varptr; struct attrblock attrblk; int error = 0; int depleted = 0; int index; int i, dir_entries; struct cat_desc *lastdescp = NULL; struct cat_entrylist *ce_list = NULL; directoryhint_t *dirhint = NULL; unsigned int tag; int shared_cnode_lock = 0; *(ap->a_actualcount) = 0; *(ap->a_eofflag) = 0; /* Check for invalid options and buffer space. */ if (((ap->a_options & ~(FSOPT_NOINMEMUPDATE | FSOPT_NOFOLLOW)) != 0) || (uio_resid(uio) <= 0) || (uio_iovcnt(uio) > 1) || (maxcount <= 0)) return (EINVAL); /* This call doesn't take volume attributes. */ if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) || ((alist->commonattr & ~ATTR_CMN_VALIDMASK) != 0) || (alist->volattr != 0) || ((alist->dirattr & ~ATTR_DIR_VALIDMASK) != 0) || ((alist->fileattr & ~ATTR_FILE_VALIDMASK) != 0)) return (EINVAL); if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK))) return (error); dcp = VTOC(dvp); hfsmp = VTOHFS(dvp); /* Reject requests for unsupported options. */ if ((alist->commonattr & (ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST | ATTR_CMN_OBJPERMANENTID)) || (alist->fileattr & (ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST | ATTR_FILE_DATAEXTENTS | ATTR_FILE_RSRCEXTENTS))) { printf("readdirattr: unsupported attributes! (%s)\n", dcp->c_desc.cd_nameptr); error = EINVAL; goto exit; } dir_entries = dcp->c_entries; if (dcp->c_attr.ca_fileid == kHFSRootFolderID && hfsmp->jnl) { dir_entries -= 3; } /* Convert uio_offset into a directory index. */ index = uio_offset(uio) & HFS_INDEX_MASK; tag = uio_offset(uio) & ~HFS_INDEX_MASK; if ((index + 1) > dir_entries) { *(ap->a_eofflag) = 1; error = 0; goto exit; } /* Get a buffer to hold packed attributes. */ fixedblocksize = (sizeof(uint32_t) + hfs_attrblksize(alist)); /* 4 bytes for length */ maxattrblocksize = fixedblocksize; if (alist->commonattr & ATTR_CMN_NAME) maxattrblocksize += kHFSPlusMaxFileNameBytes + 1; MALLOC(attrbufptr, void *, maxattrblocksize, M_TEMP, M_WAITOK); attrptr = attrbufptr; varptr = (char *)attrbufptr + fixedblocksize; /* Point to variable-length storage */ /* Initialize a catalog entry list. */ MALLOC(ce_list, struct cat_entrylist *, sizeof(*ce_list), M_TEMP, M_WAITOK); bzero(ce_list, sizeof(*ce_list)); ce_list->maxentries = MAXCATENTRIES; /* Get a directory hint (cnode must be locked exclusive) */ dirhint = hfs_getdirhint(dcp, ((index - 1) & HFS_INDEX_MASK) | tag); /* Hide tag from catalog layer. */ dirhint->dh_index &= HFS_INDEX_MASK; if (dirhint->dh_index == HFS_INDEX_MASK) { dirhint->dh_index = -1; } /* * An ATTR_CMN_USERACCESS attribute request can result in a * call to kauth_cred_ismember_gid(). So when requesting * this attribute we downgrade our exclusive lock on dcp to * a shared lock in case kauth_cred_ismember_gid generates * an indirect call back into the file system. */ if (alist->commonattr & ATTR_CMN_USERACCESS) { lck_rw_lock_exclusive_to_shared(&dcp->c_rwlock); dcp->c_lockowner = HFS_SHARED_OWNER; shared_cnode_lock = 1; } /* * Obtain a list of catalog entries and pack their attributes until * the output buffer is full or maxcount entries have been packed. */ while (!depleted) { int maxentries; int lockflags; /* Constrain our list size. */ maxentries = uio_resid(uio) / (fixedblocksize + HFS_AVERAGE_NAME_SIZE); maxentries = min(maxentries, dcp->c_entries - index); maxentries = min(maxentries, maxcount); ce_list->maxentries = min(maxentries, ce_list->maxentries); lastdescp = NULL; lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK); error = cat_getentriesattr(hfsmp, dirhint, ce_list); /* Don't forget to release the descriptors later! */ hfs_systemfile_unlock(hfsmp, lockflags); if (error == ENOENT) { *(ap->a_eofflag) = TRUE; error = 0; depleted = 1; } if (error) break; /* Process the catalog entries. */ for (i = 0; i < (int)ce_list->realentries; ++i) { struct cnode *cp = NULL; struct vnode *vp = NULL; struct cat_desc * cdescp; struct cat_attr * cattrp; struct cat_fork c_datafork; struct cat_fork c_rsrcfork; bzero(&c_datafork, sizeof(c_datafork)); bzero(&c_rsrcfork, sizeof(c_rsrcfork)); cdescp = &ce_list->entry[i].ce_desc; cattrp = &ce_list->entry[i].ce_attr; c_datafork.cf_size = ce_list->entry[i].ce_datasize; c_datafork.cf_blocks = ce_list->entry[i].ce_datablks; c_rsrcfork.cf_size = ce_list->entry[i].ce_rsrcsize; c_rsrcfork.cf_blocks = ce_list->entry[i].ce_rsrcblks; /* * Get in memory cnode data (if any). */ if (!(ap->a_options & FSOPT_NOINMEMUPDATE)) { vp = hfs_chash_getvnode(dcp->c_dev, cattrp->ca_fileid, 0, 0); if (vp != NULL) { cp = VTOC(vp); /* Only use cnode's decriptor for non-hardlinks */ if (!(cp->c_flag & C_HARDLINK)) cdescp = &cp->c_desc; cattrp = &cp->c_attr; if (cp->c_datafork) { c_datafork.cf_size = cp->c_datafork->ff_size; c_datafork.cf_blocks = cp->c_datafork->ff_blocks; } if (cp->c_rsrcfork) { c_rsrcfork.cf_size = cp->c_rsrcfork->ff_size; c_rsrcfork.cf_blocks = cp->c_rsrcfork->ff_blocks; } } } *((uint32_t *)attrptr)++ = 0; /* move it past length */ attrblk.ab_attrlist = alist; attrblk.ab_attrbufpp = &attrptr; attrblk.ab_varbufpp = &varptr; attrblk.ab_flags = 0; attrblk.ab_blocksize = maxattrblocksize; /* Pack catalog entries into attribute buffer. */ hfs_packattrblk(&attrblk, hfsmp, vp, cdescp, cattrp, &c_datafork, &c_rsrcfork, p); currattrbufsize = ((char *)varptr - (char *)attrbufptr); /* All done with cnode. */ if (vp != NULL) { hfs_unlock(VTOC(vp)); vnode_put(vp); vp = NULL; cp = NULL; } /* Make sure there's enough buffer space remaining. */ // LP64todo - fix this! if (uio_resid(uio) < 0 || currattrbufsize > (uint32_t)uio_resid(uio)) { depleted = 1; break; } else { *((uint32_t *)attrbufptr) = currattrbufsize; error = uiomove((caddr_t)attrbufptr, currattrbufsize, ap->a_uio); if (error != E_NONE) { depleted = 1; break; } attrptr = attrbufptr; varptr = (char *)attrbufptr + fixedblocksize; /* Point to variable-length storage */ /* Save the last valid catalog entry */ lastdescp = &ce_list->entry[i].ce_desc; index++; *ap->a_actualcount += 1; /* Termination checks */ if ((--maxcount <= 0) || // LP64todo - fix this! uio_resid(uio) < 0 || ((uint32_t)uio_resid(uio) < (fixedblocksize + HFS_AVERAGE_NAME_SIZE)) || (index >= dir_entries)) { depleted = 1; break; } } } /* for each catalog entry */ /* If there are more entries then save the last name. */ if (index < dir_entries && !(*(ap->a_eofflag)) && lastdescp != NULL) { /* Remember last entry */ if (dirhint->dh_desc.cd_nameptr != NULL) { vfs_removename(dirhint->dh_desc.cd_nameptr); } dirhint->dh_desc.cd_namelen = lastdescp->cd_namelen; dirhint->dh_desc.cd_nameptr = vfs_addname(lastdescp->cd_nameptr, lastdescp->cd_namelen, 0, 0); dirhint->dh_index = index - 1; dirhint->dh_desc.cd_cnid = lastdescp->cd_cnid; dirhint->dh_desc.cd_hint = lastdescp->cd_hint; dirhint->dh_desc.cd_encoding = lastdescp->cd_encoding; } /* All done with the catalog descriptors. */ for (i = 0; i < (int)ce_list->realentries; ++i) cat_releasedesc(&ce_list->entry[i].ce_desc); ce_list->realentries = 0; } /* while not depleted */ *ap->a_newstate = dcp->c_mtime; /* Make sure dcp is locked exclusive before changing c_dirhinttag. */ if (shared_cnode_lock) { lck_rw_lock_shared_to_exclusive(&dcp->c_rwlock); dcp->c_lockowner = current_thread(); shared_cnode_lock = 0; } /* Convert directory index back into a uio_offset. */ while (tag == 0) tag = (++dcp->c_dirhinttag) << HFS_INDEX_BITS; uio_setoffset(uio, index | tag); dirhint->dh_index |= tag; exit: /* Drop directory hint on error or if there are no more entries */ if (dirhint && (error || index >= dir_entries)) { if (shared_cnode_lock) { lck_rw_lock_shared_to_exclusive(&dcp->c_rwlock); dcp->c_lockowner = current_thread(); } hfs_reldirhint(dcp, dirhint); } if (attrbufptr) FREE(attrbufptr, M_TEMP); if (ce_list) FREE(ce_list, M_TEMP); hfs_unlock(dcp); return (error); } /*==================== Attribute list support routines ====================*/ /* * Pack cnode attributes into an attribute block. */ __private_extern__ void hfs_packattrblk(struct attrblock *abp, struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *datafork, struct cat_fork *rsrcfork, struct proc *p) { struct attrlist *attrlistp = abp->ab_attrlist; if (attrlistp->volattr) { if (attrlistp->commonattr) packvolcommonattr(abp, hfsmp, vp, p); if (attrlistp->volattr & ~ATTR_VOL_INFO) packvolattr(abp, hfsmp, vp); } else { if (attrlistp->commonattr) packcommonattr(abp, hfsmp, vp, descp, attrp, p); if (attrlistp->dirattr && S_ISDIR(attrp->ca_mode)) packdirattr(abp, hfsmp, vp, descp,attrp); if (attrlistp->fileattr && !S_ISDIR(attrp->ca_mode)) packfileattr(abp, hfsmp, attrp, datafork, rsrcfork); } } static char* mountpointname(struct mount *mp) { size_t namelength = strlen(mp->mnt_vfsstat.f_mntonname); int foundchars = 0; char *c; if (namelength == 0) return (NULL); /* * Look backwards through the name string, looking for * the first slash encountered (which must precede the * last part of the pathname). */ for (c = mp->mnt_vfsstat.f_mntonname + namelength - 1; namelength > 0; --c, --namelength) { if (*c != '/') { foundchars = 1; } else if (foundchars) { return (c + 1); } } return (mp->mnt_vfsstat.f_mntonname); } static void packnameattr( struct attrblock *abp, struct vnode *vp, char *name, int namelen) { void *varbufptr; struct attrreference * attr_refptr; char *mpname; size_t mpnamelen; uint32_t attrlength; char empty = 0; /* A cnode's name may be incorrect for the root of a mounted * filesystem (it can be mounted on a different directory name * than the name of the volume, such as "blah-1"). So for the * root directory, it's best to return the last element of the location where the volume's mounted: */ if ((vp != NULL) && vnode_isvroot(vp) && (mpname = mountpointname(vnode_mount(vp)))) { mpnamelen = strlen(mpname); /* Trim off any trailing slashes: */ while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/')) --mpnamelen; /* If there's anything left, use it instead of the volume's name */ if (mpnamelen > 0) { name = mpname; namelen = mpnamelen; } } if (name == NULL) { name = ∅ namelen = 0; } varbufptr = *abp->ab_varbufpp; attr_refptr = (struct attrreference *)(*abp->ab_attrbufpp); attrlength = namelen + 1; attr_refptr->attr_dataoffset = (char *)varbufptr - (char *)attr_refptr; attr_refptr->attr_length = attrlength; (void) strncpy((unsigned char *)varbufptr, name, attrlength); /* * Advance beyond the space just allocated and * round up to the next 4-byte boundary: */ (char *)(varbufptr) += attrlength + ((4 - (attrlength & 3)) & 3); ++attr_refptr; *abp->ab_attrbufpp = attr_refptr; *abp->ab_varbufpp = varbufptr; } /* * Pack common volume attributes. */ static void packvolcommonattr(struct attrblock *abp, struct hfsmount *hfsmp, struct vnode *vp, struct proc *p) { attrgroup_t attr; void *attrbufptr = *abp->ab_attrbufpp; void *varbufptr = *abp->ab_varbufpp; struct cnode *cp = VTOC(vp); struct mount *mp = VTOVFS(vp); ExtendedVCB *vcb = HFSTOVCB(hfsmp); u_int32_t attrlength; boolean_t is_64_bit = proc_is64bit(p); attr = abp->ab_attrlist->commonattr; if (ATTR_CMN_NAME & attr) { packnameattr(abp, vp, cp->c_desc.cd_nameptr, cp->c_desc.cd_namelen); attrbufptr = *abp->ab_attrbufpp; varbufptr = *abp->ab_varbufpp; } if (ATTR_CMN_DEVID & attr) { *((dev_t *)attrbufptr)++ = hfsmp->hfs_raw_dev; } if (ATTR_CMN_FSID & attr) { fsid_t fsid; fsid.val[0] = (long)hfsmp->hfs_raw_dev; fsid.val[1] = (long)vfs_typenum(mp); *((fsid_t *)attrbufptr) = fsid; ++((fsid_t *)attrbufptr); } if (ATTR_CMN_OBJTYPE & attr) { *((fsobj_type_t *)attrbufptr)++ = 0; } if (ATTR_CMN_OBJTAG & attr) { *((fsobj_tag_t *)attrbufptr)++ = VT_HFS; } if (ATTR_CMN_OBJID & attr) { ((fsobj_id_t *)attrbufptr)->fid_objno = 0; ((fsobj_id_t *)attrbufptr)->fid_generation = 0; ++((fsobj_id_t *)attrbufptr); } if (ATTR_CMN_OBJPERMANENTID & attr) { ((fsobj_id_t *)attrbufptr)->fid_objno = 0; ((fsobj_id_t *)attrbufptr)->fid_generation = 0; ++((fsobj_id_t *)attrbufptr); } if (ATTR_CMN_PAROBJID & attr) { ((fsobj_id_t *)attrbufptr)->fid_objno = 0; ((fsobj_id_t *)attrbufptr)->fid_generation = 0; ++((fsobj_id_t *)attrbufptr); } if (ATTR_CMN_SCRIPT & attr) { uint32_t encoding; if (vcb->vcbSigWord == kHFSPlusSigWord) encoding = vcb->volumeNameEncodingHint; else encoding = hfsmp->hfs_encoding; *((text_encoding_t *)attrbufptr)++ = encoding; } if (ATTR_CMN_CRTIME & attr) { if (is_64_bit) { ((struct user_timespec *)attrbufptr)->tv_sec = vcb->vcbCrDate; ((struct user_timespec *)attrbufptr)->tv_nsec = 0; ++((struct user_timespec *)attrbufptr); } else { ((struct timespec *)attrbufptr)->tv_sec = vcb->vcbCrDate; ((struct timespec *)attrbufptr)->tv_nsec = 0; ++((struct timespec *)attrbufptr); } } if (ATTR_CMN_MODTIME & attr) { if (is_64_bit) { ((struct user_timespec *)attrbufptr)->tv_sec = vcb->vcbLsMod; ((struct user_timespec *)attrbufptr)->tv_nsec = 0; ++((struct user_timespec *)attrbufptr); } else { ((struct timespec *)attrbufptr)->tv_sec = vcb->vcbLsMod; ((struct timespec *)attrbufptr)->tv_nsec = 0; ++((struct timespec *)attrbufptr); } } if (ATTR_CMN_CHGTIME & attr) { if (is_64_bit) { ((struct user_timespec *)attrbufptr)->tv_sec = vcb->vcbLsMod; ((struct user_timespec *)attrbufptr)->tv_nsec = 0; ++((struct user_timespec *)attrbufptr); } else { ((struct timespec *)attrbufptr)->tv_sec = vcb->vcbLsMod; ((struct timespec *)attrbufptr)->tv_nsec = 0; ++((struct timespec *)attrbufptr); } } if (ATTR_CMN_ACCTIME & attr) { if (is_64_bit) { ((struct user_timespec *)attrbufptr)->tv_sec = vcb->vcbLsMod; ((struct user_timespec *)attrbufptr)->tv_nsec = 0; ++((struct user_timespec *)attrbufptr); } else { ((struct timespec *)attrbufptr)->tv_sec = vcb->vcbLsMod; ((struct timespec *)attrbufptr)->tv_nsec = 0; ++((struct timespec *)attrbufptr); } } if (ATTR_CMN_BKUPTIME & attr) { if (is_64_bit) { ((struct user_timespec *)attrbufptr)->tv_sec = vcb->vcbVolBkUp; ((struct user_timespec *)attrbufptr)->tv_nsec = 0; ++((struct user_timespec *)attrbufptr); } else { ((struct timespec *)attrbufptr)->tv_sec = vcb->vcbVolBkUp; ((struct timespec *)attrbufptr)->tv_nsec = 0; ++((struct timespec *)attrbufptr); } } if (ATTR_CMN_FNDRINFO & attr) { bcopy (&vcb->vcbFndrInfo, attrbufptr, sizeof(vcb->vcbFndrInfo)); (char *)attrbufptr += sizeof(vcb->vcbFndrInfo); } if (ATTR_CMN_OWNERID & attr) { if (cp->c_uid == UNKNOWNUID) *((uid_t *)attrbufptr)++ = kauth_cred_getuid(proc_ucred(p)); else *((uid_t *)attrbufptr)++ = cp->c_uid; } if (ATTR_CMN_GRPID & attr) { *((gid_t *)attrbufptr)++ = cp->c_gid; } if (ATTR_CMN_ACCESSMASK & attr) { /* * [2856576] Since we are dynamically changing the owner, also * effectively turn off the set-user-id and set-group-id bits, * just like chmod(2) would when changing ownership. This prevents * a security hole where set-user-id programs run as whoever is * logged on (or root if nobody is logged in yet!) */ *((uint32_t *)attrbufptr)++ = (cp->c_uid == UNKNOWNUID) ? cp->c_mode & ~(S_ISUID | S_ISGID) : cp->c_mode; } if (ATTR_CMN_NAMEDATTRCOUNT & attr) { *((uint32_t *)attrbufptr)++ = 0; /* XXX PPD TBC */ } if (ATTR_CMN_NAMEDATTRLIST & attr) { attrlength = 0; ((struct attrreference *)attrbufptr)->attr_dataoffset = 0; ((struct attrreference *)attrbufptr)->attr_length = attrlength; /* * Advance beyond the space just allocated and * round up to the next 4-byte boundary: */ (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3); ++((struct attrreference *)attrbufptr); } if (ATTR_CMN_FLAGS & attr) { *((uint32_t *)attrbufptr)++ = cp->c_flags; } if (ATTR_CMN_USERACCESS & attr) { *((uint32_t *)attrbufptr)++ = DerivePermissionSummary(cp->c_uid, cp->c_gid, cp->c_mode, VTOVFS(vp), kauth_cred_get(), proc_self()); } *abp->ab_attrbufpp = attrbufptr; *abp->ab_varbufpp = varbufptr; } static void packvolattr(struct attrblock *abp, struct hfsmount *hfsmp, struct vnode *vp) { attrgroup_t attr; void *attrbufptr = *abp->ab_attrbufpp; void *varbufptr = *abp->ab_varbufpp; struct cnode *cp = VTOC(vp); struct mount *mp = VTOVFS(vp); ExtendedVCB *vcb = HFSTOVCB(hfsmp); uint32_t attrlength; attr = abp->ab_attrlist->volattr; if (ATTR_VOL_FSTYPE & attr) { *((uint32_t *)attrbufptr)++ = (uint32_t)vfs_typenum(mp); } if (ATTR_VOL_SIGNATURE & attr) { *((uint32_t *)attrbufptr)++ = (uint32_t)vcb->vcbSigWord; } if (ATTR_VOL_SIZE & attr) { *((off_t *)attrbufptr)++ = (off_t)vcb->totalBlocks * (off_t)vcb->blockSize; } if (ATTR_VOL_SPACEFREE & attr) { *((off_t *)attrbufptr)++ = (off_t)hfs_freeblks(hfsmp, 0) * (off_t)vcb->blockSize; } if (ATTR_VOL_SPACEAVAIL & attr) { *((off_t *)attrbufptr)++ = (off_t)hfs_freeblks(hfsmp, 1) * (off_t)vcb->blockSize; } if (ATTR_VOL_MINALLOCATION & attr) { *((off_t *)attrbufptr)++ = (off_t)vcb->blockSize; } if (ATTR_VOL_ALLOCATIONCLUMP & attr) { *((off_t *)attrbufptr)++ = (off_t)(vcb->vcbClpSiz); } if (ATTR_VOL_IOBLOCKSIZE & attr) { *((uint32_t *)attrbufptr)++ = hfsmp->hfs_logBlockSize; } if (ATTR_VOL_OBJCOUNT & attr) { *((uint32_t *)attrbufptr)++ = (uint32_t)vcb->vcbFilCnt + (uint32_t)vcb->vcbDirCnt; } if (ATTR_VOL_FILECOUNT & attr) { *((uint32_t *)attrbufptr)++ = (uint32_t)vcb->vcbFilCnt; } if (ATTR_VOL_DIRCOUNT & attr) { *((uint32_t *)attrbufptr)++ = (uint32_t)vcb->vcbDirCnt; } if (ATTR_VOL_MAXOBJCOUNT & attr) { *((uint32_t *)attrbufptr)++ = 0xFFFFFFFF; } if (ATTR_VOL_MOUNTPOINT & attr) { ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr; ((struct attrreference *)attrbufptr)->attr_length = strlen(mp->mnt_vfsstat.f_mntonname) + 1; attrlength = ((struct attrreference *)attrbufptr)->attr_length; /* round up to the next 4-byte boundary: */ attrlength = attrlength + ((4 - (attrlength & 3)) & 3); (void) bcopy(mp->mnt_vfsstat.f_mntonname, varbufptr, attrlength); /* Advance beyond the space just allocated: */ (char *)varbufptr += attrlength; ++((struct attrreference *)attrbufptr); } if (ATTR_VOL_NAME & attr) { ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr; ((struct attrreference *)attrbufptr)->attr_length = cp->c_desc.cd_namelen + 1; attrlength = ((struct attrreference *)attrbufptr)->attr_length; /* round up to the next 4-byte boundary: */ attrlength = attrlength + ((4 - (attrlength & 3)) & 3); /* XXX this could read off the end of cd_nameptr! */ bcopy(cp->c_desc.cd_nameptr, varbufptr, attrlength); /* Advance beyond the space just allocated: */ (char *)varbufptr += attrlength; ++((struct attrreference *)attrbufptr); } if (ATTR_VOL_MOUNTFLAGS & attr) { *((uint32_t *)attrbufptr)++ = (uint32_t)vfs_flags(mp); } if (ATTR_VOL_MOUNTEDDEVICE & attr) { ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr; ((struct attrreference *)attrbufptr)->attr_length = strlen(mp->mnt_vfsstat.f_mntfromname) + 1; attrlength = ((struct attrreference *)attrbufptr)->attr_length; /* round up to the next 4-byte boundary: */ attrlength = attrlength + ((4 - (attrlength & 3)) & 3); (void) bcopy(mp->mnt_vfsstat.f_mntfromname, varbufptr, attrlength); /* Advance beyond the space just allocated: */ (char *)varbufptr += attrlength; ++((struct attrreference *)attrbufptr); } if (ATTR_VOL_ENCODINGSUSED & attr) { *((unsigned long long *)attrbufptr)++ = (unsigned long long)vcb->encodingsBitmap; } if (ATTR_VOL_CAPABILITIES & attr) { vol_capabilities_attr_t *vcapattrptr; vcapattrptr = (vol_capabilities_attr_t *)attrbufptr; if (vcb->vcbSigWord == kHFSPlusSigWord) { u_int32_t journal_active_cap; u_int32_t case_sensitive; if (hfsmp->jnl) journal_active_cap = VOL_CAP_FMT_JOURNAL_ACTIVE; else journal_active_cap = 0; if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE) case_sensitive = VOL_CAP_FMT_CASE_SENSITIVE; else case_sensitive = 0; vcapattrptr->capabilities[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_SYMBOLICLINKS | VOL_CAP_FMT_HARDLINKS | VOL_CAP_FMT_JOURNAL | journal_active_cap | case_sensitive | VOL_CAP_FMT_CASE_PRESERVING | VOL_CAP_FMT_FAST_STATFS | VOL_CAP_FMT_2TB_FILESIZE; } else { /* Plain HFS */ vcapattrptr->capabilities[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_CASE_PRESERVING | VOL_CAP_FMT_FAST_STATFS ; } vcapattrptr->capabilities[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_SEARCHFS | VOL_CAP_INT_ATTRLIST | VOL_CAP_INT_NFSEXPORT | VOL_CAP_INT_READDIRATTR | VOL_CAP_INT_EXCHANGEDATA | VOL_CAP_INT_ALLOCATE | VOL_CAP_INT_VOL_RENAME | VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK ; vcapattrptr->capabilities[VOL_CAPABILITIES_RESERVED1] = 0; vcapattrptr->capabilities[VOL_CAPABILITIES_RESERVED2] = 0; vcapattrptr->valid[VOL_CAPABILITIES_FORMAT] = VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_SYMBOLICLINKS | VOL_CAP_FMT_HARDLINKS | VOL_CAP_FMT_JOURNAL | VOL_CAP_FMT_JOURNAL_ACTIVE | VOL_CAP_FMT_NO_ROOT_TIMES | VOL_CAP_FMT_SPARSE_FILES | VOL_CAP_FMT_ZERO_RUNS | VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING | VOL_CAP_FMT_FAST_STATFS | VOL_CAP_FMT_2TB_FILESIZE; vcapattrptr->valid[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_SEARCHFS | VOL_CAP_INT_ATTRLIST | VOL_CAP_INT_NFSEXPORT | VOL_CAP_INT_READDIRATTR | VOL_CAP_INT_EXCHANGEDATA | VOL_CAP_INT_COPYFILE | VOL_CAP_INT_ALLOCATE | VOL_CAP_INT_VOL_RENAME | VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK ; vcapattrptr->valid[VOL_CAPABILITIES_RESERVED1] = 0; vcapattrptr->valid[VOL_CAPABILITIES_RESERVED2] = 0; ++((vol_capabilities_attr_t *)attrbufptr); } if (ATTR_VOL_ATTRIBUTES & attr) { vol_attributes_attr_t *volattrattrp; volattrattrp = (vol_attributes_attr_t *)attrbufptr; volattrattrp->validattr.commonattr = ATTR_CMN_VALIDMASK; volattrattrp->validattr.volattr = ATTR_VOL_VALIDMASK; volattrattrp->validattr.dirattr = ATTR_DIR_VALIDMASK; volattrattrp->validattr.fileattr = ATTR_FILE_VALIDMASK; volattrattrp->validattr.forkattr = ATTR_FORK_VALIDMASK; volattrattrp->nativeattr.commonattr = ATTR_CMN_VALIDMASK; volattrattrp->nativeattr.volattr = ATTR_VOL_VALIDMASK; volattrattrp->nativeattr.dirattr = ATTR_DIR_VALIDMASK; volattrattrp->nativeattr.fileattr = ATTR_FILE_VALIDMASK; volattrattrp->nativeattr.forkattr = ATTR_FORK_VALIDMASK; ++((vol_attributes_attr_t *)attrbufptr); } *abp->ab_attrbufpp = attrbufptr; *abp->ab_varbufpp = varbufptr; } static void packcommonattr( struct attrblock *abp, struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc * cdp, struct cat_attr * cap, struct proc *p) { attrgroup_t attr = abp->ab_attrlist->commonattr; struct mount *mp = HFSTOVFS(hfsmp); void *attrbufptr = *abp->ab_attrbufpp; void *varbufptr = *abp->ab_varbufpp; uint32_t attrlength = 0; boolean_t is_64_bit = proc_is64bit(p); if (ATTR_CMN_NAME & attr) { packnameattr(abp, vp, cdp->cd_nameptr, cdp->cd_namelen); attrbufptr = *abp->ab_attrbufpp; varbufptr = *abp->ab_varbufpp; } if (ATTR_CMN_DEVID & attr) { *((dev_t *)attrbufptr)++ = hfsmp->hfs_raw_dev; } if (ATTR_CMN_FSID & attr) { fsid_t fsid; fsid.val[0] = (long)hfsmp->hfs_raw_dev; fsid.val[1] = (long)vfs_typenum(mp); *((fsid_t *)attrbufptr) = fsid; ++((fsid_t *)attrbufptr); } if (ATTR_CMN_OBJTYPE & attr) { *((fsobj_type_t *)attrbufptr)++ = IFTOVT(cap->ca_mode); } if (ATTR_CMN_OBJTAG & attr) { *((fsobj_tag_t *)attrbufptr)++ = VT_HFS; } /* * Exporting file IDs from HFS Plus: * * For "normal" files the c_fileid is the same value as the * c_cnid. But for hard link files, they are different - the * c_cnid belongs to the active directory entry (ie the link) * and the c_fileid is for the actual inode (ie the data file). * * The stat call (getattr) will always return the c_fileid * and Carbon APIs, which are hardlink-ignorant, will always * receive the c_cnid (from getattrlist). */ if (ATTR_CMN_OBJID & attr) { ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid; ((fsobj_id_t *)attrbufptr)->fid_generation = 0; ++((fsobj_id_t *)attrbufptr); } if (ATTR_CMN_OBJPERMANENTID & attr) { ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid; ((fsobj_id_t *)attrbufptr)->fid_generation = 0; ++((fsobj_id_t *)attrbufptr); } if (ATTR_CMN_PAROBJID & attr) { ((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_parentcnid; ((fsobj_id_t *)attrbufptr)->fid_generation = 0; ++((fsobj_id_t *)attrbufptr); } if (ATTR_CMN_SCRIPT & attr) { *((text_encoding_t *)attrbufptr)++ = cdp->cd_encoding; } if (ATTR_CMN_CRTIME & attr) { if (is_64_bit) { ((struct user_timespec *)attrbufptr)->tv_sec = cap->ca_itime; ((struct user_timespec *)attrbufptr)->tv_nsec = 0; ++((struct user_timespec *)attrbufptr); } else { ((struct timespec *)attrbufptr)->tv_sec = cap->ca_itime; ((struct timespec *)attrbufptr)->tv_nsec = 0; ++((struct timespec *)attrbufptr); } } if (ATTR_CMN_MODTIME & attr) { if (is_64_bit) { ((struct user_timespec *)attrbufptr)->tv_sec = cap->ca_mtime; ((struct user_timespec *)attrbufptr)->tv_nsec = 0; ++((struct user_timespec *)attrbufptr); } else { ((struct timespec *)attrbufptr)->tv_sec = cap->ca_mtime; ((struct timespec *)attrbufptr)->tv_nsec = 0; ++((struct timespec *)attrbufptr); } } if (ATTR_CMN_CHGTIME & attr) { if (is_64_bit) { ((struct user_timespec *)attrbufptr)->tv_sec = cap->ca_ctime; ((struct user_timespec *)attrbufptr)->tv_nsec = 0; ++((struct user_timespec *)attrbufptr); } else { ((struct timespec *)attrbufptr)->tv_sec = cap->ca_ctime; ((struct timespec *)attrbufptr)->tv_nsec = 0; ++((struct timespec *)attrbufptr); } } if (ATTR_CMN_ACCTIME & attr) { if (is_64_bit) { ((struct user_timespec *)attrbufptr)->tv_sec = cap->ca_atime; ((struct user_timespec *)attrbufptr)->tv_nsec = 0; ++((struct user_timespec *)attrbufptr); } else { ((struct timespec *)attrbufptr)->tv_sec = cap->ca_atime; ((struct timespec *)attrbufptr)->tv_nsec = 0; ++((struct timespec *)attrbufptr); } } if (ATTR_CMN_BKUPTIME & attr) { if (is_64_bit) { ((struct user_timespec *)attrbufptr)->tv_sec = cap->ca_btime; ((struct user_timespec *)attrbufptr)->tv_nsec = 0; ++((struct user_timespec *)attrbufptr); } else { ((struct timespec *)attrbufptr)->tv_sec = cap->ca_btime; ((struct timespec *)attrbufptr)->tv_nsec = 0; ++((struct timespec *)attrbufptr); } } if (ATTR_CMN_FNDRINFO & attr) { bcopy(&cap->ca_finderinfo, attrbufptr, sizeof(u_int8_t) * 32); (char *)attrbufptr += sizeof(u_int8_t) * 32; } if (ATTR_CMN_OWNERID & attr) { *((uid_t *)attrbufptr)++ = (cap->ca_uid == UNKNOWNUID) ? kauth_cred_getuid(proc_ucred(p)) : cap->ca_uid; } if (ATTR_CMN_GRPID & attr) { *((gid_t *)attrbufptr)++ = cap->ca_gid; } if (ATTR_CMN_ACCESSMASK & attr) { /* * [2856576] Since we are dynamically changing the owner, also * effectively turn off the set-user-id and set-group-id bits, * just like chmod(2) would when changing ownership. This prevents * a security hole where set-user-id programs run as whoever is * logged on (or root if nobody is logged in yet!) */ *((uint32_t *)attrbufptr)++ = (cap->ca_uid == UNKNOWNUID) ? cap->ca_mode & ~(S_ISUID | S_ISGID) : cap->ca_mode; } if (ATTR_CMN_NAMEDATTRCOUNT & attr) { *((uint32_t *)attrbufptr)++ = 0; } if (ATTR_CMN_NAMEDATTRLIST & attr) { attrlength = 0; ((struct attrreference *)attrbufptr)->attr_dataoffset = 0; ((struct attrreference *)attrbufptr)->attr_length = attrlength; /* * Advance beyond the space just allocated and * round up to the next 4-byte boundary: */ (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3); ++((struct attrreference *)attrbufptr); } if (ATTR_CMN_FLAGS & attr) { *((uint32_t *)attrbufptr)++ = cap->ca_flags; } if (ATTR_CMN_USERACCESS & attr) { *((uint32_t *)attrbufptr)++ = DerivePermissionSummary(cap->ca_uid, cap->ca_gid, cap->ca_mode, mp, proc_ucred(current_proc()), current_proc()); } *abp->ab_attrbufpp = attrbufptr; *abp->ab_varbufpp = varbufptr; } static void packdirattr( struct attrblock *abp, struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc * descp, struct cat_attr * cattrp) { attrgroup_t attr = abp->ab_attrlist->dirattr; void *attrbufptr = *abp->ab_attrbufpp; if (ATTR_DIR_LINKCOUNT & attr) *((uint32_t *)attrbufptr)++ = cattrp->ca_nlink; if (ATTR_DIR_ENTRYCOUNT & attr) { uint32_t entries = cattrp->ca_entries; if (descp->cd_parentcnid == kHFSRootParentID) { if (hfsmp->hfs_privdir_desc.cd_cnid != 0) --entries; /* hide private dir */ if (hfsmp->jnl) entries -= 2; /* hide the journal files */ } *((uint32_t *)attrbufptr)++ = entries; } if (ATTR_DIR_MOUNTSTATUS & attr) { if (vp != NULL && vnode_mountedhere(vp) != NULL) *((uint32_t *)attrbufptr)++ = DIR_MNTSTATUS_MNTPOINT; else *((uint32_t *)attrbufptr)++ = 0; } *abp->ab_attrbufpp = attrbufptr; } static void packfileattr( struct attrblock *abp, struct hfsmount *hfsmp, struct cat_attr *cattrp, struct cat_fork *datafork, struct cat_fork *rsrcfork) { attrgroup_t attr = abp->ab_attrlist->fileattr; void *attrbufptr = *abp->ab_attrbufpp; void *varbufptr = *abp->ab_varbufpp; uint32_t attrlength; uint32_t allocblksize; allocblksize = HFSTOVCB(hfsmp)->blockSize; if (ATTR_FILE_LINKCOUNT & attr) { *((uint32_t *)attrbufptr)++ = cattrp->ca_nlink; } if (ATTR_FILE_TOTALSIZE & attr) { *((off_t *)attrbufptr)++ = datafork->cf_size + rsrcfork->cf_size; } if (ATTR_FILE_ALLOCSIZE & attr) { *((off_t *)attrbufptr)++ = (off_t)cattrp->ca_blocks * (off_t)allocblksize; } if (ATTR_FILE_IOBLOCKSIZE & attr) { *((uint32_t *)attrbufptr)++ = hfsmp->hfs_logBlockSize; } if (ATTR_FILE_CLUMPSIZE & attr) { *((uint32_t *)attrbufptr)++ = HFSTOVCB(hfsmp)->vcbClpSiz; } if (ATTR_FILE_DEVTYPE & attr) { if (S_ISBLK(cattrp->ca_mode) || S_ISCHR(cattrp->ca_mode)) *((uint32_t *)attrbufptr)++ = (uint32_t)cattrp->ca_rdev; else *((uint32_t *)attrbufptr)++ = 0; } if (ATTR_FILE_FILETYPE & attr) { *((uint32_t *)attrbufptr)++ = 0; } if (ATTR_FILE_FORKCOUNT & attr) { *((uint32_t *)attrbufptr)++ = 2; } if (ATTR_FILE_FORKLIST & attr) { attrlength = 0; ((struct attrreference *)attrbufptr)->attr_dataoffset = 0; ((struct attrreference *)attrbufptr)->attr_length = attrlength; /* * Advance beyond the space just allocated and * round up to the next 4-byte boundary: */ (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3); ++((struct attrreference *)attrbufptr); } if (ATTR_FILE_DATALENGTH & attr) { *((off_t *)attrbufptr)++ = datafork->cf_size; } if (ATTR_FILE_DATAALLOCSIZE & attr) { *((off_t *)attrbufptr)++ = (off_t)datafork->cf_blocks * (off_t)allocblksize; } if (ATTR_FILE_DATAEXTENTS & attr) { bcopy(&datafork->cf_extents, attrbufptr, sizeof(extentrecord)); (char *)attrbufptr += sizeof(extentrecord); } if (ATTR_FILE_RSRCLENGTH & attr) { *((off_t *)attrbufptr)++ = rsrcfork->cf_size; } if (ATTR_FILE_RSRCALLOCSIZE & attr) { *((off_t *)attrbufptr)++ = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize; } if (ATTR_FILE_RSRCEXTENTS & attr) { bcopy(&rsrcfork->cf_extents, attrbufptr, sizeof(extentrecord)); (char *)attrbufptr += sizeof(extentrecord); } *abp->ab_attrbufpp = attrbufptr; *abp->ab_varbufpp = varbufptr; } #if 0 static int unpackattrblk(struct attrblock *abp, struct vnode *vp) { struct attrlist *attrlistp = abp->ab_attrlist; int error; if (attrlistp->volattr) { error = unpackvolattr(abp, VTOHFS(vp), vp); if (error) return (error); } else if (attrlistp->commonattr) { unpackcommonattr(abp, vp); } return (0); } static void unpackcommonattr( struct attrblock *abp, struct vnode *vp) { attrgroup_t attr = abp->ab_attrlist->commonattr; void *attrbufptr = *abp->ab_attrbufpp; struct cnode *cp = VTOC(vp); boolean_t is_64_bit = proc_is64bit(current_proc()); if (ATTR_CMN_SCRIPT & attr) { cp->c_encoding = (u_int32_t)*((text_encoding_t *)attrbufptr)++; hfs_setencodingbits(VTOHFS(vp), cp->c_encoding); } if (ATTR_CMN_CRTIME & attr) { if (is_64_bit) { cp->c_itime = ((struct user_timespec *)attrbufptr)->tv_sec; ++((struct user_timespec *)attrbufptr); } else { cp->c_itime = ((struct timespec *)attrbufptr)->tv_sec; ++((struct timespec *)attrbufptr); } } if (ATTR_CMN_MODTIME & attr) { cp->c_mtime = ((struct timespec *)attrbufptr)->tv_sec; ++((struct timespec *)attrbufptr); cp->c_touch_modtime = FALSE; } if (ATTR_CMN_CHGTIME & attr) { cp->c_ctime = ((struct timespec *)attrbufptr)->tv_sec; ++((struct timespec *)attrbufptr); cp->c_touch_chgtime = FALSE; } if (ATTR_CMN_ACCTIME & attr) { cp->c_atime = ((struct timespec *)attrbufptr)->tv_sec; ++((struct timespec *)attrbufptr); cp->c_touch_acctime = FALSE; } if (ATTR_CMN_BKUPTIME & attr) { cp->c_btime = ((struct timespec *)attrbufptr)->tv_sec; ++((struct timespec *)attrbufptr); } if (ATTR_CMN_FNDRINFO & attr) { bcopy(attrbufptr, &cp->c_attr.ca_finderinfo, sizeof(cp->c_attr.ca_finderinfo)); (char *)attrbufptr += sizeof(cp->c_attr.ca_finderinfo); } if (ATTR_CMN_OWNERID & attr) { if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) { u_int32_t uid = (u_int32_t)*((uid_t *)attrbufptr)++; if (uid != (uid_t)VNOVAL) cp->c_uid = uid; } else { ((uid_t *)attrbufptr)++; } } if (ATTR_CMN_GRPID & attr) { u_int32_t gid = (u_int32_t)*((gid_t *)attrbufptr)++; if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) { if (gid != (gid_t)VNOVAL) cp->c_gid = gid; } } if (ATTR_CMN_ACCESSMASK & attr) { u_int16_t mode = (u_int16_t)*((uint32_t *)attrbufptr)++; if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) { if (mode != (mode_t)VNOVAL) { cp->c_mode &= ~ALLPERMS; cp->c_mode |= (mode & ALLPERMS); } } } if (ATTR_CMN_FLAGS & attr) { uint32_t flags = *((uint32_t *)attrbufptr)++; /* * Flags are settable only on HFS+ volumes. A special * exception is made for the IMMUTABLE flags * (SF_IMMUTABLE and UF_IMMUTABLE), which can be set on * HFS volumes as well: */ if ((VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) || ((VTOVCB(vp)->vcbSigWord == kHFSSigWord) && ((flags & ~IMMUTABLE) == 0))) { if (flags != (uint32_t)VNOVAL) { cp->c_flags = flags; } } } *abp->ab_attrbufpp = attrbufptr; } static int unpackvolattr( struct attrblock *abp, struct hfsmount *hfsmp, struct vnode *root_vp) { void *attrbufptr = *abp->ab_attrbufpp; attrgroup_t attr; int error = 0; boolean_t is_64_bit = proc_is64bit(current_proc()); HFS_MOUNT_LOCK(hfsmp, TRUE); attr = abp->ab_attrlist->commonattr; if (attr == 0) goto volattr; if (ATTR_CMN_SCRIPT & attr) { hfsmp->volumeNameEncodingHint = (u_int32_t)*(((text_encoding_t *)attrbufptr)++); } if (ATTR_CMN_CRTIME & attr) { if (is_64_bit) { hfsmp->vcbCrDate = ((struct user_timespec *)attrbufptr)->tv_sec; ++((struct user_timespec *)attrbufptr); } else { hfsmp->vcbCrDate = ((struct timespec *)attrbufptr)->tv_sec; ++((struct timespec *)attrbufptr); } /* The volume's create date comes from the root directory */ VTOC(root_vp)->c_itime = hfsmp->vcbCrDate; VTOC(root_vp)->c_flag |= C_MODIFIED; /* * XXX Should we also do a relative change to the * the volume header's create date in local time? */ } if (ATTR_CMN_MODTIME & attr) { hfsmp->vcbLsMod = ((struct timespec *)attrbufptr)->tv_sec; ++((struct timespec *)attrbufptr); } if (ATTR_CMN_BKUPTIME & attr) { hfsmp->vcbVolBkUp = ((struct timespec *)attrbufptr)->tv_sec; ++((struct timespec *)attrbufptr); } if (ATTR_CMN_FNDRINFO & attr) { bcopy(attrbufptr, &hfsmp->vcbFndrInfo, sizeof(hfsmp->vcbFndrInfo)); (char *)attrbufptr += sizeof(hfsmp->vcbFndrInfo); } volattr: attr = abp->ab_attrlist->volattr & ~ATTR_VOL_INFO; /* * XXX - no validation is done on the name! * It could be empty or garbage (bad UTF-8). */ if (ATTR_VOL_NAME & attr) { attrreference_t * attr_refp = (attrreference_t *) attrbufptr; error = copystr(((char *)attrbufptr) + attr_refp->attr_dataoffset, hfsmp->vcbVN, MIN(attr_refp->attr_length, sizeof(hfsmp->vcbVN)), NULL); if (error == 0) (char *)attrbufptr += sizeof(struct attrreference); } *abp->ab_attrbufpp = attrbufptr; hfsmp->vcbFlags |= 0xFF00; HFS_MOUNT_UNLOCK(hfsmp, TRUE); return (error); } #endif /* * Calculate the total size of an attribute block. */ __private_extern__ int hfs_attrblksize(struct attrlist *attrlist) { int size; attrgroup_t a; int sizeof_timespec; boolean_t is_64_bit = proc_is64bit(current_proc()); if (is_64_bit) sizeof_timespec = sizeof(struct user_timespec); else sizeof_timespec = sizeof(struct timespec); #if ((ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_FSID | ATTR_CMN_OBJTYPE | \ ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | \ ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT | ATTR_CMN_CRTIME | \ ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME | \ ATTR_CMN_BKUPTIME | ATTR_CMN_FNDRINFO | ATTR_CMN_OWNERID | \ ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_NAMEDATTRCOUNT | \ ATTR_CMN_NAMEDATTRLIST | ATTR_CMN_FLAGS | ATTR_CMN_USERACCESS) \ != ATTR_CMN_VALIDMASK) #error hfs_attrblksize: Missing bits in common mask computation! #endif DBG_ASSERT((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0); #if ((ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE | ATTR_VOL_SIZE | \ ATTR_VOL_SPACEFREE | ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION | \ ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE | \ ATTR_VOL_OBJCOUNT | ATTR_VOL_FILECOUNT | ATTR_VOL_DIRCOUNT | \ ATTR_VOL_MAXOBJCOUNT | ATTR_VOL_MOUNTPOINT | ATTR_VOL_NAME | \ ATTR_VOL_MOUNTFLAGS | ATTR_VOL_INFO | ATTR_VOL_MOUNTEDDEVICE | \ ATTR_VOL_ENCODINGSUSED | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES) \ != ATTR_VOL_VALIDMASK) #error hfs_attrblksize: Missing bits in volume mask computation! #endif DBG_ASSERT((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0); #if ((ATTR_DIR_LINKCOUNT | ATTR_DIR_ENTRYCOUNT | ATTR_DIR_MOUNTSTATUS) \ != ATTR_DIR_VALIDMASK) #error hfs_attrblksize: Missing bits in directory mask computation! #endif DBG_ASSERT((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0); #if ((ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE | ATTR_FILE_ALLOCSIZE | \ ATTR_FILE_IOBLOCKSIZE | ATTR_FILE_CLUMPSIZE | ATTR_FILE_DEVTYPE | \ ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST | \ ATTR_FILE_DATALENGTH | ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_DATAEXTENTS | \ ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE | ATTR_FILE_RSRCEXTENTS) \ != ATTR_FILE_VALIDMASK) #error hfs_attrblksize: Missing bits in file mask computation! #endif DBG_ASSERT((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0); #if ((ATTR_FORK_TOTALSIZE | ATTR_FORK_ALLOCSIZE) != ATTR_FORK_VALIDMASK) #error hfs_attrblksize: Missing bits in fork mask computation! #endif DBG_ASSERT((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0); size = 0; if ((a = attrlist->commonattr) != 0) { if (a & ATTR_CMN_NAME) size += sizeof(struct attrreference); if (a & ATTR_CMN_DEVID) size += sizeof(dev_t); if (a & ATTR_CMN_FSID) size += sizeof(fsid_t); if (a & ATTR_CMN_OBJTYPE) size += sizeof(fsobj_type_t); if (a & ATTR_CMN_OBJTAG) size += sizeof(fsobj_tag_t); if (a & ATTR_CMN_OBJID) size += sizeof(fsobj_id_t); if (a & ATTR_CMN_OBJPERMANENTID) size += sizeof(fsobj_id_t); if (a & ATTR_CMN_PAROBJID) size += sizeof(fsobj_id_t); if (a & ATTR_CMN_SCRIPT) size += sizeof(text_encoding_t); if (a & ATTR_CMN_CRTIME) size += sizeof_timespec; if (a & ATTR_CMN_MODTIME) size += sizeof_timespec; if (a & ATTR_CMN_CHGTIME) size += sizeof_timespec; if (a & ATTR_CMN_ACCTIME) size += sizeof_timespec; if (a & ATTR_CMN_BKUPTIME) size += sizeof_timespec; if (a & ATTR_CMN_FNDRINFO) size += 32 * sizeof(u_int8_t); if (a & ATTR_CMN_OWNERID) size += sizeof(uid_t); if (a & ATTR_CMN_GRPID) size += sizeof(gid_t); if (a & ATTR_CMN_ACCESSMASK) size += sizeof(uint32_t); if (a & ATTR_CMN_NAMEDATTRCOUNT) size += sizeof(uint32_t); if (a & ATTR_CMN_NAMEDATTRLIST) size += sizeof(struct attrreference); if (a & ATTR_CMN_FLAGS) size += sizeof(uint32_t); if (a & ATTR_CMN_USERACCESS) size += sizeof(uint32_t); }; if ((a = attrlist->volattr) != 0) { if (a & ATTR_VOL_FSTYPE) size += sizeof(uint32_t); if (a & ATTR_VOL_SIGNATURE) size += sizeof(uint32_t); if (a & ATTR_VOL_SIZE) size += sizeof(off_t); if (a & ATTR_VOL_SPACEFREE) size += sizeof(off_t); if (a & ATTR_VOL_SPACEAVAIL) size += sizeof(off_t); if (a & ATTR_VOL_MINALLOCATION) size += sizeof(off_t); if (a & ATTR_VOL_ALLOCATIONCLUMP) size += sizeof(off_t); if (a & ATTR_VOL_IOBLOCKSIZE) size += sizeof(uint32_t); if (a & ATTR_VOL_OBJCOUNT) size += sizeof(uint32_t); if (a & ATTR_VOL_FILECOUNT) size += sizeof(uint32_t); if (a & ATTR_VOL_DIRCOUNT) size += sizeof(uint32_t); if (a & ATTR_VOL_MAXOBJCOUNT) size += sizeof(uint32_t); if (a & ATTR_VOL_MOUNTPOINT) size += sizeof(struct attrreference); if (a & ATTR_VOL_NAME) size += sizeof(struct attrreference); if (a & ATTR_VOL_MOUNTFLAGS) size += sizeof(uint32_t); if (a & ATTR_VOL_MOUNTEDDEVICE) size += sizeof(struct attrreference); if (a & ATTR_VOL_ENCODINGSUSED) size += sizeof(unsigned long long); if (a & ATTR_VOL_CAPABILITIES) size += sizeof(vol_capabilities_attr_t); if (a & ATTR_VOL_ATTRIBUTES) size += sizeof(vol_attributes_attr_t); }; if ((a = attrlist->dirattr) != 0) { if (a & ATTR_DIR_LINKCOUNT) size += sizeof(uint32_t); if (a & ATTR_DIR_ENTRYCOUNT) size += sizeof(uint32_t); if (a & ATTR_DIR_MOUNTSTATUS) size += sizeof(uint32_t); }; if ((a = attrlist->fileattr) != 0) { if (a & ATTR_FILE_LINKCOUNT) size += sizeof(uint32_t); if (a & ATTR_FILE_TOTALSIZE) size += sizeof(off_t); if (a & ATTR_FILE_ALLOCSIZE) size += sizeof(off_t); if (a & ATTR_FILE_IOBLOCKSIZE) size += sizeof(uint32_t); if (a & ATTR_FILE_CLUMPSIZE) size += sizeof(uint32_t); if (a & ATTR_FILE_DEVTYPE) size += sizeof(uint32_t); if (a & ATTR_FILE_FILETYPE) size += sizeof(uint32_t); if (a & ATTR_FILE_FORKCOUNT) size += sizeof(uint32_t); if (a & ATTR_FILE_FORKLIST) size += sizeof(struct attrreference); if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t); if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t); if (a & ATTR_FILE_DATAEXTENTS) size += sizeof(extentrecord); if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t); if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t); if (a & ATTR_FILE_RSRCEXTENTS) size += sizeof(extentrecord); }; if ((a = attrlist->forkattr) != 0) { if (a & ATTR_FORK_TOTALSIZE) size += sizeof(off_t); if (a & ATTR_FORK_ALLOCSIZE) size += sizeof(off_t); }; return size; } __private_extern__ unsigned long DerivePermissionSummary(uid_t obj_uid, gid_t obj_gid, mode_t obj_mode, struct mount *mp, kauth_cred_t cred, struct proc *p) { unsigned long permissions; if (obj_uid == UNKNOWNUID) obj_uid = kauth_cred_getuid(proc_ucred(p)); /* User id 0 (root) always gets access. */ if (!suser(cred, NULL)) { permissions = R_OK | W_OK | X_OK; goto Exit; }; /* Otherwise, check the owner. */ if (hfs_owner_rights(VFSTOHFS(mp), obj_uid, cred, p, false) == 0) { permissions = ((unsigned long)obj_mode & S_IRWXU) >> 6; goto Exit; } /* Otherwise, check the groups. */ if (! (((unsigned int)vfs_flags(mp)) & MNT_UNKNOWNPERMISSIONS)) { int is_member; if (kauth_cred_ismember_gid(cred, obj_gid, &is_member) == 0 && is_member) { permissions = ((unsigned long)obj_mode & S_IRWXG) >> 3; goto Exit; } } /* Otherwise, settle for 'others' access. */ permissions = (unsigned long)obj_mode & S_IRWXO; Exit: return (permissions); }