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