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