Annotation of XNU/bsd/hfs/hfs_lookup.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
        !             3:  *
        !             4:  * @APPLE_LICENSE_HEADER_START@
        !             5:  * 
        !             6:  * The contents of this file constitute Original Code as defined in and
        !             7:  * are subject to the Apple Public Source License Version 1.1 (the
        !             8:  * "License").  You may not use this file except in compliance with the
        !             9:  * License.  Please obtain a copy of the License at
        !            10:  * http://www.apple.com/publicsource and read it before using this file.
        !            11:  * 
        !            12:  * This Original Code and all software distributed under the License are
        !            13:  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
        !            14:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
        !            15:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
        !            16:  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
        !            17:  * License for the specific language governing rights and limitations
        !            18:  * under the License.
        !            19:  * 
        !            20:  * @APPLE_LICENSE_HEADER_END@
        !            21:  */
        !            22: /*
        !            23:  * Copyright (c) 1989, 1993
        !            24:  *     The Regents of the University of California.  All rights reserved.
        !            25:  * (c) UNIX System Laboratories, Inc.
        !            26:  * All or some portions of this file are derived from material licensed
        !            27:  * to the University of California by American Telephone and Telegraph
        !            28:  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
        !            29:  * the permission of UNIX System Laboratories, Inc.
        !            30:  *
        !            31:  * Redistribution and use in source and binary forms, with or without
        !            32:  * modification, are permitted provided that the following conditions
        !            33:  * are met:
        !            34:  * 1. Redistributions of source code must retain the above copyright
        !            35:  *       notice, this list of conditions and the following disclaimer.
        !            36:  * 2. Redistributions in binary form must reproduce the above copyright
        !            37:  *       notice, this list of conditions and the following disclaimer in the
        !            38:  *       documentation and/or other materials provided with the distribution.
        !            39:  * 3. All advertising materials mentioning features or use of this software
        !            40:  *       must display the following acknowledgement:
        !            41:  *     This product includes software developed by the University of
        !            42:  *     California, Berkeley and its contributors.
        !            43:  * 4. Neither the name of the University nor the names of its contributors
        !            44:  *       may be used to endorse or promote products derived from this software
        !            45:  *       without specific prior written permission.
        !            46:  *
        !            47:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
        !            48:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            49:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            50:  * ARE DISCLAIMED.     IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !            51:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            52:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            53:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            54:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            55:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            56:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            57:  * SUCH DAMAGE.
        !            58:  *
        !            59:  *     @(#)hfs_lookup.c        1.0
        !            60:  *     derived from @(#)ufs_lookup.c   8.15 (Berkeley) 6/16/95
        !            61:  *
        !            62:  *     (c) 1998-1999   Apple Computer, Inc.     All Rights Reserved
        !            63:  *     (c) 1990, 1992  NeXT Computer, Inc.     All Rights Reserved
        !            64:  *     
        !            65:  *
        !            66:  *     hfs_lookup.c -- code to handle directory traversal on HFS/HFS+ volume
        !            67:  *
        !            68:  *     MODIFICATION HISTORY:
        !            69:  *     21-May-1999 Don Brady           Add support for HFS rooting.
        !            70:  *      25-Feb-1999 Clark Warner       Fixed the error case of VFS_VGGET when
        !            71:  *                                      processing DotDot (..) to relock parent
        !            72:  *     23-Feb-1999 Pat Dirks           Finish cleanup around Don's last fix in "." and ".." handling.
        !            73:  *     11-Nov-1998 Don Brady           Take out VFS_VGET that got added as part of previous fix.
        !            74:  *     14-Oct-1998 Don Brady           Fix locking policy volation in hfs_lookup for ".." case
        !            75:  *                                                             (radar #2279902).
        !            76:  *      4-Jun-1998 Pat Dirks           Split off from hfs_vnodeops.c
        !            77:  */
        !            78: 
        !            79: #include <sys/param.h>
        !            80: #include <sys/namei.h>
        !            81: #include <sys/buf.h>
        !            82: #include <sys/file.h>
        !            83: #include <sys/mount.h>
        !            84: #include <sys/vnode.h>
        !            85: #include <paths.h>
        !            86: 
        !            87: #include       "hfs.h"
        !            88: #include       "hfs_dbg.h"
        !            89: #include       "hfscommon/headers/FileMgrInternal.h"
        !            90: 
        !            91: u_int16_t      GetForkFromName(struct componentname  *cnp);
        !            92: int                    hfs_vget_sibling(struct vnode *vdp, u_int16_t forkType, struct vnode **vpp);
        !            93: int            hfs_vget_catinfo(struct vnode *parent_vp, struct hfsCatalogInfo *catInfo, u_int32_t forkType, struct vnode **target_vpp);
        !            94: 
        !            95: /*
        !            96:  *     XXX SER fork strings.
        !            97:  * Put these someplace better
        !            98:  */
        !            99: #define gHFSForkIdentStr       "/"
        !           100: #define gDataForkNameStr       "data"
        !           101: #define gRsrcForkNameStr       "rsrc"
        !           102: 
        !           103: 
        !           104: extern int cache_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp);
        !           105: extern void cache_enter(struct vnode *dvp, struct vnode *vp, struct componentname *cnp);
        !           106: 
        !           107: #if DBG_VOP_TEST_LOCKS
        !           108: extern void DbgVopTest(int maxSlots, int retval, VopDbgStoreRec *VopDbgStore, char *funcname);
        !           109: #endif
        !           110: 
        !           111: /*****************************************************************************
        !           112: *
        !           113: *      Operations on vnodes
        !           114: *
        !           115: *****************************************************************************/
        !           116: 
        !           117: 
        !           118: /*     
        !           119:  * FROM FREEBSD 3.1
        !           120:  * Convert a component of a pathname into a pointer to a locked hfsnode.
        !           121:  * This is a very central and rather complicated routine.
        !           122:  * If the file system is not maintained in a strict tree hierarchy,
        !           123:  * this can result in a deadlock situation (see comments in code below).
        !           124:  *
        !           125:  * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
        !           126:  * on whether the name is to be looked up, created, renamed, or deleted.
        !           127:  * When CREATE, RENAME, or DELETE is specified, information usable in
        !           128:  * creating, renaming, or deleting a directory entry may be calculated.
        !           129:  * Notice that these are the only operations that can affect the directory of the target.
        !           130:  *
        !           131:  * If flag has LOCKPARENT or'ed into it and the target of the pathname
        !           132:  * exists, lookup returns both the target and its parent directory locked.
        !           133:  * When creating or renaming and LOCKPARENT is specified, the target may
        !           134:  * not be ".". When deleting and LOCKPARENT is specified, the target may
        !           135:  * be "."., but the caller must check to ensure it does an vrele and vput
        !           136:  * instead of two vputs.
        !           137:  *
        !           138:  * LOCKPARENT and WANTPARENT actually refer to the parent of the last item,
        !           139:  * so if ISLASTCN is not set, they should be ignored. Also they are mutually exclusive, or
        !           140:  * WANTPARENT really implies DONTLOCKPARENT. Either of them set means that the calling
        !           141:  * routine wants to access the parent of the target, locked or unlocked.
        !           142:  *
        !           143:  * Keeping the parent locked as long as possible protects from other processes
        !           144:  * looking up the same item, so it has to be locked until the hfsnode is totally finished
        !           145:  *
        !           146:  * This routine is actually used as VOP_CACHEDLOOKUP method, and the
        !           147:  * filesystem employs the generic hfs_cache_lookup() as VOP_LOOKUP
        !           148:  * method.
        !           149:  *
        !           150:  * hfs_cache_lookup() performs the following for us:
        !           151:  *     check that it is a directory
        !           152:  *     check accessibility of directory
        !           153:  *     check for modification attempts on read-only mounts
        !           154:  *     if name found in cache
        !           155:  *             if at end of path and deleting or creating
        !           156:  *             drop it
        !           157:  *              else
        !           158:  *             return name.
        !           159:  *     return VOP_CACHEDLOOKUP()
        !           160:  *
        !           161:  * Overall outline of hfs_lookup:
        !           162:  *
        !           163:  *     handle simple cases of . and ..
        !           164:  *     search for name in directory, to found or notfound
        !           165:  * notfound:
        !           166:  *     if creating, return locked directory, leaving info on available slots
        !           167:  *     else return error
        !           168:  * found:
        !           169:  *     if at end of path and deleting, return information to allow delete
        !           170:  *     if at end of path and rewriting (RENAME and LOCKPARENT), lock target
        !           171:  *       inode and return info to allow rewrite
        !           172:  *     if not at end, add name to cache; if at end and neither creating
        !           173:  *       nor deleting, add name to cache
        !           174:  */
        !           175: 
        !           176: /*     
        !           177:  *     Lookup *nm in directory *pvp, return it in *a_vpp.
        !           178:  *     **a_vpp is held on exit.
        !           179:  *     We create a hfsnode for the file, but we do NOT open the file here.
        !           180: 
        !           181: #% lookup      dvp L ? ?
        !           182: #% lookup      vpp - L -
        !           183: 
        !           184:        IN struct vnode *dvp - Parent node of file;
        !           185:        INOUT struct vnode **vpp - node of target file, its a new node if the target vnode did not exist;
        !           186:        IN struct componentname *cnp - Name of file;
        !           187: 
        !           188:  *     When should we lock parent_hp in here ??
        !           189:  */
        !           190: 
        !           191: int
        !           192: hfs_lookup(ap)
        !           193:        struct vop_cachedlookup_args /* {
        !           194:                struct vnode *a_dvp;
        !           195:                struct vnode **a_vpp;
        !           196:                struct componentname *a_cnp;
        !           197:        } */ *ap;
        !           198: {
        !           199:        struct vnode                                    *parent_vp;
        !           200:        struct vnode                                    *target_vp;
        !           201:        struct vnode                                    *tparent_vp;
        !           202:        struct hfsnode                                  *parent_hp;                             /* parent */
        !           203:        struct componentname                    *cnp;
        !           204:        struct ucred                                    *cred;
        !           205:        struct proc                                             *p;
        !           206:        struct hfsCatalogInfo                   catInfo;
        !           207:        u_int32_t                                               parent_id;
        !           208:        u_int32_t                                               nodeID;
        !           209:        u_int16_t                                               targetLen;
        !           210:        u_int16_t                                               forkType;
        !           211:        int                                                     flags;
        !           212:        int                                                             lockparent;                                             /* !0 => lockparent flag is set */
        !           213:        int                                                             wantparent;                                             /* !0 => wantparent or lockparent flag */
        !           214:        int                                                             nameiop;
        !           215:        int                                                             retval;
        !           216:        u_char                                                  isDot, isDotDot, found;
        !           217:        DBG_FUNC_NAME("lookup");
        !           218:        DBG_VOP_LOCKS_DECL(2);
        !           219:        DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
        !           220:        DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
        !           221:        DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n"));
        !           222:        DBG_HFS_NODE_CHECK(ap->a_dvp);
        !           223:         
        !           224: 
        !           225:        /*
        !           226:         * Do initial setup
        !           227:         */
        !           228:        parent_vp               = ap->a_dvp;
        !           229:        cnp                             = ap->a_cnp;
        !           230:        parent_hp               = VTOH(parent_vp);                              /* parent */
        !           231:        target_vp               = NULL;
        !           232:        targetLen               = cnp->cn_namelen;
        !           233:        nameiop                 = cnp->cn_nameiop;
        !           234:        cred                    = cnp->cn_cred;
        !           235:        p                               = cnp->cn_proc;
        !           236:        lockparent              = cnp->cn_flags & LOCKPARENT;
        !           237:        wantparent              = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
        !           238:        flags                   = cnp->cn_flags;
        !           239:        parent_id               = H_FILEID(parent_hp);
        !           240:        nodeID                  = kUnknownID;
        !           241:        found                   = FALSE;
        !           242:        isDot                   = FALSE;
        !           243:        isDotDot                = FALSE;
        !           244:        catInfo.hint    = 0;
        !           245:        retval                  = E_NONE;
        !           246:        forkType                = kUndefinedFork;
        !           247: 
        !           248: 
        !           249: 
        !           250:        /*
        !           251:         * We now have a segment name to search for, and a directory to search.
        !           252:         *
        !           253:         */
        !           254: 
        !           255:        /*
        !           256:         * First check to see if it is a . or .., else look it up.
        !           257:         */
        !           258: 
        !           259:        if (flags & ISDOTDOT) {                                                                 /* Wanting the parent */
        !           260:                isDotDot = TRUE;
        !           261:                found = TRUE;                                                                           /* .. is always defined */
        !           262:                nodeID = H_DIRID(parent_hp);
        !           263:        }                                                                                                               /* Wanting ourselves */
        !           264:        else if ((cnp->cn_nameptr[0] == '.') && (targetLen == 1)) {
        !           265:                isDot = TRUE;
        !           266:                found = TRUE;                                                                           /* We always know who we are */
        !           267:        } 
        !           268:        else {                                                                                                  /* Wanting something else */
        !           269:                /* lock catalog b-tree */
        !           270:                retval = hfs_metafilelocking(VTOHFS(parent_vp), kHFSCatalogFileID, LK_SHARED, p);
        !           271:                if (retval)
        !           272:                           goto Err_Exit;
        !           273: 
        !           274:                catInfo.hint = kNoHint;
        !           275:                retval = hfsLookup (VTOVCB(parent_vp), parent_id, cnp->cn_nameptr, targetLen, &catInfo);
        !           276:        
        !           277:        /* unlock catalog b-tree */
        !           278:                (void) hfs_metafilelocking(VTOHFS(parent_vp), kHFSCatalogFileID, LK_RELEASE, p);
        !           279:        
        !           280:                if (retval == E_NONE)
        !           281:                        found = TRUE;
        !           282:        };
        !           283: 
        !           284: 
        !           285:        /*
        !           286:         * At this point we know IF we have a valid dir/name.
        !           287:         */
        !           288: 
        !           289: 
        !           290:        retval = E_NONE;
        !           291:        if (! found) {
        !           292:        /*
        !           293:         * This is a non-existing entry
        !           294:         *
        !           295:         * If creating, and at end of pathname and current
        !           296:         * directory has not been removed, then can consider
        !           297:         * allowing file to be created.
        !           298:         */
        !           299:        if ((nameiop == CREATE || nameiop == RENAME ||
        !           300:                        (nameiop == DELETE &&
        !           301:                        (ap->a_cnp->cn_flags & DOWHITEOUT) &&
        !           302:                        (ap->a_cnp->cn_flags & ISWHITEOUT))) &&
        !           303:                        (flags & ISLASTCN)) {
        !           304:                /*
        !           305:                 * Access for write is interpreted as allowing
        !           306:                 * creation of files in the directory.
        !           307:                 */
        !           308:                retval = VOP_ACCESS(parent_vp, VWRITE, cred, cnp->cn_proc);
        !           309:                if (retval)
        !           310:                        return (retval);
        !           311:        
        !           312:                cnp->cn_flags |= SAVENAME;
        !           313:                if (!lockparent)
        !           314:                        VOP_UNLOCK(parent_vp, 0, p);
        !           315:                retval = EJUSTRETURN;
        !           316:                goto Err_Exit;
        !           317:                }
        !           318:        
        !           319:                /*
        !           320:                 * Insert name into cache (as non-existent) if appropriate.
        !           321:                 */
        !           322: 
        !           323:                /*
        !           324:                * XXX SER - Here we would store the name in cache as non-existant if not trying to create it, but,
        !           325:        * the name cache IS case-sensitive, thus maybe showing a negative hit, when the name
        !           326:        * is only different by case. So hfs does not support negative caching. Something to look at.
        !           327:        * (See radar 2293594 for a failed example)
        !           328:                if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
        !           329:                        cache_enter(parent_vp, *vpp, cnp);
        !           330:                */
        !           331:        
        !           332:                retval = ENOENT;
        !           333:        }
        !           334:        else {
        !           335:                /*
        !           336:                 * We have found an entry
        !           337:                 *
        !           338:                 * Here we have to decide what type of vnode to create.
        !           339:                 * There are 3 type of objects that are given:
        !           340:                 * 1. '.': return the same dp
        !           341:                 * 2. '..' return the parent of dp, always a VDIR
        !           342:                 * 3. catinfo rec: return depending on type:
        !           343:                 *  A. VDIR, nodeType is kCatalogFolderNode
        !           344:                 *  B. VLINK nodeType is kCatalogFileNode, the mode is IFLNK (esp. if it is a link to a directory e.g. bar/link/foo)
        !           345:                 *  C. VREG, nodeType is kCatalogFileNode, forkType at this point is unknown
        !           346:                 * To determine the forkType, we can use this algorithm (\0 in the strings mean the NULL character):
        !           347:                 * a. forkType is kDataType iff ISLASTCN is set (as in the case of the default fork e.g. data/foo).
        !           348:                 * b. forkType is kDataType iff ISLASTCN is not set and the namePtr is followed by "/?AppleHFSFork/data\0"
        !           349:                 * c. forkType is kRsrcType iff ISLASTCN is not set and the namePtr is followed by "/?AppleHFSFork/rsrc\0"
        !           350:                 * If the latter two are correct, then we 'consume' the remaining of the name buffer
        !           351:                 * and set the cnp as appropriate.
        !           352:                 * Anything else returns an retval
        !           353:                 */
        !           354: 
        !           355:                
        !           356:                /*
        !           357:                 * If deleting, and at end of pathname, return
        !           358:                 * parameters which can be used to remove file.
        !           359:                 * If the wantparent flag isn't set, we return only
        !           360:                 * the directory (in ndp->ndvp), otherwise we go
        !           361:                 * on and lock the hfsnode, being careful with ".".
        !           362:                 *
        !           363:                 * Forks cannot be deleted so scan-ahead is illegal, so just return the default fork
        !           364:                 */
        !           365:                if (nameiop == DELETE && (flags & ISLASTCN)) {
        !           366:                        /*
        !           367:                        * Write access to directory required to delete files.
        !           368:                        */
        !           369:                        retval = VOP_ACCESS(parent_vp, VWRITE, cred, cnp->cn_proc);
        !           370:                        if (retval)
        !           371:                                goto Err_Exit;
        !           372:        
        !           373:                        if (isDot) {                                    /* Want to return ourselves */
        !           374:                                VREF(parent_vp);
        !           375:                                target_vp = parent_vp;
        !           376:                                goto Err_Exit;
        !           377:                        }
        !           378:                        else if (isDotDot) {
        !           379:                                retval = VFS_VGET(parent_vp->v_mount, &nodeID, &target_vp);
        !           380:                                if (retval)
        !           381:                                        goto Err_Exit;
        !           382:                        }
        !           383:                        else {
        !           384:                                retval = hfs_vget_catinfo(parent_vp, &catInfo, kAnyFork, &target_vp);
        !           385:                                if (retval)
        !           386:                                        goto Err_Exit;
        !           387:                        };
        !           388: 
        !           389: 
        !           390:                        /*
        !           391:                         * If directory is "sticky", then user must own
        !           392:                         * the directory, or the file in it, else she
        !           393:                         * may not delete it (unless she's root). This
        !           394:                         * implements append-only directories.
        !           395:                         */
        !           396:                        if ((parent_hp->h_meta->h_mode & ISVTX) &&
        !           397:                                        cred->cr_uid != 0 &&
        !           398:                                        cred->cr_uid != parent_hp->h_meta->h_uid &&
        !           399:                                        target_vp->v_type != VLNK &&
        !           400:                                        VTOH(target_vp)->h_meta->h_uid != cred->cr_uid) {
        !           401:                                VPUT(target_vp);
        !           402:                                retval = EPERM;
        !           403:                                goto Err_Exit;
        !           404:                        }
        !           405: #if HFS_HARDLINKS
        !           406:                        /*
        !           407:                         * If this is a link node then we need to save the name
        !           408:                         * (of the link) so we can delete it from the catalog b-tree.
        !           409:                         * In this case, hfs_remove will then free the component name.
        !           410:                         */
        !           411:                        if (target_vp && (VTOH(target_vp)->h_meta->h_metaflags & IN_DATANODE))
        !           412:                                cnp->cn_flags |= SAVENAME;
        !           413: #endif
        !           414:          
        !           415:                        if (!lockparent)
        !           416:                                VOP_UNLOCK(parent_vp, 0, p);
        !           417:                        goto Err_Exit;
        !           418:                 };
        !           419:        
        !           420:                /*
        !           421:                 * If rewriting 'RENAME', return the hfsnode and the
        !           422:                 * information required to rewrite the present directory
        !           423:                 */
        !           424:                if (nameiop == RENAME && wantparent && (cnp->cn_flags & ISLASTCN)) {
        !           425:        
        !           426:                        if ((retval = VOP_ACCESS(parent_vp, VWRITE, cred, cnp->cn_proc)) != 0)
        !           427:                                goto Err_Exit;
        !           428: 
        !           429:                        /*
        !           430:                         * Careful about locking second inode.
        !           431:                         * This can only occur if the target is ".". like 'mv foo/bar foo/.'
        !           432:                         */
        !           433:                        if (isDot) {
        !           434:                                retval = EISDIR;
        !           435:                                goto Err_Exit;
        !           436:                        }
        !           437:                        else if (isDotDot) {
        !           438:                                retval = VFS_VGET(parent_vp->v_mount, &nodeID, &target_vp);
        !           439:                                if (retval)
        !           440:                                        goto Err_Exit;
        !           441:                        }
        !           442:                        else {
        !           443:                                /* If then name differs in case, then act like it does not exist 
        !           444:                                 * This allows renaming foo->Foo
        !           445:                                 */     
        !           446:                                if (strncmp(cnp->cn_nameptr, catInfo.spec.name, targetLen)) {
        !           447:                                        if (!lockparent)
        !           448:                                                VOP_UNLOCK(parent_vp, 0, p);
        !           449:                                        retval = EJUSTRETURN;
        !           450:                                        goto Err_Exit;
        !           451:                                };
        !           452: 
        !           453:                                retval = hfs_vget_catinfo(parent_vp, &catInfo, kAnyFork, &target_vp);
        !           454:                                if (retval)
        !           455:                                        goto Err_Exit;
        !           456:                        };
        !           457:                        
        !           458:                        cnp->cn_flags |= SAVENAME;
        !           459:                        if (!lockparent)
        !           460:                                VOP_UNLOCK(parent_vp, 0, p);
        !           461:                        goto Err_Exit;
        !           462:                /* Finished...all is well, goto the end */
        !           463:                 };
        !           464:        
        !           465:                /*
        !           466:                 * Step through the translation in the name.  We do not `vput' the
        !           467:                 * directory because we may need it again if a symbolic link
        !           468:                 * is relative to the current directory.  Instead we save it
        !           469:                 * unlocked as "tparent_vp".  We must get the target hfsnode before unlocking
        !           470:                 * the directory to insure that the hfsnode will not be removed
        !           471:                 * before we get it.  We prevent deadlock by always fetching
        !           472:                 * inodes from the root, moving down the directory tree. Thus
        !           473:                 * when following backward pointers ".." we must unlock the
        !           474:                 * parent directory before getting the requested directory.
        !           475:                 * There is a potential race condition here if both the current
        !           476:                 * and parent directories are removed before the VFS_VGET for the
        !           477:                 * hfsnode associated with ".." returns.  We hope that this occurs
        !           478:                 * infrequently since we cannot avoid this race condition without
        !           479:                 * implementing a sophisticated deadlock detection algorithm.
        !           480:                 * Note also that this simple deadlock detection scheme will not
        !           481:                 * work if the file system has any hard links other than ".."
        !           482:                 * that point backwards in the directory structure.
        !           483:                 */
        !           484:        
        !           485:                tparent_vp = parent_vp;
        !           486:                if (isDotDot) {
        !           487:                        VOP_UNLOCK(tparent_vp, 0, p);   /* race to get the inode */
        !           488:                        if ((retval = VFS_VGET(parent_vp->v_mount, &nodeID, &target_vp))) {
        !           489:                        vn_lock(tparent_vp, LK_EXCLUSIVE | LK_RETRY, p);
        !           490:                        goto Err_Exit;
        !           491:                }
        !           492:                        if (lockparent && (flags & ISLASTCN) &&
        !           493:                            (retval = vn_lock(tparent_vp, LK_EXCLUSIVE, p))) {
        !           494:                                vput(target_vp);
        !           495:                                goto Err_Exit;
        !           496:                        }
        !           497:                }
        !           498:                else if (isDot) {
        !           499:                        VREF(parent_vp);        /* we want ourself, ie "." */
        !           500:                        target_vp = parent_vp;
        !           501:                }
        !           502:                else {
        !           503:                        mode_t mode;
        !           504:                        /* 
        !           505:                         * Determine what fork to get, currenty 3 scenarios are supported:
        !           506:                         * 1. ./foo: if it is a dir, return a VDIR else return data fork
        !           507:                         * 2. ./foo/.__Fork/data: return data fork
        !           508:                         * 3. ./foo/.__Fork/rsrc: return resource fork
        !           509:                         * So the algorithm is:
        !           510:                         * If the object is a directory
        !           511:                         *      then return a VDIR vnode
        !           512:                         * else if ISLASTCN is true
        !           513:                         *      then get the vnode with forkType=kDataFork
        !           514:                         * else
        !           515:                         *      compare with the remaining cnp buffer with "/.__Fork/"
        !           516:                         *      if a match
        !           517:                         *              then compare string after that with either 'data' or 'rsrc'
        !           518:                         *              if match
        !           519:                         *                      then 
        !           520:                         *                      'consume' rest of cnp, setting appropriate values and flags
        !           521:                         *                      return vnode depending on match
        !           522:                         *              else
        !           523:                         *                      bad fork name
        !           524:                         *      else
        !           525:                         *              illegal path after a file object
        !           526:                         */
        !           527: 
        !           528:                        mode = (mode_t)(catInfo.nodeData.cnd_permissions & 0x0000FFFF);
        !           529:                        
        !           530:                        if (catInfo.nodeData.cnd_type == kCatalogFolderNode) {
        !           531:                                forkType = kDirectory;                          /* Really ignored */
        !           532:                        } 
        !           533:                        else if ((mode & IFMT) == IFLNK) {
        !           534:                                forkType = kDataFork;
        !           535:                        }                                                                       /* After this point, nodeType should be a file */
        !           536:                        else if (flags & ISLASTCN) {                    /* Create a default fork */
        !           537:                                forkType = kDataFork;
        !           538:                }
        !           539:                        else {                                                                  /* determine what fork was specified */
        !           540:                                forkType = GetForkFromName(cnp);
        !           541:                                flags |= ISLASTCN;                                      /* To know to unlock the parent if needed */
        !           542:                        };      /* else */
        !           543: 
        !           544:                        
        !           545:                        /* If couldn't determine what type of fork, leave */
        !           546:                        if (forkType == kUndefinedFork) {                               
        !           547:                                retval = EISDIR;
        !           548:                                goto Err_Exit;
        !           549:                        };
        !           550:                                
        !           551:                        /* Get the vnode now that what type of fork is known */
        !           552:                        DBG_ASSERT((forkType==kDirectory) || (forkType==kDataFork) || (forkType==kRsrcFork));
        !           553:                        retval = hfs_vget_catinfo(tparent_vp, &catInfo, forkType, &target_vp);
        !           554:                        if (retval != E_NONE)
        !           555:                                goto Err_Exit;
        !           556: 
        !           557:                        if (!lockparent || !(flags & ISLASTCN))
        !           558:                                VOP_UNLOCK(tparent_vp, 0, p);
        !           559: 
        !           560:                };      /* else found */
        !           561: 
        !           562: 
        !           563:                /*
        !           564:                * Insert name in cache if wanted.
        !           565:                */
        !           566:                if (cnp->cn_flags & MAKEENTRY)  {
        !           567:                        /*
        !           568:                         * XXX SER - Might be good idea to bcopy(catInfo.nodeData.fsspec.name, cnp->cn_nameptr)
        !           569:                         * to "normalize" the name cache. This will avoid polluting the name cache with
        !           570:                         * names that are different in case, and allow negative caching
        !           571:                         */
        !           572:                        cache_enter(parent_vp, target_vp, cnp);
        !           573:                        }
        !           574:        
        !           575: 
        !           576:        };      /* else found == TRUE */
        !           577:        
        !           578: Err_Exit:
        !           579: 
        !           580:        *ap->a_vpp = target_vp;
        !           581: 
        !           582:        DBG_VOP_UPDATE_VP(1, *ap->a_vpp);
        !           583:        DBG_VOP_LOOKUP_TEST (funcname, cnp, parent_vp, target_vp);
        !           584:        DBG_VOP_LOCKS_TEST(E_NONE);
        !           585:        DBG_VOP_PRINT_FUNCNAME();
        !           586:        if (retval == E_NONE) {
        !           587:                DBG_VOP_CONT(("Success\n"));
        !           588:        } else {
        !           589:                DBG_VOP_CONT(("Fails\n"));
        !           590:        }
        !           591: 
        !           592:        return (retval);
        !           593: }
        !           594: 
        !           595: 
        !           596: 
        !           597: /*
        !           598:  * Based on vn_cache_lookup (which is vfs_cache_lookup in FreeBSD 3.1)
        !           599:  *
        !           600:  * Name caching works as follows:
        !           601:  *
        !           602:  * Names found by directory scans are retained in a cache
        !           603:  * for future reference.  It is managed LRU, so frequently
        !           604:  * used names will hang around.         Cache is indexed by hash value
        !           605:  * obtained from (vp, name) where vp refers to the directory
        !           606:  * containing name.
        !           607:  *
        !           608:  * If it is a "negative" entry, (i.e. for a name that is known NOT to
        !           609:  * exist) the vnode pointer will be NULL.
        !           610:  *
        !           611:  * Upon reaching the last segment of a path, if the reference
        !           612:  * is for DELETE, or NOCACHE is set (rewrite), and the
        !           613:  * name is located in the cache, it will be dropped.
        !           614:  *
        !           615:  * In hfs, since a name can represent multiple forks, it cannot
        !           616:  * be known what fork the name matches, so further checks have to be done.
        !           617:  * Currently a policy of first requested, is the one stored, is followed.
        !           618:  *
        !           619:  * SER XXX If this proves inadequate maybe we can munge the name to contain a fork reference
        !           620:  * like foo -> foo.d for the data fork.
        !           621:  */
        !           622: 
        !           623: int
        !           624: hfs_cache_lookup(ap)
        !           625:        struct vop_lookup_args /* {
        !           626:                struct vnode *a_dvp;
        !           627:                struct vnode **a_vpp;
        !           628:                struct componentname *a_cnp;
        !           629:        } */ *ap;
        !           630: {
        !           631:        struct vnode *vdp;
        !           632:        struct vnode *pdp;
        !           633:        int lockparent; 
        !           634:        int error;
        !           635:        struct vnode **vpp = ap->a_vpp;
        !           636:        struct componentname *cnp = ap->a_cnp;
        !           637:        struct ucred *cred = cnp->cn_cred;
        !           638:        int flags = cnp->cn_flags;
        !           639:        struct proc *p = cnp->cn_proc;
        !           640:        struct hfsnode *hp;
        !           641:        u_int32_t vpid; /* capability number of vnode */
        !           642:        DBG_FUNC_NAME("cache_lookup");
        !           643:        DBG_VOP_LOCKS_DECL(2);
        !           644:        DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
        !           645:        DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
        !           646:        DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n"));
        !           647:     DBG_VOP_CONT(("\tTarget: "));DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
        !           648:        DBG_HFS_NODE_CHECK(ap->a_dvp);
        !           649: 
        !           650:        *vpp = NULL;
        !           651:        vdp = ap->a_dvp;
        !           652:        lockparent = flags & LOCKPARENT;
        !           653: 
        !           654:        if (vdp->v_type != VDIR)
        !           655:                                return (ENOTDIR);
        !           656: 
        !           657:        if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
        !           658:                (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
        !           659:                return (EROFS);
        !           660: 
        !           661:        error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc);
        !           662: 
        !           663:        if (error)
        !           664:                return (error);
        !           665: 
        !           666:        /*
        !           667:         * Lookup an entry in the cache
        !           668:         * If the lookup succeeds, the vnode is returned in *vpp, and a status of -1 is
        !           669:         * returned. If the lookup determines that the name does not exist
        !           670:         * (negative cacheing), a status of ENOENT is returned. If the lookup
        !           671:         * fails, a status of zero is returned.
        !           672:         */
        !           673:        error = cache_lookup(vdp, vpp, cnp);
        !           674: 
        !           675:        if (error == 0)  {              /* Unsuccessfull */
        !           676:                DBG_VOP(("\tWas not in name cache\n"));
        !           677:                error = hfs_lookup(ap);
        !           678: #if HFS_HARDLINKS
        !           679:                if (error)
        !           680:                        return (error);
        !           681:                /*
        !           682:                 * If this is a hard-link vnode then we need to update
        !           683:                 * the name (of the link) and update the parent ID. This
        !           684:                 * enables getattrlist calls to return correct link info.
        !           685:                 */
        !           686:                hp = VTOH(*ap->a_vpp);
        !           687:                if ((flags & ISLASTCN) && (hp->h_meta->h_metaflags & IN_DATANODE)) {
        !           688:                        H_DIRID(hp) = H_FILEID(VTOH(ap->a_dvp));
        !           689:                        hfs_set_metaname(cnp->cn_nameptr, hp->h_meta);
        !           690:                }
        !           691: #endif
        !           692:                return (error);
        !           693:        };
        !           694:        
        !           695:        DBG_VOP(("\tName was found in the name cache"));
        !           696:        if (error == ENOENT) {
        !           697:                DBG_VOP_CONT((" though it was a NEGATIVE HIT\n"));
        !           698:                return (error);
        !           699:        };
        !           700:        DBG_VOP_CONT(("\n"));
        !           701:        
        !           702: #if HFS_HARDLINKS
        !           703:        /*
        !           704:         * If this is a hard-link vnode then we need to update
        !           705:         * the name (of the link) and update the parent ID. This
        !           706:         * enables getattrlist calls to return correct link info.
        !           707:         */
        !           708:        hp = VTOH(*vpp);
        !           709:        if ((flags & ISLASTCN) && (hp->h_meta->h_metaflags & IN_DATANODE)) {
        !           710:                H_DIRID(hp) = H_FILEID(VTOH(vdp));
        !           711:                hfs_set_metaname(cnp->cn_nameptr, hp->h_meta);
        !           712:        }
        !           713: #endif
        !           714: 
        !           715:        /* We have a name that matched */
        !           716:        pdp = vdp;
        !           717:        vdp = *vpp;
        !           718:        vpid = vdp->v_id;
        !           719:        if (pdp == vdp) {       /* lookup on "." */
        !           720:                VREF(vdp);
        !           721:                error = 0;
        !           722:        } else if (flags & ISDOTDOT) {
        !           723:                /* 
        !           724:                 * Carefull on the locking policy,
        !           725:                 * remember we always lock from parent to child, so have
        !           726:                 * to release lock on child before trying to lock parent
        !           727:                 * then regain lock if needed
        !           728:                 */
        !           729:                VOP_UNLOCK(pdp, 0, p);
        !           730:                error = vget(vdp, LK_EXCLUSIVE, p);
        !           731:                if (!error && lockparent && (flags & ISLASTCN))
        !           732:                        error = vn_lock(pdp, LK_EXCLUSIVE, p);
        !           733:        } else {
        !           734:                /* 
        !           735:                 * Check to see if a specific fork is not being requested.
        !           736:                 *
        !           737:                 * If it is a file and not the last path item
        !           738:                 * then check if its a proper fork
        !           739:                 *              If it is, check to see if the matched vnode is the same fork
        !           740:                 *      else see if the proper fork exists.
        !           741:                 *              If it does, return that one, else do VOP_CACHEDLOOKUP()
        !           742:                 * Notice that nothing is done if an undefined fork is named. Just leave and let lookup()
        !           743:                 * handle strange cases.
        !           744:                 *
        !           745:                 * XXX SER Notice that when the target is not what was in the name cache,
        !           746:                 * it is locked, before trying to get its sibling. Could this be a problem since both 
        !           747:                 * siblings can be locked, but not in a determinalistic order????
        !           748:                 */
        !           749:                u_int16_t       forkType;
        !           750:                                 
        !           751:                error = vget(vdp, LK_EXCLUSIVE, p);
        !           752:                if ((! error) && (vdp->v_type == VREG) && (vpid == vdp->v_id)) {
        !           753:                        if (!(flags & ISLASTCN)) {
        !           754:                                forkType = GetForkFromName(cnp);
        !           755:                                if (forkType != kUndefinedFork) {
        !           756:                                        flags |= ISLASTCN;
        !           757:                                        if (H_FORKTYPE(VTOH(vdp)) != forkType) {
        !           758:                                                error = hfs_vget_sibling(vdp, forkType, vpp);
        !           759:                                                vput(vdp);
        !           760:                                                if (! error) {
        !           761:                                                        vdp = *vpp;
        !           762:                                                        vpid = vdp->v_id;
        !           763:                                                }
        !           764:                                        }
        !           765:                                }
        !           766:                        } 
        !           767:                        else {
        !           768:                                /* Its the last item, so we want the data fork */
        !           769:                                if (H_FORKTYPE(VTOH(vdp)) != kDataFork) {
        !           770:                                        error = hfs_vget_sibling(vdp, kDataFork, vpp);
        !           771:                                        vput(vdp);
        !           772:                                        if (! error) {
        !           773:                                                vdp = *vpp;
        !           774:                                                vpid = vdp->v_id;
        !           775:                                        }
        !           776:                                }
        !           777:                        };
        !           778:                };
        !           779:                if (!lockparent || error || !(flags & ISLASTCN))
        !           780:                        VOP_UNLOCK(pdp, 0, p);
        !           781:        };
        !           782:        /*
        !           783:         * Check that the capability number did not change
        !           784:         * while we were waiting for the lock.
        !           785:         */
        !           786:        if (!error) {
        !           787:                if (vpid == vdp->v_id)
        !           788:                        return (0);             /* HERE IS THE NORMAL EXIT FOR CACHE LOOKUP!!!! */
        !           789:                /*
        !           790:                 * The above is the NORMAL exit, after this point is an error
        !           791:                 * condition.
        !           792:                 */
        !           793:                vput(vdp);
        !           794:                if (lockparent && pdp != vdp && (flags & ISLASTCN))
        !           795:                        VOP_UNLOCK(pdp, 0, p);
        !           796:        }
        !           797:        error = vn_lock(pdp, LK_EXCLUSIVE, p);
        !           798:        if (error)
        !           799:                return (error);
        !           800:        return (hfs_lookup(ap));
        !           801: }
        !           802: 
        !           803: /*
        !           804:  *     Parses a componentname and sees if the remaining path
        !           805:  *     contains a hfs named fork specifier. If it does set the
        !           806:  *     componentname to consume the rest of the path, and
        !           807:  *     return the forkType
        !           808:  */
        !           809: 
        !           810: u_int16_t      GetForkFromName(struct componentname  *cnp)
        !           811: {
        !           812:        u_int16_t       forkType        = kUndefinedFork;
        !           813:        char            *tcp            = cnp->cn_nameptr + cnp->cn_namelen;
        !           814: 
        !           815:        if (bcmp(tcp, _PATH_FORKSPECIFIER, sizeof(_PATH_FORKSPECIFIER) - 1) == 0) {             
        !           816:                /* Its a HFS fork, so far */
        !           817:                tcp += (sizeof(_PATH_FORKSPECIFIER) - 1);
        !           818:                if (bcmp(tcp, _PATH_DATANAME, sizeof(_PATH_DATANAME)) == 0) {
        !           819:                        forkType = kDataFork;
        !           820:                        cnp->cn_consume = sizeof(_PATH_FORKSPECIFIER) + sizeof(_PATH_DATANAME) - 2;
        !           821:                }
        !           822:                else if (bcmp(tcp, _PATH_RSRCNAME, sizeof(_PATH_RSRCNAME)) == 0) {
        !           823:                        forkType = kRsrcFork;
        !           824:                        cnp->cn_consume = sizeof(_PATH_FORKSPECIFIER) + sizeof(_PATH_RSRCNAME) - 2;
        !           825:                };      /* else if */
        !           826:        };      /* if bcmp */   
        !           827: 
        !           828: 
        !           829:        /* XXX SER For backwards compatability...keep it */
        !           830:        if (forkType == kUndefinedFork) {
        !           831:                tcp = cnp->cn_nameptr + cnp->cn_namelen;
        !           832:        if (bcmp(tcp, gHFSForkIdentStr, sizeof(gHFSForkIdentStr) - 1) == 0) {           
        !           833:                /* Its a HFS fork, so far */
        !           834:                tcp += (sizeof(gHFSForkIdentStr) - 1);
        !           835:                if (bcmp(tcp, gDataForkNameStr, sizeof(gDataForkNameStr)) == 0) {
        !           836:                        forkType = kDataFork;
        !           837:                        cnp->cn_consume = sizeof(gHFSForkIdentStr) + sizeof(gDataForkNameStr) - 2;
        !           838:                }
        !           839:                else if (bcmp(tcp, gRsrcForkNameStr, sizeof(gRsrcForkNameStr)) == 0) {
        !           840:                        forkType = kRsrcFork;
        !           841:                        cnp->cn_consume = sizeof(gHFSForkIdentStr) + sizeof(gRsrcForkNameStr) - 2;
        !           842:                };      /* else if */
        !           843:        };      /* if bcmp */                                                           
        !           844:        };                                                      
        !           845: 
        !           846:        return forkType;        
        !           847: }
        !           848: 
        !           849: #if DBG_VOP_TEST_LOCKS
        !           850: 
        !           851: void DbgLookupTest( char *funcname, struct componentname  *cnp, struct vnode *dvp, struct vnode *vp)
        !           852: {
        !           853:        if (! (hfs_dbg_lookup || hfs_dbg_all))
        !           854:                return;
        !           855:                
        !           856:                
        !           857:        if (dvp) {
        !           858:                if (lockstatus(&VTOH(dvp)->h_lock)) {
        !           859:                        DBG_LOOKUP (("%s: Parent vnode exited LOCKED", funcname));
        !           860:                }
        !           861:                else {
        !           862:                        DBG_LOOKUP (("%s: Parent vnode exited UNLOCKED", funcname));
        !           863:                }
        !           864:        }
        !           865: 
        !           866:        if (vp) {
        !           867:                if (vp==dvp)
        !           868:                  {
        !           869:                        DBG_LOOKUP (("%s: Target and Parent are the same", funcname));
        !           870:                  }
        !           871:                else {
        !           872:                        if (lockstatus(&VTOH(vp)->h_lock)) {
        !           873:                                DBG_LOOKUP (("%s: Found vnode exited LOCKED", funcname));
        !           874:                        }
        !           875:                        else {
        !           876:                                DBG_LOOKUP (("%s: Found vnode exited LOCKED", funcname));
        !           877:                        }
        !           878:                }
        !           879:                DBG_LOOKUP (("%s: Found vnode 0x%x has vtype of %d\n ", funcname, (u_int)vp, vp->v_type));
        !           880:        }
        !           881:        else
        !           882:                DBG_LOOKUP (("%s: Found vnode exited NULL\n",  funcname));
        !           883: 
        !           884: 
        !           885: }
        !           886: 
        !           887: #endif /* DBG_VOP_TEST_LOCKS */
        !           888: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.