]> git.wh0rd.org Git - dump.git/blob - dump/traverse.c
Made dump able to dump files bigger than 2 GB.
[dump.git] / dump / traverse.c
1 /*
2  *      Ported to Linux's Second Extended File System as part of the
3  *      dump and restore backup suit
4  *      Remy Card <card@Linux.EU.Org>, 1994-1997
5  *      Stelian Pop <pop@cybercable.fr>, 1999-2000
6  */
7
8 /*-
9  * Copyright (c) 1980, 1988, 1991, 1993
10  *      The Regents of the University of California.  All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *      This product includes software developed by the University of
23  *      California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40
41 #ifndef lint
42 static const char rcsid[] =
43         "$Id: traverse.c,v 1.20 2000/09/26 13:17:09 stelian Exp $";
44 #endif /* not lint */
45
46 #include <sys/param.h>
47 #include <sys/stat.h>
48 #ifdef  __linux__
49 #include <linux/ext2_fs.h>
50 #include <bsdcompat.h>
51 #include <compaterr.h>
52 #include <stdlib.h>
53 #define swab32(x) ext2fs_swab32(x)
54 #else   /* __linux__ */
55 #define swab32(x) x
56 #ifdef sunos
57 #include <sys/vnode.h>
58
59 #include <ufs/fs.h>
60 #include <ufs/fsdir.h>
61 #include <ufs/inode.h>
62 #else
63 #include <ufs/ufs/dir.h>
64 #include <ufs/ufs/dinode.h>
65 #include <ufs/ffs/fs.h>
66 #endif
67 #endif  /* __linux__ */
68
69 #include <protocols/dumprestore.h>
70
71 #include <ctype.h>
72 #include <stdio.h>
73 #ifdef __STDC__
74 #include <string.h>
75 #include <unistd.h>
76 #endif
77
78 #ifdef  __linux__
79 #include <ext2fs/ext2fs.h>
80 #endif
81
82 #include "dump.h"
83
84 #define HASDUMPEDFILE   0x1
85 #define HASSUBDIRS      0x2
86
87 #ifdef __linux__
88 typedef u_quad_t fsizeT;
89 #else
90 #ifdef  FS_44INODEFMT
91 typedef quad_t fsizeT;
92 #else
93 typedef long fsizeT;
94 #endif
95 #endif
96
97 #ifdef  __linux__
98 static  int searchdir __P((struct ext2_dir_entry *dp, int offset,
99                            int blocksize, char *buf, void *private));
100 #else
101 static  int dirindir __P((ino_t ino, daddr_t blkno, int level, long *size));
102 static  void dmpindir __P((ino_t ino, daddr_t blk, int level, fsizeT *size));
103 static  int searchdir __P((ino_t ino, daddr_t blkno, long size, long filesize));
104 #endif
105 static  void mapfileino __P((ino_t ino, long *tapesize, int *dirskipped));
106 static  int exclude_ino __P((ino_t ino));
107
108 /* #define EXT3_FEATURE_INCOMPAT_RECOVER */
109
110 int dump_fs_open(const char *disk, ext2_filsys *fs)
111 {
112         int retval;
113         struct ext2fs_sb *s;
114
115 #ifdef EXT3_FEATURE_INCOMPAT_RECOVER
116         retval = ext2fs_open(disk, EXT2_FLAG_FORCE, 0, 0, unix_io_manager, fs);
117 #else
118         retval = ext2fs_open(disk, 0, 0, 0, unix_io_manager, fs);
119 #endif
120 #if defined(EXT2_LIB_FEATURE_COMPAT_SUPP) && defined(EXT2_LIB_FEATURE_INCOMPAT_SUPP) && defined(EXT2_LIB_FEATURE_RO_COMPAT_SUPP) && defined(EXT2_ET_UNSUPP_FEATURE) && defined(EXT2_ET_RO_UNSUPP_FEATURE)
121         if (!retval) {
122                 s = (struct ext2fs_sb *) (*fs)->super;
123                 if ((s->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
124 #ifdef EXT3_FEATURE_INCOMPAT_RECOVER
125                     (s->s_feature_incompat & ~(EXT3_FEATURE_INCOMPAT_RECOVER | EXT2_LIB_FEATURE_INCOMPAT_SUPP))) {
126 #else
127                     (s->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
128 #endif
129                         retval = EXT2_ET_UNSUPP_FEATURE;
130                 }
131                 else if (s->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
132                         retval = EXT2_ET_RO_UNSUPP_FEATURE;
133                 }
134         }
135 #endif /* defined && defined && defined... */
136         return retval;
137 }
138
139 /*
140  * This is an estimation of the number of TP_BSIZE blocks in the file.
141  * It estimates the number of blocks in files with holes by assuming
142  * that all of the blocks accounted for by di_blocks are data blocks
143  * (when some of the blocks are usually used for indirect pointers);
144  * hence the estimate may be high.
145  */
146 long
147 blockest(struct dinode *dp)
148 {
149         long blkest, sizeest;
150
151         /*
152          * dp->di_size is the size of the file in bytes.
153          * dp->di_blocks stores the number of sectors actually in the file.
154          * If there are more sectors than the size would indicate, this just
155          *      means that there are indirect blocks in the file or unused
156          *      sectors in the last file block; we can safely ignore these
157          *      (blkest = sizeest below).
158          * If the file is bigger than the number of sectors would indicate,
159          *      then the file has holes in it.  In this case we must use the
160          *      block count to estimate the number of data blocks used, but
161          *      we use the actual size for estimating the number of indirect
162          *      dump blocks (sizeest vs. blkest in the indirect block
163          *      calculation).
164          */
165         blkest = howmany(dbtob(dp->di_blocks), TP_BSIZE);
166         sizeest = howmany(dp->di_size, TP_BSIZE);
167         if (blkest > sizeest)
168                 blkest = sizeest;
169 #ifdef  __linux__
170         if (dp->di_size > fs->blocksize * NDADDR) {
171                 /* calculate the number of indirect blocks on the dump tape */
172                 blkest +=
173                         howmany(sizeest - NDADDR * fs->blocksize / TP_BSIZE,
174                         NINDIR(sblock) * EXT2_FRAGS_PER_BLOCK(fs->super));
175         }
176 #else
177         if (dp->di_size > sblock->fs_bsize * NDADDR) {
178                 /* calculate the number of indirect blocks on the dump tape */
179                 blkest +=
180                         howmany(sizeest - NDADDR * sblock->fs_bsize / TP_BSIZE,
181                         TP_NINDIR);
182         }
183 #endif
184         return (blkest + 1);
185 }
186
187 extern ino_t iexclude_list[IEXCLUDE_MAXNUM];    /* the inode exclude list */
188 extern int iexclude_num;        /* number of elements in the list */
189
190 /*
191  * This tests whether an inode is in the exclude list 
192  */
193 int
194 exclude_ino(ino_t ino)
195 {
196         /* 04-Feb-00 ILC */
197         if (iexclude_num) {     /* if there are inodes in the exclude list */
198                 int idx;        /* then check this inode against it */
199                 for (idx = 0; idx < iexclude_num; idx++)
200                         if (ino == iexclude_list[idx])
201                                 return 1;
202         }
203         return 0;
204 }
205
206 /* Auxiliary macro to pick up files changed since previous dump. */
207 #define CHANGEDSINCE(dp, t) \
208         ((dp)->di_mtime >= (t) || (dp)->di_ctime >= (t))
209
210 /* The NODUMP_FLAG macro tests if a file has the nodump flag. */
211 #ifdef UF_NODUMP
212 #define NODUMP_FLAG(dp) (!nonodump && (((dp)->di_flags & UF_NODUMP) == UF_NODUMP))
213 #else
214 #define NODUMP_FLAG(dp) 0
215 #endif
216
217 /* The WANTTODUMP macro decides whether a file should be dumped. */
218 #define WANTTODUMP(dp, ino) \
219         (CHANGEDSINCE(dp, spcl.c_ddate) && \
220          (!NODUMP_FLAG(dp)) && \
221          (!exclude_ino(ino)))
222
223 /*
224  * Determine if given inode should be dumped
225  */
226 void
227 mapfileino(ino_t ino, long *tapesize, int *dirskipped)
228 {
229         register int mode;
230         register struct dinode *dp;
231
232         /*
233          * Skip inode if we've already marked it for dumping
234          */
235         if (TSTINO(ino, usedinomap))
236                 return;
237         dp = getino(ino);
238         if ((mode = (dp->di_mode & IFMT)) == 0)
239                 return;
240 #ifdef  __linux__
241         if (dp->di_nlink == 0 || dp->di_dtime != 0)
242                 return;
243 #endif
244         /*
245          * Put all dirs in dumpdirmap, inodes that are to be dumped in the
246          * used map. All inode but dirs who have the nodump attribute go
247          * to the usedinomap.
248          */
249         SETINO(ino, usedinomap);
250
251         if (mode == IFDIR)
252                 SETINO(ino, dumpdirmap);
253         if (WANTTODUMP(dp, ino)) {
254                 SETINO(ino, dumpinomap);
255                 if (mode != IFREG && mode != IFDIR && mode != IFLNK)
256                         *tapesize += 1;
257                 else
258                         *tapesize += blockest(dp);
259                 return;
260         }
261         if (mode == IFDIR) {
262                 if ( NODUMP_FLAG(dp) || exclude_ino(ino) )
263                         CLRINO(ino, usedinomap);
264                 *dirskipped = 1;
265         }
266 }
267
268 /*
269  * Dump pass 1.
270  *
271  * Walk the inode list for a filesystem to find all allocated inodes
272  * that have been modified since the previous dump time. Also, find all
273  * the directories in the filesystem.
274  */
275 int
276 mapfiles(ino_t maxino, long *tapesize)
277 {
278         register ino_t ino;
279         int anydirskipped = 0;
280
281         for (ino = ROOTINO; ino < maxino; ino++)
282                 mapfileino(ino, tapesize, &anydirskipped);
283
284         /*
285          * Restore gets very upset if the root is not dumped,
286          * so ensure that it always is dumped.
287          */
288         SETINO(ROOTINO, dumpinomap);
289         return (anydirskipped);
290 }
291
292 #ifdef  __linux__
293 struct mapfile_context {
294         long *tapesize;
295         int *anydirskipped;
296 };
297
298 static int
299 mapfilesindir(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *private)
300 {
301         register struct dinode *dp;
302         register int mode;
303         errcode_t retval;
304         struct mapfile_context *mfc;
305         ino_t ino;
306
307         ino = dirent->inode;
308         mfc = (struct mapfile_context *)private;
309
310         mapfileino(dirent->inode, mfc->tapesize, mfc->anydirskipped);
311
312         dp = getino(dirent->inode);
313         mode = dp->di_mode & IFMT;
314         if (mode == IFDIR && dp->di_nlink != 0 && dp->di_dtime == 0) {
315                 if ((dirent->name[0] != '.' || ( dirent->name_len & 0xFF ) != 1) &&
316                     (dirent->name[0] != '.' || dirent->name[1] != '.' ||
317                      ( dirent->name_len & 0xFF ) != 2)) {
318                 retval = ext2fs_dir_iterate(fs, ino, 0, NULL,
319                                             mapfilesindir, private);
320                 if (retval)
321                         return retval;
322                 }
323         }
324         return 0;
325 }
326
327 /*
328  * Dump pass 1.
329  *
330  * Walk the inode list for a filesystem to find all allocated inodes
331  * that have been modified since the previous dump time. Also, find all
332  * the directories in the filesystem.
333  */
334 int
335 mapfilesfromdir(ino_t maxino, long *tapesize, char *directory)
336 {
337         errcode_t retval;
338         struct mapfile_context mfc;
339         ino_t dir_ino;
340         char dir_name [MAXPATHLEN];
341         int i, anydirskipped = 0;
342
343         /*
344          * Mark every directory in the path as being dumped
345          */
346         for (i = 0; i < strlen (directory); i++) {
347                 if (directory[i] == '/') {
348                         strncpy (dir_name, directory, i);
349                         dir_name[i] = '\0';
350                         retval = ext2fs_namei(fs, ROOTINO, ROOTINO, dir_name,
351                                               &dir_ino);
352                         if (retval) {
353                                 com_err(disk, retval, "while translating %s",
354                                         dir_name);
355                                 exit(X_ABORT);
356                         }
357                         mapfileino(dir_ino, tapesize, &anydirskipped);
358                 }
359         }
360         /*
361          * Mark the final directory
362          */
363         retval = ext2fs_namei(fs, ROOTINO, ROOTINO, directory, &dir_ino);
364         if (retval) {
365                 com_err(disk, retval, "while translating %s", directory);
366                 exit(X_ABORT);
367         }
368         mapfileino(dir_ino, tapesize, &anydirskipped);
369
370         mfc.tapesize = tapesize;
371         mfc.anydirskipped = &anydirskipped;
372         retval = ext2fs_dir_iterate(fs, dir_ino, 0, NULL, mapfilesindir,
373                                     (void *)&mfc);
374
375         if (retval) {
376                 com_err(disk, retval, "while mapping files in %s", directory);
377                 exit(X_ABORT);
378         }
379         /*
380          * Ensure that the root inode actually appears in the file list
381          * for a subdir
382          */
383         mapfileino(ROOTINO, tapesize, &anydirskipped);
384         /*
385          * Restore gets very upset if the root is not dumped,
386          * so ensure that it always is dumped.
387          */
388         SETINO(ROOTINO, dumpinomap);
389         return anydirskipped;
390 }
391 #endif
392
393 #ifdef __linux__
394 struct mapdirs_context {
395         int *ret;
396         int nodump;
397         long *tapesize;
398 };
399 #endif
400
401 /*
402  * Dump pass 2.
403  *
404  * Scan each directory on the filesystem to see if it has any modified
405  * files in it. If it does, and has not already been added to the dump
406  * list (because it was itself modified), then add it. If a directory
407  * has not been modified itself, contains no modified files and has no
408  * subdirectories, then it can be deleted from the dump list and from
409  * the list of directories. By deleting it from the list of directories,
410  * its parent may now qualify for the same treatment on this or a later
411  * pass using this algorithm.
412  */
413 int
414 mapdirs(ino_t maxino, long *tapesize)
415 {
416         register struct dinode *dp;
417         register int isdir;
418         register char *map;
419         register ino_t ino;
420 #ifndef __linux__
421         register int i;
422         long filesize;
423 #else
424         struct mapdirs_context mdc;
425 #endif
426         int ret, change = 0, nodump;
427
428         isdir = 0;              /* XXX just to get gcc to shut up */
429         for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
430                 if (((ino - 1) % NBBY) == 0)    /* map is offset by 1 */
431                         isdir = *map++;
432                 else
433                         isdir >>= 1;
434                 /*
435                  * If dir has been removed from the used map, it's either
436                  * because it had the nodump flag, or it herited it from
437                  * its parent. A directory can't be in dumpinomap if not
438                  * in usedinomap, but we have to go through it anyway 
439                  * to propagate the nodump attribute.
440                  */
441                 nodump = (TSTINO(ino, usedinomap) == 0);
442                 if ((isdir & 1) == 0 ||
443                     (TSTINO(ino, dumpinomap) && nodump == 0))
444                         continue;
445                 dp = getino(ino);
446 #ifdef  __linux__
447                 ret = 0;
448                 mdc.ret = &ret;
449                 mdc.nodump = nodump;
450                 mdc.tapesize = tapesize;
451                 ext2fs_dir_iterate(fs, ino, 0, NULL, searchdir, (void *) &mdc);
452 #else   /* __linux__ */
453                 filesize = dp->di_size;
454                 for (ret = 0, i = 0; filesize > 0 && i < NDADDR; i++) {
455                         if (dp->di_db[i] != 0)
456                                 ret |= searchdir(ino, dp->di_db[i],
457                                         (long)dblksize(sblock, dp, i),
458                                         filesize);
459                         if (ret & HASDUMPEDFILE)
460                                 filesize = 0;
461                         else
462                                 filesize -= sblock->fs_bsize;
463                 }
464                 for (i = 0; filesize > 0 && i < NIADDR; i++) {
465                         if (dp->di_ib[i] == 0)
466                                 continue;
467                         ret |= dirindir(ino, dp->di_ib[i], i, &filesize);
468                 }
469 #endif  /* __linux__ */
470                 if (ret & HASDUMPEDFILE) {
471                         SETINO(ino, dumpinomap);
472                         *tapesize += blockest(dp);
473                         change = 1;
474                         continue;
475                 }
476                 if (nodump) {
477                         if (ret & HASSUBDIRS)
478                                 change = 1; /* subdirs have inherited nodump */
479                         CLRINO(ino, dumpdirmap);
480                 } else if ((ret & HASSUBDIRS) == 0) {
481                         if (!TSTINO(ino, dumpinomap)) {
482                                 CLRINO(ino, dumpdirmap);
483                                 change = 1;
484                         }
485                 }
486         }
487         return (change);
488 }
489
490 #ifndef __linux__
491 /*
492  * Read indirect blocks, and pass the data blocks to be searched
493  * as directories. Quit as soon as any entry is found that will
494  * require the directory to be dumped.
495  */
496 static int
497 dirindir(ino_t ino, daddr_t blkno, int ind_level, long *filesize)
498 {
499         int ret = 0;
500         register int i;
501         daddr_t idblk[MAXNINDIR];
502
503         bread(fsbtodb(sblock, blkno), (char *)idblk, (int)sblock->fs_bsize);
504         if (ind_level <= 0) {
505                 for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
506                         blkno = idblk[i];
507                         if (blkno != 0)
508                                 ret |= searchdir(ino, blkno, sblock->fs_bsize,
509                                         *filesize);
510                         if (ret & HASDUMPEDFILE)
511                                 *filesize = 0;
512                         else
513                                 *filesize -= sblock->fs_bsize;
514                 }
515                 return (ret);
516         }
517         ind_level--;
518         for (i = 0; *filesize > 0 && i < NINDIR(sblock); i++) {
519                 blkno = idblk[i];
520                 if (blkno != 0)
521                         ret |= dirindir(ino, blkno, ind_level, filesize);
522         }
523         return (ret);
524 }
525 #endif  /* !__linux__ */
526
527 /*
528  * Scan a disk block containing directory information looking to see if
529  * any of the entries are on the dump list and to see if the directory
530  * contains any subdirectories.
531  */
532 #ifdef  __linux__
533 static  int
534 searchdir(struct ext2_dir_entry *dp, int offset, int blocksize, char *buf, void *private)
535 {
536         struct mapdirs_context *mdc;
537         int *ret;
538         long *tapesize;
539         struct dinode *ip;
540
541         mdc = (struct mapdirs_context *)private;
542         ret = mdc->ret;
543         tapesize = mdc->tapesize;
544
545         if (dp->inode == 0)
546                 return 0;
547         if (dp->name[0] == '.') {
548                 if (( dp->name_len & 0xFF ) == 1)
549                         return 0;
550                 if (dp->name[1] == '.' && ( dp->name_len & 0xFF ) == 2)
551                         return 0;
552         }
553         if (mdc->nodump) {
554                 ip = getino(dp->inode);
555                 if (TSTINO(dp->inode, dumpinomap)) {
556                         CLRINO(dp->inode, dumpinomap);
557                         CLRINO(dp->inode, usedinomap);
558                         *tapesize -= blockest(ip);
559                 }
560                 /* Add dir back to the dir map, to propagate nodump */
561                 if ((ip->di_mode & IFMT) == IFDIR) {
562                         SETINO(dp->inode, dumpdirmap);
563                         *ret |= HASSUBDIRS;
564                 }
565         } else {
566                 if (TSTINO(dp->inode, dumpinomap)) {
567                         *ret |= HASDUMPEDFILE;
568                         if (*ret & HASSUBDIRS)
569                                 return DIRENT_ABORT;
570                 }
571                 if (TSTINO(dp->inode, dumpdirmap)) {
572                         *ret |= HASSUBDIRS;
573                         if (*ret & HASDUMPEDFILE)
574                                 return DIRENT_ABORT;
575                 }
576         }
577         return 0;
578 }
579
580 #else   /* __linux__ */
581
582 static int
583 searchdir(ino_t ino, daddr_t blkno, long size, long filesize)
584 {
585         register struct direct *dp;
586         register long loc, ret = 0;
587         char dblk[MAXBSIZE];
588
589         bread(fsbtodb(sblock, blkno), dblk, (int)size);
590         if (filesize < size)
591                 size = filesize;
592         for (loc = 0; loc < size; ) {
593                 dp = (struct direct *)(dblk + loc);
594                 if (dp->d_reclen == 0) {
595                         msg("corrupted directory, inumber %d\n", ino);
596                         break;
597                 }
598                 loc += dp->d_reclen;
599                 if (dp->d_ino == 0)
600                         continue;
601                 if (dp->d_name[0] == '.') {
602                         if (dp->d_name[1] == '\0')
603                                 continue;
604                         if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
605                                 continue;
606                 }
607                 if (TSTINO(dp->d_ino, dumpinomap)) {
608                         ret |= HASDUMPEDFILE;
609                         if (ret & HASSUBDIRS)
610                                 break;
611                 }
612                 if (TSTINO(dp->d_ino, dumpdirmap)) {
613                         ret |= HASSUBDIRS;
614                         if (ret & HASDUMPEDFILE)
615                                 break;
616                 }
617         }
618         return (ret);
619 }
620 #endif  /* __linux__ */
621
622 #ifdef  __linux__
623
624 struct block_context {
625         ino_t   ino;
626         int     *buf;
627         int     cnt;
628         int     max;
629         int     next_block;
630 };
631
632 /*
633  * Dump a block to the tape
634  */
635 static int
636 dumponeblock(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *private)
637 {
638         struct block_context *p;
639         int i;
640
641         if (blockcnt < NDADDR)
642                 return 0;
643         p = (struct block_context *)private;
644         for (i = p->next_block; i < blockcnt; i++) {
645                 p->buf[p->cnt++] = 0;
646                 if (p->cnt == p->max) {
647                         blksout (p->buf, p->cnt, p->ino);
648                         p->cnt = 0;
649                 }
650         }
651         p->buf[p->cnt++] = *blocknr;
652         if (p->cnt == p->max) {
653                 blksout (p->buf, p->cnt, p->ino);
654                 p->cnt = 0;
655         }
656         p->next_block = blockcnt + 1;
657         return 0;
658 }
659 #endif
660
661 /*
662  * Dump passes 3 and 4.
663  *
664  * Dump the contents of an inode to tape.
665  */
666 void
667 dumpino(struct dinode *dp, ino_t ino)
668 {
669         int cnt;
670         fsizeT size;
671         char buf[TP_BSIZE];
672         struct old_bsd_inode obi;
673 #ifdef  __linux__
674         struct block_context bc;
675 #else
676         int ind_level;
677 #endif
678
679         if (newtape) {
680                 newtape = 0;
681                 dumpmap(dumpinomap, TS_BITS, ino);
682         }
683         CLRINO(ino, dumpinomap);
684 #ifdef  __linux__
685         memset(&obi, 0, sizeof(obi));
686         obi.di_mode = dp->di_mode;
687         obi.di_uid = dp->di_uid;
688         obi.di_gid = dp->di_gid;
689         obi.di_qsize.v = (u_quad_t)dp->di_size;
690         obi.di_atime = dp->di_atime;
691         obi.di_mtime = dp->di_mtime;
692         obi.di_ctime = dp->di_ctime;
693         obi.di_nlink = dp->di_nlink;
694         obi.di_blocks = dp->di_blocks;
695         obi.di_flags = dp->di_flags;
696         obi.di_gen = dp->di_gen;
697         memmove(&obi.di_db, &dp->di_db, (NDADDR + NIADDR) * sizeof(daddr_t));
698         if (dp->di_file_acl || dp->di_dir_acl)
699                 warn("ACLs in inode #%ld won't be dumped", (long)ino);
700         memmove(&spcl.c_dinode, &obi, sizeof(obi));
701 #else   /* __linux__ */
702         spcl.c_dinode = *dp;
703 #endif  /* __linux__ */
704         spcl.c_type = TS_INODE;
705         spcl.c_count = 0;
706         switch (dp->di_mode & S_IFMT) {
707
708         case 0:
709                 /*
710                  * Freed inode.
711                  */
712                 return;
713
714 #ifdef  __linux__
715         case S_IFDIR:
716                 msg("Warning: dumpino called on a directory (ino %d)\n", ino);
717                 return;
718 #endif
719
720         case S_IFLNK:
721                 /*
722                  * Check for short symbolic link.
723                  */
724 #ifdef  __linux__
725                 if (dp->di_size > 0 &&
726                     dp->di_size < EXT2_N_BLOCKS * sizeof (daddr_t)) {
727                         spcl.c_addr[0] = 1;
728                         spcl.c_count = 1;
729                         writeheader(ino);
730                         memmove(buf, dp->di_db, (u_long)dp->di_size);
731                         buf[dp->di_size] = '\0';
732                         writerec(buf, 0);
733                         return;
734                 }
735 #endif  /* __linux__ */
736 #ifdef FS_44INODEFMT
737                 if (dp->di_size > 0 &&
738                     dp->di_size < sblock->fs_maxsymlinklen) {
739                         spcl.c_addr[0] = 1;
740                         spcl.c_count = 1;
741                         writeheader(ino);
742                         memmove(buf, dp->di_shortlink, (u_long)dp->di_size);
743                         buf[dp->di_size] = '\0';
744                         writerec(buf, 0);
745                         return;
746                 }
747 #endif
748                 /* fall through */
749
750 #ifndef __linux__
751         case S_IFDIR:
752 #endif
753         case S_IFREG:
754                 if (dp->di_size > 0)
755                         break;
756                 /* fall through */
757
758         case S_IFIFO:
759         case S_IFSOCK:
760         case S_IFCHR:
761         case S_IFBLK:
762                 writeheader(ino);
763                 return;
764
765         default:
766                 msg("Warning: undefined file type 0%o\n", dp->di_mode & IFMT);
767                 return;
768         }
769         if (dp->di_size > NDADDR * sblock->fs_bsize)
770 #ifdef  __linux__
771                 cnt = NDADDR * EXT2_FRAGS_PER_BLOCK(fs->super);
772 #else
773                 cnt = NDADDR * sblock->fs_frag;
774 #endif
775         else
776                 cnt = howmany(dp->di_size, sblock->fs_fsize);
777         blksout(&dp->di_db[0], cnt, ino);
778         if ((size = dp->di_size - NDADDR * sblock->fs_bsize) <= 0)
779                 return;
780 #ifdef  __linux__
781         bc.max = NINDIR(sblock) * EXT2_FRAGS_PER_BLOCK(fs->super);
782         bc.buf = (int *)malloc (bc.max * sizeof (int));
783         bc.cnt = 0;
784         bc.ino = ino;
785         bc.next_block = NDADDR;
786
787         ext2fs_block_iterate (fs, ino, 0, NULL, dumponeblock, (void *)&bc);
788         if (bc.cnt > 0) {
789                 blksout (bc.buf, bc.cnt, bc.ino);
790         }
791         free(bc.buf);
792 #else
793         for (ind_level = 0; ind_level < NIADDR; ind_level++) {
794                 dmpindir(ino, dp->di_ib[ind_level], ind_level, &size);
795                 if (size <= 0)
796                         return;
797         }
798 #endif
799 }
800
801 #ifdef  __linux__
802
803 struct convert_dir_context {
804         char *buf;
805         int prev_offset;
806         int offset;
807         int bs;
808 };
809
810 /*
811  * This function converts an ext2fs directory entry to the BSD format.
812  *
813  * Basically, it adds a null-character at the end of the name, recomputes the
814  * size of the entry, and creates it in a temporary buffer
815  */
816 static int
817 convert_dir(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *private)
818 {
819         struct convert_dir_context *p;
820         struct olddirect *dp;
821         int reclen;
822
823         p = (struct convert_dir_context *)private;
824
825         reclen = EXT2_DIR_REC_LEN((dirent->name_len & 0xFF) + 1);
826         if (((p->offset + reclen - 1) / p->bs) != (p->offset / p->bs)) {
827                 dp = (struct olddirect *)(p->buf + p->prev_offset);
828                 dp->d_reclen += p->bs - (p->offset % p->bs);
829                 p->offset += p->bs - (p->offset % p->bs);
830         }
831
832         dp = (struct olddirect *)(p->buf + p->offset);
833         dp->d_ino = dirent->inode;
834         dp->d_reclen = reclen;
835         dp->d_namlen = dirent->name_len & 0xFF;
836         strncpy(dp->d_name, dirent->name, dp->d_namlen);
837         dp->d_name[dp->d_namlen] = '\0';
838         p->prev_offset = p->offset;
839         p->offset += reclen;
840
841         return 0;
842 }
843
844 /*
845  * Dump pass 3
846  *
847  * Dumps a directory to tape after converting it to the BSD format
848  */
849 void
850 dumpdirino(struct dinode *dp, ino_t ino)
851 {
852         fsizeT size;
853         char buf[TP_BSIZE];
854         struct old_bsd_inode obi;
855         struct convert_dir_context cdc;
856         errcode_t retval;
857         struct ext2_dir_entry *de;
858         fsizeT dir_size;
859
860         if (newtape) {
861                 newtape = 0;
862                 dumpmap(dumpinomap, TS_BITS, ino);
863         }
864         CLRINO(ino, dumpinomap);
865
866         /*
867          * Convert the directory to the BSD format
868          */
869         /* Allocate a buffer for the conversion (twice the size of the
870            ext2fs directory to avoid problems ;-) */
871         cdc.buf = (char *)malloc(dp->di_size * 2 * sizeof(char));
872         if (cdc.buf == NULL)
873                 err(1, "Cannot allocate buffer to convert directory #%lu\n",
874                     (unsigned long)ino);
875         cdc.offset = 0;
876         cdc.prev_offset = 0;
877         cdc.bs = MIN(DIRBLKSIZ, TP_BSIZE);
878         /* Do the conversion */
879         retval = ext2fs_dir_iterate(fs, ino, 0, NULL, convert_dir, (void *)&cdc);
880         if (retval) {
881                 com_err(disk, retval, "while converting directory #%ld\n", (long)ino);
882                 exit(X_ABORT);
883         }
884         /* Fix the last entry */
885         if ((cdc.offset % cdc.bs) != 0) {
886                 de = (struct ext2_dir_entry *)(cdc.buf + cdc.prev_offset);
887                 de->rec_len += cdc.bs - (cdc.offset % cdc.bs);
888                 cdc.offset += cdc.bs - (cdc.offset % cdc.bs);
889         }
890
891         dir_size = cdc.offset;
892
893 #ifdef  __linux__
894         memset(&obi, 0, sizeof(obi));
895         obi.di_mode = dp->di_mode;
896         obi.di_uid = dp->di_uid;
897         obi.di_gid = dp->di_gid;
898         obi.di_qsize.v = dir_size; /* (u_quad_t)dp->di_size; */
899         obi.di_atime = dp->di_atime;
900         obi.di_mtime = dp->di_mtime;
901         obi.di_ctime = dp->di_ctime;
902         obi.di_nlink = dp->di_nlink;
903         obi.di_blocks = dp->di_blocks;
904         obi.di_flags = dp->di_flags;
905         obi.di_gen = dp->di_gen;
906         memmove(&obi.di_db, &dp->di_db, (NDADDR + NIADDR) * sizeof(daddr_t));
907         if (dp->di_file_acl || dp->di_dir_acl)
908                 warn("ACLs in inode #%ld won't be dumped", (long)ino);
909         memmove(&spcl.c_dinode, &obi, sizeof(obi));
910 #else   /* __linux__ */
911         spcl.c_dinode = *dp;
912 #endif  /* __linux__ */
913         spcl.c_type = TS_INODE;
914         spcl.c_count = 0;
915         switch (dp->di_mode & S_IFMT) {
916
917         case 0:
918                 /*
919                  * Freed inode.
920                  */
921                 return;
922
923         case S_IFDIR:
924                 if (dir_size > 0)
925                         break;
926                 msg("Warning: size of directory inode #%d is <= 0 (%d)!\n",
927                         ino, dir_size);
928                 return;
929
930         default:
931                 msg("Warning: dumpdirino called with file type 0%o (inode #%d)\n",
932                         dp->di_mode & IFMT, ino);
933                 return;
934         }
935         for (size = 0; size < dir_size; size += TP_BSIZE) {
936                 spcl.c_addr[0] = 1;
937                 spcl.c_count = 1;
938                 writeheader(ino);
939                 memmove(buf, cdc.buf + size, TP_BSIZE);
940                 writerec(buf, 0);
941                 spcl.c_type = TS_ADDR;
942         }
943
944         (void)free(cdc.buf);
945 }
946 #endif  /* __linux__ */
947
948 #ifndef __linux__
949 /*
950  * Read indirect blocks, and pass the data blocks to be dumped.
951  */
952 static void
953 dmpindir(ino_t ino, daddr_t blk, int ind_level, fsizeT *size)
954 {
955         int i, cnt;
956 #ifdef __linux__
957         int max;
958         blk_t *swapme;
959 #endif
960         daddr_t idblk[MAXNINDIR];
961
962         if (blk != 0) {
963                 bread(fsbtodb(sblock, blk), (char *)idblk, (int) sblock->fs_bsize);
964 #ifdef __linux__
965         /* 
966          * My RedHat 4.0 system doesn't have these flags; I haven't
967          * upgraded e2fsprogs yet
968          */
969 #if defined(EXT2_FLAG_SWAP_BYTES)
970         if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
971             (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) {
972 #endif
973                 max = sblock->fs_bsize >> 2;
974                 swapme = (blk_t *) idblk;
975                 for (i = 0; i < max; i++, swapme++)
976                         *swapme = swab32(*swapme);
977 #if defined(EXT2_FLAG_SWAP_BYTES)
978         }
979 #endif
980 #endif
981         else
982                 memset(idblk, 0, (int)sblock->fs_bsize);
983         if (ind_level <= 0) {
984                 if (*size < NINDIR(sblock) * sblock->fs_bsize)
985                         cnt = howmany(*size, sblock->fs_fsize);
986                 else
987 #ifdef  __linux__
988                         cnt = NINDIR(sblock) * EXT2_FRAGS_PER_BLOCK(fs->super);
989 #else
990                         cnt = NINDIR(sblock) * sblock->fs_frag;
991 #endif
992                 *size -= NINDIR(sblock) * sblock->fs_bsize;
993                 blksout(&idblk[0], cnt, ino);
994                 return;
995         }
996         ind_level--;
997         for (i = 0; i < NINDIR(sblock); i++) {
998                 dmpindir(ino, idblk[i], ind_level, size);
999                 if (*size <= 0)
1000                         return;
1001         }
1002 }
1003 #endif
1004
1005 /*
1006  * Collect up the data into tape record sized buffers and output them.
1007  */
1008 void
1009 blksout(daddr_t *blkp, int frags, ino_t ino)
1010 {
1011         register daddr_t *bp;
1012         int i, j, count, blks, tbperdb;
1013
1014         blks = howmany(frags * sblock->fs_fsize, TP_BSIZE);
1015         tbperdb = sblock->fs_bsize >> tp_bshift;
1016         for (i = 0; i < blks; i += TP_NINDIR) {
1017                 if (i + TP_NINDIR > blks)
1018                         count = blks;
1019                 else
1020                         count = i + TP_NINDIR;
1021                 for (j = i; j < count; j++)
1022                         if (blkp[j / tbperdb] != 0)
1023                                 spcl.c_addr[j - i] = 1;
1024                         else
1025                                 spcl.c_addr[j - i] = 0;
1026                 spcl.c_count = count - i;
1027                 writeheader(ino);
1028                 bp = &blkp[i / tbperdb];
1029                 for (j = i; j < count; j += tbperdb, bp++) {
1030                         if (*bp != 0) {
1031                                 if (j + tbperdb <= count)
1032                                         dumpblock(*bp, (int)sblock->fs_bsize);
1033                                 else
1034                                         dumpblock(*bp, (count - j) * TP_BSIZE);
1035                         }
1036                 }
1037                 spcl.c_type = TS_ADDR;
1038         }
1039 }
1040
1041 /*
1042  * Dump a map to the tape.
1043  */
1044 void
1045 dumpmap(char *map, int type, ino_t ino)
1046 {
1047         register int i;
1048         char *cp;
1049
1050         spcl.c_type = type;
1051         spcl.c_count = howmany(mapsize * sizeof(char), TP_BSIZE);
1052         writeheader(ino);
1053         for (i = 0, cp = map; i < spcl.c_count; i++, cp += TP_BSIZE)
1054                 writerec(cp, 0);
1055 }
1056
1057 /*
1058  * Write a header record to the dump tape.
1059  */
1060 void
1061 writeheader(ino_t ino)
1062 {
1063 #ifdef  __linux__
1064         register __s32 sum, cnt, *lp;
1065 #else
1066         register int32_t sum, cnt, *lp;
1067 #endif
1068
1069         spcl.c_inumber = ino;
1070         spcl.c_magic = NFS_MAGIC;
1071         spcl.c_checksum = 0;
1072 #ifdef  __linux__
1073         lp = (__s32 *)&spcl;
1074 #else
1075         lp = (int32_t *)&spcl;
1076 #endif
1077         sum = 0;
1078 #ifdef  __linux__
1079         cnt = sizeof(union u_spcl) / (4 * sizeof(__s32));
1080 #else
1081         cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t));
1082 #endif
1083         while (--cnt >= 0) {
1084                 sum += *lp++;
1085                 sum += *lp++;
1086                 sum += *lp++;
1087                 sum += *lp++;
1088         }
1089         spcl.c_checksum = CHECKSUM - sum;
1090         writerec((char *)&spcl, 1);
1091 }
1092
1093 #ifdef  __linux__
1094 struct dinode *
1095 getino(ino_t inum)
1096 {
1097         static struct dinode dinode;
1098
1099         curino = inum;
1100         ext2fs_read_inode(fs, inum, (struct ext2_inode *) &dinode);
1101         return &dinode;
1102 }
1103 #else   /* __linux__ */
1104 struct dinode *
1105 getino(ino_t inum)
1106 {
1107         static daddr_t minino, maxino;
1108         static struct dinode inoblock[MAXINOPB];
1109
1110         curino = inum;
1111         if (inum >= minino && inum < maxino)
1112                 return (&inoblock[inum - minino]);
1113         bread(fsbtodb(sblock, ino_to_fsba(sblock, inum)), (char *)inoblock,
1114             (int)sblock->fs_bsize);
1115         minino = inum - (inum % INOPB(sblock));
1116         maxino = minino + INOPB(sblock);
1117         return (&inoblock[inum - minino]);
1118 }
1119 #endif  /* __linux__ */
1120
1121 /*
1122  * Read a chunk of data from the disk.
1123  * Try to recover from hard errors by reading in sector sized pieces.
1124  * Error recovery is attempted at most BREADEMAX times before seeking
1125  * consent from the operator to continue.
1126  */
1127 int     breaderrors = 0;
1128 #define BREADEMAX 32
1129
1130 void
1131 bread(daddr_t blkno, char *buf, int size)
1132 {
1133         int cnt, i;
1134         extern int errno;
1135
1136 loop:
1137 #ifdef  __linux__
1138         if (ext2fs_llseek(diskfd, (((ext2_loff_t)blkno) << dev_bshift), 0) !=
1139                         (((ext2_loff_t)blkno) << dev_bshift))
1140 #else
1141         if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) !=
1142                                                 ((off_t)blkno << dev_bshift))
1143 #endif
1144                 msg("bread: lseek fails\n");
1145         if ((cnt = read(diskfd, buf, size)) == size)
1146                 return;
1147         if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
1148                 /*
1149                  * Trying to read the final fragment.
1150                  *
1151                  * NB - dump only works in TP_BSIZE blocks, hence
1152                  * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
1153                  * It should be smarter about not actually trying to
1154                  * read more than it can get, but for the time being
1155                  * we punt and scale back the read only when it gets
1156                  * us into trouble. (mkm 9/25/83)
1157                  */
1158                 size -= dev_bsize;
1159                 goto loop;
1160         }
1161         if (cnt == -1)
1162                 msg("read error from %s: %s: [block %d]: count=%d\n",
1163                         disk, strerror(errno), blkno, size);
1164         else
1165                 msg("short read error from %s: [block %d]: count=%d, got=%d\n",
1166                         disk, blkno, size, cnt);
1167         if (++breaderrors > BREADEMAX) {
1168                 msg("More than %d block read errors from %d\n",
1169                         BREADEMAX, disk);
1170                 broadcast("DUMP IS AILING!\n");
1171                 msg("This is an unrecoverable error.\n");
1172                 if (!query("Do you want to attempt to continue?")){
1173                         dumpabort(0);
1174                         /*NOTREACHED*/
1175                 } else
1176                         breaderrors = 0;
1177         }
1178         /*
1179          * Zero buffer, then try to read each sector of buffer separately.
1180          */
1181         memset(buf, 0, size);
1182         for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
1183 #ifdef  __linux__
1184                 if (ext2fs_llseek(diskfd, (((ext2_loff_t)blkno) << dev_bshift), 0) !=
1185                                 (((ext2_loff_t)blkno) << dev_bshift))
1186 #else
1187                 if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) !=
1188                                                 ((off_t)blkno << dev_bshift))
1189 #endif
1190                         msg("bread: lseek2 fails!\n");
1191                 if ((cnt = read(diskfd, buf, (int)dev_bsize)) == dev_bsize)
1192                         continue;
1193                 if (cnt == -1) {
1194                         msg("read error from %s: %s: [sector %d]: count=%d\n",
1195                                 disk, strerror(errno), blkno, dev_bsize);
1196                         continue;
1197                 }
1198                 msg("short read error from %s: [sector %d]: count=%d, got=%d\n",
1199                         disk, blkno, dev_bsize, cnt);
1200         }
1201 }