* Copyright (c) 1992 The Regents of the University of California.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)library.c 5.2 (Berkeley) %G%";
#include <ufs/ufs/dinode.h>
void add_blocks
__P((FS_INFO
*, BLOCK_INFO
*, int *, SEGSUM
*, caddr_t
,
void add_inodes
__P((FS_INFO
*, INODE_INFO
*, int *, SEGSUM
*, caddr_t
,
int bi_compare
__P((const void *, const void *));
int bi_toss
__P((const void *, const void *, const void *));
void get_ifile
__P((FS_INFO
*));
int get_superblock
__P((FS_INFO
*, struct lfs
*));
int ii_compare
__P((const void *, const void *));
int ii_toss
__P((const void *, const void *, const void *));
int pseg_valid
__P((FS_INFO
*, SEGSUM
*));
* This function will get information on all mounted file systems
tcount
= getmntinfo(&tstatfsp
, MNT_NOWAIT
);
err(0, "getmntinfo failed");
for (count
= 0, i
= 0; i
< tcount
; ++i
)
if (tstatfsp
[i
].f_type
== type
)
if (!(*buf
= (struct statfs
*)
malloc(count
* sizeof(struct statfs
))))
err(1, "fs_getmntinfo: out of space");
for (i
= 0, sbp
= *buf
; i
< tcount
; ++i
) {
if (tstatfsp
[i
].f_type
== type
) {
* Get all the information available on an LFS file system.
* Returns an array of FS_INFO structures, NULL on error.
get_fs_info (lstatfsp
, count
)
struct statfs
*lstatfsp
; /* IN: array of statfs structs */
int count
; /* IN: number of file systems */
fsp
= (FS_INFO
*)malloc(count
* sizeof(FS_INFO
));
for (fp
= fsp
, i
= 0; i
< count
; ++i
, ++fp
) {
fp
->fi_statfsp
= lstatfsp
++;
if (get_superblock (fp
, &fp
->fi_lfs
))
err(1, "get_fs_info: get_superblock failed");
fp
->fi_lfs
.lfs_bshift
- fp
->fi_lfs
.lfs_fsbtodb
;
* If we are reading the ifile then we need to refresh it. Even if
* we are mmapping it, it might have grown. Finally, we need to
* refresh the file system information (statfs) info.
reread_fs_info(fsp
, count
)
FS_INFO
*fsp
; /* IN: array of fs_infos to free */
int count
; /* IN: number of file systems */
for (i
= 0; i
< count
; ++i
, ++fsp
) {
if (statfs(fsp
->fi_statfsp
->f_mntonname
, fsp
->fi_statfsp
))
err(0, "reread_fs_info: statfs failed");
if (munmap(fsp
->fi_cip
, fsp
->fi_ifile_length
) < 0)
err(0, "reread_fs_info: munmap failed");
* Gets the superblock from disk (possibly in face of errors)
get_superblock (fsp
, sbp
)
FS_INFO
*fsp
; /* local file system info structure */
char mntfromname
[MNAMELEN
+1];
strcpy(mntfromname
, "/dev/r");
strcat(mntfromname
, fsp
->fi_statfsp
->f_mntfromname
+5);
if ((fid
= open(mntfromname
, O_RDONLY
, (mode_t
)0)) < 0) {
err(0, "get_superblock: bad open");
get(fid
, LFS_LABELPAD
, sbp
, sizeof(struct lfs
));
* This function will map the ifile into memory. It causes a
* fatal error on failure.
ifile_name
= malloc(strlen(fsp
->fi_statfsp
->f_mntonname
) +
strcat(strcat(strcpy(ifile_name
, fsp
->fi_statfsp
->f_mntonname
), "/"),
if ((fid
= open(ifile_name
, O_RDWR
, (mode_t
)0)) < 0)
err(1, "get_ifile: bad open");
if (fstat (fid
, &file_stat
))
err(1, "get_ifile: fstat failed");
fsp
->fi_ifile_length
= file_stat
.st_size
;
if (!(ifp
= malloc ((size_t)fsp
->fi_ifile_length
)))
err (1, "get_ifile: malloc failed");
count
= read (fid
, ifp
, (size_t) fsp
->fi_ifile_length
);
err(1, "get_ifile: bad ifile read");
else if (count
< (int)fsp
->fi_ifile_length
) {
if (lseek(fid
, 0, SEEK_SET
) < 0)
err(1, "get_ifile: bad ifile lseek");
ifp
= mmap ((caddr_t
)0, (size_t) fsp
->fi_ifile_length
, PROT_READ
|PROT_WRITE
,
MAP_FILE
|MAP_SHARED
, fid
, (off_t
)0);
err(1, "get_ifile: mmap failed");
fsp
->fi_cip
= (CLEANERINFO
*)ifp
;
fsp
->fi_segusep
= (SEGUSE
*)(ifp
+ CLEANSIZE(fsp
));
fsp
->fi_ifilep
= (IFILE
*)((caddr_t
)fsp
->fi_segusep
+ SEGTABSIZE(fsp
));
* The number of ifile entries is equal to the number of blocks
* blocks in the ifile minus the ones allocated to cleaner info
* and segment usage table multiplied by the number of ifile
fsp
->fi_ifile_count
= (fsp
->fi_ifile_length
>> fsp
->fi_lfs
.lfs_bshift
-
fsp
->fi_lfs
.lfs_cleansz
- fsp
->fi_lfs
.lfs_segtabsz
) *
* This function will scan a segment and return a list of
* <inode, blocknum> pairs which indicate which blocks were
* contained as live data within the segment when the segment
* summary was read (it may have "died" since then). Any given
* pair will be listed at most once.
lfs_segmapv(fsp
, seg
, seg_buf
, blocks
, bcount
, inodes
, icount
)
FS_INFO
*fsp
; /* pointer to local file system information */
int seg
; /* the segment number */
caddr_t seg_buf
; /* the buffer containing the segment's data */
BLOCK_INFO
**blocks
; /* OUT: array of block_info for live blocks */
int *bcount
; /* OUT: number of active blocks in segment */
INODE_INFO
**inodes
; /* OUT: array of inode_info for live inodes */
int *icount
; /* OUT: number of active inodes in segment */
daddr_t pseg_addr
, seg_addr
;
int nblocks
, num_iblocks
;
num_iblocks
= lfsp
->lfs_ssize
;
if (!(bip
= malloc(lfsp
->lfs_ssize
* sizeof(BLOCK_INFO
))))
if (!(iip
= malloc(lfsp
->lfs_ssize
* sizeof(INODE_INFO
))))
sup
= SEGUSE_ENTRY(lfsp
, fsp
->fi_segusep
, seg
);
s
= seg_buf
+ (sup
->su_flags
& SEGUSE_SUPERBLOCK
? LFS_SBPAD
: 0);
seg_addr
= sntoda(lfsp
, seg
);
pseg_addr
= seg_addr
+ (sup
->su_flags
& SEGUSE_SUPERBLOCK
? btodb(LFS_SBPAD
) : 0);
printf("\tsegment buffer at: 0x%x\tseg_addr 0x%x\n", s
, seg_addr
);
for (segend
= seg_buf
+ seg_size(lfsp
), timestamp
= 0; s
< segend
; ) {
printf("\tpartial at: 0x%x\n", pseg_addr
);
nblocks
= pseg_valid(fsp
, sp
);
/* Check if we have hit old data */
if (timestamp
> ((SEGSUM
*)s
)->ss_create
)
timestamp
= ((SEGSUM
*)s
)->ss_create
;
* Right now we die if we run out of room, we could probably
* recover if we were smart.
if (*icount
+ sp
->ss_ninos
> num_iblocks
) {
num_iblocks
= *icount
+ sp
->ss_ninos
;
iip
= realloc (iip
, num_iblocks
* sizeof(INODE_INFO
));
add_inodes(fsp
, iip
, icount
, sp
, seg_buf
, seg_addr
);
add_blocks(fsp
, bip
, bcount
, sp
, seg_buf
, seg_addr
, pseg_addr
);
pseg_addr
+= fsbtodb(lfsp
, nblocks
) +
bytetoda(fsp
, LFS_SUMMARY_SIZE
);
s
+= (nblocks
<< lfsp
->lfs_bshift
) + LFS_SUMMARY_SIZE
;
qsort(iip
, *icount
, sizeof(INODE_INFO
), ii_compare
);
qsort(bip
, *bcount
, sizeof(BLOCK_INFO
), bi_compare
);
toss(iip
, icount
, sizeof(INODE_INFO
), ii_toss
, NULL
);
toss(bip
, bcount
, sizeof(BLOCK_INFO
), bi_toss
, NULL
);
for (_bip
= bip
, i
=0; i
< *bcount
; ++_bip
, ++i
)
for (_iip
= iip
, i
=0; i
< *icount
; ++_iip
, ++i
)
* This will parse a partial segment and fill in BLOCK_INFO structures
* for each block described in the segment summary. It will not include
* blocks or inodes from files with new version numbers.
add_blocks (fsp
, bip
, countp
, sp
, seg_buf
, segaddr
, psegaddr
)
FS_INFO
*fsp
; /* pointer to super block */
BLOCK_INFO
*bip
; /* Block info array */
int *countp
; /* IN/OUT: number of blocks in array */
SEGSUM
*sp
; /* segment summmary pointer */
caddr_t seg_buf
; /* buffer containing segment */
daddr_t segaddr
; /* address of this segment */
daddr_t psegaddr
; /* address of this partial segment */
daddr_t
*iaddrp
; /* pointer to current inode block */
db_per_block
= fsbtodb(&fsp
->fi_lfs
, 1);
page_size
= fsp
->fi_lfs
.lfs_bsize
;
bp
= seg_buf
+ datobyte(fsp
, psegaddr
- segaddr
) + LFS_SUMMARY_SIZE
;
psegaddr
+= bytetoda(fsp
, LFS_SUMMARY_SIZE
);
iaddrp
= (daddr_t
*)((caddr_t
)sp
+ LFS_SUMMARY_SIZE
);
for (fip
= (FINFO
*)(sp
+ 1), i
= 0; i
< sp
->ss_nfinfo
;
++i
, fip
= (FINFO
*)(&fip
->fi_blocks
[fip
->fi_nblocks
])) {
ifp
= IFILE_ENTRY(&fsp
->fi_lfs
, fsp
->fi_ifilep
, fip
->fi_ino
);
if (ifp
->if_version
> fip
->fi_version
)
dp
= &(fip
->fi_blocks
[0]);
for (j
= 0; j
< fip
->fi_nblocks
; j
++, dp
++) {
while (psegaddr
== *iaddrp
) {
psegaddr
+= db_per_block
;
bip
->bi_inode
= fip
->fi_ino
;
bip
->bi_daddr
= psegaddr
;
bip
->bi_segcreate
= (time_t)(sp
->ss_create
);
psegaddr
+= db_per_block
;
* For a particular segment summary, reads the inode blocks and adds
* INODE_INFO structures to the array. Returns the number of inodes
add_inodes (fsp
, iip
, countp
, sp
, seg_buf
, seg_addr
)
FS_INFO
*fsp
; /* pointer to super block */
int *countp
; /* pointer to current number of inodes */
SEGSUM
*sp
; /* segsum pointer */
caddr_t seg_buf
; /* the buffer containing the segment's data */
daddr_t seg_addr
; /* disk address of seg_buf */
(void) printf("INODE_INFOS:\n");
daddrp
= (daddr_t
*)((caddr_t
)sp
+ LFS_SUMMARY_SIZE
);
for (i
= 0; i
< sp
->ss_ninos
; ++i
) {
if (i
% INOPB(lfsp
) == 0) {
di
= (struct dinode
*)(seg_buf
+
((*daddrp
- seg_addr
) << fsp
->fi_daddr_shift
));
ip
->ii_segcreate
= sp
->ss_create
;
ifp
= IFILE_ENTRY(lfsp
, fsp
->fi_ifilep
, inum
);
PRINT_IINFO(ifp
->if_daddr
== *daddrp
, ip
);
if (ifp
->if_daddr
== *daddrp
) {
* Checks the summary checksum and the data checksum to determine if the
* segment is valid or not. Returns the size of the partial segment if it
* is valid, * and 0 otherwise. Use dump_summary to figure out size of the
* the partial as well as whether or not the checksum is valid.
FS_INFO
*fsp
; /* pointer to file system info */
SEGSUM
*ssp
; /* pointer to segment summary block */
if ((nblocks
= dump_summary(&fsp
->fi_lfs
, ssp
, 0, NULL
)) <= 0)
/* check data/inode block(s) checksum too */
datap
= (u_long
*)malloc(nblocks
* sizeof(u_long
));
p
= (caddr_t
)ssp
+ LFS_SUMMARY_SIZE
;
for (i
= 0; i
< nblocks
; ++i
) {
datap
[i
] = *((u_long
*)p
);
p
+= fsp
->fi_lfs
.lfs_bsize
;
if (cksum ((void *)datap
, nblocks
* sizeof(u_long
)) != ssp
->ss_datasum
)
/* #define MMAP_SEGMENT */
* read a segment into a memory buffer
mmap_segment (fsp
, segment
, segbuf
)
FS_INFO
*fsp
; /* file system information */
int segment
; /* segment number */
caddr_t
*segbuf
; /* pointer to buffer area */
int fid
; /* fildes for file system device */
daddr_t seg_daddr
; /* base disk address of segment */
char mntfromname
[MNAMELEN
+2];
/* get the disk address of the beginning of the segment */
seg_daddr
= sntoda(lfsp
, segment
);
seg_byte
= datobyte(fsp
, seg_daddr
);
strcpy(mntfromname
, "/dev/r");
strcat(mntfromname
, fsp
->fi_statfsp
->f_mntfromname
+5);
if ((fid
= open(mntfromname
, O_RDONLY
, (mode_t
)0)) < 0) {
err(0, "mmap_segment: bad open");
*segbuf
= mmap ((caddr_t
)0, seg_size(lfsp
), PROT_READ
,
MAP_FILE
, fid
, seg_byte
);
if (*(long *)segbuf
< 0) {
err(0, "mmap_segment: mmap failed");
printf("mmap_segment\tseg_daddr: %lu\tseg_size: %lu\tseg_offset: %qu\n",
seg_daddr
, ssize
, seg_byte
);
/* malloc the space for the buffer */
err(0, "mmap_segment: malloc failed");
/* read the segment data into the buffer */
if (lseek (fid
, seg_byte
, SEEK_SET
) != seg_byte
) {
err (0, "mmap_segment: bad lseek");
if (read (fid
, *segbuf
, ssize
) != ssize
) {
err (0, "mmap_segment: bad read");
#endif /* MMAP_SEGMENT */
munmap_segment (fsp
, seg_buf
)
FS_INFO
*fsp
; /* file system information */
caddr_t seg_buf
; /* pointer to buffer area */
munmap (seg_buf
, seg_size(&fsp
->fi_lfs
));
#endif /* MMAP_SEGMENT */
* USEFUL DEBUGGING TOOLS:
(void) dump_summary(lfsp
, p
, DUMP_ALL
, NULL
);
const BLOCK_INFO
*ba
, *bb
;
if (diff
= (int)(ba
->bi_inode
- bb
->bi_inode
))
if (diff
= (int)(ba
->bi_lbn
- bb
->bi_lbn
))
if (diff
= (int)(ba
->bi_segcreate
- bb
->bi_segcreate
))
diff
= (int)(ba
->bi_daddr
- bb
->bi_daddr
);
const BLOCK_INFO
*ba
, *bb
;
return(ba
->bi_inode
== bb
->bi_inode
&& ba
->bi_lbn
== bb
->bi_lbn
);
* Right now, we never look at the actually data being
* passed to the kernel in iip->ii_dinode. Therefore,
* if the same inode appears twice in the same block
* (i.e. has the same disk address), it doesn't matter
* which entry we pass. However, if we get the kernel
* to start looking at the dinode, then we will care
* and we'll need some way to distinguish which inode
* is the more recent one.
const INODE_INFO
*ia
, *ib
;
if (diff
= (int)(ia
->ii_inode
- ib
->ii_inode
))
if (diff
= (int)(ia
->ii_segcreate
- ib
->ii_segcreate
))
diff
= (int)(ia
->ii_daddr
- ib
->ii_daddr
);
const INODE_INFO
*ia
, *ib
;
return(ia
->ii_inode
== ib
->ii_inode
);
toss(p
, nump
, size
, dotoss
, client
)
int (*dotoss
) __P((const void *, const void *, const void *));
for (i
= *nump
; --i
> 0;) {
if (dotoss(client
, p
, p1
)) {