1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1991,1996,1998 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #include "dump.h" 28 #include <math.h> 29 #include <limits.h> 30 31 /* 32 * Uncomment if using mmap'ing of files for pre-fetch. 33 * #define ENABLE_MMAP 1 34 */ 35 36 struct inodesc { 37 ino_t id_inumber; /* inode number */ 38 long id_gen; /* generation number */ 39 struct inodesc *id_next; /* next on linked list */ 40 }; 41 42 char *archivefile; 43 char *tape; 44 45 int active; 46 int doingactive; 47 int doposition; 48 int pipeout; 49 int tapeout; 50 int to; 51 52 struct fs *sblock; 53 union u_spcl u_spcl; 54 55 static struct inodesc ilist; /* list of used inodesc structs */ 56 static struct inodesc *last; /* last inodesc init'd or matched */ 57 static struct inodesc *freeinodesc; /* free list of inodesc structs */ 58 static struct inodesc **ialloc; /* allocated chunks, for freeing */ 59 static int nchunks; /* number of allocations */ 60 61 #ifdef ENABLE_MMAP /* XXX part of mmap support */ 62 /* 63 * If an mmap'ed file is truncated as it is being dumped or 64 * faulted in, we are delivered a SIGBUS. 65 */ 66 static jmp_buf truncate_buf; 67 static void (*savebus)(); 68 static int incopy; 69 70 static void onsigbus(int); 71 72 #endif /* ENABLE_MMAP */ 73 74 #ifdef DEBUG 75 extern int xflag; 76 #endif 77 78 #ifdef ENABLE_MMAP /* XXX part of mmap support */ 79 static void 80 onsigbus(int sig) 81 { 82 if (!incopy) { 83 dumpabort(); 84 /*NOTREACHED*/ 85 } 86 incopy = 0; 87 longjmp(truncate_buf, 1); 88 /*NOTREACHED*/ 89 } 90 #endif /* ENABLE_MMAP */ 91 92 void 93 allocino(void) 94 { 95 ino_t maxino; 96 size_t nused; 97 98 maxino = (unsigned)(sblock->fs_ipg * sblock->fs_ncg); 99 if (maxino > ULONG_MAX) { 100 msg(gettext("allocino: filesystem too large\n")); 101 dumpabort(); 102 /*NOTREACHED*/ 103 } 104 /* LINTED maxino guaranteed to fit into a size_t by above test */ 105 nused = maxino - sblock->fs_cstotal.cs_nifree; 106 freeinodesc = (struct inodesc *)xcalloc(nused, sizeof (*freeinodesc)); 107 if (freeinodesc == (struct inodesc *)0) { 108 msg(gettext("%s: out of memory\n"), "allocino"); 109 dumpabort(); 110 /*NOTREACHED*/ 111 } 112 last = &ilist; 113 ialloc = 114 (struct inodesc **)xmalloc(2*sizeof (*ialloc)); 115 ialloc[0] = freeinodesc; 116 ialloc[1] = (struct inodesc *)0; 117 nchunks = 1; 118 } 119 120 void 121 freeino(void) 122 { 123 int i; 124 125 if (ialloc == (struct inodesc **)0) 126 return; 127 for (i = 0; i < nchunks; i++) 128 if (ialloc[i] != 0) 129 free(ialloc[i]); 130 free(ialloc); 131 ialloc = (struct inodesc **)0; 132 } 133 134 void 135 resetino(ino_t ino) 136 { 137 last = ilist.id_next; 138 while (last && last->id_inumber < ino) 139 last = last->id_next; 140 } 141 142 char * 143 unrawname(char *cp) 144 { 145 char *dp; 146 extern char *getfullblkname(); 147 148 dp = getfullblkname(cp); 149 if (dp == 0) 150 return (0); 151 if (*dp == '\0') { 152 free(dp); 153 return (0); 154 } 155 if (dp == cp) /* caller wants to always free() dp */ 156 dp = strdup(cp); 157 158 return (dp); 159 } 160 161 /* 162 * Determine if specified device is mounted at 163 * specified mount point. Returns 1 if mounted, 164 * 0 if not mounted, -1 on error. 165 */ 166 int 167 lf_ismounted(char *devname, char *dirname) 168 { 169 struct stat64 st; 170 char *blockname; /* name of block device */ 171 dev_t dev; 172 int saverr; 173 174 if ((blockname = unrawname(devname)) == NULL) { 175 msg(gettext("Cannot obtain block name from `%s'\n"), devname); 176 return (-1); 177 } 178 if (stat64(blockname, &st) < 0) { 179 saverr = errno; 180 msg(gettext("Cannot obtain status of device `%s': %s\n"), 181 blockname, strerror(saverr)); 182 free(blockname); 183 return (-1); 184 } 185 free(blockname); 186 dev = st.st_rdev; 187 if (stat64(dirname, &st) < 0) { 188 saverr = errno; 189 msg(gettext("Cannot obtain status of device `%s': %s\n"), 190 dirname, strerror(saverr)); 191 return (-1); 192 } 193 if (dev == st.st_dev) 194 return (1); 195 return (0); 196 } 197 198 #ifdef ENABLE_MMAP /* XXX mapped-file support */ 199 #define MINMAPSIZE 1024*1024 200 #define MAXMAPSIZE 1024*1024*32 201 202 static caddr_t mapbase; /* base of mapped data */ 203 static caddr_t mapend; /* last byte of mapped data */ 204 static size_t mapsize; /* amount of mapped data */ 205 /* 206 * Map a file prior to dumping and start faulting in its 207 * pages. Stop if we catch a signal indicating our turn 208 * to dump has arrived. If the file is truncated out from 209 * under us, immediately return. 210 * NB: the base of the mapped data may not coincide 211 * exactly to the requested offset, due to alignment 212 * constraints. 213 */ 214 caddr_t 215 mapfile(int fd, off_t offset, off_t bytes, int fetch) 216 { 217 /*LINTED [c used during pre-fetch faulting]*/ 218 volatile char c, *p; 219 int stride = (int)sysconf(_SC_PAGESIZE); 220 extern int caught; /* pre-fetch until set */ 221 caddr_t mapstart; /* beginning of file's mapped data */ 222 off_t mapoffset; /* page-aligned offset */ 223 int saverr; 224 225 mapbase = mapend = (caddr_t)0; 226 227 if (bytes == 0) 228 return ((caddr_t)0); 229 /* 230 * mmap the file for reading 231 */ 232 mapoffset = offset & ~(stride - 1); 233 /* LINTED: "bytes" will always fit into a size_t */ 234 mapsize = bytes + (offset - mapoffset); 235 if (mapsize > MAXMAPSIZE) 236 mapsize = MAXMAPSIZE; 237 while ((mapbase = mmap((caddr_t)0, mapsize, PROT_READ, 238 MAP_SHARED, fd, mapoffset)) == (caddr_t)-1 && 239 errno == ENOMEM && mapsize >= MINMAPSIZE) { 240 /* 241 * Due to address space limitations, we 242 * may not be able to map as much as we want. 243 */ 244 mapsize /= 2; /* exponential back-off */ 245 } 246 247 if (mapbase == (caddr_t)-1) { 248 saverr = errno; 249 msg(gettext("Cannot map file at inode `%lu' into memory: %s\n"), 250 ino, strerror(saverr)); 251 /* XXX why not call dumpailing() here? */ 252 if (!query(gettext( 253 "Do you want to attempt to continue? (\"yes\" or \"no\") "))) { 254 dumpabort(); 255 /*NOTREACHED*/ 256 } 257 mapbase = (caddr_t)0; 258 return ((caddr_t)0); 259 } 260 261 (void) madvise(mapbase, mapsize, MADV_SEQUENTIAL); 262 mapstart = mapbase + (offset - mapoffset); 263 mapend = mapbase + (mapsize - 1); 264 265 if (!fetch) 266 return (mapstart); 267 268 if (setjmp(truncate_buf) == 0) { 269 savebus = signal(SIGBUS, onsigbus); 270 /* 271 * Touch each page to pre-fetch by faulting. At least 272 * one of c or *p must be declared volatile, lest the 273 * optimizer eliminate the assignment in the loop. 274 */ 275 incopy = 1; 276 for (p = mapbase; !caught && p <= mapend; p += stride) { 277 /* LINTED: c is used for its side-effects */ 278 c = *p; 279 } 280 incopy = 0; 281 } 282 #ifdef DEBUG 283 else 284 /* XGETTEXT: #ifdef DEBUG only */ 285 msg(gettext( 286 "FILE TRUNCATED (fault): Interrupting pre-fetch\n")); 287 #endif 288 (void) signal(SIGBUS, savebus); 289 return (mapstart); 290 } 291 292 void 293 unmapfile(void) 294 { 295 if (mapbase) { 296 /* XXX we're unmapping it, so what does this gain us? */ 297 (void) msync(mapbase, mapsize, MS_ASYNC|MS_INVALIDATE); 298 (void) munmap(mapbase, mapsize); 299 mapbase = (caddr_t)0; 300 } 301 } 302 #endif /* ENABLE_MMAP */ 303 304 void 305 activepass(void) 306 { 307 static int passno = 1; /* active file pass number */ 308 char *ext, *old; 309 char buf[3000]; 310 static char defext[] = ".retry"; 311 312 if (pipeout) { 313 msg(gettext("Cannot re-dump active files to `%s'\n"), tape); 314 dumpabort(); 315 /*NOTREACHED*/ 316 } 317 318 if (active > 1) 319 (void) snprintf(buf, sizeof (buf), gettext( 320 "%d files were active and will be re-dumped\n"), active); 321 else 322 (void) snprintf(buf, sizeof (buf), gettext( 323 "1 file was active and will be re-dumped\n")); 324 msg(buf); 325 326 doingactive++; 327 active = 0; 328 reset(); /* reset tape params */ 329 spcl.c_ddate = spcl.c_date; /* chain with last dump/pass */ 330 331 /* 332 * If archiving, create a new 333 * archive file. 334 */ 335 if (archivefile) { 336 old = archivefile; 337 338 ext = strstr(old, defext); 339 if (ext != (char *)NULL) 340 *ext = '\0'; /* just want the base name */ 341 342 /* The two is for the trailing \0 and rounding up log10() */ 343 archivefile = xmalloc(strlen(old) + strlen(defext) + 344 (int)log10((double)passno) + 2); 345 346 /* Always fits */ 347 (void) sprintf(archivefile, "%s%s%d", old, defext, passno); 348 free(old); 349 } 350 351 if (tapeout) { 352 if (isrewind(to)) { 353 /* 354 * A "rewind" tape device. When we do 355 * the close, we will lose our position. 356 * Be nice and switch volumes. 357 */ 358 (void) snprintf(buf, sizeof (buf), gettext( 359 "Warning - cannot dump active files to rewind " 360 "device `%s'\n"), tape); 361 msg(buf); 362 close_rewind(); 363 changevol(); 364 } else { 365 trewind(); 366 doposition = 0; 367 filenum++; 368 } 369 } else { 370 /* 371 * Not a tape. Do a volume switch. 372 * This will advance to the next file 373 * if using a sequence of files, next 374 * diskette if using diskettes, or 375 * let the user move the old file out 376 * of the way. 377 */ 378 close_rewind(); 379 changevol(); /* switch files */ 380 } 381 (void) snprintf(buf, sizeof (buf), gettext( 382 "Dumping active files (retry pass %d) to `%s'\n"), passno, tape); 383 msg(buf); 384 passno++; 385 } 386