|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.