|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24: /*
25: * Copyright (c) 1989, 1993
26: * The Regents of the University of California. All rights reserved.
27: * (c) UNIX System Laboratories, Inc.
28: * All or some portions of this file are derived from material licensed
29: * to the University of California by American Telephone and Telegraph
30: * Co. or Unix System Laboratories, Inc. and are reproduced herein with
31: * the permission of UNIX System Laboratories, Inc.
32: *
33: * Redistribution and use in source and binary forms, with or without
34: * modification, are permitted provided that the following conditions
35: * are met:
36: * 1. Redistributions of source code must retain the above copyright
37: * notice, this list of conditions and the following disclaimer.
38: * 2. Redistributions in binary form must reproduce the above copyright
39: * notice, this list of conditions and the following disclaimer in the
40: * documentation and/or other materials provided with the distribution.
41: * 3. All advertising materials mentioning features or use of this software
42: * must display the following acknowledgement:
43: * This product includes software developed by the University of
44: * California, Berkeley and its contributors.
45: * 4. Neither the name of the University nor the names of its contributors
46: * may be used to endorse or promote products derived from this software
47: * without specific prior written permission.
48: *
49: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59: * SUCH DAMAGE.
60: *
61: * @(#)hfs_lookup.c 1.0
62: * derived from @(#)ufs_lookup.c 8.15 (Berkeley) 6/16/95
63: *
64: * (c) 1998 Apple Computer, Inc. All Rights Reserved
65: * (c) 1990, 1992 NeXT Computer, Inc. All Rights Reserved
66: *
67: *
68: * hfs_lookup.c -- code to handle directory traversal on HFS/HFS+ volume
69: *
70: * MODIFICATION HISTORY:
71: * 25-Feb-1999 Clark Warner Fixed the error case of VFS_VGGET when
72: * processing DotDot (..) to relock parent
73: * 23-Feb-1999 Pat Dirks Finish cleanup around Don's last fix in "." and ".." handling.
74: * 11-Nov-1998 Don Brady Take out VFS_VGET that got added as part of previous fix.
75: * 14-Oct-1998 Don Brady Fix locking policy volation in hfs_lookup for ".." case
76: * (radar #2279902).
77: * 4-Jun-1998 Pat Dirks Split off from hfs_vnodeops.c
78: */
79:
80: #include <sys/param.h>
81: #include <sys/systm.h>
82: #include <sys/resourcevar.h>
83: #include <sys/kernel.h>
84: #include <sys/file.h>
85: #include <sys/stat.h>
86: #include <sys/buf.h>
87: #include <sys/proc.h>
88: #include <sys/conf.h>
89: #include <sys/mount.h>
90: #include <mach/machine/vm_types.h>
91: #include <sys/vnode.h>
92: //#include <sys/vnode_if.h>
93: #include <sys/malloc.h>
94: #include <sys/namei.h>
95: #include <sys/signalvar.h>
96: #include <sys/uio.h>
97: #include <sys/time.h>
98: #include <sys/attr.h>
99: #include <miscfs/specfs/specdev.h>
100:
101: #include <sys/vm.h>
102: #include <libkern/libkern.h>
103: #include <mach/machine/simple_lock.h>
104: #include <machine/spl.h>
105:
106: #include "hfs.h"
107: #include "hfs_dbg.h"
108: #include "hfscommon/headers/FileMgrInternal.h"
109: #include "hfscommon/headers/CatalogPrivate.h"
110:
111: extern void cache_purge (struct vnode *vp);
112: extern int cache_lookup (struct vnode *dvp, struct vnode **vpp, struct componentname *cnp);
113: extern void cache_enter (struct vnode *dvp, struct vnode *vpp, struct componentname *cnp);
114:
115: #if DBG_VOP_TEST_LOCKS
116: extern void DbgVopTest(int maxSlots, int retval, VopDbgStoreRec *VopDbgStore, char *funcname);
117: #endif
118:
119: /*****************************************************************************
120: *
121: * Operations on vnodes
122: *
123: *****************************************************************************/
124:
125: /* From FreeBSD 2.2 MSDOS_LOOKUP
126: * When we search a directory the blocks containing directory entries are
127: * read and examined. The directory entries contain information that would
128: * normally be in the hfsnode of a unix filesystem. This means that some of
129: * a directory's contents may also be in memory resident denodes (sort of
130: * an hfsnode). This can cause problems if we are searching while some other
131: * process is modifying a directory. To prevent one process from accessing
132: * incompletely modified directory information we depend upon being the
133: * soul owner of a directory block. bread/brelse provide this service.
134: * This being the case, when a process modifies a directory it must first
135: * acquire the disk block that contains the directory entry to be modified.
136: * Then update the disk block and the denode, and then write the disk block
137: * out to disk. This way disk blocks containing directory entries and in
138: * memory denode's will be in synch.
139: */
140:
141: /* FROM UNIX 63
142: * Convert a component of a pathname into a pointer to a locked hfsnode.
143: * This is a very central and rather complicated routine.
144: * If the file system is not maintained in a strict tree hierarchy,
145: * this can result in a deadlock situation (see comments in code below).
146: *
147: * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
148: * whether the name is to be looked up, created, renamed, or deleted.
149: * When CREATE, RENAME, or DELETE is specified, information usable in
150: * creating, renaming, or deleting a directory entry may be calculated.
151: * If flag has LOCKPARENT or'ed into it, the parent directory is returned
152: * locked. If flag has WANTPARENT or'ed into it, the parent directory is
153: * returned unlocked. Otherwise the parent directory is not returned. If
154: * the target of the pathname exists and LOCKLEAF is or'ed into the flag
155: * the target is returned locked, otherwise it is returned unlocked.
156: * When creating or renaming and LOCKPARENT is specified, the target may not
157: * be ".". When deleting and LOCKPARENT is specified, the target may be ".".
158: *
159: * Overall outline of hfs_lookup:
160: *
161: * check accessibility of directory
162: * look for name in cache, if found, then if at end of path
163: * and deleting or creating, drop it, else return name
164: * search for name in directory, to found or notfound
165: * DoesntExist:
166: * if creating, return locked directory, leaving info on available slots
167: * else return retval
168: * Exists:
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: * hfsnode 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: * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent hfsnode unlocked.
176: */
177:
178: /*
179: * Lookup *nm in directory *pvp, return it in *a_vpp.
180: * **a_vpp is held on exit.
181: * We create a hfsnode for the file, but we do NOT open the file here.
182:
183: #% lookup dvp L ? ?
184: #% lookup vpp - L -
185:
186: IN struct vnode *dvp - Parent node of file;
187: INOUT struct vnode **vpp - node of target file, its a new node if the target vnode did not exist;
188: IN struct componentname *cnp - Name of file;
189:
190: * When should we lock parent_hp in here ??
191: */
192:
193: int
194: hfs_lookup(ap)
195: struct vop_lookup_args /* {
196: struct vnode *a_dvp;
197: struct vnode **a_vpp;
198: struct componentname *a_cnp;
199: } */ *ap;
200: {
201: struct vnode *parent_vp;
202: struct vnode *target_vp;
203: struct vnode *tparent_vp;
204: struct hfsnode *parent_hp; /* parent */
205: struct hfsnode *target_hp; /* target */
206: struct hfsmount *parent_hfs;
207: struct componentname *cnp;
208: struct ucred *cred;
209: struct proc *p;
210: struct hfsCatalogInfo catInfo;
211: u_long parent_id;
212: u_long lockparent; /* !0 => lockparent flag is set */
213: u_long wantparent; /* !0 => wantparent or lockparent flag */
214: u_short targetLen;
215: int nameiop;
216: int retval;
217: u_char isDot, isDotDot;
218: UInt32 nodeID;
219: UInt16 forkType;
220: DBG_FUNC_NAME("lookup");
221: DBG_VOP_LOCKS_DECL(2);
222: DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
223: DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
224: DBG_VOP_PRINT_FUNCNAME();
225: DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);DBG_VOP_CONT(("\n"));
226: DBG_VOP_PRINT_FUNCNAME();
227: DBG_VOP_CONT(("\tTarget: "));DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
228: DBG_HFS_NODE_CHECK(ap->a_dvp);
229: DBG_ASSERT(ap->a_dvp!=NULL);
230:
231:
232: /*
233: * Do initial setup
234: */
235: parent_vp = ap->a_dvp;
236: cnp = ap->a_cnp;
237: parent_hp = VTOH(parent_vp); /* parent */
238: target_vp = NULL;
239: parent_hfs = VTOHFS(parent_vp);
240: targetLen = cnp->cn_namelen;
241: nameiop = cnp->cn_nameiop;
242: cred = cnp->cn_cred;
243: p = cnp->cn_proc;
244: lockparent = cnp->cn_flags & LOCKPARENT;
245: wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
246: parent_id = H_FILEID(parent_hp);
247: nodeID = kUnknownID;
248: isDot = FALSE;
249: isDotDot = cnp->cn_flags & ISDOTDOT;
250: catInfo.hint = 0;
251: retval = E_NONE;
252: forkType = kDirCmplx;
253:
254:
255: /*
256: * Check accessiblity of directory.
257: */
258: if ((parent_vp->v_type != VDIR) && (parent_vp->v_type != VCPLX)) {
259: DBG_ERR(("%s: Parent VNode '%s' is not a directory, its %d\n",funcname, H_NAME(parent_hp), parent_vp->v_type));
260: retval = ENOTDIR;
261: goto Err_Exit;
262: };
263:
264: /*
265: * Check accessiblity of directory. Only if it is non complex, since it would of been checked
266: * when looking up the complex node. The 'first' lookup's parent should alsways be a directory
267: */
268: if ((parent_vp->v_type != VCPLX) && ((retval = VOP_ACCESS(parent_vp, VEXEC, cred, p)) != E_NONE)) {
269: DBG_ERR(("%s: No acces to parent %s, %s\n",funcname, H_NAME(parent_hp), ap->a_cnp->cn_nameptr));
270: goto Err_Exit;
271: };
272:
273: /* Check for accessibility of the file...is this a read-only volume */
274: if ((cnp->cn_flags & ISLASTCN) && (VTOVFS(parent_vp)->mnt_flag & MNT_RDONLY) &&
275: (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
276: DBG_ERR(("%s: Invalid cn_nameiop for read-only volume %s, %s\n",funcname, H_NAME(parent_hp), ap->a_cnp->cn_nameptr));
277: retval = EROFS;
278: goto Err_Exit;
279: };
280:
281: /*
282: * We now have a segment name to search for, and a directory to search.
283: *
284: * Before tediously performing a linear scan of the directory,
285: * check the name cache to see if the directory/name pair
286: * we are looking for is known already.
287: */
288: retval = cache_lookup(parent_vp, &target_vp, cnp);
289: if (retval != E_NONE) {
290: u_long vpid; /* capability number of vnode */
291:
292: DBG_VOP(("\tFound in name cache: "));
293: if (retval == ENOENT) {
294: DBG_VOP_CONT((" As a non-entry\n"));
295: goto Err_Exit;
296: }
297: DBG_VOP_PRINT_VNODE_INFO(target_vp);DBG_VOP_CONT(("\n"));
298: /*
299: * Get the next vnode in the path.
300: * See comment below starting `Step through' for
301: * an explaination of the locking protocol.
302: */
303: tparent_vp = parent_vp;
304: parent_vp = target_vp;
305: vpid = target_vp->v_id;
306: if (tparent_vp == parent_vp) { /* Lookup on '.' */
307: VREF(parent_vp);
308: retval = E_NONE;
309: } else if (cnp->cn_flags & ISDOTDOT) { /* If getting our parent */
310: VOP_UNLOCK(tparent_vp, 0, p);
311: retval = vget(parent_vp, LK_EXCLUSIVE, p);
312: if (!retval && lockparent && (cnp->cn_flags & ISLASTCN))
313: retval = vn_lock(tparent_vp, LK_EXCLUSIVE, p);
314: } else {
315: retval = vget(parent_vp, LK_EXCLUSIVE, p);
316: if ((!retval) && (parent_vp->v_type == VCPLX) && (cnp->cn_flags & ISLASTCN)) {
317: target_vp = VTOH(parent_vp)->h_relative;
318: VPUT(parent_vp);
319: if (target_vp == NULL) {
320: parent_vp = tparent_vp;
321: goto Skip_NameCache;
322: }
323: parent_vp = target_vp; /* do the 'skipping' */
324: vpid = parent_vp->v_id;
325: retval = vget(parent_vp, LK_EXCLUSIVE, p);
326: };
327: if (!lockparent || retval || !(cnp->cn_flags & ISLASTCN))
328: VOP_UNLOCK(tparent_vp, 0, p);
329: };
330:
331: /*
332: * Check that the node number did not change
333: * while we were waiting for the lock.
334: */
335: if (retval == E_NONE) {
336: if (vpid == parent_vp->v_id) {
337: goto Err_Exit; /* Did not change, Success! We have the node..we are finished */
338: };
339:
340: VPUT(parent_vp);
341: if (lockparent && tparent_vp != parent_vp && (cnp->cn_flags & ISLASTCN))
342: VOP_UNLOCK(tparent_vp, 0, p);
343: };
344:
345: retval = vn_lock(tparent_vp, LK_EXCLUSIVE, p);
346: if (retval) goto Err_Exit;
347:
348: parent_vp = tparent_vp;
349: target_vp = NULL;
350: };
351:
352: Skip_NameCache: ;
353:
354:
355: /*
356: * Trivial cases for current and parent directories
357: */
358: if(cnp->cn_nameptr[0] == (char)'.') {
359: /* . */
360: if(targetLen == 1) { /* self */
361: target_vp = parent_vp;
362: nodeID = H_FILEID(parent_hp);
363: isDot = TRUE;
364: catInfo.nodeData.nodeType = kCatalogFolderNode;
365: goto Exists;
366: };
367:
368: /* .. */
369: if((cnp->cn_nameptr[1] == '.') && (targetLen == 2)) { /* parent */
370: isDotDot = TRUE; /* XXX Make doubly-sure this is set for now */
371: nodeID = H_DIRID(parent_hp);
372: VOP_UNLOCK(parent_vp, 0, p);
373: retval = VFS_VGET(HTOVFS(parent_hp), &nodeID, &target_vp);
374: if (retval) {
375: vn_lock(parent_vp, LK_EXCLUSIVE | LK_RETRY, p);
376: goto Err_Exit;
377: }
378:
379: if (lockparent && (cnp->cn_flags & ISLASTCN)) {
380: retval = vn_lock(parent_vp, LK_EXCLUSIVE, p);
381: if (retval) {
382: VPUT(target_vp);
383: goto Err_Exit;
384: }
385: }
386:
387: if (target_vp == NULL) {
388: DBG_ERR(("%s: VFS_VGET FAILED on getting dir %ld: %d\n",funcname, H_DIRID(parent_hp), retval));
389: goto Err_Exit;
390: } else {
391: target_hp = VTOH(target_vp);
392: nodeID = H_FILEID(target_hp);
393: catInfo.nodeData.nodeType = kCatalogFolderNode;
394: goto Exists;
395: };
396: };
397: };
398:
399:
400: /*
401: * The file is not in the cache, nor is it a dot (or dot-dot), so do a hfs lookup
402: */
403:
404: /* lock catalog b-tree */
405: retval = hfs_metafilelocking(parent_hfs, kHFSCatalogFileID, LK_SHARED, p); /*XXX p might be wrongone! */
406: if (retval)
407: goto Err_Exit;
408:
409: if (parent_vp->v_type == VCPLX) {
410: retval = hfsLookup (HFSTOVCB(parent_hfs), H_DIRID(parent_hp), H_NAME(parent_hp), -1, H_HINT(parent_hp), &catInfo);
411: }
412: else {
413: retval = hfsLookup (HFSTOVCB(parent_hfs), parent_id, cnp->cn_nameptr, targetLen, kNoHint, &catInfo);
414: }
415:
416: /* unlock catalog b-tree */
417: (void) hfs_metafilelocking(parent_hfs, kHFSCatalogFileID, LK_RELEASE, p);
418:
419: nodeID = catInfo.nodeData.nodeID;
420: if (retval == E_NONE) {
421: goto Exists;
422: } else {
423: goto DoesntExist;
424: };
425:
426:
427: DoesntExist: ;
428:
429: /*
430: * If we get here we didn't find the entry we were looking for. But
431: * that's ok if we are creating or renaming and are at the end of
432: * the pathname and the directory hasn't been removed.
433: */
434: retval = E_NONE;
435: if ((nameiop == CREATE || nameiop == RENAME) && (cnp->cn_flags & ISLASTCN)) {
436: retval = VOP_ACCESS(parent_vp, VWRITE, cred, p);
437: if (retval)
438: goto Err_Exit;
439:
440: cnp->cn_flags |= SAVENAME;
441: if (!lockparent)/* leave searched dir locked? */
442: VOP_UNLOCK(parent_vp, 0, p);
443: retval = EJUSTRETURN;
444: goto Err_Exit;
445: }
446:
447: /*
448: * XXX SER - Here we would store the name in cache as non-existant if not trying to create it, but,
449: * the name cache IS case-sensitive, thus maybe showing a negative hit, when the name
450: * is only different by case. So hfs does not support negative caching. Something to look at.
451: * (See radar 2293594 for a failed example)
452: */
453:
454: retval = ENOENT;
455: goto Err_Exit;
456:
457:
458: Exists: ;
459:
460: DBG_ASSERT(nodeID != kUnknownID);
461: retval = E_NONE;
462:
463: if (target_vp) {
464: forkType = H_FORKTYPE(VTOH(target_vp));
465: } else {
466: /*
467: * Here we have to decide what type of vnode to create
468: * If the parent is a directory, then the child can be one of four types:
469: * 1. VDIR, nodeType is kCatalogFolderNode, forkType will be kDirCmplx
470: * 2. VLINK, nodeType is kCatalogFileNode, forkType will be kDataFork
471: * 3. VREG, nodeType is kCatalogFileNode, forkType will be kDataFork
472: * 4. VCPLX, nodeType is kCatalogFileNode, forkType will be kDirCmplx
473: * To determine which of the latter three, we can use this algorithm:
474: * a. VREG iff ISLASTCN is set (as in the case of the default fork e.g. data/foo).
475: * b. VLINK iff the mode is IFLNK (esp. if it is a link to a directory e.g. bar/link/foo)
476: * c. VCPLX iff it is followed by a valid fork name. (foo/data)
477: * If the parent is a complex, then the child must have a valid fork name
478: */
479:
480: if (parent_vp->v_type == VDIR) {
481: if (catInfo.nodeData.nodeType == kCatalogFolderNode)
482: forkType = kDirCmplx;
483: else if ((cnp->cn_flags & ISLASTCN) || ((catInfo.nodeData.permissions.permissions & IFMT)==IFLNK)) {
484: forkType = kDataFork; /* The defualt */
485: //cnp->cn_flags &= ~MAKEENTRY;
486: }
487: else
488: forkType = kDirCmplx; /* complex */
489: }
490: else if (parent_vp->v_type == VCPLX) {
491: if (! strcmp(cnp->cn_nameptr, kDataForkNameStr))
492: forkType = kDataFork;
493: else if (! strcmp(cnp->cn_nameptr, kRsrcForkNameStr))
494: forkType = kRsrcFork;
495: else {
496: if (!lockparent) /* leave parent dir locked? */
497: VOP_UNLOCK(parent_vp, 0, p);
498: DBG_ERR(("%s: Searching for an illegal fork: %s\n", funcname, cnp->cn_nameptr));
499: retval = ENOENT;
500: goto Err_Exit;
501: }
502: }
503: else
504: panic("Unexpected parent type for lookup");
505: DBG_VOP(("\tExists: Parent v_type:%d, forkType:%d, target nodeType:0x%x\n", parent_vp->v_type, forkType, (u_int)catInfo.nodeData.nodeType));
506: };
507:
508:
509: /*
510: * If deleting and at the end of the path, then if we matched on
511: * "." then don't hfsget() Otherwise
512: * hfsget() the directory entry.
513: */
514: /*
515: * If deleting, and at end of pathname, return
516: * parameters which can be used to remove file.
517: * If the wantparent flag isn't set, we return only
518: * the directory (in ndp->ndvp), otherwise we go
519: * on and lock the hfsnode, being careful with ".".
520: */
521: if (nameiop == DELETE && (cnp->cn_flags & ISLASTCN)) {
522: /*
523: * Write access to directory required to delete files.
524: */
525: retval = VOP_ACCESS(parent_vp, VWRITE, cred, p);
526: if (retval)
527: goto Err_Exit;
528:
529: if (isDot) {
530: VREF(parent_vp);
531: target_vp = parent_vp;
532: goto Err_Exit;
533: }
534: else if (target_vp == NULL) {
535: target_vp = hfs_vhashget(parent_hp->h_dev, nodeID, forkType);
536: if (target_vp == NULL)
537: retval = hfsGet (HTOVCB(parent_hp), &catInfo, forkType, parent_vp, &target_vp);
538: if (retval != E_NONE)
539: goto Err_Exit;
540: }
541: /*
542: * If directory is "sticky", then user must own
543: * the directory, or the file in it, else she
544: * may not delete it (unless she's root). This
545: * implements append-only directories.
546: */
547: if ((parent_hp->h_meta->h_mode & ISVTX) &&
548: cred->cr_uid != 0 &&
549: cred->cr_uid != parent_hp->h_meta->h_uid &&
550: target_vp->v_type != VLNK &&
551: VTOH(target_vp)->h_meta->h_uid != cred->cr_uid) {
552: VPUT(target_vp);
553: retval = EPERM;
554: goto Err_Exit;
555: }
556:
557: if (!lockparent && !isDotDot)
558: VOP_UNLOCK(parent_vp, 0, p);
559: goto Err_Exit;
560: };
561:
562: /*
563: * If rewriting 'RENAME', return the hfsnode and the
564: * information required to rewrite the present directory
565: */
566: if (nameiop == RENAME && wantparent && (cnp->cn_flags & ISLASTCN)) {
567:
568: /* cant change root */
569: if ((parent_id == kRootDirID) && isDot) {
570: retval = EISDIR;
571: goto Err_Exit;
572: };
573:
574: if (target_vp == NULL) {
575:
576: /* Make sure that this is the correct name ie. same case */
577: if (strncmp(cnp->cn_nameptr, catInfo.spec.name, targetLen)) {
578: if (!lockparent)/* leave searched dir locked? */
579: VOP_UNLOCK(parent_vp, 0, p);
580: retval = EJUSTRETURN;
581: goto Err_Exit;
582: };
583:
584: /* Create the vnode, looking in the cache first */
585: target_vp = hfs_vhashget(parent_hp->h_dev, nodeID, forkType);
586: if (target_vp == NULL)
587: retval = hfsGet (HTOVCB(parent_hp), &catInfo, forkType, parent_vp, &target_vp);
588: if (retval != E_NONE)
589: goto Err_Exit;
590: };
591:
592: if (!lockparent && !isDotDot) VOP_UNLOCK(parent_vp, 0, p);
593: cnp->cn_flags |= SAVENAME;
594: goto Err_Exit;
595: /* Finished...all is well, goto the end */
596: };
597:
598: /*
599: * Step through the translation in the name. We do not `vput' the
600: * directory because we may need it again if a symbolic link
601: * is relative to the current directory. Instead we save it
602: * unlocked as "tparent_vp". We must get the target hfsnode before unlocking
603: * the directory to insure that the hfsnode will not be removed
604: * before we get it. We prevent deadlock by always fetching
605: * inodes from the root, moving down the directory tree. Thus
606: * when following backward pointers ".." we must unlock the
607: * parent directory before getting the requested directory.
608: * There is a potential race condition here if both the current
609: * and parent directories are removed before the VFS_VGET for the
610: * hfsnode associated with ".." returns. We hope that this occurs
611: * infrequently since we cannot avoid this race condition without
612: * implementing a sophisticated deadlock detection algorithm.
613: * Note also that this simple deadlock detection scheme will not
614: * work if the file system has any hard links other than ".."
615: * that point backwards in the directory structure.
616: */
617:
618: tparent_vp = parent_vp;
619: if (cnp->cn_flags & ISDOTDOT) {
620: #if 0
621: VOP_UNLOCK(tparent_vp, 0, p);
622: /* radar #2286192
623: * UFS does a VFS_VGET here but the code below is causing a hang so
624: * I'm taking it back out for now - DJB
625: */
626: if ((retval = VFS_VGET(parent_vp->v_mount, &H_FILEID(parent_hp), &target_vp))) {
627: vn_lock(tparent_vp, LK_EXCLUSIVE | LK_RETRY, p);
628: goto Err_Exit;
629: }
630: if (lockparent && (cnp->cn_flags & ISLASTCN)
631: && (retval = vn_lock(tparent_vp, LK_EXCLUSIVE | LK_RETRY, p))) {
632: VPUT(target_vp);
633: goto Err_Exit;
634: }
635: #endif
636: } else if (isDot) { /* "." */
637: VREF(parent_vp);
638: target_vp = parent_vp;
639: } else {
640: if (target_vp == NULL)
641: target_vp = hfs_vhashget(parent_hp->h_dev, nodeID, forkType);
642: if (target_vp == NULL)
643: retval = hfsGet(HTOVCB(parent_hp), &catInfo, forkType, parent_vp, &target_vp);
644: if (retval)
645: goto Err_Exit;
646: if (!lockparent || !(cnp->cn_flags & ISLASTCN))
647: VOP_UNLOCK(tparent_vp, 0, p);
648: }
649:
650: /*
651: * Insert name in cache if wanted.
652: */
653: if (cnp->cn_flags & MAKEENTRY) {
654: struct vnode *tp;
655:
656: /* If this is bypassing the complex vnode, get it, so we only store complex vnodes */
657: if (target_vp->v_type != VREG) {
658: tp = target_vp;
659: } else {
660: if (parent_vp->v_type == VDIR) {
661: tp = VTOH(target_vp)->h_relative;
662: } else {
663: /* Parent node is complex node [already cached in earlier lookup];
664: don't add anything else in the cache now.
665:
666: XXX Someday we may want to enter data/rsrc fork vnodes in the name cache...
667: */
668: tp = NULL;
669: };
670: };
671: if (tp) {
672: DBG_ASSERT((tp->v_type==VDIR) || (tp->v_type==VCPLX) || (tp->v_type==VLNK));
673: DBG_VOP(("\tStoring into name cache: target: 0x%x ", (u_int)target_vp)); DBG_VOP_PRINT_CPN_INFO(cnp);
674: DBG_VOP_CONT((" parent: "));DBG_VOP_PRINT_VNODE_INFO(parent_vp); DBG_VOP_CONT(("\n"));
675: cache_enter(parent_vp, tp, cnp);
676: };
677: };
678:
679: Err_Exit:
680:
681: *ap->a_vpp = target_vp;
682:
683: DBG_VOP_UPDATE_VP(1, *ap->a_vpp);
684: DBG_VOP_LOOKUP_TEST (funcname, cnp, parent_vp, target_vp);
685: DBG_VOP_LOCKS_TEST(E_NONE);
686: DBG_VOP_PRINT_FUNCNAME();
687: if (retval == E_NONE) {
688: DBG_VOP_CONT(("Success\n"));
689: } else {
690: DBG_VOP_CONT(("Fails\n"));
691: }
692:
693: return (retval);
694: }
695:
696:
697:
698:
699: #if DBG_VOP_TEST_LOCKS
700:
701: void DbgLookupTest( char *funcname, struct componentname *cnp, struct vnode *dvp, struct vnode *vp)
702: {
703: int flags = cnp->cn_flags;
704: int nameiop = cnp->cn_nameiop;
705:
706: kprintf ("%X: %s: Action is: ", CURRENT_PROC->p_pid, funcname);
707: switch (nameiop)
708: {
709: case LOOKUP:
710: kprintf ("LOOKUP");
711: break;
712: case CREATE:
713: kprintf ("CREATE");
714: break;
715: case DELETE:
716: kprintf ("DELETE");
717: break;
718: case RENAME:
719: kprintf ("RENAME");
720: break;
721: default:
722: DBG_ERR (("!!!UNKNOWN!!!!"));
723: break;
724: }
725: kprintf (" flags: 0x%x ",flags );
726: if (flags & LOCKPARENT)
727: kprintf (" LOCKPARENT");
728: if (flags & ISLASTCN)
729: kprintf (" ISLASTCN");
730: kprintf ("\n");
731:
732: if (dvp) {
733: kprintf ("%X: %s: Parent vnode exited ", CURRENT_PROC->p_pid, funcname);
734: if (lockstatus(&VTOH(dvp)->h_lock)) {
735: kprintf ("LOCKED\n");
736: }
737: else {
738: kprintf ("UNLOCKED\n");
739: }
740: }
741:
742: if (vp) {
743: if (vp==dvp)
744: {
745: kprintf ("%X: %s: Target and Parent are the same\n", CURRENT_PROC->p_pid, funcname);
746: }
747: else {
748: kprintf ("%X: %s: Found vnode exited ", CURRENT_PROC->p_pid, funcname);
749: if (lockstatus(&VTOH(vp)->h_lock)) {
750: kprintf ("LOCKED\n");
751: }
752: else {
753: kprintf ("UNLOCKED\n");
754: }
755: }
756: kprintf ("%X: %s: Found vnode 0x%x has vtype of %d\n ", CURRENT_PROC->p_pid, funcname, (u_int)vp, vp->v_type);
757: }
758: else
759: kprintf ("%X: %s: Found vnode exited NULL\n", CURRENT_PROC->p_pid, funcname);
760:
761:
762: }
763:
764: #endif /* DBG_VOP_TEST_LOCKS */
765:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.