* Copyright (c) 1980, 1988, 1991, 1993
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)traverse.c 8.2 (Berkeley) 9/23/93";
#include <ufs/ufs/dinode.h>
#include <protocols/dumprestore.h>
#define HASDUMPEDFILE 0x1
static int dirindir
__P((ino_t ino
, daddr_t blkno
, int level
, long *size
));
static void dmpindir
__P((ino_t ino
, daddr_t blk
, int level
, fsizeT
*size
));
static int searchdir
__P((ino_t ino
, daddr_t blkno
, long size
, long filesize
));
* This is an estimation of the number of TP_BSIZE blocks in the file.
* It estimates the number of blocks in files with holes by assuming
* that all of the blocks accounted for by di_blocks are data blocks
* (when some of the blocks are usually used for indirect pointers);
* hence the estimate may be high.
register struct dinode
*dp
;
* dp->di_size is the size of the file in bytes.
* dp->di_blocks stores the number of sectors actually in the file.
* If there are more sectors than the size would indicate, this just
* means that there are indirect blocks in the file or unused
* sectors in the last file block; we can safely ignore these
* (blkest = sizeest below).
* If the file is bigger than the number of sectors would indicate,
* then the file has holes in it. In this case we must use the
* block count to estimate the number of data blocks used, but
* we use the actual size for estimating the number of indirect
* dump blocks (sizeest vs. blkest in the indirect block
blkest
= howmany(dbtob(dp
->di_blocks
), TP_BSIZE
);
sizeest
= howmany(dp
->di_size
, TP_BSIZE
);
if (dp
->di_size
> sblock
->fs_bsize
* NDADDR
) {
/* calculate the number of indirect blocks on the dump tape */
howmany(sizeest
- NDADDR
* sblock
->fs_bsize
/ TP_BSIZE
,
/* Auxiliary macro to pick up files changed since previous dump. */
#define CHANGEDSINCE(dp, t) \
((dp)->di_mtime.ts_sec >= (t) || (dp)->di_ctime.ts_sec >= (t))
#define CHANGEDSINCE(dp, t) \
((dp)->di_mtime >= (t) || (dp)->di_ctime >= (t))
/* The WANTTODUMP macro decides whether a file should be dumped. */
(CHANGEDSINCE(dp, spcl.c_ddate) && \
(nonodump || ((dp)->di_flags & UF_NODUMP) != UF_NODUMP))
#define WANTTODUMP(dp) CHANGEDSINCE(dp, spcl.c_ddate)
* Walk the inode list for a filesystem to find all allocated inodes
* that have been modified since the previous dump time. Also, find all
* the directories in the filesystem.
mapfiles(maxino
, tapesize
)
register struct dinode
*dp
;
for (ino
= ROOTINO
; ino
< maxino
; ino
++) {
if ((mode
= (dp
->di_mode
& IFMT
)) == 0)
if (mode
!= IFREG
&& mode
!= IFDIR
&& mode
!= IFLNK
)
*tapesize
+= blockest(dp
);
* Restore gets very upset if the root is not dumped,
* so ensure that it always is dumped.
SETINO(ROOTINO
, dumpinomap
);
* Scan each directory on the filesystem to see if it has any modified
* files in it. If it does, and has not already been added to the dump
* list (because it was itself modified), then add it. If a directory
* has not been modified itself, contains no modified files and has no
* subdirectories, then it can be deleted from the dump list and from
* the list of directories. By deleting it from the list of directories,
* its parent may now qualify for the same treatment on this or a later
* pass using this algorithm.
mapdirs(maxino
, tapesize
)
register struct dinode
*dp
;
isdir
= 0; /* XXX just to get gcc to shut up */
for (map
= dumpdirmap
, ino
= 1; ino
< maxino
; ino
++) {
if (((ino
- 1) % NBBY
) == 0) /* map is offset by 1 */
if ((isdir
& 1) == 0 || TSTINO(ino
, dumpinomap
))
for (ret
= 0, i
= 0; filesize
> 0 && i
< NDADDR
; i
++) {
ret
|= searchdir(ino
, dp
->di_db
[i
],
(long)dblksize(sblock
, dp
, i
),
filesize
-= sblock
->fs_bsize
;
for (i
= 0; filesize
> 0 && i
< NIADDR
; i
++) {
ret
|= dirindir(ino
, dp
->di_ib
[i
], i
, &filesize
);
if (ret
& HASDUMPEDFILE
) {
*tapesize
+= blockest(dp
);
if ((ret
& HASSUBDIRS
) == 0) {
if (!TSTINO(ino
, dumpinomap
)) {
* Read indirect blocks, and pass the data blocks to be searched
* as directories. Quit as soon as any entry is found that will
* require the directory to be dumped.
dirindir(ino
, blkno
, ind_level
, filesize
)
daddr_t idblk
[MAXNINDIR
];
bread(fsbtodb(sblock
, blkno
), (char *)idblk
, (int)sblock
->fs_bsize
);
for (i
= 0; *filesize
> 0 && i
< NINDIR(sblock
); i
++) {
ret
|= searchdir(ino
, blkno
, sblock
->fs_bsize
,
*filesize
-= sblock
->fs_bsize
;
for (i
= 0; *filesize
> 0 && i
< NINDIR(sblock
); i
++) {
ret
|= dirindir(ino
, blkno
, ind_level
, filesize
);
* Scan a disk block containing directory information looking to see if
* any of the entries are on the dump list and to see if the directory
* contains any subdirectories.
searchdir(ino
, blkno
, size
, filesize
)
register struct direct
*dp
;
register long loc
, ret
= 0;
bread(fsbtodb(sblock
, blkno
), dblk
, (int)size
);
for (loc
= 0; loc
< size
; ) {
dp
= (struct direct
*)(dblk
+ loc
);
msg("corrupted directory, inumber %d\n", ino
);
if (dp
->d_name
[0] == '.') {
if (dp
->d_name
[1] == '\0')
if (dp
->d_name
[1] == '.' && dp
->d_name
[2] == '\0')
if (TSTINO(dp
->d_ino
, dumpinomap
)) {
if (TSTINO(dp
->d_ino
, dumpdirmap
)) {
* Dump the contents of an inode to tape.
register struct dinode
*dp
;
dumpmap(dumpinomap
, TS_BITS
, ino
);
switch (dp
->di_mode
& S_IFMT
) {
* Check for short symbolic link.
dp
->di_size
< sblock
->fs_maxsymlinklen
) {
bcopy((caddr_t
)dp
->di_shortlink
, buf
,
msg("Warning: undefined file type 0%o\n", dp
->di_mode
& IFMT
);
if (dp
->di_size
> NDADDR
* sblock
->fs_bsize
)
cnt
= NDADDR
* sblock
->fs_frag
;
cnt
= howmany(dp
->di_size
, sblock
->fs_fsize
);
blksout(&dp
->di_db
[0], cnt
, ino
);
if ((size
= dp
->di_size
- NDADDR
* sblock
->fs_bsize
) <= 0)
for (ind_level
= 0; ind_level
< NIADDR
; ind_level
++) {
dmpindir(ino
, dp
->di_ib
[ind_level
], ind_level
, &size
);
* Read indirect blocks, and pass the data blocks to be dumped.
dmpindir(ino
, blk
, ind_level
, size
)
daddr_t idblk
[MAXNINDIR
];
bread(fsbtodb(sblock
, blk
), (char *)idblk
, (int) sblock
->fs_bsize
);
bzero((char *)idblk
, (int)sblock
->fs_bsize
);
if (*size
< NINDIR(sblock
) * sblock
->fs_bsize
)
cnt
= howmany(*size
, sblock
->fs_fsize
);
cnt
= NINDIR(sblock
) * sblock
->fs_frag
;
*size
-= NINDIR(sblock
) * sblock
->fs_bsize
;
blksout(&idblk
[0], cnt
, ino
);
for (i
= 0; i
< NINDIR(sblock
); i
++) {
dmpindir(ino
, idblk
[i
], ind_level
, size
);
* Collect up the data into tape record sized buffers and output them.
blksout(blkp
, frags
, ino
)
int i
, j
, count
, blks
, tbperdb
;
blks
= howmany(frags
* sblock
->fs_fsize
, TP_BSIZE
);
tbperdb
= sblock
->fs_bsize
>> tp_bshift
;
for (i
= 0; i
< blks
; i
+= TP_NINDIR
) {
if (i
+ TP_NINDIR
> blks
)
for (j
= i
; j
< count
; j
++)
if (blkp
[j
/ tbperdb
] != 0)
spcl
.c_count
= count
- i
;
for (j
= i
; j
< count
; j
+= tbperdb
, bp
++)
if (j
+ tbperdb
<= count
)
dumpblock(*bp
, (int)sblock
->fs_bsize
);
dumpblock(*bp
, (count
- j
) * TP_BSIZE
);
* Dump a map to the tape.
spcl
.c_count
= howmany(mapsize
* sizeof(char), TP_BSIZE
);
for (i
= 0, cp
= map
; i
< spcl
.c_count
; i
++, cp
+= TP_BSIZE
)
* Write a header record to the dump tape.
register long sum
, cnt
, *lp
;
spcl
.c_magic
= NFS_MAGIC
;
cnt
= sizeof(union u_spcl
) / (4 * sizeof(long));
spcl
.c_checksum
= CHECKSUM
- sum
;
writerec((char *)&spcl
, 1);
static daddr_t minino
, maxino
;
static struct dinode inoblock
[MAXINOPB
];
if (inum
>= minino
&& inum
< maxino
)
return (&inoblock
[inum
- minino
]);
bread(fsbtodb(sblock
, ino_to_fsba(sblock
, inum
)), (char *)inoblock
,
minino
= inum
- (inum
% INOPB(sblock
));
maxino
= minino
+ INOPB(sblock
);
return (&inoblock
[inum
- minino
]);
* Read a chunk of data from the disk.
* Try to recover from hard errors by reading in sector sized pieces.
* Error recovery is attempted at most BREADEMAX times before seeking
* consent from the operator to continue.
if ((int)lseek(diskfd
, ((off_t
)blkno
<< dev_bshift
), 0) < 0)
msg("bread: lseek fails\n");
if ((cnt
= read(diskfd
, buf
, size
)) == size
)
if (blkno
+ (size
/ dev_bsize
) > fsbtodb(sblock
, sblock
->fs_size
)) {
* Trying to read the final fragment.
* NB - dump only works in TP_BSIZE blocks, hence
* rounds `dev_bsize' fragments up to TP_BSIZE pieces.
* It should be smarter about not actually trying to
* read more than it can get, but for the time being
* we punt and scale back the read only when it gets
* us into trouble. (mkm 9/25/83)
msg("read error from %s: %s: [block %d]: count=%d\n",
disk
, strerror(errno
), blkno
, size
);
msg("short read error from %s: [block %d]: count=%d, got=%d\n",
if (++breaderrors
> BREADEMAX
) {
msg("More than %d block read errors from %d\n",
broadcast("DUMP IS AILING!\n");
msg("This is an unrecoverable error.\n");
if (!query("Do you want to attempt to continue?")){
* Zero buffer, then try to read each sector of buffer separately.
for (i
= 0; i
< size
; i
+= dev_bsize
, buf
+= dev_bsize
, blkno
++) {
if ((int)lseek(diskfd
, ((off_t
)blkno
<< dev_bshift
), 0) < 0)
msg("bread: lseek2 fails!\n");
if ((cnt
= read(diskfd
, buf
, (int)dev_bsize
)) == dev_bsize
)
msg("read error from %s: %s: [sector %d]: count=%d\n",
disk
, strerror(errno
), blkno
, dev_bsize
);
msg("short read error from %s: [sector %d]: count=%d, got=%d\n",
disk
, blkno
, dev_bsize
, cnt
);