]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfs_readwrite.c
xnu-792.17.14.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_readwrite.c
index 46f8e54e5e4837324710ccae3c6891070ed2ddf6..fd934e2f5b0ceea920f6aace30c0cf58e2d75682 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_readwrite.c     1.0
  *
@@ -40,6 +46,8 @@
 #include <sys/vnode.h>
 #include <sys/uio.h>
 #include <sys/vfs_context.h>
+#include <sys/disk.h>
+#include <sys/sysctl.h>
 
 #include <miscfs/specfs/specdev.h>
 
@@ -76,6 +84,10 @@ static int  hfs_clonefile(struct vnode *, int, int, int);
 static int  hfs_clonesysfile(struct vnode *, int, int, int, kauth_cred_t, struct proc *);
 
 
+int flush_cache_on_write = 0;
+SYSCTL_INT (_kern, OID_AUTO, flush_cache_on_write, CTLFLAG_RW, &flush_cache_on_write, 0, "always flush the drive cache on writes to uncached files");
+
+
 /*****************************************************************************
 *
 *      I/O Operations on vnodes
@@ -464,6 +476,13 @@ sizeok:
                        cp->c_touch_modtime = TRUE;
                }
        }
+
+       // XXXdbg - testing for vivek and paul lambert
+       {
+           if (flush_cache_on_write && ((ioflag & IO_NOCACHE) || vnode_isnocache(vp))) {
+               VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, NULL);
+           }
+       }
        HFS_KNOTE(vp, NOTE_WRITE);
 
 ioerr_exit:
@@ -990,6 +1009,8 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
 
                if (!(hfsmp->jnl))
                        return (ENOTSUP);
+
+               lck_rw_lock_exclusive(&hfsmp->hfs_insync);
  
                task = current_task();
                task_working_set_disable(task);
@@ -1001,9 +1022,9 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
                vnode_iterate(mp, 0, hfs_freezewrite_callback, NULL);
                hfs_global_exclusive_lock_acquire(hfsmp);
                journal_flush(hfsmp->jnl);
+
                // don't need to iterate on all vnodes, we just need to
                // wait for writes to the system files and the device vnode
-               // vnode_iterate(mp, 0, hfs_freezewrite_callback, NULL);
                if (HFSTOVCB(hfsmp)->extentsRefNum)
                    vnode_waitforwrites(HFSTOVCB(hfsmp)->extentsRefNum, 0, 0, 0, "hfs freeze");
                if (HFSTOVCB(hfsmp)->catalogRefNum)
@@ -1026,7 +1047,7 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
                // if we're not the one who froze the fs then we
                // can't thaw it.
                if (hfsmp->hfs_freezing_proc != current_proc()) {
-                   return EINVAL;
+                   return EPERM;
                }
 
                // NOTE: if you add code here, also go check the
@@ -1034,6 +1055,7 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
                //
                hfsmp->hfs_freezing_proc = NULL;
                hfs_global_exclusive_lock_release(hfsmp);
+               lck_rw_unlock_exclusive(&hfsmp->hfs_insync);
 
                return (0);
        }
@@ -1155,6 +1177,7 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
                        goto err_exit_bulk_access;
                }
                myucred.cr_rgid = myucred.cr_svgid = myucred.cr_groups[0];
+               myucred.cr_gmuid = myucred.cr_uid;
                
                my_context.vc_proc = p;
                my_context.vc_ucred = &myucred;
@@ -1262,13 +1285,18 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
        case HFS_SETACLSTATE: {
                int state;
 
-               if (!is_suser()) {
-                       return (EPERM);
-               }
                if (ap->a_data == NULL) {
                        return (EINVAL);
                }
+
+               vfsp = vfs_statfs(HFSTOVFS(hfsmp));
                state = *(int *)ap->a_data;
+
+               // super-user can enable or disable acl's on a volume.
+               // the volume owner can only enable acl's
+               if (!is_suser() && (state == 0 || kauth_cred_getuid(cred) != vfsp->f_owner)) {
+                       return (EPERM);
+               }
                if (state == 0 || state == 1)
                        return hfs_setextendedsecurity(hfsmp, state);
                else
@@ -1605,6 +1633,11 @@ hfs_vnop_blockmap(struct vnop_blockmap_args *ap)
        int started_tr = 0;
        int tooklock = 0;
 
+       /* Do not allow blockmap operation on a directory */
+       if (vnode_isdir(vp)) {
+               return (ENOTSUP);
+       }
+
        /*
         * Check for underlying vnode requests and ensure that logical
         * to physical mapping is requested.
@@ -1799,6 +1832,7 @@ do_hfs_truncate(struct vnode *vp, off_t length, int flags, int skipsetsize, vfs_
        off_t bytesToAdd;
        off_t actualBytesAdded;
        off_t filebytes;
+       u_int64_t old_filesize;
        u_long fileblocks;
        int blksize;
        struct hfsmount *hfsmp;
@@ -1807,6 +1841,7 @@ do_hfs_truncate(struct vnode *vp, off_t length, int flags, int skipsetsize, vfs_
        blksize = VTOVCB(vp)->blockSize;
        fileblocks = fp->ff_blocks;
        filebytes = (off_t)fileblocks * (off_t)blksize;
+       old_filesize = fp->ff_size;
 
        KERNEL_DEBUG((FSDBG_CODE(DBG_FSRW, 7)) | DBG_FUNC_START,
                 (int)length, (int)fp->ff_size, (int)filebytes, 0, 0);
@@ -1814,6 +1849,10 @@ do_hfs_truncate(struct vnode *vp, off_t length, int flags, int skipsetsize, vfs_
        if (length < 0)
                return (EINVAL);
 
+       /* This should only happen with a corrupt filesystem */
+       if ((off_t)fp->ff_size < 0)
+               return (EINVAL);
+
        if ((!ISHFSPLUS(VTOVCB(vp))) && (length > (off_t)MAXHFSFILESIZE))
                return (EFBIG);
 
