]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfs_vfsutils.c
xnu-792.17.14.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vfsutils.c
index c35236e693724cc6f29b2c595fc26e5a1118c0da..6024b382d54dd1cb1a17255dfb93f92b4d770779 100644 (file)
@@ -1,23 +1,29 @@
 /*
  * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
+ * @APPLE_OSREFERENCE_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 file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ * 
+ * The 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.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
- * @APPLE_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 /*     @(#)hfs_vfsutils.c      4.0
 *
@@ -245,6 +251,11 @@ OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
        hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
        hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
 
+       if (error == noErr)
+         {
+           error = cat_idlookup(hfsmp, kHFSRootFolderID, NULL, NULL, NULL);
+         }
+
     if ( error == noErr )
       {
         if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) )            //      if the disk is not write protected
@@ -529,6 +540,8 @@ OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
        /* Pick up volume name and create date */
        retval = cat_idlookup(hfsmp, kHFSRootFolderID, &cndesc, &cnattr, NULL);
        if (retval) {
+               if (hfsmp->hfs_attribute_vp)
+                       hfs_unlock(VTOC(hfsmp->hfs_attribute_vp));
                hfs_unlock(VTOC(hfsmp->hfs_allocation_vp));
                hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
                hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
@@ -562,7 +575,7 @@ OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
        //
        if (   (vcb->vcbAtrb & kHFSVolumeJournaledMask)
                && (SWAP_BE32(vhp->lastMountedVersion) != kHFSJMountVersion)
-               && (hfsmp->jnl == NULL)) {
+               && (hfsmp->jnl == NULL)) {
 
                retval = hfs_late_journal_init(hfsmp, vhp, args);
                if (retval != 0) {
@@ -604,9 +617,13 @@ OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
                } else if (hfsmp->jnl) {
                        vfs_setflags(hfsmp->hfs_mp, (uint64_t)((unsigned int)MNT_JOURNALED));
                }
-       } else if (hfsmp->jnl) {
+       } else if (hfsmp->jnl || ((vcb->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) {
                struct cat_attr jinfo_attr, jnl_attr;
                
+               if (hfsmp->hfs_flags & HFS_READ_ONLY) {
+                   vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+               }
+
                // if we're here we need to fill in the fileid's for the
                // journal and journal_info_block.
                hfsmp->hfs_jnlinfoblkid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jinfo_attr, NULL);
@@ -615,6 +632,10 @@ OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
                        printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
                        printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp->hfs_jnlfileid, hfsmp->hfs_jnlinfoblkid);
                }
+
+               if (hfsmp->hfs_flags & HFS_READ_ONLY) {
+                   vcb->vcbAtrb |= kHFSVolumeJournaledMask;
+               }
        }
 
        /*
@@ -1492,7 +1513,7 @@ short MacToVFSError(OSErr err)
                return EOVERFLOW;
        
        case btBadNode:                 /* -32731 */
-               return EBADF;
+               return EIO;
        
        case memFullErr:                /*  -108 */
                return ENOMEM;          /*   +12 */
