* Copyright (c) 1980, 1986, 1993
* The Regents of the University of California. All rights reserved.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)dir.c 8.7 (Berkeley) %G%";
#include <ufs/ufs/dinode.h>
char *lfname
= "lost+found";
struct dirtemplate emptydir
= { 0, DIRBLKSIZ
};
struct dirtemplate dirhead
= {
0, DIRBLKSIZ
- 12, DT_DIR
, 2, ".."
struct odirtemplate odirhead
= {
0, DIRBLKSIZ
- 12, 2, ".."
static int chgino
__P((struct inodesc
*));
static int dircheck
__P((struct inodesc
*, struct direct
*));
static int expanddir
__P((struct dinode
*dp
, char *name
));
static void freedir
__P((ino_t ino
, ino_t parent
));
static struct direct
*fsck_readdir
__P((struct inodesc
*));
static struct bufarea
*getdirblk
__P((ufs_daddr_t blkno
, long size
));
static int lftempname
__P((char *bufp
, ino_t ino
));
static int mkentry
__P((struct inodesc
*));
* Propagate connected state through the tree.
register struct inoinfo
**inpp
, *inp
;
inpend
= &inpsort
[inplast
];
for (inpp
= inpsort
; inpp
< inpend
; inpp
++) {
if (statemap
[inp
->i_parent
] == DFOUND
&&
statemap
[inp
->i_number
] == DSTATE
) {
statemap
[inp
->i_number
] = DFOUND
;
* Scan each entry in a directory block.
register struct inodesc
*idesc
;
register struct direct
*dp
;
register struct bufarea
*bp
;
if (idesc
->id_type
!= DATA
)
errx(EEXIT
, "wrong type to dirscan %d", idesc
->id_type
);
if (idesc
->id_entryno
== 0 &&
(idesc
->id_filesize
& (DIRBLKSIZ
- 1)) != 0)
idesc
->id_filesize
= roundup(idesc
->id_filesize
, DIRBLKSIZ
);
blksiz
= idesc
->id_numfrags
* sblock
.fs_fsize
;
if (chkrange(idesc
->id_blkno
, idesc
->id_numfrags
)) {
idesc
->id_filesize
-= blksiz
;
for (dp
= fsck_readdir(idesc
); dp
!= NULL
; dp
= fsck_readdir(idesc
)) {
bcopy((char *)dp
, dbuf
, (size_t)dsize
);
# if (BYTE_ORDER == LITTLE_ENDIAN)
struct direct
*tdp
= (struct direct
*)dbuf
;
tdp
->d_namlen
= tdp
->d_type
;
idesc
->id_dirp
= (struct direct
*)dbuf
;
if ((n
= (*idesc
->id_func
)(idesc
)) & ALTERED
) {
# if (BYTE_ORDER == LITTLE_ENDIAN)
if (!newinofmt
&& !doinglevel2
) {
tdp
= (struct direct
*)dbuf
;
tdp
->d_namlen
= tdp
->d_type
;
bp
= getdirblk(idesc
->id_blkno
, blksiz
);
bcopy(dbuf
, bp
->b_un
.b_buf
+ idesc
->id_loc
- dsize
,
return (idesc
->id_filesize
> 0 ? KEEPON
: STOP
);
* get next entry in a directory.
register struct inodesc
*idesc
;
register struct direct
*dp
, *ndp
;
register struct bufarea
*bp
;
long size
, blksiz
, fix
, dploc
;
blksiz
= idesc
->id_numfrags
* sblock
.fs_fsize
;
bp
= getdirblk(idesc
->id_blkno
, blksiz
);
if (idesc
->id_loc
% DIRBLKSIZ
== 0 && idesc
->id_filesize
> 0 &&
idesc
->id_loc
< blksiz
) {
dp
= (struct direct
*)(bp
->b_un
.b_buf
+ idesc
->id_loc
);
if (idesc
->id_fix
== IGNORE
)
fix
= dofix(idesc
, "DIRECTORY CORRUPTED");
bp
= getdirblk(idesc
->id_blkno
, blksiz
);
dp
= (struct direct
*)(bp
->b_un
.b_buf
+ idesc
->id_loc
);
dp
->d_reclen
= DIRBLKSIZ
;
idesc
->id_loc
+= DIRBLKSIZ
;
idesc
->id_filesize
-= DIRBLKSIZ
;
if (idesc
->id_filesize
<= 0 || idesc
->id_loc
>= blksiz
)
dp
= (struct direct
*)(bp
->b_un
.b_buf
+ dploc
);
idesc
->id_loc
+= dp
->d_reclen
;
idesc
->id_filesize
-= dp
->d_reclen
;
if ((idesc
->id_loc
% DIRBLKSIZ
) == 0)
ndp
= (struct direct
*)(bp
->b_un
.b_buf
+ idesc
->id_loc
);
if (idesc
->id_loc
< blksiz
&& idesc
->id_filesize
> 0 &&
dircheck(idesc
, ndp
) == 0) {
size
= DIRBLKSIZ
- (idesc
->id_loc
% DIRBLKSIZ
);
idesc
->id_filesize
-= size
;
if (idesc
->id_fix
== IGNORE
)
fix
= dofix(idesc
, "DIRECTORY CORRUPTED");
bp
= getdirblk(idesc
->id_blkno
, blksiz
);
dp
= (struct direct
*)(bp
->b_un
.b_buf
+ dploc
);
* Verify that a directory entry is valid.
* This is a superset of the checks made in the kernel.
register struct direct
*dp
;
spaceleft
= DIRBLKSIZ
- (idesc
->id_loc
% DIRBLKSIZ
);
if (dp
->d_ino
>= maxino
||
dp
->d_reclen
> spaceleft
||
(dp
->d_reclen
& 0x3) != 0)
size
= DIRSIZ(!newinofmt
, dp
);
# if (BYTE_ORDER == LITTLE_ENDIAN)
if (dp
->d_reclen
< size
||
idesc
->id_filesize
< size
||
for (cp
= dp
->d_name
, size
= 0; size
< namlen
; size
++)
if (*cp
== '\0' || (*cp
++ == '/'))
fileerror(ino
, ino
, errmesg
);
fileerror(cwd
, ino
, errmesg
)
register struct dinode
*dp
;
char pathbuf
[MAXPATHLEN
+ 1];
getpathname(pathbuf
, cwd
, ino
);
if (ino
< ROOTINO
|| ino
> maxino
) {
pfatal("NAME=%s\n", pathbuf
);
(dp
->di_mode
& IFMT
) == IFDIR
? "DIR" : "FILE", pathbuf
);
pfatal("NAME=%s\n", pathbuf
);
register struct inodesc
*idesc
;
register struct dinode
*dp
;
dp
= ginode(idesc
->id_number
);
if (dp
->di_nlink
== lcnt
) {
if (linkup(idesc
->id_number
, (ino_t
)0) == 0)
pwarn("LINK COUNT %s", (lfdir
== idesc
->id_number
) ? lfname
:
((dp
->di_mode
& IFMT
) == IFDIR
? "DIR" : "FILE"));
pinode(idesc
->id_number
);
printf(" COUNT %d SHOULD BE %d",
dp
->di_nlink
, dp
->di_nlink
- lcnt
);
pfatal("LINK COUNT INCREASING");
if (preen
|| reply("ADJUST") == 1) {
register struct direct
*dirp
= idesc
->id_dirp
;
newent
.d_namlen
= strlen(idesc
->id_name
);
newlen
= DIRSIZ(0, &newent
);
oldlen
= DIRSIZ(0, dirp
);
if (dirp
->d_reclen
- oldlen
< newlen
)
newent
.d_reclen
= dirp
->d_reclen
- oldlen
;
dirp
= (struct direct
*)(((char *)dirp
) + oldlen
);
dirp
->d_ino
= idesc
->id_parent
; /* ino to be entered is in id_parent */
dirp
->d_reclen
= newent
.d_reclen
;
dirp
->d_type
= typemap
[idesc
->id_parent
];
dirp
->d_namlen
= newent
.d_namlen
;
bcopy(idesc
->id_name
, dirp
->d_name
, (size_t)newent
.d_namlen
+ 1);
# if (BYTE_ORDER == LITTLE_ENDIAN)
* If the entry was split, dirscan() will only reverse the byte
* order of the original entry, and not the new one, before
* writing it back out. So, we reverse the byte order here if
if (oldlen
!= 0 && !newinofmt
&& !doinglevel2
) {
dirp
->d_namlen
= dirp
->d_type
;
register struct direct
*dirp
= idesc
->id_dirp
;
if (bcmp(dirp
->d_name
, idesc
->id_name
, (int)dirp
->d_namlen
+ 1))
dirp
->d_ino
= idesc
->id_parent
;
dirp
->d_type
= typemap
[idesc
->id_parent
];
linkup(orphan
, parentdir
)
register struct dinode
*dp
;
bzero((char *)&idesc
, sizeof(struct inodesc
));
lostdir
= (dp
->di_mode
& IFMT
) == IFDIR
;
pwarn("UNREF %s ", lostdir
? "DIR" : "FILE");
if (preen
&& dp
->di_size
== 0)
printf(" (RECONNECTED)\n");
if (reply("RECONNECT") == 0)
idesc
.id_number
= ROOTINO
;
if ((ckinode(dp
, &idesc
) & FOUND
) != 0) {
pwarn("NO lost+found DIRECTORY");
if (preen
|| reply("CREATE")) {
lfdir
= allocdir(ROOTINO
, (ino_t
)0, lfmode
);
if (makeentry(ROOTINO
, lfdir
, lfname
) != 0) {
pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
if ((dp
->di_mode
& IFMT
) != IFDIR
) {
pfatal("lost+found IS NOT A DIRECTORY");
if (reply("REALLOCATE") == 0)
if ((lfdir
= allocdir(ROOTINO
, (ino_t
)0, lfmode
)) == 0) {
pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
if ((changeino(ROOTINO
, lfname
, lfdir
) & ALTERED
) == 0) {
pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
idesc
.id_func
= pass4check
;
idesc
.id_number
= oldlfdir
;
adjust(&idesc
, lncntp
[oldlfdir
] + 1);
if (statemap
[lfdir
] != DFOUND
) {
pfatal("SORRY. NO lost+found DIRECTORY\n\n");
(void)lftempname(tempname
, orphan
);
if (makeentry(lfdir
, orphan
, tempname
) == 0) {
pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
if ((changeino(orphan
, "..", lfdir
) & ALTERED
) == 0 &&
(void)makeentry(orphan
, lfdir
, "..");
pwarn("DIR I=%lu CONNECTED. ", orphan
);
if (parentdir
!= (ino_t
)-1)
printf("PARENT WAS I=%lu\n", parentdir
);
* fix an entry in a directory.
changeino(dir
, name
, newnum
)
bzero((char *)&idesc
, sizeof(struct inodesc
));
idesc
.id_parent
= newnum
; /* new value for name */
return (ckinode(ginode(dir
), &idesc
));
* make an entry in a directory
makeentry(parent
, ino
, name
)
char pathbuf
[MAXPATHLEN
+ 1];
if (parent
< ROOTINO
|| parent
>= maxino
||
ino
< ROOTINO
|| ino
>= maxino
)
bzero((char *)&idesc
, sizeof(struct inodesc
));
idesc
.id_number
= parent
;
idesc
.id_parent
= ino
; /* this is the inode to enter */
if (dp
->di_size
% DIRBLKSIZ
) {
dp
->di_size
= roundup(dp
->di_size
, DIRBLKSIZ
);
if ((ckinode(dp
, &idesc
) & ALTERED
) != 0)
getpathname(pathbuf
, parent
, parent
);
if (expanddir(dp
, pathbuf
) == 0)
return (ckinode(dp
, &idesc
) & ALTERED
);
* Attempt to expand the size of a directory
register struct dinode
*dp
;
ufs_daddr_t lastbn
, newblk
;
register struct bufarea
*bp
;
char *cp
, firstblk
[DIRBLKSIZ
];
lastbn
= lblkno(&sblock
, dp
->di_size
);
if (lastbn
>= NDADDR
- 1 || dp
->di_db
[lastbn
] == 0 || dp
->di_size
== 0)
if ((newblk
= allocblk(sblock
.fs_frag
)) == 0)
dp
->di_db
[lastbn
+ 1] = dp
->di_db
[lastbn
];
dp
->di_db
[lastbn
] = newblk
;
dp
->di_size
+= sblock
.fs_bsize
;
dp
->di_blocks
+= btodb(sblock
.fs_bsize
);
bp
= getdirblk(dp
->di_db
[lastbn
+ 1],
(long)dblksize(&sblock
, dp
, lastbn
+ 1));
bcopy(bp
->b_un
.b_buf
, firstblk
, DIRBLKSIZ
);
bp
= getdirblk(newblk
, sblock
.fs_bsize
);
bcopy(firstblk
, bp
->b_un
.b_buf
, DIRBLKSIZ
);
for (cp
= &bp
->b_un
.b_buf
[DIRBLKSIZ
];
cp
< &bp
->b_un
.b_buf
[sblock
.fs_bsize
];
bcopy((char *)&emptydir
, cp
, sizeof emptydir
);
bp
= getdirblk(dp
->di_db
[lastbn
+ 1],
(long)dblksize(&sblock
, dp
, lastbn
+ 1));
bcopy((char *)&emptydir
, bp
->b_un
.b_buf
, sizeof emptydir
);
pwarn("NO SPACE LEFT IN %s", name
);
else if (reply("EXPAND") == 0)
dp
->di_db
[lastbn
] = dp
->di_db
[lastbn
+ 1];
dp
->di_db
[lastbn
+ 1] = 0;
dp
->di_size
-= sblock
.fs_bsize
;
dp
->di_blocks
-= btodb(sblock
.fs_bsize
);
freeblk(newblk
, sblock
.fs_frag
);
* allocate a new directory
allocdir(parent
, request
, mode
)
register struct bufarea
*bp
;
struct dirtemplate
*dirp
;
ino
= allocino(request
, IFDIR
|mode
);
dirp
= (struct dirtemplate
*)&odirhead
;
dirp
->dotdot_ino
= parent
;
bp
= getdirblk(dp
->di_db
[0], sblock
.fs_fsize
);
bcopy((char *)dirp
, bp
->b_un
.b_buf
, sizeof(struct dirtemplate
));
for (cp
= &bp
->b_un
.b_buf
[DIRBLKSIZ
];
cp
< &bp
->b_un
.b_buf
[sblock
.fs_fsize
];
bcopy((char *)&emptydir
, cp
, sizeof emptydir
);
lncntp
[ino
] = dp
->di_nlink
;
if (statemap
[parent
] != DSTATE
&& statemap
[parent
] != DFOUND
) {
statemap
[ino
] = statemap
[parent
];
if (statemap
[ino
] == DSTATE
) {
lncntp
[ino
] = dp
->di_nlink
;
* generate a temporary name for the lost+found directory.
for (in
= maxino
; in
> 0; in
/= 10)
* Insure that it is held until another is requested.
pdirbp
->b_flags
&= ~B_INUSE
;
pdirbp
= getdatablk(blkno
, size
);