1 /* $NetBSD: ufs_lookup.c,v 1.99.4.1 2010/02/14 13:55:29 bouyer Exp $ */
2
3 /*
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 * (c) UNIX System Laboratories, Inc.
7 * All or some portions of this file are derived from material licensed
8 * to the University of California by American Telephone and Telegraph
9 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
10 * the permission of UNIX System Laboratories, Inc.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * @(#)ufs_lookup.c 8.9 (Berkeley) 8/11/94
37 */
38
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: ufs_lookup.c,v 1.99.4.1 2010/02/14 13:55:29 bouyer Exp $");
41
42 #ifdef _KERNEL_OPT
43 #include "opt_ffs.h"
44 #endif
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/namei.h>
49 #include <sys/buf.h>
50 #include <sys/file.h>
51 #include <sys/stat.h>
52 #include <sys/mount.h>
53 #include <sys/vnode.h>
54 #include <sys/kernel.h>
55 #include <sys/kauth.h>
56 #include <sys/wapbl.h>
57 #include <sys/fstrans.h>
58 #include <sys/proc.h>
59 #include <sys/kmem.h>
60
61 #include <ufs/ufs/inode.h>
62 #include <ufs/ufs/dir.h>
63 #ifdef UFS_DIRHASH
64 #include <ufs/ufs/dirhash.h>
65 #endif
66 #include <ufs/ufs/ufsmount.h>
67 #include <ufs/ufs/ufs_extern.h>
68 #include <ufs/ufs/ufs_bswap.h>
69 #include <ufs/ufs/ufs_wapbl.h>
70
71 #include "fs_ffs.h"
72
73 #ifdef DIAGNOSTIC
74 int dirchk = 1;
75 #else
76 int dirchk = 0;
77 #endif
78
79 #define FSFMT(vp) (((vp)->v_mount->mnt_iflag & IMNT_DTYPE) == 0)
80
81 /*
82 * Convert a component of a pathname into a pointer to a locked inode.
83 * This is a very central and rather complicated routine.
84 * If the file system is not maintained in a strict tree hierarchy,
85 * this can result in a deadlock situation (see comments in code below).
86 *
87 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
88 * on whether the name is to be looked up, created, renamed, or deleted.
89 * When CREATE, RENAME, or DELETE is specified, information usable in
90 * creating, renaming, or deleting a directory entry may be calculated.
91 * If flag has LOCKPARENT or'ed into it and the target of the pathname
92 * exists, lookup returns both the target and its parent directory locked.
93 * When creating or renaming and LOCKPARENT is specified, the target may
94 * not be ".". When deleting and LOCKPARENT is specified, the target may
95 * be "."., but the caller must check to ensure it does an vrele and vput
96 * instead of two vputs.
97 *
98 * Overall outline of ufs_lookup:
99 *
100 * check accessibility of directory
101 * look for name in cache, if found, then if at end of path
102 * and deleting or creating, drop it, else return name
103 * search for name in directory, to found or notfound
104 * notfound:
105 * if creating, return locked directory, leaving info on available slots
106 * else return error
107 * found:
108 * if at end of path and deleting, return information to allow delete
109 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
110 * inode and return info to allow rewrite
111 * if not at end, add name to cache; if at end and neither creating
112 * nor deleting, add name to cache
113 */
114 int
115 ufs_lookup(void *v)
116 {
117 struct vop_lookup_args /* {
118 struct vnode *a_dvp;
119 struct vnode **a_vpp;
120 struct componentname *a_cnp;
121 } */ *ap = v;
122 struct vnode *vdp = ap->a_dvp; /* vnode for directory being searched */
123 struct inode *dp = VTOI(vdp); /* inode for directory being searched */
124 struct buf *bp; /* a buffer of directory entries */
125 struct direct *ep; /* the current directory entry */
126 int entryoffsetinblock; /* offset of ep in bp's buffer */
127 enum {NONE, COMPACT, FOUND} slotstatus;
128 doff_t slotoffset; /* offset of area with free space */
129 int slotsize; /* size of area at slotoffset */
130 int slotfreespace; /* amount of space free in slot */
131 int slotneeded; /* size of the entry we're seeking */
132 int numdirpasses; /* strategy for directory search */
133 doff_t endsearch; /* offset to end directory search */
134 doff_t prevoff; /* prev entry dp->i_offset */
135 struct vnode *pdp; /* saved dp during symlink work */
136 struct vnode *tdp; /* returned by VFS_VGET */
137 doff_t enduseful; /* pointer past last used dir slot */
138 u_long bmask; /* block offset mask */
139 int namlen, error;
140 struct vnode **vpp = ap->a_vpp;
141 struct componentname *cnp = ap->a_cnp;
142 kauth_cred_t cred = cnp->cn_cred;
143 int flags;
144 int nameiop = cnp->cn_nameiop;
145 struct ufsmount *ump = dp->i_ump;
146 const int needswap = UFS_MPNEEDSWAP(ump);
147 int dirblksiz = ump->um_dirblksiz;
148 ino_t foundino;
149
150 flags = cnp->cn_flags;
151
152 bp = NULL;
153 slotoffset = -1;
154 *vpp = NULL;
155 endsearch = 0; /* silence compiler warning */
156 /*
157 * Check accessiblity of directory.
158 */
159 if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0)
160 return (error);
161
162 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
163 (nameiop == DELETE || nameiop == RENAME))
164 return (EROFS);
165
166 /*
167 * We now have a segment name to search for, and a directory to search.
168 *
169 * Before tediously performing a linear scan of the directory,
170 * check the name cache to see if the directory/name pair
171 * we are looking for is known already.
172 */
173 if ((error = cache_lookup(vdp, vpp, cnp)) >= 0) {
174 return (error);
175 }
176
177 fstrans_start(vdp->v_mount, FSTRANS_SHARED);
178
179 /*
180 * Suppress search for slots unless creating
181 * file and at end of pathname, in which case
182 * we watch for a place to put the new file in
183 * case it doesn't already exist.
184 */
185 slotstatus = FOUND;
186 slotfreespace = slotsize = slotneeded = 0;
187 if ((nameiop == CREATE || nameiop == RENAME) &&
188 (flags & ISLASTCN)) {
189 slotstatus = NONE;
190 slotneeded = DIRECTSIZ(cnp->cn_namelen);
191 }
192
193 /*
194 * If there is cached information on a previous search of
195 * this directory, pick up where we last left off.
196 * We cache only lookups as these are the most common
197 * and have the greatest payoff. Caching CREATE has little
198 * benefit as it usually must search the entire directory
199 * to determine that the entry does not exist. Caching the
200 * location of the last DELETE or RENAME has not reduced
201 * profiling time and hence has been removed in the interest
202 * of simplicity.
203 */
204 bmask = vdp->v_mount->mnt_stat.f_iosize - 1;
205
206 #ifdef UFS_DIRHASH
207 /*
208 * Use dirhash for fast operations on large directories. The logic
209 * to determine whether to hash the directory is contained within
210 * ufsdirhash_build(); a zero return means that it decided to hash
211 * this directory and it successfully built up the hash table.
212 */
213 if (ufsdirhash_build(dp) == 0) {
214 /* Look for a free slot if needed. */
215 enduseful = dp->i_size;
216 if (slotstatus != FOUND) {
217 slotoffset = ufsdirhash_findfree(dp, slotneeded,
218 &slotsize);
219 if (slotoffset >= 0) {
220 slotstatus = COMPACT;
221 enduseful = ufsdirhash_enduseful(dp);
222 if (enduseful < 0)
223 enduseful = dp->i_size;
224 }
225 }
226 /* Look up the component. */
227 numdirpasses = 1;
228 entryoffsetinblock = 0; /* silence compiler warning */
229 switch (ufsdirhash_lookup(dp, cnp->cn_nameptr, cnp->cn_namelen,
230 &dp->i_offset, &bp, nameiop == DELETE ? &prevoff : NULL)) {
231 case 0:
232 ep = (struct direct *)((char *)bp->b_data +
233 (dp->i_offset & bmask));
234 goto foundentry;
235 case ENOENT:
236 dp->i_offset = roundup(dp->i_size, dirblksiz);
237 goto notfound;
238 default:
239 /* Something failed; just do a linear search. */
240 break;
241 }
242 }
243 #endif /* UFS_DIRHASH */
244
245 if (nameiop != LOOKUP || dp->i_diroff == 0 ||
246 dp->i_diroff >= dp->i_size) {
247 entryoffsetinblock = 0;
248 dp->i_offset = 0;
249 numdirpasses = 1;
250 } else {
251 dp->i_offset = dp->i_diroff;
252 if ((entryoffsetinblock = dp->i_offset & bmask) &&
253 (error = ufs_blkatoff(vdp, (off_t)dp->i_offset,
254 NULL, &bp, false)))
255 goto out;
256 numdirpasses = 2;
257 nchstats.ncs_2passes++;
258 }
259 prevoff = dp->i_offset;
260 endsearch = roundup(dp->i_size, dirblksiz);
261 enduseful = 0;
262
263 searchloop:
264 while (dp->i_offset < endsearch) {
265 if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
266 preempt();
267 /*
268 * If necessary, get the next directory block.
269 */
270 if ((dp->i_offset & bmask) == 0) {
271 if (bp != NULL)
272 brelse(bp, 0);
273 error = ufs_blkatoff(vdp, (off_t)dp->i_offset, NULL,
274 &bp, false);
275 if (error)
276 goto out;
277 entryoffsetinblock = 0;
278 }
279 /*
280 * If still looking for a slot, and at a DIRBLKSIZ
281 * boundary, have to start looking for free space again.
282 */
283 if (slotstatus == NONE &&
284 (entryoffsetinblock & (dirblksiz - 1)) == 0) {
285 slotoffset = -1;
286 slotfreespace = 0;
287 }
288 /*
289 * Get pointer to next entry.
290 * Full validation checks are slow, so we only check
291 * enough to insure forward progress through the
292 * directory. Complete checks can be run by patching
293 * "dirchk" to be true.
294 */
295 KASSERT(bp != NULL);
296 ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock);
297 if (ep->d_reclen == 0 ||
298 (dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock))) {
299 int i;
300
301 ufs_dirbad(dp, dp->i_offset, "mangled entry");
302 i = dirblksiz - (entryoffsetinblock & (dirblksiz - 1));
303 dp->i_offset += i;
304 entryoffsetinblock += i;
305 continue;
306 }
307
308 /*
309 * If an appropriate sized slot has not yet been found,
310 * check to see if one is available. Also accumulate space
311 * in the current block so that we can determine if
312 * compaction is viable.
313 */
314 if (slotstatus != FOUND) {
315 int size = ufs_rw16(ep->d_reclen, needswap);
316
317 if (ep->d_ino != 0)
318 size -= DIRSIZ(FSFMT(vdp), ep, needswap);
319 if (size > 0) {
320 if (size >= slotneeded) {
321 slotstatus = FOUND;
322 slotoffset = dp->i_offset;
323 slotsize = ufs_rw16(ep->d_reclen,
324 needswap);
325 } else if (slotstatus == NONE) {
326 slotfreespace += size;
327 if (slotoffset == -1)
328 slotoffset = dp->i_offset;
329 if (slotfreespace >= slotneeded) {
330 slotstatus = COMPACT;
331 slotsize = dp->i_offset +
332 ufs_rw16(ep->d_reclen,
333 needswap) -
334 slotoffset;
335 }
336 }
337 }
338 }
339
340 /*
341 * Check for a name match.
342 */
343 if (ep->d_ino) {
344 #if (BYTE_ORDER == LITTLE_ENDIAN)
345 if (FSFMT(vdp) && needswap == 0)
346 namlen = ep->d_type;
347 else
348 namlen = ep->d_namlen;
349 #else
350 if (FSFMT(vdp) && needswap != 0)
351 namlen = ep->d_type;
352 else
353 namlen = ep->d_namlen;
354 #endif
355 if (namlen == cnp->cn_namelen &&
356 !memcmp(cnp->cn_nameptr, ep->d_name,
357 (unsigned)namlen)) {
358 #ifdef UFS_DIRHASH
359 foundentry:
360 #endif
361 /*
362 * Save directory entry's inode number and
363 * reclen in ndp->ni_ufs area, and release
364 * directory buffer.
365 */
366 if (!FSFMT(vdp) && ep->d_type == DT_WHT) {
367 slotstatus = FOUND;
368 slotoffset = dp->i_offset;
369 slotsize = ufs_rw16(ep->d_reclen,
370 needswap);
371 dp->i_reclen = slotsize;
372 /*
373 * This is used to set dp->i_endoff,
374 * which may be used by ufs_direnter2()
375 * as a length to truncate the
376 * directory to. Therefore, it must
377 * point past the end of the last
378 * non-empty directory entry. We don't
379 * know where that is in this case, so
380 * we effectively disable shrinking by
381 * using the existing size of the
382 * directory.
383 *
384 * Note that we wouldn't expect to
385 * shrink the directory while rewriting
386 * an existing entry anyway.
387 */
388 enduseful = endsearch;
389 ap->a_cnp->cn_flags |= ISWHITEOUT;
390 numdirpasses--;
391 goto notfound;
392 }
393 foundino = ufs_rw32(ep->d_ino, needswap);
394 dp->i_reclen = ufs_rw16(ep->d_reclen, needswap);
395 goto found;
396 }
397 }
398 prevoff = dp->i_offset;
399 dp->i_offset += ufs_rw16(ep->d_reclen, needswap);
400 entryoffsetinblock += ufs_rw16(ep->d_reclen, needswap);
401 if (ep->d_ino)
402 enduseful = dp->i_offset;
403 }
404 notfound:
405 /*
406 * If we started in the middle of the directory and failed
407 * to find our target, we must check the beginning as well.
408 */
409 if (numdirpasses == 2) {
410 numdirpasses--;
411 dp->i_offset = 0;
412 endsearch = dp->i_diroff;
413 goto searchloop;
414 }
415 if (bp != NULL)
416 brelse(bp, 0);
417 /*
418 * If creating, and at end of pathname and current
419 * directory has not been removed, then can consider
420 * allowing file to be created.
421 */
422 if ((nameiop == CREATE || nameiop == RENAME ||
423 (nameiop == DELETE &&
424 (ap->a_cnp->cn_flags & DOWHITEOUT) &&
425 (ap->a_cnp->cn_flags & ISWHITEOUT))) &&
426 (flags & ISLASTCN) && dp->i_ffs_effnlink != 0) {
427 /*
428 * Access for write is interpreted as allowing
429 * creation of files in the directory.
430 */
431 error = VOP_ACCESS(vdp, VWRITE, cred);
432 if (error)
433 goto out;
434 /*
435 * Return an indication of where the new directory
436 * entry should be put. If we didn't find a slot,
437 * then set dp->i_count to 0 indicating
438 * that the new slot belongs at the end of the
439 * directory. If we found a slot, then the new entry
440 * can be put in the range from dp->i_offset to
441 * dp->i_offset + dp->i_count.
442 */
443 if (slotstatus == NONE) {
444 dp->i_offset = roundup(dp->i_size, dirblksiz);
445 dp->i_count = 0;
446 enduseful = dp->i_offset;
447 } else if (nameiop == DELETE) {
448 dp->i_offset = slotoffset;
449 if ((dp->i_offset & (dirblksiz - 1)) == 0)
450 dp->i_count = 0;
451 else
452 dp->i_count = dp->i_offset - prevoff;
453 } else {
454 dp->i_offset = slotoffset;
455 dp->i_count = slotsize;
456 if (enduseful < slotoffset + slotsize)
457 enduseful = slotoffset + slotsize;
458 }
459 dp->i_endoff = roundup(enduseful, dirblksiz);
460 #if 0 /* commented out by dbj. none of the on disk fields changed */
461 dp->i_flag |= IN_CHANGE | IN_UPDATE;
462 #endif
463 /*
464 * We return with the directory locked, so that
465 * the parameters we set up above will still be
466 * valid if we actually decide to do a direnter().
467 * We return ni_vp == NULL to indicate that the entry
468 * does not currently exist; we leave a pointer to
469 * the (locked) directory inode in ndp->ni_dvp.
470 * The pathname buffer is saved so that the name
471 * can be obtained later.
472 *
473 * NB - if the directory is unlocked, then this
474 * information cannot be used.
475 */
476 cnp->cn_flags |= SAVENAME;
477 error = EJUSTRETURN;
478 goto out;
479 }
480 /*
481 * Insert name into cache (as non-existent) if appropriate.
482 */
483 if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
484 cache_enter(vdp, *vpp, cnp);
485 error = ENOENT;
486 goto out;
487
488 found:
489 if (numdirpasses == 2)
490 nchstats.ncs_pass2++;
491 /*
492 * Check that directory length properly reflects presence
493 * of this entry.
494 */
495 if (dp->i_offset + DIRSIZ(FSFMT(vdp), ep, needswap) > dp->i_size) {
496 ufs_dirbad(dp, dp->i_offset, "i_size too small");
497 dp->i_size = dp->i_offset + DIRSIZ(FSFMT(vdp), ep, needswap);
498 DIP_ASSIGN(dp, size, dp->i_size);
499 dp->i_flag |= IN_CHANGE | IN_UPDATE;
500 UFS_WAPBL_UPDATE(vdp, NULL, NULL, UPDATE_DIROP);
501 }
502 brelse(bp, 0);
503
504 /*
505 * Found component in pathname.
506 * If the final component of path name, save information
507 * in the cache as to where the entry was found.
508 */
509 if ((flags & ISLASTCN) && nameiop == LOOKUP)
510 dp->i_diroff = dp->i_offset &~ (dirblksiz - 1);
511
512 /*
513 * If deleting, and at end of pathname, return
514 * parameters which can be used to remove file.
515 * Lock the inode, being careful with ".".
516 */
517 if (nameiop == DELETE && (flags & ISLASTCN)) {
518 /*
519 * Write access to directory required to delete files.
520 */
521 error = VOP_ACCESS(vdp, VWRITE, cred);
522 if (error)
523 goto out;
524 /*
525 * Return pointer to current entry in dp->i_offset,
526 * and distance past previous entry (if there
527 * is a previous entry in this block) in dp->i_count.
528 * Save directory inode pointer in ndp->ni_dvp for dirremove().
529 */
530 if ((dp->i_offset & (dirblksiz - 1)) == 0)
531 dp->i_count = 0;
532 else
533 dp->i_count = dp->i_offset - prevoff;
534 if (dp->i_number == foundino) {
535 VREF(vdp);
536 *vpp = vdp;
537 error = 0;
538 goto out;
539 }
540 if (flags & ISDOTDOT)
541 VOP_UNLOCK(vdp, 0); /* race to get the inode */
542 error = VFS_VGET(vdp->v_mount, foundino, &tdp);
543 if (flags & ISDOTDOT)
544 vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY);
545 if (error)
546 goto out;
547 /*
548 * If directory is "sticky", then user must own
549 * the directory, or the file in it, else she
550 * may not delete it (unless she's root). This
551 * implements append-only directories.
552 */
553 if ((dp->i_mode & ISVTX) &&
554 kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
555 NULL) != 0 &&
556 kauth_cred_geteuid(cred) != dp->i_uid &&
557 VTOI(tdp)->i_uid != kauth_cred_geteuid(cred)) {
558 vput(tdp);
559 error = EPERM;
560 goto out;
561 }
562 *vpp = tdp;
563 error = 0;
564 goto out;
565 }
566
567 /*
568 * If rewriting (RENAME), return the inode and the
569 * information required to rewrite the present directory
570 * Must get inode of directory entry to verify it's a
571 * regular file, or empty directory.
572 */
573 if (nameiop == RENAME && (flags & ISLASTCN)) {
574 error = VOP_ACCESS(vdp, VWRITE, cred);
575 if (error)
576 goto out;
577 /*
578 * Careful about locking second inode.
579 * This can only occur if the target is ".".
580 */
581 if (dp->i_number == foundino) {
582 error = EISDIR;
583 goto out;
584 }
585 if (flags & ISDOTDOT)
586 VOP_UNLOCK(vdp, 0); /* race to get the inode */
587 error = VFS_VGET(vdp->v_mount, foundino, &tdp);
588 if (flags & ISDOTDOT)
589 vn_lock(vdp, LK_EXCLUSIVE | LK_RETRY);
590 if (error)
591 goto out;
592 *vpp = tdp;
593 cnp->cn_flags |= SAVENAME;
594 error = 0;
595 goto out;
596 }
597
598 /*
599 * Step through the translation in the name. We do not `vput' the
600 * directory because we may need it again if a symbolic link
601 * is relative to the current directory. Instead we save it
602 * unlocked as "pdp". We must get the target inode before unlocking
603 * the directory to insure that the inode will not be removed
604 * before we get it. We prevent deadlock by always fetching
605 * inodes from the root, moving down the directory tree. Thus
606 * when following backward pointers ".." we must unlock the
607 * parent directory before getting the requested directory.
608 * There is a potential race condition here if both the current
609 * and parent directories are removed before the VFS_VGET for the
610 * inode associated with ".." returns. We hope that this occurs
611 * infrequently since we cannot avoid this race condition without
612 * implementing a sophisticated deadlock detection algorithm.
613 * Note also that this simple deadlock detection scheme will not
614 * work if the file system has any hard links other than ".."
615 * that point backwards in the directory structure.
616 */
617 pdp = vdp;
618 if (flags & ISDOTDOT) {
619 VOP_UNLOCK(pdp, 0); /* race to get the inode */
620 error = VFS_VGET(vdp->v_mount, foundino, &tdp);
621 vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY);
622 if (error) {
623 goto out;
624 }
625 *vpp = tdp;
626 } else if (dp->i_number == foundino) {
627 VREF(vdp); /* we want ourself, ie "." */
628 *vpp = vdp;
629 } else {
630 error = VFS_VGET(vdp->v_mount, foundino, &tdp);
631 if (error)
632 goto out;
633 *vpp = tdp;
634 }
635
636 /*
637 * Insert name into cache if appropriate.
638 */
639 if (cnp->cn_flags & MAKEENTRY)
640 cache_enter(vdp, *vpp, cnp);
641 error = 0;
642
643 out:
644 fstrans_done(vdp->v_mount);
645 return error;
646 }
647
648 void
649 ufs_dirbad(struct inode *ip, doff_t offset, const char *how)
650 {
651 struct mount *mp;
652
653 mp = ITOV(ip)->v_mount;
654 printf("%s: bad dir ino %llu at offset %d: %s\n",
655 mp->mnt_stat.f_mntonname, (unsigned long long)ip->i_number,
656 offset, how);
657 if ((mp->mnt_stat.f_flag & MNT_RDONLY) == 0)
658 panic("bad dir");
659 }
660
661 /*
662 * Do consistency checking on a directory entry:
663 * record length must be multiple of 4
664 * entry must fit in rest of its DIRBLKSIZ block
665 * record must be large enough to contain entry
666 * name is not longer than FFS_MAXNAMLEN
667 * name must be as long as advertised, and null terminated
668 */
669 int
670 ufs_dirbadentry(struct vnode *dp, struct direct *ep, int entryoffsetinblock)
671 {
672 int i;
673 int namlen;
674 struct ufsmount *ump = VFSTOUFS(dp->v_mount);
675 const int needswap = UFS_MPNEEDSWAP(ump);
676 int dirblksiz = ump->um_dirblksiz;
677
678 #if (BYTE_ORDER == LITTLE_ENDIAN)
679 if (FSFMT(dp) && needswap == 0)
680 namlen = ep->d_type;
681 else
682 namlen = ep->d_namlen;
683 #else
684 if (FSFMT(dp) && needswap != 0)
685 namlen = ep->d_type;
686 else
687 namlen = ep->d_namlen;
688 #endif
689 if ((ufs_rw16(ep->d_reclen, needswap) & 0x3) != 0 ||
690 ufs_rw16(ep->d_reclen, needswap) >
691 dirblksiz - (entryoffsetinblock & (dirblksiz - 1)) ||
692 ufs_rw16(ep->d_reclen, needswap) <
693 DIRSIZ(FSFMT(dp), ep, needswap) ||
694 namlen > FFS_MAXNAMLEN) {
695 /*return (1); */
696 printf("First bad, reclen=%#x, DIRSIZ=%lu, namlen=%d, "
697 "flags=%#x, entryoffsetinblock=%d, dirblksiz = %d\n",
698 ufs_rw16(ep->d_reclen, needswap),
699 (u_long)DIRSIZ(FSFMT(dp), ep, needswap),
700 namlen, dp->v_mount->mnt_flag, entryoffsetinblock,
701 dirblksiz);
702 goto bad;
703 }
704 if (ep->d_ino == 0)
705 return (0);
706 for (i = 0; i < namlen; i++)
707 if (ep->d_name[i] == '\0') {
708 /*return (1); */
709 printf("Second bad\n");
710 goto bad;
711 }
712 if (ep->d_name[i])
713 goto bad;
714 return (0);
715 bad:
716 return (1);
717 }
718
719 /*
720 * Construct a new directory entry after a call to namei, using the
721 * parameters that it left in the componentname argument cnp. The
722 * argument ip is the inode to which the new directory entry will refer.
723 */
724 void
725 ufs_makedirentry(struct inode *ip, struct componentname *cnp,
726 struct direct *newdirp)
727 {
728 #ifdef DIAGNOSTIC
729 if ((cnp->cn_flags & SAVENAME) == 0)
730 panic("makedirentry: missing name");
731 #endif
732 newdirp->d_ino = ip->i_number;
733 newdirp->d_namlen = cnp->cn_namelen;
734 memcpy(newdirp->d_name, cnp->cn_nameptr, (size_t)cnp->cn_namelen);
735 newdirp->d_name[cnp->cn_namelen] = '\0';
736 if (FSFMT(ITOV(ip)))
737 newdirp->d_type = 0;
738 else
739 newdirp->d_type = IFTODT(ip->i_mode);
740 }
741
742 /*
743 * Write a directory entry after a call to namei, using the parameters
744 * that it left in nameidata. The argument dirp is the new directory
745 * entry contents. Dvp is a pointer to the directory to be written,
746 * which was left locked by namei. Remaining parameters (dp->i_offset,
747 * dp->i_count) indicate how the space for the new entry is to be obtained.
748 * Non-null bp indicates that a directory is being created (for the
749 * soft dependency code).
750 */
751 int
752 ufs_direnter(struct vnode *dvp, struct vnode *tvp, struct direct *dirp,
753 struct componentname *cnp, struct buf *newdirbp)
754 {
755 kauth_cred_t cr;
756 struct lwp *l;
757 int newentrysize;
758 struct inode *dp;
759 struct buf *bp;
760 u_int dsize;
761 struct direct *ep, *nep;
762 int error, ret, blkoff, loc, spacefree, flags;
763 char *dirbuf;
764 struct timespec ts;
765 struct ufsmount *ump = VFSTOUFS(dvp->v_mount);
766 const int needswap = UFS_MPNEEDSWAP(ump);
767 int dirblksiz = ump->um_dirblksiz;
768
769 UFS_WAPBL_JLOCK_ASSERT(dvp->v_mount);
770
771 error = 0;
772 cr = cnp->cn_cred;
773 l = curlwp;
774
775 dp = VTOI(dvp);
776 newentrysize = DIRSIZ(0, dirp, 0);
777
778 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 if (dp->i_offset & (dirblksiz - 1))
786 panic("ufs_direnter: newblk");
787 flags = B_CLRBUF;
788 if (!DOINGSOFTDEP(dvp))
789 flags |= B_SYNC;
790 if ((error = UFS_BALLOC(dvp, (off_t)dp->i_offset, dirblksiz,
791 cr, flags, &bp)) != 0) {
792 if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
793 bdwrite(newdirbp);
794 return (error);
795 }
796 dp->i_size = dp->i_offset + dirblksiz;
797 DIP_ASSIGN(dp, size, dp->i_size);
798 dp->i_flag |= IN_CHANGE | IN_UPDATE;
799 uvm_vnp_setsize(dvp, dp->i_size);
800 dirp->d_reclen = ufs_rw16(dirblksiz, needswap);
801 dirp->d_ino = ufs_rw32(dirp->d_ino, needswap);
802 if (FSFMT(dvp)) {
803 #if (BYTE_ORDER == LITTLE_ENDIAN)
804 if (needswap == 0) {
805 #else
806 if (needswap != 0) {
807 #endif
808 u_char tmp = dirp->d_namlen;
809 dirp->d_namlen = dirp->d_type;
810 dirp->d_type = tmp;
811 }
812 }
813 blkoff = dp->i_offset & (ump->um_mountp->mnt_stat.f_iosize - 1);
814 memcpy((char *)bp->b_data + blkoff, dirp, newentrysize);
815 #ifdef UFS_DIRHASH
816 if (dp->i_dirhash != NULL) {
817 ufsdirhash_newblk(dp, dp->i_offset);
818 ufsdirhash_add(dp, dirp, dp->i_offset);
819 ufsdirhash_checkblock(dp, (char *)bp->b_data + blkoff,
820 dp->i_offset);
821 }
822 #endif
823 if (DOINGSOFTDEP(dvp)) {
824 /*
825 * Ensure that the entire newly allocated block is a
826 * valid directory so that future growth within the
827 * block does not have to ensure that the block is
828 * written before the inode.
829 */
830 blkoff += dirblksiz;
831 while (blkoff < bp->b_bcount) {
832 ((struct direct *)
833 ((char *)bp->b_data + blkoff))->d_reclen = dirblksiz;
834 blkoff += dirblksiz;
835 }
836 if (softdep_setup_directory_add(bp, dp, dp->i_offset,
837 ufs_rw32(dirp->d_ino, needswap), newdirbp, 1) == 0) {
838 bdwrite(bp);
839 vfs_timestamp(&ts);
840 return UFS_UPDATE(dvp, &ts, &ts, UPDATE_DIROP);
841 }
842 /* We have just allocated a directory block in an
843 * indirect block. Rather than tracking when it gets
844 * claimed by the inode, we simply do a VOP_FSYNC
845 * now to ensure that it is there (in case the user
846 * does a future fsync). Note that we have to unlock
847 * the inode for the entry that we just entered, as
848 * the VOP_FSYNC may need to lock other inodes which
849 * can lead to deadlock if we also hold a lock on
850 * the newly entered node.
851 */
852 error = VOP_BWRITE(bp);
853 if (error != 0)
854 return (error);
855 if (tvp != NULL)
856 VOP_UNLOCK(tvp, 0);
857 error = VOP_FSYNC(dvp, l->l_cred, FSYNC_WAIT, 0, 0);
858 if (tvp != 0)
859 vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
860 return (error);
861 } else {
862 error = VOP_BWRITE(bp);
863 }
864 vfs_timestamp(&ts);
865 ret = UFS_UPDATE(dvp, &ts, &ts, UPDATE_DIROP);
866 if (error == 0)
867 return (ret);
868 return (error);
869 }
870
871 /*
872 * If dp->i_count is non-zero, then namei found space for the new
873 * entry in the range dp->i_offset to dp->i_offset + dp->i_count
874 * in the directory. To use this space, we may have to compact
875 * the entries located there, by copying them together towards the
876 * beginning of the block, leaving the free space in one usable
877 * chunk at the end.
878 */
879
880 /*
881 * Increase size of directory if entry eats into new space.
882 * This should never push the size past a new multiple of
883 * DIRBLKSIZ.
884 *
885 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
886 */
887 if (dp->i_offset + dp->i_count > dp->i_size) {
888 dp->i_size = dp->i_offset + dp->i_count;
889 DIP_ASSIGN(dp, size, dp->i_size);
890 dp->i_flag |= IN_CHANGE | IN_UPDATE;
891 UFS_WAPBL_UPDATE(dvp, NULL, NULL, UPDATE_DIROP);
892 }
893 /*
894 * Get the block containing the space for the new directory entry.
895 */
896 error = ufs_blkatoff(dvp, (off_t)dp->i_offset, &dirbuf, &bp, true);
897 if (error) {
898 if (DOINGSOFTDEP(dvp) && newdirbp != NULL)
899 bdwrite(newdirbp);
900 return (error);
901 }
902 /*
903 * Find space for the new entry. In the simple case, the entry at
904 * offset base will have the space. If it does not, then namei
905 * arranged that compacting the region dp->i_offset to
906 * dp->i_offset + dp->i_count would yield the space.
907 */
908 ep = (struct direct *)dirbuf;
909 dsize = (ep->d_ino != 0) ? DIRSIZ(FSFMT(dvp), ep, needswap) : 0;
910 spacefree = ufs_rw16(ep->d_reclen, needswap) - dsize;
911 for (loc = ufs_rw16(ep->d_reclen, needswap); loc < dp->i_count; ) {
912 uint16_t reclen;
913
914 nep = (struct direct *)(dirbuf + loc);
915
916 /* Trim the existing slot (NB: dsize may be zero). */
917 ep->d_reclen = ufs_rw16(dsize, needswap);
918 ep = (struct direct *)((char *)ep + dsize);
919
920 reclen = ufs_rw16(nep->d_reclen, needswap);
921 loc += reclen;
922 if (nep->d_ino == 0) {
923 /*
924 * A mid-block unused entry. Such entries are
925 * never created by the kernel, but fsck_ffs
926 * can create them (and it doesn't fix them).
927 *
928 * Add up the free space, and initialise the
929 * relocated entry since we don't memcpy it.
930 */
931 spacefree += reclen;
932 ep->d_ino = 0;
933 dsize = 0;
934 continue;
935 }
936 dsize = DIRSIZ(FSFMT(dvp), nep, needswap);
937 spacefree += reclen - dsize;
938 #ifdef UFS_DIRHASH
939 if (dp->i_dirhash != NULL)
940 ufsdirhash_move(dp, nep,
941 dp->i_offset + ((char *)nep - dirbuf),
942 dp->i_offset + ((char *)ep - dirbuf));
943 #endif
944 if (DOINGSOFTDEP(dvp))
945 softdep_change_directoryentry_offset(dp, dirbuf,
946 (void *)nep, (void *)ep, dsize);
947 else
948 memcpy((void *)ep, (void *)nep, dsize);
949 }
950 /*
951 * Here, `ep' points to a directory entry containing `dsize' in-use
952 * bytes followed by `spacefree' unused bytes. If ep->d_ino == 0,
953 * then the entry is completely unused (dsize == 0). The value
954 * of ep->d_reclen is always indeterminate.
955 *
956 * Update the pointer fields in the previous entry (if any),
957 * copy in the new entry, and write out the block.
958 */
959 if (ep->d_ino == 0 ||
960 (ufs_rw32(ep->d_ino, needswap) == WINO &&
961 memcmp(ep->d_name, dirp->d_name, dirp->d_namlen) == 0)) {
962 if (spacefree + dsize < newentrysize)
963 panic("ufs_direnter: compact1");
964 dirp->d_reclen = spacefree + dsize;
965 } else {
966 if (spacefree < newentrysize)
967 panic("ufs_direnter: compact2");
968 dirp->d_reclen = spacefree;
969 ep->d_reclen = ufs_rw16(dsize, needswap);
970 ep = (struct direct *)((char *)ep + dsize);
971 }
972 dirp->d_reclen = ufs_rw16(dirp->d_reclen, needswap);
973 dirp->d_ino = ufs_rw32(dirp->d_ino, needswap);
974 if (FSFMT(dvp)) {
975 #if (BYTE_ORDER == LITTLE_ENDIAN)
976 if (needswap == 0) {
977 #else
978 if (needswap != 0) {
979 #endif
980 u_char tmp = dirp->d_namlen;
981 dirp->d_namlen = dirp->d_type;
982 dirp->d_type = tmp;
983 }
984 }
985 #ifdef UFS_DIRHASH
986 if (dp->i_dirhash != NULL && (ep->d_ino == 0 ||
987 dirp->d_reclen == spacefree))
988 ufsdirhash_add(dp, dirp, dp->i_offset + ((char *)ep - dirbuf));
989 #endif
990 memcpy((void *)ep, (void *)dirp, (u_int)newentrysize);
991 #ifdef UFS_DIRHASH
992 if (dp->i_dirhash != NULL)
993 ufsdirhash_checkblock(dp, dirbuf -
994 (dp->i_offset & (dirblksiz - 1)),
995 dp->i_offset & ~(dirblksiz - 1));
996 #endif
997 if (DOINGSOFTDEP(dvp)) {
998 softdep_setup_directory_add(bp, dp,
999 dp->i_offset + (char *)ep - dirbuf,
1000 ufs_rw32(dirp->d_ino, needswap), newdirbp, 0);
1001 bdwrite(bp);
1002 } else {
1003 error = VOP_BWRITE(bp);
1004 }
1005 dp->i_flag |= IN_CHANGE | IN_UPDATE;
1006 /*
1007 * If all went well, and the directory can be shortened, proceed
1008 * with the truncation. Note that we have to unlock the inode for
1009 * the entry that we just entered, as the truncation may need to
1010 * lock other inodes which can lead to deadlock if we also hold a
1011 * lock on the newly entered node.
1012 */
1013 if (error == 0 && dp->i_endoff && dp->i_endoff < dp->i_size) {
1014 if (DOINGSOFTDEP(dvp) && (tvp != NULL))
1015 VOP_UNLOCK(tvp, 0);
1016 #ifdef UFS_DIRHASH
1017 if (dp->i_dirhash != NULL)
1018 ufsdirhash_dirtrunc(dp, dp->i_endoff);
1019 #endif
1020 (void) UFS_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC, cr);
1021 if (DOINGSOFTDEP(dvp) && (tvp != NULL))
1022 vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY);
1023 }
1024 UFS_WAPBL_UPDATE(dvp, NULL, NULL, UPDATE_DIROP);
1025 return (error);
1026 }
1027
1028 /*
1029 * Remove a directory entry after a call to namei, using
1030 * the parameters which it left in nameidata. The entry
1031 * dp->i_offset contains the offset into the directory of the
1032 * entry to be eliminated. The dp->i_count field contains the
1033 * size of the previous record in the directory. If this
1034 * is 0, the first entry is being deleted, so we need only
1035 * zero the inode number to mark the entry as free. If the
1036 * entry is not the first in the directory, we must reclaim
1037 * the space of the now empty record by adding the record size
1038 * to the size of the previous entry.
1039 */
1040 int
1041 ufs_dirremove(struct vnode *dvp, struct inode *ip, int flags, int isrmdir)
1042 {
1043 struct inode *dp = VTOI(dvp);
1044 struct direct *ep;
1045 struct buf *bp;
1046 int error;
1047 #ifdef FFS_EI
1048 const int needswap = UFS_MPNEEDSWAP(dp->i_ump);
1049 #endif
1050
1051 UFS_WAPBL_JLOCK_ASSERT(dvp->v_mount);
1052
1053 if (flags & DOWHITEOUT) {
1054 /*
1055 * Whiteout entry: set d_ino to WINO.
1056 */
1057 error = ufs_blkatoff(dvp, (off_t)dp->i_offset, (void *)&ep,
1058 &bp, true);
1059 if (error)
1060 return (error);
1061 ep->d_ino = ufs_rw32(WINO, needswap);
1062 ep->d_type = DT_WHT;
1063 goto out;
1064 }
1065
1066 if ((error = ufs_blkatoff(dvp,
1067 (off_t)(dp->i_offset - dp->i_count), (void *)&ep, &bp, true)) != 0)
1068 return (error);
1069
1070 #ifdef UFS_DIRHASH
1071 /*
1072 * Remove the dirhash entry. This is complicated by the fact
1073 * that `ep' is the previous entry when dp->i_count != 0.
1074 */
1075 if (dp->i_dirhash != NULL)
1076 ufsdirhash_remove(dp, (dp->i_count == 0) ? ep :
1077 (struct direct *)((char *)ep +
1078 ufs_rw16(ep->d_reclen, needswap)), dp->i_offset);
1079 #endif
1080
1081 if (dp->i_count == 0) {
1082 /*
1083 * First entry in block: set d_ino to zero.
1084 */
1085 ep->d_ino = 0;
1086 } else {
1087 /*
1088 * Collapse new free space into previous entry.
1089 */
1090 ep->d_reclen =
1091 ufs_rw16(ufs_rw16(ep->d_reclen, needswap) + dp->i_reclen,
1092 needswap);
1093 }
1094
1095 #ifdef UFS_DIRHASH
1096 if (dp->i_dirhash != NULL) {
1097 int dirblksiz = ip->i_ump->um_dirblksiz;
1098 ufsdirhash_checkblock(dp, (char *)ep -
1099 ((dp->i_offset - dp->i_count) & (dirblksiz - 1)),
1100 dp->i_offset & ~(dirblksiz - 1));
1101 }
1102 #endif
1103
1104 out:
1105 if (DOINGSOFTDEP(dvp)) {
1106 if (ip) {
1107 ip->i_ffs_effnlink--;
1108 softdep_change_linkcnt(ip);
1109 softdep_setup_remove(bp, dp, ip, isrmdir);
1110 }
1111 bdwrite(bp);
1112 } else {
1113 if (ip) {
1114 ip->i_ffs_effnlink--;
1115 ip->i_nlink--;
1116 DIP_ASSIGN(ip, nlink, ip->i_nlink);
1117 ip->i_flag |= IN_CHANGE;
1118 UFS_WAPBL_UPDATE(ITOV(ip), NULL, NULL, 0);
1119 }
1120 error = VOP_BWRITE(bp);
1121 }
1122 dp->i_flag |= IN_CHANGE | IN_UPDATE;
1123 #ifdef FFS
1124 /*
1125 * If the last named reference to a snapshot goes away,
1126 * drop its snapshot reference so that it will be reclaimed
1127 * when last open reference goes away.
1128 */
1129 if (ip != 0 && (ip->i_flags & SF_SNAPSHOT) != 0 &&
1130 ip->i_ffs_effnlink == 0)
1131 ffs_snapgone(ip);
1132 UFS_WAPBL_UPDATE(dvp, NULL, NULL, 0);
1133 #endif
1134 return (error);
1135 }
1136
1137 /*
1138 * Rewrite an existing directory entry to point at the inode
1139 * supplied. The parameters describing the directory entry are
1140 * set up by a call to namei.
1141 */
1142 int
1143 ufs_dirrewrite(struct inode *dp, struct inode *oip, ino_t newinum, int newtype,
1144 int isrmdir, int iflags)
1145 {
1146 struct buf *bp;
1147 struct direct *ep;
1148 struct vnode *vdp = ITOV(dp);
1149 int error;
1150
1151 error = ufs_blkatoff(vdp, (off_t)dp->i_offset, (void *)&ep, &bp, true);
1152 if (error)
1153 return (error);
1154 ep->d_ino = ufs_rw32(newinum, UFS_MPNEEDSWAP(dp->i_ump));
1155 if (!FSFMT(vdp))
1156 ep->d_type = newtype;
1157 oip->i_ffs_effnlink--;
1158 if (DOINGSOFTDEP(vdp)) {
1159 softdep_change_linkcnt(oip);
1160 softdep_setup_directory_change(bp, dp, oip, newinum, isrmdir);
1161 bdwrite(bp);
1162 } else {
1163 oip->i_nlink--;
1164 DIP_ASSIGN(oip, nlink, oip->i_nlink);
1165 oip->i_flag |= IN_CHANGE;
1166 UFS_WAPBL_UPDATE(ITOV(oip), NULL, NULL, UPDATE_DIROP);
1167 error = VOP_BWRITE(bp);
1168 }
1169 dp->i_flag |= iflags;
1170 #ifdef FFS
1171 /*
1172 * If the last named reference to a snapshot goes away,
1173 * drop its snapshot reference so that it will be reclaimed
1174 * when last open reference goes away.
1175 */
1176 if ((oip->i_flags & SF_SNAPSHOT) != 0 && oip->i_ffs_effnlink == 0)
1177 ffs_snapgone(oip);
1178 UFS_WAPBL_UPDATE(vdp, NULL, NULL, UPDATE_DIROP);
1179 #endif
1180 return (error);
1181 }
1182
1183 /*
1184 * Check if a directory is empty or not.
1185 * Inode supplied must be locked.
1186 *
1187 * Using a struct dirtemplate here is not precisely
1188 * what we want, but better than using a struct direct.
1189 *
1190 * NB: does not handle corrupted directories.
1191 */
1192 int
1193 ufs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred)
1194 {
1195 doff_t off;
1196 struct dirtemplate dbuf;
1197 struct direct *dp = (struct direct *)&dbuf;
1198 int error, namlen;
1199 size_t count;
1200 const int needswap = UFS_IPNEEDSWAP(ip);
1201 #define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
1202
1203 for (off = 0; off < ip->i_size;
1204 off += ufs_rw16(dp->d_reclen, needswap)) {
1205 error = vn_rdwr(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ, off,
1206 UIO_SYSSPACE, IO_NODELOCKED, cred, &count, NULL);
1207 /*
1208 * Since we read MINDIRSIZ, residual must
1209 * be 0 unless we're at end of file.
1210 */
1211 if (error || count != 0)
1212 return (0);
1213 /* avoid infinite loops */
1214 if (dp->d_reclen == 0)
1215 return (0);
1216 /* skip empty entries */
1217 if (dp->d_ino == 0 || ufs_rw32(dp->d_ino, needswap) == WINO)
1218 continue;
1219 /* accept only "." and ".." */
1220 #if (BYTE_ORDER == LITTLE_ENDIAN)
1221 if (FSFMT(ITOV(ip)) && needswap == 0)
1222 namlen = dp->d_type;
1223 else
1224 namlen = dp->d_namlen;
1225 #else
1226 if (FSFMT(ITOV(ip)) && needswap != 0)
1227 namlen = dp->d_type;
1228 else
1229 namlen = dp->d_namlen;
1230 #endif
1231 if (namlen > 2)
1232 return (0);
1233 if (dp->d_name[0] != '.')
1234 return (0);
1235 /*
1236 * At this point namlen must be 1 or 2.
1237 * 1 implies ".", 2 implies ".." if second
1238 * char is also "."
1239 */
1240 if (namlen == 1 &&
1241 ufs_rw32(dp->d_ino, needswap) == ip->i_number)
1242 continue;
1243 if (dp->d_name[1] == '.' &&
1244 ufs_rw32(dp->d_ino, needswap) == parentino)
1245 continue;
1246 return (0);
1247 }
1248 return (1);
1249 }
1250
1251 /*
1252 * Check if source directory is in the path of the target directory.
1253 * Target is supplied locked, source is unlocked.
1254 * The target is always vput before returning.
1255 */
1256 int
1257 ufs_checkpath(struct inode *source, struct inode *target, kauth_cred_t cred)
1258 {
1259 struct vnode *nextvp, *vp;
1260 int error, rootino, namlen;
1261 struct dirtemplate dirbuf;
1262 const int needswap = UFS_MPNEEDSWAP(target->i_ump);
1263
1264 vp = ITOV(target);
1265 if (target->i_number == source->i_number) {
1266 error = EEXIST;
1267 goto out;
1268 }
1269 rootino = ROOTINO;
1270 error = 0;
1271 if (target->i_number == rootino)
1272 goto out;
1273
1274 for (;;) {
1275 if (vp->v_type != VDIR) {
1276 error = ENOTDIR;
1277 break;
1278 }
1279 error = vn_rdwr(UIO_READ, vp, (void *)&dirbuf,
1280 sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
1281 IO_NODELOCKED, cred, NULL, NULL);
1282 if (error != 0)
1283 break;
1284 #if (BYTE_ORDER == LITTLE_ENDIAN)
1285 if (FSFMT(vp) && needswap == 0)
1286 namlen = dirbuf.dotdot_type;
1287 else
1288 namlen = dirbuf.dotdot_namlen;
1289 #else
1290 if (FSFMT(vp) && needswap != 0)
1291 namlen = dirbuf.dotdot_type;
1292 else
1293 namlen = dirbuf.dotdot_namlen;
1294 #endif
1295 if (namlen != 2 ||
1296 dirbuf.dotdot_name[0] != '.' ||
1297 dirbuf.dotdot_name[1] != '.') {
1298 error = ENOTDIR;
1299 break;
1300 }
1301 if (ufs_rw32(dirbuf.dotdot_ino, needswap) == source->i_number) {
1302 error = EINVAL;
1303 break;
1304 }
1305 if (ufs_rw32(dirbuf.dotdot_ino, needswap) == rootino)
1306 break;
1307 VOP_UNLOCK(vp, 0);
1308 error = VFS_VGET(vp->v_mount,
1309 ufs_rw32(dirbuf.dotdot_ino, needswap), &nextvp);
1310 vrele(vp);
1311 if (error) {
1312 vp = NULL;
1313 break;
1314 }
1315 vp = nextvp;
1316 }
1317
1318 out:
1319 if (error == ENOTDIR)
1320 printf("checkpath: .. not a directory\n");
1321 if (vp != NULL)
1322 vput(vp);
1323 return (error);
1324 }
1325
1326 #define UFS_DIRRABLKS 0
1327 int ufs_dirrablks = UFS_DIRRABLKS;
1328
1329 /*
1330 * ufs_blkatoff: Return buffer with the contents of block "offset" from
1331 * the beginning of directory "vp". If "res" is non-zero, fill it in with
1332 * a pointer to the remaining space in the directory. If the caller intends
1333 * to modify the buffer returned, "modify" must be true.
1334 */
1335
1336 int
1337 ufs_blkatoff(struct vnode *vp, off_t offset, char **res, struct buf **bpp,
1338 bool modify)
1339 {
1340 struct inode *ip;
1341 struct buf *bp;
1342 daddr_t lbn;
1343 const int dirrablks = ufs_dirrablks;
1344 daddr_t *blks;
1345 int *blksizes;
1346 int run, error;
1347 struct mount *mp = vp->v_mount;
1348 const int bshift = mp->mnt_fs_bshift;
1349 const int bsize = 1 << bshift;
1350 off_t eof;
1351
1352 blks = kmem_alloc((1 + dirrablks) * sizeof(daddr_t), KM_SLEEP);
1353 blksizes = kmem_alloc((1 + dirrablks) * sizeof(int), KM_SLEEP);
1354 ip = VTOI(vp);
1355 KASSERT(vp->v_size == ip->i_size);
1356 GOP_SIZE(vp, vp->v_size, &eof, 0);
1357 lbn = offset >> bshift;
1358
1359 for (run = 0; run <= dirrablks;) {
1360 const off_t curoff = lbn << bshift;
1361 const int size = MIN(eof - curoff, bsize);
1362
1363 if (size == 0) {
1364 break;
1365 }
1366 KASSERT(curoff < eof);
1367 blks[run] = lbn;
1368 blksizes[run] = size;
1369 lbn++;
1370 run++;
1371 if (size != bsize) {
1372 break;
1373 }
1374 }
1375 KASSERT(run >= 1);
1376 error = breadn(vp, blks[0], blksizes[0], &blks[1], &blksizes[1],
1377 run - 1, NOCRED, (modify ? B_MODIFY : 0), &bp);
1378 if (error != 0) {
1379 brelse(bp, 0);
1380 *bpp = NULL;
1381 goto out;
1382 }
1383 if (res) {
1384 *res = (char *)bp->b_data + (offset & (bsize - 1));
1385 }
1386 *bpp = bp;
1387
1388 out:
1389 kmem_free(blks, (1 + dirrablks) * sizeof(daddr_t));
1390 kmem_free(blksizes, (1 + dirrablks) * sizeof(int));
1391 return error;
1392 }
Cache object: bbe25bbb02f284ace62e345c80035e1c
|