ext2fs_lookup.c revision 1.48 1 /* $NetBSD: ext2fs_lookup.c,v 1.48 2007/01/04 16:55:29 elad Exp $ */
2
3 /*
4 * Modified for NetBSD 1.2E
5 * May 1997, Manuel Bouyer
6 * Laboratoire d'informatique de Paris VI
7 */
8 /*
9 * modified for Lites 1.1
10 *
11 * Aug 1995, Godmar Back (gback (at) cs.utah.edu)
12 * University of Utah, Department of Computer Science
13 */
14 /*
15 * Copyright (c) 1989, 1993
16 * The Regents of the University of California. All rights reserved.
17 * (c) UNIX System Laboratories, Inc.
18 * All or some portions of this file are derived from material licensed
19 * to the University of California by American Telephone and Telegraph
20 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
21 * the permission of UNIX System Laboratories, Inc.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
25 * are met:
26 * 1. Redistributions of source code must retain the above copyright
27 * notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 * notice, this list of conditions and the following disclaimer in the
30 * documentation and/or other materials provided with the distribution.
31 * 3. Neither the name of the University nor the names of its contributors
32 * may be used to endorse or promote products derived from this software
33 * without specific prior written permission.
34 *
35 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
36 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
39 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 * SUCH DAMAGE.
46 *
47 * @(#)ufs_lookup.c 8.6 (Berkeley) 4/1/94
48 */
49
50 #include <sys/cdefs.h>
51 __KERNEL_RCSID(0, "$NetBSD: ext2fs_lookup.c,v 1.48 2007/01/04 16:55:29 elad Exp $");
52
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/namei.h>
56 #include <sys/buf.h>
57 #include <sys/file.h>
58 #include <sys/mount.h>
59 #include <sys/vnode.h>
60 #include <sys/malloc.h>
61 #include <sys/dirent.h>
62 #include <sys/kauth.h>
63
64 #include <ufs/ufs/inode.h>
65 #include <ufs/ufs/ufsmount.h>
66 #include <ufs/ufs/ufs_extern.h>
67
68 #include <ufs/ext2fs/ext2fs_extern.h>
69 #include <ufs/ext2fs/ext2fs_dir.h>
70 #include <ufs/ext2fs/ext2fs.h>
71
72 extern int dirchk;
73
74 static void ext2fs_dirconv2ffs(struct ext2fs_direct *e2dir,
75 struct dirent *ffsdir);
76 static int ext2fs_dirbadentry(struct vnode *dp,
77 struct ext2fs_direct *de,
78 int entryoffsetinblock);
79
80 /*
81 * the problem that is tackled below is the fact that FFS
82 * includes the terminating zero on disk while EXT2FS doesn't
83 * this implies that we need to introduce some padding.
84 * For instance, a filename "sbin" has normally a reclen 12
85 * in EXT2, but 16 in FFS.
86 * This reminds me of that Pepsi commercial: 'Kid saved a lousy nine cents...'
87 * If it wasn't for that, the complete ufs code for directories would
88 * have worked w/o changes (except for the difference in DIRBLKSIZ)
89 */
90 static void
91 ext2fs_dirconv2ffs(struct ext2fs_direct *e2dir, struct dirent *ffsdir)
92 {
93 memset(ffsdir, 0, sizeof(struct dirent));
94 ffsdir->d_fileno = fs2h32(e2dir->e2d_ino);
95 ffsdir->d_namlen = e2dir->e2d_namlen;
96
97 ffsdir->d_type = DT_UNKNOWN; /* don't know more here */
98 #ifdef DIAGNOSTIC
99 #if MAXNAMLEN < E2FS_MAXNAMLEN
100 /*
101 * we should handle this more gracefully !
102 */
103 if (e2dir->e2d_namlen > MAXNAMLEN)
104 panic("ext2fs: e2dir->e2d_namlen");
105 #endif
106 #endif
107 strncpy(ffsdir->d_name, e2dir->e2d_name, ffsdir->d_namlen);
108
109 /* Godmar thinks: since e2dir->e2d_reclen can be big and means
110 nothing anyway, we compute our own reclen according to what
111 we think is right
112 */
113 ffsdir->d_reclen = _DIRENT_SIZE(ffsdir);
114 }
115
116 /*
117 * Vnode op for reading directories.
118 *
119 * Convert the on-disk entries to <sys/dirent.h> entries.
120 * the problem is that the conversion will blow up some entries by four bytes,
121 * so it can't be done in place. This is too bad. Right now the conversion is
122 * done entry by entry, the converted entry is sent via uiomove.
123 *
124 * XXX allocate a buffer, convert as many entries as possible, then send
125 * the whole buffer to uiomove
126 */
127 int
128 ext2fs_readdir(void *v)
129 {
130 struct vop_readdir_args /* {
131 struct vnode *a_vp;
132 struct uio *a_uio;
133 kauth_cred_t a_cred;
134 int **a_eofflag;
135 off_t **a_cookies;
136 int ncookies;
137 } */ *ap = v;
138 struct uio *uio = ap->a_uio;
139 int error;
140 size_t e2fs_count, readcnt;
141 struct vnode *vp = ap->a_vp;
142 struct m_ext2fs *fs = VTOI(vp)->i_e2fs;
143
144 struct ext2fs_direct *dp;
145 struct dirent dstd;
146 struct uio auio;
147 struct iovec aiov;
148 caddr_t dirbuf;
149 off_t off = uio->uio_offset;
150 off_t *cookies = NULL;
151 int nc = 0, ncookies = 0;
152 int e2d_reclen;
153
154 if (vp->v_type != VDIR)
155 return (ENOTDIR);
156
157 e2fs_count = uio->uio_resid;
158 /* Make sure we don't return partial entries. */
159 e2fs_count -= (uio->uio_offset + e2fs_count) & (fs->e2fs_bsize -1);
160 if (e2fs_count <= 0)
161 return (EINVAL);
162
163 auio = *uio;
164 auio.uio_iov = &aiov;
165 auio.uio_iovcnt = 1;
166 aiov.iov_len = e2fs_count;
167 auio.uio_resid = e2fs_count;
168 UIO_SETUP_SYSSPACE(&auio);
169 dirbuf = malloc(e2fs_count, M_TEMP, M_WAITOK);
170 if (ap->a_ncookies) {
171 nc = ncookies = e2fs_count / 16;
172 cookies = malloc(sizeof (off_t) * ncookies, M_TEMP, M_WAITOK);
173 *ap->a_cookies = cookies;
174 }
175 memset(dirbuf, 0, e2fs_count);
176 aiov.iov_base = dirbuf;
177
178 error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
179 if (error == 0) {
180 readcnt = e2fs_count - auio.uio_resid;
181 for (dp = (struct ext2fs_direct *)dirbuf;
182 (char *)dp < (char *)dirbuf + readcnt; ) {
183 e2d_reclen = fs2h16(dp->e2d_reclen);
184 if (e2d_reclen == 0) {
185 error = EIO;
186 break;
187 }
188 ext2fs_dirconv2ffs(dp, &dstd);
189 if(dstd.d_reclen > uio->uio_resid) {
190 break;
191 }
192 if ((error = uiomove((caddr_t)&dstd, dstd.d_reclen, uio)) != 0) {
193 break;
194 }
195 off = off + e2d_reclen;
196 if (cookies != NULL) {
197 *cookies++ = off;
198 if (--ncookies <= 0){
199 break; /* out of cookies */
200 }
201 }
202 /* advance dp */
203 dp = (struct ext2fs_direct *) ((char *)dp + e2d_reclen);
204 }
205 /* we need to correct uio_offset */
206 uio->uio_offset = off;
207 }
208 FREE(dirbuf, M_TEMP);
209 *ap->a_eofflag = ext2fs_size(VTOI(ap->a_vp)) <= uio->uio_offset;
210 if (ap->a_ncookies) {
211 if (error) {
212 free(*ap->a_cookies, M_TEMP);
213 *ap->a_ncookies = 0;
214 *ap->a_cookies = NULL;
215 } else
216 *ap->a_ncookies = nc - ncookies;
217 }
218 return (error);
219 }
220
221 /*
222 * Convert a component of a pathname into a pointer to a locked inode.
223 * This is a very central and rather complicated routine.
224 * If the file system is not maintained in a strict tree hierarchy,
225 * this can result in a deadlock situation (see comments in code below).
226 *
227 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
228 * on whether the name is to be looked up, created, renamed, or deleted.
229 * When CREATE, RENAME, or DELETE is specified, information usable in
230 * creating, renaming, or deleting a directory entry may be calculated.
231 * If flag has LOCKPARENT or'ed into it and the target of the pathname
232 * exists, lookup returns both the target and its parent directory locked.
233 * When creating or renaming and LOCKPARENT is specified, the target may
234 * not be ".". When deleting and LOCKPARENT is specified, the target may
235 * be "."., but the caller must check to ensure it does an vrele and vput
236 * instead of two vputs.
237 *
238 * Overall outline of ext2fs_lookup:
239 *
240 * check accessibility of directory
241 * look for name in cache, if found, then if at end of path
242 * and deleting or creating, drop it, else return name
243 * search for name in directory, to found or notfound
244 * notfound:
245 * if creating, return locked directory, leaving info on available slots
246 * else return error
247 * found:
248 * if at end of path and deleting, return information to allow delete
249 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
250 * inode and return info to allow rewrite
251 * if not at end, add name to cache; if at end and neither creating
252 * nor deleting, add name to cache
253 */
254 int
255 ext2fs_lookup(void *v)
256 {
257 struct vop_lookup_args /* {
258 struct vnode *a_dvp;
259 struct vnode **a_vpp;
260 struct componentname *a_cnp;
261 } */ *ap = v;
262 struct vnode *vdp = ap->a_dvp; /* vnode for directory being searched */
263 struct inode *dp = VTOI(vdp); /* inode for directory being searched */
264 struct buf *bp; /* a buffer of directory entries */
265 struct ext2fs_direct *ep; /* the current directory entry */
266 int entryoffsetinblock; /* offset of ep in bp's buffer */
267 enum {NONE, COMPACT, FOUND} slotstatus;
268 doff_t slotoffset; /* offset of area with free space */
269 int slotsize; /* size of area at slotoffset */
270 int slotfreespace; /* amount of space free in slot */
271 int slotneeded; /* size of the entry we're seeking */
272 int numdirpasses; /* strategy for directory search */
273 doff_t endsearch; /* offset to end directory search */
274 doff_t prevoff; /* prev entry dp->i_offset */
275 struct vnode *pdp; /* saved dp during symlink work */
276 struct vnode *tdp; /* returned by VFS_VGET */
277 doff_t enduseful; /* pointer past last used dir slot */
278 u_long bmask; /* block offset mask */
279 int namlen, error;
280 struct vnode **vpp = ap->a_vpp;
281 struct componentname *cnp = ap->a_cnp;
282 kauth_cred_t cred = cnp->cn_cred;
283 int flags;
284 int nameiop = cnp->cn_nameiop;
285 struct ufsmount *ump = dp->i_ump;
286 int dirblksiz = ump->um_dirblksiz;
287 ino_t foundino;
288
289 flags = cnp->cn_flags;
290
291 bp = NULL;
292 slotoffset = -1;
293 *vpp = NULL;
294
295 /*
296 * Check accessiblity of directory.
297 */
298 if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_lwp)) != 0)
299 return (error);
300
301 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
302 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
303 return (EROFS);
304
305 /*
306 * We now have a segment name to search for, and a directory to search.
307 *
308 * Before tediously performing a linear scan of the directory,
309 * check the name cache to see if the directory/name pair
310 * we are looking for is known already.
311 */
312 if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
313 return (error);
314
315 /*
316 * Suppress search for slots unless creating
317 * file and at end of pathname, in which case
318 * we watch for a place to put the new file in
319 * case it doesn't already exist.
320 */
321 slotstatus = FOUND;
322 slotfreespace = slotsize = slotneeded = 0;
323 if ((nameiop == CREATE || nameiop == RENAME) &&
324 (flags & ISLASTCN)) {
325 slotstatus = NONE;
326 slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen);
327 }
328
329 /*
330 * If there is cached information on a previous search of
331 * this directory, pick up where we last left off.
332 * We cache only lookups as these are the most common
333 * and have the greatest payoff. Caching CREATE has little
334 * benefit as it usually must search the entire directory
335 * to determine that the entry does not exist. Caching the
336 * location of the last DELETE or RENAME has not reduced
337 * profiling time and hence has been removed in the interest
338 * of simplicity.
339 */
340 bmask = vdp->v_mount->mnt_stat.f_iosize - 1;
341 if (nameiop != LOOKUP || dp->i_diroff == 0 ||
342 dp->i_diroff >= ext2fs_size(dp)) {
343 entryoffsetinblock = 0;
344 dp->i_offset = 0;
345 numdirpasses = 1;
346 } else {
347 dp->i_offset = dp->i_diroff;
348 if ((entryoffsetinblock = dp->i_offset & bmask) &&
349 (error = ext2fs_blkatoff(vdp, (off_t)dp->i_offset, NULL, &bp)))
350 return (error);
351 numdirpasses = 2;
352 nchstats.ncs_2passes++;
353 }
354 prevoff = dp->i_offset;
355 endsearch = roundup(ext2fs_size(dp), dirblksiz);
356 enduseful = 0;
357
358 searchloop:
359 while (dp->i_offset < endsearch) {
360 if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
361 preempt(1);
362 /*
363 * If necessary, get the next directory block.
364 */
365 if ((dp->i_offset & bmask) == 0) {
366 if (bp != NULL)
367 brelse(bp);
368 error = ext2fs_blkatoff(vdp, (off_t)dp->i_offset, NULL,
369 &bp);
370 if (error != 0)
371 return (error);
372 entryoffsetinblock = 0;
373 }
374 /*
375 * If still looking for a slot, and at a dirblksize
376 * boundary, have to start looking for free space again.
377 */
378 if (slotstatus == NONE &&
379 (entryoffsetinblock & (dirblksiz - 1)) == 0) {
380 slotoffset = -1;
381 slotfreespace = 0;
382 }
383 /*
384 * Get pointer to next entry.
385 * Full validation checks are slow, so we only check
386 * enough to insure forward progress through the
387 * directory. Complete checks can be run by patching
388 * "dirchk" to be true.
389 */
390 KASSERT(bp != NULL);
391 ep = (struct ext2fs_direct *)
392 ((char *)bp->b_data + entryoffsetinblock);
393 if (ep->e2d_reclen == 0 ||
394 (dirchk &&
395 ext2fs_dirbadentry(vdp, ep, entryoffsetinblock))) {
396 int i;
397
398 ufs_dirbad(dp, dp->i_offset, "mangled entry");
399 i = dirblksiz - (entryoffsetinblock & (dirblksiz - 1));
400 dp->i_offset += i;
401 entryoffsetinblock += i;
402 continue;
403 }
404
405 /*
406 * If an appropriate sized slot has not yet been found,
407 * check to see if one is available. Also accumulate space
408 * in the current block so that we can determine if
409 * compaction is viable.
410 */
411 if (slotstatus != FOUND) {
412 int size = fs2h16(ep->e2d_reclen);
413
414 if (ep->e2d_ino != 0)
415 size -= EXT2FS_DIRSIZ(ep->e2d_namlen);
416 if (size > 0) {
417 if (size >= slotneeded) {
418 slotstatus = FOUND;
419 slotoffset = dp->i_offset;
420 slotsize = fs2h16(ep->e2d_reclen);
421 } else if (slotstatus == NONE) {
422 slotfreespace += size;
423 if (slotoffset == -1)
424 slotoffset = dp->i_offset;
425 if (slotfreespace >= slotneeded) {
426 slotstatus = COMPACT;
427 slotsize = dp->i_offset +
428 fs2h16(ep->e2d_reclen) -
429 slotoffset;
430 }
431 }
432 }
433 }
434
435 /*
436 * Check for a name match.
437 */
438 if (ep->e2d_ino) {
439 namlen = ep->e2d_namlen;
440 if (namlen == cnp->cn_namelen &&
441 !memcmp(cnp->cn_nameptr, ep->e2d_name,
442 (unsigned)namlen)) {
443 /*
444 * Save directory entry's inode number and
445 * reclen in ndp->ni_ufs area, and release
446 * directory buffer.
447 */
448 foundino = fs2h32(ep->e2d_ino);
449 dp->i_reclen = fs2h16(ep->e2d_reclen);
450 goto found;
451 }
452 }
453 prevoff = dp->i_offset;
454 dp->i_offset += fs2h16(ep->e2d_reclen);
455 entryoffsetinblock += fs2h16(ep->e2d_reclen);
456 if (ep->e2d_ino)
457 enduseful = dp->i_offset;
458 }
459 /* notfound: */
460 /*
461 * If we started in the middle of the directory and failed
462 * to find our target, we must check the beginning as well.
463 */
464 if (numdirpasses == 2) {
465 numdirpasses--;
466 dp->i_offset = 0;
467 endsearch = dp->i_diroff;
468 goto searchloop;
469 }
470 if (bp != NULL)
471 brelse(bp);
472 /*
473 * If creating, and at end of pathname and current
474 * directory has not been removed, then can consider
475 * allowing file to be created.
476 */
477 if ((nameiop == CREATE || nameiop == RENAME) &&
478 (flags & ISLASTCN) && dp->i_e2fs_nlink != 0) {
479 /*
480 * Access for write is interpreted as allowing
481 * creation of files in the directory.
482 */
483 error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_lwp);
484 if (error)
485 return (error);
486 /*
487 * Return an indication of where the new directory
488 * entry should be put. If we didn't find a slot,
489 * then set dp->i_count to 0 indicating
490 * that the new slot belongs at the end of the
491 * directory. If we found a slot, then the new entry
492 * can be put in the range from dp->i_offset to
493 * dp->i_offset + dp->i_count.
494 */
495 if (slotstatus == NONE) {
496 dp->i_offset = roundup(ext2fs_size(dp), dirblksiz);
497 dp->i_count = 0;
498 enduseful = dp->i_offset;
499 } else {
500 dp->i_offset = slotoffset;
501 dp->i_count = slotsize;
502 if (enduseful < slotoffset + slotsize)
503 enduseful = slotoffset + slotsize;
504 }
505 dp->i_endoff = roundup(enduseful, dirblksiz);
506 #if 0
507 dp->i_flag |= IN_CHANGE | IN_UPDATE;
508 #endif
509 /*
510 * We return with the directory locked, so that
511 * the parameters we set up above will still be
512 * valid if we actually decide to do a direnter().
513 * We return ni_vp == NULL to indicate that the entry
514 * does not currently exist; we leave a pointer to
515 * the (locked) directory inode in ndp->ni_dvp.
516 * The pathname buffer is saved so that the name
517 * can be obtained later.
518 *
519 * NB - if the directory is unlocked, then this
520 * information cannot be used.
521 */
522 cnp->cn_flags |= SAVENAME;
523 return (EJUSTRETURN);
524 }
525 /*
526 * Insert name into cache (as non-existent) if appropriate.
527 */
528 if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
529 cache_enter(vdp, *vpp, cnp);
530 return (ENOENT);
531
532 found:
533 if (numdirpasses == 2)
534 nchstats.ncs_pass2++;
535 /*
536 * Check that directory length properly reflects presence
537 * of this entry.
538 */
539 if (dp->i_offset + EXT2FS_DIRSIZ(ep->e2d_namlen) > ext2fs_size(dp)) {
540 ufs_dirbad(dp, dp->i_offset, "i_size too small");
541 error = ext2fs_setsize(dp,
542 dp->i_offset + EXT2FS_DIRSIZ(ep->e2d_namlen));
543 if (error) {
544 brelse(bp);
545 return (error);
546 }
547 dp->i_flag |= IN_CHANGE | IN_UPDATE;
548 uvm_vnp_setsize(vdp, ext2fs_size(dp));
549 }
550 brelse(bp);
551
552 /*
553 * Found component in pathname.
554 * If the final component of path name, save information
555 * in the cache as to where the entry was found.
556 */
557 if ((flags & ISLASTCN) && nameiop == LOOKUP)
558 dp->i_diroff = dp->i_offset &~ (dirblksiz - 1);
559
560 /*
561 * If deleting, and at end of pathname, return
562 * parameters which can be used to remove file.
563 * Lock the inode, being careful with ".".
564 */
565 if (nameiop == DELETE && (flags & ISLASTCN)) {
566 /*
567 * Write access to directory required to delete files.
568 */
569 if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_lwp)) != 0)
570 return (error);
571 /*
572 * Return pointer to current entry in dp->i_offset,
573 * and distance past previous entry (if there
574 * is a previous entry in this block) in dp->i_count.
575 * Save directory inode pointer in ndp->ni_dvp for dirremove().
576 */
577 if ((dp->i_offset & (dirblksiz - 1)) == 0)
578 dp->i_count = 0;
579 else
580 dp->i_count = dp->i_offset - prevoff;
581 if (dp->i_number == foundino) {
582 VREF(vdp);
583 *vpp = vdp;
584 return (0);
585 }
586 if (flags & ISDOTDOT)
587 VOP_UNLOCK(vdp, 0); /* race to get the inode */
588 error = VFS_VGET(vdp->v_mount, foundino, &tdp);
589 if (flags & ISDOTDOT)
590 vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY);
591 if (error)
592 return (error);
593 /*
594 * If directory is "sticky", then user must own
595 * the directory, or the file in it, else she
596 * may not delete it (unless she's root). This
597 * implements append-only directories.
598 */
599 if ((dp->i_e2fs_mode & ISVTX) &&
600 kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER, NULL) &&
601 kauth_cred_geteuid(cred) != dp->i_e2fs_uid &&
602 VTOI(tdp)->i_e2fs_uid != kauth_cred_geteuid(cred)) {
603 vput(tdp);
604 return (EPERM);
605 }
606 *vpp = tdp;
607 return (0);
608 }
609
610 /*
611 * If rewriting (RENAME), return the inode and the
612 * information required to rewrite the present directory
613 * Must get inode of directory entry to verify it's a
614 * regular file, or empty directory.
615 */
616 if (nameiop == RENAME && (flags & ISLASTCN)) {
617 error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_lwp);
618 if (error)
619 return (error);
620 /*
621 * Careful about locking second inode.
622 * This can only occur if the target is ".".
623 */
624 if (dp->i_number == foundino)
625 return (EISDIR);
626 if (flags & ISDOTDOT)
627 VOP_UNLOCK(vdp, 0); /* race to get the inode */
628 error = VFS_VGET(vdp->v_mount, foundino, &tdp);
629 if (flags & ISDOTDOT)
630 vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY);
631 if (error)
632 return (error);
633 *vpp = tdp;
634 cnp->cn_flags |= SAVENAME;
635 return (0);
636 }
637
638 /*
639 * Step through the translation in the name. We do not `vput' the
640 * directory because we may need it again if a symbolic link
641 * is relative to the current directory. Instead we save it
642 * unlocked as "pdp". We must get the target inode before unlocking
643 * the directory to insure that the inode will not be removed
644 * before we get it. We prevent deadlock by always fetching
645 * inodes from the root, moving down the directory tree. Thus
646 * when following backward pointers ".." we must unlock the
647 * parent directory before getting the requested directory.
648 * There is a potential race condition here if both the current
649 * and parent directories are removed before the VFS_VGET for the
650 * inode associated with ".." returns. We hope that this occurs
651 * infrequently since we cannot avoid this race condition without
652 * implementing a sophisticated deadlock detection algorithm.
653 * Note also that this simple deadlock detection scheme will not
654 * work if the file system has any hard links other than ".."
655 * that point backwards in the directory structure.
656 */
657 pdp = vdp;
658 if (flags & ISDOTDOT) {
659 VOP_UNLOCK(pdp, 0); /* race to get the inode */
660 error = VFS_VGET(vdp->v_mount, foundino, &tdp);
661 vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY);
662 if (error) {
663 return (error);
664 }
665 *vpp = tdp;
666 } else if (dp->i_number == foundino) {
667 VREF(vdp); /* we want ourself, ie "." */
668 *vpp = vdp;
669 } else {
670 error = VFS_VGET(vdp->v_mount, foundino, &tdp);
671 if (error)
672 return (error);
673 *vpp = tdp;
674 }
675
676 /*
677 * Insert name into cache if appropriate.
678 */
679 if (cnp->cn_flags & MAKEENTRY)
680 cache_enter(vdp, *vpp, cnp);
681 return (0);
682 }
683
684 /*
685 * Do consistency checking on a directory entry:
686 * record length must be multiple of 4
687 * entry must fit in rest of its dirblksize block
688 * record must be large enough to contain entry
689 * name is not longer than EXT2FS_MAXNAMLEN
690 * name must be as long as advertised, and null terminated
691 */
692 /*
693 * changed so that it confirms to ext2fs_check_dir_entry
694 */
695 static int
696 ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de,
697 int entryoffsetinblock)
698 {
699 struct ufsmount *ump = VFSTOUFS(dp->v_mount);
700 int dirblksiz = ump->um_dirblksiz;
701
702 const char *error_msg = NULL;
703 int reclen = fs2h16(de->e2d_reclen);
704 int namlen = de->e2d_namlen;
705
706 if (reclen < EXT2FS_DIRSIZ(1)) /* e2d_namlen = 1 */
707 error_msg = "rec_len is smaller than minimal";
708 else if (reclen % 4 != 0)
709 error_msg = "rec_len % 4 != 0";
710 else if (namlen > EXT2FS_MAXNAMLEN)
711 error_msg = "namlen > EXT2FS_MAXNAMLEN";
712 else if (reclen < EXT2FS_DIRSIZ(namlen))
713 error_msg = "reclen is too small for name_len";
714 else if (entryoffsetinblock + reclen > dirblksiz)
715 error_msg = "directory entry across blocks";
716 else if (fs2h32(de->e2d_ino) >
717 VTOI(dp)->i_e2fs->e2fs.e2fs_icount)
718 error_msg = "inode out of bounds";
719
720 if (error_msg != NULL) {
721 printf( "bad directory entry: %s\n"
722 "offset=%d, inode=%lu, rec_len=%d, name_len=%d \n",
723 error_msg, entryoffsetinblock,
724 (unsigned long) fs2h32(de->e2d_ino),
725 reclen, namlen);
726 panic("ext2fs_dirbadentry");
727 }
728 return error_msg == NULL ? 0 : 1;
729 }
730
731 /*
732 * Write a directory entry after a call to namei, using the parameters
733 * that it left in nameidata. The argument ip is the inode which the new
734 * directory entry will refer to. Dvp is a pointer to the directory to
735 * be written, which was left locked by namei. Remaining parameters
736 * (dp->i_offset, dp->i_count) indicate how the space for the new
737 * entry is to be obtained.
738 */
739 int
740 ext2fs_direnter(struct inode *ip, struct vnode *dvp, struct componentname *cnp)
741 {
742 struct ext2fs_direct *ep, *nep;
743 struct inode *dp;
744 struct buf *bp;
745 struct ext2fs_direct newdir;
746 struct iovec aiov;
747 struct uio auio;
748 u_int dsize;
749 int error, loc, newentrysize, spacefree;
750 char *dirbuf;
751 struct ufsmount *ump = VFSTOUFS(dvp->v_mount);
752 int dirblksiz = ump->um_dirblksiz;
753
754 #ifdef DIAGNOSTIC
755 if ((cnp->cn_flags & SAVENAME) == 0)
756 panic("direnter: missing name");
757 #endif
758 dp = VTOI(dvp);
759 newdir.e2d_ino = h2fs32(ip->i_number);
760 newdir.e2d_namlen = cnp->cn_namelen;
761 if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
762 (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
763 newdir.e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
764 } else {
765 newdir.e2d_type = 0;
766 };
767 memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1);
768 newentrysize = EXT2FS_DIRSIZ(cnp->cn_namelen);
769 if (dp->i_count == 0) {
770 /*
771 * If dp->i_count is 0, then namei could find no
772 * space in the directory. Here, dp->i_offset will
773 * be on a directory block boundary and we will write the
774 * new entry into a fresh block.
775 */
776 if (dp->i_offset & (dirblksiz - 1))
777 panic("ext2fs_direnter: newblk");
778 auio.uio_offset = dp->i_offset;
779 newdir.e2d_reclen = h2fs16(dirblksiz);
780 auio.uio_resid = newentrysize;
781 aiov.iov_len = newentrysize;
782 aiov.iov_base = (caddr_t)&newdir;
783 auio.uio_iov = &aiov;
784 auio.uio_iovcnt = 1;
785 auio.uio_rw = UIO_WRITE;
786 UIO_SETUP_SYSSPACE(&auio);
787 error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
788 if (dirblksiz > dvp->v_mount->mnt_stat.f_bsize)
789 /* XXX should grow with balloc() */
790 panic("ext2fs_direnter: frag size");
791 else if (!error) {
792 error = ext2fs_setsize(dp,
793 roundup(ext2fs_size(dp), dirblksiz));
794 if (error)
795 return (error);
796 dp->i_flag |= IN_CHANGE;
797 uvm_vnp_setsize(dvp, ext2fs_size(dp));
798 }
799 return (error);
800 }
801
802 /*
803 * If dp->i_count is non-zero, then namei found space
804 * for the new entry in the range dp->i_offset to
805 * dp->i_offset + dp->i_count in the directory.
806 * To use this space, we may have to compact the entries located
807 * there, by copying them together towards the beginning of the
808 * block, leaving the free space in one usable chunk at the end.
809 */
810
811 /*
812 * Get the block containing the space for the new directory entry.
813 */
814 if ((error = ext2fs_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf, &bp)) != 0)
815 return (error);
816 /*
817 * Find space for the new entry. In the simple case, the entry at
818 * offset base will have the space. If it does not, then namei
819 * arranged that compacting the region dp->i_offset to
820 * dp->i_offset + dp->i_count would yield the
821 * space.
822 */
823 ep = (struct ext2fs_direct *)dirbuf;
824 dsize = EXT2FS_DIRSIZ(ep->e2d_namlen);
825 spacefree = fs2h16(ep->e2d_reclen) - dsize;
826 for (loc = fs2h16(ep->e2d_reclen); loc < dp->i_count; ) {
827 nep = (struct ext2fs_direct *)(dirbuf + loc);
828 if (ep->e2d_ino) {
829 /* trim the existing slot */
830 ep->e2d_reclen = h2fs16(dsize);
831 ep = (struct ext2fs_direct *)((char *)ep + dsize);
832 } else {
833 /* overwrite; nothing there; header is ours */
834 spacefree += dsize;
835 }
836 dsize = EXT2FS_DIRSIZ(nep->e2d_namlen);
837 spacefree += fs2h16(nep->e2d_reclen) - dsize;
838 loc += fs2h16(nep->e2d_reclen);
839 memcpy((caddr_t)ep, (caddr_t)nep, dsize);
840 }
841 /*
842 * Update the pointer fields in the previous entry (if any),
843 * copy in the new entry, and write out the block.
844 */
845 if (ep->e2d_ino == 0) {
846 #ifdef DIAGNOSTIC
847 if (spacefree + dsize < newentrysize)
848 panic("ext2fs_direnter: compact1");
849 #endif
850 newdir.e2d_reclen = h2fs16(spacefree + dsize);
851 } else {
852 #ifdef DIAGNOSTIC
853 if (spacefree < newentrysize) {
854 printf("ext2fs_direnter: compact2 %u %u",
855 (u_int)spacefree, (u_int)newentrysize);
856 panic("ext2fs_direnter: compact2");
857 }
858 #endif
859 newdir.e2d_reclen = h2fs16(spacefree);
860 ep->e2d_reclen = h2fs16(dsize);
861 ep = (struct ext2fs_direct *)((char *)ep + dsize);
862 }
863 memcpy((caddr_t)ep, (caddr_t)&newdir, (u_int)newentrysize);
864 error = VOP_BWRITE(bp);
865 dp->i_flag |= IN_CHANGE | IN_UPDATE;
866 if (!error && dp->i_endoff && dp->i_endoff < ext2fs_size(dp))
867 error = ext2fs_truncate(dvp, (off_t)dp->i_endoff, IO_SYNC,
868 cnp->cn_cred, cnp->cn_lwp->l_proc);
869 return (error);
870 }
871
872 /*
873 * Remove a directory entry after a call to namei, using
874 * the parameters which it left in nameidata. The entry
875 * dp->i_offset contains the offset into the directory of the
876 * entry to be eliminated. The dp->i_count field contains the
877 * size of the previous record in the directory. If this
878 * is 0, the first entry is being deleted, so we need only
879 * zero the inode number to mark the entry as free. If the
880 * entry is not the first in the directory, we must reclaim
881 * the space of the now empty record by adding the record size
882 * to the size of the previous entry.
883 */
884 int
885 ext2fs_dirremove(struct vnode *dvp, struct componentname *cnp)
886 {
887 struct inode *dp;
888 struct ext2fs_direct *ep;
889 struct buf *bp;
890 int error;
891
892 dp = VTOI(dvp);
893 if (dp->i_count == 0) {
894 /*
895 * First entry in block: set d_ino to zero.
896 */
897 error = ext2fs_blkatoff(dvp, (off_t)dp->i_offset,
898 (void *)&ep, &bp);
899 if (error != 0)
900 return (error);
901 ep->e2d_ino = 0;
902 error = VOP_BWRITE(bp);
903 dp->i_flag |= IN_CHANGE | IN_UPDATE;
904 return (error);
905 }
906 /*
907 * Collapse new free space into previous entry.
908 */
909 error = ext2fs_blkatoff(dvp, (off_t)(dp->i_offset - dp->i_count),
910 (void *)&ep, &bp);
911 if (error != 0)
912 return (error);
913 ep->e2d_reclen = h2fs16(fs2h16(ep->e2d_reclen) + dp->i_reclen);
914 error = VOP_BWRITE(bp);
915 dp->i_flag |= IN_CHANGE | IN_UPDATE;
916 return (error);
917 }
918
919 /*
920 * Rewrite an existing directory entry to point at the inode
921 * supplied. The parameters describing the directory entry are
922 * set up by a call to namei.
923 */
924 int
925 ext2fs_dirrewrite(struct inode *dp, struct inode *ip,
926 struct componentname *cnp)
927 {
928 struct buf *bp;
929 struct ext2fs_direct *ep;
930 struct vnode *vdp = ITOV(dp);
931 int error;
932
933 error = ext2fs_blkatoff(vdp, (off_t)dp->i_offset, (void *)&ep, &bp);
934 if (error != 0)
935 return (error);
936 ep->e2d_ino = h2fs32(ip->i_number);
937 if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
938 (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
939 ep->e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
940 } else {
941 ep->e2d_type = 0;
942 }
943 error = VOP_BWRITE(bp);
944 dp->i_flag |= IN_CHANGE | IN_UPDATE;
945 return (error);
946 }
947
948 /*
949 * Check if a directory is empty or not.
950 * Inode supplied must be locked.
951 *
952 * Using a struct dirtemplate here is not precisely
953 * what we want, but better than using a struct ext2fs_direct.
954 *
955 * NB: does not handle corrupted directories.
956 */
957 int
958 ext2fs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred)
959 {
960 off_t off;
961 struct ext2fs_dirtemplate dbuf;
962 struct ext2fs_direct *dp = (struct ext2fs_direct *)&dbuf;
963 int error, namlen;
964 size_t count;
965
966 #define MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2)
967
968 for (off = 0; off < ext2fs_size(ip); off += fs2h16(dp->e2d_reclen)) {
969 error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
970 UIO_SYSSPACE, IO_NODELOCKED, cred, &count, NULL);
971 /*
972 * Since we read MINDIRSIZ, residual must
973 * be 0 unless we're at end of file.
974 */
975 if (error || count != 0)
976 return (0);
977 /* avoid infinite loops */
978 if (dp->e2d_reclen == 0)
979 return (0);
980 /* skip empty entries */
981 if (dp->e2d_ino == 0)
982 continue;
983 /* accept only "." and ".." */
984 namlen = dp->e2d_namlen;
985 if (namlen > 2)
986 return (0);
987 if (dp->e2d_name[0] != '.')
988 return (0);
989 /*
990 * At this point namlen must be 1 or 2.
991 * 1 implies ".", 2 implies ".." if second
992 * char is also "."
993 */
994 if (namlen == 1)
995 continue;
996 if (dp->e2d_name[1] == '.' && fs2h32(dp->e2d_ino) == parentino)
997 continue;
998 return (0);
999 }
1000 return (1);
1001 }
1002
1003 /*
1004 * Check if source directory is in the path of the target directory.
1005 * Target is supplied locked, source is unlocked.
1006 * The target is always vput before returning.
1007 */
1008 int
1009 ext2fs_checkpath(struct inode *source, struct inode *target,
1010 kauth_cred_t cred)
1011 {
1012 struct vnode *vp;
1013 int error, rootino, namlen;
1014 struct ext2fs_dirtemplate dirbuf;
1015 u_int32_t ino;
1016
1017 vp = ITOV(target);
1018 if (target->i_number == source->i_number) {
1019 error = EEXIST;
1020 goto out;
1021 }
1022 rootino = ROOTINO;
1023 error = 0;
1024 if (target->i_number == rootino)
1025 goto out;
1026
1027 for (;;) {
1028 if (vp->v_type != VDIR) {
1029 error = ENOTDIR;
1030 break;
1031 }
1032 error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
1033 sizeof (struct ext2fs_dirtemplate), (off_t)0,
1034 UIO_SYSSPACE, IO_NODELOCKED, cred, (size_t *)0,
1035 NULL);
1036 if (error != 0)
1037 break;
1038 namlen = dirbuf.dotdot_namlen;
1039 if (namlen != 2 ||
1040 dirbuf.dotdot_name[0] != '.' ||
1041 dirbuf.dotdot_name[1] != '.') {
1042 error = ENOTDIR;
1043 break;
1044 }
1045 ino = fs2h32(dirbuf.dotdot_ino);
1046 if (ino == source->i_number) {
1047 error = EINVAL;
1048 break;
1049 }
1050 if (ino == rootino)
1051 break;
1052 vput(vp);
1053 error = VFS_VGET(vp->v_mount, ino, &vp);
1054 if (error != 0) {
1055 vp = NULL;
1056 break;
1057 }
1058 }
1059
1060 out:
1061 if (error == ENOTDIR) {
1062 printf("checkpath: .. not a directory\n");
1063 panic("checkpath");
1064 }
1065 if (vp != NULL)
1066 vput(vp);
1067 return (error);
1068 }
1069