|
|
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.