reindent-20030715
[openafs.git] / src / vfsck / dir.c
1 /*
2  * Copyright (c) 1980, 1986 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17
18 #include <afsconfig.h>
19 #include <afs/param.h>
20
21 RCSID
22     ("$Header$");
23
24 #define VICE                    /* allow us to put our changes in at will */
25 #include <stdio.h>
26
27 #include <sys/param.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30
31 #ifdef  AFS_OSF_ENV
32 #include <sys/vnode.h>
33 #include <sys/mount.h>
34 #include <ufs/inode.h>
35 #include <ufs/fs.h>
36 #define _BSD
37 #define _KERNEL
38 #include <ufs/dir.h>
39 #undef  _KERNEL
40 #undef  _BSD
41 #else /* AFS_OSF_ENV */
42 #ifdef AFS_VFSINCL_ENV
43 #define VFS
44 #include <sys/vnode.h>
45 #ifdef    AFS_SUN5_ENV
46 #include <sys/fs/ufs_inode.h>
47 #include <sys/fs/ufs_fs.h>
48 #define _KERNEL
49 #include <sys/fs/ufs_fsdir.h>
50 #undef _KERNEL
51 #else
52 #include <ufs/inode.h>
53 #include <ufs/fs.h>
54 #define KERNEL
55 #include <ufs/fsdir.h>
56 #undef KERNEL
57 #endif
58
59 #else /* AFS_VFSINCL_ENV */
60 #include <sys/inode.h>
61 #ifdef  AFS_HPUX_ENV
62 #include <ctype.h>
63 #define LONGFILENAMES   1
64 #include <sys/sysmacros.h>
65 #include <sys/ino.h>
66 #define DIRSIZ_MACRO
67 #include <ndir.h>
68 #else
69 #define KERNEL
70 #include <sys/dir.h>
71 #undef KERNEL
72 #endif
73 #include <sys/fs.h>
74 #endif /* AFS_VFSINCL_ENV */
75 #endif /* AFS_OSF_ENV */
76
77 #ifdef AFS_SUN_ENV
78 #ifdef  AFS_SUN5_ENV
79 #include <sys/mnttab.h>
80 #include <sys/mntent.h>
81 #else
82 #include <mntent.h>
83 #endif
84 #endif
85 #include "fsck.h"
86
87
88 #ifdef  AFS_HPUX_ENV
89 struct dirtemplate_lfn {
90     afs_uint32 dot_ino;
91     short dot_reclen;
92     short dot_namlen;
93     char dot_name[4];           /* must be multiple of 4 */
94     afs_uint32 dotdot_ino;
95     short dotdot_reclen;
96     short dotdot_namlen;
97     char dotdot_name[4];        /* ditto */
98 };
99 #define dirtemplate dirtemplate_lfn
100 #endif
101
102 #define MINDIRSIZE      (sizeof (struct dirtemplate))
103
104 char *endpathname = &pathname[BUFSIZ - 2];
105 char *lfname = "lost+found";
106 int lfmode = 01777;
107 struct dirtemplate emptydir = { 0, DIRBLKSIZ };
108 struct dirtemplate dirhead = { 0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".." };
109
110 struct direct *fsck_readdir();
111 struct bufarea *getdirblk();
112
113 descend(parentino, inumber)
114      struct inodesc *parentino;
115      ino_t inumber;
116 {
117     register struct dinode *dp;
118     struct inodesc curino;
119
120     memset((char *)&curino, 0, sizeof(struct inodesc));
121     if (statemap[inumber] != DSTATE)
122         errexit("BAD INODE %d TO DESCEND", statemap[inumber]);
123 #if defined(ACLS) && defined(AFS_HPUX_ENV)
124     /* 
125      * keep any continuation inode information 
126      */
127     if (statemap[inumber] & HASCINODE)
128         statemap[inumber] = HASCINODE | DFOUND;
129     else
130         statemap[inumber] = DFOUND;
131 #else /* no ACLS */
132     statemap[inumber] = DFOUND;
133 #endif /* ACLS */
134     dp = ginode(inumber);
135     if (dp->di_size == 0) {
136         direrror(inumber, "ZERO LENGTH DIRECTORY");
137         if (reply("REMOVE") == 1)
138 #if defined(ACLS) && defined(AFS_HPUX_ENV)
139             /* 
140              * keep any continuation inode information 
141              */
142             if (statemap[inumber] & HASCINODE)
143                 statemap[inumber] = HASCINODE | DCLEAR;
144             else
145                 statemap[inumber] = DCLEAR;
146 #else /* no ACLS */
147             statemap[inumber] = DCLEAR;
148 #endif /* ACLS */
149         return;
150     }
151     if (dp->di_size < MINDIRSIZE) {
152         direrror(inumber, "DIRECTORY TOO SHORT");
153         dp->di_size = MINDIRSIZE;
154         if (reply("FIX") == 1)
155             inodirty();
156     }
157 #if     !defined(AFS_HPUX_ENV)
158     /* For remaining 4.2 systems.  We shouldn't convert
159      * dir size, since Unix 4.2 kernels won't maintain this, and we'll have a
160      * lot of spurious directory conversions scaring people */
161     if ((dp->di_size & (DIRBLKSIZ - 1)) != 0) {
162         pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", pathname,
163               dp->di_size, DIRBLKSIZ);
164         dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
165         if (preen)
166             printf(" (ADJUSTED)\n");
167         if (preen || reply("ADJUST") == 1)
168             inodirty();
169     }
170 #endif
171     curino.id_type = DATA;
172     curino.id_func = parentino->id_func;
173     curino.id_parent = parentino->id_number;
174     curino.id_number = inumber;
175     (void)ckinode(dp, &curino);
176     if (curino.id_entryno < 2) {
177         direrror(inumber, "NULL DIRECTORY");
178         if (reply("REMOVE") == 1)
179             statemap[inumber] = DCLEAR;
180     }
181 }
182
183 dirscan(idesc)
184      register struct inodesc *idesc;
185 {
186     register struct direct *dp;
187     register struct bufarea *bp;
188     int dsize, n;
189     long blksiz;
190     char dbuf[DIRBLKSIZ];
191
192     if (idesc->id_type != DATA)
193         errexit("wrong type to dirscan %d\n", idesc->id_type);
194     if (idesc->id_entryno == 0 && (idesc->id_filesize & (DIRBLKSIZ - 1)) != 0)
195         idesc->id_filesize = roundup(idesc->id_filesize, DIRBLKSIZ);
196     blksiz = idesc->id_numfrags * sblock.fs_fsize;
197
198     if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
199         idesc->id_filesize -= blksiz;
200         return (SKIP);
201     }
202     idesc->id_loc = 0;
203     for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
204         dsize = dp->d_reclen;
205         memcpy(dbuf, (char *)dp, dsize);
206         idesc->id_dirp = (struct direct *)dbuf;
207         if ((n = (*idesc->id_func) (idesc)) & ALTERED) {
208             bp = getdirblk(idesc->id_blkno, blksiz);
209             memcpy((char *)dp, dbuf, dsize);
210             dirty(bp);
211             sbdirty();
212         }
213         if (n & STOP)
214             return (n);
215     }
216     return (idesc->id_filesize > 0 ? KEEPON : STOP);
217 }
218
219 /*
220  * get next entry in a directory.
221  */
222 struct direct *
223 fsck_readdir(idesc)
224      register struct inodesc *idesc;
225 {
226     register struct direct *dp, *ndp;
227     register struct bufarea *bp;
228     long size, blksiz;
229
230     blksiz = idesc->id_numfrags * sblock.fs_fsize;
231     bp = getdirblk(idesc->id_blkno, blksiz);
232     if (idesc->id_loc % DIRBLKSIZ == 0 && idesc->id_filesize > 0
233         && idesc->id_loc < blksiz) {
234         dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
235         if (dircheck(idesc, dp)) {
236             goto dpok;
237         }
238         idesc->id_loc += DIRBLKSIZ;
239         idesc->id_filesize -= DIRBLKSIZ;
240         dp->d_reclen = DIRBLKSIZ;
241         dp->d_ino = 0;
242         dp->d_namlen = 0;
243         dp->d_name[0] = '\0';
244         if (dofix(idesc, "DIRECTORY CORRUPTED"))
245             dirty(bp);
246         return (dp);
247     }
248   dpok:
249     if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
250         return NULL;
251     dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
252     idesc->id_loc += dp->d_reclen;
253     idesc->id_filesize -= dp->d_reclen;
254     if ((idesc->id_loc % DIRBLKSIZ) == 0)
255         return (dp);
256     ndp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc);
257     if (idesc->id_loc < blksiz && idesc->id_filesize > 0
258         && dircheck(idesc, ndp) == 0) {
259         size = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
260         dp->d_reclen += size;
261         idesc->id_loc += size;
262         idesc->id_filesize -= size;
263         if (dofix(idesc, "DIRECTORY CORRUPTED"))
264             dirty(bp);
265     }
266     return (dp);
267 }
268
269 /*
270  * Verify that a directory entry is valid.
271  * This is a superset of the checks made in the kernel.
272  */
273 dircheck(idesc, dp)
274      struct inodesc *idesc;
275      register struct direct *dp;
276 {
277     register int size;
278     register char *cp;
279     int spaceleft;
280
281     size = DIRSIZ(dp);
282     spaceleft = DIRBLKSIZ - (idesc->id_loc % DIRBLKSIZ);
283     if (dp->d_ino < maxino && dp->d_reclen != 0 && dp->d_reclen <= spaceleft
284         && (dp->d_reclen & 0x3) == 0 && dp->d_reclen >= size
285         && idesc->id_filesize >= size && dp->d_namlen <= MAXNAMLEN) {
286         if (dp->d_ino == 0)
287             return (1);
288         for (cp = dp->d_name, size = 0; size < dp->d_namlen; size++)
289 #if     defined(Next) || defined(AFS_SUN5_ENV)
290             if (*cp == 0)
291                 return (0);
292             else
293                 ++cp;
294 #else
295             if (*cp == 0 || (*cp++ & 0200)) {
296                 return (0);
297             }
298 #endif
299         if (*cp == 0)
300             return (1);
301     }
302     return (0);
303 }
304
305 direrror(ino, errmesg)
306      ino_t ino;
307      char *errmesg;
308 {
309     register struct dinode *dp;
310
311     pwarn("%s ", errmesg);
312     pinode(ino);
313     printf("\n");
314     if (ino < ROOTINO || ino > maxino) {
315         pfatal("NAME=%s\n", pathname);
316         return;
317     }
318     dp = ginode(ino);
319     if (ftypeok(dp))
320         pfatal("%s=%s\n", (dp->di_mode & IFMT) == IFDIR ? "DIR" : "FILE",
321                pathname);
322     else
323         pfatal("NAME=%s\n", pathname);
324 }
325
326 adjust(idesc, lcnt)
327      register struct inodesc *idesc;
328      short lcnt;
329 {
330     register struct dinode *dp;
331
332     dp = ginode(idesc->id_number);
333     if (dp->di_nlink == lcnt) {
334         if (linkup(idesc->id_number, (ino_t) 0) == 0)
335             clri(idesc, "UNREF", 0);
336     } else {
337         pwarn("LINK COUNT %s",
338               (lfdir == idesc->id_number) ? lfname : ((dp->di_mode & IFMT) ==
339                                                       IFDIR ? "DIR" :
340                                                       "FILE"));
341         pinode(idesc->id_number);
342         printf(" COUNT %d SHOULD BE %d", dp->di_nlink, dp->di_nlink - lcnt);
343         if (preen) {
344             if (lcnt < 0) {
345                 printf("\n");
346                 pfatal("LINK COUNT INCREASING");
347             }
348             printf(" (ADJUSTED)\n");
349         }
350         if (preen || reply("ADJUST") == 1) {
351             dp->di_nlink -= lcnt;
352             inodirty();
353         }
354     }
355 }
356
357 mkentry(idesc)
358      struct inodesc *idesc;
359 {
360     register struct direct *dirp = idesc->id_dirp;
361     struct direct newent;
362     int newlen, oldlen;
363
364     newent.d_namlen = 11;
365     newlen = DIRSIZ(&newent);
366     if (dirp->d_ino != 0)
367         oldlen = DIRSIZ(dirp);
368     else
369         oldlen = 0;
370     if (dirp->d_reclen - oldlen < newlen)
371         return (KEEPON);
372     newent.d_reclen = dirp->d_reclen - oldlen;
373     dirp->d_reclen = oldlen;
374     dirp = (struct direct *)(((char *)dirp) + oldlen);
375     dirp->d_ino = idesc->id_parent;     /* ino to be entered is in id_parent */
376     dirp->d_reclen = newent.d_reclen;
377     dirp->d_namlen = strlen(idesc->id_name);
378     memcpy(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1);
379     return (ALTERED | STOP);
380 }
381
382 chgino(idesc)
383      struct inodesc *idesc;
384 {
385     register struct direct *dirp = idesc->id_dirp;
386
387     if (memcmp(dirp->d_name, idesc->id_name, (int)dirp->d_namlen + 1))
388         return (KEEPON);
389     dirp->d_ino = idesc->id_parent;
390     return (ALTERED | STOP);
391 }
392
393 linkup(orphan, parentdir)
394      ino_t orphan;
395      ino_t parentdir;
396 {
397     register struct dinode *dp;
398     int lostdir, len;
399     ino_t oldlfdir;
400     struct inodesc idesc;
401     char tempname[BUFSIZ];
402     extern int pass4check();
403
404     memset((char *)&idesc, 0, sizeof(struct inodesc));
405     dp = ginode(orphan);
406     lostdir = (dp->di_mode & IFMT) == IFDIR;
407     pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
408     pinode(orphan);
409     if (preen && dp->di_size == 0)
410         return (0);
411     if (preen)
412         printf(" (RECONNECTED)\n");
413     else if (reply("RECONNECT") == 0)
414         return (0);
415     pathp = pathname;
416     *pathp++ = '/';
417     *pathp = '\0';
418     if (lfdir == 0) {
419         dp = ginode(ROOTINO);
420         idesc.id_name = lfname;
421         idesc.id_type = DATA;
422         idesc.id_func = findino;
423         idesc.id_number = ROOTINO;
424         if ((ckinode(dp, &idesc) & FOUND) != 0) {
425             lfdir = idesc.id_parent;
426         } else {
427             pwarn("NO lost+found DIRECTORY");
428             if (preen || reply("CREATE")) {
429                 lfdir = allocdir(ROOTINO, (ino_t) 0, lfmode);
430                 if (lfdir != 0) {
431                     if (makeentry(ROOTINO, lfdir, lfname) != 0) {
432                         if (preen)
433                             printf(" (CREATED)\n");
434                     } else {
435                         freedir(lfdir, ROOTINO);
436                         lfdir = 0;
437                         if (preen)
438                             printf("\n");
439                     }
440                 }
441             }
442         }
443         if (lfdir == 0) {
444             pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
445             printf("\n\n");
446             return (0);
447         }
448     }
449     dp = ginode(lfdir);
450     if ((dp->di_mode & IFMT) != IFDIR) {
451         pfatal("lost+found IS NOT A DIRECTORY");
452         if (reply("REALLOCATE") == 0)
453             return (0);
454         oldlfdir = lfdir;
455         if ((lfdir = allocdir(ROOTINO, (ino_t) 0, lfmode)) == 0) {
456             pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
457             return (0);
458         }
459         idesc.id_type = DATA;
460         idesc.id_func = chgino;
461         idesc.id_number = ROOTINO;
462         idesc.id_parent = lfdir;        /* new inumber for lost+found */
463         idesc.id_name = lfname;
464         if ((ckinode(ginode(ROOTINO), &idesc) & ALTERED) == 0) {
465             pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
466             return (0);
467         }
468         inodirty();
469         idesc.id_type = ADDR;
470         idesc.id_func = pass4check;
471         idesc.id_number = oldlfdir;
472         adjust(&idesc, lncntp[oldlfdir] + 1);
473         lncntp[oldlfdir] = 0;
474         dp = ginode(lfdir);
475     }
476     if (statemap[lfdir] != DFOUND) {
477         pfatal("SORRY. NO lost+found DIRECTORY\n\n");
478         return (0);
479     }
480     len = strlen(lfname);
481     memcpy(pathp, lfname, len + 1);
482     pathp += len;
483     len = lftempname(tempname, orphan);
484     if (makeentry(lfdir, orphan, tempname) == 0) {
485         pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
486         printf("\n\n");
487         return (0);
488     }
489     lncntp[orphan]--;
490     *pathp++ = '/';
491     memcpy(pathp, tempname, len + 1);
492     pathp += len;
493     if (lostdir) {
494         dp = ginode(orphan);
495         idesc.id_type = DATA;
496         idesc.id_func = chgino;
497         idesc.id_number = orphan;
498         idesc.id_fix = DONTKNOW;
499         idesc.id_name = "..";
500         idesc.id_parent = lfdir;        /* new value for ".." */
501         (void)ckinode(dp, &idesc);
502         dp = ginode(lfdir);
503         dp->di_nlink++;
504         inodirty();
505         lncntp[lfdir]++;
506         pwarn("DIR I=%u CONNECTED. ", orphan);
507         printf("PARENT WAS I=%u\n", parentdir);
508         if (preen == 0)
509             printf("\n");
510     }
511     return (1);
512 }
513
514 /*
515  * make an entry in a directory
516  */
517 makeentry(parent, ino, name)
518      ino_t parent, ino;
519      char *name;
520 {
521     struct dinode *dp;
522     struct inodesc idesc;
523
524     if (parent < ROOTINO || parent >= maxino || ino < ROOTINO
525         || ino >= maxino)
526         return (0);
527     memset((char *)&idesc, 0, sizeof(struct inodesc));
528     idesc.id_type = DATA;
529     idesc.id_func = mkentry;
530     idesc.id_number = parent;
531     idesc.id_parent = ino;      /* this is the inode to enter */
532     idesc.id_fix = DONTKNOW;
533     idesc.id_name = name;
534     dp = ginode(parent);
535     if (dp->di_size % DIRBLKSIZ) {
536         dp->di_size = roundup(dp->di_size, DIRBLKSIZ);
537         inodirty();
538     }
539     if ((ckinode(dp, &idesc) & ALTERED) != 0)
540         return (1);
541     if (expanddir(dp) == 0)
542         return (0);
543     return (ckinode(dp, &idesc) & ALTERED);
544 }
545
546 /*
547  * Attempt to expand the size of a directory
548  */
549 expanddir(dp)
550      register struct dinode *dp;
551 {
552     daddr_t lastbn, newblk;
553     register struct bufarea *bp;
554     char *cp, firstblk[DIRBLKSIZ];
555
556     lastbn = lblkno(&sblock, dp->di_size);
557     if (lastbn >= NDADDR - 1)
558         return (0);
559     if ((newblk = allocblk(sblock.fs_frag)) == 0)
560         return (0);
561     dp->di_db[lastbn + 1] = dp->di_db[lastbn];
562     dp->di_db[lastbn] = newblk;
563     dp->di_size += (UOFF_T) sblock.fs_bsize;
564     dp->di_blocks += btodb(sblock.fs_bsize);
565     bp = getdirblk(dp->di_db[lastbn + 1], dblksize(&sblock, dp, lastbn + 1));
566     if (bp->b_errs)
567         goto bad;
568     memcpy(firstblk, bp->b_un.b_buf, DIRBLKSIZ);
569     bp = getdirblk(newblk, sblock.fs_bsize);
570     if (bp->b_errs)
571         goto bad;
572     memcpy(bp->b_un.b_buf, firstblk, DIRBLKSIZ);
573     for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
574          cp < &bp->b_un.b_buf[sblock.fs_bsize]; cp += DIRBLKSIZ)
575         memcpy(cp, (char *)&emptydir, sizeof emptydir);
576     dirty(bp);
577     bp = getdirblk(dp->di_db[lastbn + 1], dblksize(&sblock, dp, lastbn + 1));
578     if (bp->b_errs)
579         goto bad;
580     memcpy(bp->b_un.b_buf, (char *)&emptydir, sizeof emptydir);
581     pwarn("NO SPACE LEFT IN %s", pathname);
582     if (preen)
583         printf(" (EXPANDED)\n");
584     else if (reply("EXPAND") == 0)
585         goto bad;
586     dirty(bp);
587     inodirty();
588     return (1);
589   bad:
590     dp->di_db[lastbn] = dp->di_db[lastbn + 1];
591     dp->di_db[lastbn + 1] = 0;
592     dp->di_size -= (UOFF_T) sblock.fs_bsize;
593     dp->di_blocks -= btodb(sblock.fs_bsize);
594     freeblk(newblk, sblock.fs_frag);
595     return (0);
596 }
597
598 /*
599  * allocate a new directory
600  */
601 allocdir(parent, request, mode)
602      ino_t parent, request;
603      int mode;
604 {
605     ino_t ino;
606     char *cp;
607     struct dinode *dp;
608     register struct bufarea *bp;
609
610     ino = allocino(request, IFDIR | mode);
611     dirhead.dot_ino = ino;
612     dirhead.dotdot_ino = parent;
613     dp = ginode(ino);
614     bp = getdirblk(dp->di_db[0], sblock.fs_fsize);
615     if (bp->b_errs) {
616         freeino(ino);
617         return (0);
618     }
619     memcpy(bp->b_un.b_buf, (char *)&dirhead, sizeof dirhead);
620     for (cp = &bp->b_un.b_buf[DIRBLKSIZ];
621          cp < &bp->b_un.b_buf[sblock.fs_fsize]; cp += DIRBLKSIZ)
622         memcpy(cp, (char *)&emptydir, sizeof emptydir);
623     dirty(bp);
624     dp->di_nlink = 2;
625     inodirty();
626 #ifdef  AFS_SUN5_ENVX
627     if (!inocached(ino)) {
628         if (debug)
629             printf("inode %d added to directory cache\n", ino);
630         cacheino(dp, ino);
631     } else {
632         /*
633          * re-using an old directory inode
634          */
635         inp = getinoinfo(ino);
636         inp->i_isize = dp->di_size;
637         inp->i_numblks = dp->di_blocks * sizeof(daddr_t);
638         inp->i_parent = parent;
639         memcpy((char *)&inp->i_blks[0], (char *)&dp->di_db[0],
640                (int)inp->i_numblks);
641     }
642 #endif
643     if (ino == ROOTINO) {
644         lncntp[ino] = dp->di_nlink;
645         return (ino);
646     }
647     if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
648         freeino(ino);
649         return (0);
650     }
651     statemap[ino] = statemap[parent];
652     if (statemap[ino] == DSTATE) {
653         lncntp[ino] = dp->di_nlink;
654         lncntp[parent]++;
655     }
656     dp = ginode(parent);
657     dp->di_nlink++;
658     inodirty();
659     return (ino);
660 }
661
662 /*
663  * free a directory inode
664  */
665 freedir(ino, parent)
666      ino_t ino, parent;
667 {
668     struct dinode *dp;
669
670     if (ino != parent) {
671         dp = ginode(parent);
672         dp->di_nlink--;
673         inodirty();
674     }
675     freeino(ino);
676 }
677
678 /*
679  * generate a temporary name for the lost+found directory.
680  */
681 lftempname(bufp, ino)
682      char *bufp;
683      ino_t ino;
684 {
685     register ino_t in;
686     register char *cp;
687     int namlen;
688
689     cp = bufp + 2;
690     for (in = maxino; in > 0; in /= 10)
691         cp++;
692     *--cp = 0;
693     namlen = cp - bufp;
694     in = ino;
695     while (cp > bufp) {
696         *--cp = (in % 10) + '0';
697         in /= 10;
698     }
699     *cp = '#';
700     return (namlen);
701 }
702
703 /*
704  * Get a directory block.
705  * Insure that it is held until another is requested.
706  */
707 struct bufarea *
708 getdirblk(blkno, size)
709      daddr_t blkno;
710      long size;
711 {
712     if (mlk_pbp != 0)
713         mlk_pbp->b_flags &= ~B_INUSE;
714     mlk_pbp = getdatablk(blkno, size);
715     return (mlk_pbp);
716 }