]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfs_vfsops.c
xnu-1228.7.58.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vfsops.c
index d5a05045bce0619d5f1b0d943ee6118fefeb2386..7b67b6686ddbac8b27fc33c0b55cb3509814208d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 1999-2008 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -75,6 +75,7 @@
 #include <sys/kauth.h>
 
 #include <sys/ubc.h>
+#include <sys/ubc_internal.h>
 #include <sys/vnode_internal.h>
 #include <sys/mount_internal.h>
 #include <sys/sysctl.h>
@@ -117,6 +118,9 @@ lck_grp_t *  hfs_mutex_group;
 lck_grp_t *  hfs_rwlock_group;
 
 extern struct vnodeopv_desc hfs_vnodeop_opv_desc;
+/* not static so we can re-use in hfs_readwrite.c for build_path */
+int hfs_vfs_vget(struct mount *mp, ino64_t ino, struct vnode **vpp, vfs_context_t context);
+
 
 static int hfs_changefs(struct mount *mp, struct hfs_mount_args *args);
 static int hfs_fhtovp(struct mount *mp, int fhlen, unsigned char *fhp, struct vnode **vpp, vfs_context_t context);
@@ -135,7 +139,6 @@ static int hfs_sync(struct mount *mp, int waitfor, vfs_context_t context);
 static int hfs_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp, 
                       user_addr_t newp, size_t newlen, vfs_context_t context);
 static int hfs_unmount(struct mount *mp, int mntflags, vfs_context_t context);
-static int hfs_vfs_vget(struct mount *mp, ino64_t ino, struct vnode **vpp, vfs_context_t context);
 static int hfs_vptofh(struct vnode *vp, int *fhlenp, unsigned char *fhp, vfs_context_t context);
 
 static int hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk, u_long reclaimblks, vfs_context_t context);
@@ -371,13 +374,18 @@ hfs_changefs_callback(struct vnode *vp, void *cargs)
        struct cat_desc cndesc;
        struct cat_attr cnattr;
        struct hfs_changefs_cargs *args;
+       int lockflags;
+       int error;
 
        args = (struct hfs_changefs_cargs *)cargs;
 
        cp = VTOC(vp);
        vcb = HFSTOVCB(args->hfsmp);
 
