2 * Copyright (c) 1980, 1986 The Regents of the University of California.
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.
18 #include <afsconfig.h>
19 #include <afs/param.h>
22 /* We need the old directory type headers (included below), so don't include
23 * the normal dirent.h, or it will conflict. */
25 # include <sys/inode.h>
26 # define LONGFILENAMES 1
27 # include <sys/sysmacros.h>
30 # ifdef HAVE_USR_OLD_USR_INCLUDE_NDIR_H
31 # include </usr/old/usr/include/ndir.h>
41 #define VICE /* allow us to put our changes in at will */
44 #include <sys/vnode.h>
45 #include <sys/mount.h>
46 #include <ufs/inode.h>
53 #else /* AFS_OSF_ENV */
54 #ifdef AFS_VFSINCL_ENV
56 #include <sys/vnode.h>
58 #include <sys/fs/ufs_inode.h>
59 #include <sys/fs/ufs_fs.h>
61 #include <sys/fs/ufs_fsdir.h>
64 #include <ufs/inode.h>
67 #include <ufs/fsdir.h>
71 #else /* AFS_VFSINCL_ENV */
72 #include <sys/inode.h>
79 #endif /* AFS_VFSINCL_ENV */
80 #endif /* AFS_OSF_ENV */
84 #include <sys/mnttab.h>
85 #include <sys/mntent.h>
94 struct dirtemplate_lfn
{
98 char dot_name
[4]; /* must be multiple of 4 */
99 afs_uint32 dotdot_ino
;
102 char dotdot_name
[4]; /* ditto */
104 #define dirtemplate dirtemplate_lfn
107 #define MINDIRSIZE (sizeof (struct dirtemplate))
109 char *endpathname
= &pathname
[BUFSIZ
- 2];
110 char *lfname
= "lost+found";
112 struct dirtemplate emptydir
= { 0, DIRBLKSIZ
};
113 struct dirtemplate dirhead
= { 0, 12, 1, ".", 0, DIRBLKSIZ
- 12, 2, ".." };
115 struct direct
*fsck_readdir();
116 struct bufarea
*getdirblk();
118 descend(parentino
, inumber
)
119 struct inodesc
*parentino
;
123 struct inodesc curino
;
125 memset(&curino
, 0, sizeof(struct inodesc
));
126 if (statemap
[inumber
] != DSTATE
)
127 errexit("BAD INODE %d TO DESCEND", statemap
[inumber
]);
128 #if defined(ACLS) && defined(AFS_HPUX_ENV)
130 * keep any continuation inode information
132 if (statemap
[inumber
] & HASCINODE
)
133 statemap
[inumber
] = HASCINODE
| DFOUND
;
135 statemap
[inumber
] = DFOUND
;
137 statemap
[inumber
] = DFOUND
;
139 dp
= ginode(inumber
);
140 if (dp
->di_size
== 0) {
141 direrror(inumber
, "ZERO LENGTH DIRECTORY");
142 if (reply("REMOVE") == 1)
143 #if defined(ACLS) && defined(AFS_HPUX_ENV)
145 * keep any continuation inode information
147 if (statemap
[inumber
] & HASCINODE
)
148 statemap
[inumber
] = HASCINODE
| DCLEAR
;
150 statemap
[inumber
] = DCLEAR
;
152 statemap
[inumber
] = DCLEAR
;
156 if (dp
->di_size
< MINDIRSIZE
) {
157 direrror(inumber
, "DIRECTORY TOO SHORT");
158 dp
->di_size
= MINDIRSIZE
;
159 if (reply("FIX") == 1)
162 #if !defined(AFS_HPUX_ENV)
163 /* For remaining 4.2 systems. We shouldn't convert
164 * dir size, since Unix 4.2 kernels won't maintain this, and we'll have a
165 * lot of spurious directory conversions scaring people */
166 if ((dp
->di_size
& (DIRBLKSIZ
- 1)) != 0) {
167 pwarn("DIRECTORY %s: LENGTH %d NOT MULTIPLE OF %d", pathname
,
168 dp
->di_size
, DIRBLKSIZ
);
169 dp
->di_size
= roundup(dp
->di_size
, DIRBLKSIZ
);
171 printf(" (ADJUSTED)\n");
172 if (preen
|| reply("ADJUST") == 1)
176 curino
.id_type
= DATA
;
177 curino
.id_func
= parentino
->id_func
;
178 curino
.id_parent
= parentino
->id_number
;
179 curino
.id_number
= inumber
;
180 (void)ckinode(dp
, &curino
);
181 if (curino
.id_entryno
< 2) {
182 direrror(inumber
, "NULL DIRECTORY");
183 if (reply("REMOVE") == 1)
184 statemap
[inumber
] = DCLEAR
;
189 struct inodesc
*idesc
;
195 char dbuf
[DIRBLKSIZ
];
197 if (idesc
->id_type
!= DATA
)
198 errexit("wrong type to dirscan %d\n", idesc
->id_type
);
199 if (idesc
->id_entryno
== 0 && (idesc
->id_filesize
& (DIRBLKSIZ
- 1)) != 0)
200 idesc
->id_filesize
= roundup(idesc
->id_filesize
, DIRBLKSIZ
);
201 blksiz
= idesc
->id_numfrags
* sblock
.fs_fsize
;
203 if (chkrange(idesc
->id_blkno
, idesc
->id_numfrags
)) {
204 idesc
->id_filesize
-= blksiz
;
208 for (dp
= fsck_readdir(idesc
); dp
!= NULL
; dp
= fsck_readdir(idesc
)) {
209 dsize
= dp
->d_reclen
;
210 memcpy(dbuf
, (char *)dp
, dsize
);
211 idesc
->id_dirp
= (struct direct
*)dbuf
;
212 if ((n
= (*idesc
->id_func
) (idesc
)) & ALTERED
) {
213 bp
= getdirblk(idesc
->id_blkno
, blksiz
);
214 memcpy((char *)dp
, dbuf
, dsize
);
221 return (idesc
->id_filesize
> 0 ? KEEPON
: STOP
);
225 * get next entry in a directory.
229 struct inodesc
*idesc
;
231 struct direct
*dp
, *ndp
;
235 blksiz
= idesc
->id_numfrags
* sblock
.fs_fsize
;
236 bp
= getdirblk(idesc
->id_blkno
, blksiz
);
237 if (idesc
->id_loc
% DIRBLKSIZ
== 0 && idesc
->id_filesize
> 0
238 && idesc
->id_loc
< blksiz
) {
239 dp
= (struct direct
*)(bp
->b_un
.b_buf
+ idesc
->id_loc
);
240 if (dircheck(idesc
, dp
)) {
243 idesc
->id_loc
+= DIRBLKSIZ
;
244 idesc
->id_filesize
-= DIRBLKSIZ
;
245 dp
->d_reclen
= DIRBLKSIZ
;
248 dp
->d_name
[0] = '\0';
249 if (dofix(idesc
, "DIRECTORY CORRUPTED"))
254 if (idesc
->id_filesize
<= 0 || idesc
->id_loc
>= blksiz
)
256 dp
= (struct direct
*)(bp
->b_un
.b_buf
+ idesc
->id_loc
);
257 idesc
->id_loc
+= dp
->d_reclen
;
258 idesc
->id_filesize
-= dp
->d_reclen
;
259 if ((idesc
->id_loc
% DIRBLKSIZ
) == 0)
261 ndp
= (struct direct
*)(bp
->b_un
.b_buf
+ idesc
->id_loc
);
262 if (idesc
->id_loc
< blksiz
&& idesc
->id_filesize
> 0
263 && dircheck(idesc
, ndp
) == 0) {
264 size
= DIRBLKSIZ
- (idesc
->id_loc
% DIRBLKSIZ
);
265 dp
->d_reclen
+= size
;
266 idesc
->id_loc
+= size
;
267 idesc
->id_filesize
-= size
;
268 if (dofix(idesc
, "DIRECTORY CORRUPTED"))
275 * Verify that a directory entry is valid.
276 * This is a superset of the checks made in the kernel.
279 struct inodesc
*idesc
;
287 spaceleft
= DIRBLKSIZ
- (idesc
->id_loc
% DIRBLKSIZ
);
288 if (dp
->d_ino
< maxino
&& dp
->d_reclen
!= 0 && dp
->d_reclen
<= spaceleft
289 && (dp
->d_reclen
& 0x3) == 0 && dp
->d_reclen
>= size
290 && idesc
->id_filesize
>= size
&& dp
->d_namlen
<= MAXNAMLEN
) {
293 for (cp
= dp
->d_name
, size
= 0; size
< dp
->d_namlen
; size
++)
294 #if defined(Next) || defined(AFS_SUN5_ENV)
300 if (*cp
== 0 || (*cp
++ & 0200)) {
310 direrror(ino
, errmesg
)
316 pwarn("%s ", errmesg
);
319 if (ino
< ROOTINO
|| ino
> maxino
) {
320 pfatal("NAME=%s\n", pathname
);
325 pfatal("%s=%s\n", (dp
->di_mode
& IFMT
) == IFDIR
? "DIR" : "FILE",
328 pfatal("NAME=%s\n", pathname
);
332 struct inodesc
*idesc
;
337 dp
= ginode(idesc
->id_number
);
338 if (dp
->di_nlink
== lcnt
) {
339 if (linkup(idesc
->id_number
, (ino_t
) 0) == 0)
340 clri(idesc
, "UNREF", 0);
342 pwarn("LINK COUNT %s",
343 (lfdir
== idesc
->id_number
) ? lfname
: ((dp
->di_mode
& IFMT
) ==
346 pinode(idesc
->id_number
);
347 printf(" COUNT %d SHOULD BE %d", dp
->di_nlink
, dp
->di_nlink
- lcnt
);
351 pfatal("LINK COUNT INCREASING");
353 printf(" (ADJUSTED)\n");
355 if (preen
|| reply("ADJUST") == 1) {
356 dp
->di_nlink
-= lcnt
;
363 struct inodesc
*idesc
;
365 struct direct
*dirp
= idesc
->id_dirp
;
366 struct direct newent
;
369 newent
.d_namlen
= 11;
370 newlen
= DIRSIZ(&newent
);
371 if (dirp
->d_ino
!= 0)
372 oldlen
= DIRSIZ(dirp
);
375 if (dirp
->d_reclen
- oldlen
< newlen
)
377 newent
.d_reclen
= dirp
->d_reclen
- oldlen
;
378 dirp
->d_reclen
= oldlen
;
379 dirp
= (struct direct
*)(((char *)dirp
) + oldlen
);
380 dirp
->d_ino
= idesc
->id_parent
; /* ino to be entered is in id_parent */
381 dirp
->d_reclen
= newent
.d_reclen
;
382 dirp
->d_namlen
= strlen(idesc
->id_name
);
383 memcpy(dirp
->d_name
, idesc
->id_name
, (int)dirp
->d_namlen
+ 1);
384 return (ALTERED
| STOP
);
388 struct inodesc
*idesc
;
390 struct direct
*dirp
= idesc
->id_dirp
;
392 if (memcmp(dirp
->d_name
, idesc
->id_name
, (int)dirp
->d_namlen
+ 1))
394 dirp
->d_ino
= idesc
->id_parent
;
395 return (ALTERED
| STOP
);
398 linkup(orphan
, parentdir
)
405 struct inodesc idesc
;
406 char tempname
[BUFSIZ
];
407 extern int pass4check();
409 memset(&idesc
, 0, sizeof(struct inodesc
));
411 lostdir
= (dp
->di_mode
& IFMT
) == IFDIR
;
412 pwarn("UNREF %s ", lostdir
? "DIR" : "FILE");
414 if (preen
&& dp
->di_size
== 0)
417 printf(" (RECONNECTED)\n");
418 else if (reply("RECONNECT") == 0)
424 dp
= ginode(ROOTINO
);
425 idesc
.id_name
= lfname
;
426 idesc
.id_type
= DATA
;
427 idesc
.id_func
= findino
;
428 idesc
.id_number
= ROOTINO
;
429 if ((ckinode(dp
, &idesc
) & FOUND
) != 0) {
430 lfdir
= idesc
.id_parent
;
432 pwarn("NO lost+found DIRECTORY");
433 if (preen
|| reply("CREATE")) {
434 lfdir
= allocdir(ROOTINO
, (ino_t
) 0, lfmode
);
436 if (makeentry(ROOTINO
, lfdir
, lfname
) != 0) {
438 printf(" (CREATED)\n");
440 freedir(lfdir
, ROOTINO
);
449 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
455 if ((dp
->di_mode
& IFMT
) != IFDIR
) {
456 pfatal("lost+found IS NOT A DIRECTORY");
457 if (reply("REALLOCATE") == 0)
460 if ((lfdir
= allocdir(ROOTINO
, (ino_t
) 0, lfmode
)) == 0) {
461 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
464 idesc
.id_type
= DATA
;
465 idesc
.id_func
= chgino
;
466 idesc
.id_number
= ROOTINO
;
467 idesc
.id_parent
= lfdir
; /* new inumber for lost+found */
468 idesc
.id_name
= lfname
;
469 if ((ckinode(ginode(ROOTINO
), &idesc
) & ALTERED
) == 0) {
470 pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
474 idesc
.id_type
= ADDR
;
475 idesc
.id_func
= pass4check
;
476 idesc
.id_number
= oldlfdir
;
477 adjust(&idesc
, lncntp
[oldlfdir
] + 1);
478 lncntp
[oldlfdir
] = 0;
481 if (statemap
[lfdir
] != DFOUND
) {
482 pfatal("SORRY. NO lost+found DIRECTORY\n\n");
485 len
= strlen(lfname
);
486 memcpy(pathp
, lfname
, len
+ 1);
488 len
= lftempname(tempname
, orphan
);
489 if (makeentry(lfdir
, orphan
, tempname
) == 0) {
490 pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
496 memcpy(pathp
, tempname
, len
+ 1);
500 idesc
.id_type
= DATA
;
501 idesc
.id_func
= chgino
;
502 idesc
.id_number
= orphan
;
503 idesc
.id_fix
= DONTKNOW
;
504 idesc
.id_name
= "..";
505 idesc
.id_parent
= lfdir
; /* new value for ".." */
506 (void)ckinode(dp
, &idesc
);
511 pwarn("DIR I=%u CONNECTED. ", orphan
);
512 printf("PARENT WAS I=%u\n", parentdir
);
520 * make an entry in a directory
522 makeentry(parent
, ino
, name
)
527 struct inodesc idesc
;
529 if (parent
< ROOTINO
|| parent
>= maxino
|| ino
< ROOTINO
532 memset(&idesc
, 0, sizeof(struct inodesc
));
533 idesc
.id_type
= DATA
;
534 idesc
.id_func
= mkentry
;
535 idesc
.id_number
= parent
;
536 idesc
.id_parent
= ino
; /* this is the inode to enter */
537 idesc
.id_fix
= DONTKNOW
;
538 idesc
.id_name
= name
;
540 if (dp
->di_size
% DIRBLKSIZ
) {
541 dp
->di_size
= roundup(dp
->di_size
, DIRBLKSIZ
);
544 if ((ckinode(dp
, &idesc
) & ALTERED
) != 0)
546 if (expanddir(dp
) == 0)
548 return (ckinode(dp
, &idesc
) & ALTERED
);
552 * Attempt to expand the size of a directory
557 daddr_t lastbn
, newblk
;
559 char *cp
, firstblk
[DIRBLKSIZ
];
561 lastbn
= lblkno(&sblock
, dp
->di_size
);
562 if (lastbn
>= NDADDR
- 1)
564 if ((newblk
= allocblk(sblock
.fs_frag
)) == 0)
566 dp
->di_db
[lastbn
+ 1] = dp
->di_db
[lastbn
];
567 dp
->di_db
[lastbn
] = newblk
;
568 dp
->di_size
+= (UOFF_T
) sblock
.fs_bsize
;
569 dp
->di_blocks
+= btodb(sblock
.fs_bsize
);
570 bp
= getdirblk(dp
->di_db
[lastbn
+ 1], dblksize(&sblock
, dp
, lastbn
+ 1));
573 memcpy(firstblk
, bp
->b_un
.b_buf
, DIRBLKSIZ
);
574 bp
= getdirblk(newblk
, sblock
.fs_bsize
);
577 memcpy(bp
->b_un
.b_buf
, firstblk
, DIRBLKSIZ
);
578 for (cp
= &bp
->b_un
.b_buf
[DIRBLKSIZ
];
579 cp
< &bp
->b_un
.b_buf
[sblock
.fs_bsize
]; cp
+= DIRBLKSIZ
)
580 memcpy(cp
, (char *)&emptydir
, sizeof emptydir
);
582 bp
= getdirblk(dp
->di_db
[lastbn
+ 1], dblksize(&sblock
, dp
, lastbn
+ 1));
585 memcpy(bp
->b_un
.b_buf
, (char *)&emptydir
, sizeof emptydir
);
586 pwarn("NO SPACE LEFT IN %s", pathname
);
588 printf(" (EXPANDED)\n");
589 else if (reply("EXPAND") == 0)
595 dp
->di_db
[lastbn
] = dp
->di_db
[lastbn
+ 1];
596 dp
->di_db
[lastbn
+ 1] = 0;
597 dp
->di_size
-= (UOFF_T
) sblock
.fs_bsize
;
598 dp
->di_blocks
-= btodb(sblock
.fs_bsize
);
599 freeblk(newblk
, sblock
.fs_frag
);
604 * allocate a new directory
606 allocdir(parent
, request
, mode
)
607 ino_t parent
, request
;
615 ino
= allocino(request
, IFDIR
| mode
);
616 dirhead
.dot_ino
= ino
;
617 dirhead
.dotdot_ino
= parent
;
619 bp
= getdirblk(dp
->di_db
[0], sblock
.fs_fsize
);
624 memcpy(bp
->b_un
.b_buf
, (char *)&dirhead
, sizeof dirhead
);
625 for (cp
= &bp
->b_un
.b_buf
[DIRBLKSIZ
];
626 cp
< &bp
->b_un
.b_buf
[sblock
.fs_fsize
]; cp
+= DIRBLKSIZ
)
627 memcpy(cp
, (char *)&emptydir
, sizeof emptydir
);
632 if (!inocached(ino
)) {
634 printf("inode %d added to directory cache\n", ino
);
638 * re-using an old directory inode
640 inp
= getinoinfo(ino
);
641 inp
->i_isize
= dp
->di_size
;
642 inp
->i_numblks
= dp
->di_blocks
* sizeof(daddr_t
);
643 inp
->i_parent
= parent
;
644 memcpy((char *)&inp
->i_blks
[0], (char *)&dp
->di_db
[0],
645 (int)inp
->i_numblks
);
648 if (ino
== ROOTINO
) {
649 lncntp
[ino
] = dp
->di_nlink
;
652 if (statemap
[parent
] != DSTATE
&& statemap
[parent
] != DFOUND
) {
656 statemap
[ino
] = statemap
[parent
];
657 if (statemap
[ino
] == DSTATE
) {
658 lncntp
[ino
] = dp
->di_nlink
;
668 * free a directory inode
684 * generate a temporary name for the lost+found directory.
686 lftempname(bufp
, ino
)
695 for (in
= maxino
; in
> 0; in
/= 10)
701 *--cp
= (in
% 10) + '0';
709 * Get a directory block.
710 * Insure that it is held until another is requested.
713 getdirblk(blkno
, size
)
718 mlk_pbp
->b_flags
&= ~B_INUSE
;
719 mlk_pbp
= getdatablk(blkno
, size
);