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