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