add USL's copyright notice
[unix-history] / usr / src / sys / ufs / ffs / ufs_lookup.c
CommitLineData
da7c5cc6 1/*
ad0f93d2
KB
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
adb35f79
KB
4 * (c) UNIX System Laboratories, Inc.
5 * All or some portions of this file are derived from material licensed
6 * to the University of California by American Telephone and Telegraph
7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8 * the permission of UNIX System Laboratories, Inc.
da7c5cc6 9 *
b702c21d 10 * %sccs.include.redist.c%
7604aef4 11 *
adb35f79 12 * @(#)ufs_lookup.c 8.5 (Berkeley) %G%
da7c5cc6 13 */
10873320 14
8f7ab13a
KB
15#include <sys/param.h>
16#include <sys/namei.h>
17#include <sys/buf.h>
18#include <sys/file.h>
19#include <sys/mount.h>
20#include <sys/vnode.h>
c6f5111d 21
8f7ab13a
KB
22#include <ufs/ufs/quota.h>
23#include <ufs/ufs/inode.h>
24#include <ufs/ufs/dir.h>
25#include <ufs/ufs/ufsmount.h>
26#include <ufs/ufs/ufs_extern.h>
10873320 27
7604aef4 28struct nchstats nchstats;
06406258 29#ifdef DIAGNOSTIC
658f5fdc 30int dirchk = 1;
06406258
KM
31#else
32int dirchk = 0;
33#endif
f93197fc 34
7657a77a
KM
35#define FSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0)
36
f93197fc 37/*
7604aef4 38 * Convert a component of a pathname into a pointer to a locked inode.
6bd0bb92 39 * This is a very central and rather complicated routine.
4f083fd7 40 * If the file system is not maintained in a strict tree hierarchy,
7f69b0e6
KM
41 * this can result in a deadlock situation (see comments in code below).
42 *
7166190c
KM
43 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
44 * on whether the name is to be looked up, created, renamed, or deleted.
7604aef4
KM
45 * When CREATE, RENAME, or DELETE is specified, information usable in
46 * creating, renaming, or deleting a directory entry may be calculated.
47 * If flag has LOCKPARENT or'ed into it and the target of the pathname
48 * exists, lookup returns both the target and its parent directory locked.
49 * When creating or renaming and LOCKPARENT is specified, the target may
50 * not be ".". When deleting and LOCKPARENT is specified, the target may
2c3a3d05
KM
51 * be "."., but the caller must check to ensure it does an vrele and vput
52 * instead of two vputs.
f93197fc 53 *
7604aef4 54 * Overall outline of ufs_lookup:
f93197fc 55 *
6bd0bb92 56 * check accessibility of directory
f93197fc 57 * look for name in cache, if found, then if at end of path
7604aef4 58 * and deleting or creating, drop it, else return name
6bd0bb92
BJ
59 * search for name in directory, to found or notfound
60 * notfound:
7604aef4 61 * if creating, return locked directory, leaving info on available slots
6bd0bb92
BJ
62 * else return error
63 * found:
64 * if at end of path and deleting, return information to allow delete
7604aef4 65 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
4f083fd7 66 * inode and return info to allow rewrite
7f69b0e6
KM
67 * if not at end, add name to cache; if at end and neither creating
68 * nor deleting, add name to cache
10873320 69 */
8f7ab13a 70int
66090215 71ufs_lookup(ap)
5bad4469 72 struct vop_lookup_args /* {
2b3ef074
KM
73 struct vnode *a_dvp;
74 struct vnode **a_vpp;
75 struct componentname *a_cnp;
5bad4469 76 } */ *ap;
10873320 77{
2b3ef074
KM
78 register struct vnode *vdp; /* vnode for directory being searched */
79 register struct inode *dp; /* inode for directory being searched */
8f7ab13a 80 struct buf *bp; /* a buffer of directory entries */
6bd0bb92
BJ
81 register struct direct *ep; /* the current directory entry */
82 int entryoffsetinblock; /* offset of ep in bp's buffer */
6bd0bb92 83 enum {NONE, COMPACT, FOUND} slotstatus;
7166190c 84 doff_t slotoffset; /* offset of area with free space */
6bd0bb92
BJ
85 int slotsize; /* size of area at slotoffset */
86 int slotfreespace; /* amount of space free in slot */
87 int slotneeded; /* size of the entry we're seeking */
84ef30ec 88 int numdirpasses; /* strategy for directory search */
7166190c
KM
89 doff_t endsearch; /* offset to end directory search */
90 doff_t prevoff; /* prev entry dp->i_offset */
2c3a3d05 91 struct vnode *pdp; /* saved dp during symlink work */
66090215 92 struct vnode *tdp; /* returned by VFS_VGET */
7166190c 93 doff_t enduseful; /* pointer past last used dir slot */
8f7ab13a 94 u_long bmask; /* block offset mask */
7604aef4
KM
95 int lockparent; /* 1 => lockparent flag is set */
96 int wantparent; /* 1 => wantparent or lockparent flag */
7657a77a 97 int namlen, error;
2b3ef074
KM
98 struct vnode **vpp = ap->a_vpp;
99 struct componentname *cnp = ap->a_cnp;
100 struct ucred *cred = cnp->cn_cred;
101 int flags = cnp->cn_flags;
102 int nameiop = cnp->cn_nameiop;
7604aef4 103
8f7ab13a
KB
104 bp = NULL;
105 slotoffset = -1;
2b3ef074
KM
106 *vpp = NULL;
107 vdp = ap->a_dvp;
108 dp = VTOI(vdp);
109 lockparent = flags & LOCKPARENT;
110 wantparent = flags & (LOCKPARENT|WANTPARENT);
6bd0bb92 111
a0aead5a 112 /*
6bd0bb92 113 * Check accessiblity of directory.
a0aead5a 114 */
f1500ee7 115 if ((dp->i_mode & IFMT) != IFDIR)
7604aef4 116 return (ENOTDIR);
2b3ef074 117 if (error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc))
7604aef4 118 return (error);
e47da406 119
f93197fc
KM
120 /*
121 * We now have a segment name to search for, and a directory to search.
122 *
123 * Before tediously performing a linear scan of the directory,
124 * check the name cache to see if the directory/name pair
7604aef4 125 * we are looking for is known already.
f93197fc 126 */
2b3ef074 127 if (error = cache_lookup(vdp, vpp, cnp)) {
2bd0d28d
KM
128 int vpid; /* capability number of vnode */
129
130 if (error == ENOENT)
131 return (error);
7604aef4
KM
132 /*
133 * Get the next vnode in the path.
2bd0d28d 134 * See comment below starting `Step through' for
7604aef4
KM
135 * an explaination of the locking protocol.
136 */
2c3a3d05 137 pdp = vdp;
2b3ef074
KM
138 dp = VTOI(*vpp);
139 vdp = *vpp;
140 vpid = vdp->v_id;
2c3a3d05 141 if (pdp == vdp) { /* lookup on "." */
2b3ef074 142 VREF(vdp);
27c192da 143 error = 0;
2b3ef074 144 } else if (flags & ISDOTDOT) {
2c3a3d05 145 VOP_UNLOCK(pdp);
97104ed5 146 error = vget(vdp, 1);
2b3ef074 147 if (!error && lockparent && (flags & ISLASTCN))
2c3a3d05 148 error = VOP_LOCK(pdp);
f93197fc 149 } else {
97104ed5 150 error = vget(vdp, 1);
2b3ef074 151 if (!lockparent || error || !(flags & ISLASTCN))
2c3a3d05 152 VOP_UNLOCK(pdp);
f93197fc 153 }
2bd0d28d
KM
154 /*
155 * Check that the capability number did not change
156 * while we were waiting for the lock.
157 */
27c192da 158 if (!error) {
2b3ef074 159 if (vpid == vdp->v_id)
27c192da 160 return (0);
2c3a3d05
KM
161 vput(vdp);
162 if (lockparent && pdp != vdp && (flags & ISLASTCN))
163 VOP_UNLOCK(pdp);
27c192da 164 }
2c3a3d05
KM
165 if (error = VOP_LOCK(pdp))
166 return (error);
167 vdp = pdp;
168 dp = VTOI(pdp);
2b3ef074 169 *vpp = NULL;
f93197fc
KM
170 }
171
6459ebe0 172 /*
6bd0bb92
BJ
173 * Suppress search for slots unless creating
174 * file and at end of pathname, in which case
175 * we watch for a place to put the new file in
176 * case it doesn't already exist.
6459ebe0 177 */
6bd0bb92 178 slotstatus = FOUND;
0fd9d45b 179 slotfreespace = slotsize = slotneeded = 0;
2b3ef074
KM
180 if ((nameiop == CREATE || nameiop == RENAME) &&
181 (flags & ISLASTCN)) {
6bd0bb92 182 slotstatus = NONE;
7166190c 183 slotneeded = (sizeof(struct direct) - MAXNAMLEN +
2b3ef074 184 cnp->cn_namelen + 3) &~ 3;
6bd0bb92 185 }
7604aef4 186
84ef30ec 187 /*
7604aef4
KM
188 * If there is cached information on a previous search of
189 * this directory, pick up where we last left off.
f93197fc 190 * We cache only lookups as these are the most common
84ef30ec
KM
191 * and have the greatest payoff. Caching CREATE has little
192 * benefit as it usually must search the entire directory
193 * to determine that the entry does not exist. Caching the
7604aef4
KM
194 * location of the last DELETE or RENAME has not reduced
195 * profiling time and hence has been removed in the interest
196 * of simplicity.
84ef30ec 197 */
2b3ef074
KM
198 bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
199 if (nameiop != LOOKUP || dp->i_diroff == 0 ||
f1500ee7 200 dp->i_diroff > dp->i_size) {
7166190c
KM
201 entryoffsetinblock = 0;
202 dp->i_offset = 0;
84ef30ec
KM
203 numdirpasses = 1;
204 } else {
f1500ee7
KM
205 dp->i_offset = dp->i_diroff;
206 if ((entryoffsetinblock = dp->i_offset & bmask) &&
2b3ef074 207 (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)))
8f7ab13a 208 return (error);
84ef30ec 209 numdirpasses = 2;
f93197fc 210 nchstats.ncs_2passes++;
84ef30ec 211 }
0fd9d45b 212 prevoff = dp->i_offset;
84ef30ec 213 endsearch = roundup(dp->i_size, DIRBLKSIZ);
9e7c949b 214 enduseful = 0;
6bd0bb92 215
84ef30ec 216searchloop:
f1500ee7 217 while (dp->i_offset < endsearch) {
f5039631 218 /*
b63c6f8d 219 * If necessary, get the next directory block.
f5039631 220 */
f1500ee7 221 if ((dp->i_offset & bmask) == 0) {
f5039631
BJ
222 if (bp != NULL)
223 brelse(bp);
7166190c 224 if (error =
2b3ef074 225 VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp))
7604aef4 226 return (error);
6bd0bb92 227 entryoffsetinblock = 0;
6459ebe0
KM
228 }
229 /*
6bd0bb92 230 * If still looking for a slot, and at a DIRBLKSIZE
d82d5deb 231 * boundary, have to start looking for free space again.
6459ebe0 232 */
6bd0bb92 233 if (slotstatus == NONE &&
7604aef4 234 (entryoffsetinblock & (DIRBLKSIZ - 1)) == 0) {
6bd0bb92
BJ
235 slotoffset = -1;
236 slotfreespace = 0;
237 }
6bd0bb92 238 /*
d82d5deb
KM
239 * Get pointer to next entry.
240 * Full validation checks are slow, so we only check
241 * enough to insure forward progress through the
242 * directory. Complete checks can be run by patching
243 * "dirchk" to be true.
6bd0bb92 244 */
cb84e0ab 245 ep = (struct direct *)((char *)bp->b_data + entryoffsetinblock);
a5e62f37 246 if (ep->d_reclen == 0 ||
7657a77a 247 dirchk && ufs_dirbadentry(vdp, ep, entryoffsetinblock)) {
7604aef4
KM
248 int i;
249
f1500ee7 250 ufs_dirbad(dp, dp->i_offset, "mangled entry");
d82d5deb 251 i = DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1));
f1500ee7 252 dp->i_offset += i;
6bd0bb92 253 entryoffsetinblock += i;
6459ebe0
KM
254 continue;
255 }
6bd0bb92 256
6459ebe0 257 /*
6bd0bb92 258 * If an appropriate sized slot has not yet been found,
6459ebe0
KM
259 * check to see if one is available. Also accumulate space
260 * in the current block so that we can determine if
261 * compaction is viable.
262 */
6bd0bb92
BJ
263 if (slotstatus != FOUND) {
264 int size = ep->d_reclen;
265
6459ebe0 266 if (ep->d_ino != 0)
7657a77a 267 size -= DIRSIZ(FSFMT(vdp), ep);
6459ebe0 268 if (size > 0) {
6bd0bb92
BJ
269 if (size >= slotneeded) {
270 slotstatus = FOUND;
f1500ee7 271 slotoffset = dp->i_offset;
6bd0bb92
BJ
272 slotsize = ep->d_reclen;
273 } else if (slotstatus == NONE) {
274 slotfreespace += size;
275 if (slotoffset == -1)
f1500ee7 276 slotoffset = dp->i_offset;
6bd0bb92
BJ
277 if (slotfreespace >= slotneeded) {
278 slotstatus = COMPACT;
f1500ee7 279 slotsize = dp->i_offset +
d870be74 280 ep->d_reclen - slotoffset;
6bd0bb92 281 }
6459ebe0 282 }
f5039631 283 }
f5039631 284 }
6bd0bb92 285
f5039631 286 /*
6bd0bb92 287 * Check for a name match.
f5039631 288 */
6bd0bb92 289 if (ep->d_ino) {
7657a77a
KM
290# if (BYTE_ORDER == LITTLE_ENDIAN)
291 if (vdp->v_mount->mnt_maxsymlinklen > 0)
292 namlen = ep->d_namlen;
293 else
294 namlen = ep->d_type;
295# else
296 namlen = ep->d_namlen;
297# endif
298 if (namlen == cnp->cn_namelen &&
2b3ef074 299 !bcmp(cnp->cn_nameptr, ep->d_name,
7657a77a 300 (unsigned)namlen)) {
7604aef4
KM
301 /*
302 * Save directory entry's inode number and
75a31db8 303 * reclen in ndp->ni_ufs area, and release
7604aef4
KM
304 * directory buffer.
305 */
f1500ee7
KM
306 dp->i_ino = ep->d_ino;
307 dp->i_reclen = ep->d_reclen;
7604aef4 308 brelse(bp);
6bd0bb92 309 goto found;
7604aef4 310 }
6bd0bb92 311 }
f1500ee7
KM
312 prevoff = dp->i_offset;
313 dp->i_offset += ep->d_reclen;
6bd0bb92 314 entryoffsetinblock += ep->d_reclen;
9e7c949b 315 if (ep->d_ino)
f1500ee7 316 enduseful = dp->i_offset;
6bd0bb92 317 }
f93197fc 318/* notfound: */
84ef30ec 319 /*
f93197fc 320 * If we started in the middle of the directory and failed
84ef30ec
KM
321 * to find our target, we must check the beginning as well.
322 */
323 if (numdirpasses == 2) {
324 numdirpasses--;
f1500ee7 325 dp->i_offset = 0;
7604aef4 326 endsearch = dp->i_diroff;
84ef30ec
KM
327 goto searchloop;
328 }
7604aef4
KM
329 if (bp != NULL)
330 brelse(bp);
6bd0bb92
BJ
331 /*
332 * If creating, and at end of pathname and current
4f083fd7
SL
333 * directory has not been removed, then can consider
334 * allowing file to be created.
6bd0bb92 335 */
2b3ef074
KM
336 if ((nameiop == CREATE || nameiop == RENAME) &&
337 (flags & ISLASTCN) && dp->i_nlink != 0) {
f5039631 338 /*
6bd0bb92
BJ
339 * Access for write is interpreted as allowing
340 * creation of files in the directory.
f5039631 341 */
2b3ef074 342 if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc))
7604aef4 343 return (error);
f5039631 344 /*
6bd0bb92
BJ
345 * Return an indication of where the new directory
346 * entry should be put. If we didn't find a slot,
f1500ee7 347 * then set dp->i_count to 0 indicating
75a31db8
KM
348 * that the new slot belongs at the end of the
349 * directory. If we found a slot, then the new entry
f1500ee7
KM
350 * can be put in the range from dp->i_offset to
351 * dp->i_offset + dp->i_count.
f5039631 352 */
84ef30ec 353 if (slotstatus == NONE) {
f1500ee7
KM
354 dp->i_offset = roundup(dp->i_size, DIRBLKSIZ);
355 dp->i_count = 0;
356 enduseful = dp->i_offset;
84ef30ec 357 } else {
f1500ee7
KM
358 dp->i_offset = slotoffset;
359 dp->i_count = slotsize;
9e7c949b
KM
360 if (enduseful < slotoffset + slotsize)
361 enduseful = slotoffset + slotsize;
5485e062 362 }
f1500ee7 363 dp->i_endoff = roundup(enduseful, DIRBLKSIZ);
cf5ef508 364 dp->i_flag |= IN_CHANGE | IN_UPDATE;
f5039631 365 /*
6bd0bb92
BJ
366 * We return with the directory locked, so that
367 * the parameters we set up above will still be
368 * valid if we actually decide to do a direnter().
7604aef4
KM
369 * We return ni_vp == NULL to indicate that the entry
370 * does not currently exist; we leave a pointer to
371 * the (locked) directory inode in ndp->ni_dvp.
75a31db8
KM
372 * The pathname buffer is saved so that the name
373 * can be obtained later.
7604aef4
KM
374 *
375 * NB - if the directory is unlocked, then this
376 * information cannot be used.
f5039631 377 */
2b3ef074 378 cnp->cn_flags |= SAVENAME;
7604aef4 379 if (!lockparent)
2c3a3d05 380 VOP_UNLOCK(vdp);
39a892ba 381 return (EJUSTRETURN);
6bd0bb92 382 }
2bd0d28d
KM
383 /*
384 * Insert name into cache (as non-existent) if appropriate.
385 */
4166c1bf 386 if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
2b3ef074 387 cache_enter(vdp, *vpp, cnp);
7604aef4
KM
388 return (ENOENT);
389
6bd0bb92 390found:
f93197fc
KM
391 if (numdirpasses == 2)
392 nchstats.ncs_pass2++;
6bd0bb92
BJ
393 /*
394 * Check that directory length properly reflects presence
395 * of this entry.
396 */
7657a77a 397 if (entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep) > dp->i_size) {
f1500ee7 398 ufs_dirbad(dp, dp->i_offset, "i_size too small");
7657a77a 399 dp->i_size = entryoffsetinblock + DIRSIZ(FSFMT(vdp), ep);
cf5ef508 400 dp->i_flag |= IN_CHANGE | IN_UPDATE;
6bd0bb92
BJ
401 }
402
403 /*
84ef30ec 404 * Found component in pathname.
f93197fc 405 * If the final component of path name, save information
84ef30ec
KM
406 * in the cache as to where the entry was found.
407 */
2b3ef074 408 if ((flags & ISLASTCN) && nameiop == LOOKUP)
f1500ee7 409 dp->i_diroff = dp->i_offset &~ (DIRBLKSIZ - 1);
6bd0bb92
BJ
410
411 /*
412 * If deleting, and at end of pathname, return
413 * parameters which can be used to remove file.
7604aef4
KM
414 * If the wantparent flag isn't set, we return only
415 * the directory (in ndp->ni_dvp), otherwise we go
4f083fd7 416 * on and lock the inode, being careful with ".".
6bd0bb92 417 */
2b3ef074 418 if (nameiop == DELETE && (flags & ISLASTCN)) {
6bd0bb92
BJ
419 /*
420 * Write access to directory required to delete files.
421 */
2b3ef074 422 if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc))
7604aef4 423 return (error);
6bd0bb92 424 /*
f1500ee7 425 * Return pointer to current entry in dp->i_offset,
6bd0bb92 426 * and distance past previous entry (if there
f1500ee7 427 * is a previous entry in this block) in dp->i_count.
2bd0d28d 428 * Save directory inode pointer in ndp->ni_dvp for dirremove().
6bd0bb92 429 */
f1500ee7
KM
430 if ((dp->i_offset & (DIRBLKSIZ - 1)) == 0)
431 dp->i_count = 0;
6bd0bb92 432 else
f1500ee7
KM
433 dp->i_count = dp->i_offset - prevoff;
434 if (dp->i_number == dp->i_ino) {
435 VREF(vdp);
2b3ef074 436 *vpp = vdp;
a48aecd3 437 return (0);
4f083fd7 438 }
66090215 439 if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp))
a48aecd3
KM
440 return (error);
441 /*
442 * If directory is "sticky", then user must own
443 * the directory, or the file in it, else she
444 * may not delete it (unless she's root). This
445 * implements append-only directories.
446 */
447 if ((dp->i_mode & ISVTX) &&
2b3ef074
KM
448 cred->cr_uid != 0 &&
449 cred->cr_uid != dp->i_uid &&
450 VTOI(tdp)->i_uid != cred->cr_uid) {
15298a02 451 vput(tdp);
a48aecd3
KM
452 return (EPERM);
453 }
2b3ef074 454 *vpp = tdp;
a48aecd3 455 if (!lockparent)
2c3a3d05 456 VOP_UNLOCK(vdp);
7604aef4 457 return (0);
6bd0bb92
BJ
458 }
459
4f083fd7 460 /*
7604aef4 461 * If rewriting (RENAME), return the inode and the
4f083fd7
SL
462 * information required to rewrite the present directory
463 * Must get inode of directory entry to verify it's a
7604aef4 464 * regular file, or empty directory.
4f083fd7 465 */
2b3ef074
KM
466 if (nameiop == RENAME && wantparent &&
467 (flags & ISLASTCN)) {
468 if (error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc))
7604aef4 469 return (error);
4f083fd7 470 /*
7604aef4
KM
471 * Careful about locking second inode.
472 * This can only occur if the target is ".".
4f083fd7 473 */
f1500ee7 474 if (dp->i_number == dp->i_ino)
7604aef4 475 return (EISDIR);
66090215 476 if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp))
7604aef4 477 return (error);
2b3ef074
KM
478 *vpp = tdp;
479 cnp->cn_flags |= SAVENAME;
7604aef4 480 if (!lockparent)
2c3a3d05 481 VOP_UNLOCK(vdp);
7604aef4 482 return (0);
4f083fd7
SL
483 }
484
6bd0bb92 485 /*
2c3a3d05 486 * Step through the translation in the name. We do not `vput' the
7604aef4 487 * directory because we may need it again if a symbolic link
bde63aa5
KM
488 * is relative to the current directory. Instead we save it
489 * unlocked as "pdp". We must get the target inode before unlocking
490 * the directory to insure that the inode will not be removed
491 * before we get it. We prevent deadlock by always fetching
492 * inodes from the root, moving down the directory tree. Thus
493 * when following backward pointers ".." we must unlock the
494 * parent directory before getting the requested directory.
495 * There is a potential race condition here if both the current
496 * and parent directories are removed before the `iget' for the
497 * inode associated with ".." returns. We hope that this occurs
498 * infrequently since we cannot avoid this race condition without
a4358a25 499 * implementing a sophisticated deadlock detection algorithm.
bde63aa5
KM
500 * Note also that this simple deadlock detection scheme will not
501 * work if the file system has any hard links other than ".."
502 * that point backwards in the directory structure.
6bd0bb92 503 */
2c3a3d05 504 pdp = vdp;
2b3ef074 505 if (flags & ISDOTDOT) {
2c3a3d05 506 VOP_UNLOCK(pdp); /* race to get the inode */
66090215 507 if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) {
2c3a3d05
KM
508 VOP_LOCK(pdp);
509 return (error);
510 }
511 if (lockparent && (flags & ISLASTCN) &&
512 (error = VOP_LOCK(pdp))) {
513 vput(tdp);
7604aef4
KM
514 return (error);
515 }
2b3ef074 516 *vpp = tdp;
f1500ee7 517 } else if (dp->i_number == dp->i_ino) {
2b3ef074
KM
518 VREF(vdp); /* we want ourself, ie "." */
519 *vpp = vdp;
bde63aa5 520 } else {
66090215 521 if (error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp))
7604aef4 522 return (error);
2b3ef074 523 if (!lockparent || !(flags & ISLASTCN))
2c3a3d05 524 VOP_UNLOCK(pdp);
2b3ef074 525 *vpp = tdp;
bde63aa5 526 }
f93197fc
KM
527
528 /*
a5e62f37 529 * Insert name into cache if appropriate.
f93197fc 530 */
4166c1bf 531 if (cnp->cn_flags & MAKEENTRY)
2b3ef074 532 cache_enter(vdp, *vpp, cnp);
7604aef4 533 return (0);
10873320
BJ
534}
535
8f7ab13a
KB
536void
537ufs_dirbad(ip, offset, how)
6bd0bb92 538 struct inode *ip;
7166190c 539 doff_t offset;
6bd0bb92
BJ
540 char *how;
541{
8f7ab13a 542 struct mount *mp;
6bd0bb92 543
8f7ab13a
KB
544 mp = ITOV(ip)->v_mount;
545 (void)printf("%s: bad dir ino %d at offset %d: %s\n",
546 mp->mnt_stat.f_mntonname, ip->i_number, offset, how);
547 if ((mp->mnt_stat.f_flags & MNT_RDONLY) == 0)
0f3242db 548 panic("bad dir");
6bd0bb92
BJ
549}
550
d82d5deb
KM
551/*
552 * Do consistency checking on a directory entry:
553 * record length must be multiple of 4
d82d5deb
KM
554 * entry must fit in rest of its DIRBLKSIZ block
555 * record must be large enough to contain entry
556 * name is not longer than MAXNAMLEN
557 * name must be as long as advertised, and null terminated
558 */
8f7ab13a 559int
7657a77a
KM
560ufs_dirbadentry(dp, ep, entryoffsetinblock)
561 struct vnode *dp;
6bd0bb92 562 register struct direct *ep;
d82d5deb 563 int entryoffsetinblock;
6bd0bb92 564{
6bd0bb92 565 register int i;
7657a77a 566 int namlen;
6bd0bb92 567
7657a77a
KM
568# if (BYTE_ORDER == LITTLE_ENDIAN)
569 if (dp->v_mount->mnt_maxsymlinklen > 0)
570 namlen = ep->d_namlen;
571 else
572 namlen = ep->d_type;
573# else
574 namlen = ep->d_namlen;
575# endif
a5e62f37 576 if ((ep->d_reclen & 0x3) != 0 ||
d82d5deb 577 ep->d_reclen > DIRBLKSIZ - (entryoffsetinblock & (DIRBLKSIZ - 1)) ||
7657a77a 578 ep->d_reclen < DIRSIZ(FSFMT(dp), ep) || namlen > MAXNAMLEN) {
8f7ab13a
KB
579 /*return (1); */
580 printf("First bad\n");
581 goto bad;
582 }
7657a77a 583 for (i = 0; i < namlen; i++)
8f7ab13a
KB
584 if (ep->d_name[i] == '\0') {
585 /*return (1); */
586 printf("Second bad\n");
587 goto bad;
588 }
589 if (ep->d_name[i])
590 goto bad;
6bd0bb92 591 return (ep->d_name[i]);
8f7ab13a 592bad:
8f7ab13a 593 return(1);
6bd0bb92
BJ
594}
595
6bd0bb92
BJ
596/*
597 * Write a directory entry after a call to namei, using the parameters
75a31db8 598 * that it left in nameidata. The argument ip is the inode which the new
f1500ee7
KM
599 * directory entry will refer to. Dvp is a pointer to the directory to
600 * be written, which was left locked by namei. Remaining parameters
601 * (dp->i_offset, dp->i_count) indicate how the space for the new
602 * entry is to be obtained.
6bd0bb92 603 */
8f7ab13a 604int
d5a78f43 605ufs_direnter(ip, dvp, cnp)
6bd0bb92 606 struct inode *ip;
cfef4373
JH
607 struct vnode *dvp;
608 register struct componentname *cnp;
f5039631 609{
6bd0bb92 610 register struct direct *ep, *nep;
8f7ab13a 611 register struct inode *dp;
6bd0bb92 612 struct buf *bp;
8f7ab13a
KB
613 struct direct newdir;
614 struct iovec aiov;
8f7ab13a 615 struct uio auio;
b32450f4 616 u_int dsize;
8f7ab13a 617 int error, loc, newentrysize, spacefree;
6bd0bb92 618 char *dirbuf;
f5039631 619
75a31db8 620#ifdef DIAGNOSTIC
cfef4373 621 if ((cnp->cn_flags & SAVENAME) == 0)
75a31db8
KM
622 panic("direnter: missing name");
623#endif
15298a02 624 dp = VTOI(dvp);
75a31db8 625 newdir.d_ino = ip->i_number;
cfef4373
JH
626 newdir.d_namlen = cnp->cn_namelen;
627 bcopy(cnp->cn_nameptr, newdir.d_name, (unsigned)cnp->cn_namelen + 1);
7657a77a
KM
628 if (dvp->v_mount->mnt_maxsymlinklen > 0)
629 newdir.d_type = IFTODT(ip->i_mode);
630 else {
631 newdir.d_type = 0;
632# if (BYTE_ORDER == LITTLE_ENDIAN)
633 { u_char tmp = newdir.d_namlen;
634 newdir.d_namlen = newdir.d_type;
635 newdir.d_type = tmp; }
636# endif
637 }
638 newentrysize = DIRSIZ(FSFMT(dvp), &newdir);
f1500ee7 639 if (dp->i_count == 0) {
6bd0bb92 640 /*
f1500ee7
KM
641 * If dp->i_count is 0, then namei could find no
642 * space in the directory. Here, dp->i_offset will
75a31db8
KM
643 * be on a directory block boundary and we will write the
644 * new entry into a fresh block.
6bd0bb92 645 */
f1500ee7 646 if (dp->i_offset & (DIRBLKSIZ - 1))
cf5ef508 647 panic("ufs_direnter: newblk");
f1500ee7 648 auio.uio_offset = dp->i_offset;
75a31db8
KM
649 newdir.d_reclen = DIRBLKSIZ;
650 auio.uio_resid = newentrysize;
651 aiov.iov_len = newentrysize;
652 aiov.iov_base = (caddr_t)&newdir;
653 auio.uio_iov = &aiov;
654 auio.uio_iovcnt = 1;
655 auio.uio_rw = UIO_WRITE;
656 auio.uio_segflg = UIO_SYSSPACE;
657 auio.uio_procp = (struct proc *)0;
cfef4373 658 error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
15298a02 659 if (DIRBLKSIZ >
cc173077 660 VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
8f7ab13a
KB
661 /* XXX should grow with balloc() */
662 panic("ufs_direnter: frag size");
663 else if (!error) {
23de9f20 664 dp->i_size = roundup(dp->i_size, DIRBLKSIZ);
cf5ef508 665 dp->i_flag |= IN_CHANGE;
15365e07 666 }
f2a5ad78 667 return (error);
6bd0bb92
BJ
668 }
669
670 /*
f1500ee7
KM
671 * If dp->i_count is non-zero, then namei found space
672 * for the new entry in the range dp->i_offset to
673 * dp->i_offset + dp->i_count in the directory.
75a31db8
KM
674 * To use this space, we may have to compact the entries located
675 * there, by copying them together towards the beginning of the
676 * block, leaving the free space in one usable chunk at the end.
6bd0bb92
BJ
677 */
678
679 /*
680 * Increase size of directory if entry eats into new space.
681 * This should never push the size past a new multiple of
682 * DIRBLKSIZE.
23de9f20
KM
683 *
684 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN.
6bd0bb92 685 */
f1500ee7
KM
686 if (dp->i_offset + dp->i_count > dp->i_size)
687 dp->i_size = dp->i_offset + dp->i_count;
6bd0bb92 688 /*
7604aef4 689 * Get the block containing the space for the new directory entry.
6bd0bb92 690 */
7166190c 691 if (error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp))
7604aef4 692 return (error);
6bd0bb92 693 /*
75a31db8
KM
694 * Find space for the new entry. In the simple case, the entry at
695 * offset base will have the space. If it does not, then namei
f1500ee7
KM
696 * arranged that compacting the region dp->i_offset to
697 * dp->i_offset + dp->i_count would yield the
75a31db8 698 * space.
6bd0bb92
BJ
699 */
700 ep = (struct direct *)dirbuf;
7657a77a 701 dsize = DIRSIZ(FSFMT(dvp), ep);
efa3a91c 702 spacefree = ep->d_reclen - dsize;
f1500ee7 703 for (loc = ep->d_reclen; loc < dp->i_count; ) {
6bd0bb92
BJ
704 nep = (struct direct *)(dirbuf + loc);
705 if (ep->d_ino) {
706 /* trim the existing slot */
707 ep->d_reclen = dsize;
708 ep = (struct direct *)((char *)ep + dsize);
709 } else {
710 /* overwrite; nothing there; header is ours */
7604aef4 711 spacefree += dsize;
6bd0bb92 712 }
7657a77a 713 dsize = DIRSIZ(FSFMT(dvp), nep);
efa3a91c 714 spacefree += nep->d_reclen - dsize;
6bd0bb92 715 loc += nep->d_reclen;
9f024ffc 716 bcopy((caddr_t)nep, (caddr_t)ep, dsize);
6bd0bb92
BJ
717 }
718 /*
719 * Update the pointer fields in the previous entry (if any),
720 * copy in the new entry, and write out the block.
721 */
722 if (ep->d_ino == 0) {
efa3a91c 723 if (spacefree + dsize < newentrysize)
cf5ef508 724 panic("ufs_direnter: compact1");
75a31db8 725 newdir.d_reclen = spacefree + dsize;
6bd0bb92 726 } else {
efa3a91c 727 if (spacefree < newentrysize)
cf5ef508 728 panic("ufs_direnter: compact2");
75a31db8 729 newdir.d_reclen = spacefree;
6bd0bb92
BJ
730 ep->d_reclen = dsize;
731 ep = (struct direct *)((char *)ep + dsize);
732 }
75a31db8 733 bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize);
15298a02 734 error = VOP_BWRITE(bp);
cf5ef508 735 dp->i_flag |= IN_CHANGE | IN_UPDATE;
f1500ee7 736 if (!error && dp->i_endoff && dp->i_endoff < dp->i_size)
5bad4469
KM
737 error = VOP_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC,
738 cnp->cn_cred, cnp->cn_proc);
f2a5ad78 739 return (error);
6bd0bb92
BJ
740}
741
4f083fd7 742/*
5c299d0e
KM
743 * Remove a directory entry after a call to namei, using
744 * the parameters which it left in nameidata. The entry
f1500ee7
KM
745 * dp->i_offset contains the offset into the directory of the
746 * entry to be eliminated. The dp->i_count field contains the
4f083fd7
SL
747 * size of the previous record in the directory. If this
748 * is 0, the first entry is being deleted, so we need only
749 * zero the inode number to mark the entry as free. If the
75a31db8 750 * entry is not the first in the directory, we must reclaim
4f083fd7
SL
751 * the space of the now empty record by adding the record size
752 * to the size of the previous entry.
753 */
8f7ab13a 754int
d5a78f43 755ufs_dirremove(dvp, cnp)
cfef4373
JH
756 struct vnode *dvp;
757 struct componentname *cnp;
6bd0bb92 758{
8f7ab13a 759 register struct inode *dp;
6bd0bb92 760 struct direct *ep;
7604aef4
KM
761 struct buf *bp;
762 int error;
6bd0bb92 763
cfef4373 764 dp = VTOI(dvp);
f1500ee7 765 if (dp->i_count == 0) {
6bd0bb92
BJ
766 /*
767 * First entry in block: set d_ino to zero.
768 */
7166190c
KM
769 if (error =
770 VOP_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp))
7604aef4 771 return (error);
75a31db8 772 ep->d_ino = 0;
15298a02 773 error = VOP_BWRITE(bp);
cf5ef508 774 dp->i_flag |= IN_CHANGE | IN_UPDATE;
75a31db8
KM
775 return (error);
776 }
777 /*
778 * Collapse new free space into previous entry.
779 */
7166190c
KM
780 if (error = VOP_BLKATOFF(dvp, (off_t)(dp->i_offset - dp->i_count),
781 (char **)&ep, &bp))
75a31db8 782 return (error);
f1500ee7 783 ep->d_reclen += dp->i_reclen;
15298a02 784 error = VOP_BWRITE(bp);
cf5ef508 785 dp->i_flag |= IN_CHANGE | IN_UPDATE;
7604aef4 786 return (error);
10873320 787}
6459ebe0 788
4f083fd7
SL
789/*
790 * Rewrite an existing directory entry to point at the inode
791 * supplied. The parameters describing the directory entry are
792 * set up by a call to namei.
793 */
8f7ab13a 794int
cfef4373 795ufs_dirrewrite(dp, ip, cnp)
4f083fd7 796 struct inode *dp, *ip;
cfef4373 797 struct componentname *cnp;
4f083fd7 798{
75a31db8 799 struct buf *bp;
8f7ab13a 800 struct direct *ep;
7657a77a 801 struct vnode *vdp = ITOV(dp);
75a31db8 802 int error;
4f083fd7 803
7657a77a 804 if (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp))
75a31db8
KM
805 return (error);
806 ep->d_ino = ip->i_number;
7657a77a
KM
807 if (vdp->v_mount->mnt_maxsymlinklen > 0)
808 ep->d_type = IFTODT(ip->i_mode);
15298a02 809 error = VOP_BWRITE(bp);
cf5ef508 810 dp->i_flag |= IN_CHANGE | IN_UPDATE;
75a31db8 811 return (error);
4f083fd7
SL
812}
813
4f083fd7
SL
814/*
815 * Check if a directory is empty or not.
816 * Inode supplied must be locked.
17d11193
SL
817 *
818 * Using a struct dirtemplate here is not precisely
819 * what we want, but better than using a struct direct.
820 *
821 * NB: does not handle corrupted directories.
4f083fd7 822 */
8f7ab13a
KB
823int
824ufs_dirempty(ip, parentino, cred)
05d2bbce 825 register struct inode *ip;
68f21562 826 ino_t parentino;
7604aef4 827 struct ucred *cred;
4f083fd7
SL
828{
829 register off_t off;
17d11193
SL
830 struct dirtemplate dbuf;
831 register struct direct *dp = (struct direct *)&dbuf;
7657a77a 832 int error, count, namlen;
17d11193 833#define MINDIRSIZ (sizeof (struct dirtemplate) / 2)
4f083fd7
SL
834
835 for (off = 0; off < ip->i_size; off += dp->d_reclen) {
b7581321
KM
836 error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
837 UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0);
17d11193
SL
838 /*
839 * Since we read MINDIRSIZ, residual must
840 * be 0 unless we're at end of file.
841 */
842 if (error || count != 0)
4f083fd7 843 return (0);
d14b14c3 844 /* avoid infinite loops */
a5e62f37 845 if (dp->d_reclen == 0)
d14b14c3 846 return (0);
17d11193 847 /* skip empty entries */
4f083fd7
SL
848 if (dp->d_ino == 0)
849 continue;
17d11193 850 /* accept only "." and ".." */
7657a77a
KM
851# if (BYTE_ORDER == LITTLE_ENDIAN)
852 if (ITOV(ip)->v_mount->mnt_maxsymlinklen > 0)
853 namlen = dp->d_namlen;
854 else
855 namlen = dp->d_type;
856# else
857 namlen = dp->d_namlen;
858# endif
859 if (namlen > 2)
17d11193 860 return (0);
4f083fd7
SL
861 if (dp->d_name[0] != '.')
862 return (0);
17d11193 863 /*
7657a77a 864 * At this point namlen must be 1 or 2.
17d11193
SL
865 * 1 implies ".", 2 implies ".." if second
866 * char is also "."
867 */
7657a77a 868 if (namlen == 1)
68f21562
KM
869 continue;
870 if (dp->d_name[1] == '.' && dp->d_ino == parentino)
4f083fd7
SL
871 continue;
872 return (0);
873 }
874 return (1);
875}
b1aa93b9
KM
876
877/*
878 * Check if source directory is in the path of the target directory.
879 * Target is supplied locked, source is unlocked.
2c3a3d05 880 * The target is always vput before returning.
b1aa93b9 881 */
8f7ab13a
KB
882int
883ufs_checkpath(source, target, cred)
b1aa93b9 884 struct inode *source, *target;
7604aef4 885 struct ucred *cred;
b1aa93b9 886{
15298a02 887 struct vnode *vp;
7657a77a 888 int error, rootino, namlen;
2c3a3d05 889 struct dirtemplate dirbuf;
b1aa93b9 890
2c3a3d05
KM
891 vp = ITOV(target);
892 if (target->i_number == source->i_number) {
b1aa93b9
KM
893 error = EEXIST;
894 goto out;
895 }
8f7ab13a 896 rootino = ROOTINO;
8f7ab13a 897 error = 0;
2c3a3d05 898 if (target->i_number == rootino)
b1aa93b9
KM
899 goto out;
900
901 for (;;) {
2c3a3d05 902 if (vp->v_type != VDIR) {
b1aa93b9
KM
903 error = ENOTDIR;
904 break;
905 }
15298a02 906 error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
7604aef4 907 sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
b7581321 908 IO_NODELOCKED, cred, (int *)0, (struct proc *)0);
b1aa93b9
KM
909 if (error != 0)
910 break;
7657a77a
KM
911# if (BYTE_ORDER == LITTLE_ENDIAN)
912 if (vp->v_mount->mnt_maxsymlinklen > 0)
913 namlen = dirbuf.dotdot_namlen;
914 else
915 namlen = dirbuf.dotdot_type;
916# else
917 namlen = dirbuf.dotdot_namlen;
918# endif
919 if (namlen != 2 ||
4a287f19
KM
920 dirbuf.dotdot_name[0] != '.' ||
921 dirbuf.dotdot_name[1] != '.') {
b1aa93b9
KM
922 error = ENOTDIR;
923 break;
924 }
925 if (dirbuf.dotdot_ino == source->i_number) {
926 error = EINVAL;
927 break;
928 }
8f7ab13a 929 if (dirbuf.dotdot_ino == rootino)
b1aa93b9 930 break;
2c3a3d05
KM
931 vput(vp);
932 if (error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp)) {
933 vp = NULL;
b1aa93b9 934 break;
2c3a3d05 935 }
b1aa93b9
KM
936 }
937
938out:
939 if (error == ENOTDIR)
940 printf("checkpath: .. not a directory\n");
2c3a3d05
KM
941 if (vp != NULL)
942 vput(vp);
b1aa93b9
KM
943 return (error);
944}