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