1 /* @(#)dumpdate.c	1.32 19/01/19 Copyright 2003-2019 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)dumpdate.c	1.32 19/01/19 Copyright 2003-2019 J. Schilling";
6 #endif
7 /*
8  *	Copyright (c) 2003-2019 J. Schilling
9  */
10 /*
11  * The contents of this file are subject to the terms of the
12  * Common Development and Distribution License, Version 1.0 only
13  * (the "License").  You may not use this file except in compliance
14  * with the License.
15  *
16  * See the file CDDL.Schily.txt in this distribution for details.
17  * A copy of the CDDL is also available via the Internet at
18  * http://www.opensource.org/licenses/cddl1.txt
19  *
20  * When distributing Covered Code, include this CDDL HEADER in each
21  * file and include the License file CDDL.Schily.txt from this distribution.
22  */
23 
24 #include <schily/stdio.h>
25 #include <schily/stdlib.h>
26 #include <schily/unistd.h>
27 #include <schily/string.h>
28 #include <schily/errno.h>
29 #include <schily/utypes.h>
30 #include <schily/standard.h>
31 #include <schily/fcntl.h>
32 #define	GT_COMERR		/* #define comerr gtcomerr */
33 #define	GT_ERROR		/* #define error gterror   */
34 #include <schily/schily.h>
35 
36 #include "star.h"
37 #include "dumpdate.h"
38 #include "starsubs.h"
39 
40 #ifdef	HAVE_LARGEFILES
41 /*
42  * XXX Hack until fseeko()/ftello() are available everywhere or until
43  * XXX we know a secure way to let autoconf ckeck for fseeko()/ftello()
44  * XXX without defining FILE_OFFSETBITS to 64 in confdefs.h
45  */
46 #define	fseek	fseeko
47 #define	ftell	ftello
48 #endif
49 
50 #if	defined(HAVE_FLOCK) || defined(HAVE_LOCKF) || defined(HAVE_FCNTL_LOCKF)
51 #define	HAVE_LOCKING
52 #endif
53 
54 #ifndef	HAVE_FLOCK
55 #ifdef	HAVE_FCNTL_LOCKF
56 LOCAL	struct flock	__fl;
57 #define	flock(fd, flag)	(__fl.l_type = (flag), fcntl(fd, F_SETLKW, &__fl))
58 /*
59  * #undef before as AIX has left over junk from a no longer existing flock()
60  */
61 #undef	LOCK_EX
62 #define	LOCK_EX	F_WRLCK
63 #undef	LOCK_SH
64 #define	LOCK_SH	F_RDLCK
65 #undef	LOCK_UN
66 #define	LOCK_UN	F_UNLCK
67 #else
68 #define	flock(fd, flag)	lockf(fd, flag, (off_t)0)
69 #define	LOCK_EX	F_LOCK
70 #define	LOCK_SH	F_LOCK
71 #define	LOCK_UN	F_ULOCK
72 #endif
73 #endif	/* HAVE_FLOCK */
74 
75 #ifndef	HAVE_LOCKING
76 #undef	flock
77 #define	flock(fd, flag)	(0)
78 #endif
79 
80 LOCAL	dumpd_t	*dumpdates;
81 LOCAL	dumpd_t	**dumptail = &dumpdates;
82 
83 EXPORT	void	initdumpdates	__PR((char *fname, BOOL doupdate));
84 LOCAL	void	readdumpdates	__PR((FILE *f, char *fname));
85 EXPORT	void	writedumpdates	__PR((char *fname, const char *filesys,
86 							int level, int dflags,
87 							struct timespec *date));
88 LOCAL	void	outentry	__PR((FILE *f, char *name, int level, int dflags,
89 							struct timespec *date));
90 EXPORT	char	*dumpdate	__PR((struct timespec *date));
91 LOCAL	char	*skipwht	__PR((char *p));
92 LOCAL	BOOL	getentry	__PR((char *line, char *fname));
93 EXPORT	BOOL	getdumptime	__PR((char *p, struct timespec *tvp));
94 EXPORT	dumpd_t *checkdumpdates	__PR((const char *name, int level, int dflags));
95 LOCAL	dumpd_t *_checkdumpdates __PR((const char *name, int level, int dflags));
96 EXPORT	void	adddumpdates	__PR((const char *name, int level, int dflags,
97 							struct timespec *date,
98 								BOOL useold));
99 LOCAL	dumpd_t *newdumpdates	__PR((const char *name, int level, int dflags,
100 							struct timespec *date));
101 LOCAL	dumpd_t	*freedumpdates	__PR((dumpd_t *dp));
102 
103 EXPORT void
initdumpdates(fname,doupdate)104 initdumpdates(fname, doupdate)
105 	char	*fname;
106 	BOOL	doupdate;
107 {
108 	FILE	*f;
109 
110 	f = lfilemopen(fname, "r", S_IRWALL);
111 	if (f == NULL) {
112 		if (geterrno() == ENOENT) {
113 			errmsg("Warning no %s.\n", fname);
114 			return;
115 		}
116 		comerr("Cannot open %s.\n", fname);
117 		/* NOTREACHED */
118 	}
119 	if (doupdate && laccess(fname, W_OK) < 0)
120 		comerr("Cannot access '%s' for update\n", fname);
121 
122 	(void) flock(fdown(f), LOCK_SH);
123 	readdumpdates(f, fname);
124 	(void) flock(fdown(f), LOCK_UN);
125 	fclose(f);
126 }
127 
128 LOCAL void
readdumpdates(f,fname)129 readdumpdates(f, fname)
130 	FILE	*f;
131 	char	*fname;
132 {
133 	char	buf[4096];
134 	int	line = 0;
135 
136 	while (fgetline(f, buf, sizeof (buf)) >= 0) {
137 		line++;
138 
139 		if (!getentry(buf, fname)) {
140 			if (*skipwht(buf) != '\0') {
141 				errmsgno(EX_BAD,
142 					"Unknown format in '%s' line %d\n",
143 					fname, line);
144 			}
145 			continue;
146 		}
147 	}
148 }
149 
150 EXPORT void
writedumpdates(fname,filesys,level,dflags,date)151 writedumpdates(fname, filesys, level, dflags, date)
152 	char		*fname;
153 	const char	*filesys;
154 	int		level;
155 	int		dflags;
156 	struct timespec	*date;
157 {
158 	FILE	*f;
159 	dumpd_t	*dp;
160 	off_t	fsize;
161 	off_t	fpos;
162 
163 	f = lfilemopen(fname, "rwc", S_IRWALL);
164 	if (f == NULL) {
165 		errmsg("Cannot open '%s'.\n", fname);
166 		return;
167 	}
168 	(void) flock(fdown(f), LOCK_EX);
169 
170 	for (dp = dumpdates; dp; dp = freedumpdates(dp))
171 		;
172 	dumpdates = NULL;
173 	dumptail = &dumpdates;
174 
175 	readdumpdates(f, fname);
176 	adddumpdates(filesys, level, dflags, date, TRUE); /* Upd. curr. entry */
177 	fseek(f, 0L, SEEK_SET);
178 
179 	fsize = filesize(f);
180 	for (dp = dumpdates; dp; dp = dp->dd_next) {
181 		outentry(f, dp->dd_name, dp->dd_level,
182 				dp->dd_flags, &dp->dd_date);
183 	}
184 	fflush(f);
185 	fpos = filepos(f);
186 #ifdef	HAVE_FTRUNCATE
187 	if (fpos < fsize)
188 		ftruncate(fdown(f), fpos);
189 #else
190 	while (fpos++ < fsize)
191 		putc(' ', f);
192 #endif
193 
194 	(void) flock(fdown(f), LOCK_UN);
195 	fclose(f);
196 	error("Dump record  level %d%s dump: %s written\n",
197 			level, (dflags & DD_PARTIAL) ? "P":" ",
198 			dumpdate(date));
199 }
200 
201 LOCAL void
outentry(f,name,level,dflags,date)202 outentry(f, name, level, dflags, date)
203 	FILE		*f;
204 	char		*name;
205 	int		level;
206 	int		dflags;
207 	struct timespec	*date;
208 {
209 	int	len;
210 	time_t	t = date->tv_sec; /* FreeBSD/MacOS X -> broken tv_sec/time_t */
211 
212 	len = strlen(name);
213 	if ((len % 8) == 0)
214 		len += 8;
215 	len += 7;
216 	len &= ~7;
217 	len = 41 -len;
218 	if (len < 1)
219 		len = 1;
220 
221 	fprintf(f, "%s\t%*s%2d%s %10lld.%9.9lld %s",
222 		name,
223 		len,
224 		"",
225 		level,
226 		(dflags & DD_PARTIAL) ? "P":"",
227 		(Llong)date->tv_sec,
228 		(Llong)date->tv_nsec,
229 		ctime(&t));
230 }
231 
232 EXPORT char *
dumpdate(date)233 dumpdate(date)
234 	struct timespec	*date;
235 {
236 	char	*p;
237 	time_t	t = date->tv_sec; /* FreeBSD/MacOS X -> broken tv_sec/time_t */
238 
239 	if (date->tv_sec == 0)
240 		return ("the epoch");
241 	p = ctime(&t);
242 	p[24] = '\0';
243 	return (p);
244 }
245 
246 LOCAL char *
skipwht(p)247 skipwht(p)
248 	char	*p;
249 {
250 	while (*p && (*p == ' ' || *p == '\t'))
251 		p++;
252 
253 	return (p);
254 }
255 
256 LOCAL BOOL
getentry(line,fname)257 getentry(line, fname)
258 	char	*line;
259 	char	*fname;
260 {
261 	char	*p;
262 	int	level;
263 	int	dflags = 0;
264 	struct timespec	tdate;
265 
266 	p = line;
267 	do {
268 		p = strchr(++p, '\t');
269 	} while (p && p[0] != '\0' && p[1] != ' ');
270 
271 	if (p == NULL || p[0] == '\0') {
272 		errmsgno(EX_BAD, "Error parsing mount point in '%s'\n", fname);
273 		return (FALSE);
274 	}
275 	*p++ = '\0';
276 
277 	p = skipwht(p);
278 	p = astoi(p, &level);
279 	if (*p == 'P') {
280 		dflags |= DD_PARTIAL;
281 		p++;
282 	}
283 	if (*p != ' ') {
284 		errmsgno(EX_BAD, "Error parsing dump level in '%s'\n", fname);
285 		return (FALSE);
286 	}
287 	p = skipwht(p);
288 	if (!getdumptime(p, &tdate))
289 		return (FALSE);
290 	adddumpdates(line, level, dflags, &tdate, FALSE);
291 
292 	return (TRUE);
293 }
294 
295 EXPORT BOOL
getdumptime(p,tvp)296 getdumptime(p, tvp)
297 	char		*p;
298 	struct timespec	*tvp;
299 {
300 	Llong		date;
301 	Llong		dfrac = 0;
302 
303 	p = astollb(p, &date, 10);
304 	if (*p == '.') {
305 		int	l;
306 		char	*s = ++p;
307 
308 		p = astollb(s, &dfrac, 10);
309 		l = p - s;
310 		if (l > 9) {		/* Number too big, limit to nsec */
311 			char	*p2 = p;
312 
313 			l = s[9];
314 			s[9] = '\0';
315 			p = astollb(s, &dfrac, 10);
316 			s[9] = l;
317 			l = p - s;
318 			p = p2;
319 		}
320 		while (l < 9) {		/* Convert to nsecs */
321 			dfrac  *= 10;
322 			l++;
323 		}
324 		while (l > 9) {		/* Convert to nsecs */
325 			dfrac  /= 10;
326 			l--;
327 		}
328 	}
329 	if (*p != ' ' && *p != '\0') {
330 		errmsgno(EX_BAD, "Error parsing dump date\n");
331 		return (FALSE);
332 	}
333 	tvp->tv_sec = date;
334 	tvp->tv_nsec = dfrac;
335 	return (TRUE);
336 }
337 
338 EXPORT dumpd_t *
checkdumpdates(name,level,dflags)339 checkdumpdates(name, level, dflags)
340 	const char	*name;
341 	int		level;
342 	int		dflags;
343 {
344 	dumpd_t	*rp = NULL;
345 	dumpd_t	*rp2 = NULL;
346 
347 	rp = _checkdumpdates(name, level, dflags & ~DD_CUMULATIVE);
348 	rp2 = _checkdumpdates(name, level, dflags);
349 
350 	if (rp && rp2 && (rp2->dd_date.tv_sec > rp->dd_date.tv_sec))
351 		return (rp2);
352 	return (rp);
353 }
354 
355 LOCAL dumpd_t *
_checkdumpdates(name,level,dflags)356 _checkdumpdates(name, level, dflags)
357 	const char	*name;
358 	int		level;
359 	int		dflags;
360 {
361 	dumpd_t	*dp = dumpdates;
362 	dumpd_t	*rp = NULL;
363 
364 	for (; dp; dp = dp->dd_next) {
365 		if (!streql(name, dp->dd_name))
366 			continue;
367 		if ((dp->dd_flags & DD_PARTIAL) != (dflags & DD_PARTIAL))
368 			continue;
369 		if ((dflags & DD_CUMULATIVE) && level == dp->dd_level) {
370 			rp = dp;
371 			break;
372 		}
373 		/*
374 		 * We are not interested in tardump entries for
375 		 * this level or higher, so skip them.
376 		 */
377 		if (level <= dp->dd_level)
378 			continue;
379 
380 		/*
381 		 * If we did already find a more recent entry in tardumps
382 		 * we consider this one outdated.
383 		 */
384 		if (rp && rp->dd_date.tv_sec > dp->dd_date.tv_sec)
385 			continue;
386 		rp = dp;
387 	}
388 #ifdef	DEBUG
389 	if (rp)
390 		outentry(stderr, rp->dd_name, rp->dd_level, rp->dd_flags, &rp->dd_date);
391 #endif
392 	return (rp);
393 }
394 
395 EXPORT void
adddumpdates(name,level,dflags,date,useold)396 adddumpdates(name, level, dflags, date, useold)
397 	const char	*name;
398 	int		level;
399 	int		dflags;
400 	struct timespec	*date;
401 	BOOL		useold;
402 {
403 	dumpd_t	*dp = dumpdates;
404 
405 	for (; dp; dp = dp->dd_next) {
406 		if (streql(name, dp->dd_name) && level == dp->dd_level &&
407 		    (dp->dd_flags & DD_PARTIAL) == (dflags & DD_PARTIAL)) {
408 			if (useold) {
409 				dp->dd_date = *date;
410 				return;
411 			}
412 			errmsgno(EX_BAD,
413 				"Duplicate tardumps entry '%s %d%s %lld'.\n",
414 				dp->dd_name, dp->dd_level,
415 				(dflags & DD_PARTIAL) ? "P":"",
416 				(Llong)dp->dd_date.tv_sec);
417 
418 			if (date->tv_sec == dp->dd_date.tv_sec)
419 				return;
420 			comerrno(EX_BAD, "Timestamps differs - aborting.\n");
421 		}
422 	}
423 	dp = newdumpdates(name, level, dflags, date);
424 	*dumptail = dp;
425 	dumptail = &dp->dd_next;
426 }
427 
428 LOCAL dumpd_t *
newdumpdates(name,level,dflags,date)429 newdumpdates(name, level, dflags, date)
430 	const char	*name;
431 	int		level;
432 	int		dflags;
433 	struct timespec	*date;
434 {
435 	dumpd_t	*dp;
436 
437 	dp		= ___malloc(sizeof (*dp), "tardumps entry");
438 	dp->dd_next	= NULL;
439 	dp->dd_name	= ___savestr(name);
440 	dp->dd_level	= level;
441 	dp->dd_flags	= dflags;
442 	dp->dd_date	= *date;
443 
444 	return (dp);
445 }
446 
447 LOCAL dumpd_t *
freedumpdates(dp)448 freedumpdates(dp)
449 	dumpd_t	*dp;
450 {
451 	dumpd_t	*next = dp->dd_next;
452 
453 	free(dp->dd_name);
454 	free(dp);
455 
456 	return (next);
457 }
458