@@ -1540,45 +1561,57 @@ directoryhint_t *
 hfs_getdirhint(struct cnode *dcp, int index)
 {
        struct timeval tv;
-       directoryhint_t *hint, *next, *oldest;
+       directoryhint_t *hint;
+       boolean_t need_remove, need_init;
        char * name;
 
-       oldest = NULL;
        microuptime(&tv);
 
-       /* Look for an existing hint first */
-       for(hint = dcp->c_hintlist.slh_first; hint != NULL; hint = next) {
-               next = hint->dh_link.sle_next;
-               if (hint->dh_index == index) {
-                       goto out;
-               } else if (oldest == NULL || (hint->dh_time < oldest->dh_time)) {
-                       oldest = hint;
-               }
+       /*
+        *  Look for an existing hint first.  If not found, create a new one (when
+        *  the list is not full) or recycle the oldest hint.  Since new hints are
+        *  always added to the head of the list, the last hint is always the
+        *  oldest.
+        */
+       TAILQ_FOREACH(hint, &dcp->c_hintlist, dh_link) {
+               if (hint->dh_index == index)
+                       break;
        }
-       /* Recycle one if we have too many already. */
-       if ((dcp->c_dirhintcnt >= HFS_MAXDIRHINTS) && (oldest != NULL)) {
-               hint = oldest;
-               if ((name = hint->dh_desc.cd_nameptr)) {
-                       hint->dh_desc.cd_nameptr = NULL;
-                       vfs_removename(name);
+       if (hint != NULL) { /* found an existing hint */
+               need_init = false;
+               need_remove = true;
+       } else { /* cannot find an existing hint */
+               need_init = true;
+               if (dcp->c_dirhintcnt < HFS_MAXDIRHINTS) { /* we don't need recycling */
+                       /* Create a default directory hint */
+                       MALLOC_ZONE(hint, directoryhint_t *, sizeof(directoryhint_t), M_HFSDIRHINT, M_WAITOK);
+                       ++dcp->c_dirhintcnt;
+                       need_remove = false;
+               } else {                                /* recycle the last (i.e., the oldest) hint */
+                       hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead);
+                       if ((name = hint->dh_desc.cd_nameptr)) {
+                               hint->dh_desc.cd_nameptr = NULL;
+                               vfs_removename(name);
+                       }
+                       need_remove = true;
                }
-               goto init;
-       }
-
-       /* Create a default directory hint */
-       MALLOC_ZONE(hint, directoryhint_t *, sizeof(directoryhint_t), M_HFSDIRHINT, M_WAITOK);
-       SLIST_INSERT_HEAD(&dcp->c_hintlist, hint, dh_link);
-       ++dcp->c_dirhintcnt;
-init:
-       hint->dh_index = index;
-       hint->dh_desc.cd_flags = 0;
-       hint->dh_desc.cd_encoding = 0;
-       hint->dh_desc.cd_namelen = 0;
-       hint->dh_desc.cd_nameptr = NULL;
-       hint->dh_desc.cd_parentcnid = dcp->c_cnid;
-       hint->dh_desc.cd_hint = dcp->c_childhint;
-       hint->dh_desc.cd_cnid = 0;
-out:
+       }
+
+       if (need_remove)
+               TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link);
+
+       TAILQ_INSERT_HEAD(&dcp->c_hintlist, hint, dh_link);
+
+       if (need_init) {
+               hint->dh_index = index;
+               hint->dh_desc.cd_flags = 0;
+               hint->dh_desc.cd_encoding = 0;
+               hint->dh_desc.cd_namelen = 0;
+               hint->dh_desc.cd_nameptr = NULL;
+               hint->dh_desc.cd_parentcnid = dcp->c_cnid;
+               hint->dh_desc.cd_hint = dcp->c_childhint;
+               hint->dh_desc.cd_cnid = 0;
+       }
        hint->dh_time = tv.tv_sec;
        return (hint);
 }
