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 *
prdate(d)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__
inititimes(void)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
readitimes(df)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__
getitime(void)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__
putitime(void)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
recout(file,what)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
getrecord(df,idatep)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
makeidate(ip,buf)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
est(ip)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
bmapest(map)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
is_fssnap_dump(char * disk)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