ext2fs_lookup.c revision 1.86 1 /* $NetBSD: ext2fs_lookup.c,v 1.86 2016/08/14 11:46:05 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.86 2016/08/14 11:46:05 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 #if 0
843 else if (reclen % 4 != 0)
844 error_msg = "rec_len % 4 != 0";
845 #endif
846 else if (namlen > EXT2FS_MAXNAMLEN)
847 error_msg = "namlen > EXT2FS_MAXNAMLEN";
848 else if (reclen < EXT2FS_DIRSIZ(namlen))
849 error_msg = "reclen is too small for name_len";
850 else if (entryoffsetinblock + reclen > dirblksiz)
851 error_msg = "directory entry across blocks";
852 else if (fs2h32(de->e2d_ino) >
853 VTOI(dp)->i_e2fs->e2fs.e2fs_icount)
854 error_msg = "inode out of bounds";
855
856 if (error_msg != NULL) {
857 printf( "bad directory entry: %s\n"
858 "offset=%d, inode=%lu, rec_len=%d, name_len=%d \n",
859 error_msg, entryoffsetinblock,
860 (unsigned long) fs2h32(de->e2d_ino),
861 reclen, namlen);
862 panic("ext2fs_dirbadentry");
863 }
864 return error_msg == NULL ? 0 : 1;
865 }
866
867 /*
868 * Write a directory entry after a call to namei, using the parameters
869 * that it left in nameidata. The argument ip is the inode which the new
870 * directory entry will refer to. Dvp is a pointer to the directory to
871 * be written, which was left locked by namei. Remaining parameters
872 * (ulr_offset, ulr_count) indicate how the space for the new
873 * entry is to be obtained.
874 */
875 int
876 ext2fs_direnter(struct inode *ip, struct vnode *dvp,
877 const struct ufs_lookup_results *ulr, struct componentname *cnp)
878 {
879 struct inode *dp;
880 struct ext2fs_direct newdir;
881 struct iovec aiov;
882 struct uio auio;
883 int error, newentrysize;
884 struct ufsmount *ump = VFSTOUFS(dvp->v_mount);
885 int dirblksiz = ump->um_dirblksiz;
886
887 dp = VTOI(dvp);
888
889 newdir.e2d_ino = h2fs32(ip->i_number);
890 newdir.e2d_namlen = cnp->cn_namelen;
891 if (EXT2F_HAS_INCOMPAT_FEATURE(ip->i_e2fs, EXT2F_INCOMPAT_FTYPE)) {
892 newdir.e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
893 } else {
894 newdir.e2d_type = 0;
895 }
896 memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1);
897 newentrysize = EXT2FS_DIRSIZ(cnp->cn_namelen);
898
899 if (ext2fs_htree_has_idx(dp)) {
900 error = ext2fs_htree_add_entry(dvp, &newdir, cnp);
901 if (error) {
902 dp->i_e2fs_flags&= ~EXT2_INDEX;
903 dp->i_flag |= IN_CHANGE | IN_UPDATE;
904 }
905 return error;
906 }
907
908 /*
909 * TODO check if Htree index is not created for the directory then
910 * create one if directory entries get overflew the first dir-block
911 */
912 if (ulr->ulr_count == 0) {
913 /*
914 * If ulr_count is 0, then namei could find no
915 * space in the directory. Here, ulr_offset will
916 * be on a directory block boundary and we will write the
917 * new entry into a fresh block.
918 */
919 if (ulr->ulr_offset & (dirblksiz - 1))
920 panic("ext2fs_direnter: newblk");
921 auio.uio_offset = ulr->ulr_offset;
922 newdir.e2d_reclen = h2fs16(dirblksiz);
923 auio.uio_resid = newentrysize;
924 aiov.iov_len = newentrysize;
925 aiov.iov_base = (void *)&newdir;
926 auio.uio_iov = &aiov;
927 auio.uio_iovcnt = 1;
928 auio.uio_rw = UIO_WRITE;
929 UIO_SETUP_SYSSPACE(&auio);
930 error = ext2fs_bufwr(dvp, &auio, IO_SYNC, cnp->cn_cred);
931 if (dirblksiz > dvp->v_mount->mnt_stat.f_bsize)
932 /* XXX should grow with balloc() */
933 panic("ext2fs_direnter: frag size");
934 else if (!error) {
935 error = ext2fs_setsize(dp,
936 roundup(ext2fs_size(dp), dirblksiz));
937 if (error)
938 return error;
939 dp->i_flag |= IN_CHANGE;
940 uvm_vnp_setsize(dvp, ext2fs_size(dp));
941 }
942 return error;
943 }
944
945 error = ext2fs_add_entry(dvp, &newdir, ulr);
946
947 if (!error && ulr->ulr_endoff && ulr->ulr_endoff < ext2fs_size(dp))
948 error = ext2fs_truncate(dvp, (off_t)ulr->ulr_endoff, IO_SYNC,
949 cnp->cn_cred);
950 return error;
951 }
952
953 /*
954 * Insert an entry into the directory block.
955 * Compact the contents.
956 */
957
958 int
959 ext2fs_add_entry (struct vnode* dvp, struct ext2fs_direct *entry,
960 const struct ufs_lookup_results *ulr)
961 {
962 struct ext2fs_direct *ep, *nep;
963 struct inode *dp;
964 struct buf *bp;
965 u_int dsize;
966 int error, loc, newentrysize, spacefree;
967 char *dirbuf;
968
969 dp = VTOI(dvp);
970
971 /*
972 * If ulr_count is non-zero, then namei found space
973 * for the new entry in the range ulr_offset to
974 * ulr_offset + ulr_count in the directory.
975 * To use this space, we may have to compact the entries located
976 * there, by copying them together towards the beginning of the
977 * block, leaving the free space in one usable chunk at the end.
978 */
979
980 /*
981 * Get the block containing the space for the new directory entry.
982 */
983 if ((error = ext2fs_blkatoff(dvp, (off_t)ulr->ulr_offset, &dirbuf, &bp)) != 0)
984 return error;
985 /*
986 * Find space for the new entry. In the simple case, the entry at
987 * offset base will have the space. If it does not, then namei
988 * arranged that compacting the region ulr_offset to
989 * ulr_offset + ulr_count would yield the
990 * space.
991 */
992 ep = (struct ext2fs_direct *)dirbuf;
993 newentrysize = dsize = EXT2FS_DIRSIZ(ep->e2d_namlen);
994 spacefree = fs2h16(ep->e2d_reclen) - dsize;
995 for (loc = fs2h16(ep->e2d_reclen); loc < ulr->ulr_count;) {
996 nep = (struct ext2fs_direct *)(dirbuf + loc);
997 if (ep->e2d_ino) {
998 /* trim the existing slot */
999 ep->e2d_reclen = h2fs16(dsize);
1000 ep = (struct ext2fs_direct *)((char *)ep + dsize);
1001 } else {
1002 /* overwrite; nothing there; header is ours */
1003 spacefree += dsize;
1004 }
1005 dsize = EXT2FS_DIRSIZ(nep->e2d_namlen);
1006 spacefree += fs2h16(nep->e2d_reclen) - dsize;
1007 loc += fs2h16(nep->e2d_reclen);
1008 memcpy((void *)ep, (void *)nep, dsize);
1009 }
1010 /*
1011 * Update the pointer fields in the previous entry (if any),
1012 * copy in the new entry, and write out the block.
1013 */
1014 if (ep->e2d_ino == 0) {
1015 #ifdef DIAGNOSTIC
1016 if (spacefree + dsize < newentrysize)
1017 panic("ext2fs_direnter: compact1");
1018 #endif
1019 entry->e2d_reclen = h2fs16(spacefree + dsize);
1020 } else {
1021 #ifdef DIAGNOSTIC
1022 if (spacefree < newentrysize) {
1023 printf("ext2fs_direnter: compact2 %u %u",
1024 (u_int)spacefree, (u_int)newentrysize);
1025 panic("ext2fs_direnter: compact2");
1026 }
1027 #endif
1028 entry->e2d_reclen = h2fs16(spacefree);
1029 ep->e2d_reclen = h2fs16(dsize);
1030 ep = (struct ext2fs_direct *)((char *)ep + dsize);
1031 }
1032 memcpy(ep, entry, (u_int)newentrysize);
1033 error = VOP_BWRITE(bp->b_vp, bp);
1034 dp->i_flag |= IN_CHANGE | IN_UPDATE;
1035 return error;
1036 }
1037
1038 /*
1039 * Remove a directory entry after a call to namei, using
1040 * the auxiliary results it provided. The entry
1041 * ulr_offset contains the offset into the directory of the
1042 * entry to be eliminated. The ulr_count field contains the
1043 * size of the previous record in the directory. If this
1044 * is 0, the first entry is being deleted, so we need only
1045 * zero the inode number to mark the entry as free. If the
1046 * entry is not the first in the directory, we must reclaim
1047 * the space of the now empty record by adding the record size
1048 * to the size of the previous entry.
1049 */
1050 int
1051 ext2fs_dirremove(struct vnode *dvp, const struct ufs_lookup_results *ulr,
1052 struct componentname *cnp)
1053 {
1054 struct inode *dp;
1055 struct ext2fs_direct *ep;
1056 struct buf *bp;
1057 int error;
1058
1059 dp = VTOI(dvp);
1060
1061 if (ulr->ulr_count == 0) {
1062 /*
1063 * First entry in block: set d_ino to zero.
1064 */
1065 error = ext2fs_blkatoff(dvp, (off_t)ulr->ulr_offset,
1066 (void *)&ep, &bp);
1067 if (error != 0)
1068 return error;
1069 ep->e2d_ino = 0;
1070 error = VOP_BWRITE(bp->b_vp, bp);
1071 dp->i_flag |= IN_CHANGE | IN_UPDATE;
1072 return error;
1073 }
1074 /*
1075 * Collapse new free space into previous entry.
1076 */
1077 error = ext2fs_blkatoff(dvp, (off_t)(ulr->ulr_offset - ulr->ulr_count),
1078 (void *)&ep, &bp);
1079 if (error != 0)
1080 return error;
1081 ep->e2d_reclen = h2fs16(fs2h16(ep->e2d_reclen) + ulr->ulr_reclen);
1082 error = VOP_BWRITE(bp->b_vp, bp);
1083 dp->i_flag |= IN_CHANGE | IN_UPDATE;
1084 return error;
1085 }
1086
1087 /*
1088 * Rewrite an existing directory entry to point at the inode
1089 * supplied. The parameters describing the directory entry are
1090 * set up by a call to namei.
1091 */
1092 int
1093 ext2fs_dirrewrite(struct inode *dp, const struct ufs_lookup_results *ulr,
1094 struct inode *ip, struct componentname *cnp)
1095 {
1096 struct buf *bp;
1097 struct ext2fs_direct *ep;
1098 struct vnode *vdp = ITOV(dp);
1099 int error;
1100
1101 error = ext2fs_blkatoff(vdp, (off_t)ulr->ulr_offset, (void *)&ep, &bp);
1102 if (error != 0)
1103 return error;
1104 ep->e2d_ino = h2fs32(ip->i_number);
1105 if (EXT2F_HAS_INCOMPAT_FEATURE(dp->i_e2fs, EXT2F_INCOMPAT_FTYPE)) {
1106 ep->e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
1107 } else {
1108 ep->e2d_type = 0;
1109 }
1110 error = VOP_BWRITE(bp->b_vp, bp);
1111 dp->i_flag |= IN_CHANGE | IN_UPDATE;
1112 return error;
1113 }
1114
1115 /*
1116 * Check if a directory is empty or not.
1117 * Inode supplied must be locked.
1118 *
1119 * Using a struct dirtemplate here is not precisely
1120 * what we want, but better than using a struct ext2fs_direct.
1121 *
1122 * NB: does not handle corrupted directories.
1123 */
1124 int
1125 ext2fs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred)
1126 {
1127 off_t off;
1128 struct ext2fs_dirtemplate dbuf;
1129 struct ext2fs_direct *dp = (struct ext2fs_direct *)&dbuf;
1130 int error, namlen;
1131 size_t count;
1132
1133 #define MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2)
1134
1135 for (off = 0; off < ext2fs_size(ip); off += fs2h16(dp->e2d_reclen)) {
1136 error = ufs_bufio(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ,
1137 off, IO_NODELOCKED, cred, &count, NULL);
1138 /*
1139 * Since we read MINDIRSIZ, residual must
1140 * be 0 unless we're at end of file.
1141 */
1142 if (error || count != 0)
1143 return 0;
1144 /* avoid infinite loops */
1145 if (dp->e2d_reclen == 0)
1146 return 0;
1147 /* skip empty entries */
1148 if (dp->e2d_ino == 0)
1149 continue;
1150 /* accept only "." and ".." */
1151 namlen = dp->e2d_namlen;
1152 if (namlen > 2)
1153 return 0;
1154 if (dp->e2d_name[0] != '.')
1155 return 0;
1156 /*
1157 * At this point namlen must be 1 or 2.
1158 * 1 implies ".", 2 implies ".." if second
1159 * char is also "."
1160 */
1161 if (namlen == 1)
1162 continue;
1163 if (dp->e2d_name[1] == '.' && fs2h32(dp->e2d_ino) == parentino)
1164 continue;
1165 return 0;
1166 }
1167 return 1;
1168 }
1169