Annotation of src/sys/ufs/ufs/ufs_lookup.c, Revision 1.32
1.32 ! mrg 1: /* $NetBSD: ufs_lookup.c,v 1.31 2000/05/13 23:43:16 perseant Exp $ */
1.2 cgd 2:
1.1 mycroft 3: /*
4: * Copyright (c) 1989, 1993
5: * The Regents of the University of California. All rights reserved.
6: * (c) UNIX System Laboratories, Inc.
7: * All or some portions of this file are derived from material licensed
8: * to the University of California by American Telephone and Telegraph
9: * Co. or Unix System Laboratories, Inc. and are reproduced herein with
10: * the permission of UNIX System Laboratories, Inc.
11: *
12: * Redistribution and use in source and binary forms, with or without
13: * modification, are permitted provided that the following conditions
14: * are met:
15: * 1. Redistributions of source code must retain the above copyright
16: * notice, this list of conditions and the following disclaimer.
17: * 2. Redistributions in binary form must reproduce the above copyright
18: * notice, this list of conditions and the following disclaimer in the
19: * documentation and/or other materials provided with the distribution.
20: * 3. All advertising materials mentioning features or use of this software
21: * must display the following acknowledgement:
22: * This product includes software developed by the University of
23: * California, Berkeley and its contributors.
24: * 4. Neither the name of the University nor the names of its contributors
25: * may be used to endorse or promote products derived from this software
26: * without specific prior written permission.
27: *
28: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38: * SUCH DAMAGE.
39: *
1.3 mycroft 40: * @(#)ufs_lookup.c 8.9 (Berkeley) 8/11/94
1.1 mycroft 41: */
42:
43: #include <sys/param.h>
1.7 christos 44: #include <sys/systm.h>
1.1 mycroft 45: #include <sys/namei.h>
46: #include <sys/buf.h>
47: #include <sys/file.h>
1.27 fvdl 48: #include <sys/stat.h>
1.1 mycroft 49: #include <sys/mount.h>
50: #include <sys/vnode.h>
1.27 fvdl 51: #include <sys/kernel.h>
1.1 mycroft 52:
53: #include <ufs/ufs/quota.h>
54: #include <ufs/ufs/inode.h>
55: #include <ufs/ufs/dir.h>
56: #include <ufs/ufs/ufsmount.h>
57: #include <ufs/ufs/ufs_extern.h>
1.15 bouyer 58: #include <ufs/ufs/ufs_bswap.h>
1.1 mycroft 59:
60: struct nchstats nchstats;
61: #ifdef DIAGNOSTIC
62: int dirchk = 1;
63: #else
64: int dirchk = 0;
65: #endif
66:
1.15 bouyer 67: #define FSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0)
1.1 mycroft 68:
69: /*
70: * Convert a component of a pathname into a pointer to a locked inode.
71: * This is a very central and rather complicated routine.
72: * If the file system is not maintained in a strict tree hierarchy,
73: * this can result in a deadlock situation (see comments in code below).
74: *
75: * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
76: * on whether the name is to be looked up, created, renamed, or deleted.
77: * When CREATE, RENAME, or DELETE is specified, information usable in
78: * creating, renaming, or deleting a directory entry may be calculated.
79: * If flag has LOCKPARENT or'ed into it and the target of the pathname
80: * exists, lookup returns both the target and its parent directory locked.
81: * When creating or renaming and LOCKPARENT is specified, the target may
82: * not be ".". When deleting and LOCKPARENT is specified, the target may
83: * be "."., but the caller must check to ensure it does an vrele and vput
84: * instead of two vputs.
85: *
86: * Overall outline of ufs_lookup:
87: *
88: * check accessibility of directory
89: * look for name in cache, if found, then if at end of path
90: * and deleting or creating, drop it, else return name
91: * search for name in directory, to found or notfound
92: * notfound:
93: * if creating, return locked directory, leaving info on available slots
94: * else return error
95: * found:
96: * if at end of path and deleting, return information to allow delete
97: * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
98: * inode and return info to allow rewrite
99: * if not at end, add name to cache; if at end and neither creating
100: * nor deleting, add name to cache
101: */
102: int
1.7 christos 103: ufs_lookup(v)
104: void *v;
105: {
1.1 mycroft 106: struct vop_lookup_args /* {
107: struct vnode *a_dvp;
108: struct vnode **a_vpp;
109: struct componentname *a_cnp;
1.7 christos 110: } */ *ap = v;
1.30 augustss 111: struct vnode *vdp; /* vnode for directory being searched */
112: struct inode *dp; /* inode for directory being searched */
1.1 mycroft 113: struct buf *bp; /* a buffer of directory entries */
1.30 augustss 114: struct direct *ep; /* the current directory entry */
1.1 mycroft 115: int entryoffsetinblock; /* offset of ep in bp's buffer */
116: enum {NONE, COMPACT, FOUND} slotstatus;
117: doff_t slotoffset; /* offset of area with free space */
118: int slotsize; /* size of area at slotoffset */
119: int slotfreespace; /* amount of space free in slot */
120: int slotneeded; /* size of the entry we're seeking */
121: int numdirpasses; /* strategy for directory search */
122: doff_t endsearch; /* offset to end directory search */
123: doff_t prevoff; /* prev entry dp->i_offset */
124: struct vnode *pdp; /* saved dp during symlink work */
125: struct vnode *tdp; /* returned by VFS_VGET */
126: doff_t enduseful; /* pointer past last used dir slot */
127: u_long bmask; /* block offset mask */
128: int lockparent; /* 1 => lockparent flag is set */
129: int wantparent; /* 1 => wantparent or lockparent flag */
130: int namlen, error;
131: struct vnode **vpp = ap->a_vpp;
132: struct componentname *cnp = ap->a_cnp;
133: struct ucred *cred = cnp->cn_cred;
1.21 wrstuden 134: int flags;
1.1 mycroft 135: int nameiop = cnp->cn_nameiop;
1.15 bouyer 136: const int needswap = UFS_MPNEEDSWAP(ap->a_dvp->v_mount);
1.1 mycroft 137:
1.21 wrstuden 138: cnp->cn_flags &= ~PDIRUNLOCK;
139: flags = cnp->cn_flags;
140:
1.1 mycroft 141: bp = NULL;
142: slotoffset = -1;
143: *vpp = NULL;
144: vdp = ap->a_dvp;
145: dp = VTOI(vdp);
146: lockparent = flags & LOCKPARENT;
147: wantparent = flags & (LOCKPARENT|WANTPARENT);
148:
1.15 bouyer 149:
1.1 mycroft 150: /*
151: * Check accessiblity of directory.
152: */
1.11 mycroft 153: if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0)
1.1 mycroft 154: return (error);
155:
1.14 fvdl 156: if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
157: (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
158: return (EROFS);
159:
1.1 mycroft 160: /*
161: * We now have a segment name to search for, and a directory to search.
162: *
163: * Before tediously performing a linear scan of the directory,
164: * check the name cache to see if the directory/name pair
165: * we are looking for is known already.
166: */
1.26 jdolecek 167: if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
168: return (error);
1.1 mycroft 169:
170: /*
171: * Suppress search for slots unless creating
172: * file and at end of pathname, in which case
173: * we watch for a place to put the new file in
174: * case it doesn't already exist.
175: */
176: slotstatus = FOUND;
177: slotfreespace = slotsize = slotneeded = 0;
178: if ((nameiop == CREATE || nameiop == RENAME) &&
179: (flags & ISLASTCN)) {
180: slotstatus = NONE;
181: slotneeded = (sizeof(struct direct) - MAXNAMLEN +
182: cnp->cn_namelen + 3) &~ 3;
183: }
184:
185: /*
186: * If there is cached information on a previous search of
187: * this directory, pick up where we last left off.
188: * We cache only lookups as these are the most common
189: * and have the greatest payoff. Caching CREATE has little
190: * benefit as it usually must search the entire directory
191: * to determine that the entry does not exist. Caching the
192: * location of the last DELETE or RENAME has not reduced
193: * profiling time and hence has been removed in the interest
194: * of simplicity.
195: */
196: bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
197: if (nameiop != LOOKUP || dp->i_diroff == 0 ||
1.13 bouyer 198: dp->i_diroff > dp->i_ffs_size) {
1.1 mycroft 199: entryoffsetinblock = 0;
200: dp->i_offset = 0;
201: numdirpasses = 1;
202: } else {
203: dp->i_offset = dp->i_diroff;
204: if ((entryoffsetinblock = dp->i_offset & bmask) &&
205: (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)))
206: return (error);
207: numdirpasses = 2;
208: nchstats.ncs_2passes++;
209: }
210: prevoff = dp->i_offset;
1.13 bouyer 211: endsearch = roundup(dp->i_ffs_size, DIRBLKSIZ);
1.1 mycroft 212: enduseful = 0;
213:
214: searchloop:
215: while (dp->i_offset < endsearch) {
216: /*
217: * If necessary, get the next directory block.
218: */
219: if ((dp->i_offset & bmask) == 0) {
220: if (bp != NULL)
221: brelse(bp);
1.7 christos 222: error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL,
223: &bp);
224: if (error)
1.1 mycroft 225: return (error);
226: entryoffsetinblock = 0;
227: }
228: /*
229: * If still looking for a slot, and at a DIRBLKSIZE
230: * boundary, have to start looking for free space again.
231: */
232: if (slotstatus == NONE &&
233: (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
234: slotoffset = -1;
235: slotfreespace = 0;
236: }
237: /*
238: * Get pointer to next entry.
239: * Full validation checks are slow, so we only check
240: * enough to insure forward progress through the
241: * directory. Complete checks can be run by patching
242: * "dirchk" to be true.
243: */
244: ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock);
245: if (ep->d_reclen == 0 ||
1.7 christos 246: (dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock))) {
1.1 mycroft 247: int i;
248:
249: ufs_dirbad(dp, dp->i_offset, "mangled entry");
250: i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
251: dp->i_offset += i;
252: entryoffsetinblock += i;
253: continue;
254: }
255:
256: /*
257: * If an appropriate sized slot has not yet been found,
258: * check to see if one is available. Also accumulate space
259: * in the current block so that we can determine if
260: * compaction is viable.
261: */
262: if (slotstatus != FOUND) {
1.15 bouyer 263: int size = ufs_rw16(ep->d_reclen, needswap);
1.1 mycroft 264:
265: if (ep->d_ino != 0)
1.15 bouyer 266: size -= DIRSIZ(FSFMT(vdp), ep, needswap);
1.1 mycroft 267: if (size > 0) {
268: if (size >= slotneeded) {
269: slotstatus = FOUND;
270: slotoffset = dp->i_offset;
1.15 bouyer 271: slotsize = ufs_rw16(ep->d_reclen,
1.27 fvdl 272: needswap);
1.1 mycroft 273: } else if (slotstatus == NONE) {
274: slotfreespace += size;
275: if (slotoffset == -1)
276: slotoffset = dp->i_offset;
277: if (slotfreespace >= slotneeded) {
278: slotstatus = COMPACT;
279: slotsize = dp->i_offset +
1.16 kleink 280: ufs_rw16(ep->d_reclen,
1.27 fvdl 281: needswap)
1.16 kleink 282: - slotoffset;
1.1 mycroft 283: }
284: }
285: }
286: }
287:
288: /*
289: * Check for a name match.
290: */
291: if (ep->d_ino) {
1.15 bouyer 292: #if (BYTE_ORDER == LITTLE_ENDIAN)
1.27 fvdl 293: if (vdp->v_mount->mnt_maxsymlinklen > 0 ||
294: needswap != 0)
1.1 mycroft 295: namlen = ep->d_namlen;
296: else
297: namlen = ep->d_type;
1.15 bouyer 298: #else
1.27 fvdl 299: if (vdp->v_mount->mnt_maxsymlinklen <= 0
300: && needswap != 0)
1.15 bouyer 301: namlen = ep->d_type;
302: else
1.1 mycroft 303: namlen = ep->d_namlen;
1.15 bouyer 304: #endif
1.1 mycroft 305: if (namlen == cnp->cn_namelen &&
1.18 perry 306: !memcmp(cnp->cn_nameptr, ep->d_name,
1.1 mycroft 307: (unsigned)namlen)) {
308: /*
309: * Save directory entry's inode number and
310: * reclen in ndp->ni_ufs area, and release
311: * directory buffer.
312: */
1.4 mycroft 313: if (vdp->v_mount->mnt_maxsymlinklen > 0 &&
314: ep->d_type == DT_WHT) {
1.3 mycroft 315: slotstatus = FOUND;
316: slotoffset = dp->i_offset;
1.16 kleink 317: slotsize = ufs_rw16(ep->d_reclen,
318: needswap);
1.3 mycroft 319: dp->i_reclen = slotsize;
1.6 mycroft 320: /*
321: * This is used to set dp->i_endoff,
322: * which may be used by ufs_direnter2()
323: * as a length to truncate the
324: * directory to. Therefore, it must
325: * point past the end of the last
326: * non-empty directory entry. We don't
327: * know where that is in this case, so
328: * we effectively disable shrinking by
329: * using the existing size of the
330: * directory.
331: *
332: * Note that we wouldn't expect to
333: * shrink the directory while rewriting
334: * an existing entry anyway.
335: */
336: enduseful = endsearch;
1.3 mycroft 337: ap->a_cnp->cn_flags |= ISWHITEOUT;
338: numdirpasses--;
339: goto notfound;
340: }
1.15 bouyer 341: dp->i_ino = ufs_rw32(ep->d_ino, needswap);
342: dp->i_reclen = ufs_rw16(ep->d_reclen, needswap);
1.3 mycroft 343: brelse(bp);
1.1 mycroft 344: goto found;
345: }
346: }
347: prevoff = dp->i_offset;
1.15 bouyer 348: dp->i_offset += ufs_rw16(ep->d_reclen, needswap);
349: entryoffsetinblock += ufs_rw16(ep->d_reclen, needswap);
1.1 mycroft 350: if (ep->d_ino)
351: enduseful = dp->i_offset;
352: }
1.3 mycroft 353: notfound:
1.1 mycroft 354: /*
355: * If we started in the middle of the directory and failed
356: * to find our target, we must check the beginning as well.
357: */
358: if (numdirpasses == 2) {
359: numdirpasses--;
360: dp->i_offset = 0;
361: endsearch = dp->i_diroff;
362: goto searchloop;
363: }
364: if (bp != NULL)
365: brelse(bp);
366: /*
367: * If creating, and at end of pathname and current
368: * directory has not been removed, then can consider
369: * allowing file to be created.
370: */
1.3 mycroft 371: if ((nameiop == CREATE || nameiop == RENAME ||
372: (nameiop == DELETE &&
373: (ap->a_cnp->cn_flags & DOWHITEOUT) &&
374: (ap->a_cnp->cn_flags & ISWHITEOUT))) &&
1.27 fvdl 375: (flags & ISLASTCN) && dp->i_ffs_effnlink != 0) {
1.12 kleink 376: /*
1.1 mycroft 377: * Access for write is interpreted as allowing
378: * creation of files in the directory.
379: */
1.7 christos 380: error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc);
381: if (error)
1.1 mycroft 382: return (error);
383: /*
384: * Return an indication of where the new directory
385: * entry should be put. If we didn't find a slot,
386: * then set dp->i_count to 0 indicating
387: * that the new slot belongs at the end of the
388: * directory. If we found a slot, then the new entry
389: * can be put in the range from dp->i_offset to
390: * dp->i_offset + dp->i_count.
391: */
392: if (slotstatus == NONE) {
1.13 bouyer 393: dp->i_offset = roundup(dp->i_ffs_size, DIRBLKSIZ);
1.1 mycroft 394: dp->i_count = 0;
1.6 mycroft 395: enduseful = dp->i_offset;
1.3 mycroft 396: } else if (nameiop == DELETE) {
397: dp->i_offset = slotoffset;
398: if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
399: dp->i_count = 0;
400: else
401: dp->i_count = dp->i_offset - prevoff;
1.1 mycroft 402: } else {
403: dp->i_offset = slotoffset;
404: dp->i_count = slotsize;
405: if (enduseful < slotoffset + slotsize)
406: enduseful = slotoffset + slotsize;
407: }
1.6 mycroft 408: dp->i_endoff = roundup(enduseful, DIRBLKSIZ);
1.1 mycroft 409: dp->i_flag |= IN_CHANGE | IN_UPDATE;
410: /*
411: * We return with the directory locked, so that
412: * the parameters we set up above will still be
413: * valid if we actually decide to do a direnter().
414: * We return ni_vp == NULL to indicate that the entry
415: * does not currently exist; we leave a pointer to
416: * the (locked) directory inode in ndp->ni_dvp.
417: * The pathname buffer is saved so that the name
418: * can be obtained later.
419: *
420: * NB - if the directory is unlocked, then this
421: * information cannot be used.
422: */
423: cnp->cn_flags |= SAVENAME;
1.21 wrstuden 424: if (!lockparent) {
1.14 fvdl 425: VOP_UNLOCK(vdp, 0);
1.21 wrstuden 426: cnp->cn_flags |= PDIRUNLOCK;
427: }
1.1 mycroft 428: return (EJUSTRETURN);
429: }
430: /*
431: * Insert name into cache (as non-existent) if appropriate.
432: */
433: if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
434: cache_enter(vdp, *vpp, cnp);
435: return (ENOENT);
436:
437: found:
438: if (numdirpasses == 2)
439: nchstats.ncs_pass2++;
440: /*
441: * Check that directory length properly reflects presence
442: * of this entry.
443: */
1.15 bouyer 444: if (entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep, needswap) >
445: dp->i_ffs_size) {
1.1 mycroft 446: ufs_dirbad(dp, dp->i_offset, "i_size too small");
1.15 bouyer 447: dp->i_ffs_size = entryoffsetinblock +
448: DIRSIZ(FSFMT(vdp), ep, needswap);
1.1 mycroft 449: dp->i_flag |= IN_CHANGE | IN_UPDATE;
450: }
451:
452: /*
453: * Found component in pathname.
454: * If the final component of path name, save information
455: * in the cache as to where the entry was found.
456: */
457: if ((flags & ISLASTCN) && nameiop == LOOKUP)
458: dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1);
459:
460: /*
461: * If deleting, and at end of pathname, return
462: * parameters which can be used to remove file.
463: * If the wantparent flag isn't set, we return only
464: * the directory (in ndp->ni_dvp), otherwise we go
465: * on and lock the inode, being careful with ".".
466: */
467: if (nameiop == DELETE && (flags & ISLASTCN)) {
468: /*
469: * Write access to directory required to delete files.
470: */
1.7 christos 471: error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc);
472: if (error)
1.1 mycroft 473: return (error);
474: /*
475: * Return pointer to current entry in dp->i_offset,
476: * and distance past previous entry (if there
477: * is a previous entry in this block) in dp->i_count.
478: * Save directory inode pointer in ndp->ni_dvp for dirremove().
479: */
480: if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
481: dp->i_count = 0;
482: else
483: dp->i_count = dp->i_offset - prevoff;
484: if (dp->i_number == dp->i_ino) {
485: VREF(vdp);
486: *vpp = vdp;
487: return (0);
488: }
1.28 fvdl 489: if (flags & ISDOTDOT)
490: VOP_UNLOCK(vdp, 0); /* race to get the inode */
1.7 christos 491: error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
1.28 fvdl 492: if (flags & ISDOTDOT)
493: vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY);
1.7 christos 494: if (error)
1.1 mycroft 495: return (error);
496: /*
497: * If directory is "sticky", then user must own
498: * the directory, or the file in it, else she
499: * may not delete it (unless she's root). This
500: * implements append-only directories.
501: */
1.13 bouyer 502: if ((dp->i_ffs_mode & ISVTX) &&
1.1 mycroft 503: cred->cr_uid != 0 &&
1.13 bouyer 504: cred->cr_uid != dp->i_ffs_uid &&
505: VTOI(tdp)->i_ffs_uid != cred->cr_uid) {
1.1 mycroft 506: vput(tdp);
507: return (EPERM);
508: }
509: *vpp = tdp;
1.21 wrstuden 510: if (!lockparent) {
1.14 fvdl 511: VOP_UNLOCK(vdp, 0);
1.21 wrstuden 512: cnp->cn_flags |= PDIRUNLOCK;
513: }
1.1 mycroft 514: return (0);
515: }
516:
517: /*
518: * If rewriting (RENAME), return the inode and the
519: * information required to rewrite the present directory
520: * Must get inode of directory entry to verify it's a
521: * regular file, or empty directory.
522: */
1.14 fvdl 523: if (nameiop == RENAME && wantparent && (flags & ISLASTCN)) {
1.7 christos 524: error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc);
525: if (error)
1.1 mycroft 526: return (error);
527: /*
528: * Careful about locking second inode.
529: * This can only occur if the target is ".".
530: */
531: if (dp->i_number == dp->i_ino)
532: return (EISDIR);
1.28 fvdl 533: if (flags & ISDOTDOT)
534: VOP_UNLOCK(vdp, 0); /* race to get the inode */
1.7 christos 535: error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
1.28 fvdl 536: if (flags & ISDOTDOT)
537: vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY);
1.7 christos 538: if (error)
1.1 mycroft 539: return (error);
540: *vpp = tdp;
541: cnp->cn_flags |= SAVENAME;
1.21 wrstuden 542: if (!lockparent) {
1.14 fvdl 543: VOP_UNLOCK(vdp, 0);
1.21 wrstuden 544: cnp->cn_flags |= PDIRUNLOCK;
545: }
1.1 mycroft 546: return (0);
547: }
548:
549: /*
550: * Step through the translation in the name. We do not `vput' the
551: * directory because we may need it again if a symbolic link
552: * is relative to the current directory. Instead we save it
553: * unlocked as "pdp". We must get the target inode before unlocking
554: * the directory to insure that the inode will not be removed
555: * before we get it. We prevent deadlock by always fetching
556: * inodes from the root, moving down the directory tree. Thus
557: * when following backward pointers ".." we must unlock the
558: * parent directory before getting the requested directory.
559: * There is a potential race condition here if both the current
560: * and parent directories are removed before the VFS_VGET for the
561: * inode associated with ".." returns. We hope that this occurs
562: * infrequently since we cannot avoid this race condition without
563: * implementing a sophisticated deadlock detection algorithm.
564: * Note also that this simple deadlock detection scheme will not
565: * work if the file system has any hard links other than ".."
566: * that point backwards in the directory structure.
567: */
568: pdp = vdp;
569: if (flags & ISDOTDOT) {
1.14 fvdl 570: VOP_UNLOCK(pdp, 0); /* race to get the inode */
1.21 wrstuden 571: cnp->cn_flags |= PDIRUNLOCK;
1.7 christos 572: error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
573: if (error) {
1.21 wrstuden 574: if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY) == 0)
575: cnp->cn_flags &= ~PDIRUNLOCK;
1.1 mycroft 576: return (error);
577: }
1.23 wrstuden 578: if (lockparent && (flags & ISLASTCN)) {
1.25 wrstuden 579: if ((error = vn_lock(pdp, LK_EXCLUSIVE))) {
1.23 wrstuden 580: vput(tdp);
581: return (error);
582: }
583: cnp->cn_flags &= ~PDIRUNLOCK;
1.1 mycroft 584: }
585: *vpp = tdp;
586: } else if (dp->i_number == dp->i_ino) {
587: VREF(vdp); /* we want ourself, ie "." */
588: *vpp = vdp;
589: } else {
1.7 christos 590: error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp);
591: if (error)
1.1 mycroft 592: return (error);
1.21 wrstuden 593: if (!lockparent || !(flags & ISLASTCN)) {
1.14 fvdl 594: VOP_UNLOCK(pdp, 0);
1.21 wrstuden 595: cnp->cn_flags |= PDIRUNLOCK;
596: }
1.1 mycroft 597: *vpp = tdp;
598: }
599:
600: /*
601: * Insert name into cache if appropriate.
602: */
603: if (cnp->cn_flags & MAKEENTRY)
604: cache_enter(vdp, *vpp, cnp);
605: return (0);
606: }
607:
608: void
609: ufs_dirbad(ip, offset, how)
610: struct inode *ip;
611: doff_t offset;
612: char *how;
613: {
614: struct mount *mp;
615:
616: mp = ITOV(ip)->v_mount;
1.9 christos 617: printf("%s: bad dir ino %d at offset %d: %s\n",
1.1 mycroft 618: mp->mnt_stat.f_mntonname, ip->i_number, offset, how);
619: if ((mp->mnt_stat.f_flags & MNT_RDONLY) == 0)
620: panic("bad dir");
621: }
622:
623: /*
624: * Do consistency checking on a directory entry:
625: * record length must be multiple of 4
626: * entry must fit in rest of its DIRBLKSIZ block
627: * record must be large enough to contain entry
628: * name is not longer than MAXNAMLEN
629: * name must be as long as advertised, and null terminated
630: */
631: int
632: ufs_dirbadentry(dp, ep, entryoffsetinblock)
633: struct vnode *dp;
1.30 augustss 634: struct direct *ep;
1.1 mycroft 635: int entryoffsetinblock;
636: {
1.30 augustss 637: int i;
1.1 mycroft 638: int namlen;
1.15 bouyer 639: const int needswap = UFS_MPNEEDSWAP(dp->v_mount);
1.1 mycroft 640:
1.16 kleink 641: #if (BYTE_ORDER == LITTLE_ENDIAN)
642: if (dp->v_mount->mnt_maxsymlinklen > 0 || needswap != 0)
643: namlen = ep->d_namlen;
644: else
645: namlen = ep->d_type;
646: #else
1.19 fvdl 647: if (dp->v_mount->mnt_maxsymlinklen <= 0 && needswap != 0)
1.16 kleink 648: namlen = ep->d_type;
649: else
1.27 fvdl 650: namlen = ep->d_namlen;
1.16 kleink 651: #endif
1.15 bouyer 652: if ((ufs_rw16(ep->d_reclen, needswap) & 0x3) != 0 ||
653: ufs_rw16(ep->d_reclen, needswap) >
1.27 fvdl 654: DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
1.15 bouyer 655: ufs_rw16(ep->d_reclen, needswap) <
1.27 fvdl 656: DIRSIZ(FSFMT(dp), ep, needswap) ||
1.16 kleink 657: namlen > MAXNAMLEN) {
1.1 mycroft 658: /*return (1); */
1.20 thorpej 659: printf("First bad, reclen=%x, DIRSIZ=%lu, namlen=%d, flags=%x "
1.15 bouyer 660: "entryoffsetinblock=%d\n",
661: ufs_rw16(ep->d_reclen, needswap),
1.20 thorpej 662: (u_long)DIRSIZ(FSFMT(dp), ep, needswap),
1.15 bouyer 663: namlen, dp->v_mount->mnt_flag, entryoffsetinblock);
1.1 mycroft 664: goto bad;
665: }
666: if (ep->d_ino == 0)
667: return (0);
668: for (i = 0; i < namlen; i++)
669: if (ep->d_name[i] == '\0') {
670: /*return (1); */
1.9 christos 671: printf("Second bad\n");
1.1 mycroft 672: goto bad;
673: }
674: if (ep->d_name[i])
675: goto bad;
676: return (0);
677: bad:
678: return (1);
679: }
680:
681: /*
1.27 fvdl 682: * Construct a new directory entry after a call to namei, using the
683: * parameters that it left in the componentname argument cnp. The
684: * argument ip is the inode to which the new directory entry will refer.
1.1 mycroft 685: */
1.27 fvdl 686: void
687: ufs_makedirentry(ip, cnp, newdirp)
1.1 mycroft 688: struct inode *ip;
1.27 fvdl 689: struct componentname *cnp;
690: struct direct *newdirp;
1.1 mycroft 691: {
692: #ifdef DIAGNOSTIC
693: if ((cnp->cn_flags & SAVENAME) == 0)
1.27 fvdl 694: panic("makedirentry: missing name");
1.1 mycroft 695: #endif
1.27 fvdl 696: newdirp->d_ino = ip->i_number;
697: newdirp->d_namlen = cnp->cn_namelen;
698: memcpy(newdirp->d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1);
699: if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0)
700: newdirp->d_type = IFTODT(ip->i_ffs_mode);
701: else {
702: newdirp->d_type = 0;
703: }
1.3 mycroft 704: }
705:
706: /*
1.27 fvdl 707: * Write a directory entry after a call to namei, using the parameters
708: * that it left in nameidata. The argument dirp is the new directory
709: * entry contents. Dvp is a pointer to the directory to be written,
710: * which was left locked by namei. Remaining parameters (dp->i_offset,
711: * dp->i_count) indicate how the space for the new entry is to be obtained.
712: * Non-null bp indicates that a directory is being created (for the
713: * soft dependency code).
1.3 mycroft 714: */
1.7 christos 715: int
1.27 fvdl 716: ufs_direnter(dvp, tvp, dirp, cnp, newdirbp)
1.3 mycroft 717: struct vnode *dvp;
1.27 fvdl 718: struct vnode *tvp;
1.3 mycroft 719: struct direct *dirp;
1.27 fvdl 720: struct componentname *cnp;
721: struct buf *newdirbp;
722: {
1.3 mycroft 723: struct ucred *cr;
724: struct proc *p;
725: int newentrysize;
726: struct inode *dp;
727: struct buf *bp;
728: u_int dsize;
729: struct direct *ep, *nep;
1.27 fvdl 730: int error, ret, blkoff, loc, spacefree, flags;
1.3 mycroft 731: char *dirbuf;
1.27 fvdl 732: struct timespec ts;
1.15 bouyer 733: const int needswap = UFS_MPNEEDSWAP(dvp->v_mount);
1.3 mycroft 734:
1.27 fvdl 735: error = 0;
736: cr = cnp->cn_cred;
737: p = cnp->cn_proc;
738:
1.3 mycroft 739: dp = VTOI(dvp);
1.15 bouyer 740: newentrysize = DIRSIZ(0, dirp, 0);
1.3 mycroft 741:
1.1 mycroft 742: if (dp->i_count == 0) {
743: /*
744: * If dp->i_count is 0, then namei could find no
745: * space in the directory. Here, dp->i_offset will
746: * be on a directory block boundary and we will write the
747: * new entry into a fresh block.
748: */
749: if (dp->i_offset & (DIRBLKSIZ - 1))
1.27 fvdl 750: panic("ufs_direnter: newblk");
751: flags = B_CLRBUF;
752: if (!DOINGSOFTDEP(dvp))
753: flags |= B_SYNC;
754: if ((error = VOP_BALLOC(dvp, (off_t)dp->i_offset, DIRBLKSIZ,
755: cr, flags, &bp)) != 0) {
756: if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
757: bdwrite(newdirbp);
758: return (error);
759: }
760: dp->i_ffs_size = dp->i_offset + DIRBLKSIZ;
761: dp->i_flag |= IN_CHANGE | IN_UPDATE;
762: uvm_vnp_setsize(dvp, dp->i_ffs_size);
1.15 bouyer 763: dirp->d_reclen = ufs_rw16(DIRBLKSIZ, needswap);
764: dirp->d_ino = ufs_rw32(dirp->d_ino, needswap);
1.27 fvdl 765: if (dvp->v_mount->mnt_maxsymlinklen <= 0) {
1.15 bouyer 766: #if (BYTE_ORDER == LITTLE_ENDIAN)
1.16 kleink 767: if (needswap == 0) {
1.15 bouyer 768: #else
1.16 kleink 769: if (needswap != 0) {
1.15 bouyer 770: #endif
771: u_char tmp = dirp->d_namlen;
772: dirp->d_namlen = dirp->d_type;
773: dirp->d_type = tmp;
774: }
1.1 mycroft 775: }
1.27 fvdl 776: blkoff = dp->i_offset &
777: (VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_iosize - 1);
778: memcpy((caddr_t)bp->b_data + blkoff, (caddr_t)dirp,
779: newentrysize);
780: if (DOINGSOFTDEP(dvp)) {
781: /*
782: * Ensure that the entire newly allocated block is a
783: * valid directory so that future growth within the
784: * block does not have to ensure that the block is
785: * written before the inode.
786: */
787: blkoff += DIRBLKSIZ;
788: while (blkoff < bp->b_bcount) {
789: ((struct direct *)
790: (bp->b_data + blkoff))->d_reclen = DIRBLKSIZ;
791: blkoff += DIRBLKSIZ;
792: }
793: softdep_setup_directory_add(bp, dp, dp->i_offset,
794: ufs_rw32(dirp->d_ino, needswap), newdirbp);
795: bdwrite(bp);
796: } else {
797: error = VOP_BWRITE(bp);
798: }
799: TIMEVAL_TO_TIMESPEC(&time, &ts);
1.31 perseant 800: ret = VOP_UPDATE(dvp, &ts, &ts, UPDATE_DIROP);
1.27 fvdl 801: if (error == 0)
802: return (ret);
1.1 mycroft 803: return (error);
804: }
805:
806: /*
1.27 fvdl 807: * If dp->i_count is non-zero, then namei found space for the new
808: * entry in the range dp->i_offset to dp->i_offset + dp->i_count
809: * in the directory. To use this space, we may have to compact
810: * the entries located there, by copying them together towards the
811: * beginning of the block, leaving the free space in one usable
812: * chunk at the end.
1.1 mycroft 813: */
814:
815: /*
816: * Increase size of directory if entry eats into new space.
817: * This should never push the size past a new multiple of
818: * DIRBLKSIZE.
819: *
820: * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
821: */
1.13 bouyer 822: if (dp->i_offset + dp->i_count > dp->i_ffs_size)
823: dp->i_ffs_size = dp->i_offset + dp->i_count;
1.1 mycroft 824: /*
825: * Get the block containing the space for the new directory entry.
826: */
1.7 christos 827: error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp);
1.27 fvdl 828: if (error) {
829: if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
830: bdwrite(newdirbp);
1.1 mycroft 831: return (error);
1.27 fvdl 832: }
1.1 mycroft 833: /*
834: * Find space for the new entry. In the simple case, the entry at
835: * offset base will have the space. If it does not, then namei
836: * arranged that compacting the region dp->i_offset to
1.27 fvdl 837: * dp->i_offset + dp->i_count would yield the space.
1.1 mycroft 838: */
839: ep = (struct direct *)dirbuf;
1.15 bouyer 840: dsize = DIRSIZ(FSFMT(dvp), ep, needswap);
841: spacefree = ufs_rw16(ep->d_reclen, needswap) - dsize;
842: for (loc = ufs_rw16(ep->d_reclen, needswap); loc < dp->i_count; ) {
1.1 mycroft 843: nep = (struct direct *)(dirbuf + loc);
844: if (ep->d_ino) {
845: /* trim the existing slot */
1.15 bouyer 846: ep->d_reclen = ufs_rw16(dsize, needswap);
1.1 mycroft 847: ep = (struct direct *)((char *)ep + dsize);
848: } else {
849: /* overwrite; nothing there; header is ours */
850: spacefree += dsize;
851: }
1.15 bouyer 852: dsize = DIRSIZ(FSFMT(dvp), nep, needswap);
853: spacefree += ufs_rw16(nep->d_reclen, needswap) - dsize;
854: loc += ufs_rw16(nep->d_reclen, needswap);
1.27 fvdl 855: if (DOINGSOFTDEP(dvp))
856: softdep_change_directoryentry_offset(dp, dirbuf,
857: (caddr_t)nep, (caddr_t)ep, dsize);
858: else
859: memcpy((caddr_t)ep, (caddr_t)nep, dsize);
1.1 mycroft 860: }
861: /*
862: * Update the pointer fields in the previous entry (if any),
863: * copy in the new entry, and write out the block.
864: */
1.3 mycroft 865: if (ep->d_ino == 0 ||
1.15 bouyer 866: (ufs_rw32(ep->d_ino, needswap) == WINO &&
1.18 perry 867: memcmp(ep->d_name, dirp->d_name, dirp->d_namlen) == 0)) {
1.1 mycroft 868: if (spacefree + dsize < newentrysize)
1.27 fvdl 869: panic("ufs_direnter: compact1");
1.3 mycroft 870: dirp->d_reclen = spacefree + dsize;
1.1 mycroft 871: } else {
872: if (spacefree < newentrysize)
1.27 fvdl 873: panic("ufs_direnter: compact2");
1.3 mycroft 874: dirp->d_reclen = spacefree;
1.15 bouyer 875: ep->d_reclen = ufs_rw16(dsize, needswap);
1.1 mycroft 876: ep = (struct direct *)((char *)ep + dsize);
877: }
1.15 bouyer 878: dirp->d_reclen = ufs_rw16(dirp->d_reclen, needswap);
879: dirp->d_ino = ufs_rw32(dirp->d_ino, needswap);
1.27 fvdl 880: if (dvp->v_mount->mnt_maxsymlinklen <= 0) {
1.15 bouyer 881: #if (BYTE_ORDER == LITTLE_ENDIAN)
1.16 kleink 882: if (needswap == 0) {
1.15 bouyer 883: #else
1.16 kleink 884: if (needswap != 0) {
1.15 bouyer 885: #endif
886: u_char tmp = dirp->d_namlen;
887: dirp->d_namlen = dirp->d_type;
888: dirp->d_type = tmp;
889: }
1.27 fvdl 890: }
1.18 perry 891: memcpy((caddr_t)ep, (caddr_t)dirp, (u_int)newentrysize);
1.27 fvdl 892: if (DOINGSOFTDEP(dvp)) {
893: softdep_setup_directory_add(bp, dp,
894: dp->i_offset + (caddr_t)ep - dirbuf,
895: ufs_rw32(dirp->d_ino, needswap), newdirbp);
896: bdwrite(bp);
897: } else {
898: error = VOP_BWRITE(bp);
899: }
1.1 mycroft 900: dp->i_flag |= IN_CHANGE | IN_UPDATE;
1.27 fvdl 901: /*
902: * If all went well, and the directory can be shortened, proceed
903: * with the truncation. Note that we have to unlock the inode for
904: * the entry that we just entered, as the truncation may need to
905: * lock other inodes which can lead to deadlock if we also hold a
906: * lock on the newly entered node.
907: */
908: if (error == 0 && dp->i_endoff && dp->i_endoff < dp->i_ffs_size) {
909: if (tvp != NULL)
910: VOP_UNLOCK(tvp, 0);
911: (void) VOP_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC, cr, p);
912: if (tvp != NULL)
913: vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
914: }
1.1 mycroft 915: return (error);
916: }
917:
918: /*
919: * Remove a directory entry after a call to namei, using
920: * the parameters which it left in nameidata. The entry
921: * dp->i_offset contains the offset into the directory of the
922: * entry to be eliminated. The dp->i_count field contains the
923: * size of the previous record in the directory. If this
924: * is 0, the first entry is being deleted, so we need only
925: * zero the inode number to mark the entry as free. If the
926: * entry is not the first in the directory, we must reclaim
927: * the space of the now empty record by adding the record size
928: * to the size of the previous entry.
929: */
930: int
1.27 fvdl 931: ufs_dirremove(dvp, ip, flags, isrmdir)
1.1 mycroft 932: struct vnode *dvp;
1.27 fvdl 933: struct inode *ip;
934: int flags;
935: int isrmdir;
1.1 mycroft 936: {
1.27 fvdl 937: struct inode *dp;
1.1 mycroft 938: struct direct *ep;
939: struct buf *bp;
940: int error;
941:
942: dp = VTOI(dvp);
1.3 mycroft 943:
1.27 fvdl 944: if (flags & DOWHITEOUT) {
1.3 mycroft 945: /*
946: * Whiteout entry: set d_ino to WINO.
947: */
1.7 christos 948: error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep,
949: &bp);
950: if (error)
1.3 mycroft 951: return (error);
1.15 bouyer 952: ep->d_ino = ufs_rw32(WINO, UFS_MPNEEDSWAP(dvp->v_mount));
1.3 mycroft 953: ep->d_type = DT_WHT;
1.27 fvdl 954: goto out;
955: }
956:
957: if ((error = VOP_BLKATOFF(dvp,
958: (off_t)(dp->i_offset - dp->i_count), (char **)&ep, &bp)) != 0)
1.3 mycroft 959: return (error);
960:
1.1 mycroft 961: if (dp->i_count == 0) {
962: /*
963: * First entry in block: set d_ino to zero.
964: */
965: ep->d_ino = 0;
1.27 fvdl 966: } else {
967: /*
968: * Collapse new free space into previous entry.
969: */
970: ep->d_reclen =
971: ufs_rw16(ufs_rw16(ep->d_reclen,
972: UFS_MPNEEDSWAP(dvp->v_mount))
973: + dp->i_reclen, UFS_MPNEEDSWAP(dvp->v_mount));
974: }
975: out:
976: if (DOINGSOFTDEP(dvp)) {
1.28 fvdl 977: if (ip) {
978: ip->i_ffs_effnlink--;
979: softdep_change_linkcnt(ip);
1.27 fvdl 980: softdep_setup_remove(bp, dp, ip, isrmdir);
1.28 fvdl 981: }
1.27 fvdl 982: bdwrite(bp);
983: } else {
1.28 fvdl 984: if (ip) {
985: ip->i_ffs_effnlink--;
1.27 fvdl 986: ip->i_ffs_nlink--;
1.28 fvdl 987: ip->i_flag |= IN_CHANGE;
988: }
1.1 mycroft 989: error = VOP_BWRITE(bp);
990: }
991: dp->i_flag |= IN_CHANGE | IN_UPDATE;
992: return (error);
993: }
994:
995: /*
996: * Rewrite an existing directory entry to point at the inode
997: * supplied. The parameters describing the directory entry are
998: * set up by a call to namei.
999: */
1000: int
1.27 fvdl 1001: ufs_dirrewrite(dp, oip, newinum, newtype, isrmdir)
1002: struct inode *dp, *oip;
1003: ino_t newinum;
1004: int newtype;
1005: int isrmdir;
1.1 mycroft 1006: {
1007: struct buf *bp;
1008: struct direct *ep;
1009: struct vnode *vdp = ITOV(dp);
1010: int error;
1011:
1.7 christos 1012: error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp);
1013: if (error)
1.1 mycroft 1014: return (error);
1.27 fvdl 1015: ep->d_ino = ufs_rw32(newinum, UFS_MPNEEDSWAP(vdp->v_mount));
1.1 mycroft 1016: if (vdp->v_mount->mnt_maxsymlinklen > 0)
1.27 fvdl 1017: ep->d_type = newtype;
1018: oip->i_ffs_effnlink--;
1019: if (DOINGSOFTDEP(vdp)) {
1.28 fvdl 1020: softdep_change_linkcnt(oip);
1.27 fvdl 1021: softdep_setup_directory_change(bp, dp, oip, newinum, isrmdir);
1022: bdwrite(bp);
1023: } else {
1024: oip->i_ffs_nlink--;
1.28 fvdl 1025: oip->i_flag |= IN_CHANGE;
1.27 fvdl 1026: error = VOP_BWRITE(bp);
1027: }
1.1 mycroft 1028: dp->i_flag |= IN_CHANGE | IN_UPDATE;
1029: return (error);
1030: }
1031:
1032: /*
1033: * Check if a directory is empty or not.
1034: * Inode supplied must be locked.
1035: *
1036: * Using a struct dirtemplate here is not precisely
1037: * what we want, but better than using a struct direct.
1038: *
1039: * NB: does not handle corrupted directories.
1040: */
1041: int
1042: ufs_dirempty(ip, parentino, cred)
1.30 augustss 1043: struct inode *ip;
1.1 mycroft 1044: ino_t parentino;
1045: struct ucred *cred;
1046: {
1.30 augustss 1047: off_t off;
1.1 mycroft 1048: struct dirtemplate dbuf;
1.30 augustss 1049: struct direct *dp = (struct direct *)&dbuf;
1.17 thorpej 1050: int error, namlen;
1051: size_t count;
1.1 mycroft 1052: #define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
1053:
1.15 bouyer 1054: for (off = 0; off < ip->i_ffs_size;
1.27 fvdl 1055: off += ufs_rw16(dp->d_reclen, UFS_IPNEEDSWAP(ip))) {
1.1 mycroft 1056: error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
1057: UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0);
1058: /*
1059: * Since we read MINDIRSIZ, residual must
1060: * be 0 unless we're at end of file.
1061: */
1062: if (error || count != 0)
1063: return (0);
1064: /* avoid infinite loops */
1065: if (dp->d_reclen == 0)
1066: return (0);
1067: /* skip empty entries */
1.15 bouyer 1068: if (dp->d_ino == 0 ||
1.16 kleink 1069: ufs_rw32(dp->d_ino, UFS_IPNEEDSWAP(ip)) == WINO)
1.1 mycroft 1070: continue;
1071: /* accept only "." and ".." */
1.16 kleink 1072: #if (BYTE_ORDER == LITTLE_ENDIAN)
1073: if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0 ||
1.27 fvdl 1074: UFS_IPNEEDSWAP(ip) != 0)
1.16 kleink 1075: namlen = dp->d_namlen;
1076: else
1077: namlen = dp->d_type;
1078: #else
1.19 fvdl 1079: if (ITOV(ip)->v_mount->mnt_maxsymlinklen <= 0 &&
1.16 kleink 1080: UFS_IPNEEDSWAP(ip) != 0)
1081: namlen = dp->d_type;
1082: else
1083: namlen = dp->d_namlen;
1084: #endif
1.1 mycroft 1085: if (namlen > 2)
1086: return (0);
1087: if (dp->d_name[0] != '.')
1088: return (0);
1089: /*
1090: * At this point namlen must be 1 or 2.
1091: * 1 implies ".", 2 implies ".." if second
1092: * char is also "."
1093: */
1.27 fvdl 1094: if (namlen == 1 &&
1095: ufs_rw32(dp->d_ino, UFS_IPNEEDSWAP(ip)) == ip->i_number)
1.1 mycroft 1096: continue;
1.15 bouyer 1097: if (dp->d_name[1] == '.' &&
1.16 kleink 1098: ufs_rw32(dp->d_ino, UFS_IPNEEDSWAP(ip)) == parentino)
1.1 mycroft 1099: continue;
1100: return (0);
1101: }
1102: return (1);
1103: }
1104:
1105: /*
1106: * Check if source directory is in the path of the target directory.
1107: * Target is supplied locked, source is unlocked.
1108: * The target is always vput before returning.
1109: */
1110: int
1111: ufs_checkpath(source, target, cred)
1112: struct inode *source, *target;
1113: struct ucred *cred;
1114: {
1.27 fvdl 1115: struct vnode *vp = ITOV(target);
1.1 mycroft 1116: int error, rootino, namlen;
1117: struct dirtemplate dirbuf;
1.15 bouyer 1118: const int needswap = UFS_MPNEEDSWAP(vp->v_mount);
1.1 mycroft 1119:
1120: vp = ITOV(target);
1121: if (target->i_number == source->i_number) {
1122: error = EEXIST;
1123: goto out;
1124: }
1125: rootino = ROOTINO;
1126: error = 0;
1127: if (target->i_number == rootino)
1128: goto out;
1129:
1130: for (;;) {
1131: if (vp->v_type != VDIR) {
1132: error = ENOTDIR;
1133: break;
1134: }
1135: error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
1.27 fvdl 1136: sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
1137: IO_NODELOCKED, cred, NULL, (struct proc *)0);
1.1 mycroft 1138: if (error != 0)
1139: break;
1.16 kleink 1140: #if (BYTE_ORDER == LITTLE_ENDIAN)
1141: if (vp->v_mount->mnt_maxsymlinklen > 0 ||
1.27 fvdl 1142: needswap != 0)
1.16 kleink 1143: namlen = dirbuf.dotdot_namlen;
1144: else
1145: namlen = dirbuf.dotdot_type;
1146: #else
1.27 fvdl 1147: if (vp->v_mount->mnt_maxsymlinklen == 0 &&
1.16 kleink 1148: needswap != 0)
1149: namlen = dirbuf.dotdot_type;
1150: else
1.1 mycroft 1151: namlen = dirbuf.dotdot_namlen;
1.16 kleink 1152: #endif
1.1 mycroft 1153: if (namlen != 2 ||
1154: dirbuf.dotdot_name[0] != '.' ||
1155: dirbuf.dotdot_name[1] != '.') {
1156: error = ENOTDIR;
1157: break;
1158: }
1.15 bouyer 1159: if (ufs_rw32(dirbuf.dotdot_ino, needswap) == source->i_number) {
1.1 mycroft 1160: error = EINVAL;
1161: break;
1162: }
1.15 bouyer 1163: if (ufs_rw32(dirbuf.dotdot_ino, needswap) == rootino)
1.1 mycroft 1164: break;
1165: vput(vp);
1.15 bouyer 1166: error = VFS_VGET(vp->v_mount,
1.16 kleink 1167: ufs_rw32(dirbuf.dotdot_ino, needswap), &vp);
1.7 christos 1168: if (error) {
1.1 mycroft 1169: vp = NULL;
1170: break;
1171: }
1172: }
1173:
1174: out:
1175: if (error == ENOTDIR)
1.9 christos 1176: printf("checkpath: .. not a directory\n");
1.1 mycroft 1177: if (vp != NULL)
1178: vput(vp);
1179: return (error);
1180: }
CVSweb <webmaster@jp.NetBSD.org>