@@ -1592,22 +1625,16 @@ __private_extern__
 void
 hfs_reldirhint(struct cnode *dcp, directoryhint_t * relhint)
 {
-       directoryhint_t *hint;
        char * name;
 
-       SLIST_FOREACH(hint, &dcp->c_hintlist, dh_link) {
-               if (hint == relhint) {
-                       SLIST_REMOVE(&dcp->c_hintlist, hint, directoryhint, dh_link);
-                       name = hint->dh_desc.cd_nameptr;
-                       if (name != NULL) {
-                               hint->dh_desc.cd_nameptr = NULL;
-                               vfs_removename(name);
-                       }
-                       FREE_ZONE(hint, sizeof(directoryhint_t), M_HFSDIRHINT);
-                       --dcp->c_dirhintcnt;
-                       break;
-               }
+       TAILQ_REMOVE(&dcp->c_hintlist, relhint, dh_link);
+       name = relhint->dh_desc.cd_nameptr;
+       if (name != NULL) {
+               relhint->dh_desc.cd_nameptr = NULL;
+               vfs_removename(name);
        }
+       FREE_ZONE(relhint, sizeof(directoryhint_t), M_HFSDIRHINT);
+       --dcp->c_dirhintcnt;
 }
 
 /*
@@ -1620,32 +1647,26 @@ void
 hfs_reldirhints(struct cnode *dcp, int stale_hints_only)
 {
        struct timeval tv;
-       directoryhint_t *hint, *next;
+       directoryhint_t *hint, *prev;
        char * name;
 
        if (stale_hints_only)
                microuptime(&tv);
-       else
-               tv.tv_sec = 0;
-
-       for (hint = dcp->c_hintlist.slh_first; hint != NULL; hint = next) {
-               next = hint->dh_link.sle_next;
-               if (stale_hints_only) {
-                       /* Skip over newer entries. */
-                       if ((tv.tv_sec - hint->dh_time) < HFS_DIRHINT_TTL)
-                               continue;
-                       SLIST_REMOVE(&dcp->c_hintlist, hint, directoryhint, dh_link);
-               }
+
+       /* searching from the oldest to the newest, so we can stop early when releasing stale hints only */
+       for (hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead); hint != NULL; hint = prev) {
+               if (stale_hints_only && (tv.tv_sec - hint->dh_time) < HFS_DIRHINT_TTL)
+                       break;  /* stop here if this entry is too new */
                name = hint->dh_desc.cd_nameptr;
                if (name != NULL) {
                        hint->dh_desc.cd_nameptr = NULL;
                        vfs_removename(name);
                }
+               prev = TAILQ_PREV(hint, hfs_hinthead, dh_link); /* must save this pointer before calling FREE_ZONE on this node */
+               TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link);
                FREE_ZONE(hint, sizeof(directoryhint_t), M_HFSDIRHINT);
                --dcp->c_dirhintcnt;
        }
-       if (!stale_hints_only)
-               dcp->c_hintlist.slh_first = NULL;
 }
 
 
@@ -1728,6 +1749,28 @@ hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
        hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize);
        hfsmp->jnl_size  = jibp->size;
 
+       if ((hfsmp->hfs_flags & HFS_READ_ONLY) && (vfs_flags(hfsmp->hfs_mp) & MNT_ROOTFS) == 0) {
+           // if the file system is read-only, check if the journal is empty.
+           // if it is, then we can allow the mount.  otherwise we have to
+           // return failure.
+           retval = journal_is_clean(hfsmp->jvp,
+                                     jibp->offset + embeddedOffset,
+                                     jibp->size,
+                                     devvp,
+                                     hfsmp->hfs_phys_block_size);
+
+           hfsmp->jnl = NULL;
+
+           buf_brelse(jinfo_bp);
+
+           if (retval) {
+             printf("hfs: early journal init: volume on %s is read-only and journal is dirty.  Can not mount volume.\n",
+                     vnode_name(devvp));
+           }
+
+           return retval;
+       }
+
        if (jibp->flags & kJIJournalNeedInitMask) {
                printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
                           jibp->offset + embeddedOffset, jibp->size);
@@ -1915,6 +1958,28 @@ hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_a
        hfsmp->jnl_start = jibp->offset / SWAP_BE32(vhp->blockSize);
        hfsmp->jnl_size  = jibp->size;
 
+       if ((hfsmp->hfs_flags & HFS_READ_ONLY) && (vfs_flags(hfsmp->hfs_mp) & MNT_ROOTFS) == 0) {
+           // if the file system is read-only, check if the journal is empty.
+           // if it is, then we can allow the mount.  otherwise we have to
+           // return failure.
+           retval = journal_is_clean(hfsmp->jvp,
+                                     jibp->offset + (off_t)vcb->hfsPlusIOPosOffset,
+                                     jibp->size,
+                                     devvp,
+                                     hfsmp->hfs_phys_block_size);
+
+           hfsmp->jnl = NULL;
+
+           buf_brelse(jinfo_bp);
+
+           if (retval) {
+             printf("hfs: late journal init: volume on %s is read-only and journal is dirty.  Can not mount volume.\n",
+                    vnode_name(devvp));
+           }
+
+           return retval;
+       }
+
        if (jibp->flags & kJIJournalNeedInitMask) {
                printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
                           jibp->offset + (off_t)vcb->hfsPlusIOPosOffset, jibp->size);