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