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