+__private_extern__
+int
+hfs_truncatefs(struct hfsmount *hfsmp, u_int64_t newsize, __unused vfs_context_t context)
+{
+ struct vnode* rvp = NULL;
+ struct buf *bp = NULL;
+ u_int64_t oldsize;
+ u_int32_t newblkcnt;
+ u_int32_t reclaimblks;
+ int lockflags = 0;
+ int transaction_begun = 0;
+ int error;
+
+ /*
+ * Grab the root vnode to serialize with another hfs_truncatefs call.
+ */
+ error = hfs_vget(hfsmp, kHFSRootFolderID, &rvp, 0);
+ if (error) {
+ return (error);
+ }
+ /*
+ * - HFS Plus file systems only.
+ * - Journaling must be enabled.
+ * - No embedded volumes.
+ */
+ if ((hfsmp->hfs_flags & HFS_STANDARD) ||
+ (hfsmp->jnl == NULL) ||
+ (hfsmp->hfsPlusIOPosOffset != 0)) {
+ error = EPERM;
+ goto out;
+ }
+ oldsize = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize;
+ newblkcnt = newsize / hfsmp->blockSize;
+ reclaimblks = hfsmp->totalBlocks - newblkcnt;
+
+ /* Make sure new size is valid. */
+ if ((newsize < HFS_MIN_SIZE) ||
+ (newsize >= oldsize) ||
+ (newsize % hfsmp->hfs_phys_block_size)) {
+ error = EINVAL;
+ goto out;
+ }
+ /* Make sure there's enough space to work with. */
+ if (reclaimblks > (hfsmp->freeBlocks / 4)) {
+ error = ENOSPC;
+ goto out;
+ }
+
+ printf("hfs_truncatefs: shrinking %s by %d blocks out of %d\n",
+ hfsmp->vcbVN, reclaimblks, hfsmp->totalBlocks);
+
+ if (hfs_start_transaction(hfsmp) != 0) {
+ error = EINVAL;
+ goto out;
+ }
+ transaction_begun = 1;
+
+ /*
+ * Look for files that have blocks beyond newblkcnt.
+ */
+ if (hfs_isallocated(hfsmp, newblkcnt, reclaimblks - 1)) {
+ /*
+ * hfs_reclaimspace will use separate transactions when
+ * relocating files (so we don't overwhelm the journal).
+ */
+ hfs_end_transaction(hfsmp);
+ transaction_begun = 0;
+
+ /* Attempt to reclaim some space. */
+ if (hfs_reclaimspace(hfsmp, newblkcnt) != 0) {
+ printf("hfs_truncatefs: couldn't reclaim space on %s\n", hfsmp->vcbVN);
+ error = ENOSPC;
+ goto out;
+ }
+ if (hfs_start_transaction(hfsmp) != 0) {
+ error = EINVAL;
+ goto out;
+ }
+ transaction_begun = 1;
+
+ /* Check if we're clear now. */
+ if (hfs_isallocated(hfsmp, newblkcnt, reclaimblks - 1)) {
+ printf("hfs_truncatefs: didn't reclaim enough space on %s\n", hfsmp->vcbVN);
+ error = ENOSPC;
+ goto out;
+ }
+ }
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
+
+ /*
+ * Mark the old alternate volume header as free.
+ * We don't bother shrinking allocation bitmap file.
+ */
+ if (hfsmp->blockSize == 512)
+ (void) BlockMarkFree(hfsmp, hfsmp->totalBlocks - 2, 2);
+ else
+ (void) BlockMarkFree(hfsmp, hfsmp->totalBlocks - 1, 1);
+
+ /*
+ * Allocate last block for alternate volume header.
+ */
+ if (hfsmp->blockSize == 512)
+ error = BlockMarkAllocated(hfsmp, newblkcnt - 2, 2);
+ else
+ error = BlockMarkAllocated(hfsmp, newblkcnt - 1, 1);
+
+ if (error) {
+ goto out;
+ }
+
+ /*
+ * Invalidate the existing alternate volume header.
+ */
+ if (hfsmp->hfs_alt_id_sector) {
+ if (buf_meta_bread(hfsmp->hfs_devvp, hfsmp->hfs_alt_id_sector,
+ hfsmp->hfs_phys_block_size, NOCRED, &bp) == 0) {
+ journal_modify_block_start(hfsmp->jnl, bp);
+
+ bzero((void*)((char *)buf_dataptr(bp) + HFS_ALT_OFFSET(hfsmp->hfs_phys_block_size)), kMDBSize);
+
+ journal_modify_block_end(hfsmp->jnl, bp);
+ } else if (bp) {
+ buf_brelse(bp);
+ }
+ bp = NULL;
+ }
+
+ /*
+ * Adjust file system variables and flush them to disk.
+ */
+ hfsmp->freeBlocks -= hfsmp->totalBlocks - newblkcnt;
+ hfsmp->totalBlocks = newblkcnt;
+ hfsmp->hfs_phys_block_count = newsize / hfsmp->hfs_phys_block_size;
+ hfsmp->hfs_alt_id_sector = HFS_ALT_SECTOR(hfsmp->hfs_phys_block_size, hfsmp->hfs_phys_block_count);
+ MarkVCBDirty(hfsmp);
+ error = hfs_flushvolumeheader(hfsmp, MNT_WAIT, HFS_ALTFLUSH);
+ if (error)
+ panic("hfs_truncatefs: unexpected error flushing volume header (%d)\n", error);
+out:
+ if (lockflags) {
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ }
+ if (transaction_begun) {
+ hfs_end_transaction(hfsmp);
+ }
+ if (rvp) {
+ hfs_unlock(VTOC(rvp));
+ vnode_put(rvp);
+ }
+ return (error);
+}
+
+/*
+ * Reclaim space at the end of a file system.
+ */
+static int
+hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk)
+{
+ struct vnode *vp = NULL;
+ FCB *fcb;
+ struct BTreeIterator * iterator = NULL;
+ struct FSBufferDescriptor btdata;
+ struct HFSPlusCatalogFile filerec;
+ u_int32_t saved_next_allocation;
+ cnid_t * cnidbufp;
+ size_t cnidbufsize;
+ int filecnt;
+ int maxfilecnt;
+ u_long block;
+ int lockflags;
+ int i;
+ int error;
+
+ /*
+ * Check if Attributes file overlaps.
+ */
+ if (hfsmp->hfs_attribute_vp) {
+ struct filefork *fp;
+
+ fp = VTOF(hfsmp->hfs_attribute_vp);
+ for (i = 0; i < kHFSPlusExtentDensity; ++i) {
+ block = fp->ff_extents[i].startBlock +
+ fp->ff_extents[i].blockCount;
+ if (block >= startblk) {
+ printf("hfs_reclaimspace: Attributes file can't move\n");
+ return (EPERM);
+ }
+ }
+ }
+
+ /* For now we'll move a maximum of 16,384 files. */
+ maxfilecnt = MIN(hfsmp->hfs_filecount, 16384);
+ cnidbufsize = maxfilecnt * sizeof(cnid_t);
+ if (kmem_alloc(kernel_map, (vm_offset_t *)&cnidbufp, cnidbufsize)) {
+ return (ENOMEM);
+ }
+ if (kmem_alloc(kernel_map, (vm_offset_t *)&iterator, sizeof(*iterator))) {
+ kmem_free(kernel_map, (vm_offset_t)cnidbufp, cnidbufsize);
+ return (ENOMEM);
+ }
+
+ saved_next_allocation = hfsmp->nextAllocation;
+ hfsmp->nextAllocation = hfsmp->hfs_metazone_start;
+
+ fcb = VTOF(hfsmp->hfs_catalog_vp);
+ bzero(iterator, sizeof(*iterator));
+
+ btdata.bufferAddress = &filerec;
+ btdata.itemSize = sizeof(filerec);
+ btdata.itemCount = 1;
+
+ /* Keep the Catalog file locked during iteration. */
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
+ error = BTIterateRecord(fcb, kBTreeFirstRecord, iterator, NULL, NULL);
+ if (error) {
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ goto out;
+ }
+
+ /*
+ * Iterate over all the catalog records looking for files
+ * that overlap into the space we're trying to free up.
+ */
+ for (filecnt = 0; filecnt < maxfilecnt; ) {
+ error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
+ if (error) {
+ if (error == btNotFound)
+ error = 0;
+ break;
+ }
+ if (filerec.recordType != kHFSPlusFileRecord ||
+ filerec.fileID == hfsmp->hfs_jnlfileid)
+ continue;
+ /*
+ * Check if either fork overlaps target space.
+ */
+ for (i = 0; i < kHFSPlusExtentDensity; ++i) {
+ block = filerec.dataFork.extents[i].startBlock +
+ filerec.dataFork.extents[i].blockCount;
+ if (block >= startblk) {
+ if (filerec.fileID == hfsmp->hfs_jnlfileid) {
+ printf("hfs_reclaimspace: cannot move active journal\n");
+ error = EPERM;
+ break;
+ }
+ cnidbufp[filecnt++] = filerec.fileID;
+ break;
+ }
+ block = filerec.resourceFork.extents[i].startBlock +
+ filerec.resourceFork.extents[i].blockCount;
+ if (block >= startblk) {
+ cnidbufp[filecnt++] = filerec.fileID;
+ break;
+ }
+ }
+ }
+ /* All done with catalog. */
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ if (error)
+ goto out;
+
+ /* Now move any files that are in the way. */
+ for (i = 0; i < filecnt; ++i) {
+ struct vnode * rvp;
+
+ if (hfs_vget(hfsmp, cnidbufp[i], &vp, 0) != 0)
+ continue;
+
+ /* Relocate any data fork blocks. */
+ if (VTOF(vp)->ff_blocks > 0) {
+ error = hfs_relocate(vp, hfsmp->hfs_metazone_end + 1, kauth_cred_get(), current_proc());
+ }
+ hfs_unlock(VTOC(vp));
+ if (error)
+ break;
+
+ /* Relocate any resource fork blocks. */
+ if ((VTOC((vp))->c_blocks - VTOF((vp))->ff_blocks) > 0) {
+ error = hfs_vgetrsrc(hfsmp, vp, &rvp, current_proc());
+ if (error)
+ break;
+ hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK);
+ error = hfs_relocate(rvp, hfsmp->hfs_metazone_end + 1, kauth_cred_get(), current_proc());
+ hfs_unlock(VTOC(rvp));
+ vnode_put(rvp);
+ if (error)
+ break;
+ }
+ vnode_put(vp);
+ vp = NULL;
+ }
+ if (vp) {
+ vnode_put(vp);
+ vp = NULL;
+ }
+
+ /*
+ * Note: this implementation doesn't handle overflow extents.
+ */
+out:
+ kmem_free(kernel_map, (vm_offset_t)iterator, sizeof(*iterator));
+ kmem_free(kernel_map, (vm_offset_t)cnidbufp, cnidbufsize);
+
+ /* On errors restore the roving allocation pointer. */
+ if (error) {
+ hfsmp->nextAllocation = saved_next_allocation;
+ }
+ return (error);
+}
+
+
+/*
+ * Get file system attributes.
+ */
+static int
+hfs_vfs_getattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t context)
+{
+ ExtendedVCB *vcb = VFSTOVCB(mp);
+ struct hfsmount *hfsmp = VFSTOHFS(mp);
+ u_long freeCNIDs;
+
+ freeCNIDs = (u_long)0xFFFFFFFF - (u_long)hfsmp->vcbNxtCNID;
+
+ VFSATTR_RETURN(fsap, f_objcount, (uint64_t)hfsmp->vcbFilCnt + (uint64_t)hfsmp->vcbDirCnt);
+ VFSATTR_RETURN(fsap, f_filecount, (uint64_t)hfsmp->vcbFilCnt);
+ VFSATTR_RETURN(fsap, f_dircount, (uint64_t)hfsmp->vcbDirCnt);
+ VFSATTR_RETURN(fsap, f_maxobjcount, (uint64_t)0xFFFFFFFF);
+ VFSATTR_RETURN(fsap, f_iosize, (size_t)(MAX_UPL_TRANSFER * PAGE_SIZE));
+ VFSATTR_RETURN(fsap, f_blocks, (uint64_t)hfsmp->totalBlocks);
+ VFSATTR_RETURN(fsap, f_bfree, (uint64_t)hfs_freeblks(hfsmp, 0));
+ VFSATTR_RETURN(fsap, f_bavail, (uint64_t)hfs_freeblks(hfsmp, 1));
+ VFSATTR_RETURN(fsap, f_bsize, (uint32_t)vcb->blockSize);
+ /* XXX needs clarification */
+ VFSATTR_RETURN(fsap, f_bused, hfsmp->totalBlocks - hfs_freeblks(hfsmp, 1));
+ /* Maximum files is constrained by total blocks. */
+ VFSATTR_RETURN(fsap, f_files, (uint64_t)(hfsmp->totalBlocks - 2));
+ VFSATTR_RETURN(fsap, f_ffree, MIN((uint64_t)freeCNIDs, (uint64_t)hfs_freeblks(hfsmp, 1)));
+
+ fsap->f_fsid.val[0] = hfsmp->hfs_raw_dev;
+ fsap->f_fsid.val[1] = vfs_typenum(mp);
+ VFSATTR_SET_SUPPORTED(fsap, f_fsid);
+
+ VFSATTR_RETURN(fsap, f_signature, vcb->vcbSigWord);
+ VFSATTR_RETURN(fsap, f_carbon_fsid, 0);
+
+ if (VFSATTR_IS_ACTIVE(fsap, f_capabilities)) {
+ vol_capabilities_attr_t *cap;
+
+ cap = &fsap->f_capabilities;
+
+ if (hfsmp->hfs_flags & HFS_STANDARD) {
+ cap->capabilities[VOL_CAPABILITIES_FORMAT] =
+ VOL_CAP_FMT_PERSISTENTOBJECTIDS |
+ VOL_CAP_FMT_CASE_PRESERVING |
+ VOL_CAP_FMT_FAST_STATFS;
+ } else {
+ cap->capabilities[VOL_CAPABILITIES_FORMAT] =
+ VOL_CAP_FMT_PERSISTENTOBJECTIDS |
+ VOL_CAP_FMT_SYMBOLICLINKS |
+ VOL_CAP_FMT_HARDLINKS |
+ VOL_CAP_FMT_JOURNAL |
+ (hfsmp->jnl ? VOL_CAP_FMT_JOURNAL_ACTIVE : 0) |
+ (hfsmp->hfs_flags & HFS_CASE_SENSITIVE ? VOL_CAP_FMT_CASE_SENSITIVE : 0) |
+ VOL_CAP_FMT_CASE_PRESERVING |
+ VOL_CAP_FMT_FAST_STATFS |
+ VOL_CAP_FMT_2TB_FILESIZE;
+ }
+ cap->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;
+ cap->capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
+ cap->capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
+
+ cap->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;
+ cap->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;
+ cap->valid[VOL_CAPABILITIES_RESERVED1] = 0;
+ cap->valid[VOL_CAPABILITIES_RESERVED2] = 0;
+ VFSATTR_SET_SUPPORTED(fsap, f_capabilities);
+ }
+ if (VFSATTR_IS_ACTIVE(fsap, f_attributes)) {
+ vol_attributes_attr_t *attrp = &fsap->f_attributes;
+
+ attrp->validattr.commonattr = ATTR_CMN_VALIDMASK;
+ attrp->validattr.volattr = ATTR_VOL_VALIDMASK & ~ATTR_VOL_INFO;
+ attrp->validattr.dirattr = ATTR_DIR_VALIDMASK;
+ attrp->validattr.fileattr = ATTR_FILE_VALIDMASK;
+ attrp->validattr.forkattr = 0;
+
+ attrp->nativeattr.commonattr = ATTR_CMN_VALIDMASK;
+ attrp->nativeattr.volattr = ATTR_VOL_VALIDMASK & ~ATTR_VOL_INFO;
+ attrp->nativeattr.dirattr = ATTR_DIR_VALIDMASK;
+ attrp->nativeattr.fileattr = ATTR_FILE_VALIDMASK;
+ attrp->nativeattr.forkattr = 0;
+ VFSATTR_SET_SUPPORTED(fsap, f_attributes);
+ }
+ fsap->f_create_time.tv_sec = hfsmp->vcbCrDate;
+ fsap->f_create_time.tv_nsec = 0;
+ VFSATTR_SET_SUPPORTED(fsap, f_create_time);
+ fsap->f_modify_time.tv_sec = hfsmp->vcbLsMod;
+ fsap->f_modify_time.tv_nsec = 0;
+ VFSATTR_SET_SUPPORTED(fsap, f_modify_time);
+
+ fsap->f_backup_time.tv_sec = hfsmp->vcbVolBkUp;
+ fsap->f_backup_time.tv_nsec = 0;
+ VFSATTR_SET_SUPPORTED(fsap, f_backup_time);
+ if (VFSATTR_IS_ACTIVE(fsap, f_fssubtype)) {
+ uint16_t subtype = 0;
+
+ /*
+ * Subtypes (flavors) for HFS
+ * 0: Mac OS Extended
+ * 1: Mac OS Extended (Journaled)
+ * 2: Mac OS Extended (Case Sensitive)
+ * 3: Mac OS Extended (Case Sensitive, Journaled)
+ * 4 - 127: Reserved
+ * 128: Mac OS Standard
+ *
+ */
+ if (hfsmp->hfs_flags & HFS_STANDARD) {
+ subtype = HFS_SUBTYPE_STANDARDHFS;
+ } else /* HFS Plus */ {
+ if (hfsmp->jnl)
+ subtype |= HFS_SUBTYPE_JOURNALED;
+ if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
+ subtype |= HFS_SUBTYPE_CASESENSITIVE;
+ }
+ fsap->f_fssubtype = subtype;
+ VFSATTR_SET_SUPPORTED(fsap, f_fssubtype);
+ }
+
+ if (VFSATTR_IS_ACTIVE(fsap, f_vol_name)) {
+ strncpy(fsap->f_vol_name, hfsmp->vcbVN, MAXPATHLEN);
+ fsap->f_vol_name[MAXPATHLEN - 1] = 0;
+ VFSATTR_SET_SUPPORTED(fsap, f_vol_name);
+ }
+ return (0);
+}
+
+/*
+ * Perform a volume rename. Requires the FS' root vp.
+ */
+static int
+hfs_rename_volume(struct vnode *vp, const char *name, proc_t p)
+{
+ ExtendedVCB *vcb = VTOVCB(vp);
+ struct cnode *cp = VTOC(vp);
+ struct hfsmount *hfsmp = VTOHFS(vp);
+ struct cat_desc to_desc;
+ struct cat_desc todir_desc;
+ struct cat_desc new_desc;
+ cat_cookie_t cookie;
+ int lockflags;
+ int error = 0;
+
+ /*
+ * Ignore attempts to rename a volume to a zero-length name.
+ */
+ if (name[0] == 0)
+ return(0);
+
+ 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 = name;
+ to_desc.cd_namelen = strlen(name);
+ to_desc.cd_parentcnid = kHFSRootParentID;
+ to_desc.cd_cnid = cp->c_cnid;
+ to_desc.cd_flags = CD_ISDIR;
+
+ if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK)) == 0) {
+ if ((error = hfs_start_transaction(hfsmp)) == 0) {
+ if ((error = cat_preflight(hfsmp, CAT_RENAME, &cookie, p)) == 0) {
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+
+ error = cat_rename(hfsmp, &cp->c_desc, &todir_desc, &to_desc, &new_desc);
+
+ /*
+ * If successful, update the name in the VCB, ensure it's terminated.
+ */
+ if (!error) {
+ strncpy(vcb->vcbVN, name, sizeof(vcb->vcbVN));
+ vcb->vcbVN[sizeof(vcb->vcbVN) - 1] = 0;
+ }
+
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ cat_postflight(hfsmp, &cookie, p);
+
+ if (error)
+ vcb->vcbFlags |= 0xFF00;
+ (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT, 0);
+ }
+ hfs_end_transaction(hfsmp);
+ }
+ if (!error) {
+ /* 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;
+ }
+
+ hfs_unlock(cp);
+ }
+
+ return(error);
+}
+
+/*
+ * Get file system attributes.
+ */
+static int
+hfs_vfs_setattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t context)
+{
+ kauth_cred_t cred = vfs_context_ucred(context);
+ int error = 0;
+
+ /*
+ * Must be superuser or owner of filesystem to change volume attributes
+ */
+ if (!kauth_cred_issuser(cred) && (kauth_cred_getuid(cred) != vfs_statfs(mp)->f_owner))
+ return(EACCES);
+
+ if (VFSATTR_IS_ACTIVE(fsap, f_vol_name)) {
+ vnode_t root_vp;
+
+ error = hfs_vfs_root(mp, &root_vp, context);
+ if (error)
+ goto out;
+
+ error = hfs_rename_volume(root_vp, fsap->f_vol_name, vfs_context_proc(context));
+ (void) vnode_put(root_vp);
+ if (error)
+ goto out;
+
+ VFSATTR_SET_SUPPORTED(fsap, f_vol_name);
+ }
+
+out:
+ return error;
+}
+