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