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