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