1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * Copyright (c) 2007-2013 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Authors: the Amanda Development Team.  Its members are listed in a
25  * file named AUTHORS, in the root directory of this distribution.
26  */
27 /*
28  * $Id: amandates.c,v 1.21 2006/07/25 18:35:21 martinea Exp $
29  *
30  * manage amandates file, that mimics /etc/dumpdates, but stores
31  * GNUTAR dates
32  */
33 
34 #include "amanda.h"
35 #include "getfsent.h"
36 #include "util.h"
37 
38 #include "amandates.h"
39 
40 static amandates_t *amandates_list = NULL;
41 static FILE *amdf = NULL;
42 static int updated, readonly;
43 static char *g_amandates_file = NULL;
44 static void import_dumpdates(amandates_t *);
45 static void enter_record(char *, int , time_t);
46 static amandates_t *lookup(char *name, int import);
47 
48 int
start_amandates(char * amandates_file,int open_readwrite)49 start_amandates(
50     char *amandates_file,
51     int	  open_readwrite)
52 {
53     int rc, level = 0;
54     long ldate = 0L;
55     char *line;
56     char *name;
57     char *s;
58     int ch;
59     char *qname;
60 
61     if (amandates_file == NULL) {
62 	errno = 0;
63 	return 0;
64     }
65 
66     /* clean up from previous invocation */
67 
68     if(amdf != NULL)
69 	finish_amandates();
70     if(amandates_list != NULL)
71 	free_amandates();
72     amfree(g_amandates_file);
73 
74     /* initialize state */
75 
76     updated = 0;
77     readonly = !open_readwrite;
78     amdf = NULL;
79     amandates_list = NULL;
80     g_amandates_file = stralloc(amandates_file);
81     /* open the file */
82 
83     if (access(amandates_file,F_OK))
84 	/* not yet existing */
85 	if ( (rc = open(amandates_file,(O_CREAT|O_RDWR),0644)) != -1 )
86 	    /* open/create successfull */
87 	    aclose(rc);
88 
89     if(open_readwrite)
90 	amdf = fopen(amandates_file, "r+");
91     else
92 	amdf = fopen(amandates_file, "r");
93 
94     /* create it if we need to */
95 
96     if(amdf == NULL && (errno == EINTR || errno == ENOENT) && open_readwrite)
97 	amdf = fopen(amandates_file, "w");
98 
99     if(amdf == NULL)
100 	return 0;
101 
102     if(open_readwrite)
103 	rc = amflock(fileno(amdf), amandates_file);
104     else
105 	rc = amroflock(fileno(amdf), amandates_file);
106 
107     if(rc == -1) {
108 	error(_("could not lock %s: %s"), amandates_file, strerror(errno));
109 	/*NOTREACHED*/
110     }
111 
112     for(; (line = agets(amdf)) != NULL; free(line)) {
113 	if (line[0] == '\0')
114 	    continue;
115 	s = line;
116 	ch = *s++;
117 
118 	skip_whitespace(s, ch);
119 	if(ch == '\0') {
120 	    continue;				/* no name field */
121 	}
122 	qname = s - 1;
123 	skip_quoted_string(s, ch);
124 	s[-1] = '\0';				/* terminate the name */
125 	name = unquote_string(qname);
126 
127 	skip_whitespace(s, ch);
128 	if(ch == '\0' || sscanf(s - 1, "%d %ld", &level, &ldate) != 2) {
129 	    amfree(name);
130 	    continue;				/* no more fields */
131 	}
132 
133 	if(level < 0 || level >= DUMP_LEVELS) {
134 	    amfree(name);
135 	    continue;
136 	}
137 
138 	enter_record(name, level, (time_t) ldate);
139 	amfree(name);
140     }
141 
142     if(ferror(amdf)) {
143 	error(_("reading %s: %s"), amandates_file, strerror(errno));
144 	/*NOTREACHED*/
145     }
146 
147     updated = 0;	/* reset updated flag */
148     return 1;
149 }
150 
151 void
finish_amandates(void)152 finish_amandates(void)
153 {
154     amandates_t *amdp;
155     int level;
156     char *qname;
157 
158     if(amdf == NULL)
159 	return;
160 
161     if(updated) {
162 	if(readonly) {
163 	    error(_("updated amandates after opening readonly"));
164 	    /*NOTREACHED*/
165 	}
166 
167 	rewind(amdf);
168 	for(amdp = amandates_list; amdp != NULL; amdp = amdp->next) {
169 	    for(level = 0; level < DUMP_LEVELS; level++) {
170 		if(amdp->dates[level] == EPOCH) continue;
171 		qname = quote_string(amdp->name);
172 		g_fprintf(amdf, "%s %d %ld\n",
173 			qname, level, (long) amdp->dates[level]);
174 		amfree(qname);
175 	    }
176 	}
177     }
178 
179     if(amfunlock(fileno(amdf), g_amandates_file) == -1) {
180 	error(_("could not unlock %s: %s"), g_amandates_file, strerror(errno));
181 	/*NOTREACHED*/
182     }
183     if (fclose(amdf) == EOF) {
184 	error(_("error [closing %s: %s]"), g_amandates_file, strerror(errno));
185 	/*NOTREACHED*/
186     }
187     amdf = NULL;
188 }
189 
190 void
free_amandates(void)191 free_amandates(void)
192 {
193     amandates_t *amdp, *nextp;
194 
195     for(amdp = amandates_list; amdp != NULL; amdp = nextp) {
196 	nextp = amdp->next;
197 	amfree(amdp->name);
198 	amfree(amdp);
199     }
200     amandates_list = NULL;
201 }
202 
203 static amandates_t *
lookup(char * name,int import)204 lookup(
205     char *	name,
206     int		import)
207 {
208     amandates_t *prevp, *amdp;
209     int rc, level;
210 
211     (void)import;	/* Quiet unused parameter warning */
212     rc = 0;
213 
214     prevp = NULL;
215     amdp = amandates_list;
216     while (amdp != NULL) {
217 	if ((rc = strcmp(name, amdp->name)) <= 0)
218 	    break;
219 	prevp = amdp;
220 	amdp = amdp->next;
221     }
222     if (!(amdp && (rc == 0))) {
223 	amandates_t *newp = alloc(SIZEOF(amandates_t));
224 	newp->name = stralloc(name);
225 	for (level = 0; level < DUMP_LEVELS; level++)
226 	    newp->dates[level] = EPOCH;
227 	newp->next = amdp;
228 	if (prevp != NULL) {
229 #ifndef __lint	/* Remove complaint about NULL pointer assignment */
230 	    prevp->next = newp;
231 #else
232 	    (void)prevp;
233 #endif
234 	} else {
235 	    amandates_list = newp;
236 	}
237 	import_dumpdates(newp);
238 	return newp;
239     }
240     return amdp;
241 }
242 
243 amandates_t *
amandates_lookup(char * name)244 amandates_lookup(
245     char *	name)
246 {
247     return lookup(name, 1);
248 }
249 
250 static void
enter_record(char * name,int level,time_t dumpdate)251 enter_record(
252     char *	name,
253     int		level,
254     time_t	dumpdate)
255 {
256     amandates_t *amdp;
257     char *qname;
258 
259     amdp = lookup(name, 0);
260 
261     if(level < 0 || level >= DUMP_LEVELS || dumpdate < amdp->dates[level]) {
262 	qname = quote_string(name);
263 	/* this is not allowed, but we can ignore it */
264         dbprintf(_("amandates botch: %s lev %d: new dumpdate %ld old %ld\n"),
265 		  qname, level, (long) dumpdate, (long) amdp->dates[level]);
266 	amfree(qname);
267 	return;
268     }
269 
270     amdp->dates[level] = dumpdate;
271 }
272 
273 
274 void
amandates_updateone(char * name,int level,time_t dumpdate)275 amandates_updateone(
276     char *	name,
277     int		level,
278     time_t	dumpdate)
279 {
280     amandates_t *amdp;
281     char *qname;
282 
283     assert(!readonly);
284 
285     amdp = lookup(name, 1);
286 
287     if(level < 0 || level >= DUMP_LEVELS || dumpdate < amdp->dates[level]) {
288 	/* this is not allowed, but we can ignore it */
289 	qname = quote_string(name);
290 	dbprintf(_("amandates updateone: %s lev %d: new dumpdate %ld old %ld"),
291 		  name, level, (long) dumpdate, (long) amdp->dates[level]);
292 	amfree(qname);
293 	return;
294     }
295 
296     amdp->dates[level] = dumpdate;
297     updated = 1;
298 }
299 
300 
301 /* -------------------------- */
302 
303 static void
import_dumpdates(amandates_t * amdp)304 import_dumpdates(
305     amandates_t *	amdp)
306 {
307     char *devname;
308     char *line;
309     char *fname;
310     int level = 0;
311     time_t dumpdate;
312     FILE *dumpdf;
313     char *s;
314     int ch;
315 
316     devname = amname_to_devname(amdp->name);
317 
318     if((dumpdf = fopen("/etc/dumpdates", "r")) == NULL) {
319 	amfree(devname);
320 	return;
321     }
322 
323     for(; (line = agets(dumpdf)) != NULL; free(line)) {
324 	if (line[0] == '\0')
325 	    continue;
326 	s = line;
327 	ch = *s++;
328 
329 	skip_whitespace(s, ch);
330 	if(ch == '\0') {
331 	    continue;				/* no fname field */
332 	}
333 	fname = s - 1;
334 	skip_non_whitespace(s, ch);
335 	s[-1] = '\0';				/* terminate fname */
336 
337 	skip_whitespace(s, ch);
338 	if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
339 	    continue;				/* no level field */
340 	}
341 	skip_integer(s, ch);
342 
343 	skip_whitespace(s, ch);
344 	if(ch == '\0') {
345 	    continue;				/* no dumpdate field */
346 	}
347 	dumpdate = unctime(s-1);
348 
349 	if(strcmp(fname, devname) != 0 || level < 0 || level >= DUMP_LEVELS) {
350 	    continue;
351 	}
352 
353 	if(dumpdate != -1 && dumpdate > amdp->dates[level]) {
354 	    if(!readonly) updated = 1;
355 	    amdp->dates[level] = dumpdate;
356 	}
357     }
358     afclose(dumpdf);
359     amfree(devname);
360 }
361