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