1 /*        $NetBSD: traverse.c,v 1.56 2023/08/07 23:26:40 mrg Exp $    */
2 
3 /*-
4  * Copyright (c) 1980, 1988, 1991, 1993
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)traverse.c  8.7 (Berkeley) 6/15/95";
36 #else
37 __RCSID("$NetBSD: traverse.c,v 1.56 2023/08/07 23:26:40 mrg Exp $");
38 #endif
39 #endif /* not lint */
40 
41 #include <sys/param.h>
42 #include <sys/time.h>
43 #include <sys/stat.h>
44 #include <ufs/ufs/dir.h>
45 #include <ufs/ffs/fs.h>
46 #include <ufs/ffs/ffs_extern.h>
47 
48 #include <assert.h>
49 #include <ctype.h>
50 #include <errno.h>
51 #include <fts.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 
57 #include "dump.h"
58 
59 #define   HASDUMPEDFILE       0x1
60 #define   HASSUBDIRS          0x2
61 
62 static    int appendextdata(union dinode *dp);
63 static    void writeextdata(union dinode *dp, ino_t ino, int added);
64 static    int dirindir(ino_t, daddr_t, int, off_t *, u_int64_t *, int);
65 static    void dmpindir(union dinode *dp, ino_t, daddr_t, int, off_t *);
66 static    int searchdir(ino_t, daddr_t, long, off_t, u_int64_t *, int);
67 
68 /*
69  * This is an estimation of the number of TP_BSIZE blocks in the file.
70  * It estimates the number of blocks in files with holes by assuming
71  * that all of the blocks accounted for by di_blocks are data blocks
72  * (when some of the blocks are usually used for indirect pointers);
73  * hence the estimate may be high.
74  */
75 int64_t
blockest(union dinode * dp)76 blockest(union dinode *dp)
77 {
78           int64_t blkest, sizeest;
79 
80           /*
81            * dp->di_size is the size of the file in bytes.
82            * dp->di_blocks stores the number of sectors actually in the file.
83            * If there are more sectors than the size would indicate, this just
84            *        means that there are indirect blocks in the file or unused
85            *        sectors in the last file block; we can safely ignore these
86            *        (blkest = sizeest below).
87            * If the file is bigger than the number of sectors would indicate,
88            *        then the file has holes in it.          In this case we must use the
89            *        block count to estimate the number of data blocks used, but
90            *        we use the actual size for estimating the number of indirect
91            *        dump blocks (sizeest vs. blkest in the indirect block
92            *        calculation).
93            */
94           if (DIP(dp, flags) & SF_SNAPSHOT)
95                     return (1);
96           blkest = howmany(ufs_fragroundup(ufsib,
97               dbtob((u_int64_t)DIP(dp, blocks))), TP_BSIZE);
98           sizeest = howmany(ufs_fragroundup(ufsib, DIP(dp, size)), TP_BSIZE);
99           if (blkest > sizeest)
100                     blkest = sizeest;
101           if (DIP(dp, size) > ufsib->ufs_bsize * UFS_NDADDR) {
102                     /* calculate the number of indirect blocks on the dump tape */
103                     blkest +=
104                               howmany(sizeest - UFS_NDADDR * ufsib->ufs_bsize / TP_BSIZE,
105                               TP_NINDIR);
106           }
107           return (blkest + 1);
108 }
109 
110 /* Auxiliary macro to pick up files changed since previous dump. */
111 #define   CHANGEDSINCE(dp, t) \
112           (DIP((dp), mtime) >= (t) || DIP((dp), ctime) >= (t))
113 
114 /* The WANTTODUMP macro decides whether a file should be dumped. */
115 #ifdef UF_NODUMP
116 #define   WANTTODUMP(dp) \
117           (CHANGEDSINCE(dp, iswap64(spcl.c_ddate)) && \
118            (nonodump || (DIP((dp), flags) & UF_NODUMP) != UF_NODUMP))
119 #else
120 #define   WANTTODUMP(dp) CHANGEDSINCE(dp, iswap64(spcl.c_ddate))
121 #endif
122 
123 /*
124  * Determine if given inode should be dumped
125  */
126 void
mapfileino(ino_t ino,u_int64_t * tape_size,int * dirskipped)127 mapfileino(ino_t ino, u_int64_t *tape_size, int *dirskipped)
128 {
129           int mode;
130           union dinode *dp;
131 
132           /*
133            * Skip inode if we've already marked it for dumping
134            */
135           if (TSTINO(ino, usedinomap))
136                     return;
137           dp = getino(ino);
138           if (dp == NULL || (mode = (DIP(dp, mode) & IFMT)) == 0)
139                     return;
140           /*
141            * Skip WAPBL log file inodes.
142            */
143           if (DIP(dp, flags) & SF_LOG)
144                     return;
145           /*
146            * Put all dirs in dumpdirmap, inodes that are to be dumped in the
147            * used map. All inode but dirs who have the nodump attribute go
148            * to the usedinomap.
149            */
150           SETINO(ino, usedinomap);
151           if (mode == IFDIR)
152                     SETINO(ino, dumpdirmap);
153           if (WANTTODUMP(dp)) {
154                     SETINO(ino, dumpinomap);
155                     if (mode != IFREG && mode != IFDIR && mode != IFLNK)
156                               *tape_size += 1;
157                     else
158                               *tape_size += blockest(dp);
159                     return;
160           }
161           if (mode == IFDIR) {
162 #ifdef UF_NODUMP
163                     if (!nonodump && (DIP(dp, flags) & UF_NODUMP))
164                               CLRINO(ino, usedinomap);
165 #endif
166                     *dirskipped = 1;
167           }
168 }
169 
170 /*
171  * Dump pass 1.
172  *
173  * Walk the inode list for a file system to find all allocated inodes
174  * that have been modified since the previous dump time. Also, find all
175  * the directories in the file system.
176  * disk may be NULL if dirv is NULL.
177  */
178 int
mapfiles(ino_t maxino,u_int64_t * tape_size,char * diskname,char * const * dirv)179 mapfiles(ino_t maxino, u_int64_t *tape_size, char *diskname, char * const *dirv)
180 {
181           int anydirskipped = 0;
182 
183           if (dirv != NULL) {
184                     char       curdir[MAXPATHLEN];
185                     FTS       *dirh;
186                     FTSENT    *entry;
187                     int        d;
188 
189                     if (getcwd(curdir, sizeof(curdir)) == NULL) {
190                               msg("Can't determine cwd: %s\n", strerror(errno));
191                               dumpabort(0);
192                     }
193                     if ((dirh = fts_open(dirv, FTS_PHYSICAL|FTS_SEEDOT|FTS_XDEV,
194                                             NULL)) == NULL) {
195                               msg("fts_open failed: %s\n", strerror(errno));
196                               dumpabort(0);
197                     }
198                     while ((entry = fts_read(dirh)) != NULL) {
199                               switch (entry->fts_info) {
200                               case FTS_DNR:                 /* an error */
201                               case FTS_ERR:
202                               case FTS_NS:
203                                         msg("Can't fts_read %s: %s\n", entry->fts_path,
204                                             strerror(errno));
205                                         /* FALLTHROUGH */
206                               case FTS_DP:                  /* already seen dir */
207                                         continue;
208                               }
209                               mapfileino(entry->fts_statp->st_ino, tape_size,
210                                   &anydirskipped);
211                     }
212                     (void)fts_close(dirh);
213 
214                     /*
215                      * Add any parent directories
216                      */
217                     for (d = 0 ; dirv[d] != NULL ; d++) {
218                               char path[MAXPATHLEN];
219 
220                               if (dirv[d][0] != '/')
221                                         (void)snprintf(path, sizeof(path), "%s/%s",
222                                             curdir, dirv[d]);
223                               else
224                                         (void)snprintf(path, sizeof(path), "%s",
225                                             dirv[d]);
226                               while (strcmp(path, diskname) != 0) {
227                                         char *p;
228                                         struct stat sb;
229 
230                                         if (*path == '\0')
231                                                   break;
232                                         if ((p = strrchr(path, '/')) == NULL)
233                                                   break;
234                                         if (p == path)
235                                                   break;
236                                         *p = '\0';
237                                         if (stat(path, &sb) == -1) {
238                                                   msg("Can't stat %s: %s\n", path,
239                                                       strerror(errno));
240                                                   break;
241                                         }
242                                         mapfileino(sb.st_ino, tape_size, &anydirskipped);
243                               }
244                     }
245 
246                     /*
247                      * Ensure that the root inode actually appears in the
248                      * file list for a subdir
249                      */
250                     mapfileino(UFS_ROOTINO, tape_size, &anydirskipped);
251           } else {
252                     fs_mapinodes(maxino, tape_size, &anydirskipped);
253           }
254           /*
255            * Restore gets very upset if the root is not dumped,
256            * so ensure that it always is dumped.
257            */
258           SETINO(UFS_ROOTINO, dumpinomap);
259           return (anydirskipped);
260 }
261 
262 /*
263  * Dump pass 2.
264  *
265  * Scan each directory on the file system to see if it has any modified
266  * files in it. If it does, and has not already been added to the dump
267  * list (because it was itself modified), then add it. If a directory
268  * has not been modified itself, contains no modified files and has no
269  * subdirectories, then it can be deleted from the dump list and from
270  * the list of directories. By deleting it from the list of directories,
271  * its parent may now qualify for the same treatment on this or a later
272  * pass using this algorithm.
273  */
274 int
mapdirs(ino_t maxino,u_int64_t * tape_size)275 mapdirs(ino_t maxino, u_int64_t *tape_size)
276 {
277           union dinode *dp, di;
278           int i, isdir, nodump;
279           char *map;
280           ino_t ino;
281           off_t filesize;
282           int ret, change = 0;
283           daddr_t blk;
284 
285           isdir = 0;                    /* XXX just to get gcc to shut up */
286           for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
287                     if (((ino - 1) % NBBY) == 0)  /* map is offset by 1 */
288                               isdir = *map++;
289                     else
290                               isdir >>= 1;
291                     /*
292                      * If dir has been removed from the used map, it's either
293                      * because it had the nodump flag, or it inherited it from
294                      * its parent. A directory can't be in dumpinomap if
295                      * not in usedinomap, but we have to go throuh it anyway
296                      * to propagate the nodump attribute.
297                      */
298                     nodump = (TSTINO(ino, usedinomap) == 0);
299                     if ((isdir & 1) == 0 ||
300                         (TSTINO(ino, dumpinomap) && nodump == 0))
301                               continue;
302 
303                     dp = getino(ino);
304                     /*
305                      * inode buf may be changed in searchdir().
306                      */
307                     if (is_ufs2)
308                               di.dp2 = dp->dp2;
309                     else
310                               di.dp1 = dp->dp1;
311                     filesize = DIP(&di, size);
312                     for (ret = 0, i = 0; filesize > 0 && i < UFS_NDADDR; i++) {
313                               if (is_ufs2)
314                                         blk = iswap64(di.dp2.di_db[i]);
315                               else
316                                         blk = iswap32(di.dp1.di_db[i]);
317                               if (blk != 0)
318                                         ret |= searchdir(ino, blk,
319                                                   (long)ufs_dblksize(ufsib, &di, i),
320                                                   filesize, tape_size, nodump);
321                               if (ret & HASDUMPEDFILE)
322                                         filesize = 0;
323                               else
324                                         filesize -= ufsib->ufs_bsize;
325                     }
326                     for (i = 0; filesize > 0 && i < UFS_NIADDR; i++) {
327                               if (is_ufs2)
328                                         blk = iswap64(di.dp2.di_ib[i]);
329                               else
330                                         blk = iswap32(di.dp1.di_ib[i]);
331                               if (blk == 0)
332                                         continue;
333                               ret |= dirindir(ino, blk, i, &filesize,
334                                   tape_size, nodump);
335                     }
336                     if (ret & HASDUMPEDFILE) {
337                               SETINO(ino, dumpinomap);
338                               *tape_size += blockest(&di);
339                               change = 1;
340                               continue;
341                     }
342                     if (nodump) {
343                               if (ret & HASSUBDIRS)
344                                         change = 1; /* subdirs have inherited nodump */
345                               CLRINO(ino, dumpdirmap);
346                     } else if ((ret & HASSUBDIRS) == 0) {
347                               if (!TSTINO(ino, dumpinomap)) {
348                                         CLRINO(ino, dumpdirmap);
349                                         change = 1;
350                               }
351                     }
352           }
353           return (change);
354 }
355 
356 /*
357  * Read indirect blocks, and pass the data blocks to be searched
358  * as directories. Quit as soon as any entry is found that will
359  * require the directory to be dumped.
360  */
361 static int
dirindir(ino_t ino,daddr_t blkno,int ind_level,off_t * filesize,u_int64_t * tape_size,int nodump)362 dirindir(ino_t ino, daddr_t blkno, int ind_level, off_t *filesize,
363           u_int64_t *tape_size, int nodump)
364 {
365           int ret = 0;
366           int i;
367           union {
368                     int64_t i64[MAXBSIZE / sizeof (int64_t)];
369                     int32_t i32[MAXBSIZE / sizeof (int32_t)];
370           } idblk;
371 
372           bread(fsatoda(ufsib, blkno), (char *)&idblk, (int)ufsib->ufs_bsize);
373           if (ind_level <= 0) {
374                     for (i = 0; *filesize > 0 && i < ufsib->ufs_nindir; i++) {
375                               if (is_ufs2)
376                                         blkno = iswap64(idblk.i64[i]);
377                               else
378                                         blkno = iswap32(idblk.i32[i]);
379                               if (blkno != 0)
380                                         ret |= searchdir(ino, blkno,
381                                             ufsib->ufs_bsize, *filesize,
382                                             tape_size, nodump);
383                               if (ret & HASDUMPEDFILE)
384                                         *filesize = 0;
385                               else
386                                         *filesize -= ufsib->ufs_bsize;
387                     }
388                     return (ret);
389           }
390           ind_level--;
391           for (i = 0; *filesize > 0 && i < ufsib->ufs_nindir; i++) {
392                     if (is_ufs2)
393                               blkno = iswap64(idblk.i64[i]);
394                     else
395                               blkno = iswap32(idblk.i32[i]);
396                     if (blkno != 0)
397                               ret |= dirindir(ino, blkno, ind_level, filesize,
398                                   tape_size, nodump);
399           }
400           return (ret);
401 }
402 
403 /*
404  * Scan a disk block containing directory information looking to see if
405  * any of the entries are on the dump list and to see if the directory
406  * contains any subdirectories.
407  */
408 static int
searchdir(ino_t dino,daddr_t blkno,long size,off_t filesize,u_int64_t * tape_size,int nodump)409 searchdir(ino_t dino, daddr_t blkno, long size, off_t filesize,
410           u_int64_t *tape_size, int nodump)
411 {
412           struct direct *dp;
413           union dinode *ip;
414           long loc, ret = 0;
415           char *dblk;
416           ino_t ino;
417 
418           dblk = malloc(size);
419           if (dblk == NULL)
420                     quit("%s: cannot allocate directory memory", __func__);
421           bread(fsatoda(ufsib, blkno), dblk, (int)size);
422           if (filesize < size)
423                     size = filesize;
424           for (loc = 0; loc < size; ) {
425                     dp = (struct direct *)(dblk + loc);
426                     if (dp->d_reclen == 0) {
427                               msg("corrupted directory, inumber %llu\n",
428                                   (unsigned long long)dino);
429                               break;
430                     }
431                     loc += iswap16(dp->d_reclen);
432                     if (dp->d_ino == 0)
433                               continue;
434                     if (dp->d_name[0] == '.') {
435                               if (dp->d_name[1] == '\0')
436                                         continue;
437                               if (dp->d_name[1] == '.' && dp->d_name[2] == '\0')
438                                         continue;
439                     }
440                     ino = iswap32(dp->d_ino);
441                     if (nodump) {
442                               ip = getino(ino);
443                               if (TSTINO(ino, dumpinomap)) {
444                                         CLRINO(ino, dumpinomap);
445                                         *tape_size -= blockest(ip);
446                               }
447                               /*
448                                * Add back to dumpdirmap and remove from usedinomap
449                                * to propagate nodump.
450                                */
451                               if ((DIP(ip, mode) & IFMT) == IFDIR) {
452                                         SETINO(ino, dumpdirmap);
453                                         CLRINO(ino, usedinomap);
454                                         ret |= HASSUBDIRS;
455                               }
456                     } else {
457                               if (TSTINO(ino, dumpinomap)) {
458                                         ret |= HASDUMPEDFILE;
459                                         if (ret & HASSUBDIRS)
460                                                   break;
461                               }
462                               if (TSTINO(ino, dumpdirmap)) {
463                                         ret |= HASSUBDIRS;
464                                         if (ret & HASDUMPEDFILE)
465                                                   break;
466                               }
467                     }
468           }
469           free(dblk);
470           return (ret);
471 }
472 
473 /*
474  * Dump passes 3 and 4.
475  *
476  * Dump the contents of an inode to tape.
477  */
478 void
dumpino(union dinode * dp,ino_t ino)479 dumpino(union dinode *dp, ino_t ino)
480 {
481           int ind_level, cnt, last, added;
482           off_t size;
483           char buf[TP_BSIZE];
484           daddr_t blk;
485           void *shortlink;
486 
487           if (newtape) {
488                     newtape = 0;
489                     dumpmap(dumpinomap, TS_BITS, ino);
490           }
491           CLRINO(ino, dumpinomap);
492           /*
493            * Zero out the size of a snapshot so that it will be dumped
494            * as a zero length file.
495            */
496           if (DIP(dp, flags) & SF_SNAPSHOT) {
497                     DIP_SET(dp, size, 0);
498                     DIP_SET(dp, flags, DIP(dp, flags) & ~SF_SNAPSHOT);
499           }
500           if (!is_ufs2) {
501                     if (needswap)
502                               ffs_dinode1_swap(&dp->dp1, &spcl.c_dinode);
503                     else
504                               spcl.c_dinode = dp->dp1;
505                     spcl.c_extsize = 0;
506           } else {
507                     if (needswap)
508                               ffs_dinode2_swap(&dp->dp2, &dp->dp2);
509                     spcl.c_mode = dp->dp2.di_mode;
510                     spcl.c_size = dp->dp2.di_size;
511                     spcl.c_extsize = dp->dp2.di_extsize;
512                     spcl.c_atime = dp->dp2.di_atime;
513                     spcl.c_atimensec = dp->dp2.di_atimensec;
514                     spcl.c_mtime = dp->dp2.di_mtime;
515                     spcl.c_mtimensec = dp->dp2.di_mtimensec;
516                     spcl.c_birthtime = dp->dp2.di_birthtime;
517                     spcl.c_birthtimensec = dp->dp2.di_birthnsec;
518                     spcl.c_rdev = dp->dp2.di_rdev;
519                     spcl.c_file_flags = dp->dp2.di_flags;
520                     spcl.c_uid = dp->dp2.di_uid;
521                     spcl.c_gid = dp->dp2.di_gid;
522           }
523           spcl.c_type = iswap32(TS_INODE);
524           spcl.c_count = 0;
525           switch (DIP(dp, mode) & IFMT) {
526 
527           case 0:
528                     /*
529                      * Freed inode.
530                      */
531                     return;
532 
533           case IFLNK:
534                     /*
535                      * Check for short symbolic link.
536                      */
537                     if (DIP(dp, size) > 0 &&
538 #ifdef FS_44INODEFMT
539                         (DIP(dp, size) < ufsib->ufs_maxsymlinklen ||
540                          (ufsib->ufs_maxsymlinklen == 0 && DIP(dp, blocks) == 0))
541 #else
542                         DIP(dp, blocks) == 0
543 #endif
544                               ) {
545                               spcl.c_addr[0] = 1;
546                               spcl.c_count = iswap32(1);
547                               added = appendextdata(dp);
548                               writeheader(ino);
549                               if (is_ufs2)
550                                         shortlink = dp->dp2.di_db;
551                               else
552                                         shortlink = dp->dp1.di_db;
553                               memmove(buf, shortlink, DIP(dp, size));
554                               buf[DIP(dp, size)] = '\0';
555                               writerec(buf, 0);
556                               writeextdata(dp, ino, added);
557                               return;
558                     }
559                     /* fall through */
560 
561           case IFDIR:
562           case IFREG:
563                     if (DIP(dp, size) > 0)
564                               break;
565                     /* fall through */
566 
567           case IFIFO:
568           case IFSOCK:
569           case IFCHR:
570           case IFBLK:
571                     added = appendextdata(dp);
572                     writeheader(ino);
573                     writeextdata(dp, ino, added);
574                     return;
575 
576           default:
577                     msg("Warning: undefined file type 0%o\n", DIP(dp, mode) & IFMT);
578                     return;
579           }
580           if (DIP(dp, size) > UFS_NDADDR * ufsib->ufs_bsize) {
581                     cnt = UFS_NDADDR * ufsib->ufs_frag;
582                     last = 0;
583           } else {
584                     cnt = howmany(DIP(dp, size), ufsib->ufs_fsize);
585                     last = 1;
586           }
587           if (is_ufs2)
588                     blksout64(dp, &dp->dp2.di_db[0], cnt, ino, last);
589           else
590                     blksout32(&dp->dp1.di_db[0], cnt, ino);
591 
592           if ((size = DIP(dp, size) - UFS_NDADDR * ufsib->ufs_bsize) <= 0)
593                     return;
594           for (ind_level = 0; ind_level < UFS_NIADDR; ind_level++) {
595                     if (is_ufs2)
596                               blk = iswap64(dp->dp2.di_ib[ind_level]);
597                     else
598                               blk = iswap32(dp->dp1.di_ib[ind_level]);
599                     dmpindir(dp, ino, blk, ind_level, &size);
600                     if (size <= 0)
601                               return;
602           }
603 }
604 
605 /*
606  * Read indirect blocks, and pass the data blocks to be dumped.
607  */
608 static void
dmpindir(union dinode * dp,ino_t ino,daddr_t blk,int ind_level,off_t * size)609 dmpindir(union dinode *dp, ino_t ino, daddr_t blk, int ind_level, off_t *size)
610 {
611           int i, cnt, last;
612           union {
613                     int32_t i32[MAXBSIZE / sizeof (int32_t)];
614                     int64_t i64[MAXBSIZE / sizeof (int64_t)];
615           } idblk;
616           daddr_t iblk;
617 
618 
619           if (blk != 0)
620                     bread(fsatoda(ufsib, blk), (char *)&idblk,
621                               (int) ufsib->ufs_bsize);
622           else
623                     memset(&idblk, 0, (int)ufsib->ufs_bsize);
624           if (ind_level <= 0) {
625                     if (*size < ufsib->ufs_nindir * ufsib->ufs_bsize) {
626                               cnt = howmany(*size, ufsib->ufs_fsize);
627                               last = 0;
628                     } else {
629                               cnt = ufsib->ufs_nindir * ufsib->ufs_frag;
630                               last = 1;
631                     }
632                     *size -= ufsib->ufs_nindir * ufsib->ufs_bsize;
633                     if (is_ufs2)
634                               blksout64(dp, &idblk.i64[0], cnt, ino, last);
635                     else
636                               blksout32(&idblk.i32[0], cnt, ino);
637                     return;
638           }
639           ind_level--;
640           for (i = 0; i < ufsib->ufs_nindir; i++) {
641                     if (is_ufs2)
642                               iblk = iswap64(idblk.i64[i]);
643                     else
644                               iblk = iswap32(idblk.i32[i]);
645                     dmpindir(dp, ino, iblk, ind_level, size);
646                     if (*size <= 0)
647                               return;
648           }
649 }
650 
651 /*
652  * Collect up the data into tape record sized buffers and output them.
653  */
654 void
blksout32(int32_t * blkp,int frags,ino_t ino)655 blksout32(int32_t *blkp, int frags, ino_t ino)
656 {
657           int32_t *bp;
658           int i, j, count, blks, tbperdb;
659 
660           blks = howmany(frags * ufsib->ufs_fsize, TP_BSIZE);
661           tbperdb = ufsib->ufs_bsize >> tp_bshift;
662           for (i = 0; i < blks; i += TP_NINDIR) {
663                     if (i + TP_NINDIR > blks)
664                               count = blks;
665                     else
666                               count = i + TP_NINDIR;
667                     for (j = i; j < count; j++)
668                               if (blkp[j / tbperdb] != 0)
669                                         spcl.c_addr[j - i] = 1;
670                               else
671                                         spcl.c_addr[j - i] = 0;
672                     spcl.c_count = iswap32(count - i);
673                     writeheader(ino);
674                     bp = &blkp[i / tbperdb];
675                     for (j = i; j < count; j += tbperdb, bp++)
676                               if (*bp != 0) {
677                                         if (j + tbperdb <= count)
678                                                   dumpblock(iswap32(*bp), (int)ufsib->ufs_bsize);
679                                         else
680                                                   dumpblock(iswap32(*bp), (count - j) * TP_BSIZE);
681                               }
682                     spcl.c_type = iswap32(TS_ADDR);
683           }
684 }
685 
686 void
blksout64(union dinode * dp,int64_t * blkp,int frags,ino_t ino,int last)687 blksout64(union dinode *dp, int64_t *blkp, int frags, ino_t ino, int last)
688 {
689           int64_t *bp;
690           int i, j, count, blks, tbperdb, added = 0;
691           static int writingextdata = 0;
692 
693           blks = howmany(frags * ufsib->ufs_fsize, TP_BSIZE);
694 #if 0
695           if (last) {
696                     int resid;
697                     int extsize = iswap32(spcl.c_extsize);
698                     if (writingextdata)
699                               resid = howmany(ufsib->ufs_qfmask & extsize,
700                                   TP_BSIZE);
701                     else
702                               resid = howmany(ufsib->ufs_qfmask & dp->dp2.di_size,
703                                   TP_BSIZE);
704                     if (resid > 0)
705                               blks -= howmany(ufsib->ufs_fsize, TP_BSIZE) - resid;
706           }
707 #endif
708           tbperdb = ufsib->ufs_bsize >> tp_bshift;
709           for (i = 0; i < blks; i += TP_NINDIR) {
710                     if (i + TP_NINDIR > blks)
711                               count = blks;
712                     else
713                               count = i + TP_NINDIR;
714                     assert(count <= TP_NINDIR + i);
715                     for (j = i; j < count; j++)
716                               if (blkp[j / tbperdb] != 0)
717                                         spcl.c_addr[j - i] = 1;
718                               else
719                                         spcl.c_addr[j - i] = 0;
720                     spcl.c_count = iswap32(count - i);
721                     if (last && count == blks && !writingextdata)
722                               added = appendextdata(dp);
723                     writeheader(ino);
724                     bp = &blkp[i / tbperdb];
725                     for (j = i; j < count; j += tbperdb, bp++)
726                               if (*bp != 0) {
727                                         if (j + tbperdb <= count)
728                                                   dumpblock(iswap64(*bp), (int)ufsib->ufs_bsize);
729                                         else
730                                                   dumpblock(iswap64(*bp), (count - j) * TP_BSIZE);
731                               }
732                     spcl.c_type = iswap32(TS_ADDR);
733                     spcl.c_count = 0;
734                     if (last && count == blks && !writingextdata) {
735                               writingextdata = 1;
736                               writeextdata(dp, ino, added);
737                               writingextdata = 0;
738                     }
739           }
740 }
741 
742 /*
743  * If there is room in the current block for the extended attributes
744  * as well as the file data, update the header to reflect the added
745  * attribute data at the end. Attributes are placed at the end so that
746  * old versions of restore will correctly restore the file and simply
747  * discard the extra data at the end that it does not understand.
748  * The attribute data is dumped following the file data by the
749  * writeextdata() function (below).
750  */
751 static int
appendextdata(union dinode * dp)752 appendextdata(union dinode *dp)
753 {
754           int i, blks, tbperdb, count, extsize;
755 
756           count = iswap32(spcl.c_count);
757           extsize = iswap32(spcl.c_extsize);
758           /*
759            * If no extended attributes, there is nothing to do.
760            */
761           if (extsize == 0)
762                     return (0);
763           /*
764            * If there is not enough room at the end of this block
765            * to add the extended attributes, then rather than putting
766            * part of them here, we simply push them entirely into a
767            * new block rather than putting some here and some later.
768            */
769           if (extsize > UFS_NXADDR * ufsib->ufs_bsize)
770                     blks = howmany(UFS_NXADDR * ufsib->ufs_bsize, TP_BSIZE);
771           else
772                     blks = howmany(extsize, TP_BSIZE);
773           if (count + blks > TP_NINDIR)
774                     return (0);
775           /*
776            * Update the block map in the header to indicate the added
777            * extended attribute. They will be appended after the file
778            * data by the writeextdata() routine.
779            */
780           tbperdb = ufsib->ufs_bsize >> tp_bshift;
781           assert(count + blks < TP_NINDIR);
782           for (i = 0; i < blks; i++)
783                     if (dp->dp2.di_extb[i / tbperdb] != 0)
784                                         spcl.c_addr[count + i] = 1;
785                               else
786                                         spcl.c_addr[count + i] = 0;
787           spcl.c_count = iswap32(count + blks);
788           return (blks);
789 }
790 
791 /*
792  * Dump the extended attribute data. If there was room in the file
793  * header, then all we need to do is output the data blocks. If there
794  * was not room in the file header, then an additional TS_ADDR header
795  * is created to hold the attribute data.
796  */
797 static void
writeextdata(union dinode * dp,ino_t ino,int added)798 writeextdata(union dinode *dp, ino_t ino, int added)
799 {
800           int i, frags, blks, tbperdb, last, extsize;
801           int64_t *bp;
802           off_t size;
803 
804           extsize = iswap32(spcl.c_extsize);
805 
806           /*
807            * If no extended attributes, there is nothing to do.
808            */
809           if (extsize == 0)
810                     return;
811           /*
812            * If there was no room in the file block for the attributes,
813            * dump them out in a new block, otherwise just dump the data.
814            */
815           if (added == 0) {
816                     if (extsize > UFS_NXADDR * ufsib->ufs_bsize) {
817                               frags = UFS_NXADDR * ufsib->ufs_frag;
818                               last = 0;
819                     } else {
820                               frags = howmany(extsize, ufsib->ufs_fsize);
821                               last = 1;
822                     }
823                     blksout64(dp, &dp->dp2.di_extb[0], frags, ino, last);
824           } else {
825                     if (extsize > UFS_NXADDR * ufsib->ufs_bsize)
826                               blks = howmany(UFS_NXADDR * ufsib->ufs_bsize, TP_BSIZE);
827                     else
828                               blks = howmany(extsize, TP_BSIZE);
829                     tbperdb = ufsib->ufs_bsize >> tp_bshift;
830                     for (i = 0; i < blks; i += tbperdb) {
831                               bp = &dp->dp2.di_extb[i / tbperdb];
832                               if (*bp != 0) {
833                                         if (i + tbperdb <= blks)
834                                                   dumpblock(iswap64(*bp), (int)ufsib->ufs_bsize);
835                                         else
836                                                   dumpblock(iswap64(*bp), (blks - i) * TP_BSIZE);
837                               }
838                     }
839 
840           }
841           /*
842            * If an indirect block is added for extended attributes, then
843            * di_exti below should be changed to the structure element
844            * that references the extended attribute indirect block. This
845            * definition is here only to make it compile without complaint.
846            */
847 #define di_exti di_spare[0]
848           /*
849            * If the extended attributes fall into an indirect block,
850            * dump it as well.
851            */
852           if ((size = extsize - UFS_NXADDR * ufsib->ufs_bsize) > 0)
853                     dmpindir(dp, ino, dp->dp2.di_exti, 0, &size);
854 }
855 
856 /*
857  * Dump a map to the tape.
858  */
859 void
dumpmap(char * map,int type,ino_t ino)860 dumpmap(char *map, int type, ino_t ino)
861 {
862           int i;
863           char *cp;
864 
865           spcl.c_type = iswap32(type);
866           spcl.c_count = iswap32(howmany(mapsize * sizeof(char), TP_BSIZE));
867           writeheader(ino);
868           for (i = 0, cp = map; i < iswap32(spcl.c_count); i++, cp += TP_BSIZE)
869                     writerec(cp, 0);
870 }
871 
872 /*
873  * Write a header record to the dump tape.
874  */
875 void
writeheader(ino_t ino)876 writeheader(ino_t ino)
877 {
878           int32_t sum, cnt, *lp;
879 
880           spcl.c_inumber = iswap32(ino);
881           if (is_ufs2)
882                     spcl.c_magic = iswap32(FS_UFS2_MAGIC);
883           else {
884                     spcl.c_magic = iswap32(NFS_MAGIC);
885                     spcl.c_old_date = iswap32(iswap64(spcl.c_date));
886                     spcl.c_old_ddate = iswap32(iswap64(spcl.c_ddate));
887                     spcl.c_old_tapea = iswap32(iswap64(spcl.c_tapea));
888                     spcl.c_old_firstrec = iswap32(iswap64(spcl.c_firstrec));
889           }
890           spcl.c_checksum = 0;
891           lp = (int32_t *)&spcl;
892           sum = 0;
893           cnt = sizeof(union u_spcl) / (4 * sizeof(int32_t));
894           while (--cnt >= 0) {
895                     sum += iswap32(*lp++);
896                     sum += iswap32(*lp++);
897                     sum += iswap32(*lp++);
898                     sum += iswap32(*lp++);
899           }
900           spcl.c_checksum = iswap32(CHECKSUM - sum);
901           writerec((char *)&spcl, 1);
902 }
903