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