|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights ! 7: * Reserved. This file contains Original Code and/or Modifications of ! 8: * Original Code as defined in and that are subject to the Apple Public ! 9: * Source License Version 1.1 (the "License"). You may not use this file ! 10: * except in compliance with the License. Please obtain a copy of the ! 11: * License at http://www.apple.com/publicsource and read it before using ! 12: * this file. ! 13: * ! 14: * The Original Code and all software distributed under the License are ! 15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 19: * License for the specific language governing rights and limitations ! 20: * under the License. ! 21: * ! 22: * @APPLE_LICENSE_HEADER_END@ ! 23: */ ! 24: /* ! 25: * Copyright (c) 1991, 1993, 1994 ! 26: * The Regents of the University of California. All rights reserved. ! 27: * (c) UNIX System Laboratories, Inc. ! 28: * All or some portions of this file are derived from material licensed ! 29: * to the University of California by American Telephone and Telegraph ! 30: * Co. or Unix System Laboratories, Inc. and are reproduced herein with ! 31: * the permission of UNIX System Laboratories, Inc. ! 32: * ! 33: * Redistribution and use in source and binary forms, with or without ! 34: * modification, are permitted provided that the following conditions ! 35: * are met: ! 36: * 1. Redistributions of source code must retain the above copyright ! 37: * notice, this list of conditions and the following disclaimer. ! 38: * 2. Redistributions in binary form must reproduce the above copyright ! 39: * notice, this list of conditions and the following disclaimer in the ! 40: * documentation and/or other materials provided with the distribution. ! 41: * 3. All advertising materials mentioning features or use of this software ! 42: * must display the following acknowledgement: ! 43: * This product includes software developed by the University of ! 44: * California, Berkeley and its contributors. ! 45: * 4. Neither the name of the University nor the names of its contributors ! 46: * may be used to endorse or promote products derived from this software ! 47: * without specific prior written permission. ! 48: * ! 49: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ! 50: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ! 51: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ! 52: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ! 53: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ! 54: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ! 55: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 56: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ! 57: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ! 58: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ! 59: * SUCH DAMAGE. ! 60: * ! 61: * hfs_vfsops.c ! 62: * derived from @(#)ufs_vfsops.c 8.8 (Berkeley) 5/20/95 ! 63: * ! 64: * (c) Copyright 1997-1998 Apple Computer, Inc. All rights reserved. ! 65: * ! 66: * hfs_vfsops.c -- VFS layer for loadable HFS file system. ! 67: * ! 68: * HISTORY ! 69: * 12-Nov-1998 Pat Dirks Changed hfs_statfs to return volume's actual log. block size (#2286198). ! 70: * 22-Aug-1998 Scott Roberts Assign uid,gid, and mask for default on objects. ! 71: * 29-Jul-1998 Pat Dirks Fixed changed hfs_vget() to release complex node when retrying for data fork node. ! 72: * 27-Jul-1998 Scott Roberts Changes hfs_vget() to return data forks instead of complex. ! 73: * 14-Jul-1998 CHW Added check for use count of device node in hfs_mountfs ! 74: * 1-Jul-1998 Don Brady Always set kHFSVolumeUnmountedMask bit of vcb->vcbAtrb in hfs_unmount. ! 75: * 30-Jun-1998 Don Brady Removed hard-coded EINVAL error in hfs_mountfs (for radar #2249539). ! 76: * 24-Jun-1998 Don Brady Added setting of timezone to hfs_mount (radar #2226387). ! 77: * 4-Jun-1998 Don Brady Use VPUT/VRELE macros instead of vput/vrele. ! 78: * 6-May-1998 Scott Roberts Updated hfs_vget with kernel changes. ! 79: * 29-Apr-1998 Don Brady Update hfs_statfs to actually fill in statfs fields (radar #2227092). ! 80: * 23-Apr-1998 Pat Dirks Cleaned up code to call brelse() on errors from bread(). ! 81: * 4/20/1998 Don Brady Remove course-grained hfs metadata locking. ! 82: * 4/18/1998 Don Brady Add VCB locking. ! 83: * 4/16/1998 Don Brady hfs_unmount now flushes the volume bitmap. Add b-tree locking to hfs_vget. ! 84: * 4/8/1998 Don Brady Replace hfs_mdbupdate with hfs_flushvolumeheader and hfs_flushMDB. ! 85: * 4/8/1998 Don Brady In hfs_unmount call hfs_mdbupdate before trashing metafiles! ! 86: * 4/3/1998 Don Brady Call InitCatalogCache instead of PostInitFS. ! 87: * 4/1/1998 Don Brady Get rid of gHFSFlags, gReqstVol and gFlushOnlyFlag globals (not used). ! 88: * 3/30/1998 Don Brady In hfs_unmount use SKIPSYSTEM option on first vflush. ! 89: * 3/26/1998 Don Brady Changed hfs_unmount to call vflush before calling hfsUnmount. ! 90: * In hfs_mountfs don't mount hfs-wrapper. ! 91: * 3/19/1998 Pat Dirks Fixed bug in hfs_mount where device vnode was being ! 92: * released on way out. ! 93: * 11/14/1997 Pat Dirks Derived from hfs_vfsops.c ! 94: */ ! 95: ! 96: #include <sys/types.h> ! 97: #include <sys/namei.h> ! 98: #include <sys/param.h> ! 99: #include <sys/proc.h> ! 100: #include <mach/machine/vm_types.h> ! 101: #include <sys/vnode.h> ! 102: #include <sys/mount.h> ! 103: #include <sys/malloc.h> ! 104: #include <sys/fcntl.h> ! 105: #include <sys/stat.h> ! 106: #include <libkern/libkern.h> ! 107: #include <bsd/dev/disk.h> ! 108: #include <mach/machine/simple_lock.h> ! 109: #include <miscfs/specfs/specdev.h> ! 110: #include <sys/systm.h> ! 111: #include <mach/features.h> ! 112: #include <kern/mapfs.h> ! 113: ! 114: #include "hfs.h" ! 115: #include "hfs_dbg.h" ! 116: #include "mount_hfs.h" ! 117: ! 118: #include "hfscommon/headers/FileMgrInternal.h" ! 119: ! 120: typedef (*PFI)(); ! 121: ! 122: extern char *strncpy __P((char *, const char *, size_t)); /* Kernel already includes a copy of strncpy somewhere... */ ! 123: extern int strcmp __P((const char *, const char *)); /* Kernel already includes a copy of strcmp somewhere... */ ! 124: ! 125: extern struct proc * current_proc_EXTERNAL(); ! 126: ! 127: /* The following refer to kernel global variables used in the loading/initialization: */ ! 128: extern int maxvfsslots; /* Total number of slots in the system's vfsconf table */ ! 129: extern int maxvfsconf; /* The highest fs type number [old-style ID] in use [dispite its name] */ ! 130: extern int vfs_opv_numops; /* The total number of defined vnode operations */ ! 131: extern int kdp_flag; ! 132: ! 133: #if DIAGNOSTIC ! 134: int hfs_dbg_all = 0; ! 135: int hfs_dbg_vfs = 0; ! 136: int hfs_dbg_vop = 1; ! 137: int hfs_dbg_load = 0; ! 138: int hfs_dbg_io = 1; ! 139: int hfs_dbg_utils = 1; ! 140: int hfs_dbg_rw = 1; ! 141: int hfs_dbg_DIT = 0; ! 142: int hfs_dbg_tree = 0; ! 143: int hfs_dbg_err = 0; ! 144: int hfs_dbg_test = 0; ! 145: #endif ! 146: ! 147: /* ! 148: * HFS File System globals: ! 149: */ ! 150: Ptr gBufferAddress[BUFFERPTRLISTSIZE]; ! 151: struct buf *gBufferHeaderPtr[BUFFERPTRLISTSIZE]; ! 152: int gBufferListIndex; ! 153: simple_lock_data_t gBufferPtrListLock; ! 154: ! 155: static char hfs_fs_name[MFSNAMELEN] = "hfs"; ! 156: ! 157: /* The following represent information held in low-memory on the MacOS: */ ! 158: ! 159: struct FSVarsRec *gFSMVars; ! 160: ! 161: /* ! 162: * Global variables defined in other modules: ! 163: */ ! 164: extern struct vnodeopv_desc hfs_vnodeop_opv_desc; ! 165: ! 166: ! 167: int hfs_reload(struct mount *mp, struct ucred *cred, struct proc *p); ! 168: int hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p); ! 169: int hfs_vget(struct mount *mp, void *objID, struct vnode **vpp); ! 170: void hfs_vhashinit(); ! 171: ! 172: static int hfs_statfs(); ! 173: ! 174: ! 175: /* ! 176: * VFS Operations. ! 177: * ! 178: * mount system call ! 179: */ ! 180: ! 181: static int hfs_mount (mp, path, data, ndp, p) ! 182: register struct mount *mp; ! 183: char *path; ! 184: caddr_t data; ! 185: struct nameidata *ndp; ! 186: struct proc *p; ! 187: { ! 188: struct hfsmount *hfsmp; ! 189: struct vnode *devvp; ! 190: struct hfs_mount_args args; ! 191: size_t size; ! 192: int retval = E_NONE; ! 193: mode_t accessmode; ! 194: ! 195: DBG_FUNC_NAME("hfs_mount"); ! 196: DBG_PRINT_FUNC_NAME(); ! 197: ! 198: #if DIAGNOSTIC ! 199: // call_kdp(); ! 200: #endif ! 201: ! 202: #if FORCE_READONLY ! 203: // DBG_ASSERT((mp->mnt_flag & MNT_RDONLY) == MNT_RDONLY); /* Should already be set */ ! 204: mp->mnt_flag |= MNT_RDONLY; /* ALWAYS act as if it's mounted read-only */ ! 205: #endif /* FORCE_READONLY */ ! 206: ! 207: if ((retval = copyin(data, (caddr_t)&args, sizeof(args)))) ! 208: goto error_exit; ! 209: ! 210: /* ! 211: * If updating, check whether changing from read-only to ! 212: * read/write; if there is no device name, that's all we do. ! 213: */ ! 214: if (mp->mnt_flag & MNT_UPDATE) { ! 215: unsigned short flags; ! 216: ! 217: DBG_VFS(("hfs_mount: handling update...\n")); ! 218: ! 219: hfsmp = VFSTOHFS(mp); ! 220: if (hfsmp->hfs_fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) { ! 221: flags = WRITECLOSE; ! 222: if (mp->mnt_flag & MNT_FORCE) ! 223: flags |= FORCECLOSE; ! 224: if ((retval = hfs_flushfiles(mp, flags))) ! 225: goto error_exit; ! 226: hfsmp->hfs_fs_clean = 1; ! 227: hfsmp->hfs_fs_ronly = 1; ! 228: if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord) ! 229: retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT); ! 230: else ! 231: retval = hfs_flushMDB(hfsmp, MNT_WAIT); ! 232: ! 233: if (retval) { ! 234: hfsmp->hfs_fs_clean = 0; ! 235: hfsmp->hfs_fs_ronly = 0; ! 236: goto error_exit; ! 237: } ! 238: } ! 239: ! 240: if ((mp->mnt_flag & MNT_RELOAD) && ! 241: (retval = hfs_reload(mp, ndp->ni_cnd.cn_cred, p))) ! 242: goto error_exit; ! 243: ! 244: if (hfsmp->hfs_fs_ronly && (mp->mnt_flag & MNT_WANTRDWR)) { ! 245: /* ! 246: * If upgrade to read-write by non-root, then verify ! 247: * that user has necessary permissions on the device. ! 248: */ ! 249: if (p->p_ucred->cr_uid != 0) { ! 250: devvp = hfsmp->hfs_devvp; ! 251: vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); ! 252: if ((retval = VOP_ACCESS(devvp, VREAD | VWRITE, p->p_ucred, p))) { ! 253: VOP_UNLOCK(devvp, 0, p); ! 254: goto error_exit; ! 255: } ! 256: VOP_UNLOCK(devvp, 0, p); ! 257: } ! 258: hfsmp->hfs_fs_ronly = 0; ! 259: hfsmp->hfs_fs_clean = 0; ! 260: if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord) ! 261: retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT); ! 262: else ! 263: retval = hfs_flushMDB(hfsmp, MNT_WAIT); ! 264: ! 265: if (retval != E_NONE) ! 266: goto error_exit; ! 267: } ! 268: ! 269: #if 0 /* XXX PPD */ ! 270: if (args.ma_fspec == 0) { ! 271: /* ! 272: * Process export requests. ! 273: */ ! 274: retval = (vfs_export(mp, &hfsmp->hfs_export, &args.ma_export)); ! 275: } ! 276: #endif ! 277: ! 278: goto std_exit; ! 279: } ! 280: ! 281: /* ! 282: * Not an update, or updating the name: look up the name ! 283: * and verify that it refers to a sensible block device. ! 284: */ ! 285: DBG_VFS(("hfs_mount: looking up device name (%s)...\n", args.ma_fspec)); ! 286: ! 287: NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.ma_fspec, p); ! 288: retval = namei(ndp); ! 289: if (retval != E_NONE) { ! 290: DBG_ERR(("hfs_mount: CAN'T GET DEVICE: %s, %x\n", args.ma_fspec, ndp->ni_vp->v_rdev)); ! 291: goto error_exit; ! 292: } ! 293: ! 294: devvp = ndp->ni_vp; ! 295: DBG_VFS(("hfs_mount: Device is %x\n",(uint)devvp)); ! 296: ! 297: if (devvp->v_type != VBLK) { ! 298: VRELE(devvp); ! 299: retval = ENOTBLK; ! 300: goto error_exit; ! 301: }; ! 302: if (major(devvp->v_rdev) >= nblkdev) { ! 303: VRELE(devvp); ! 304: retval = ENXIO; ! 305: goto error_exit; ! 306: }; ! 307: ! 308: /* ! 309: * If mount by non-root, then verify that user has necessary ! 310: * permissions on the device. ! 311: */ ! 312: if (p->p_ucred->cr_uid != 0) { ! 313: DBG_VFS(("hfs_mount: Checking mounting access\n")); ! 314: accessmode = VREAD; ! 315: if ((mp->mnt_flag & MNT_RDONLY) == 0) ! 316: accessmode |= VWRITE; ! 317: vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p); ! 318: if ((retval = VOP_ACCESS(devvp, accessmode, p->p_ucred, p))) { ! 319: VPUT(devvp); ! 320: goto error_exit; ! 321: } ! 322: VOP_UNLOCK(devvp, 0, p); ! 323: } ! 324: ! 325: /* establish the zimezone (not used by HFS Plus) */ ! 326: gTimeZone = args.ma_timezone; ! 327: ! 328: DBG_VFS(("hfs_mount: Attempting to mount on %s\n", path)); ! 329: retval = hfs_mountfs(devvp, mp, p); ! 330: ! 331: if (retval != E_NONE) { ! 332: VRELE(devvp); ! 333: goto error_exit; ! 334: }; ! 335: ! 336: hfsmp = VFSTOHFS(mp); ! 337: ! 338: hfsmp->hfs_uid = args.ma_uid; ! 339: hfsmp->hfs_gid = args.ma_gid; ! 340: hfsmp->hfs_dir_mask = args.ma_mask & ALLPERMS; ! 341: if (args.ma_flags & HFSFSMNT_NOXONFILES) ! 342: hfsmp->hfs_file_mask = (args.ma_mask & DEFFILEMODE); ! 343: else ! 344: hfsmp->hfs_file_mask = args.ma_mask & ALLPERMS; ! 345: ! 346: hfsmp->hfs_encoding = args.ma_encoding; ! 347: ! 348: /* Set the mount flag to indicate that we support volfs */ ! 349: mp->mnt_flag |= MNT_DOVOLFS; ! 350: ! 351: ! 352: (void) copyinstr(path, hfsmp->hfs_mountpoint, sizeof(hfsmp->hfs_mountpoint) - 1, &size); ! 353: bzero(hfsmp->hfs_mountpoint + size, sizeof(hfsmp->hfs_mountpoint) - size); ! 354: bcopy((caddr_t)hfsmp->hfs_mountpoint, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN); ! 355: (void) copyinstr(args.ma_fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); ! 356: bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); ! 357: (void)hfs_statfs(mp, &mp->mnt_stat, p); ! 358: retval = E_NONE; ! 359: DBG_VFS(("hfs_mount: SUCCESSFULL MOUNT of %s on %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname)); ! 360: ! 361: goto std_exit; ! 362: ! 363: error_exit: ! 364: /* No special exit-path processing to do */ ! 365: DBG_VFS(("hfs_mount: BAD MOUNT of %s on %s with an error of %d\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname, retval)); ! 366: ! 367: std_exit: ! 368: return (retval); ! 369: ! 370: } ! 371: ! 372: ! 373: /* ! 374: * Reload all incore data for a filesystem (used after running fsck on ! 375: * the root filesystem and finding things to fix). The filesystem must ! 376: * be mounted read-only. ! 377: * ! 378: * Things to do to update the mount: ! 379: * 1) invalidate all cached meta-data. ! 380: * 2) re-read superblock from disk. ! 381: * 3) re-read summary information from disk. ! 382: * 4) invalidate all inactive vnodes. ! 383: * 5) invalidate all cached file data. ! 384: * 6) re-read hfsnode data for all active vnodes. ! 385: */ ! 386: int hfs_reload(mountp, cred, p) ! 387: register struct mount *mountp; ! 388: struct ucred *cred; ! 389: struct proc *p; ! 390: { ! 391: #if 0 /* XXX PPD */ ! 392: register struct vnode *vp, *nvp, *devvp; ! 393: struct hfsnode *hp; ! 394: struct csum *space; ! 395: struct buf *bp; ! 396: struct hfsmount *hfsmp, *newfs; ! 397: struct disk_label *lab; ! 398: int i, blks, size, retval; ! 399: int32_t *lp; ! 400: ! 401: if ((mountp->mnt_flag & MNT_RDONLY) == 0) ! 402: return (EINVAL); ! 403: /* ! 404: * Step 1: invalidate all cached meta-data. ! 405: */ ! 406: devvp = VFSTOUFS(mountp)->um_devvp; ! 407: if (vinvalbuf(devvp, 0, cred, p, 0, 0)) ! 408: panic("ffs_reload: dirty1"); ! 409: /* ! 410: * Step 2: re-read superblock from disk. ! 411: */ ! 412: ! 413: ! 414: if (retval = bread(devvp, (ufs_daddr_t)(SBOFF/size), SBSIZE, NOCRED,&bp)) { ! 415: if (bp) brelse(bp); ! 416: return (retval); ! 417: }; ! 418: ! 419: /* ! 420: * Copy pointer fields back into superblock before copying in XXX ! 421: * new superblock. These should really be in the HFS mount point. XXX ! 422: * Note that important parameters (eg fs_ncg) are unchanged. ! 423: */ ! 424: bcopy(&hfsmp->hfs_fs_csp[0], &newfs->fs_csp[0], sizeof(hfsmp->hfs_fs_csp)); ! 425: newfs->fs_maxcluster = hfsmp->hfs_fs_maxcluster; ! 426: bcopy(newfs, hfsmp, (u_int)hfsmp->hfs_fs_sbsize); ! 427: if (hfsmp->hfs_fs_sbsize < SBSIZE) ! 428: bp->b_flags |= B_INVAL; ! 429: brelse(bp); ! 430: mountp->mnt_maxsymlinklen = 0; ! 431: ffs_oldfscompat(hfsmp); ! 432: /* ! 433: * Step 3: re-read summary information from disk. ! 434: */ ! 435: blks = howmany(hfsmp->hfs_fs_cssize, hfsmp->hfs_fs_fsize); ! 436: space = hfsmp->hfs_fs_csp[0]; ! 437: for (i = 0; i < blks; i += hfsmp->hfs_fs_frag) { ! 438: size = hfsmp->hfs_fs_bsize; ! 439: if (i + hfsmp->hfs_fs_frag > blks) ! 440: size = (blks - i) * hfsmp->hfs_fs_fsize; ! 441: if (retval = bread(devvp, fsbtodb(hfsmp, hfsmp->hfs_fs_csaddr + i), size, NOCRED, &bp)) { ! 442: if (bp) brelse(bp); ! 443: return (retval); ! 444: }; ! 445: bcopy(bp->b_data, hfsmp->hfs_fs_csp[fragstoblks(hfsmp, i)], (u_int)size); ! 446: brelse(bp); ! 447: } ! 448: /* ! 449: * We no longer know anything about clusters per cylinder group. ! 450: */ ! 451: if (hfsmp->hfs_fs_contigsumsize > 0) { ! 452: lp = hfsmp->hfs_fs_maxcluster; ! 453: for (i = 0; i < hfsmp->hfs_fs_ncg; i++) ! 454: *lp++ = hfsmp->hfs_fs_contigsumsize; ! 455: } ! 456: ! 457: loop: ! 458: simple_lock(&mntvnode_slock); ! 459: for (vp = mountp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) { ! 460: if (vp->v_mount != mountp) { ! 461: simple_unlock(&mntvnode_slock); ! 462: goto loop; ! 463: } ! 464: nvp = vp->v_mntvnodes.le_next; ! 465: /* ! 466: * Step 4: invalidate all inactive vnodes. ! 467: */ ! 468: if (vrecycle(vp, &mntvnode_slock, p)) ! 469: goto loop; ! 470: /* ! 471: * Step 5: invalidate all cached file data. ! 472: */ ! 473: simple_lock(&vp->v_interlock); ! 474: simple_unlock(&mntvnode_slock); ! 475: if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p)) { ! 476: goto loop; ! 477: } ! 478: if (vinvalbuf(vp, 0, cred, p, 0, 0)) ! 479: panic("hfs_reload: dirty2"); ! 480: /* ! 481: * Step 6: re-read hfsnode data for all active vnodes. ! 482: */ ! 483: hp = VTOI(vp); ! 484: if ((retval = bread(devvp, fsbtodb(hfsmp, ino_to_fsba(hfsmp, hp->h_i_number)), ! 485: (int)hfsmp->hfs_fs_bsize, NOCRED, &bp))) { ! 486: VPUT(vp); ! 487: if (bp) brelse(bp); ! 488: return (retval); ! 489: } ! 490: hp->h_i_din = *((struct dinode *)bp->b_data + ! 491: ino_to_fsbo(hfsmp, hp->h_i_number)); ! 492: brelse(bp); ! 493: VPUT(vp); ! 494: simple_lock(&mntvnode_slock); ! 495: } ! 496: simple_unlock(&mntvnode_slock); ! 497: #endif ! 498: return (0); ! 499: } ! 500: ! 501: /* ! 502: * Common code for mount and mountroot ! 503: */ ! 504: int ! 505: hfs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p) ! 506: { ! 507: int retval = E_NONE; ! 508: register struct hfsmount *hfsmp; ! 509: struct buf *bp; ! 510: dev_t dev; ! 511: HFSMasterDirectoryBlock *mdbp; ! 512: int size, ronly; ! 513: struct ucred *cred; ! 514: ! 515: DBG_VFS(("hfs_mountfs: mp = 0x%lX\n", (u_long)mp)); ! 516: ! 517: dev = devvp->v_rdev; ! 518: cred = p ? p->p_ucred : NOCRED; ! 519: /* ! 520: * Disallow multiple mounts of the same device. ! 521: * Disallow mounting of a device that is currently in use ! 522: * (except for root, which might share swap device for miniroot). ! 523: * Flush out any old buffers remaining from a previous use. ! 524: */ ! 525: if ((retval = vfs_mountedon(devvp))) ! 526: return (retval); ! 527: if (vcount(devvp) > 1 && devvp != rootvp) ! 528: return (EBUSY); ! 529: if ((retval = vinvalbuf(devvp, V_SAVE, cred, p, 0, 0))) ! 530: return (retval); ! 531: ! 532: ronly = (mp->mnt_flag & MNT_RDONLY) != 0; ! 533: DBG_VFS(("hfs_mountfs: opening device...\n")); ! 534: if ((retval = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p))) ! 535: return (retval); ! 536: ! 537: size = 512; ! 538: DBG_VFS(("hfs_mountfs: size = %d (DEV_BSIZE = %d).\n", size, DEV_BSIZE)); ! 539: ! 540: bp = NULL; ! 541: hfsmp = NULL; ! 542: DBG_VFS(("hfs_mountfs: reading MDB [block no. %d + %d bytes, size %d bytes]...\n", ! 543: IOBLKNOFORBLK(kMasterDirectoryBlock, size), ! 544: IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, size), ! 545: IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size))); ! 546: ! 547: if ((retval = bread(devvp, IOBLKNOFORBLK(kMasterDirectoryBlock, size), ! 548: IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size), cred, &bp))) { ! 549: if (bp) brelse(bp); ! 550: goto error_exit; ! 551: }; ! 552: mdbp = (HFSMasterDirectoryBlock*) ((char *)bp->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, size)); ! 553: ! 554: DBG_VFS(("hfs_mountfs: allocating hfsmount structure...\n")); ! 555: MALLOC(hfsmp, struct hfsmount *, sizeof(struct hfsmount), M_HFSMNT, M_WAITOK); ! 556: bzero(hfsmp, sizeof(struct hfsmount)); ! 557: ! 558: DBG_VFS(("hfs_mountfs: Initializing hfsmount structure at 0x%lX...\n", (u_long)hfsmp)); ! 559: /* ! 560: * Init the volume information structure ! 561: */ ! 562: mp->mnt_data = (qaddr_t)hfsmp; ! 563: hfsmp->hfs_mp = mp; /* Make VFSTOHFS work */ ! 564: hfsmp->hfs_vcb.vcb_hfsmp = hfsmp; /* Make VCBTOHFS work */ ! 565: hfsmp->hfs_raw_dev = devvp->v_rdev; ! 566: hfsmp->hfs_devvp = devvp; ! 567: hfsmp->hfs_phys_block_size = size; ! 568: /* The hfs_log_block_size field is updated in the respective hfs_MountHFS[Plus]Volume routine */ ! 569: hfsmp->hfs_log_block_size = BestBlockSizeFit(mdbp->drAlBlkSiz, MAXBSIZE, hfsmp->hfs_phys_block_size); ! 570: hfsmp->hfs_fs_ronly = ronly; ! 571: ! 572: if (mdbp->drSigWord == kHFSPlusSigWord) { ! 573: DBG_VFS(("hfs_mountfs: mounting wrapper-less HFS-Plus volume...\n")); ! 574: retval = hfs_MountHFSPlusVolume(hfsmp, (HFSPlusVolumeHeader*) bp->b_data, 0, p); ! 575: } ! 576: else if (mdbp->drEmbedSigWord == kHFSPlusSigWord) { ! 577: u_long embBlkOffset; ! 578: HFSPlusVolumeHeader *vhp; ! 579: ! 580: embBlkOffset = (mdbp->drEmbedExtent.startBlock * (mdbp->drAlBlkSiz)/kHFSBlockSize) + mdbp->drAlBlSt; ! 581: DBG_VFS(("hfs_mountfs: mounting embedded HFS-Plus volume at sector %ld...\n", embBlkOffset)); ! 582: brelse(bp); ! 583: bp = NULL; /* done with MDB, go grab Volume Header */ ! 584: mdbp = NULL; ! 585: ! 586: retval = bread( devvp, ! 587: IOBLKNOFORBLK(kMasterDirectoryBlock+embBlkOffset, size), ! 588: IOBYTECCNTFORBLK(kMasterDirectoryBlock+embBlkOffset, kMDBSize, size), ! 589: cred, ! 590: &bp); ! 591: if (retval) { ! 592: if (bp) brelse(bp); ! 593: goto error_exit; ! 594: }; ! 595: vhp = (HFSPlusVolumeHeader*) ((char *)bp->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, size)); ! 596: ! 597: retval = hfs_MountHFSPlusVolume(hfsmp, vhp, embBlkOffset, p); ! 598: } ! 599: else { // pass all other volume formats to MountHFSVolume ! 600: DBG_VFS(("hfs_mountfs: mounting regular HFS volume...\n")); ! 601: retval = hfs_MountHFSVolume( hfsmp, mdbp, p); ! 602: } ! 603: ! 604: if ( retval ) { ! 605: goto error_exit; ! 606: } ! 607: ! 608: brelse(bp); ! 609: bp = NULL; ! 610: ! 611: if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord) ! 612: mp->mnt_stat.f_fsid.val[0] = (long)dev; ! 613: else ! 614: mp->mnt_stat.f_fsid.val[0] = (long)dev | HFSFSMNT_HFSSTDMASK; ! 615: mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; ! 616: mp->mnt_maxsymlinklen = 0; ! 617: devvp->v_specflags |= SI_MOUNTEDON; ! 618: ! 619: if (ronly == 0) { ! 620: hfsmp->hfs_fs_clean = 0; ! 621: if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord) ! 622: (void) hfs_flushvolumeheader(hfsmp, MNT_WAIT); ! 623: else ! 624: (void) hfs_flushMDB(hfsmp, MNT_WAIT); ! 625: } ! 626: goto std_exit; ! 627: ! 628: error_exit: ! 629: DBG_VFS(("hfs_mountfs: exiting with error %d...\n", retval)); ! 630: ! 631: if (bp) ! 632: brelse(bp); ! 633: (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p); ! 634: if (hfsmp) { ! 635: FREE(hfsmp, M_HFSMNT); ! 636: mp->mnt_data = (qaddr_t)0; ! 637: } ! 638: ! 639: std_exit: ! 640: return (retval); ! 641: } ! 642: ! 643: ! 644: /* ! 645: * Make a filesystem operational. ! 646: * Nothing to do at the moment. ! 647: */ ! 648: /* ARGSUSED */ ! 649: int hfs_start(mp, flags, p) ! 650: struct mount *mp; ! 651: int flags; ! 652: struct proc *p; ! 653: { ! 654: DBG_FUNC_NAME("hfs_start"); ! 655: DBG_PRINT_FUNC_NAME(); ! 656: ! 657: return (0); ! 658: } ! 659: ! 660: ! 661: /* ! 662: * unmount system call ! 663: */ ! 664: int hfs_unmount(mp, mntflags, p) ! 665: struct mount *mp; ! 666: int mntflags; ! 667: struct proc *p; ! 668: { ! 669: struct hfsmount *hfsmp = VFSTOHFS(mp); ! 670: int retval = E_NONE; ! 671: int uflags; /* vflush flag for user vnodes pass */ ! 672: ! 673: DBG_VFS(("hfs_unmount: volume: 0x%x\n", (uint)hfsmp)); ! 674: uflags = SKIPSYSTEM; ! 675: if (mntflags & MNT_FORCE) ! 676: uflags |= FORCECLOSE; ! 677: ! 678: /* ! 679: * Remove any user vnodes (non-system) ! 680: */ ! 681: retval = vflush(mp, NULL, uflags); ! 682: ! 683: /* Run it again, if we were busy...there might be complex nodes left */ ! 684: if (retval == EBUSY) ! 685: retval = vflush(mp, NULL, uflags); ! 686: ! 687: if (retval != E_NONE) ! 688: return (retval); ! 689: ! 690: /* ! 691: * Flush out the volume bitmap and MDB/Volume Header ! 692: */ ! 693: if (hfsmp->hfs_fs_ronly == 0) { ! 694: hfsmp->hfs_fs_clean = 1; ! 695: HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeUnmountedMask; ! 696: if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord) ! 697: retval = hfs_flushvolumeheader(hfsmp, MNT_WAIT); ! 698: else ! 699: retval = hfs_flushMDB(hfsmp, MNT_WAIT); ! 700: ! 701: if (retval == 0) ! 702: retval = VOP_FSYNC(hfsmp->hfs_devvp, NOCRED, MNT_WAIT, p); ! 703: ! 704: if (retval) { ! 705: hfsmp->hfs_fs_clean = 0; ! 706: if ((mntflags & MNT_FORCE) == 0) ! 707: return (retval); /* could not flush everything */ ! 708: } ! 709: } ! 710: ! 711: /* ! 712: * Invalidate our caches and release metadata vnodes ! 713: */ ! 714: retval = hfsUnmount(hfsmp, p); ! 715: if(retval != E_NONE) { ! 716: DBG_ERR(("hfs_unmount: hfsUnmount FAILED: %d\n", retval)); ! 717: } ! 718: ! 719: /* ! 720: * Remove the metadata vnodes (they should not be busy) ! 721: */ ! 722: retval = vflush(mp, NULL, 0); ! 723: ASSERT(retval != EBUSY); ! 724: if (retval != E_NONE) ! 725: return (retval); /* XXX leaving now would be bad - we're in an inconsistent state! */ ! 726: ! 727: hfsmp->hfs_devvp->v_specflags &= ~SI_MOUNTEDON; ! 728: ! 729: retval = VOP_CLOSE(hfsmp->hfs_devvp, hfsmp->hfs_fs_ronly ? FREAD : FREAD|FWRITE, ! 730: NOCRED, p); ! 731: VRELE(hfsmp->hfs_devvp); ! 732: ! 733: FREE(hfsmp, M_HFSMNT); ! 734: mp->mnt_data = (qaddr_t)0; ! 735: ! 736: DBG_VFS(("hfs_unmount: Finished, returning %d\n", retval)); ! 737: ! 738: return(retval); ! 739: } ! 740: ! 741: ! 742: /* ! 743: * Return the root of a filesystem. ! 744: * ! 745: * OUT - vpp, should be locked and vget()'d (to increment usecount and lock) ! 746: */ ! 747: int hfs_root(mp, vpp) ! 748: struct mount *mp; ! 749: struct vnode **vpp; ! 750: { ! 751: struct vnode *nvp; ! 752: int retval; ! 753: UInt32 rootObjID = kRootDirID; ! 754: ! 755: DBG_FUNC_NAME("hfs_root"); ! 756: DBG_PRINT_FUNC_NAME(); ! 757: ! 758: if ((retval = VFS_VGET(mp, &rootObjID, &nvp))) ! 759: return (retval); ! 760: ! 761: *vpp = nvp; ! 762: return (0); ! 763: } ! 764: ! 765: /* ! 766: * Do operations associated with quotas ! 767: */ ! 768: int hfs_quotactl(mp, cmds, uid, arg, p) ! 769: struct mount *mp; ! 770: int cmds; ! 771: uid_t uid; ! 772: caddr_t arg; ! 773: struct proc *p; ! 774: { ! 775: DBG_FUNC_NAME("hfs_quotactl"); ! 776: DBG_PRINT_FUNC_NAME(); ! 777: ! 778: return (EOPNOTSUPP); ! 779: } ! 780: ! 781: ! 782: ! 783: /* ! 784: * Get file system statistics. ! 785: */ ! 786: static int hfs_statfs(mp, sbp, p) ! 787: struct mount *mp; ! 788: register struct statfs *sbp; ! 789: struct proc *p; ! 790: { ! 791: ExtendedVCB *vcb = VFSTOVCB(mp); ! 792: struct hfsmount *hfsmp = VFSTOHFS(mp); ! 793: u_long freeCNIDs; ! 794: ! 795: DBG_FUNC_NAME("hfs_statfs"); ! 796: DBG_PRINT_FUNC_NAME(); ! 797: ! 798: freeCNIDs = (u_long)0xFFFFFFFF - (u_long)vcb->vcbNxtCNID; ! 799: ! 800: sbp->f_bsize = vcb->blockSize; ! 801: sbp->f_iosize = hfsmp->hfs_log_block_size; ! 802: sbp->f_blocks = vcb->totalBlocks; ! 803: sbp->f_bfree = vcb->freeBlocks; ! 804: sbp->f_bavail = vcb->freeBlocks; ! 805: sbp->f_files = (u_long)vcb->vcbFilCnt + (u_long)vcb->vcbDirCnt; ! 806: sbp->f_ffree = MIN(freeCNIDs, vcb->freeBlocks); ! 807: ! 808: sbp->f_type = 0; ! 809: if (sbp != &mp->mnt_stat) { ! 810: sbp->f_type = mp->mnt_vfc->vfc_typenum; ! 811: bcopy((caddr_t)mp->mnt_stat.f_mntonname, ! 812: (caddr_t)&sbp->f_mntonname[0], MNAMELEN); ! 813: bcopy((caddr_t)mp->mnt_stat.f_mntfromname, ! 814: (caddr_t)&sbp->f_mntfromname[0], MNAMELEN); ! 815: } ! 816: return (0); ! 817: } ! 818: ! 819: ! 820: /* ! 821: * Go through the disk queues to initiate sandbagged IO; ! 822: * go through the inodes to write those that have been modified; ! 823: * initiate the writing of the super block if it has been modified. ! 824: * ! 825: * Note: we are always called with the filesystem marked `MPBUSY'. ! 826: */ ! 827: static int hfs_sync(mp, waitfor, cred, p) ! 828: struct mount *mp; ! 829: int waitfor; ! 830: struct ucred *cred; ! 831: struct proc *p; ! 832: { ! 833: struct vnode *nvp, *vp, *btvp; ! 834: struct hfsnode *hp; ! 835: struct hfsmount *hfsmp = VFSTOHFS(mp); ! 836: ExtendedVCB *vcb; ! 837: int error, allerror = 0; ! 838: ! 839: DBG_FUNC_NAME("hfs_sync"); ! 840: DBG_PRINT_FUNC_NAME(); ! 841: ! 842: hfsmp = VFSTOHFS(mp); ! 843: if (hfsmp->hfs_fs_ronly != 0) { ! 844: panic("update: rofs mod"); ! 845: }; ! 846: ! 847: /* ! 848: * Write back each 'modified' vnode ! 849: */ ! 850: btvp = 0; ! 851: simple_lock(&mntvnode_slock); ! 852: ! 853: loop:; ! 854: ! 855: for (vp = mp->mnt_vnodelist.lh_first; ! 856: vp != NULL; ! 857: vp = nvp) { ! 858: /* ! 859: * If the vnode that we are about to sync is no longer ! 860: * associated with this mount point, start over. ! 861: */ ! 862: if (vp->v_mount != mp) ! 863: goto loop; ! 864: simple_lock(&vp->v_interlock); ! 865: nvp = vp->v_mntvnodes.le_next; ! 866: hp = VTOH(vp); ! 867: if (H_FILEID(hp) == 3) ! 868: btvp = vp; ! 869: if ((hp->h_meta->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 && ! 870: vp->v_dirtyblkhd.lh_first == NULL) { ! 871: simple_unlock(&vp->v_interlock); ! 872: continue; ! 873: } ! 874: ! 875: simple_unlock(&mntvnode_slock); ! 876: error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p); ! 877: DBG_ASSERT(*((int*)&vp->v_interlock) == 0); ! 878: if (error) { ! 879: DBG_ERR(("hfs_sync: error %d on vget for vnode 0x%lX.\n", error, (u_long)vp)); ! 880: simple_lock(&mntvnode_slock); ! 881: if (error == ENOENT) ! 882: goto loop; ! 883: continue; ! 884: } ! 885: ! 886: if ((error = VOP_FSYNC(vp, cred, waitfor, p))) { ! 887: DBG_ERR(("hfs_sync: error %d calling fsync on vnode 0x%X.\n", error, (u_int)vp)); ! 888: allerror = error; ! 889: }; ! 890: DBG_ASSERT(*((volatile int *)(&(vp)->v_interlock))==0); ! 891: VPUT(vp); ! 892: simple_lock(&mntvnode_slock); ! 893: }; ! 894: ! 895: /* Now reprocess the BTree node, stored above */ ! 896: { ! 897: /* ! 898: * If the vnode that we are about to sync is no longer ! 899: * associated with this mount point, start over. ! 900: */ ! 901: if ((btvp==0) || (btvp->v_mount != mp)) ! 902: goto skipBtree; ! 903: simple_lock(&btvp->v_interlock); ! 904: hp = VTOH(btvp); ! 905: if ((hp->h_meta->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 && ! 906: btvp->v_dirtyblkhd.lh_first == NULL) { ! 907: simple_unlock(&btvp->v_interlock); ! 908: goto skipBtree; ! 909: } ! 910: simple_unlock(&mntvnode_slock); ! 911: error = vget(btvp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p); ! 912: if (error) { ! 913: simple_lock(&mntvnode_slock); ! 914: goto skipBtree; ! 915: } ! 916: if ((error = VOP_FSYNC(btvp, cred, waitfor, p))) ! 917: allerror = error; ! 918: VOP_UNLOCK(btvp, 0, p); ! 919: VRELE(btvp); ! 920: simple_lock(&mntvnode_slock); ! 921: }; ! 922: ! 923: skipBtree:; ! 924: ! 925: simple_unlock(&mntvnode_slock); ! 926: ! 927: /* ! 928: * Force stale file system control information to be flushed. ! 929: */ ! 930: if ((error = VOP_FSYNC(hfsmp->hfs_devvp, cred, waitfor, p))) ! 931: allerror = error; ! 932: /* ! 933: * Write back modified superblock. ! 934: */ ! 935: vcb = HFSTOVCB(hfsmp); ! 936: ! 937: if (IsVCBDirty(vcb)) { ! 938: if (vcb->vcbSigWord == kHFSPlusSigWord) ! 939: error = hfs_flushvolumeheader(hfsmp, waitfor); ! 940: else ! 941: error = hfs_flushMDB(hfsmp, waitfor); ! 942: ! 943: if (error) ! 944: allerror = error; ! 945: }; ! 946: ! 947: return (allerror); ! 948: } ! 949: ! 950: ! 951: /* ! 952: * File handle to vnode ! 953: * ! 954: * Have to be really careful about stale file handles: ! 955: * - check that the hfsnode number is valid ! 956: * - call hfs_vget() to get the locked hfsnode ! 957: * - check for an unallocated hfsnode (i_mode == 0) ! 958: * - check that the given client host has export rights and return ! 959: * those rights via. exflagsp and credanonp ! 960: */ ! 961: int ! 962: hfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) ! 963: register struct mount *mp; ! 964: struct fid *fhp; ! 965: struct mbuf *nam; ! 966: struct vnode **vpp; ! 967: int *exflagsp; ! 968: struct ucred **credanonp; ! 969: { ! 970: DBG_FUNC_NAME("hfs_fhtovp"); ! 971: DBG_PRINT_FUNC_NAME(); ! 972: ! 973: return (EOPNOTSUPP); ! 974: } ! 975: ! 976: /* ! 977: * Vnode pointer to File handle ! 978: */ ! 979: /* ARGSUSED */ ! 980: static int hfs_vptofh(vp, fhp) ! 981: struct vnode *vp; ! 982: struct fid *fhp; ! 983: { ! 984: DBG_FUNC_NAME("hfs_vptofh"); ! 985: DBG_PRINT_FUNC_NAME(); ! 986: ! 987: return (EOPNOTSUPP); ! 988: } ! 989: ! 990: ! 991: ! 992: /* ! 993: * Initial HFS filesystems, done only once. ! 994: */ ! 995: int ! 996: hfs_init(vfsp) ! 997: struct vfsconf *vfsp; ! 998: { ! 999: int i; ! 1000: static int done = 0; ! 1001: OSErr err; ! 1002: ! 1003: DBG_FUNC_NAME("hfs_init"); ! 1004: DBG_PRINT_FUNC_NAME(); ! 1005: ! 1006: if (done) ! 1007: return (0); ! 1008: done = 1; ! 1009: hfs_vhashinit(); ! 1010: ! 1011: simple_lock_init (&gBufferPtrListLock); ! 1012: ! 1013: for (i = BUFFERPTRLISTSIZE - 1; i >= 0; --i) { ! 1014: gBufferAddress[i] = NULL; ! 1015: gBufferHeaderPtr[i] = NULL; ! 1016: }; ! 1017: gBufferListIndex = 0; ! 1018: ! 1019: /* ! 1020: * Do any initialization that the MacOS/Rhapsody shared code relies on ! 1021: * (normally done as part of MacOS's startup): ! 1022: */ ! 1023: MALLOC(gFSMVars, FSVarsRec *, sizeof(FSVarsRec), M_HFSMNT, M_WAITOK); ! 1024: bzero(gFSMVars, sizeof(FSVarsRec)); ! 1025: ! 1026: /* ! 1027: * Allocate Catalog Iterator cache... ! 1028: */ ! 1029: err = InitCatalogCache(); ! 1030: #if DIAGNOSTIC ! 1031: if (err) PANIC("hfs_init: Error returned from InitCatalogCache() call."); ! 1032: #endif ! 1033: /* ! 1034: * XXX do we need to setup the following? ! 1035: * ! 1036: * GMT offset, Unicode globals, CatSearch Buffers, BTSscanner ! 1037: */ ! 1038: ! 1039: return E_NONE; ! 1040: } ! 1041: ! 1042: /* ! 1043: * fast filesystem related variables. ! 1044: */ ! 1045: static int hfs_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) ! 1046: int *name; ! 1047: u_int namelen; ! 1048: void *oldp; ! 1049: size_t *oldlenp; ! 1050: void *newp; ! 1051: size_t newlen; ! 1052: struct proc *p; ! 1053: { ! 1054: DBG_FUNC_NAME("hfs_sysctl"); ! 1055: DBG_PRINT_FUNC_NAME(); ! 1056: ! 1057: return (EOPNOTSUPP); ! 1058: } ! 1059: ! 1060: /* This will return a vnode or either a directory or a data vnode based on an object id. If ! 1061: * it is a file id, its data fork will be returned. ! 1062: */ ! 1063: ! 1064: int hfs_vget(struct mount *mp, ! 1065: void *ino, ! 1066: struct vnode **vpp) ! 1067: { ! 1068: struct hfsmount *hfsmp; ! 1069: struct proc *p; ! 1070: dev_t dev; ! 1071: hfsCatalogInfo catInfo; ! 1072: UInt8 forkType; ! 1073: int retval = E_NONE; ! 1074: ! 1075: DBG_VFS(("hfs_vget: ino = %ld\n", *(UInt32 *)ino)); ! 1076: ! 1077: hfsmp = VFSTOHFS(mp); ! 1078: p = CURRENT_PROC; ! 1079: dev = hfsmp->hfs_raw_dev; ! 1080: ! 1081: /* First check to see if it is in the cache */ ! 1082: /* If it is, and it is complex, check again if its data fork is there */ ! 1083: *vpp = hfs_vhashget(dev, *(UInt32 *)ino, kDirCmplx); ! 1084: if (*vpp && (*vpp)->v_type == VCPLX) { ! 1085: vput(*vpp); ! 1086: *vpp = hfs_vhashget(dev, *(UInt32 *)ino, kDataFork); ! 1087: } ! 1088: ! 1089: /* The vnode is not in the cache, so lets make it */ ! 1090: if (*vpp == NULL) ! 1091: { ! 1092: /* lock catalog b-tree */ ! 1093: retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p); ! 1094: if (retval != E_NONE) goto Err_Exit; ! 1095: ! 1096: retval = hfsLookup(VFSTOVCB(mp), *(UInt32 *)ino, NULL, -1, 0, &catInfo); ! 1097: ! 1098: /* unlock catalog b-tree */ ! 1099: (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p); ! 1100: ! 1101: if (retval != E_NONE) goto Err_Exit; ! 1102: ! 1103: forkType = (catInfo.nodeData.nodeType == kCatalogFolderNode) ? kDirCmplx : kDataFork; ! 1104: retval = hfsGet(VFSTOVCB(mp), &catInfo, forkType, NULL, vpp); ! 1105: } ! 1106: ! 1107: #if MACH_NBC ! 1108: if (*vpp && ((*vpp)->v_type == VREG) && !((*vpp)->v_vm_info)){ ! 1109: vm_info_init(*vpp); ! 1110: } ! 1111: #endif /* MACH_NBC */ ! 1112: ! 1113: Err_Exit: ! 1114: ! 1115: if (retval != E_NONE) { ! 1116: DBG_VFS(("hfs_vget: Error returned of %d\n", retval)); ! 1117: } ! 1118: else { ! 1119: DBG_VFS(("hfs_vget: vp = 0x%x\n", (u_int)*vpp)); ! 1120: } ! 1121: ! 1122: return (retval); ! 1123: ! 1124: } ! 1125: ! 1126: short hfs_flushfiles(struct mount *mp, unsigned short flags) ! 1127: { ! 1128: int retval; ! 1129: ! 1130: DBG_FUNC_NAME("hfs_flushfiles"); ! 1131: DBG_PRINT_FUNC_NAME(); ! 1132: ! 1133: retval = vflush(mp, VFSTOVCB(mp)->catalogRefNum, flags); ! 1134: if (retval) return retval; ! 1135: DBG_VFS(("\tAll files except Catalog is flushed\n")); ! 1136: ! 1137: /* Now 'close' the catalog Btree, and then flush it */ ! 1138: VRELE(VFSTOVCB(mp)->catalogRefNum); ! 1139: retval = vflush(mp, NULL, flags); ! 1140: DBG_VFS(("\tCatalog is now flushed\n")); ! 1141: return (retval); ! 1142: } ! 1143: ! 1144: ! 1145: short hfs_flushMDB(struct hfsmount *hfsmp, int waitfor) ! 1146: { ! 1147: ExtendedVCB *vcb = HFSTOVCB(hfsmp); ! 1148: FCB *fcb; ! 1149: HFSMasterDirectoryBlock *mdb; ! 1150: struct buf *bp; ! 1151: int retval; ! 1152: int size = kMDBSize; /* 512 */ ! 1153: size_t namelen; ! 1154: ! 1155: if (vcb->vcbSigWord != kHFSSigWord) ! 1156: return EINVAL; ! 1157: ! 1158: ASSERT(hfsmp->hfs_devvp != NULL); ! 1159: ! 1160: retval = bread(hfsmp->hfs_devvp, IOBLKNOFORBLK(kMasterDirectoryBlock, size), ! 1161: IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size), NOCRED, &bp); ! 1162: if (retval) { ! 1163: DBG_VFS((" hfs_flushMDB bread return error! (%d)\n", retval)); ! 1164: if (bp) brelse(bp); ! 1165: return retval; ! 1166: } ! 1167: ! 1168: ASSERT(bp != NULL); ! 1169: ASSERT(bp->b_data != NULL); ! 1170: ASSERT(bp->b_bcount == size); ! 1171: ! 1172: mdb = (HFSMasterDirectoryBlock *)((char *)bp->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, size)); ! 1173: ! 1174: VCB_LOCK(vcb); ! 1175: mdb->drCrDate = vcb->vcbCrDate; ! 1176: mdb->drLsMod = vcb->vcbLsMod; ! 1177: mdb->drAtrb = vcb->vcbAtrb; ! 1178: mdb->drNmFls = vcb->vcbNmFls; ! 1179: mdb->drAllocPtr = vcb->nextAllocation; ! 1180: mdb->drClpSiz = vcb->vcbClpSiz; ! 1181: mdb->drNxtCNID = vcb->vcbNxtCNID; ! 1182: mdb->drFreeBks = vcb->freeBlocks; ! 1183: ! 1184: copystr(vcb->vcbVN, &mdb->drVN[1], sizeof(mdb->drVN)-1, &namelen); ! 1185: mdb->drVN[0] = namelen-1; ! 1186: mdb->drVN[namelen] = '\0'; ! 1187: ! 1188: mdb->drVolBkUp = vcb->vcbVolBkUp; ! 1189: mdb->drVSeqNum = vcb->vcbVSeqNum; ! 1190: mdb->drWrCnt = vcb->vcbWrCnt; ! 1191: mdb->drNmRtDirs = vcb->vcbNmRtDirs; ! 1192: mdb->drFilCnt = vcb->vcbFilCnt; ! 1193: mdb->drDirCnt = vcb->vcbDirCnt; ! 1194: ! 1195: bcopy(vcb->vcbFndrInfo, mdb->drFndrInfo, sizeof(mdb->drFndrInfo)); ! 1196: ! 1197: fcb = VTOFCB(vcb->extentsRefNum); ! 1198: bcopy(fcb->fcbExtRec, mdb->drXTExtRec, sizeof(HFSExtentRecord)); ! 1199: mdb->drXTFlSize = fcb->fcbPLen; ! 1200: mdb->drXTClpSiz = fcb->fcbClmpSize; ! 1201: ! 1202: fcb = VTOFCB(vcb->catalogRefNum); ! 1203: bcopy(fcb->fcbExtRec, mdb->drCTExtRec, sizeof(HFSExtentRecord)); ! 1204: mdb->drCTFlSize = fcb->fcbPLen; ! 1205: mdb->drCTClpSiz = fcb->fcbClmpSize; ! 1206: VCB_UNLOCK(vcb); ! 1207: ! 1208: ! 1209: if (waitfor != MNT_WAIT) ! 1210: bawrite(bp); ! 1211: else ! 1212: retval = bwrite(bp); ! 1213: ! 1214: MarkVCBClean( vcb ); ! 1215: ! 1216: return (retval); ! 1217: } ! 1218: ! 1219: ! 1220: short hfs_flushvolumeheader(struct hfsmount *hfsmp, int waitfor) ! 1221: { ! 1222: ExtendedVCB *vcb = HFSTOVCB(hfsmp); ! 1223: ExtendedFCB *extendedFCB; ! 1224: FCB *fcb; ! 1225: HFSPlusVolumeHeader *volumeHeader; ! 1226: struct vnode *vp; ! 1227: int retval; ! 1228: int size = sizeof(HFSPlusVolumeHeader); ! 1229: struct buf *bp; ! 1230: ! 1231: if (vcb->vcbSigWord != kHFSPlusSigWord) ! 1232: return EINVAL; ! 1233: ! 1234: retval = bread(hfsmp->hfs_devvp, IOBLKNOFORBLK((vcb->hfsPlusIOPosOffset / 512) + kMasterDirectoryBlock, size), ! 1235: IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size), NOCRED, &bp); ! 1236: if (retval) { ! 1237: DBG_VFS((" hfs_flushvolumeheader bread return error! (%d)\n", retval)); ! 1238: if (bp) brelse(bp); ! 1239: return retval; ! 1240: } ! 1241: ! 1242: ASSERT(bp != NULL); ! 1243: ASSERT(bp->b_data != NULL); ! 1244: ASSERT(bp->b_bcount == size); ! 1245: ! 1246: volumeHeader = (HFSPlusVolumeHeader *)((char *)bp->b_data + ! 1247: IOBYTEOFFSETFORBLK((vcb->hfsPlusIOPosOffset / 512) + kMasterDirectoryBlock, size)); ! 1248: ! 1249: /* ! 1250: * For embedded HFS+ volumes, update create date if neccessary ! 1251: */ ! 1252: if (vcb->hfsPlusIOPosOffset != 0 && volumeHeader->createDate != vcb->vcbCrDate) ! 1253: { ! 1254: struct buf *bp2; ! 1255: HFSMasterDirectoryBlock *mdb; ! 1256: ! 1257: retval = bread(hfsmp->hfs_devvp, IOBLKNOFORBLK(kMasterDirectoryBlock, kMDBSize), ! 1258: IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, kMDBSize), NOCRED, &bp2); ! 1259: if (retval != E_NONE) { ! 1260: if (bp2) brelse(bp2); ! 1261: } else { ! 1262: mdb = (HFSMasterDirectoryBlock *)((char *)bp2->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, kMDBSize)); ! 1263: if ( mdb->drCrDate != vcb->vcbCrDate ) ! 1264: { ! 1265: mdb->drCrDate = vcb->vcbCrDate; /* ppick up the new create date */ ! 1266: (void) bwrite(bp2); /* write out the changes */ ! 1267: } ! 1268: else ! 1269: { ! 1270: brelse(bp2); /* just release it */ ! 1271: } ! 1272: } ! 1273: } ! 1274: ! 1275: VCB_LOCK(vcb); ! 1276: /* Note: only update the lower 16 bits worth of attributes */ ! 1277: volumeHeader->attributes = (volumeHeader->attributes & 0xFFFF0000) + (UInt16) vcb->vcbAtrb; ! 1278: volumeHeader->lastMountedVersion = kHFSPlusMountVersion; ! 1279: volumeHeader->createDate = vcb->vcbCrDate; ! 1280: volumeHeader->modifyDate = LocalToUTC(vcb->vcbLsMod); ! 1281: volumeHeader->backupDate = LocalToUTC(vcb->vcbVolBkUp); ! 1282: volumeHeader->checkedDate = vcb->checkedDate; ! 1283: volumeHeader->fileCount = vcb->vcbFilCnt; ! 1284: volumeHeader->folderCount = vcb->vcbDirCnt; ! 1285: // volumeHeader->blockSize = vcb->blockSize; ! 1286: // volumeHeader->totalBlocks = vcb->totalBlocks; ! 1287: volumeHeader->freeBlocks = vcb->freeBlocks; ! 1288: volumeHeader->nextAllocation = vcb->nextAllocation; ! 1289: volumeHeader->rsrcClumpSize = vcb->vcbClpSiz; ! 1290: volumeHeader->dataClumpSize = vcb->vcbClpSiz; ! 1291: volumeHeader->nextCatalogID = vcb->vcbNxtCNID; ! 1292: volumeHeader->writeCount = vcb->vcbWrCnt; ! 1293: volumeHeader->encodingsBitmap = vcb->encodingsBitmap; ! 1294: ! 1295: //XXX djb should we use the vcb or fcb clumpSize values? ! 1296: volumeHeader->allocationFile.clumpSize = vcb->allocationsClumpSize; ! 1297: volumeHeader->extentsFile.clumpSize = vcb->vcbXTClpSiz; ! 1298: volumeHeader->catalogFile.clumpSize = vcb->vcbCTClpSiz; ! 1299: ! 1300: bcopy( vcb->vcbFndrInfo, volumeHeader->finderInfo, sizeof(volumeHeader->finderInfo) ); ! 1301: ! 1302: VCB_UNLOCK(vcb); ! 1303: ! 1304: vp = vcb->extentsRefNum; ! 1305: extendedFCB = GetParallelFCB(vp); ! 1306: bcopy( extendedFCB->extents, volumeHeader->extentsFile.extents, sizeof(HFSPlusExtentRecord) ); ! 1307: fcb = VTOFCB(vp); ! 1308: volumeHeader->extentsFile.logicalSize.lo = fcb->fcbEOF; ! 1309: volumeHeader->extentsFile.logicalSize.hi = 0; ! 1310: volumeHeader->extentsFile.totalBlocks = fcb->fcbPLen / vcb->blockSize; ! 1311: ! 1312: vp = vcb->catalogRefNum; ! 1313: extendedFCB = GetParallelFCB(vp); ! 1314: bcopy( extendedFCB->extents, volumeHeader->catalogFile.extents, sizeof(HFSPlusExtentRecord) ); ! 1315: fcb = VTOFCB(vp); ! 1316: volumeHeader->catalogFile.logicalSize.lo = fcb->fcbPLen; ! 1317: volumeHeader->catalogFile.logicalSize.hi = 0; ! 1318: volumeHeader->catalogFile.totalBlocks = fcb->fcbPLen / vcb->blockSize; ! 1319: ! 1320: vp = vcb->allocationsRefNum; ! 1321: extendedFCB = GetParallelFCB(vp); ! 1322: bcopy( extendedFCB->extents, volumeHeader->allocationFile.extents, sizeof(HFSPlusExtentRecord) ); ! 1323: fcb = VTOFCB(vp); ! 1324: volumeHeader->allocationFile.logicalSize.lo = fcb->fcbPLen; ! 1325: volumeHeader->allocationFile.logicalSize.hi = 0; ! 1326: volumeHeader->allocationFile.totalBlocks = fcb->fcbPLen / vcb->blockSize; ! 1327: ! 1328: vp = vcb->attributesRefNum; ! 1329: if (vp != 0) // Only update fields if an attributes file existed and was open ! 1330: { ! 1331: extendedFCB = GetParallelFCB(vp); ! 1332: bcopy( extendedFCB->extents, volumeHeader->attributesFile.extents, sizeof(HFSPlusExtentRecord) ); ! 1333: fcb = VTOFCB(vp); ! 1334: volumeHeader->attributesFile.logicalSize.lo = fcb->fcbPLen; ! 1335: volumeHeader->attributesFile.logicalSize.hi = 0; ! 1336: volumeHeader->attributesFile.clumpSize = fcb->fcbClmpSize; ! 1337: volumeHeader->attributesFile.totalBlocks = fcb->fcbPLen / vcb->blockSize; ! 1338: } ! 1339: ! 1340: if (waitfor != MNT_WAIT) ! 1341: bawrite(bp); ! 1342: else ! 1343: retval = bwrite(bp); ! 1344: ! 1345: MarkVCBClean( vcb ); ! 1346: ! 1347: return (retval); ! 1348: } ! 1349: ! 1350: ! 1351: /* ! 1352: * Moved here to avoid having to define prototypes ! 1353: */ ! 1354: ! 1355: /* ! 1356: * hfs vfs operations. ! 1357: */ ! 1358: struct vfsops hfs_vfsops = { ! 1359: hfs_mount, ! 1360: hfs_start, ! 1361: hfs_unmount, ! 1362: hfs_root, ! 1363: hfs_quotactl, ! 1364: hfs_statfs, ! 1365: hfs_sync, ! 1366: hfs_vget, ! 1367: hfs_fhtovp, ! 1368: hfs_vptofh, ! 1369: hfs_init, ! 1370: hfs_sysctl ! 1371: }; ! 1372: ! 1373: void ! 1374: load_hfs(int loadArgument) { ! 1375: struct vfsconf *vfsconflistentry; ! 1376: int entriesRemaining; ! 1377: struct vfsconf *newvfsconf = NULL; ! 1378: struct vfsconf *lastentry = NULL; ! 1379: int j; ! 1380: int (***opv_desc_vector_p)(); ! 1381: int (**opv_desc_vector)(); ! 1382: struct vnodeopv_entry_desc *opve_descp; ! 1383: ! 1384: #pragma unused(loadArgument) ! 1385: ! 1386: /* ! 1387: * This routine is responsible for all the initialization that would ! 1388: * ordinarily be done as part of the system startup; it calls hfs_init ! 1389: * to do the initialization that is strictly HFS-specific. ! 1390: */ ! 1391: ! 1392: #if DIAGNOSTIC ! 1393: // call_kdp(); ! 1394: #endif ! 1395: DBG_LOAD(("load_hfs: Starting...\n")); ! 1396: ! 1397: /* ! 1398: prevvfsconf is supposed to be the entry preceding the new entry. ! 1399: To make sure we can always get hooked in SOMEWHERE in the list, ! 1400: start it out at the first entry of the list. This assumes the ! 1401: first entry in the list will be non-empty and not HFS. ! 1402: ! 1403: This becomes irrelevant when HFS is compiled into the list. ! 1404: */ ! 1405: DBG_LOAD(("load_hfs: Scanning vfsconf list...\n")); ! 1406: vfsconflistentry = vfsconf; ! 1407: for (entriesRemaining = maxvfsslots; entriesRemaining > 0; --entriesRemaining) { ! 1408: if (vfsconflistentry->vfc_vfsops != NULL) { ! 1409: /* ! 1410: * Check to see if we're reloading a new version of hfs during debugging ! 1411: * and overwrite the previously assigned entry if we find one: ! 1412: */ ! 1413: if (strcmp(vfsconflistentry->vfc_name, hfs_fs_name) == 0) { ! 1414: newvfsconf = vfsconflistentry; ! 1415: break; ! 1416: } else { ! 1417: lastentry = vfsconflistentry; ! 1418: }; ! 1419: } else { ! 1420: /* ! 1421: * This is at least a POSSIBLE place to insert the new entry... ! 1422: */ ! 1423: newvfsconf = vfsconflistentry; ! 1424: }; ! 1425: ++vfsconflistentry; ! 1426: }; ! 1427: ! 1428: if (newvfsconf) { ! 1429: DBG_LOAD(("load_hfs: filling in vfsconf entry at 0x%X; lastentry = 0x%X.\n", (u_int)newvfsconf, (u_int)lastentry)); ! 1430: newvfsconf->vfc_vfsops = &hfs_vfsops; ! 1431: strncpy(&newvfsconf->vfc_name[0], hfs_fs_name, MFSNAMELEN); ! 1432: newvfsconf->vfc_typenum = maxvfsconf++; ! 1433: newvfsconf->vfc_refcount = 0; ! 1434: newvfsconf->vfc_flags = 0; ! 1435: newvfsconf->vfc_mountroot = NULL; /* Can't mount root of file system [yet] */ ! 1436: ! 1437: /* Hook into the list: */ ! 1438: newvfsconf->vfc_next = NULL; ! 1439: if (lastentry) { ! 1440: newvfsconf->vfc_next = lastentry->vfc_next; ! 1441: lastentry->vfc_next = newvfsconf; ! 1442: }; ! 1443: ! 1444: /* Based on vfs_op_init and ... */ ! 1445: opv_desc_vector_p = hfs_vnodeop_opv_desc.opv_desc_vector_p; ! 1446: ! 1447: DBG_LOAD(("load_hfs: Allocating and initializing VNode ops vector...\n")); ! 1448: ! 1449: /* ! 1450: * Allocate and init the vector. ! 1451: * Also handle backwards compatibility. ! 1452: */ ! 1453: MALLOC(*opv_desc_vector_p, PFI *, vfs_opv_numops*sizeof(PFI), M_TEMP, M_WAITOK); ! 1454: bzero (*opv_desc_vector_p, vfs_opv_numops*sizeof(PFI)); ! 1455: ! 1456: opv_desc_vector = *opv_desc_vector_p; ! 1457: for (j=0; hfs_vnodeop_opv_desc.opv_desc_ops[j].opve_op; j++) { ! 1458: opve_descp = &(hfs_vnodeop_opv_desc.opv_desc_ops[j]); ! 1459: ! 1460: /* ! 1461: * Sanity check: is this operation listed ! 1462: * in the list of operations? We check this ! 1463: * by seeing if its offest is zero. Since ! 1464: * the default routine should always be listed ! 1465: * first, it should be the only one with a zero ! 1466: * offset. Any other operation with a zero ! 1467: * offset is probably not listed in ! 1468: * vfs_op_descs, and so is probably an error. ! 1469: * ! 1470: * A panic here means the layer programmer ! 1471: * has committed the all-too common bug ! 1472: * of adding a new operation to the layer's ! 1473: * list of vnode operations but ! 1474: * not adding the operation to the system-wide ! 1475: * list of supported operations. ! 1476: */ ! 1477: if (opve_descp->opve_op->vdesc_offset == 0 && ! 1478: opve_descp->opve_op->vdesc_offset != VOFFSET(vop_default)) { ! 1479: printf("load_hfs: operation %s not listed in %s.\n", ! 1480: opve_descp->opve_op->vdesc_name, ! 1481: "vfs_op_descs"); ! 1482: panic ("load_hfs: bad operation"); ! 1483: } ! 1484: /* ! 1485: * Fill in this entry. ! 1486: */ ! 1487: opv_desc_vector[opve_descp->opve_op->vdesc_offset] = ! 1488: opve_descp->opve_impl; ! 1489: } ! 1490: ! 1491: /* ! 1492: * Finally, go back and replace unfilled routines ! 1493: * with their default. (Sigh, an O(n^3) algorithm. I ! 1494: * could make it better, but that'd be work, and n is small.) ! 1495: */ ! 1496: opv_desc_vector_p = hfs_vnodeop_opv_desc.opv_desc_vector_p; ! 1497: ! 1498: /* ! 1499: * Force every operations vector to have a default routine. ! 1500: */ ! 1501: opv_desc_vector = *opv_desc_vector_p; ! 1502: if (opv_desc_vector[VOFFSET(vop_default)]==NULL) { ! 1503: panic("load_hfs: operation vector without default routine."); ! 1504: } ! 1505: for (j = 0;j<vfs_opv_numops; j++) ! 1506: if (opv_desc_vector[j] == NULL) ! 1507: opv_desc_vector[j] = ! 1508: opv_desc_vector[VOFFSET(vop_default)]; ! 1509: ! 1510: DBG_LOAD(("load_hfs: calling hfs_init()...\n")); ! 1511: hfs_init(newvfsconf); ! 1512: }; ! 1513: } ! 1514: ! 1515: void ! 1516: unload_hfs(int unloadArgument) { ! 1517: int entriesRemaining; ! 1518: struct vfsconf *vfsconflistentry; ! 1519: struct vfsconf *prevconf = NULL; ! 1520: struct vfsconf *hfsconf = NULL; ! 1521: ! 1522: DBG_LOAD(("unload_hfs: removing HFS from vfs conf. list...\n")); ! 1523: ! 1524: prevconf = vfsconflistentry = vfsconf; ! 1525: for (entriesRemaining = maxvfsslots; ! 1526: (entriesRemaining > 0) && (vfsconflistentry != NULL); ! 1527: --entriesRemaining) { ! 1528: if ((vfsconflistentry->vfc_vfsops != NULL) && (strcmp(vfsconflistentry->vfc_name, hfs_fs_name) == 0)) { ! 1529: hfsconf = vfsconflistentry; ! 1530: break; ! 1531: }; ! 1532: prevconf = vfsconflistentry; ! 1533: vfsconflistentry = vfsconflistentry->vfc_next; ! 1534: }; ! 1535: ! 1536: if (hfsconf != NULL) { ! 1537: if (prevconf != NULL) { ! 1538: /* Unlink the HFS entry from the list: */ ! 1539: prevconf->vfc_next = hfsconf->vfc_next; ! 1540: } else { ! 1541: DBG_LOAD(("unload_hfs: no previous entry in list.\n")); ! 1542: } ! 1543: } else { ! 1544: DBG_LOAD(("unload_hfs: couldn't find hfs conf. list entry.\n")); ! 1545: }; ! 1546: ! 1547: FREE(*hfs_vnodeop_opv_desc.opv_desc_vector_p, M_TEMP); ! 1548: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.