ext2fs_lookup.c revision 1.87 1 /* $NetBSD: ext2fs_lookup.c,v 1.87 2016/08/19 00:05:43 jdolecek 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.87 2016/08/19 00:05:43 jdolecek 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/kmem.h>
61 #include <sys/malloc.h>
62 #include <sys/dirent.h>
63 #include <sys/kauth.h>
64 #include <sys/proc.h>
65
66 #include <ufs/ufs/inode.h>
67 #include <ufs/ufs/ufsmount.h>
68 #include <ufs/ufs/ufs_extern.h>
69
70 #include <ufs/ext2fs/ext2fs_extern.h>
71 #include <ufs/ext2fs/ext2fs_dir.h>
72 #include <ufs/ext2fs/ext2fs.h>
73 #include <ufs/ext2fs/ext2fs_htree.h>
74
75 #include <miscfs/genfs/genfs.h>
76
77 extern int dirchk;
78
79 static void ext2fs_dirconv2ffs(struct m_ext2fs *fs,
80 struct ext2fs_direct *e2dir,
81 struct dirent *ffsdir);
82 static int ext2fs_dirbadentry(struct vnode *dp,
83 struct ext2fs_direct *de,
84 int entryoffsetinblock);
85
86 /*
87 * the problem that is tackled below is the fact that FFS
88 * includes the terminating zero on disk while EXT2FS doesn't
89 * this implies that we need to introduce some padding.
90 * For instance, a filename "sbin" has normally a reclen 12
91 * in EXT2, but 16 in FFS.
92 * This reminds me of that Pepsi commercial: 'Kid saved a lousy nine cents...'
93 * If it wasn't for that, the complete ufs code for directories would
94 * have worked w/o changes (except for the difference in DIRBLKSIZ)
95 */
96 static void
97 ext2fs_dirconv2ffs(struct m_ext2fs *fs, struct ext2fs_direct *e2dir, struct dirent *ffsdir)
98 {
99 memset(ffsdir, 0, sizeof(struct dirent));
100 ffsdir->d_fileno = fs2h32(e2dir->e2d_ino);
101 ffsdir->d_namlen = e2dir->e2d_namlen;
102
103 if (EXT2F_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_FTYPE))
104 ffsdir->d_type = ext2dt2dt(e2dir->e2d_type);
105 else
106 ffsdir->d_type = DT_UNKNOWN;
107
108 #ifdef DIAGNOSTIC
109 #if MAXNAMLEN < E2FS_MAXNAMLEN
110 /*
111 * we should handle this more gracefully !
112 */
113 if (e2dir->e2d_namlen > MAXNAMLEN)
114 panic("ext2fs: e2dir->e2d_namlen");
115 #endif
116 #endif
117 strncpy(ffsdir->d_name, e2dir->e2d_name, ffsdir->d_namlen);
118
119 /* Godmar thinks: since e2dir->e2d_reclen can be big and means
120 nothing anyway, we compute our own reclen according to what
121 we think is right
122 */
123 ffsdir->d_reclen = _DIRENT_SIZE(ffsdir);
124 }
125
126 static int
127 ext2fs_is_dot_entry(struct componentname *cnp)
128 {
129 return cnp->cn_namelen <= 2 && cnp->cn_nameptr[0] == '.' &&
130 (cnp->cn_nameptr[1] == '.' || cnp->cn_nameptr[1] == '\0');
131 }
132
133 /*
134 * Vnode op for reading directories.
135 *
136 * Convert the on-disk entries to <sys/dirent.h> entries.
137 * the problem is that the conversion will blow up some entries by four bytes,
138 * so it can't be done in place. This is too bad. Right now the conversion is
139 * done entry by entry, the converted entry is sent via uiomove.
140 *
141 * XXX allocate a buffer, convert as many entries as possible, then send
142 * the whole buffer to uiomove
143 */
144 int
145 ext2fs_readdir(void *v)
146 {
147 struct vop_readdir_args /* {
148 struct vnode *a_vp;
149 struct uio *a_uio;
150 kauth_cred_t a_cred;
151 int **a_eofflag;
152 off_t **a_cookies;
153 int ncookies;
154 } */ *ap = v;
155 struct uio *uio = ap->a_uio;
156 int error;
157 size_t e2fs_count, readcnt;
158 struct vnode *vp = ap->a_vp;
159 struct m_ext2fs *fs = VTOI(vp)->i_e2fs;
160
161 struct ext2fs_direct *dp;
162 struct dirent *dstd;
163 struct uio auio;
164 struct iovec aiov;
165 void *dirbuf;
166 off_t off = uio->uio_offset;
167 off_t *cookies = NULL;
168 int nc = 0, ncookies = 0;
169 int e2d_reclen;
170
171 if (vp->v_type != VDIR)
172 return ENOTDIR;
173
174 e2fs_count = uio->uio_resid;
175 /* Make sure we don't return partial entries. */
176 e2fs_count -= (uio->uio_offset + e2fs_count) & (fs->e2fs_bsize -1);
177 if (e2fs_count <= 0)
178 return EINVAL;
179
180 auio = *uio;
181 auio.uio_iov = &aiov;
182 auio.uio_iovcnt = 1;
183 aiov.iov_len = e2fs_count;
184 auio.uio_resid = e2fs_count;
185 UIO_SETUP_SYSSPACE(&auio);
186 dirbuf = kmem_alloc(e2fs_count, KM_SLEEP);
187 dstd = kmem_zalloc(sizeof(struct dirent), KM_SLEEP);
188 if (ap->a_ncookies) {
189 nc = e2fs_count / _DIRENT_MINSIZE((struct dirent *)0);
190 ncookies = nc;
191 cookies = malloc(sizeof (off_t) * ncookies, M_TEMP, M_WAITOK);
192 *ap->a_cookies = cookies;
193 }
194 aiov.iov_base = dirbuf;
195
196 error = UFS_BUFRD(ap->a_vp, &auio, 0, ap->a_cred);
197 if (error == 0) {
198 readcnt = e2fs_count - auio.uio_resid;
199 for (dp = (struct ext2fs_direct *)dirbuf;
200 (char *)dp < (char *)dirbuf + readcnt;) {
201 e2d_reclen = fs2h16(dp->e2d_reclen);
202 if (e2d_reclen == 0) {
203 error = EIO;
204 break;
205 }
206 ext2fs_dirconv2ffs(fs, dp, dstd);
207 if(dstd->d_reclen > uio->uio_resid) {
208 break;
209 }
210 error = uiomove(dstd, dstd->d_reclen, uio);
211 if (error != 0) {
212 break;
213 }
214 off = off + e2d_reclen;
215 if (cookies != NULL) {
216 *cookies++ = off;
217 if (--ncookies <= 0){
218 break; /* out of cookies */
219 }
220 }
221 /* advance dp */
222 dp = (struct ext2fs_direct *) ((char *)dp + e2d_reclen);
223 }
224 /* we need to correct uio_offset */
225 uio->uio_offset = off;
226 }
227 kmem_free(dirbuf, e2fs_count);
228 kmem_free(dstd, sizeof(*dstd));
229 *ap->a_eofflag = ext2fs_size(VTOI(ap->a_vp)) <= uio->uio_offset;
230 if (ap->a_ncookies) {
231 if (error) {
232 free(*ap->a_cookies, M_TEMP);
233 *ap->a_ncookies = 0;
234 *ap->a_cookies = NULL;
235 } else
236 *ap->a_ncookies = nc - ncookies;
237 }
238 return error;
239 }
240
241 /*
242 * Convert a component of a pathname into a pointer to a locked inode.
243 * This is a very central and rather complicated routine.
244 * If the file system is not maintained in a strict tree hierarchy,
245 * this can result in a deadlock situation (see comments in code below).
246 *
247 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
248 * on whether the name is to be looked up, created, renamed, or deleted.
249 * When CREATE, RENAME, or DELETE is specified, information usable in
250 * creating, renaming, or deleting a directory entry may be calculated.
251 * If flag has LOCKPARENT or'ed into it and the target of the pathname
252 * exists, lookup returns both the target and its parent directory locked.
253 * When creating or renaming and LOCKPARENT is specified, the target may
254 * not be ".". When deleting and LOCKPARENT is specified, the target may
255 * be "."., but the caller must check to ensure it does an vrele and vput
256 * instead of two vputs.
257 *
258 * Overall outline of ext2fs_lookup:
259 *
260 * check accessibility of directory
261 * look for name in cache, if found, then if at end of path
262 * and deleting or creating, drop it, else return name
263 * search for name in directory, to found or notfound
264 * notfound:
265 * if creating, return locked directory, leaving info on available slots
266 * else return error
267 * found:
268 * if at end of path and deleting, return information to allow delete
269 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
270 * inode and return info to allow rewrite
271 * if not at end, add name to cache; if at end and neither creating
272 * nor deleting, add name to cache
273 */
274 int
275 ext2fs_lookup(void *v)
276 {
277 struct vop_lookup_v2_args /* {
278 struct vnode *a_dvp;
279 struct vnode **a_vpp;
280 struct componentname *a_cnp;
281 } */ *ap = v;
282 struct vnode *vdp = ap->a_dvp; /* vnode for directory being searched */
283 struct inode *dp = VTOI(vdp); /* inode for directory being searched */
284 struct buf *bp; /* a buffer of directory entries */
285 struct ext2fs_direct *ep; /* the current directory entry */
286 int entryoffsetinblock; /* offset of ep in bp's buffer */
287 enum ext2fs_slotstatus slotstatus;
288 doff_t slotoffset; /* offset of area with free space */
289 int slotsize; /* size of area at slotoffset */
290 int slotfreespace; /* amount of space free in slot */
291 int slotneeded; /* size of the entry we're seeking */
292 int numdirpasses; /* strategy for directory search */
293 doff_t endsearch; /* offset to end directory search */
294 doff_t prevoff; /* prev entry dp->i_offset */
295 struct vnode *tdp; /* returned by vcache_get */
296 doff_t enduseful; /* pointer past last used dir slot */
297 u_long bmask; /* block offset mask */
298 int namlen, error;
299 struct vnode **vpp = ap->a_vpp;
300 struct componentname *cnp = ap->a_cnp;
301 kauth_cred_t cred = cnp->cn_cred;
302 int flags;
303 int nameiop = cnp->cn_nameiop;
304 struct ufsmount *ump = dp->i_ump;
305 int dirblksiz = ump->um_dirblksiz;
306 ino_t foundino;
307 struct ufs_lookup_results *results;
308 doff_t i_offset; /* cached i_offset value */
309 struct ext2fs_searchslot ss;
310
311 flags = cnp->cn_flags;
312
313 bp = NULL;
314 slotoffset = -1;
315 *vpp = NULL;
316
317 /*
318 * Produce the auxiliary lookup results into i_crap. Increment
319 * its serial number so elsewhere we can tell if we're using
320 * stale results. This should not be done this way. XXX.
321 */
322 results = &dp->i_crap;
323 dp->i_crapcounter++;
324
325 /*
326 * Check accessiblity of directory.
327 */
328 if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0)
329 return error;
330
331 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
332 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
333 return EROFS;
334
335 /*
336 * We now have a segment name to search for, and a directory to search.
337 *
338 * Before tediously performing a linear scan of the directory,
339 * check the name cache to see if the directory/name pair
340 * we are looking for is known already.
341 */
342 if (cache_lookup(vdp, cnp->cn_nameptr, cnp->cn_namelen,
343 cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) {
344 return *vpp == NULLVP ? ENOENT : 0;
345 }
346
347 /*
348 * Suppress search for slots unless creating
349 * file and at end of pathname, in which case
350 * we watch for a place to put the new file in
351 * case it doesn't already exist.
352 */
353 slotstatus = FOUND;
354 slotfreespace = slotsize = slotneeded = 0;
355 if ((nameiop == CREATE || nameiop == RENAME) &&
356 (flags & ISLASTCN)) {
357 slotstatus = NONE;
358 slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen);
359 }
360
361 /*
362 * If there is cached information on a previous search of
363 * this directory, pick up where we last left off.
364 * We cache only lookups as these are the most common
365 * and have the greatest payoff. Caching CREATE has little
366 * benefit as it usually must search the entire directory
367 * to determine that the entry does not exist. Caching the
368 * location of the last DELETE or RENAME has not reduced
369 * profiling time and hence has been removed in the interest
370 * of simplicity.
371 */
372 bmask = vdp->v_mount->mnt_stat.f_iosize - 1;
373 if (nameiop != LOOKUP || results->ulr_diroff == 0 ||
374 results->ulr_diroff >= ext2fs_size(dp)) {
375 entryoffsetinblock = 0;
376 results->ulr_offset = 0;
377 numdirpasses = 1;
378 } else {
379 results->ulr_offset = results->ulr_diroff;
380 if ((entryoffsetinblock = results->ulr_offset & bmask) &&
381 (error = ext2fs_blkatoff(vdp, (off_t)results->ulr_offset, NULL, &bp)))
382 return error;
383 numdirpasses = 2;
384 namecache_count_2passes();
385 }
386 prevoff = results->ulr_offset;
387 endsearch = roundup(ext2fs_size(dp), dirblksiz);
388 enduseful = 0;
389
390 /*
391 * Try to lookup dir entry using htree directory index.
392 *
393 * If we got an error or we want to find '.' or '..' entry,
394 * we will fall back to linear search.
395 */
396 if (!ext2fs_is_dot_entry(cnp) && ext2fs_htree_has_idx(dp)) {
397 numdirpasses = 1;
398 entryoffsetinblock = 0;
399
400 int htree_lookup_ret = ext2fs_htree_lookup(dp, cnp->cn_nameptr,
401 cnp->cn_namelen, &bp, &entryoffsetinblock, &i_offset,
402 &prevoff, &enduseful, &ss);
403 switch (htree_lookup_ret) {
404 case 0:
405 ep = (struct ext2fs_direct*)((char *)bp->b_data +
406 (i_offset & bmask));
407 foundino = ep->e2d_ino;
408 goto found;
409 case ENOENT:
410 i_offset = roundup2(dp->i_size, dp->i_e2fs->e2fs_bsize);
411 goto notfound;
412 default:
413 /*
414 * Something failed; just fallback to do a linear
415 * search.
416 */
417 break;
418 }
419 }
420
421 searchloop:
422 while (results->ulr_offset < endsearch) {
423 if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
424 preempt();
425 /*
426 * If necessary, get the next directory block.
427 */
428 if ((results->ulr_offset & bmask) == 0) {
429 if (bp != NULL)
430 brelse(bp, 0);
431 error = ext2fs_blkatoff(vdp, (off_t)results->ulr_offset, NULL,
432 &bp);
433 if (error != 0)
434 return error;
435 entryoffsetinblock = 0;
436 }
437 /*
438 * If still looking for a slot, and at a dirblksize
439 * boundary, have to start looking for free space again.
440 */
441 if (slotstatus == NONE &&
442 (entryoffsetinblock & (dirblksiz - 1)) == 0) {
443 slotoffset = -1;
444 slotfreespace = 0;
445 }
446 /*
447 * Get pointer to next entry.
448 * Full validation checks are slow, so we only check
449 * enough to insure forward progress through the
450 * directory. Complete checks can be run by patching
451 * "dirchk" to be true.
452 */
453 KASSERT(bp != NULL);
454 ep = (struct ext2fs_direct *)
455 ((char *)bp->b_data + entryoffsetinblock);
456 if (ep->e2d_reclen == 0 ||
457 (dirchk &&
458 ext2fs_dirbadentry(vdp, ep, entryoffsetinblock))) {
459 int i;
460
461 ufs_dirbad(dp, results->ulr_offset, "mangled entry");
462 i = dirblksiz - (entryoffsetinblock & (dirblksiz - 1));
463 results->ulr_offset += i;
464 entryoffsetinblock += i;
465 continue;
466 }
467
468 /*
469 * If an appropriate sized slot has not yet been found,
470 * check to see if one is available. Also accumulate space
471 * in the current block so that we can determine if
472 * compaction is viable.
473 */
474 if (slotstatus != FOUND) {
475 int size = fs2h16(ep->e2d_reclen);
476
477 if (ep->e2d_ino != 0)
478 size -= EXT2FS_DIRSIZ(ep->e2d_namlen);
479 if (size > 0) {
480 if (size >= slotneeded) {
481 slotstatus = FOUND;
482 slotoffset = results->ulr_offset;
483 slotsize = fs2h16(ep->e2d_reclen);
484 } else if (slotstatus == NONE) {
485 slotfreespace += size;
486 if (slotoffset == -1)
487 slotoffset = results->ulr_offset;
488 if (slotfreespace >= slotneeded) {
489 slotstatus = COMPACT;
490 slotsize = results->ulr_offset +
491 fs2h16(ep->e2d_reclen) -
492 slotoffset;
493 }
494 }
495 }
496 }
497
498 /*
499 * Check for a name match.
500 */
501 if (ep->e2d_ino) {
502 namlen = ep->e2d_namlen;
503 if (namlen == cnp->cn_namelen &&
504 !memcmp(cnp->cn_nameptr, ep->e2d_name,
505 (unsigned)namlen)) {
506 /*
507 * Save directory entry's inode number and
508 * reclen in ndp->ni_ufs area, and release
509 * directory buffer.
510 */
511 foundino = fs2h32(ep->e2d_ino);
512 results->ulr_reclen = fs2h16(ep->e2d_reclen);
513 goto found;
514 }
515 }
516 prevoff = results->ulr_offset;
517 results->ulr_offset += fs2h16(ep->e2d_reclen);
518 entryoffsetinblock += fs2h16(ep->e2d_reclen);
519 if (ep->e2d_ino)
520 enduseful = results->ulr_offset;
521 }
522 notfound:
523 /*
524 * If we started in the middle of the directory and failed
525 * to find our target, we must check the beginning as well.
526 */
527 if (numdirpasses == 2) {
528 numdirpasses--;
529 results->ulr_offset = 0;
530 endsearch = results->ulr_diroff;
531 goto searchloop;
532 }
533 if (bp != NULL)
534 brelse(bp, 0);
535 /*
536 * If creating, and at end of pathname and current
537 * directory has not been removed, then can consider
538 * allowing file to be created.
539 */
540 if ((nameiop == CREATE || nameiop == RENAME) &&
541 (flags & ISLASTCN) && dp->i_e2fs_nlink != 0) {
542 /*
543 * Access for write is interpreted as allowing
544 * creation of files in the directory.
545 */
546 error = VOP_ACCESS(vdp, VWRITE, cred);
547 if (error)
548 return error;
549 /*
550 * Return an indication of where the new directory
551 * entry should be put. If we didn't find a slot,
552 * then set results->ulr_count to 0 indicating
553 * that the new slot belongs at the end of the
554 * directory. If we found a slot, then the new entry
555 * can be put in the range from results->ulr_offset to
556 * results->ulr_offset + results->ulr_count.
557 */
558 if (slotstatus == NONE) {
559 results->ulr_offset = roundup(ext2fs_size(dp), dirblksiz);
560 results->ulr_count = 0;
561 enduseful = results->ulr_offset;
562 } else {
563 results->ulr_offset = slotoffset;
564 results->ulr_count = slotsize;
565 if (enduseful < slotoffset + slotsize)
566 enduseful = slotoffset + slotsize;
567 }
568 results->ulr_endoff = roundup(enduseful, dirblksiz);
569 #if 0
570 dp->i_flag |= IN_CHANGE | IN_UPDATE;
571 #endif
572 /*
573 * We return with the directory locked, so that
574 * the parameters we set up above will still be
575 * valid if we actually decide to do a direnter().
576 * We return ni_vp == NULL to indicate that the entry
577 * does not currently exist; we leave a pointer to
578 * the (locked) directory inode in ndp->ni_dvp.
579 *
580 * NB - if the directory is unlocked, then this
581 * information cannot be used.
582 */
583 return EJUSTRETURN;
584 }
585 /*
586 * Insert name into cache (as non-existent) if appropriate.
587 */
588 if (nameiop != CREATE) {
589 cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen,
590 cnp->cn_flags);
591 }
592 return ENOENT;
593
594 found:
595 if (numdirpasses == 2)
596 namecache_count_pass2();
597 /*
598 * Check that directory length properly reflects presence
599 * of this entry.
600 */
601 if (results->ulr_offset + EXT2FS_DIRSIZ(ep->e2d_namlen) > ext2fs_size(dp)) {
602 ufs_dirbad(dp, results->ulr_offset, "i_size too small");
603 error = ext2fs_setsize(dp,
604 results->ulr_offset + EXT2FS_DIRSIZ(ep->e2d_namlen));
605 if (error) {
606 brelse(bp, 0);
607 return error;
608 }
609 dp->i_flag |= IN_CHANGE | IN_UPDATE;
610 uvm_vnp_setsize(vdp, ext2fs_size(dp));
611 }
612 brelse(bp, 0);
613
614 /*
615 * Found component in pathname.
616 * If the final component of path name, save information
617 * in the cache as to where the entry was found.
618 */
619 if ((flags & ISLASTCN) && nameiop == LOOKUP)
620 results->ulr_diroff = results->ulr_offset &~ (dirblksiz - 1);
621
622 /*
623 * If deleting, and at end of pathname, return
624 * parameters which can be used to remove file.
625 * Lock the inode, being careful with ".".
626 */
627 if (nameiop == DELETE && (flags & ISLASTCN)) {
628 /*
629 * Return pointer to current entry in results->ulr_offset,
630 * and distance past previous entry (if there
631 * is a previous entry in this block) in results->ulr_count.
632 * Save directory inode pointer in ndp->ni_dvp for dirremove().
633 */
634 if ((results->ulr_offset & (dirblksiz - 1)) == 0)
635 results->ulr_count = 0;
636 else
637 results->ulr_count = results->ulr_offset - prevoff;
638 if (dp->i_number == foundino) {
639 vref(vdp);
640 tdp = vdp;
641 } else {
642 error = vcache_get(vdp->v_mount,
643 &foundino, sizeof(foundino), &tdp);
644 if (error)
645 return error;
646 }
647 /*
648 * Write access to directory required to delete files.
649 */
650 if ((error = VOP_ACCESS(vdp, VWRITE, cred)) != 0) {
651 vrele(tdp);
652 return error;
653 }
654 /*
655 * If directory is "sticky", then user must own
656 * the directory, or the file in it, else she
657 * may not delete it (unless she's root). This
658 * implements append-only directories.
659 */
660 if (dp->i_e2fs_mode & ISVTX) {
661 error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE,
662 tdp, vdp, genfs_can_sticky(cred, dp->i_uid,
663 VTOI(tdp)->i_uid));
664 if (error) {
665 vrele(tdp);
666 return EPERM;
667 }
668 }
669 *vpp = tdp;
670 return 0;
671 }
672
673 /*
674 * If rewriting (RENAME), return the inode and the
675 * information required to rewrite the present directory
676 * Must get inode of directory entry to verify it's a
677 * regular file, or empty directory.
678 */
679 if (nameiop == RENAME && (flags & ISLASTCN)) {
680 error = VOP_ACCESS(vdp, VWRITE, cred);
681 if (error)
682 return error;
683 /*
684 * Careful about locking second inode.
685 * This can only occur if the target is ".".
686 */
687 if (dp->i_number == foundino)
688 return EISDIR;
689 error = vcache_get(vdp->v_mount,
690 &foundino, sizeof(foundino), &tdp);
691 if (error)
692 return error;
693 *vpp = tdp;
694 return 0;
695 }
696
697 if (dp->i_number == foundino) {
698 vref(vdp); /* we want ourself, ie "." */
699 *vpp = vdp;
700 } else {
701 error = vcache_get(vdp->v_mount,
702 &foundino, sizeof(foundino), &tdp);
703 if (error)
704 return error;
705 *vpp = tdp;
706 }
707
708 /*
709 * Insert name into cache if appropriate.
710 */
711 cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags);
712 return 0;
713 }
714 static void
715 ext2fs_accumulatespace (struct ext2fs_searchslot *ssp, struct ext2fs_direct *ep,
716 doff_t *offp)
717 {
718 int size = ep->e2d_reclen;
719
720 if (ep->e2d_ino != 0)
721 size -= EXT2_DIR_REC_LEN(ep->e2d_namlen);
722
723 if (size <= 0)
724 return;
725
726 if (size >= ssp->slotneeded) {
727 ssp->slotstatus = FOUND;
728 ssp->slotoffset = *offp;
729 ssp->slotsize = ep->e2d_reclen;
730 return;
731 }
732
733 if (ssp->slotstatus != NONE)
734 return;
735
736 ssp->slotfreespace += size;
737 if (ssp->slotoffset == -1)
738 ssp->slotoffset = *offp;
739
740 if (ssp->slotfreespace >= ssp->slotneeded) {
741 ssp->slotstatus = COMPACT;
742 ssp->slotsize = *offp + ep->e2d_reclen - ssp->slotoffset;
743 }
744 }
745
746 int
747 ext2fs_search_dirblock(struct inode *ip, void *data, int *foundp,
748 const char *name, int namelen, int *entryoffsetinblockp,
749 doff_t *offp, doff_t *prevoffp, doff_t *endusefulp,
750 struct ext2fs_searchslot *ssp)
751 {
752 struct vnode *vdp = ITOV(ip);
753 struct ext2fs_direct *ep, *top;
754 uint32_t bsize = ip->i_e2fs->e2fs_bsize;
755 int offset = *entryoffsetinblockp;
756 int namlen;
757
758 ep = (void *)((char *)data + offset);
759 top = (void *)((char *)data + bsize - EXT2_DIR_REC_LEN(0));
760
761 while (ep < top) {
762 /*
763 * Full validation checks are slow, so we only check
764 * enough to insure forward progress through the
765 * directory. Complete checks can be run by setting
766 * "vfs.e2fs.dirchk" to be true.
767 */
768 if (ep->e2d_reclen == 0 ||
769 (dirchk && ext2fs_dirbadentry(vdp, ep, offset))) {
770 int i;
771 ufs_dirbad(ip, *offp, "mangled entry");
772 i = bsize - (offset & (bsize - 1));
773 *offp += i;
774 offset += i;
775 continue;
776 }
777
778 /*
779 * If an appropriate sized slot has not yet been found,
780 * check to see if one is available. Also accumulate space
781 * in the current block so that we can determine if
782 * compaction is viable.
783 */
784 if (ssp->slotstatus != FOUND)
785 ext2fs_accumulatespace(ssp, ep, offp);
786
787 /*
788 * Check for a name match.
789 */
790 if (ep->e2d_ino) {
791 namlen = ep->e2d_namlen;
792 if (namlen == namelen &&
793 !memcmp(name, ep->e2d_name, (unsigned)namlen)) {
794 /*
795 * Save directory entry's inode number and
796 * reclen in ndp->ni_ufs area, and release
797 * directory buffer.
798 */
799 *foundp = 1;
800 return 0;
801 }
802 }
803 *prevoffp = *offp;
804 *offp += ep->e2d_reclen;
805 offset += ep->e2d_reclen;
806 *entryoffsetinblockp = offset;
807 if (ep->e2d_ino)
808 *endusefulp = *offp;
809 /*
810 * Get pointer to the next entry.
811 */
812 ep = (void *)((char *)data + offset);
813 }
814
815 return 0;
816 }
817
818 /*
819 * Do consistency checking on a directory entry:
820 * record length must be multiple of 4
821 * entry must fit in rest of its dirblksize block
822 * record must be large enough to contain entry
823 * name is not longer than EXT2FS_MAXNAMLEN
824 * name must be as long as advertised, and null terminated
825 */
826 /*
827 * changed so that it confirms to ext2fs_check_dir_entry
828 */
829 static int
830 ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de,
831 int entryoffsetinblock)
832 {
833 struct ufsmount *ump = VFSTOUFS(dp->v_mount);
834 int dirblksiz = ump->um_dirblksiz;
835
836 const char *error_msg = NULL;
837 int reclen = fs2h16(de->e2d_reclen);
838 int namlen = de->e2d_namlen;
839
840 if (reclen < EXT2FS_DIRSIZ(1)) /* e2d_namlen = 1 */
841 error_msg = "rec_len is smaller than minimal";
842 else if (reclen % 4 != 0)
843 error_msg = "rec_len % 4 != 0";
844 else if (namlen > EXT2FS_MAXNAMLEN)
845 error_msg = "namlen > EXT2FS_MAXNAMLEN";
846 else if (reclen < EXT2FS_DIRSIZ(namlen))
847 error_msg = "reclen is too small for name_len";
848 else if (entryoffsetinblock + reclen > dirblksiz)
849 error_msg = "directory entry across blocks";
850 else if (fs2h32(de->e2d_ino) >
851 VTOI(dp)->i_e2fs->e2fs.e2fs_icount)
852 error_msg = "inode out of bounds";
853
854 if (error_msg != NULL) {
855 printf( "bad directory entry: %s\n"
856 "offset=%d, inode=%lu, rec_len=%d, name_len=%d \n",
857 error_msg, entryoffsetinblock,
858 (unsigned long) fs2h32(de->e2d_ino),
859 reclen, namlen);
860 panic("ext2fs_dirbadentry");
861 }
862 return error_msg == NULL ? 0 : 1;
863 }
864
865 /*
866 * Write a directory entry after a call to namei, using the parameters
867 * that it left in nameidata. The argument ip is the inode which the new
868 * directory entry will refer to. Dvp is a pointer to the directory to
869 * be written, which was left locked by namei. Remaining parameters
870 * (ulr_offset, ulr_count) indicate how the space for the new
871 * entry is to be obtained.
872 */
873 int
874 ext2fs_direnter(struct inode *ip, struct vnode *dvp,
875 const struct ufs_lookup_results *ulr, struct componentname *cnp)
876 {
877 struct inode *dp;
878 struct ext2fs_direct newdir;
879 struct iovec aiov;
880 struct uio auio;
881 int error;
882 struct ufsmount *ump = VFSTOUFS(dvp->v_mount);
883 int dirblksiz = ump->um_dirblksiz;
884 size_t newentrysize;
885
886 dp = VTOI(dvp);
887
888 newdir.e2d_ino = h2fs32(ip->i_number);
889 newdir.e2d_namlen = cnp->cn_namelen;
890 if (EXT2F_HAS_INCOMPAT_FEATURE(ip->i_e2fs, EXT2F_INCOMPAT_FTYPE)) {
891 newdir.e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
892 } else {
893 newdir.e2d_type = 0;
894 }
895 memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1);
896 newentrysize = EXT2FS_DIRSIZ(cnp->cn_namelen);
897
898 if (ext2fs_htree_has_idx(dp)) {
899 error = ext2fs_htree_add_entry(dvp, &newdir, cnp, newentrysize);
900 if (error) {
901 dp->i_e2fs_flags &= ~EXT2_INDEX;
902 dp->i_flag |= IN_CHANGE | IN_UPDATE;
903 }
904 return error;
905 }
906
907 /*
908 * TODO check if Htree index is not created for the directory then
909 * create one if directory entries get overflew the first dir-block
910 */
911 if (ulr->ulr_count == 0) {
912 /*
913 * If ulr_count is 0, then namei could find no
914 * space in the directory. Here, ulr_offset will
915 * be on a directory block boundary and we will write the
916 * new entry into a fresh block.
917 */
918 if (ulr->ulr_offset & (dirblksiz - 1))
919 panic("ext2fs_direnter: newblk");
920 auio.uio_offset = ulr->ulr_offset;
921 newdir.e2d_reclen = h2fs16(dirblksiz);
922 auio.uio_resid = newentrysize;
923 aiov.iov_len = newentrysize;
924 aiov.iov_base = (void *)&newdir;
925 auio.uio_iov = &aiov;
926 auio.uio_iovcnt = 1;
927 auio.uio_rw = UIO_WRITE;
928 UIO_SETUP_SYSSPACE(&auio);
929 error = ext2fs_bufwr(dvp, &auio, IO_SYNC, cnp->cn_cred);
930 if (dirblksiz > dvp->v_mount->mnt_stat.f_bsize)
931 /* XXX should grow with balloc() */
932 panic("ext2fs_direnter: frag size");
933 else if (!error) {
934 error = ext2fs_setsize(dp,
935 roundup(ext2fs_size(dp), dirblksiz));
936 if (error)
937 return error;
938 dp->i_flag |= IN_CHANGE;
939 uvm_vnp_setsize(dvp, ext2fs_size(dp));
940 }
941 return error;
942 }
943
944 error = ext2fs_add_entry(dvp, &newdir, ulr, newentrysize);
945
946 if (!error && ulr->ulr_endoff && ulr->ulr_endoff < ext2fs_size(dp))
947 error = ext2fs_truncate(dvp, (off_t)ulr->ulr_endoff, IO_SYNC,
948 cnp->cn_cred);
949 return error;
950 }
951
952 /*
953 * Insert an entry into the directory block.
954 * Compact the contents.
955 */
956
957 int
958 ext2fs_add_entry(struct vnode* dvp, struct ext2fs_direct *entry,
959 const struct ufs_lookup_results *ulr, size_t newentrysize)
960 {
961 struct ext2fs_direct *ep, *nep;
962 struct inode *dp;
963 struct buf *bp;
964 u_int dsize;
965 int error, loc, spacefree;
966 char *dirbuf;
967
968 dp = VTOI(dvp);
969
970 /*
971 * If ulr_count is non-zero, then namei found space
972 * for the new entry in the range ulr_offset to
973 * ulr_offset + ulr_count in the directory.
974 * To use this space, we may have to compact the entries located
975 * there, by copying them together towards the beginning of the
976 * block, leaving the free space in one usable chunk at the end.
977 */
978
979 /*
980 * Get the block containing the space for the new directory entry.
981 */
982 if ((error = ext2fs_blkatoff(dvp, (off_t)ulr->ulr_offset, &dirbuf, &bp)) != 0)
983 return error;
984 /*
985 * Find space for the new entry. In the simple case, the entry at
986 * offset base will have the space. If it does not, then namei
987 * arranged that compacting the region ulr_offset to
988 * ulr_offset + ulr_count would yield the
989 * space.
990 */
991 ep = (struct ext2fs_direct *)dirbuf;
992 dsize = EXT2FS_DIRSIZ(ep->e2d_namlen);
993 spacefree = fs2h16(ep->e2d_reclen) - dsize;
994 for (loc = fs2h16(ep->e2d_reclen); loc < ulr->ulr_count;) {
995 nep = (struct ext2fs_direct *)(dirbuf + loc);
996 if (ep->e2d_ino) {
997 /* trim the existing slot */
998 ep->e2d_reclen = h2fs16(dsize);
999 ep = (struct ext2fs_direct *)((char *)ep + dsize);
1000 } else {
1001 /* overwrite; nothing there; header is ours */
1002 spacefree += dsize;
1003 }
1004 dsize = EXT2FS_DIRSIZ(nep->e2d_namlen);
1005 spacefree += fs2h16(nep->e2d_reclen) - dsize;
1006 loc += fs2h16(nep->e2d_reclen);
1007 memcpy((void *)ep, (void *)nep, dsize);
1008 }
1009 /*
1010 * Update the pointer fields in the previous entry (if any),
1011 * copy in the new entry, and write out the block.
1012 */
1013 if (ep->e2d_ino == 0) {
1014 #ifdef DIAGNOSTIC
1015 if (spacefree + dsize < newentrysize)
1016 panic("ext2fs_direnter: compact1");
1017 #endif
1018 entry->e2d_reclen = h2fs16(spacefree + dsize);
1019 } else {
1020 #ifdef DIAGNOSTIC
1021 if (spacefree < newentrysize) {
1022 printf("ext2fs_direnter: compact2 %u %u",
1023 (u_int)spacefree, (u_int)newentrysize);
1024 panic("ext2fs_direnter: compact2");
1025 }
1026 #endif
1027 entry->e2d_reclen = h2fs16(spacefree);
1028 ep->e2d_reclen = h2fs16(dsize);
1029 ep = (struct ext2fs_direct *)((char *)ep + dsize);
1030 }
1031 memcpy(ep, entry, (u_int)newentrysize);
1032 error = VOP_BWRITE(bp->b_vp, bp);
1033 dp->i_flag |= IN_CHANGE | IN_UPDATE;
1034 return error;
1035 }
1036
1037 /*
1038 * Remove a directory entry after a call to namei, using
1039 * the auxiliary results it provided. The entry
1040 * ulr_offset contains the offset into the directory of the
1041 * entry to be eliminated. The ulr_count field contains the
1042 * size of the previous record in the directory. If this
1043 * is 0, the first entry is being deleted, so we need only
1044 * zero the inode number to mark the entry as free. If the
1045 * entry is not the first in the directory, we must reclaim
1046 * the space of the now empty record by adding the record size
1047 * to the size of the previous entry.
1048 */
1049 int
1050 ext2fs_dirremove(struct vnode *dvp, const struct ufs_lookup_results *ulr,
1051 struct componentname *cnp)
1052 {
1053 struct inode *dp;
1054 struct ext2fs_direct *ep;
1055 struct buf *bp;
1056 int error;
1057
1058 dp = VTOI(dvp);
1059
1060 if (ulr->ulr_count == 0) {
1061 /*
1062 * First entry in block: set d_ino to zero.
1063 */
1064 error = ext2fs_blkatoff(dvp, (off_t)ulr->ulr_offset,
1065 (void *)&ep, &bp);
1066 if (error != 0)
1067 return error;
1068 ep->e2d_ino = 0;
1069 error = VOP_BWRITE(bp->b_vp, bp);
1070 dp->i_flag |= IN_CHANGE | IN_UPDATE;
1071 return error;
1072 }
1073 /*
1074 * Collapse new free space into previous entry.
1075 */
1076 error = ext2fs_blkatoff(dvp, (off_t)(ulr->ulr_offset - ulr->ulr_count),
1077 (void *)&ep, &bp);
1078 if (error != 0)
1079 return error;
1080 ep->e2d_reclen = h2fs16(fs2h16(ep->e2d_reclen) + ulr->ulr_reclen);
1081 error = VOP_BWRITE(bp->b_vp, bp);
1082 dp->i_flag |= IN_CHANGE | IN_UPDATE;
1083 return error;
1084 }
1085
1086 /*
1087 * Rewrite an existing directory entry to point at the inode
1088 * supplied. The parameters describing the directory entry are
1089 * set up by a call to namei.
1090 */
1091 int
1092 ext2fs_dirrewrite(struct inode *dp, const struct ufs_lookup_results *ulr,
1093 struct inode *ip, struct componentname *cnp)
1094 {
1095 struct buf *bp;
1096 struct ext2fs_direct *ep;
1097 struct vnode *vdp = ITOV(dp);
1098 int error;
1099
1100 error = ext2fs_blkatoff(vdp, (off_t)ulr->ulr_offset, (void *)&ep, &bp);
1101 if (error != 0)
1102 return error;
1103 ep->e2d_ino = h2fs32(ip->i_number);
1104 if (EXT2F_HAS_INCOMPAT_FEATURE(dp->i_e2fs, EXT2F_INCOMPAT_FTYPE)) {
1105 ep->e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
1106 } else {
1107 ep->e2d_type = 0;
1108 }
1109 error = VOP_BWRITE(bp->b_vp, bp);
1110 dp->i_flag |= IN_CHANGE | IN_UPDATE;
1111 return error;
1112 }
1113
1114 /*
1115 * Check if a directory is empty or not.
1116 * Inode supplied must be locked.
1117 *
1118 * Using a struct dirtemplate here is not precisely
1119 * what we want, but better than using a struct ext2fs_direct.
1120 *
1121 * NB: does not handle corrupted directories.
1122 */
1123 int
1124 ext2fs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred)
1125 {
1126 off_t off;
1127 struct ext2fs_dirtemplate dbuf;
1128 struct ext2fs_direct *dp = (struct ext2fs_direct *)&dbuf;
1129 int error, namlen;
1130 size_t count;
1131
1132 #define MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2)
1133
1134 for (off = 0; off < ext2fs_size(ip); off += fs2h16(dp->e2d_reclen)) {
1135 error = ufs_bufio(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ,
1136 off, IO_NODELOCKED, cred, &count, NULL);
1137 /*
1138 * Since we read MINDIRSIZ, residual must
1139 * be 0 unless we're at end of file.
1140 */
1141 if (error || count != 0)
1142 return 0;
1143 /* avoid infinite loops */
1144 if (dp->e2d_reclen == 0)
1145 return 0;
1146 /* skip empty entries */
1147 if (dp->e2d_ino == 0)
1148 continue;
1149 /* accept only "." and ".." */
1150 namlen = dp->e2d_namlen;
1151 if (namlen > 2)
1152 return 0;
1153 if (dp->e2d_name[0] != '.')
1154 return 0;
1155 /*
1156 * At this point namlen must be 1 or 2.
1157 * 1 implies ".", 2 implies ".." if second
1158 * char is also "."
1159 */
1160 if (namlen == 1)
1161 continue;
1162 if (dp->e2d_name[1] == '.' && fs2h32(dp->e2d_ino) == parentino)
1163 continue;
1164 return 0;
1165 }
1166 return 1;
1167 }
1168