1 /* 2 * Copyright 1996-1998, 2002-2003 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1980 Regents of the University of California. 11 * All rights reserved. The Berkeley software License Agreement 12 * specifies the terms and conditions for redistribution. 13 */ 14 15 #pragma ident "%Z%%M% %I% %E% SMI" 16 17 #include "dump.h" 18 19 #ifndef LOCK_EX 20 static struct flock fl; 21 #define flock(fd, flag) (fl.l_type = (flag), fcntl(fd, F_SETLKW, &fl)) 22 #define LOCK_EX F_WRLCK 23 #define LOCK_SH F_RDLCK 24 #define LOCK_UN F_UNLCK 25 #endif 26 27 /* 28 * Print a date. A date of 0 is the beginning of time (the "epoch"). 29 * If the 2nd argument is non-zero, it is ok to format the date in 30 * locale-specific form, otherwise we use ctime. We must use ctime 31 * for dates such as those in the dumpdates file, which must be 32 * locale-independent. 33 */ 34 char * 35 prdate(d) 36 time_t d; 37 { 38 static char buf[256]; 39 struct tm *tm; 40 char *p; 41 42 if (d == 0) 43 return (gettext("the epoch")); 44 45 tm = localtime(&d); 46 if (strftime(buf, sizeof (buf), "%c", tm) != 0) { 47 p = buf; 48 } else { 49 /* Wouldn't fit in buf, fall back */ 50 p = ctime(&d); 51 p[24] = '\0'; /* lose trailing newline */ 52 } 53 return (p); 54 } 55 56 struct idates **idatev = 0; 57 size_t nidates = 0; 58 static int idates_in = 0; /* we have read the increment file */ 59 static int recno; 60 61 #ifdef __STDC__ 62 static void readitimes(FILE *); 63 static void recout(FILE *, struct idates *); 64 static int getrecord(FILE *, struct idates *); 65 static int makeidate(struct idates *, char *); 66 #else 67 static void readitimes(); 68 static void recout(); 69 static int getrecord(); 70 static int makeidate(); 71 #endif 72 73 void 74 #ifdef __STDC__ 75 inititimes(void) 76 #else 77 inititimes() 78 #endif 79 { 80 FILE *df; 81 int saverr; 82 83 if (idates_in) 84 return; 85 if (increm == NULL || *increm == '\0') { 86 msg(gettext("inititimes: No dump record file name defined\n")); 87 dumpabort(); 88 /*NOTREACHED*/ 89 } 90 /* 91 * No need to secure this, as increm is hard-coded to NINCREM, 92 * and that file is in /etc. If random people have write-permission 93 * there, then there are more problems than any degree of paranoia 94 * on our part can fix. 95 */ 96 if ((df = fopen(increm, "r")) == NULL) { 97 saverr = errno; 98 if (errno == ENOENT) 99 msg(gettext( 100 "Warning - dump record file `%s' does not exist\n"), 101 increm); 102 else { 103 msg(gettext("Cannot open dump record file `%s': %s\n"), 104 increm, strerror(saverr)); 105 dumpabort(); 106 /*NOTREACHED*/ 107 } 108 return; 109 } 110 if (uflag && access(increm, W_OK) < 0) { 111 msg(gettext("Cannot access dump record file `%s' for update\n"), 112 increm); 113 dumpabort(); 114 /*NOTREACHED*/ 115 } 116 (void) flock(fileno(df), LOCK_SH); 117 readitimes(df); 118 (void) fclose(df); 119 } 120 121 static void 122 readitimes(df) 123 FILE *df; 124 { 125 struct idates *idp; 126 127 recno = 0; 128 for (;;) { 129 idp = (struct idates *)xcalloc(1, sizeof (*idp)); 130 if (getrecord(df, idp) < 0) { 131 free((char *)idp); 132 break; 133 } 134 nidates++; 135 idatev = (struct idates **)xrealloc((void *)idatev, 136 nidates * (size_t)sizeof (*idatev)); 137 idatev[nidates - 1] = idp; 138 } 139 /* LINTED: assigned value is used in inititimes */ 140 idates_in = 1; 141 } 142 143 void 144 #ifdef __STDC__ 145 getitime(void) 146 #else 147 getitime() 148 #endif 149 { 150 struct idates *ip; 151 int i; 152 char *fname; 153 154 /* 155 * if an alternate name was specified via the N flag, use it instead 156 * of the disk name. 157 */ 158 if (dname != NULL) 159 fname = dname; 160 else 161 fname = disk; 162 163 #ifdef FDEBUG 164 165 /* XGETTEXT: #ifdef FDEBUG only */ 166 msg(gettext("Looking for name %s in increm = %s for delta = %c\n"), 167 fname, increm, (uchar_t)incno); 168 #endif 169 spcl.c_ddate = 0; 170 lastincno = '0'; 171 172 inititimes(); 173 if (idatev == 0) 174 return; 175 /* 176 * Go find the entry with the same name for a lower increment 177 * and older date 178 */ 179 ITITERATE(i, ip) { 180 if (strncmp(fname, ip->id_name, sizeof (ip->id_name)) != 0) 181 continue; 182 if (ip->id_incno >= incno) 183 continue; 184 if (ip->id_ddate <= spcl.c_ddate) 185 continue; 186 spcl.c_ddate = ip->id_ddate; 187 lastincno = ip->id_incno; 188 } 189 } 190 191 void 192 #ifdef __STDC__ 193 putitime(void) 194 #else 195 putitime() 196 #endif 197 { 198 FILE *df; 199 struct idates *itwalk; 200 int i; 201 int fd, saverr; 202 char *fname; 203 204 if (uflag == 0) 205 return; 206 if ((df = safe_fopen(increm, "r+", 0664)) == (FILE *)NULL) { 207 msg("%s: %s\n", increm, strerror(errno)); 208 (void) unlink(increm); 209 dumpabort(); 210 /*NOTREACHED*/ 211 } 212 fd = fileno(df); 213 (void) flock(fd, LOCK_EX); 214 215 /* 216 * if an alternate name was specified via the N flag, use it instead 217 * of the disk name. 218 */ 219 if (dname != NULL) 220 fname = dname; 221 else 222 fname = disk; 223 224 if (idatev != 0) { 225 for (i = 0; i < nidates && idatev[i] != 0; i++) 226 free((char *)idatev[i]); 227 free((char *)idatev); 228 } 229 idatev = 0; 230 nidates = 0; 231 readitimes(df); 232 if (fseek(df, 0L, 0) < 0) { /* rewind() was redefined in dumptape.c */ 233 saverr = errno; 234 msg(gettext("%s: %s error:\n"), 235 increm, "fseek", strerror(saverr)); 236 dumpabort(); 237 /*NOTREACHED*/ 238 } 239 spcl.c_ddate = 0; 240 /* LINTED: won't dereference idatev if it is NULL (see readitimes) */ 241 ITITERATE(i, itwalk) { 242 if (strncmp(fname, itwalk->id_name, 243 sizeof (itwalk->id_name)) != 0) 244 continue; 245 if (itwalk->id_incno != incno) 246 continue; 247 goto found; 248 } 249 /* 250 * Add one more entry to idatev 251 */ 252 nidates++; 253 idatev = (struct idates **)xrealloc((void *)idatev, 254 nidates * (size_t)sizeof (struct idates *)); 255 itwalk = idatev[nidates - 1] = 256 (struct idates *)xcalloc(1, sizeof (*itwalk)); 257 found: 258 (void) strncpy(itwalk->id_name, fname, sizeof (itwalk->id_name)); 259 itwalk->id_name[sizeof (itwalk->id_name) - 1] = '\0'; 260 itwalk->id_incno = incno; 261 itwalk->id_ddate = spcl.c_date; 262 263 ITITERATE(i, itwalk) { 264 recout(df, itwalk); 265 } 266 if (ftruncate64(fd, ftello64(df))) { 267 saverr = errno; 268 msg(gettext("%s: %s error:\n"), 269 increm, "ftruncate64", strerror(saverr)); 270 dumpabort(); 271 /*NOTREACHED*/ 272 } 273 (void) fclose(df); 274 msg(gettext("Level %c dump on %s\n"), 275 (uchar_t)incno, prdate(spcl.c_date)); 276 } 277 278 static void 279 recout(file, what) 280 FILE *file; 281 struct idates *what; 282 { 283 time_t ddate = what->id_ddate; 284 /* must use ctime, so we can later use unctime() */ 285 (void) fprintf(file, DUMPOUTFMT, 286 what->id_name, 287 (uchar_t)what->id_incno, 288 ctime(&ddate)); 289 } 290 291 static int 292 getrecord(df, idatep) 293 FILE *df; 294 struct idates *idatep; 295 { 296 char buf[BUFSIZ]; 297 298 if ((fgets(buf, BUFSIZ, df)) != buf) 299 return (-1); 300 recno++; 301 if (makeidate(idatep, buf) < 0) { 302 msg(gettext( 303 "Malformed entry in dump record file `%s', line %d\n"), 304 increm, recno); 305 if (strcmp(increm, NINCREM)) { 306 msg(gettext("`%s' not a dump record file\n"), increm); 307 dumpabort(); 308 /*NOTREACHED*/ 309 } 310 return (-1); 311 } 312 313 #ifdef FDEBUG 314 msg("getrecord: %s %c %s\n", 315 idatep->id_name, 316 (uchar_t)idatep->id_incno, 317 prdate(idatep->id_ddate)); 318 #endif 319 return (0); 320 } 321 322 static int 323 makeidate(ip, buf) 324 struct idates *ip; 325 char *buf; 326 { 327 char un_buf[128]; /* size must be >= second one in DUMPINFMT */ 328 329 /* 330 * MAXNAMLEN has different values in dirent.h and ufs_fsdir.h, 331 * and we need to ensure that the length in DUMPINFMT matches 332 * what we allow for. Can't just use MAXNAMLEN in the test, 333 * because there's no convenient way to substitute it into 334 * DUMPINFMT. 335 * XXX There's got to be a better way. 336 */ 337 /*LINTED [assertion always true]*/ 338 assert(sizeof (ip->id_name) == (255 + 3)); 339 340 if (sscanf(buf, DUMPINFMT, ip->id_name, &ip->id_incno, un_buf) != 3) 341 return (-1); 342 /* LINTED casting from 64-bit to 32-bit time */ 343 ip->id_ddate = (time32_t)unctime(un_buf); 344 if (ip->id_ddate < 0) 345 return (-1); 346 return (0); 347 } 348 349 /* 350 * This is an estimation of the number of tp_bsize blocks in the file. 351 * It estimates the number of blocks in files with holes by assuming 352 * that all of the blocks accounted for by di_blocks are data blocks 353 * (when some of the blocks are usually used for indirect pointers); 354 * hence the estimate may be high. 355 */ 356 void 357 est(ip) 358 struct dinode *ip; 359 { 360 u_offset_t s, t; 361 362 /* 363 * ip->di_size is the size of the file in bytes. 364 * ip->di_blocks stores the number of sectors actually in the file. 365 * If there are more sectors than the size would indicate, this just 366 * means that there are indirect blocks in the file or unused 367 * sectors in the last file block; we can safely ignore these 368 * (s = t below). 369 * If the file is bigger than the number of sectors would indicate, 370 * then the file has holes in it. In this case we must use the 371 * block count to estimate the number of data blocks used, but 372 * we use the actual size for estimating the number of indirect 373 * dump blocks (t vs. s in the indirect block calculation). 374 */ 375 o_esize++; 376 s = (unsigned)(ip->di_blocks) / (unsigned)(tp_bsize / DEV_BSIZE); 377 /* LINTED: spurious complaint about sign-extending 32 to 64 bits */ 378 t = d_howmany(ip->di_size, (unsigned)tp_bsize); 379 if (s > t) 380 s = t; 381 if (ip->di_size > (u_offset_t)((unsigned)(sblock->fs_bsize) * NDADDR)) { 382 /* calculate the number of indirect blocks on the dump tape */ 383 /* LINTED: spurious complaint sign-extending 32 to 64 bits */ 384 s += d_howmany(t - 385 (unsigned)(NDADDR * sblock->fs_bsize / tp_bsize), 386 (unsigned)TP_NINDIR); 387 } 388 f_esize += s; 389 } 390 391 /*ARGSUSED*/ 392 void 393 bmapest(map) 394 uchar_t *map; 395 { 396 o_esize++; 397 /* LINTED: spurious complaint sign-extending 32 to 64 bits */ 398 f_esize += d_howmany(msiz * sizeof (map[0]), (unsigned)tp_bsize); 399 } 400 401 402 /* 403 * Check to see if what we are trying to dump is a fs snapshot 404 * If so, we can use the snapshot's create time to populate 405 * the dumpdates file, instead of the time of the dump. 406 */ 407 time32_t 408 is_fssnap_dump(char *disk) 409 { 410 struct stat st; 411 char *last; 412 int snapnum; 413 kstat_ctl_t *kslib; 414 kstat_t *ksnum; 415 kstat_named_t *numval; 416 417 last = basename(disk); 418 if ((strstr(disk, SNAP_NAME) == NULL) || (stat(disk, &st) == -1) || 419 (isdigit(last[0]) == 0)) 420 return (0); 421 422 snapnum = atoi(last); 423 424 if ((kslib = kstat_open()) == NULL) 425 return (0); 426 427 ksnum = kstat_lookup(kslib, SNAP_NAME, snapnum, FSSNAP_KSTAT_NUM); 428 if (ksnum == NULL) { 429 (void) kstat_close(kslib); 430 return (0); 431 } 432 433 if (kstat_read(kslib, ksnum, NULL) == -1) { 434 (void) kstat_close(kslib); 435 return (0); 436 } 437 438 numval = kstat_data_lookup(ksnum, FSSNAP_KSTAT_NUM_CREATETIME); 439 if (numval == NULL) { 440 (void) kstat_close(kslib); 441 return (0); 442 } 443 444 (void) kstat_close(kslib); 445 /* LINTED casting from long to 32-bit time */ 446 return (time32_t)(numval->value.l & INT_MAX); 447 } 448