+ if (VATTR_IS_ACTIVE(vap, va_dirlinkcount)) {
+ VATTR_RETURN(vap, va_dirlinkcount, (uint32_t)cp->c_linkcount);
+ }
+ } else /* !VDIR */ {
+ data_size = VCTOF(vp, cp)->ff_size;
+
+ VATTR_RETURN(vap, va_nlink, (u_int64_t)cp->c_linkcount);
+ if (VATTR_IS_ACTIVE(vap, va_data_alloc)) {
+ u_int64_t blocks;
+
+#if HFS_COMPRESSION
+ if (hide_size) {
+ VATTR_RETURN(vap, va_data_alloc, 0);
+ } else if (compressed) {
+ /* for compressed files, we report all allocated blocks as belonging to the data fork */
+ blocks = cp->c_blocks;
+ VATTR_RETURN(vap, va_data_alloc, blocks * (u_int64_t)hfsmp->blockSize);
+ }
+ else
+#endif
+ {
+ blocks = VCTOF(vp, cp)->ff_blocks;
+ VATTR_RETURN(vap, va_data_alloc, blocks * (u_int64_t)hfsmp->blockSize);
+ }
+ }
+ }
+
+ /* conditional because 64-bit arithmetic can be expensive */
+ if (VATTR_IS_ACTIVE(vap, va_total_size)) {
+ if (v_type == VDIR) {
+ VATTR_RETURN(vap, va_total_size, (cp->c_entries + 2) * AVERAGE_HFSDIRENTRY_SIZE);
+ } else {
+ u_int64_t total_size = ~0ULL;
+ struct cnode *rcp;
+#if HFS_COMPRESSION
+ if (hide_size) {
+ /* we're hiding the size of this file, so just return 0 */
+ total_size = 0;
+ } else if (compressed) {
+ if (uncompressed_size == -1) {
+ /*
+ * We failed to get the uncompressed size above,
+ * so we'll fall back to the standard path below
+ * since total_size is still -1
+ */
+ } else {
+ /* use the uncompressed size we fetched above */
+ total_size = uncompressed_size;
+ }
+ }
+#endif
+ if (total_size == ~0ULL) {
+ if (cp->c_datafork) {
+ total_size = cp->c_datafork->ff_size;
+ }
+
+ if (cp->c_blocks - VTOF(vp)->ff_blocks) {
+ /* We deal with rsrc fork vnode iocount at the end of the function */
+ error = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE, FALSE);
+ if (error) {
+ /*
+ * Note that we call hfs_vgetrsrc with error_on_unlinked
+ * set to FALSE. This is because we may be invoked via
+ * fstat() on an open-unlinked file descriptor and we must
+ * continue to support access to the rsrc fork until it disappears.
+ * The code at the end of this function will be
+ * responsible for releasing the iocount generated by
+ * hfs_vgetrsrc. This is because we can't drop the iocount
+ * without unlocking the cnode first.
+ */
+ goto out;
+ }
+
+ rcp = VTOC(rvp);
+ if (rcp && rcp->c_rsrcfork) {
+ total_size += rcp->c_rsrcfork->ff_size;
+ }
+ }
+ }
+
+ VATTR_RETURN(vap, va_total_size, total_size);
+ }
+ }
+ if (VATTR_IS_ACTIVE(vap, va_total_alloc)) {
+ if (v_type == VDIR) {
+ VATTR_RETURN(vap, va_total_alloc, 0);
+ } else {
+ VATTR_RETURN(vap, va_total_alloc, (u_int64_t)cp->c_blocks * (u_int64_t)hfsmp->blockSize);
+ }
+ }
+
+ /*
+ * If the VFS wants extended security data, and we know that we
+ * don't have any (because it never told us it was setting any)
+ * then we can return the supported bit and no data. If we do
+ * have extended security, we can just leave the bit alone and
+ * the VFS will use the fallback path to fetch it.
+ */
+ if (VATTR_IS_ACTIVE(vap, va_acl)) {
+ if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
+ vap->va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
+ VATTR_SET_SUPPORTED(vap, va_acl);
+ }
+ }
+ if (VATTR_IS_ACTIVE(vap, va_access_time)) {
+ /* Access times are lazily updated, get current time if needed */
+ if (cp->c_touch_acctime) {
+ struct timeval tv;
+
+ microtime(&tv);
+ vap->va_access_time.tv_sec = tv.tv_sec;
+ } else {
+ vap->va_access_time.tv_sec = cp->c_atime;
+ }
+ vap->va_access_time.tv_nsec = 0;
+ VATTR_SET_SUPPORTED(vap, va_access_time);
+ }
+ vap->va_create_time.tv_sec = cp->c_itime;
+ vap->va_create_time.tv_nsec = 0;
+ vap->va_modify_time.tv_sec = cp->c_mtime;
+ vap->va_modify_time.tv_nsec = 0;
+ vap->va_change_time.tv_sec = cp->c_ctime;
+ vap->va_change_time.tv_nsec = 0;
+ vap->va_backup_time.tv_sec = cp->c_btime;
+ vap->va_backup_time.tv_nsec = 0;
+
+ /* See if we need to emit the date added field to the user */
+ if (VATTR_IS_ACTIVE(vap, va_addedtime)) {
+ u_int32_t dateadded = hfs_get_dateadded (cp);
+ if (dateadded) {
+ vap->va_addedtime.tv_sec = dateadded;
+ vap->va_addedtime.tv_nsec = 0;
+ VATTR_SET_SUPPORTED (vap, va_addedtime);
+ }
+ }
+
+ /* XXX is this really a good 'optimal I/O size'? */
+ vap->va_iosize = hfsmp->hfs_logBlockSize;
+ vap->va_uid = cp->c_uid;
+ vap->va_gid = cp->c_gid;
+ vap->va_mode = cp->c_mode;
+ vap->va_flags = cp->c_bsdflags;
+
+ /*
+ * 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) uses va_fileid and the Carbon APIs,
+ * which are hardlink-ignorant, will ask for va_linkid.
+ */
+ vap->va_fileid = (u_int64_t)cp->c_fileid;
+ /*
+ * We need to use the origin cache for both hardlinked files
+ * and directories. Hardlinked directories have multiple cnids
+ * and parents (one per link). Hardlinked files also have their
+ * own parents and link IDs separate from the indirect inode number.
+ * If we don't use the cache, we could end up vending the wrong ID
+ * because the cnode will only reflect the link that was looked up most recently.
+ */
+ if (cp->c_flag & C_HARDLINK) {
+ vap->va_linkid = (u_int64_t)hfs_currentcnid(cp);
+ vap->va_parentid = (u_int64_t)hfs_currentparent(cp);
+ } else {
+ vap->va_linkid = (u_int64_t)cp->c_cnid;
+ vap->va_parentid = (u_int64_t)cp->c_parentcnid;
+ }
+ vap->va_fsid = hfsmp->hfs_raw_dev;
+ vap->va_filerev = 0;
+ vap->va_encoding = cp->c_encoding;
+ vap->va_rdev = (v_type == VBLK || v_type == VCHR) ? cp->c_rdev : 0;
+#if HFS_COMPRESSION
+ if (VATTR_IS_ACTIVE(vap, va_data_size)) {
+ if (hide_size)
+ vap->va_data_size = 0;
+ else if (compressed) {
+ if (uncompressed_size == -1) {
+ /* failed to get the uncompressed size above, so just return data_size */
+ vap->va_data_size = data_size;
+ } else {
+ /* use the uncompressed size we fetched above */
+ vap->va_data_size = uncompressed_size;
+ }
+ } else
+ vap->va_data_size = data_size;
+// vap->va_supported |= VNODE_ATTR_va_data_size;
+ VATTR_SET_SUPPORTED(vap, va_data_size);
+ }
+#else
+ vap->va_data_size = data_size;
+ vap->va_supported |= VNODE_ATTR_va_data_size;
+#endif
+
+ if (VATTR_IS_ACTIVE(vap, va_gen)) {
+ if (UBCINFOEXISTS(vp) && (vp->v_ubcinfo->ui_flags & UI_ISMAPPED)) {
+ /* While file is mmapped the generation count is invalid.
+ * However, bump the value so that the write-gen counter
+ * will be different once the file is unmapped (since,
+ * when unmapped the pageouts may not yet have happened)
+ */
+ if (vp->v_ubcinfo->ui_flags & UI_MAPPEDWRITE) {
+ hfs_incr_gencount (cp);
+ }
+ vap->va_gen = 0;
+ } else {
+ vap->va_gen = hfs_get_gencount(cp);
+ }
+
+ VATTR_SET_SUPPORTED(vap, va_gen);
+ }
+ if (VATTR_IS_ACTIVE(vap, va_document_id)) {
+ vap->va_document_id = hfs_get_document_id(cp);
+ VATTR_SET_SUPPORTED(vap, va_document_id);
+ }
+
+ /* Mark them all at once instead of individual VATTR_SET_SUPPORTED calls. */
+ vap->va_supported |= VNODE_ATTR_va_create_time | VNODE_ATTR_va_modify_time |
+ VNODE_ATTR_va_change_time| VNODE_ATTR_va_backup_time |
+ VNODE_ATTR_va_iosize | VNODE_ATTR_va_uid |
+ VNODE_ATTR_va_gid | VNODE_ATTR_va_mode |
+ VNODE_ATTR_va_flags |VNODE_ATTR_va_fileid |
+ VNODE_ATTR_va_linkid | VNODE_ATTR_va_parentid |
+ VNODE_ATTR_va_fsid | VNODE_ATTR_va_filerev |
+ VNODE_ATTR_va_encoding | VNODE_ATTR_va_rdev;
+
+ /* If this is the root, let VFS to find out the mount name, which
+ * may be different from the real name. Otherwise, we need to take care
+ * for hardlinked files, which need to be looked up, if necessary
+ */
+ if (VATTR_IS_ACTIVE(vap, va_name) && (cp->c_cnid != kHFSRootFolderID)) {
+ struct cat_desc linkdesc;
+ int lockflags;
+ int uselinkdesc = 0;
+ cnid_t nextlinkid = 0;
+ cnid_t prevlinkid = 0;
+
+ /* Get the name for ATTR_CMN_NAME. We need to take special care for hardlinks
+ * here because the info. for the link ID requested by getattrlist may be
+ * different than what's currently in the cnode. This is because the cnode
+ * will be filled in with the information for the most recent link ID that went
+ * through namei/lookup(). If there are competing lookups for hardlinks that point
+ * to the same inode, one (or more) getattrlists could be vended incorrect name information.
+ * Also, we need to beware of open-unlinked files which could have a namelen of 0.
+ */
+
+ if ((cp->c_flag & C_HARDLINK) &&
+ ((cp->c_desc.cd_namelen == 0) || (vap->va_linkid != cp->c_cnid))) {
+ /*
+ * If we have no name and our link ID is the raw inode number, then we may
+ * have an open-unlinked file. Go to the next link in this case.
+ */
+ if ((cp->c_desc.cd_namelen == 0) && (vap->va_linkid == cp->c_fileid)) {
+ if ((error = hfs_lookup_siblinglinks(hfsmp, vap->va_linkid, &prevlinkid, &nextlinkid))){
+ goto out;
+ }
+ }
+ else {
+ /* just use link obtained from vap above */
+ nextlinkid = vap->va_linkid;
+ }
+
+ /* We need to probe the catalog for the descriptor corresponding to the link ID
+ * stored in nextlinkid. Note that we don't know if we have the exclusive lock
+ * for the cnode here, so we can't just update the descriptor. Instead,
+ * we should just store the descriptor's value locally and then use it to pass
+ * out the name value as needed below.
+ */
+ if (nextlinkid){
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
+ error = cat_findname(hfsmp, nextlinkid, &linkdesc);
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ if (error == 0) {
+ uselinkdesc = 1;
+ }
+ }
+ }
+
+ /* By this point, we've either patched up the name above and the c_desc
+ * points to the correct data, or it already did, in which case we just proceed
+ * by copying the name into the vap. Note that we will never set va_name to
+ * supported if nextlinkid is never initialized. This could happen in the degenerate
+ * case above involving the raw inode number, where it has no nextlinkid. In this case
+ * we will simply not mark the name bit as supported.
+ */
+ if (uselinkdesc) {
+ strlcpy(vap->va_name, (const char*) linkdesc.cd_nameptr, MAXPATHLEN);
+ VATTR_SET_SUPPORTED(vap, va_name);
+ cat_releasedesc(&linkdesc);
+ }
+ else if (cp->c_desc.cd_namelen) {
+ strlcpy(vap->va_name, (const char*) cp->c_desc.cd_nameptr, MAXPATHLEN);
+ VATTR_SET_SUPPORTED(vap, va_name);
+ }
+ }
+
+out:
+ hfs_unlock(cp);
+ /*
+ * We need to vnode_put the rsrc fork vnode only *after* we've released
+ * the cnode lock, since vnode_put can trigger an inactive call, which
+ * will go back into HFS and try to acquire a cnode lock.
+ */
+ if (rvp) {
+ vnode_put (rvp);
+ }
+
+ return (error);
+}
+
+int
+hfs_vnop_setattr(ap)
+ struct vnop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vnode_attr *a_vap;
+ vfs_context_t a_context;
+ } */ *ap;
+{
+ struct vnode_attr *vap = ap->a_vap;
+ struct vnode *vp = ap->a_vp;
+ struct cnode *cp = NULL;
+ struct hfsmount *hfsmp;
+ kauth_cred_t cred = vfs_context_ucred(ap->a_context);
+ struct proc *p = vfs_context_proc(ap->a_context);
+ int error = 0;
+ uid_t nuid;
+ gid_t ngid;
+ time_t orig_ctime;
+
+ orig_ctime = VTOC(vp)->c_ctime;
+
+#if HFS_COMPRESSION
+ int decmpfs_reset_state = 0;
+ /*
+ we call decmpfs_update_attributes even if the file is not compressed
+ because we want to update the incoming flags if the xattrs are invalid
+ */
+ error = decmpfs_update_attributes(vp, vap);
+ if (error)
+ return error;
+#endif
+ //
+ // if this is not a size-changing setattr and it is not just
+ // an atime update, then check for a snapshot.
+ //
+ if (!VATTR_IS_ACTIVE(vap, va_data_size) && !(vap->va_active == VNODE_ATTR_va_access_time)) {
+ check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_MOD, NSPACE_REARM_NO_ARG);
+ }
+
+#if CONFIG_PROTECT
+ if ((error = cp_handle_vnop(vp, CP_WRITE_ACCESS, 0)) != 0) {
+ return (error);
+ }
+#endif /* CONFIG_PROTECT */
+
+ hfsmp = VTOHFS(vp);
+
+ /* Don't allow modification of the journal. */
+ if (hfs_is_journal_file(hfsmp, VTOC(vp))) {
+ return (EPERM);
+ }
+
+ //
+ // Check if we'll need a document_id and if so, get it before we lock the
+ // the cnode to avoid any possible deadlock with the root vnode which has
+ // to get locked to get the document id
+ //
+ u_int32_t document_id=0;
+ if (VATTR_IS_ACTIVE(vap, va_flags) && (vap->va_flags & UF_TRACKED) && !(VTOC(vp)->c_bsdflags & UF_TRACKED)) {
+ struct FndrExtendedDirInfo *fip = (struct FndrExtendedDirInfo *)((char *)&(VTOC(vp)->c_attr.ca_finderinfo) + 16);
+ //
+ // If the document_id is not set, get a new one. It will be set
+ // on the file down below once we hold the cnode lock.
+ //
+ if (fip->document_id == 0) {
+ if (hfs_generate_document_id(hfsmp, &document_id) != 0) {
+ document_id = 0;
+ }