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