Merge tag 'upstream/3.3.3'
[debian/amanda] / client-src / amandates.c
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-2012 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 /var/lib/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
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
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
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 *
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 *
244 amandates_lookup(
245     char *      name)
246 {
247     return lookup(name, 1);
248 }
249
250 static void
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
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
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("/var/lib/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 }