#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/attr.h>
#include <sys/stat.h>
#include <sys/unistd.h>
#include <sys/mount_internal.h>
#include <sys/kauth.h>
#include <sys/fsctl.h>
#include <kern/locks.h>
#include "hfs.h"
#include "hfs_cnode.h"
#include "hfs_mount.h"
#include "hfs_dbg.h"
#include "hfs_attrlist.h"
#include "hfs_btreeio.h"
static void packnameattr(struct attrblock *abp, struct vnode *vp,
const u_int8_t *name, int namelen);
static void packcommonattr(struct attrblock *abp, struct hfsmount *hfsmp,
struct vnode *vp, struct cat_desc * cdp,
struct cat_attr * cap, struct vfs_context *ctx);
static void packfileattr(struct attrblock *abp, struct hfsmount *hfsmp,
struct cat_attr *cattrp, struct cat_fork *datafork,
struct cat_fork *rsrcfork, struct vnode *vp);
static void packdirattr(struct attrblock *abp, struct hfsmount *hfsmp,
struct vnode *vp, struct cat_desc * descp,
struct cat_attr * cattrp);
static u_int32_t hfs_real_user_access(vnode_t vp, vfs_context_t ctx);
static void get_vattr_data_for_attrs(struct attrlist *, struct vnode_attr *,
struct hfsmount *, struct vnode *, struct cat_desc *, struct cat_attr *,
struct cat_fork *, struct cat_fork *, vfs_context_t);
static void vattr_data_for_common_attrs(struct attrlist *, struct vnode_attr *,
struct hfsmount *, struct vnode *, struct cat_desc *, struct cat_attr *,
vfs_context_t);
static void vattr_data_for_dir_attrs(struct attrlist *, struct vnode_attr *,
struct hfsmount *, struct vnode *, struct cat_desc *, struct cat_attr *);
static void vattr_data_for_file_attrs(struct attrlist *, struct vnode_attr *,
struct hfsmount *, struct cat_attr *, struct cat_fork *, struct cat_fork *,
struct vnode *vp);
static int hfs_readdirattr_internal(struct vnode *, struct attrlist *,
struct vnode_attr *, uio_t, uint64_t, int, uint32_t *, int *, int *,
vfs_context_t);
int
hfs_vnop_readdirattr(ap)
struct vnop_readdirattr_args *ap;
{
int error;
struct attrlist *alist = ap->a_alist;
if (((ap->a_options & ~(FSOPT_NOINMEMUPDATE | FSOPT_NOFOLLOW)) != 0) ||
(ap->a_maxcount <= 0)) {
return (EINVAL);
}
if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
(alist->commonattr & ~HFS_ATTR_CMN_VALID) ||
(alist->volattr != 0) ||
(alist->dirattr & ~HFS_ATTR_DIR_VALID) ||
(alist->fileattr & ~HFS_ATTR_FILE_VALID) ||
(alist->forkattr != 0)) {
return (EINVAL);
}
error = hfs_readdirattr_internal(ap->a_vp, alist, NULL, ap->a_uio,
(uint64_t)ap->a_options, ap->a_maxcount, ap->a_newstate,
ap->a_eofflag, (int *)ap->a_actualcount, ap->a_context);
return (error);
}
int
hfs_vnop_getattrlistbulk(ap)
struct vnop_getattrlistbulk_args *ap;
{
int error = 0;
error = hfs_readdirattr_internal(ap->a_vp, ap->a_alist, ap->a_vap,
ap->a_uio, (uint64_t)ap->a_options, 0, NULL, ap->a_eofflag,
(int *)ap->a_actualcount, ap->a_context);
return (error);
}
int
hfs_readdirattr_internal(struct vnode *dvp, struct attrlist *alist,
struct vnode_attr *vap, uio_t uio, uint64_t options, int maxcount,
uint32_t *newstate, int *eofflag, int *actualcount, vfs_context_t ctx)
{
struct cnode *dcp;
struct hfsmount * hfsmp;
u_int32_t fixedblocksize;
u_int32_t maxattrblocksize;
u_int32_t currattrbufsize;
void *attrbufptr = NULL;
void *attrptr = NULL;
void *varptr = NULL;
caddr_t namebuf = NULL;
struct attrblock attrblk;
int error = 0;
int index = 0;
int i = 0;
struct cat_desc *lastdescp = NULL;
struct cat_entrylist *ce_list = NULL;
directoryhint_t *dirhint = NULL;
unsigned int tag;
int maxentries;
int lockflags;
u_int32_t dirchg = 0;
int reachedeof = 0;
*(actualcount) = 0;
*(eofflag) = 0;
if ((uio_resid(uio) <= 0) || (uio_iovcnt(uio) > 1))
return (EINVAL);
if (VTOC(dvp)->c_bsdflags & UF_COMPRESSED) {
int compressed = hfs_file_is_compressed(VTOC(dvp), 0);
if (!compressed) {
error = check_for_dataless_file(dvp, NAMESPACE_HANDLER_READ_OP);
if (error) {
return error;
}
}
}
if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
return (error);
}
dcp = VTOC(dvp);
hfsmp = VTOHFS(dvp);
dirchg = dcp->c_dirchangecnt;
index = uio_offset(uio) & HFS_INDEX_MASK;
tag = uio_offset(uio) & ~HFS_INDEX_MASK;
fixedblocksize = (sizeof(u_int32_t) + hfs_attrblksize(alist));
if (!vap) {
maxattrblocksize = fixedblocksize;
if (alist->commonattr & ATTR_CMN_NAME)
maxattrblocksize += kHFSPlusMaxFileNameBytes + 1;
MALLOC(attrbufptr, void *, maxattrblocksize, M_TEMP, M_WAITOK);
if (attrbufptr == NULL) {
error = ENOMEM;
goto exit2;
}
attrptr = attrbufptr;
varptr = (char *)attrbufptr + fixedblocksize;
} else {
if ((alist->commonattr & ATTR_CMN_NAME) && !vap->va_name) {
MALLOC(namebuf, caddr_t, MAXPATHLEN, M_TEMP, M_WAITOK);
if (!namebuf) {
error = ENOMEM;
goto exit2;
}
vap->va_name = namebuf;
}
}
dirhint = hfs_getdirhint(dcp, ((index - 1) & HFS_INDEX_MASK) | tag, TRUE);
dirhint->dh_index &= HFS_INDEX_MASK;
if (dirhint->dh_index == HFS_INDEX_MASK) {
dirhint->dh_index = -1;
}
maxentries = uio_resid(uio) / (fixedblocksize + HFS_AVERAGE_NAME_SIZE);
if (!vap)
maxentries = min(maxentries, maxcount);
maxentries = min(maxentries, MAXCATENTRIES);
if (maxentries < 1) {
error = EINVAL;
goto exit2;
}
MALLOC(ce_list, struct cat_entrylist *, CE_LIST_SIZE(maxentries), M_TEMP, M_WAITOK);
if (ce_list == NULL) {
error = ENOMEM;
goto exit2;
}
bzero(ce_list, CE_LIST_SIZE(maxentries));
ce_list->maxentries = maxentries;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
error = cat_getentriesattr(hfsmp, dirhint, ce_list, &reachedeof);
hfs_systemfile_unlock(hfsmp, lockflags);
if ((error == ENOENT) || (reachedeof != 0)) {
*(eofflag) = TRUE;
error = 0;
}
if (error) {
goto exit1;
}
if ((dcp->c_entries == 0) && (ce_list->realentries > 0)) {
dcp->c_entries++;
dcp->c_flag |= C_MODIFIED;
printf("hfs_vnop_readdirattr: repairing valence to non-zero! \n");
hfs_update(dvp, 0);
}
hfs_unlock(dcp);
dcp = NULL;
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;
if (((alist->commonattr & ATTR_CMN_USERACCESS) &&
(cattrp->ca_recflags & kHFSHasSecurityMask))
#if CONFIG_PROTECT
||
((alist->commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) && (vap))
#endif
) {
if (hfs_vget(hfsmp, cattrp->ca_fileid, &vp, 0, 0) != 0) {
vp = NULL;
}
} else if (vap || !(options & FSOPT_NOINMEMUPDATE)) {
vp = hfs_chash_getvnode(hfsmp, cattrp->ca_fileid, 0, 0, 0);
}
if (vp != NULL) {
cp = VTOC(vp);
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;
}
hfs_unlock(cp);
cp = NULL;
}
if (!vap) {
*((u_int32_t *)attrptr) = 0;
attrptr = ((u_int32_t *)attrptr) + 1;
attrblk.ab_attrlist = alist;
attrblk.ab_attrbufpp = &attrptr;
attrblk.ab_varbufpp = &varptr;
attrblk.ab_flags = 0;
attrblk.ab_blocksize = maxattrblocksize;
attrblk.ab_context = ctx;
hfs_packattrblk(&attrblk, hfsmp, vp, cdescp, cattrp, &c_datafork, &c_rsrcfork, ctx);
currattrbufsize = ((char *)varptr - (char *)attrbufptr);
if (vp != NULL) {
vnode_put(vp);
vp = NULL;
}
if (uio_resid(uio) < 0 ||
currattrbufsize > (u_int32_t)uio_resid(uio)) {
break;
} else {
*((u_int32_t *)attrbufptr) = currattrbufsize;
error = uiomove((caddr_t)attrbufptr, currattrbufsize, uio);
if (error != E_NONE) {
break;
}
attrptr = attrbufptr;
varptr = (char *)attrbufptr + fixedblocksize;
lastdescp = &ce_list->entry[i].ce_desc;
index++;
*actualcount += 1;
if ((--maxcount <= 0) ||
uio_resid(uio) < 0 ||
((u_int32_t)uio_resid(uio) < (fixedblocksize + HFS_AVERAGE_NAME_SIZE))){
break;
}
}
} else {
size_t orig_resid = (size_t)uio_resid(uio);
size_t resid;
get_vattr_data_for_attrs(alist, vap, hfsmp, vp, cdescp,
cattrp, &c_datafork, &c_rsrcfork, ctx);
#if CONFIG_PROTECT
if ((alist->commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) &&
vp) {
int class;
if (!cp_vnode_getclass(vp, &class)) {
VATTR_RETURN(vap, va_dataprotect_class,
(uint32_t)class);
}
}
#endif
error = vfs_attr_pack(vp, uio, alist, options, vap,
NULL, ctx);
if (vp) {
vnode_put(vp);
vp = NULL;
}
resid = uio_resid(uio);
if (error || resid == orig_resid)
break;
lastdescp = &ce_list->entry[i].ce_desc;
index++;
*actualcount += 1;
if (resid < sizeof(uint32_t))
break;
}
}
if (*eofflag && (*actualcount < (int)ce_list->realentries))
*eofflag = 0;
if (ce_list->skipentries) {
index += ce_list->skipentries;
ce_list->skipentries = 0;
}
if ((*(eofflag) == 0) && lastdescp != NULL) {
if ((dirhint->dh_desc.cd_flags & CD_HASBUF) &&
(dirhint->dh_desc.cd_nameptr != NULL)) {
dirhint->dh_desc.cd_flags &= ~CD_HASBUF;
vfs_removename((const char *)dirhint->dh_desc.cd_nameptr);
}
dirhint->dh_desc.cd_namelen = lastdescp->cd_namelen;
dirhint->dh_desc.cd_nameptr = (const u_int8_t *)
vfs_addname((const char *)lastdescp->cd_nameptr, lastdescp->cd_namelen, 0, 0);
dirhint->dh_desc.cd_flags |= CD_HASBUF;
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;
}
for (i = 0; i < (int)ce_list->realentries; ++i)
cat_releasedesc(&ce_list->entry[i].ce_desc);
ce_list->realentries = 0;
(void) hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
dcp = VTOC(dvp);
exit1:
while (tag == 0) tag = (++dcp->c_dirhinttag) << HFS_INDEX_BITS;
uio_setoffset(uio, index | tag);
dirhint->dh_index |= tag;
exit2:
if (newstate)
*newstate = dirchg;
if (dirhint) {
if ((error != 0) || *(eofflag))
hfs_reldirhint(dcp, dirhint);
else
hfs_insertdirhint(dcp, dirhint);
}
if (namebuf) {
FREE(namebuf, M_TEMP);
vap->va_name = NULL;
}
if (attrbufptr)
FREE(attrbufptr, M_TEMP);
if (ce_list)
FREE(ce_list, M_TEMP);
if (vap && *actualcount && error)
error = 0;
hfs_unlock(dcp);
return (error);
}
__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 vfs_context *ctx)
{
struct attrlist *attrlistp = abp->ab_attrlist;
if (attrlistp->commonattr)
packcommonattr(abp, hfsmp, vp, descp, attrp, ctx);
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, vp);
}
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);
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,
const u_int8_t *name,
int namelen)
{
void *varbufptr;
struct attrreference * attr_refptr;
char *mpname;
size_t mpnamelen;
u_int32_t attrlength;
u_int8_t empty = 0;
if ((vp != NULL) && vnode_isvroot(vp) &&
(mpname = mountpointname(vnode_mount(vp)))) {
mpnamelen = strlen(mpname);
while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/'))
--mpnamelen;
if (mpnamelen > 0) {
name = (u_int8_t *)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((char *)varbufptr, (const char *) name, attrlength);
varbufptr = ((char *)varbufptr) + attrlength + ((4 - (attrlength & 3)) & 3);
++attr_refptr;
*abp->ab_attrbufpp = attr_refptr;
*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 vfs_context * ctx)
{
attrgroup_t attr = abp->ab_attrlist->commonattr;
struct mount *mp = HFSTOVFS(hfsmp);
void *attrbufptr = *abp->ab_attrbufpp;
void *varbufptr = *abp->ab_varbufpp;
boolean_t is_64_bit = proc_is64bit(vfs_context_proc(ctx));
uid_t cuid = 1;
int isroot = 0;
if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) {
cuid = kauth_cred_getuid(vfs_context_ucred(ctx));
isroot = cuid == 0;
}
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;
attrbufptr = ((dev_t *)attrbufptr) + 1;
}
if (ATTR_CMN_FSID & attr) {
fsid_t fsid;
fsid.val[0] = hfsmp->hfs_raw_dev;
fsid.val[1] = vfs_typenum(mp);
*((fsid_t *)attrbufptr) = fsid;
attrbufptr = ((fsid_t *)attrbufptr) + 1;
}
if (ATTR_CMN_OBJTYPE & attr) {
*((fsobj_type_t *)attrbufptr) = IFTOVT(cap->ca_mode);
attrbufptr = ((fsobj_type_t *)attrbufptr) + 1;
}
if (ATTR_CMN_OBJTAG & attr) {
*((fsobj_tag_t *)attrbufptr) = VT_HFS;
attrbufptr = ((fsobj_tag_t *)attrbufptr) + 1;
}
if (ATTR_CMN_OBJID & attr) {
((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid;
((fsobj_id_t *)attrbufptr)->fid_generation = 0;
attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
}
if (ATTR_CMN_OBJPERMANENTID & attr) {
((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_cnid;
((fsobj_id_t *)attrbufptr)->fid_generation = 0;
attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
}
if (ATTR_CMN_PAROBJID & attr) {
((fsobj_id_t *)attrbufptr)->fid_objno = cdp->cd_parentcnid;
((fsobj_id_t *)attrbufptr)->fid_generation = 0;
attrbufptr = ((fsobj_id_t *)attrbufptr) + 1;
}
if (ATTR_CMN_SCRIPT & attr) {
*((text_encoding_t *)attrbufptr) = cdp->cd_encoding;
attrbufptr = ((text_encoding_t *)attrbufptr) + 1;
}
if (ATTR_CMN_CRTIME & attr) {
if (is_64_bit) {
((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_itime;
((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
}
else {
((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_itime;
((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
}
}
if (ATTR_CMN_MODTIME & attr) {
if (is_64_bit) {
((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_mtime;
((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
}
else {
((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_mtime;
((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
}
}
if (ATTR_CMN_CHGTIME & attr) {
if (is_64_bit) {
((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_ctime;
((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
}
else {
((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_ctime;
((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
}
}
if (ATTR_CMN_ACCTIME & attr) {
if (is_64_bit) {
((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_atime;
((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
}
else {
((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_atime;
((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
}
}
if (ATTR_CMN_BKUPTIME & attr) {
if (is_64_bit) {
((struct user64_timespec *)attrbufptr)->tv_sec = cap->ca_btime;
((struct user64_timespec *)attrbufptr)->tv_nsec = 0;
attrbufptr = ((struct user64_timespec *)attrbufptr) + 1;
}
else {
((struct user32_timespec *)attrbufptr)->tv_sec = cap->ca_btime;
((struct user32_timespec *)attrbufptr)->tv_nsec = 0;
attrbufptr = ((struct user32_timespec *)attrbufptr) + 1;
}
}
if (ATTR_CMN_FNDRINFO & attr) {
u_int8_t *finfo = NULL;
bcopy(&cap->ca_finderinfo, attrbufptr, sizeof(u_int8_t) * 32);
finfo = (u_int8_t*)attrbufptr;
if (S_ISLNK(cap->ca_mode)) {
struct FndrFileInfo *fip;
fip = (struct FndrFileInfo *)attrbufptr;
fip->fdType = 0;
fip->fdCreator = 0;
}
finfo = finfo + 16;
if (S_ISREG(cap->ca_mode) || S_ISLNK(cap->ca_mode)) {
struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
extinfo->document_id = 0;
extinfo->date_added = 0;
extinfo->write_gen_counter = 0;
}
else if (S_ISDIR(cap->ca_mode)) {
struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
extinfo->document_id = 0;
extinfo->date_added = 0;
extinfo->write_gen_counter = 0;
}
attrbufptr = (char *)attrbufptr + sizeof(u_int8_t) * 32;
}
if (ATTR_CMN_OWNERID & attr) {
uid_t nuid = cap->ca_uid;
if (!isroot) {
if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS)
nuid = cuid;
else if (nuid == UNKNOWNUID)
nuid = cuid;
}
*((uid_t *)attrbufptr) = nuid;
attrbufptr = ((uid_t *)attrbufptr) + 1;
}
if (ATTR_CMN_GRPID & attr) {
gid_t ngid = cap->ca_gid;
if (!isroot) {
gid_t cgid = kauth_cred_getgid(vfs_context_ucred(ctx));
if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS)
ngid = cgid;
else if (ngid == UNKNOWNUID)
ngid = cgid;
}
*((gid_t *)attrbufptr) = ngid;
attrbufptr = ((gid_t *)attrbufptr) + 1;
}
if (ATTR_CMN_ACCESSMASK & attr) {
*((u_int32_t *)attrbufptr) = (cap->ca_uid == UNKNOWNUID) ?
cap->ca_mode & ~(S_ISUID | S_ISGID) : cap->ca_mode;
attrbufptr = ((u_int32_t *)attrbufptr) + 1;
}
if (ATTR_CMN_FLAGS & attr) {
*((u_int32_t *)attrbufptr) = cap->ca_flags;
attrbufptr = ((u_int32_t *)attrbufptr) + 1;
}
if (ATTR_CMN_USERACCESS & attr) {
u_int32_t user_access;
if ((vp != NULLVP) && (cap->ca_recflags & kHFSHasSecurityMask)) {
user_access = hfs_real_user_access(vp, abp->ab_context);
} else {
user_access = DerivePermissionSummary(cap->ca_uid, cap->ca_gid,
cap->ca_mode, mp, vfs_context_ucred(ctx), 0);
}
if (vfs_flags(mp) & MNT_RDONLY) {
user_access &= ~W_OK;
}
if ((cap->ca_flags & UF_IMMUTABLE) && (vfs_context_suser(abp->ab_context) != 0))
user_access &= ~W_OK;
if ((cap->ca_flags & SF_IMMUTABLE) && (vfs_context_suser(abp->ab_context) == 0))
user_access &= ~W_OK;
*((u_int32_t *)attrbufptr) = user_access;
attrbufptr = ((u_int32_t *)attrbufptr) + 1;
}
if (ATTR_CMN_FILEID & attr) {
*((u_int64_t *)attrbufptr) = cap->ca_fileid;
attrbufptr = ((u_int64_t *)attrbufptr) + 1;
}
if (ATTR_CMN_PARENTID & attr) {
*((u_int64_t *)attrbufptr) = cdp->cd_parentcnid;
attrbufptr = ((u_int64_t *)attrbufptr) + 1;
}
*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;
u_int32_t entries;
if (ATTR_DIR_LINKCOUNT & attr) {
*((u_int32_t *)attrbufptr) = cattrp->ca_linkcount;
attrbufptr = ((u_int32_t *)attrbufptr) + 1;
}
if (ATTR_DIR_ENTRYCOUNT & attr) {
entries = cattrp->ca_entries;
if (descp->cd_parentcnid == kHFSRootParentID) {
if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0)
--entries;
if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0)
--entries;
if (hfsmp->jnl ||
((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) &&
(hfsmp->hfs_flags & HFS_READ_ONLY)))
entries -= 2;
}
*((u_int32_t *)attrbufptr) = entries;
attrbufptr = ((u_int32_t *)attrbufptr) + 1;
}
if (ATTR_DIR_MOUNTSTATUS & attr) {
if (vp != NULL && vnode_mountedhere(vp) != NULL)
*((u_int32_t *)attrbufptr) = DIR_MNTSTATUS_MNTPOINT;
else
*((u_int32_t *)attrbufptr) = 0;
attrbufptr = ((u_int32_t *)attrbufptr) + 1;
}
*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,
struct vnode *vp)
{
#if !HFS_COMPRESSION
#pragma unused(vp)
#endif
attrgroup_t attr = abp->ab_attrlist->fileattr;
void *attrbufptr = *abp->ab_attrbufpp;
void *varbufptr = *abp->ab_varbufpp;
u_int32_t allocblksize;
allocblksize = HFSTOVCB(hfsmp)->blockSize;
off_t datasize = datafork->cf_size;
off_t totalsize = datasize + rsrcfork->cf_size;
#if HFS_COMPRESSION
int handle_compressed;
handle_compressed = (cattrp->ca_flags & UF_COMPRESSED);
if (handle_compressed) {
if (attr & (ATTR_FILE_DATALENGTH|ATTR_FILE_TOTALSIZE)) {
if ( 0 == hfs_uncompressed_size_of_compressed_file(hfsmp, vp, cattrp->ca_fileid, &datasize, 1) ) {
totalsize = datasize;
}
}
}
#endif
if (ATTR_FILE_LINKCOUNT & attr) {
*((u_int32_t *)attrbufptr) = cattrp->ca_linkcount;
attrbufptr = ((u_int32_t *)attrbufptr) + 1;
}
if (ATTR_FILE_TOTALSIZE & attr) {
*((off_t *)attrbufptr) = totalsize;
attrbufptr = ((off_t *)attrbufptr) + 1;
}
if (ATTR_FILE_ALLOCSIZE & attr) {
*((off_t *)attrbufptr) =
(off_t)cattrp->ca_blocks * (off_t)allocblksize;
attrbufptr = ((off_t *)attrbufptr) + 1;
}
if (ATTR_FILE_IOBLOCKSIZE & attr) {
*((u_int32_t *)attrbufptr) = hfsmp->hfs_logBlockSize;
attrbufptr = ((u_int32_t *)attrbufptr) + 1;
}
if (ATTR_FILE_CLUMPSIZE & attr) {
*((u_int32_t *)attrbufptr) = hfsmp->vcbClpSiz;
attrbufptr = ((u_int32_t *)attrbufptr) + 1;
}
if (ATTR_FILE_DEVTYPE & attr) {
if (S_ISBLK(cattrp->ca_mode) || S_ISCHR(cattrp->ca_mode))
*((u_int32_t *)attrbufptr) = (u_int32_t)cattrp->ca_rdev;
else
*((u_int32_t *)attrbufptr) = 0;
attrbufptr = ((u_int32_t *)attrbufptr) + 1;
}
if (ATTR_FILE_DATALENGTH & attr) {
*((off_t *)attrbufptr) = datasize;
attrbufptr = ((off_t *)attrbufptr) + 1;
}
#if HFS_COMPRESSION
if ( handle_compressed ) {
if (attr & ATTR_FILE_DATAALLOCSIZE) {
*((off_t *)attrbufptr) = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize;
attrbufptr = ((off_t *)attrbufptr) + 1;
}
if (attr & ATTR_FILE_RSRCLENGTH) {
*((off_t *)attrbufptr) = 0;
attrbufptr = ((off_t *)attrbufptr) + 1;
}
if (attr & ATTR_FILE_RSRCALLOCSIZE) {
*((off_t *)attrbufptr) = 0;
attrbufptr = ((off_t *)attrbufptr) + 1;
}
}
else
#endif
{
if (ATTR_FILE_DATAALLOCSIZE & attr) {
*((off_t *)attrbufptr) = (off_t)datafork->cf_blocks * (off_t)allocblksize;
attrbufptr = ((off_t *)attrbufptr) + 1;
}
if (ATTR_FILE_RSRCLENGTH & attr) {
*((off_t *)attrbufptr) = rsrcfork->cf_size;
attrbufptr = ((off_t *)attrbufptr) + 1;
}
if (ATTR_FILE_RSRCALLOCSIZE & attr) {
*((off_t *)attrbufptr) = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize;
attrbufptr = ((off_t *)attrbufptr) + 1;
}
}
*abp->ab_attrbufpp = attrbufptr;
*abp->ab_varbufpp = varbufptr;
}
__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 user64_timespec);
else
sizeof_timespec = sizeof(struct user32_timespec);
DBG_ASSERT((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0);
DBG_ASSERT((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0);
DBG_ASSERT((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0);
DBG_ASSERT((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0);
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(u_int32_t);
if (a & ATTR_CMN_FLAGS) size += sizeof(u_int32_t);
if (a & ATTR_CMN_USERACCESS) size += sizeof(u_int32_t);
if (a & ATTR_CMN_FILEID) size += sizeof(u_int64_t);
if (a & ATTR_CMN_PARENTID) size += sizeof(u_int64_t);
}
if ((a = attrlist->dirattr) != 0) {
if (a & ATTR_DIR_LINKCOUNT) size += sizeof(u_int32_t);
if (a & ATTR_DIR_ENTRYCOUNT) size += sizeof(u_int32_t);
if (a & ATTR_DIR_MOUNTSTATUS) size += sizeof(u_int32_t);
}
if ((a = attrlist->fileattr) != 0) {
if (a & ATTR_FILE_LINKCOUNT) size += sizeof(u_int32_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(u_int32_t);
if (a & ATTR_FILE_CLUMPSIZE) size += sizeof(u_int32_t);
if (a & ATTR_FILE_DEVTYPE) size += sizeof(u_int32_t);
if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t);
if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t);
if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t);
if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t);
}
return (size);
}
#define KAUTH_DIR_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | \
KAUTH_VNODE_ADD_SUBDIRECTORY | \
KAUTH_VNODE_DELETE_CHILD)
#define KAUTH_DIR_READ_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY)
#define KAUTH_DIR_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH)
#define KAUTH_FILE_WRITE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA)
#define KAUTH_FILE_READRIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA)
#define KAUTH_FILE_EXECUTE_RIGHTS (KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE)
static u_int32_t
hfs_real_user_access(vnode_t vp, vfs_context_t ctx)
{
u_int32_t user_access = 0;
if (vnode_isdir(vp)) {
if (vnode_authorize(vp, NULLVP, KAUTH_DIR_WRITE_RIGHTS, ctx) == 0)
user_access |= W_OK;
if (vnode_authorize(vp, NULLVP, KAUTH_DIR_READ_RIGHTS, ctx) == 0)
user_access |= R_OK;
if (vnode_authorize(vp, NULLVP, KAUTH_DIR_EXECUTE_RIGHTS, ctx) == 0)
user_access |= X_OK;
} else {
if (vnode_authorize(vp, NULLVP, KAUTH_FILE_WRITE_RIGHTS, ctx) == 0)
user_access |= W_OK;
if (vnode_authorize(vp, NULLVP, KAUTH_FILE_READRIGHTS, ctx) == 0)
user_access |= R_OK;
if (vnode_authorize(vp, NULLVP, KAUTH_FILE_EXECUTE_RIGHTS, ctx) == 0)
user_access |= X_OK;
}
return (user_access);
}
u_int32_t
DerivePermissionSummary(uid_t obj_uid, gid_t obj_gid, mode_t obj_mode,
struct mount *mp, kauth_cred_t cred, __unused struct proc *p)
{
u_int32_t permissions;
if (obj_uid == UNKNOWNUID)
obj_uid = kauth_cred_getuid(cred);
if (!suser(cred, NULL)) {
permissions = R_OK | W_OK | X_OK;
goto Exit;
};
if (hfs_owner_rights(VFSTOHFS(mp), obj_uid, cred, NULL, false) == 0) {
permissions = ((u_int32_t)obj_mode & S_IRWXU) >> 6;
goto Exit;
}
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 = ((u_int32_t)obj_mode & S_IRWXG) >> 3;
goto Exit;
}
}
permissions = (u_int32_t)obj_mode & S_IRWXO;
Exit:
return (permissions);
}
void
get_vattr_data_for_attrs(struct attrlist *alp, struct vnode_attr *vap,
struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc *descp,
struct cat_attr *atrp, struct cat_fork *datafork, struct cat_fork *rsrcfork,
vfs_context_t ctx)
{
if (alp->commonattr)
vattr_data_for_common_attrs(alp, vap, hfsmp, vp, descp, atrp,
ctx);
if (alp->dirattr && S_ISDIR(atrp->ca_mode))
vattr_data_for_dir_attrs(alp, vap, hfsmp, vp, descp, atrp);
if (alp->fileattr && !S_ISDIR(atrp->ca_mode)) {
vattr_data_for_file_attrs(alp, vap, hfsmp, atrp, datafork,
rsrcfork, vp);
}
}
static void
copy_name_attr(struct vnode_attr *vap, struct vnode *vp, const u_int8_t *name,
int namelen)
{
char *mpname;
size_t mpnamelen;
u_int32_t attrlength;
u_int8_t empty = 0;
if ((vp != NULL) && vnode_isvroot(vp) &&
(mpname = mountpointname(vnode_mount(vp)))) {
mpnamelen = strlen(mpname);
while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/'))
--mpnamelen;
if (mpnamelen > 0) {
name = (u_int8_t *)mpname;
namelen = mpnamelen;
}
}
if (name == NULL) {
name = ∅
namelen = 0;
}
attrlength = namelen + 1;
(void) strncpy((char *)vap->va_name, (const char *) name, attrlength);
attrlength = min(kHFSPlusMaxFileNameBytes, ((attrlength + 7) & ~0x07));
bzero(vap->va_name + attrlength, kHFSPlusMaxFileNameBytes - attrlength);
}
static void
vattr_data_for_common_attrs( struct attrlist *alp, struct vnode_attr *vap,
struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc *cdp,
struct cat_attr *cap, vfs_context_t ctx)
{
attrgroup_t attr = alp->commonattr;
struct mount *mp = HFSTOVFS(hfsmp);
uid_t cuid = 1;
int isroot = 0;
if (attr & (ATTR_CMN_OWNERID | ATTR_CMN_GRPID)) {
cuid = kauth_cred_getuid(vfs_context_ucred(ctx));
isroot = cuid == 0;
}
if (ATTR_CMN_NAME & attr) {
if (vap->va_name) {
copy_name_attr(vap, vp, cdp->cd_nameptr,
cdp->cd_namelen);
VATTR_SET_SUPPORTED(vap, va_name);
} else {
VATTR_CLEAR_SUPPORTED(vap, va_name);
}
}
if (ATTR_CMN_DEVID & attr) {
vap->va_devid = hfsmp->hfs_raw_dev;
VATTR_SET_SUPPORTED(vap, va_devid);
}
if (ATTR_CMN_FSID & attr) {
vap->va_fsid64.val[0] = hfsmp->hfs_raw_dev;
vap->va_fsid64.val[1] = vfs_typenum(mp);
VATTR_SET_SUPPORTED(vap, va_fsid64);
}
vap->va_objtype = IFTOVT(cap->ca_mode);
VATTR_SET_SUPPORTED(vap, va_objtype);
if (ATTR_CMN_OBJTAG & attr) {
vap->va_objtag = VT_HFS;
VATTR_SET_SUPPORTED(vap, va_objtag);
}
if ((ATTR_CMN_OBJID & attr) ||
(ATTR_CMN_OBJPERMANENTID & attr)) {
vap->va_linkid = cdp->cd_cnid;
VATTR_SET_SUPPORTED(vap, va_linkid);
}
if (ATTR_CMN_PAROBJID & attr) {
vap->va_parentid = cdp->cd_parentcnid;
VATTR_SET_SUPPORTED(vap, va_parentid);
}
if (ATTR_CMN_SCRIPT & attr) {
vap->va_encoding = cdp->cd_encoding;
VATTR_SET_SUPPORTED(vap, va_encoding);
}
if (ATTR_CMN_CRTIME & attr) {
vap->va_create_time.tv_sec = cap->ca_itime;
vap->va_create_time.tv_nsec = 0;
VATTR_SET_SUPPORTED(vap, va_create_time);
}
if (ATTR_CMN_MODTIME & attr) {
vap->va_modify_time.tv_sec = cap->ca_mtime;
vap->va_modify_time.tv_nsec = 0;
VATTR_SET_SUPPORTED(vap, va_modify_time);
}
if (ATTR_CMN_CHGTIME & attr) {
vap->va_change_time.tv_sec = cap->ca_ctime;
vap->va_change_time.tv_nsec = 0;
VATTR_SET_SUPPORTED(vap, va_change_time);
}
if (ATTR_CMN_ACCTIME & attr) {
vap->va_access_time.tv_sec = cap->ca_atime;
vap->va_access_time.tv_nsec = 0;
VATTR_SET_SUPPORTED(vap, va_access_time);
}
if (ATTR_CMN_BKUPTIME & attr) {
vap->va_backup_time.tv_sec = cap->ca_btime;
vap->va_backup_time.tv_nsec = 0;
VATTR_SET_SUPPORTED(vap, va_backup_time);
}
if (ATTR_CMN_FNDRINFO & attr) {
u_int8_t *finfo = NULL;
bcopy(&cap->ca_finderinfo, &vap->va_finderinfo[0],
sizeof(u_int8_t) * 32);
finfo = (u_int8_t*)(&vap->va_finderinfo[0]);
if (S_ISLNK(cap->ca_mode)) {
struct FndrFileInfo *fip;
fip = (struct FndrFileInfo *)finfo;
fip->fdType = 0;
fip->fdCreator = 0;
}
finfo = finfo + 16;
if (S_ISREG(cap->ca_mode) || S_ISLNK(cap->ca_mode)) {
struct FndrExtendedFileInfo *extinfo =
(struct FndrExtendedFileInfo *)finfo;
extinfo->document_id = 0;
extinfo->date_added = 0;
extinfo->write_gen_counter = 0;
} else if (S_ISDIR(cap->ca_mode)) {
struct FndrExtendedDirInfo *extinfo =
(struct FndrExtendedDirInfo *)finfo;
extinfo->document_id = 0;
extinfo->date_added = 0;
extinfo->write_gen_counter = 0;
}
VATTR_SET_SUPPORTED(vap, va_finderinfo);
}
if (ATTR_CMN_OWNERID & attr) {
uid_t nuid = cap->ca_uid;
if (!isroot) {
if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS)
nuid = cuid;
else if (nuid == UNKNOWNUID)
nuid = cuid;
}
vap->va_uid = nuid;
VATTR_SET_SUPPORTED(vap, va_uid);
}
if (ATTR_CMN_GRPID & attr) {
gid_t ngid = cap->ca_gid;
if (!isroot) {
gid_t cgid = kauth_cred_getgid(vfs_context_ucred(ctx));
if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS)
ngid = cgid;
else if (ngid == UNKNOWNUID)
ngid = cgid;
}
vap->va_gid = ngid;
VATTR_SET_SUPPORTED(vap, va_gid);
}
if (ATTR_CMN_ACCESSMASK & attr) {
uint32_t nmode;
nmode = (cap->ca_uid == UNKNOWNUID) ?
cap->ca_mode & ~(S_ISUID | S_ISGID) : cap->ca_mode;
vap->va_mode = nmode;
VATTR_SET_SUPPORTED(vap, va_mode);
}
if (ATTR_CMN_FLAGS & attr) {
vap->va_flags = cap->ca_flags;
VATTR_SET_SUPPORTED(vap, va_flags);
}
if (ATTR_CMN_GEN_COUNT & attr) {
vap->va_write_gencount = hfs_get_gencount_from_blob(
(const uint8_t *)cap->ca_finderinfo, cap->ca_mode);
VATTR_SET_SUPPORTED(vap, va_write_gencount);
}
if (ATTR_CMN_DOCUMENT_ID & attr) {
vap->va_document_id = hfs_get_document_id_from_blob(
(const uint8_t *)cap->ca_finderinfo, cap->ca_mode);
VATTR_SET_SUPPORTED(vap, va_document_id);
}
if (ATTR_CMN_USERACCESS & attr) {
u_int32_t user_access;
if ((vp != NULLVP) && (cap->ca_recflags & kHFSHasSecurityMask)) {
user_access = hfs_real_user_access(vp, ctx);
} else {
user_access = DerivePermissionSummary(cap->ca_uid, cap->ca_gid,
cap->ca_mode, mp, vfs_context_ucred(ctx), 0);
}
if (vfs_flags(mp) & MNT_RDONLY) {
user_access &= ~W_OK;
}
if ((cap->ca_flags & UF_IMMUTABLE) && (vfs_context_suser(ctx) != 0))
user_access &= ~W_OK;
if ((cap->ca_flags & SF_IMMUTABLE) && (vfs_context_suser(ctx) == 0))
user_access &= ~W_OK;
vap->va_user_access = user_access;
VATTR_SET_SUPPORTED(vap, va_user_access);
}
if (ATTR_CMN_EXTENDED_SECURITY & attr) {
if (!(cap->ca_recflags & kHFSHasSecurityMask)) {
vap->va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
VATTR_SET_SUPPORTED(vap, va_acl);
}
}
if (ATTR_CMN_FILEID & attr) {
vap->va_fileid = cap->ca_fileid;
VATTR_SET_SUPPORTED(vap, va_fileid);
}
if (ATTR_CMN_PARENTID & attr) {
vap->va_parentid = cdp->cd_parentcnid;
VATTR_SET_SUPPORTED(vap, va_parentid);
}
if (ATTR_CMN_ADDEDTIME & attr) {
if (cap->ca_recflags & kHFSHasDateAddedMask) {
vap->va_addedtime.tv_sec = hfs_get_dateadded_from_blob(
(const uint8_t *)cap->ca_finderinfo, cap->ca_mode);
vap->va_addedtime.tv_nsec = 0;
VATTR_SET_SUPPORTED(vap, va_addedtime);
}
}
}
static void
vattr_data_for_dir_attrs(struct attrlist *alp, struct vnode_attr *vap,
struct hfsmount *hfsmp, struct vnode *vp, struct cat_desc * descp,
struct cat_attr * cattrp)
{
attrgroup_t attr = alp->dirattr;
u_int32_t entries;
if (ATTR_DIR_LINKCOUNT & attr) {
vap->va_dirlinkcount = cattrp->ca_linkcount;
VATTR_SET_SUPPORTED(vap, va_dirlinkcount);
}
if (ATTR_DIR_ENTRYCOUNT & attr) {
entries = cattrp->ca_entries;
if (descp->cd_parentcnid == kHFSRootParentID) {
if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0)
--entries;
if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0)
--entries;
if (hfsmp->jnl ||
((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) &&
(hfsmp->hfs_flags & HFS_READ_ONLY)))
entries -= 2;
}
vap->va_nchildren = entries;
VATTR_SET_SUPPORTED(vap, va_nchildren);
}
if (ATTR_DIR_MOUNTSTATUS & attr) {
u_int32_t mstatus = 0;
if (vp != NULL && vnode_mountedhere(vp) != NULL)
mstatus = DIR_MNTSTATUS_MNTPOINT;
}
}
static void
vattr_data_for_file_attrs(struct attrlist *alp, struct vnode_attr *vap,
struct hfsmount *hfsmp, struct cat_attr *cattrp, struct cat_fork *datafork,
struct cat_fork *rsrcfork, struct vnode *vp)
{
#if !HFS_COMPRESSION
#pragma unused(vp)
#endif
attrgroup_t attr = alp->fileattr;
off_t da_size, rsrc_len, rsrc_alloc;
u_int32_t allocblksize;
allocblksize = HFSTOVCB(hfsmp)->blockSize;
off_t datasize = datafork->cf_size;
off_t totalsize = datasize + rsrcfork->cf_size;
#if HFS_COMPRESSION
int handle_compressed;
handle_compressed = (cattrp->ca_flags & UF_COMPRESSED);
if (handle_compressed) {
if (attr & (ATTR_FILE_DATALENGTH|ATTR_FILE_TOTALSIZE)) {
if ( 0 == hfs_uncompressed_size_of_compressed_file(hfsmp, vp, cattrp->ca_fileid, &datasize, 1) ) {
totalsize = datasize;
}
}
}
#endif
if (ATTR_FILE_LINKCOUNT & attr) {
vap->va_nlink = cattrp->ca_linkcount;
VATTR_SET_SUPPORTED(vap, va_nlink);
}
if (ATTR_FILE_TOTALSIZE & attr) {
VATTR_RETURN(vap, va_total_size, totalsize);
}
if (ATTR_FILE_ALLOCSIZE & attr) {
VATTR_RETURN(vap, va_total_alloc,
(off_t)cattrp->ca_blocks * (off_t)allocblksize );
}
if (ATTR_FILE_IOBLOCKSIZE & attr) {
VATTR_RETURN(vap, va_iosize, hfsmp->hfs_logBlockSize);
}
if (ATTR_FILE_DEVTYPE & attr) {
dev_t dev = 0;
if (S_ISBLK(cattrp->ca_mode) || S_ISCHR(cattrp->ca_mode))
dev = (u_int32_t)cattrp->ca_rdev;
VATTR_RETURN(vap, va_rdev, dev);
}
if (ATTR_FILE_DATALENGTH & attr) {
VATTR_RETURN(vap, va_data_size, datasize);
}
#if HFS_COMPRESSION
if (handle_compressed) {
da_size = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize;
rsrc_len = 0;
rsrc_alloc = 0;
}
else
#endif
{
da_size = (off_t)datafork->cf_blocks * (off_t)allocblksize;
rsrc_len = rsrcfork->cf_size;
rsrc_alloc = (off_t)rsrcfork->cf_blocks * (off_t)allocblksize;
}
if (ATTR_FILE_DATAALLOCSIZE & attr) {
VATTR_RETURN(vap, va_data_alloc, da_size);
}
if (ATTR_FILE_RSRCLENGTH & attr) {
VATTR_RETURN(vap, va_rsrc_length, rsrc_len);
}
if (ATTR_FILE_RSRCALLOCSIZE & attr) {
VATTR_RETURN(vap, va_rsrc_alloc, rsrc_alloc);
}
}