xref: /illumos-gate/usr/src/cmd/backup/dump/dumpitime.c (revision e86372a01d2d16a5dd4a64e144ed978ba17fe7dd)
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