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