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