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