]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/ext2fs/ext2_lookup.c
Import tzdata 2018f
[FreeBSD/FreeBSD.git] / sys / fs / ext2fs / ext2_lookup.c
1 /*-
2  *  modified for Lites 1.1
3  *
4  *  Aug 1995, Godmar Back (gback@cs.utah.edu)
5  *  University of Utah, Department of Computer Science
6  */
7 /*-
8  * SPDX-License-Identifier: BSD-3-Clause
9  *
10  * Copyright (c) 1989, 1993
11  *      The Regents of the University of California.  All rights reserved.
12  * (c) UNIX System Laboratories, Inc.
13  * All or some portions of this file are derived from material licensed
14  * to the University of California by American Telephone and Telegraph
15  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
16  * the permission of UNIX System Laboratories, Inc.
17  *
18  * Redistribution and use in source and binary forms, with or without
19  * modification, are permitted provided that the following conditions
20  * are met:
21  * 1. Redistributions of source code must retain the above copyright
22  *    notice, this list of conditions and the following disclaimer.
23  * 2. Redistributions in binary form must reproduce the above copyright
24  *    notice, this list of conditions and the following disclaimer in the
25  *    documentation and/or other materials provided with the distribution.
26  * 3. Neither the name of the University nor the names of its contributors
27  *    may be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40  * SUCH DAMAGE.
41  *
42  *      @(#)ufs_lookup.c        8.6 (Berkeley) 4/1/94
43  * $FreeBSD$
44  */
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/namei.h>
49 #include <sys/bio.h>
50 #include <sys/buf.h>
51 #include <sys/endian.h>
52 #include <sys/mount.h>
53 #include <sys/vnode.h>
54 #include <sys/malloc.h>
55 #include <sys/dirent.h>
56 #include <sys/sysctl.h>
57
58 #include <ufs/ufs/dir.h>
59
60 #include <fs/ext2fs/fs.h>
61 #include <fs/ext2fs/inode.h>
62 #include <fs/ext2fs/ext2_mount.h>
63 #include <fs/ext2fs/ext2fs.h>
64 #include <fs/ext2fs/ext2_dinode.h>
65 #include <fs/ext2fs/ext2_dir.h>
66 #include <fs/ext2fs/ext2_extern.h>
67 #include <fs/ext2fs/fs.h>
68
69 #ifdef INVARIANTS
70 static int dirchk = 1;
71 #else
72 static int dirchk = 0;
73 #endif
74
75 static SYSCTL_NODE(_vfs, OID_AUTO, e2fs, CTLFLAG_RD, 0, "EXT2FS filesystem");
76 SYSCTL_INT(_vfs_e2fs, OID_AUTO, dircheck, CTLFLAG_RW, &dirchk, 0, "");
77
78 /*
79    DIRBLKSIZE in ffs is DEV_BSIZE (in most cases 512)
80    while it is the native blocksize in ext2fs - thus, a #define
81    is no longer appropriate
82 */
83 #undef  DIRBLKSIZ
84
85 static u_char ext2_ft_to_dt[] = {
86         DT_UNKNOWN,             /* EXT2_FT_UNKNOWN */
87         DT_REG,                 /* EXT2_FT_REG_FILE */
88         DT_DIR,                 /* EXT2_FT_DIR */
89         DT_CHR,                 /* EXT2_FT_CHRDEV */
90         DT_BLK,                 /* EXT2_FT_BLKDEV */
91         DT_FIFO,                /* EXT2_FT_FIFO */
92         DT_SOCK,                /* EXT2_FT_SOCK */
93         DT_LNK,                 /* EXT2_FT_SYMLINK */
94 };
95 #define FTTODT(ft) \
96     ((ft) < nitems(ext2_ft_to_dt) ? ext2_ft_to_dt[(ft)] : DT_UNKNOWN)
97
98 static u_char dt_to_ext2_ft[] = {
99         EXT2_FT_UNKNOWN,        /* DT_UNKNOWN */
100         EXT2_FT_FIFO,           /* DT_FIFO */
101         EXT2_FT_CHRDEV,         /* DT_CHR */
102         EXT2_FT_UNKNOWN,        /* unused */
103         EXT2_FT_DIR,            /* DT_DIR */
104         EXT2_FT_UNKNOWN,        /* unused */
105         EXT2_FT_BLKDEV,         /* DT_BLK */
106         EXT2_FT_UNKNOWN,        /* unused */
107         EXT2_FT_REG_FILE,       /* DT_REG */
108         EXT2_FT_UNKNOWN,        /* unused */
109         EXT2_FT_SYMLINK,        /* DT_LNK */
110         EXT2_FT_UNKNOWN,        /* unused */
111         EXT2_FT_SOCK,           /* DT_SOCK */
112         EXT2_FT_UNKNOWN,        /* unused */
113         EXT2_FT_UNKNOWN,        /* DT_WHT */
114 };
115 #define DTTOFT(dt) \
116     ((dt) < nitems(dt_to_ext2_ft) ? dt_to_ext2_ft[(dt)] : EXT2_FT_UNKNOWN)
117
118 static int      ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de,
119                     int entryoffsetinblock);
120 static int      ext2_is_dot_entry(struct componentname *cnp);
121 static int      ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp,
122                     struct componentname *cnp, ino_t *dd_ino);
123
124 static int
125 ext2_is_dot_entry(struct componentname *cnp)
126 {
127         if (cnp->cn_namelen <= 2 && cnp->cn_nameptr[0] == '.' &&
128             (cnp->cn_nameptr[1] == '.' || cnp->cn_nameptr[1] == '\0'))
129                 return (1);
130         return (0);
131 }
132
133 /*
134  * Vnode op for reading directories.
135  */
136 int
137 ext2_readdir(struct vop_readdir_args *ap)
138 {
139         struct vnode *vp = ap->a_vp;
140         struct uio *uio = ap->a_uio;
141         struct buf *bp;
142         struct inode *ip;
143         struct ext2fs_direct_2 *dp, *edp;
144         u_long *cookies;
145         struct dirent dstdp;
146         off_t offset, startoffset;
147         size_t readcnt, skipcnt;
148         ssize_t startresid;
149         u_int ncookies;
150         int DIRBLKSIZ = VTOI(ap->a_vp)->i_e2fs->e2fs_bsize;
151         int error;
152
153         if (uio->uio_offset < 0)
154                 return (EINVAL);
155         ip = VTOI(vp);
156         if (ap->a_ncookies != NULL) {
157                 if (uio->uio_resid < 0)
158                         ncookies = 0;
159                 else
160                         ncookies = uio->uio_resid;
161                 if (uio->uio_offset >= ip->i_size)
162                         ncookies = 0;
163                 else if (ip->i_size - uio->uio_offset < ncookies)
164                         ncookies = ip->i_size - uio->uio_offset;
165                 ncookies = ncookies / (offsetof(struct ext2fs_direct_2,
166                     e2d_namlen) + 4) + 1;
167                 cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK);
168                 *ap->a_ncookies = ncookies;
169                 *ap->a_cookies = cookies;
170         } else {
171                 ncookies = 0;
172                 cookies = NULL;
173         }
174         offset = startoffset = uio->uio_offset;
175         startresid = uio->uio_resid;
176         error = 0;
177         while (error == 0 && uio->uio_resid > 0 &&
178             uio->uio_offset < ip->i_size) {
179                 error = ext2_blkatoff(vp, uio->uio_offset, NULL, &bp);
180                 if (error)
181                         break;
182                 if (bp->b_offset + bp->b_bcount > ip->i_size)
183                         readcnt = ip->i_size - bp->b_offset;
184                 else
185                         readcnt = bp->b_bcount;
186                 skipcnt = (size_t)(uio->uio_offset - bp->b_offset) &
187                     ~(size_t)(DIRBLKSIZ - 1);
188                 offset = bp->b_offset + skipcnt;
189                 dp = (struct ext2fs_direct_2 *)&bp->b_data[skipcnt];
190                 edp = (struct ext2fs_direct_2 *)&bp->b_data[readcnt];
191                 while (error == 0 && uio->uio_resid > 0 && dp < edp) {
192                         if (dp->e2d_reclen <= offsetof(struct ext2fs_direct_2,
193                             e2d_namlen) || (caddr_t)dp + dp->e2d_reclen >
194                             (caddr_t)edp) {
195                                 error = EIO;
196                                 break;
197                         }
198                         /*-
199                          * "New" ext2fs directory entries differ in 3 ways
200                          * from ufs on-disk ones:
201                          * - the name is not necessarily NUL-terminated.
202                          * - the file type field always exists and always
203                          *   follows the name length field.
204                          * - the file type is encoded in a different way.
205                          *
206                          * "Old" ext2fs directory entries need no special
207                          * conversions, since they are binary compatible
208                          * with "new" entries having a file type of 0 (i.e.,
209                          * EXT2_FT_UNKNOWN).  Splitting the old name length
210                          * field didn't make a mess like it did in ufs,
211                          * because ext2fs uses a machine-independent disk
212                          * layout.
213                          */
214                         dstdp.d_namlen = dp->e2d_namlen;
215                         dstdp.d_type = FTTODT(dp->e2d_type);
216                         if (offsetof(struct ext2fs_direct_2, e2d_namlen) +
217                             dstdp.d_namlen > dp->e2d_reclen) {
218                                 error = EIO;
219                                 break;
220                         }
221                         if (offset < startoffset || dp->e2d_ino == 0)
222                                 goto nextentry;
223                         dstdp.d_fileno = dp->e2d_ino;
224                         dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
225                         bcopy(dp->e2d_name, dstdp.d_name, dstdp.d_namlen);
226                         dstdp.d_name[dstdp.d_namlen] = '\0';
227                         if (dstdp.d_reclen > uio->uio_resid) {
228                                 if (uio->uio_resid == startresid)
229                                         error = EINVAL;
230                                 else
231                                         error = EJUSTRETURN;
232                                 break;
233                         }
234                         /* Advance dp. */
235                         error = uiomove((caddr_t)&dstdp, dstdp.d_reclen, uio);
236                         if (error)
237                                 break;
238                         if (cookies != NULL) {
239                                 KASSERT(ncookies > 0,
240                                     ("ext2_readdir: cookies buffer too small"));
241                                 *cookies = offset + dp->e2d_reclen;
242                                 cookies++;
243                                 ncookies--;
244                         }
245 nextentry:
246                         offset += dp->e2d_reclen;
247                         dp = (struct ext2fs_direct_2 *)((caddr_t)dp +
248                             dp->e2d_reclen);
249                 }
250                 bqrelse(bp);
251                 uio->uio_offset = offset;
252         }
253         /* We need to correct uio_offset. */
254         uio->uio_offset = offset;
255         if (error == EJUSTRETURN)
256                 error = 0;
257         if (ap->a_ncookies != NULL) {
258                 if (error == 0) {
259                         ap->a_ncookies -= ncookies;
260                 } else {
261                         free(*ap->a_cookies, M_TEMP);
262                         *ap->a_ncookies = 0;
263                         *ap->a_cookies = NULL;
264                 }
265         }
266         if (error == 0 && ap->a_eofflag)
267                 *ap->a_eofflag = ip->i_size <= uio->uio_offset;
268         return (error);
269 }
270
271 /*
272  * Convert a component of a pathname into a pointer to a locked inode.
273  * This is a very central and rather complicated routine.
274  * If the file system is not maintained in a strict tree hierarchy,
275  * this can result in a deadlock situation (see comments in code below).
276  *
277  * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
278  * on whether the name is to be looked up, created, renamed, or deleted.
279  * When CREATE, RENAME, or DELETE is specified, information usable in
280  * creating, renaming, or deleting a directory entry may be calculated.
281  * If flag has LOCKPARENT or'ed into it and the target of the pathname
282  * exists, lookup returns both the target and its parent directory locked.
283  * When creating or renaming and LOCKPARENT is specified, the target may
284  * not be ".".  When deleting and LOCKPARENT is specified, the target may
285  * be "."., but the caller must check to ensure it does an vrele and vput
286  * instead of two vputs.
287  *
288  * Overall outline of ext2_lookup:
289  *
290  *      search for name in directory, to found or notfound
291  * notfound:
292  *      if creating, return locked directory, leaving info on available slots
293  *      else return error
294  * found:
295  *      if at end of path and deleting, return information to allow delete
296  *      if at end of path and rewriting (RENAME and LOCKPARENT), lock target
297  *        inode and return info to allow rewrite
298  *      if not at end, add name to cache; if at end and neither creating
299  *        nor deleting, add name to cache
300  */
301 int
302 ext2_lookup(struct vop_cachedlookup_args *ap)
303 {
304
305         return (ext2_lookup_ino(ap->a_dvp, ap->a_vpp, ap->a_cnp, NULL));
306 }
307
308 static int
309 ext2_lookup_ino(struct vnode *vdp, struct vnode **vpp, struct componentname *cnp,
310     ino_t *dd_ino)
311 {
312         struct inode *dp;               /* inode for directory being searched */
313         struct buf *bp;                 /* a buffer of directory entries */
314         struct ext2fs_direct_2 *ep;     /* the current directory entry */
315         int entryoffsetinblock;         /* offset of ep in bp's buffer */
316         struct ext2fs_searchslot ss;
317         doff_t i_diroff;                /* cached i_diroff value */
318         doff_t i_offset;                /* cached i_offset value */
319         int numdirpasses;               /* strategy for directory search */
320         doff_t endsearch;               /* offset to end directory search */
321         doff_t prevoff;                 /* prev entry dp->i_offset */
322         struct vnode *pdp;              /* saved dp during symlink work */
323         struct vnode *tdp;              /* returned by VFS_VGET */
324         doff_t enduseful;               /* pointer past last used dir slot */
325         u_long bmask;                   /* block offset mask */
326         int error;
327         struct ucred *cred = cnp->cn_cred;
328         int flags = cnp->cn_flags;
329         int nameiop = cnp->cn_nameiop;
330         ino_t ino, ino1;
331         int ltype;
332         int entry_found = 0;
333
334         int DIRBLKSIZ = VTOI(vdp)->i_e2fs->e2fs_bsize;
335
336         if (vpp != NULL)
337                 *vpp = NULL;
338
339         dp = VTOI(vdp);
340         bmask = VFSTOEXT2(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
341 restart:
342         bp = NULL;
343         ss.slotoffset = -1;
344
345         /*
346          * We now have a segment name to search for, and a directory to search.
347          *
348          * Suppress search for slots unless creating
349          * file and at end of pathname, in which case
350          * we watch for a place to put the new file in
351          * case it doesn't already exist.
352          */
353         i_diroff = dp->i_diroff;
354         ss.slotstatus = FOUND;
355         ss.slotfreespace = ss.slotsize = ss.slotneeded = 0;
356         if ((nameiop == CREATE || nameiop == RENAME) &&
357             (flags & ISLASTCN)) {
358                 ss.slotstatus = NONE;
359                 ss.slotneeded = EXT2_DIR_REC_LEN(cnp->cn_namelen);
360                 /*
361                  * was ss.slotneeded = (sizeof(struct direct) - MAXNAMLEN +
362                  * cnp->cn_namelen + 3) &~ 3;
363                  */
364         }
365         /*
366          * Try to lookup dir entry using htree directory index.
367          *
368          * If we got an error or we want to find '.' or '..' entry,
369          * we will fall back to linear search.
370          */
371         if (!ext2_is_dot_entry(cnp) && ext2_htree_has_idx(dp)) {
372                 numdirpasses = 1;
373                 entryoffsetinblock = 0;
374                 switch (ext2_htree_lookup(dp, cnp->cn_nameptr, cnp->cn_namelen,
375                     &bp, &entryoffsetinblock, &i_offset, &prevoff,
376                     &enduseful, &ss)) {
377                 case 0:
378                         ep = (struct ext2fs_direct_2 *)((char *)bp->b_data +
379                             (i_offset & bmask));
380                         goto foundentry;
381                 case ENOENT:
382                         i_offset = roundup2(dp->i_size, DIRBLKSIZ);
383                         goto notfound;
384                 default:
385                         /*
386                          * Something failed; just fallback to do a linear
387                          * search.
388                          */
389                         break;
390                 }
391         }
392
393         /*
394          * If there is cached information on a previous search of
395          * this directory, pick up where we last left off.
396          * We cache only lookups as these are the most common
397          * and have the greatest payoff. Caching CREATE has little
398          * benefit as it usually must search the entire directory
399          * to determine that the entry does not exist. Caching the
400          * location of the last DELETE or RENAME has not reduced
401          * profiling time and hence has been removed in the interest
402          * of simplicity.
403          */
404         if (nameiop != LOOKUP || i_diroff == 0 ||
405             i_diroff > dp->i_size) {
406                 entryoffsetinblock = 0;
407                 i_offset = 0;
408                 numdirpasses = 1;
409         } else {
410                 i_offset = i_diroff;
411                 if ((entryoffsetinblock = i_offset & bmask) &&
412                     (error = ext2_blkatoff(vdp, (off_t)i_offset, NULL,
413                     &bp)))
414                         return (error);
415                 numdirpasses = 2;
416                 nchstats.ncs_2passes++;
417         }
418         prevoff = i_offset;
419         endsearch = roundup2(dp->i_size, DIRBLKSIZ);
420         enduseful = 0;
421
422 searchloop:
423         while (i_offset < endsearch) {
424                 /*
425                  * If necessary, get the next directory block.
426                  */
427                 if (bp != NULL)
428                         brelse(bp);
429                 error = ext2_blkatoff(vdp, (off_t)i_offset, NULL, &bp);
430                 if (error != 0)
431                         return (error);
432
433                 entryoffsetinblock = 0;
434                 if (ss.slotstatus == NONE) {
435                         ss.slotoffset = -1;
436                         ss.slotfreespace = 0;
437                 }
438
439                 error = ext2_search_dirblock(dp, bp->b_data, &entry_found,
440                     cnp->cn_nameptr, cnp->cn_namelen,
441                     &entryoffsetinblock, &i_offset, &prevoff,
442                     &enduseful, &ss);
443                 if (error != 0) {
444                         brelse(bp);
445                         return (error);
446                 }
447                 if (entry_found) {
448                         ep = (struct ext2fs_direct_2 *)((char *)bp->b_data +
449                             (entryoffsetinblock & bmask));
450 foundentry:
451                         ino = ep->e2d_ino;
452                         goto found;
453                 }
454         }
455 notfound:
456         /*
457          * If we started in the middle of the directory and failed
458          * to find our target, we must check the beginning as well.
459          */
460         if (numdirpasses == 2) {
461                 numdirpasses--;
462                 i_offset = 0;
463                 endsearch = i_diroff;
464                 goto searchloop;
465         }
466         if (bp != NULL)
467                 brelse(bp);
468         /*
469          * If creating, and at end of pathname and current
470          * directory has not been removed, then can consider
471          * allowing file to be created.
472          */
473         if ((nameiop == CREATE || nameiop == RENAME) &&
474             (flags & ISLASTCN) && dp->i_nlink != 0) {
475                 /*
476                  * Access for write is interpreted as allowing
477                  * creation of files in the directory.
478                  */
479                 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
480                         return (error);
481                 /*
482                  * Return an indication of where the new directory
483                  * entry should be put.  If we didn't find a slot,
484                  * then set dp->i_count to 0 indicating
485                  * that the new slot belongs at the end of the
486                  * directory. If we found a slot, then the new entry
487                  * can be put in the range from dp->i_offset to
488                  * dp->i_offset + dp->i_count.
489                  */
490                 if (ss.slotstatus == NONE) {
491                         dp->i_offset = roundup2(dp->i_size, DIRBLKSIZ);
492                         dp->i_count = 0;
493                         enduseful = dp->i_offset;
494                 } else {
495                         dp->i_offset = ss.slotoffset;
496                         dp->i_count = ss.slotsize;
497                         if (enduseful < ss.slotoffset + ss.slotsize)
498                                 enduseful = ss.slotoffset + ss.slotsize;
499                 }
500                 dp->i_endoff = roundup2(enduseful, DIRBLKSIZ);
501                 /*
502                  * We return with the directory locked, so that
503                  * the parameters we set up above will still be
504                  * valid if we actually decide to do a direnter().
505                  * We return ni_vp == NULL to indicate that the entry
506                  * does not currently exist; we leave a pointer to
507                  * the (locked) directory inode in ndp->ni_dvp.
508                  * The pathname buffer is saved so that the name
509                  * can be obtained later.
510                  *
511                  * NB - if the directory is unlocked, then this
512                  * information cannot be used.
513                  */
514                 cnp->cn_flags |= SAVENAME;
515                 return (EJUSTRETURN);
516         }
517         /*
518          * Insert name into cache (as non-existent) if appropriate.
519          */
520         if ((cnp->cn_flags & MAKEENTRY) != 0)
521                 cache_enter(vdp, NULL, cnp);
522         return (ENOENT);
523
524 found:
525         if (dd_ino != NULL)
526                 *dd_ino = ino;
527         if (numdirpasses == 2)
528                 nchstats.ncs_pass2++;
529         /*
530          * Check that directory length properly reflects presence
531          * of this entry.
532          */
533         if (entryoffsetinblock + EXT2_DIR_REC_LEN(ep->e2d_namlen)
534             > dp->i_size) {
535                 ext2_dirbad(dp, i_offset, "i_size too small");
536                 dp->i_size = entryoffsetinblock + EXT2_DIR_REC_LEN(ep->e2d_namlen);
537                 dp->i_flag |= IN_CHANGE | IN_UPDATE;
538         }
539         brelse(bp);
540
541         /*
542          * Found component in pathname.
543          * If the final component of path name, save information
544          * in the cache as to where the entry was found.
545          */
546         if ((flags & ISLASTCN) && nameiop == LOOKUP)
547                 dp->i_diroff = rounddown2(i_offset, DIRBLKSIZ);
548         /*
549          * If deleting, and at end of pathname, return
550          * parameters which can be used to remove file.
551          */
552         if (nameiop == DELETE && (flags & ISLASTCN)) {
553                 if (flags & LOCKPARENT)
554                         ASSERT_VOP_ELOCKED(vdp, __FUNCTION__);
555                 /*
556                  * Write access to directory required to delete files.
557                  */
558                 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
559                         return (error);
560                 /*
561                  * Return pointer to current entry in dp->i_offset,
562                  * and distance past previous entry (if there
563                  * is a previous entry in this block) in dp->i_count.
564                  * Save directory inode pointer in ndp->ni_dvp for dirremove().
565                  *
566                  * Technically we shouldn't be setting these in the
567                  * WANTPARENT case (first lookup in rename()), but any
568                  * lookups that will result in directory changes will
569                  * overwrite these.
570                  */
571                 dp->i_offset = i_offset;
572                 if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
573                         dp->i_count = 0;
574                 else
575                         dp->i_count = dp->i_offset - prevoff;
576                 if (dd_ino != NULL)
577                         return (0);
578                 if (dp->i_number == ino) {
579                         VREF(vdp);
580                         *vpp = vdp;
581                         return (0);
582                 }
583                 if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE,
584                     &tdp)) != 0)
585                         return (error);
586                 /*
587                  * If directory is "sticky", then user must own
588                  * the directory, or the file in it, else she
589                  * may not delete it (unless she's root). This
590                  * implements append-only directories.
591                  */
592                 if ((dp->i_mode & ISVTX) &&
593                     cred->cr_uid != 0 &&
594                     cred->cr_uid != dp->i_uid &&
595                     VTOI(tdp)->i_uid != cred->cr_uid) {
596                         vput(tdp);
597                         return (EPERM);
598                 }
599                 *vpp = tdp;
600                 return (0);
601         }
602
603         /*
604          * If rewriting (RENAME), return the inode and the
605          * information required to rewrite the present directory
606          * Must get inode of directory entry to verify it's a
607          * regular file, or empty directory.
608          */
609         if (nameiop == RENAME && (flags & ISLASTCN)) {
610                 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread)) != 0)
611                         return (error);
612                 /*
613                  * Careful about locking second inode.
614                  * This can only occur if the target is ".".
615                  */
616                 dp->i_offset = i_offset;
617                 if (dp->i_number == ino)
618                         return (EISDIR);
619                 if (dd_ino != NULL)
620                         return (0);
621                 if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE,
622                     &tdp)) != 0)
623                         return (error);
624                 *vpp = tdp;
625                 cnp->cn_flags |= SAVENAME;
626                 return (0);
627         }
628         if (dd_ino != NULL)
629                 return (0);
630
631         /*
632          * Step through the translation in the name.  We do not `vput' the
633          * directory because we may need it again if a symbolic link
634          * is relative to the current directory.  Instead we save it
635          * unlocked as "pdp".  We must get the target inode before unlocking
636          * the directory to insure that the inode will not be removed
637          * before we get it.  We prevent deadlock by always fetching
638          * inodes from the root, moving down the directory tree. Thus
639          * when following backward pointers ".." we must unlock the
640          * parent directory before getting the requested directory.
641          * There is a potential race condition here if both the current
642          * and parent directories are removed before the VFS_VGET for the
643          * inode associated with ".." returns.  We hope that this occurs
644          * infrequently since we cannot avoid this race condition without
645          * implementing a sophisticated deadlock detection algorithm.
646          * Note also that this simple deadlock detection scheme will not
647          * work if the file system has any hard links other than ".."
648          * that point backwards in the directory structure.
649          */
650         pdp = vdp;
651         if (flags & ISDOTDOT) {
652                 error = vn_vget_ino(pdp, ino, cnp->cn_lkflags, &tdp);
653                 if (pdp->v_iflag & VI_DOOMED) {
654                         if (error == 0)
655                                 vput(tdp);
656                         error = ENOENT;
657                 }
658                 if (error)
659                         return (error);
660                 /*
661                  * Recheck that ".." entry in the vdp directory points
662                  * to the inode we looked up before vdp lock was
663                  * dropped.
664                  */
665                 error = ext2_lookup_ino(pdp, NULL, cnp, &ino1);
666                 if (error) {
667                         vput(tdp);
668                         return (error);
669                 }
670                 if (ino1 != ino) {
671                         vput(tdp);
672                         goto restart;
673                 }
674                 *vpp = tdp;
675         } else if (dp->i_number == ino) {
676                 VREF(vdp);      /* we want ourself, ie "." */
677                 /*
678                  * When we lookup "." we still can be asked to lock it
679                  * differently.
680                  */
681                 ltype = cnp->cn_lkflags & LK_TYPE_MASK;
682                 if (ltype != VOP_ISLOCKED(vdp)) {
683                         if (ltype == LK_EXCLUSIVE)
684                                 vn_lock(vdp, LK_UPGRADE | LK_RETRY);
685                         else    /* if (ltype == LK_SHARED) */
686                                 vn_lock(vdp, LK_DOWNGRADE | LK_RETRY);
687                 }
688                 *vpp = vdp;
689         } else {
690                 if ((error = VFS_VGET(vdp->v_mount, ino, cnp->cn_lkflags,
691                     &tdp)) != 0)
692                         return (error);
693                 *vpp = tdp;
694         }
695
696         /*
697          * Insert name into cache if appropriate.
698          */
699         if (cnp->cn_flags & MAKEENTRY)
700                 cache_enter(vdp, *vpp, cnp);
701         return (0);
702 }
703
704 int
705 ext2_search_dirblock(struct inode *ip, void *data, int *foundp,
706     const char *name, int namelen, int *entryoffsetinblockp,
707     doff_t *offp, doff_t *prevoffp, doff_t *endusefulp,
708     struct ext2fs_searchslot *ssp)
709 {
710         struct vnode *vdp;
711         struct ext2fs_direct_2 *ep, *top;
712         uint32_t bsize = ip->i_e2fs->e2fs_bsize;
713         int offset = *entryoffsetinblockp;
714         int namlen;
715
716         vdp = ITOV(ip);
717
718         ep = (struct ext2fs_direct_2 *)((char *)data + offset);
719         top = (struct ext2fs_direct_2 *)((char *)data + bsize);
720         while (ep < top) {
721                 /*
722                  * Full validation checks are slow, so we only check
723                  * enough to insure forward progress through the
724                  * directory. Complete checks can be run by setting
725                  * "vfs.e2fs.dirchk" to be true.
726                  */
727                 if (ep->e2d_reclen == 0 ||
728                     (dirchk && ext2_dirbadentry(vdp, ep, offset))) {
729                         int i;
730
731                         ext2_dirbad(ip, *offp, "mangled entry");
732                         i = bsize - (offset & (bsize - 1));
733                         *offp += i;
734                         offset += i;
735                         continue;
736                 }
737
738                 /*
739                  * If an appropriate sized slot has not yet been found,
740                  * check to see if one is available. Also accumulate space
741                  * in the current block so that we can determine if
742                  * compaction is viable.
743                  */
744                 if (ssp->slotstatus != FOUND) {
745                         int size = ep->e2d_reclen;
746
747                         if (ep->e2d_ino != 0)
748                                 size -= EXT2_DIR_REC_LEN(ep->e2d_namlen);
749                         else if (ext2_is_dirent_tail(ip, ep))
750                                 size -= sizeof(struct ext2fs_direct_tail);
751                         if (size > 0) {
752                                 if (size >= ssp->slotneeded) {
753                                         ssp->slotstatus = FOUND;
754                                         ssp->slotoffset = *offp;
755                                         ssp->slotsize = ep->e2d_reclen;
756                                 } else if (ssp->slotstatus == NONE) {
757                                         ssp->slotfreespace += size;
758                                         if (ssp->slotoffset == -1)
759                                                 ssp->slotoffset = *offp;
760                                         if (ssp->slotfreespace >= ssp->slotneeded) {
761                                                 ssp->slotstatus = COMPACT;
762                                                 ssp->slotsize = *offp +
763                                                     ep->e2d_reclen -
764                                                     ssp->slotoffset;
765                                         }
766                                 }
767                         }
768                 }
769                 /*
770                  * Check for a name match.
771                  */
772                 if (ep->e2d_ino) {
773                         namlen = ep->e2d_namlen;
774                         if (namlen == namelen &&
775                             !bcmp(name, ep->e2d_name, (unsigned)namlen)) {
776                                 /*
777                                  * Save directory entry's inode number and
778                                  * reclen in ndp->ni_ufs area, and release
779                                  * directory buffer.
780                                  */
781                                 *foundp = 1;
782                                 return (0);
783                         }
784                 }
785                 *prevoffp = *offp;
786                 *offp += ep->e2d_reclen;
787                 offset += ep->e2d_reclen;
788                 *entryoffsetinblockp = offset;
789                 if (ep->e2d_ino)
790                         *endusefulp = *offp;
791                 /*
792                  * Get pointer to the next entry.
793                  */
794                 ep = (struct ext2fs_direct_2 *)((char *)data + offset);
795         }
796
797         return (0);
798 }
799
800 void
801 ext2_dirbad(struct inode *ip, doff_t offset, char *how)
802 {
803         struct mount *mp;
804
805         mp = ITOV(ip)->v_mount;
806         if ((mp->mnt_flag & MNT_RDONLY) == 0)
807                 panic("ext2_dirbad: %s: bad dir ino %ju at offset %ld: %s\n",
808                     mp->mnt_stat.f_mntonname, (uintmax_t)ip->i_number,
809                     (long)offset, how);
810         else
811                 (void)printf("%s: bad dir ino %ju at offset %ld: %s\n",
812                     mp->mnt_stat.f_mntonname, (uintmax_t)ip->i_number,
813                     (long)offset, how);
814
815 }
816
817 /*
818  * Do consistency checking on a directory entry:
819  *      record length must be multiple of 4
820  *      entry must fit in rest of its DIRBLKSIZ block
821  *      record must be large enough to contain entry
822  *      name is not longer than MAXNAMLEN
823  *      name must be as long as advertised, and null terminated
824  */
825 /*
826  *      changed so that it confirms to ext2_check_dir_entry
827  */
828 static int
829 ext2_dirbadentry(struct vnode *dp, struct ext2fs_direct_2 *de,
830     int entryoffsetinblock)
831 {
832         int DIRBLKSIZ = VTOI(dp)->i_e2fs->e2fs_bsize;
833
834         char *error_msg = NULL;
835
836         if (de->e2d_reclen < EXT2_DIR_REC_LEN(1))
837                 error_msg = "rec_len is smaller than minimal";
838         else if (de->e2d_reclen % 4 != 0)
839                 error_msg = "rec_len % 4 != 0";
840         else if (de->e2d_reclen < EXT2_DIR_REC_LEN(de->e2d_namlen))
841                 error_msg = "reclen is too small for name_len";
842         else if (entryoffsetinblock + de->e2d_reclen > DIRBLKSIZ)
843                 error_msg = "directory entry across blocks";
844         /* else LATER
845              if (de->inode > dir->i_sb->u.ext2_sb.s_es->s_inodes_count)
846                 error_msg = "inode out of bounds";
847         */
848
849         if (error_msg != NULL) {
850                 printf("bad directory entry: %s\n", error_msg);
851                 printf("offset=%d, inode=%lu, rec_len=%u, name_len=%u\n",
852                         entryoffsetinblock, (unsigned long)de->e2d_ino,
853                         de->e2d_reclen, de->e2d_namlen);
854         }
855         return error_msg == NULL ? 0 : 1;
856 }
857
858 /*
859  * Insert an entry into the fresh directory block.
860  * Initialize entry tail if the metadata_csum feature is turned on.
861  */
862 static int
863 ext2_add_first_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry,
864     struct componentname *cnp)
865 {
866         struct inode *dp;
867         struct iovec aiov;
868         struct uio auio;
869         char* buf = NULL;
870         int dirblksize, error;
871
872         dp = VTOI(dvp);
873         dirblksize = dp->i_e2fs->e2fs_bsize;
874
875         if (dp->i_offset & (dirblksize - 1))
876                 panic("ext2_add_first_entry: bad directory offset");
877
878         if (EXT2_HAS_RO_COMPAT_FEATURE(dp->i_e2fs,
879             EXT2F_ROCOMPAT_METADATA_CKSUM)) {
880                 entry->e2d_reclen = dirblksize - sizeof(struct ext2fs_direct_tail);
881                 buf = malloc(dirblksize, M_TEMP, M_WAITOK);
882                 if (!buf) {
883                         error = ENOMEM;
884                         goto out;
885                 }
886                 memcpy(buf, entry, EXT2_DIR_REC_LEN(entry->e2d_namlen));
887                 ext2_init_dirent_tail(EXT2_DIRENT_TAIL(buf, dirblksize));
888                 ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)buf);
889
890                 auio.uio_offset = dp->i_offset;
891                 auio.uio_resid = dirblksize;
892                 aiov.iov_len = auio.uio_resid;
893                 aiov.iov_base = (caddr_t)buf;
894         } else {
895                 entry->e2d_reclen = dirblksize;
896                 auio.uio_offset = dp->i_offset;
897                 auio.uio_resid = EXT2_DIR_REC_LEN(entry->e2d_namlen);
898                 aiov.iov_len = auio.uio_resid;
899                 aiov.iov_base = (caddr_t)entry;
900         }
901
902         auio.uio_iov = &aiov;
903         auio.uio_iovcnt = 1;
904         auio.uio_rw = UIO_WRITE;
905         auio.uio_segflg = UIO_SYSSPACE;
906         auio.uio_td = (struct thread *)0;
907         error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
908         if (error)
909                 goto out;
910
911         dp->i_size = roundup2(dp->i_size, dirblksize);
912         dp->i_flag |= IN_CHANGE;
913
914 out:
915         free(buf, M_TEMP);
916         return (error);
917
918 }
919
920 /*
921  * Write a directory entry after a call to namei, using the parameters
922  * that it left in nameidata.  The argument ip is the inode which the new
923  * directory entry will refer to.  Dvp is a pointer to the directory to
924  * be written, which was left locked by namei. Remaining parameters
925  * (dp->i_offset, dp->i_count) indicate how the space for the new
926  * entry is to be obtained.
927  */
928 int
929 ext2_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp)
930 {
931         struct inode *dp;
932         struct ext2fs_direct_2 newdir;
933         int DIRBLKSIZ = ip->i_e2fs->e2fs_bsize;
934         int error;
935
936
937 #ifdef INVARIANTS
938         if ((cnp->cn_flags & SAVENAME) == 0)
939                 panic("ext2_direnter: missing name");
940 #endif
941         dp = VTOI(dvp);
942         newdir.e2d_ino = ip->i_number;
943         newdir.e2d_namlen = cnp->cn_namelen;
944         if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs,
945             EXT2F_INCOMPAT_FTYPE))
946                 newdir.e2d_type = DTTOFT(IFTODT(ip->i_mode));
947         else
948                 newdir.e2d_type = EXT2_FT_UNKNOWN;
949         bcopy(cnp->cn_nameptr, newdir.e2d_name, (unsigned)cnp->cn_namelen + 1);
950
951         if (ext2_htree_has_idx(dp)) {
952                 error = ext2_htree_add_entry(dvp, &newdir, cnp);
953                 if (error) {
954                         dp->i_flag &= ~IN_E3INDEX;
955                         dp->i_flag |= IN_CHANGE | IN_UPDATE;
956                 }
957                 return (error);
958         }
959
960         if (EXT2_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_DIRHASHINDEX) &&
961             !ext2_htree_has_idx(dp)) {
962                 if ((dp->i_size / DIRBLKSIZ) == 1 &&
963                     dp->i_offset == DIRBLKSIZ) {
964                         /*
965                          * Making indexed directory when one block is not
966                          * enough to save all entries.
967                          */
968                         return ext2_htree_create_index(dvp, cnp, &newdir);
969                 }
970         }
971
972         /*
973          * If dp->i_count is 0, then namei could find no
974          * space in the directory. Here, dp->i_offset will
975          * be on a directory block boundary and we will write the
976          * new entry into a fresh block.
977          */
978         if (dp->i_count == 0)
979                 return ext2_add_first_entry(dvp, &newdir, cnp);
980
981         error = ext2_add_entry(dvp, &newdir);
982         if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
983                 error = ext2_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC,
984                     cnp->cn_cred, cnp->cn_thread);
985         return (error);
986 }
987
988 /*
989  * Insert an entry into the directory block.
990  * Compact the contents.
991  */
992 int
993 ext2_add_entry(struct vnode *dvp, struct ext2fs_direct_2 *entry)
994 {
995         struct ext2fs_direct_2 *ep, *nep;
996         struct inode *dp;
997         struct buf *bp;
998         u_int dsize;
999         int error, loc, newentrysize, spacefree;
1000         char *dirbuf;
1001
1002         dp = VTOI(dvp);
1003
1004         /*
1005          * If dp->i_count is non-zero, then namei found space
1006          * for the new entry in the range dp->i_offset to
1007          * dp->i_offset + dp->i_count in the directory.
1008          * To use this space, we may have to compact the entries located
1009          * there, by copying them together towards the beginning of the
1010          * block, leaving the free space in one usable chunk at the end.
1011          */
1012
1013         /*
1014          * Increase size of directory if entry eats into new space.
1015          * This should never push the size past a new multiple of
1016          * DIRBLKSIZE.
1017          *
1018          * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
1019          */
1020         if (dp->i_offset + dp->i_count > dp->i_size)
1021                 dp->i_size = dp->i_offset + dp->i_count;
1022         /*
1023          * Get the block containing the space for the new directory entry.
1024          */
1025         if ((error = ext2_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf,
1026             &bp)) != 0)
1027                 return (error);
1028         /*
1029          * Find space for the new entry. In the simple case, the entry at
1030          * offset base will have the space. If it does not, then namei
1031          * arranged that compacting the region dp->i_offset to
1032          * dp->i_offset + dp->i_count would yield the
1033          * space.
1034          */
1035         newentrysize = EXT2_DIR_REC_LEN(entry->e2d_namlen);
1036         ep = (struct ext2fs_direct_2 *)dirbuf;
1037         dsize = EXT2_DIR_REC_LEN(ep->e2d_namlen);
1038         spacefree = ep->e2d_reclen - dsize;
1039         for (loc = ep->e2d_reclen; loc < dp->i_count; ) {
1040                 nep = (struct ext2fs_direct_2 *)(dirbuf + loc);
1041                 if (ep->e2d_ino) {
1042                         /* trim the existing slot */
1043                         ep->e2d_reclen = dsize;
1044                         ep = (struct ext2fs_direct_2 *)((char *)ep + dsize);
1045                 } else {
1046                         /* overwrite; nothing there; header is ours */
1047                         spacefree += dsize;
1048                 }
1049                 dsize = EXT2_DIR_REC_LEN(nep->e2d_namlen);
1050                 spacefree += nep->e2d_reclen - dsize;
1051                 loc += nep->e2d_reclen;
1052                 bcopy((caddr_t)nep, (caddr_t)ep, dsize);
1053         }
1054         /*
1055          * Update the pointer fields in the previous entry (if any),
1056          * copy in the new entry, and write out the block.
1057          */
1058         if (ep->e2d_ino == 0) {
1059                 if (spacefree + dsize < newentrysize)
1060                         panic("ext2_direnter: compact1");
1061                 entry->e2d_reclen = spacefree + dsize;
1062         } else {
1063                 if (spacefree < newentrysize)
1064                         panic("ext2_direnter: compact2");
1065                 entry->e2d_reclen = spacefree;
1066                 ep->e2d_reclen = dsize;
1067                 ep = (struct ext2fs_direct_2 *)((char *)ep + dsize);
1068         }
1069         bcopy((caddr_t)entry, (caddr_t)ep, (u_int)newentrysize);
1070         ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data);
1071         if (DOINGASYNC(dvp)) {
1072                 bdwrite(bp);
1073                 error = 0;
1074         } else {
1075                 error = bwrite(bp);
1076         }
1077         dp->i_flag |= IN_CHANGE | IN_UPDATE;
1078         return (error);
1079 }
1080
1081 /*
1082  * Remove a directory entry after a call to namei, using
1083  * the parameters which it left in nameidata. The entry
1084  * dp->i_offset contains the offset into the directory of the
1085  * entry to be eliminated.  The dp->i_count field contains the
1086  * size of the previous record in the directory.  If this
1087  * is 0, the first entry is being deleted, so we need only
1088  * zero the inode number to mark the entry as free.  If the
1089  * entry is not the first in the directory, we must reclaim
1090  * the space of the now empty record by adding the record size
1091  * to the size of the previous entry.
1092  */
1093 int
1094 ext2_dirremove(struct vnode *dvp, struct componentname *cnp)
1095 {
1096         struct inode *dp;
1097         struct ext2fs_direct_2 *ep, *rep;
1098         struct buf *bp;
1099         int error;
1100
1101         dp = VTOI(dvp);
1102         if (dp->i_count == 0) {
1103                 /*
1104                  * First entry in block: set d_ino to zero.
1105                  */
1106                 if ((error =
1107                     ext2_blkatoff(dvp, (off_t)dp->i_offset, (char **)&ep,
1108                     &bp)) != 0)
1109                         return (error);
1110                 ep->e2d_ino = 0;
1111                 ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data);
1112                 error = bwrite(bp);
1113                 dp->i_flag |= IN_CHANGE | IN_UPDATE;
1114                 return (error);
1115         }
1116         /*
1117          * Collapse new free space into previous entry.
1118          */
1119         if ((error = ext2_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count),
1120             (char **)&ep, &bp)) != 0)
1121                 return (error);
1122
1123         /* Set 'rep' to the entry being removed. */
1124         if (dp->i_count == 0)
1125                 rep = ep;
1126         else
1127                 rep = (struct ext2fs_direct_2 *)((char *)ep + ep->e2d_reclen);
1128         ep->e2d_reclen += rep->e2d_reclen;
1129         ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data);
1130         if (DOINGASYNC(dvp) && dp->i_count != 0)
1131                 bdwrite(bp);
1132         else
1133                 error = bwrite(bp);
1134         dp->i_flag |= IN_CHANGE | IN_UPDATE;
1135         return (error);
1136 }
1137
1138 /*
1139  * Rewrite an existing directory entry to point at the inode
1140  * supplied.  The parameters describing the directory entry are
1141  * set up by a call to namei.
1142  */
1143 int
1144 ext2_dirrewrite(struct inode *dp, struct inode *ip, struct componentname *cnp)
1145 {
1146         struct buf *bp;
1147         struct ext2fs_direct_2 *ep;
1148         struct vnode *vdp = ITOV(dp);
1149         int error;
1150
1151         if ((error = ext2_blkatoff(vdp, (off_t)dp->i_offset, (char **)&ep,
1152             &bp)) != 0)
1153                 return (error);
1154         ep->e2d_ino = ip->i_number;
1155         if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs,
1156             EXT2F_INCOMPAT_FTYPE))
1157                 ep->e2d_type = DTTOFT(IFTODT(ip->i_mode));
1158         else
1159                 ep->e2d_type = EXT2_FT_UNKNOWN;
1160         ext2_dirent_csum_set(dp, (struct ext2fs_direct_2 *)bp->b_data);
1161         error = bwrite(bp);
1162         dp->i_flag |= IN_CHANGE | IN_UPDATE;
1163         return (error);
1164 }
1165
1166 /*
1167  * Check if a directory is empty or not.
1168  * Inode supplied must be locked.
1169  *
1170  * Using a struct dirtemplate here is not precisely
1171  * what we want, but better than using a struct direct.
1172  *
1173  * NB: does not handle corrupted directories.
1174  */
1175 int
1176 ext2_dirempty(struct inode *ip, ino_t parentino, struct ucred *cred)
1177 {
1178         off_t off;
1179         struct dirtemplate dbuf;
1180         struct ext2fs_direct_2 *dp = (struct ext2fs_direct_2 *)&dbuf;
1181         int error, namlen;
1182         ssize_t count;
1183 #define MINDIRSIZ (sizeof(struct dirtemplate) / 2)
1184
1185         for (off = 0; off < ip->i_size; off += dp->e2d_reclen) {
1186                 error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ,
1187                     off, UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK, cred,
1188                     NOCRED, &count, (struct thread *)0);
1189                 /*
1190                  * Since we read MINDIRSIZ, residual must
1191                  * be 0 unless we're at end of file.
1192                  */
1193                 if (error || count != 0)
1194                         return (0);
1195                 /* avoid infinite loops */
1196                 if (dp->e2d_reclen == 0)
1197                         return (0);
1198                 /* skip empty entries */
1199                 if (dp->e2d_ino == 0)
1200                         continue;
1201                 /* accept only "." and ".." */
1202                 namlen = dp->e2d_namlen;
1203                 if (namlen > 2)
1204                         return (0);
1205                 if (dp->e2d_name[0] != '.')
1206                         return (0);
1207                 /*
1208                  * At this point namlen must be 1 or 2.
1209                  * 1 implies ".", 2 implies ".." if second
1210                  * char is also "."
1211                  */
1212                 if (namlen == 1)
1213                         continue;
1214                 if (dp->e2d_name[1] == '.' && dp->e2d_ino == parentino)
1215                         continue;
1216                 return (0);
1217         }
1218         return (1);
1219 }
1220
1221 /*
1222  * Check if source directory is in the path of the target directory.
1223  * Target is supplied locked, source is unlocked.
1224  * The target is always vput before returning.
1225  */
1226 int
1227 ext2_checkpath(struct inode *source, struct inode *target, struct ucred *cred)
1228 {
1229         struct vnode *vp;
1230         int error, namlen;
1231         struct dirtemplate dirbuf;
1232
1233         vp = ITOV(target);
1234         if (target->i_number == source->i_number) {
1235                 error = EEXIST;
1236                 goto out;
1237         }
1238         if (target->i_number == EXT2_ROOTINO) {
1239                 error = 0;
1240                 goto out;
1241         }
1242
1243         for (;;) {
1244                 if (vp->v_type != VDIR) {
1245                         error = ENOTDIR;
1246                         break;
1247                 }
1248                 error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
1249                     sizeof(struct dirtemplate), (off_t)0, UIO_SYSSPACE,
1250                     IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, NULL,
1251                     NULL);
1252                 if (error != 0)
1253                         break;
1254                 namlen = dirbuf.dotdot_type;    /* like ufs little-endian */
1255                 if (namlen != 2 ||
1256                     dirbuf.dotdot_name[0] != '.' ||
1257                     dirbuf.dotdot_name[1] != '.') {
1258                         error = ENOTDIR;
1259                         break;
1260                 }
1261                 if (dirbuf.dotdot_ino == source->i_number) {
1262                         error = EINVAL;
1263                         break;
1264                 }
1265                 if (dirbuf.dotdot_ino == EXT2_ROOTINO)
1266                         break;
1267                 vput(vp);
1268                 if ((error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino,
1269                     LK_EXCLUSIVE, &vp)) != 0) {
1270                         vp = NULL;
1271                         break;
1272                 }
1273         }
1274
1275 out:
1276         if (error == ENOTDIR)
1277                 printf("checkpath: .. not a directory\n");
1278         if (vp != NULL)
1279                 vput(vp);
1280         return (error);
1281 }