@@ -2057,6 +2096,9 @@ do_hfs_truncate(struct vnode *vp, off_t length, int flags, int skipsetsize, vfs_
                                hfs_systemfile_unlock(hfsmp, lockflags);
                        }
                        if (hfsmp->jnl) {
+                               if (retval == 0) {
+                                       fp->ff_size = length;
+                               }
                                (void) hfs_update(vp, TRUE);
                                (void) hfs_volupdate(hfsmp, VOL_UPDATE, 0);
                        }
@@ -2072,7 +2114,7 @@ do_hfs_truncate(struct vnode *vp, off_t length, int flags, int skipsetsize, vfs_
 #endif /* QUOTA */
                }
                /* Only set update flag if the logical length changes */
-               if ((off_t)fp->ff_size != length)
+               if (old_filesize != length)
                        cp->c_touch_modtime = TRUE;
                fp->ff_size = length;
        }
@@ -2106,6 +2148,7 @@ hfs_truncate(struct vnode *vp, off_t length, int flags, int skipsetsize,
        off_t filebytes;
        u_long fileblocks;
        int blksize, error = 0;
+       struct cnode *cp = VTOC(vp);
 
        if (vnode_isdir(vp))
                return (EISDIR);        /* cannot truncate an HFS directory! */
@@ -2125,6 +2168,7 @@ hfs_truncate(struct vnode *vp, off_t length, int flags, int skipsetsize,
                        } else {
                                filebytes = length;
                        }
+                       cp->c_flag |= C_FORCEUPDATE;
                        error = do_hfs_truncate(vp, filebytes, flags, skipsetsize, context);
                        if (error)
                                break;
@@ -2136,6 +2180,7 @@ hfs_truncate(struct vnode *vp, off_t length, int flags, int skipsetsize,
                        } else {
                                filebytes = length;
                        }
+                       cp->c_flag |= C_FORCEUPDATE;
                        error = do_hfs_truncate(vp, filebytes, flags, skipsetsize, context);
                        if (error)
                                break;
@@ -2472,6 +2517,12 @@ hfs_vnop_pageout(struct vnop_pageout_args *ap)
                      cp->c_desc.cd_nameptr ? cp->c_desc.cd_nameptr : "");
        }
        if ( (retval = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
+               if (!(ap->a_flags & UPL_NOCOMMIT)) {
+                       ubc_upl_abort_range(ap->a_pl,
+                                           ap->a_pl_offset,
+                                           ap->a_size,
+                                           UPL_ABORT_FREE_ON_EMPTY);
+               }
                return (retval);
        }
        fp = VTOF(vp);
@@ -2516,7 +2567,6 @@ hfs_vnop_bwrite(struct vnop_bwrite_args *ap)
        int retval = 0;
        register struct buf *bp = ap->a_bp;
        register struct vnode *vp = buf_vnode(bp);
-#if BYTE_ORDER == LITTLE_ENDIAN
        BlockDescriptor block;
 
        /* Trap B-Tree writes */
@@ -2524,22 +2574,29 @@ hfs_vnop_bwrite(struct vnop_bwrite_args *ap)
            (VTOC(vp)->c_fileid == kHFSCatalogFileID) ||
            (VTOC(vp)->c_fileid == kHFSAttributesFileID)) {
 
-               /* Swap if the B-Tree node is in native byte order */
+               /* 
+                * Swap and validate the node if it is in native byte order.
+                * This is always be true on big endian, so we always validate
+                * before writing here.  On little endian, the node typically has
+                * been swapped and validatated when it was written to the journal,
+                * so we won't do anything here.
+                */
                if (((UInt16 *)((char *)buf_dataptr(bp) + buf_count(bp) - 2))[0] == 0x000e) {
                        /* Prepare the block pointer */
                        block.blockHeader = bp;
                        block.buffer = (char *)buf_dataptr(bp);
+                       block.blockNum = buf_lblkno(bp);
                        /* not found in cache ==> came from disk */
                        block.blockReadFromDisk = (buf_fromcache(bp) == 0);
                        block.blockSize = buf_count(bp);
     
                        /* Endian un-swap B-Tree node */
-                       SWAP_BT_NODE (&block, ISHFSPLUS (VTOVCB(vp)), VTOC(vp)->c_fileid, 1);
+                       retval = hfs_swap_BTNode (&block, vp, kSwapBTNodeHostToBig);
+                       if (retval)
+                               panic("hfs_vnop_bwrite: about to write corrupt node!\n");
                }
-
-               /* We don't check to make sure that it's 0x0e00 because it could be all zeros */
        }
-#endif
+
        /* This buffer shouldn't be locked anymore but if it is clear it */
        if ((buf_flags(bp) & B_LOCKED)) {
                // XXXdbg