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