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