-       if (cat_lookup(args->hfsmp, &cp->c_desc, 0, &cndesc, &cnattr, NULL, NULL)) {
+       lockflags = hfs_systemfile_lock(args->hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
+       error = cat_lookup(args->hfsmp, &cp->c_desc, 0, &cndesc, &cnattr, NULL, NULL);
+       hfs_systemfile_unlock(args->hfsmp, lockflags);
+       if (error) {
                /*
                 * If we couldn't find this guy skip to the next one
                 */
@@ -525,8 +533,9 @@ hfs_changefs(struct mount *mp, struct hfs_mount_args *args)
         *
         * hfs_changefs_callback will be called for each vnode
         * hung off of this mount point
-        * the vnode will be
-        * properly referenced and unreferenced around the callback
+        *
+        * The vnode will be properly referenced and unreferenced 
+        * around the callback
         */
        cargs.hfsmp = hfsmp;
        cargs.namefix = namefix;
@@ -560,6 +569,7 @@ hfs_reload_callback(struct vnode *vp, void *cargs)
 {
        struct cnode *cp;
        struct hfs_reload_cargs *args;
+       int lockflags;
 
        args = (struct hfs_reload_cargs *)cargs;
        /*
@@ -584,8 +594,12 @@ hfs_reload_callback(struct vnode *vp, void *cargs)
                datafork = cp->c_datafork ? &cp->c_datafork->ff_data : NULL;
 
                /* lookup by fileID since name could have changed */
-               if ((args->error = cat_idlookup(args->hfsmp, cp->c_fileid, 0, &desc, &cp->c_attr, datafork)))
+               lockflags = hfs_systemfile_lock(args->hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
+               args->error = cat_idlookup(args->hfsmp, cp->c_fileid, 0, &desc, &cp->c_attr, datafork);
+               hfs_systemfile_unlock(args->hfsmp, lockflags);
+               if (args->error) {
                        return (VNODE_RETURNED_DONE);
+               }
 
                /* update cnode's catalog descriptor */
                (void) replace_desc(cp, &desc);
@@ -1608,7 +1622,7 @@ hfs_statfs(struct mount *mp, register struct vfsstatfs *sbp, __unused vfs_contex
        freeCNIDs = (u_long)0xFFFFFFFF - (u_long)vcb->vcbNxtCNID;
 
        sbp->f_bsize = (u_int32_t)vcb->blockSize;
-       sbp->f_iosize = (size_t)(MAX_UPL_TRANSFER * PAGE_SIZE);
+       sbp->f_iosize = (size_t)cluster_max_io_size(mp, 0);
        sbp->f_blocks = (u_int64_t)((unsigned long)vcb->totalBlocks);
        sbp->f_bfree = (u_int64_t)((unsigned long )hfs_freeblks(hfsmp, 0));
        sbp->f_bavail = (u_int64_t)((unsigned long )hfs_freeblks(hfsmp, 1));
@@ -2275,33 +2289,48 @@ hfs_sysctl(int *name, __unused u_int namelen, user_addr_t oldp, size_t *oldlenp,
        return (ENOTSUP);
 }
 
-
-static int
+/* hfs_vfs_vget is not static since it is used in hfs_readwrite.c to support the
+ * build_path ioctl.  We use it to leverage the code below that updates the origin
+ * cache if necessary.
+ */
+int
 hfs_vfs_vget(struct mount *mp, ino64_t ino, struct vnode **vpp, __unused vfs_context_t context)
 {
        int error;
+       int lockflags;
+       struct hfsmount *hfsmp;
+
+       hfsmp = VFSTOHFS(mp);
 
-       error = hfs_vget(VFSTOHFS(mp), (cnid_t)ino, vpp, 1);
+       error = hfs_vget(hfsmp, (cnid_t)ino, vpp, 1);
        if (error)
                return (error);
 
        /*
         * ADLs may need to have their origin state updated
-        * since build_path needs a valid parent.
+        * since build_path needs a valid parent. The same is true
+        * for hardlinked files as well. There isn't a race window here in re-acquiring
+        * the cnode lock since we aren't pulling any data out of the cnode; instead, we're
+        * going back to the catalog.
         */
-       if (vnode_isdir(*vpp) &&
-           (VTOC(*vpp)->c_flag & C_HARDLINK) &&
+       if ((VTOC(*vpp)->c_flag & C_HARDLINK) &&
            (hfs_lock(VTOC(*vpp), HFS_EXCLUSIVE_LOCK) == 0)) {
                cnode_t *cp = VTOC(*vpp);
                struct cat_desc cdesc;
                
-               if (!hfs_haslinkorigin(cp) &&
-                   (cat_findname(VFSTOHFS(mp), (cnid_t)ino, &cdesc) == 0)) {
-                       if (cdesc.cd_parentcnid !=
-                           VFSTOHFS(mp)->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
-                               hfs_savelinkorigin(cp, cdesc.cd_parentcnid);
+               if (!hfs_haslinkorigin(cp)) {
+                       lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
+                   error = cat_findname(hfsmp, (cnid_t)ino, &cdesc);
+                       hfs_systemfile_unlock(hfsmp, lockflags);
+                       if (error == 0) {
+                               if ((cdesc.cd_parentcnid !=
+                               hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) && 
+                                       (cdesc.cd_parentcnid != 
+                                       hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid)) {
+                                       hfs_savelinkorigin(cp, cdesc.cd_parentcnid);
+                               }
+                               cat_releasedesc(&cdesc);
                        }
-                       cat_releasedesc(&cdesc);
                }
                hfs_unlock(cp);
        }
@@ -2398,6 +2427,7 @@ hfs_vget(struct hfsmount *hfsmp, cnid_t cnid, struct vnode **vpp, int skiplock)
                } else if ((pid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
                           (bcmp(nameptr, HFS_DELETE_PREFIX, HFS_DELETE_PREFIX_LEN) == 0)) {
                        *vpp = NULL;
+                       cat_releasedesc(&cndesc);
                        return (ENOENT);  /* open unlinked file */
                }
        }
@@ -2411,6 +2441,7 @@ hfs_vget(struct hfsmount *hfsmp, cnid_t cnid, struct vnode **vpp, int skiplock)
                cnid_t nextlinkid;
                cnid_t prevlinkid;
                struct cat_desc linkdesc;
+               int lockflags;
 
                cnattr.ca_linkref = linkref;
 
@@ -2420,7 +2451,10 @@ hfs_vget(struct hfsmount *hfsmp, cnid_t cnid, struct vnode **vpp, int skiplock)
                 */
                if ((hfs_lookuplink(hfsmp, linkref, &prevlinkid,  &nextlinkid) == 0) &&
                    (nextlinkid != 0)) {
-                       if (cat_findname(hfsmp, nextlinkid, &linkdesc) == 0) {
+                       lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
+                       error = cat_findname(hfsmp, nextlinkid, &linkdesc);
+                       hfs_systemfile_unlock(hfsmp, lockflags);
+                       if (error == 0) {
                                cat_releasedesc(&cndesc);
                                bcopy(&linkdesc, &cndesc, sizeof(linkdesc));
                        }
@@ -2450,7 +2484,7 @@ hfs_vget(struct hfsmount *hfsmp, cnid_t cnid, struct vnode **vpp, int skiplock)
        
                error = hfs_getnewvnode(hfsmp, NULLVP, &cn, &cndesc, 0, &cnattr, &cnfork, &vp);
 
-               if (error == 0 && (VTOC(vp)->c_flag & C_HARDLINK) && vnode_isdir(vp)) {
+               if ((error == 0) && (VTOC(vp)->c_flag & C_HARDLINK)) {
                        hfs_savelinkorigin(VTOC(vp), cndesc.cd_parentcnid);
                }
                FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
@@ -3313,6 +3347,12 @@ out:
                VTOC(vp)->c_blocks = fp->ff_blocks;
 
        }
+       /*
+          Regardless of whether or not the totalblocks actually increased,
+          we should reset the allocLimit field. If it changed, it will
+          get updated; if not, it will remain the same.
+       */
+       hfsmp->allocLimit = vcb->totalBlocks;
        hfs_systemfile_unlock(hfsmp, lockflags);
        hfs_end_transaction(hfsmp);
 
@@ -4026,6 +4066,7 @@ hfs_reclaim_journal_file(struct hfsmount *hfsmp, vfs_context_t context)
        journal_fork.cf_extents[0].blockCount = newBlockCount;
        journal_fork.cf_blocks = newBlockCount;
        error = cat_update(hfsmp, &journal_desc, &journal_attr, &journal_fork, NULL);
+       cat_releasedesc(&journal_desc);  /* all done with cat descriptor */
        if (error) {
                printf("hfs_reclaim_journal_file: cat_update returned %d\n", error);
                goto free_fail;
@@ -4140,6 +4181,7 @@ hfs_reclaim_journal_info_block(struct hfsmount *hfsmp, vfs_context_t context)
        jib_fork.cf_extents[0].blockCount = 1;
        jib_fork.cf_blocks = 1;
        error = cat_update(hfsmp, &jib_desc, &jib_attr, &jib_fork, NULL);
+       cat_releasedesc(&jib_desc);  /* all done with cat descriptor */
        if (error) {
                printf("hfs_reclaim_journal_info_block: cat_update returned %d\n", error);
                goto fail;
@@ -4409,7 +4451,7 @@ end_iteration:
                        if (error)
                                break;
                        error = hfs_relocate(rvp, hfsmp->hfs_metazone_end + 1, kauth_cred_get(), current_proc());
-                       vnode_put(rvp);
+                       VTOC(rvp)->c_flag |= C_NEED_RVNODE_PUT;
                        if (error)
                                break;
                }
@@ -4556,7 +4598,7 @@ hfs_vfs_getattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t
        VFSATTR_RETURN(fsap, f_filecount, (u_int64_t)hfsmp->vcbFilCnt);
        VFSATTR_RETURN(fsap, f_dircount, (u_int64_t)hfsmp->vcbDirCnt);
        VFSATTR_RETURN(fsap, f_maxobjcount, (u_int64_t)0xFFFFFFFF);
-       VFSATTR_RETURN(fsap, f_iosize, (size_t)(MAX_UPL_TRANSFER * PAGE_SIZE));
+       VFSATTR_RETURN(fsap, f_iosize, (size_t)cluster_max_io_size(mp, 0));
        VFSATTR_RETURN(fsap, f_blocks, (u_int64_t)hfsmp->totalBlocks);
        VFSATTR_RETURN(fsap, f_bfree, (u_int64_t)hfs_freeblks(hfsmp, 0));
        VFSATTR_RETURN(fsap, f_bavail, (u_int64_t)hfs_freeblks(hfsmp, 1));