Imported Upstream version 2.4.5
[debian/amanda] / server-src / holding.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: holding.c,v 1.17.2.12.4.3.2.10.2.2 2004/10/21 13:12:29 martinea Exp $
28  *
29  * Functions to access holding disk
30  */
31
32 #include "amanda.h"
33 #include "util.h"
34 #include "holding.h"
35 #include "fileheader.h"
36 #include "util.h"
37 #include "logfile.h"
38
39 static sl_t *scan_holdingdisk P((sl_t *holding_list, char *diskdir, int verbose));
40
41 int is_dir(fname)
42 char *fname;
43 {
44     struct stat statbuf;
45
46     if(stat(fname, &statbuf) == -1) return 0;
47
48     return (statbuf.st_mode & S_IFDIR) == S_IFDIR;
49 }
50
51 int is_emptyfile(fname)
52 char *fname;
53 {
54     struct stat statbuf;
55
56     if(stat(fname, &statbuf) == -1) return 0;
57
58     return (statbuf.st_mode & S_IFDIR) != S_IFDIR && statbuf.st_size == 0;
59 }
60
61 int is_datestr(fname)
62 char *fname;
63 /* sanity check on datestamp of the form YYYYMMDD or YYYYMMDDhhmmss*/
64 {
65     char *cp;
66     int ch, num, date, year, month, hour, minute, second;
67     char ymd[9], hms[7];
68
69     /* must be 8 digits */
70     for(cp = fname; (ch = *cp) != '\0'; cp++) {
71         if(!isdigit(ch)) {
72             break;
73         }
74     }
75     if(ch != '\0' || (cp-fname != 8 && cp-fname != 14)) {
76         return 0;
77     }
78
79     /* sanity check year, month, and day */
80
81     strncpy(ymd, fname, 8);
82     ymd[8] = '\0';
83     num = atoi(ymd);
84     year = num / 10000;
85     month = (num / 100) % 100;
86     date = num % 100;
87     if(year<1990 || year>2100 || month<1 || month>12 || date<1 || date>31)
88         return 0;
89
90     if(cp-fname == 8)
91         return 1;
92
93     /* sanity check hour, minute, and second */
94     strncpy(hms, fname+8, 6);
95     hms[6] = '\0';
96     num = atoi(hms);
97     hour = num / 10000;
98     minute = (num / 100) % 100;
99     second = num % 100;
100     if(hour> 23 || minute>59 || second>59)
101         return 0;
102
103     /* yes, we passed all the checks */
104
105     return 1;
106 }
107
108
109 int non_empty(fname)
110 char *fname;
111 {
112     DIR *dir;
113     struct dirent *entry;
114     int gotentry;
115
116     if((dir = opendir(fname)) == NULL)
117         return 0;
118
119     gotentry = 0;
120     while(!gotentry && (entry = readdir(dir)) != NULL) {
121         gotentry = !is_dot_or_dotdot(entry->d_name);
122     }
123
124     closedir(dir);
125     return gotentry;
126 }
127
128
129 sl_t *scan_holdingdisk(holding_list, diskdir, verbose)
130 sl_t *holding_list;
131 char *diskdir;
132 int verbose;
133 {
134     DIR *topdir;
135     struct dirent *workdir;
136     char *entryname = NULL;
137
138     if((topdir = opendir(diskdir)) == NULL) {
139         if(verbose && errno != ENOENT)
140            printf("Warning: could not open holding dir %s: %s\n",
141                   diskdir, strerror(errno));
142         return holding_list;
143     }
144
145     /* find all directories of the right format  */
146
147     if(verbose)
148         printf("Scanning %s...\n", diskdir);
149     while((workdir = readdir(topdir)) != NULL) {
150         if(is_dot_or_dotdot(workdir->d_name)
151            || strcmp(workdir->d_name, "lost+found") == 0) {
152             continue;
153         }
154         entryname = newvstralloc(entryname,
155                                  diskdir, "/", workdir->d_name, NULL);
156         if(verbose) {
157             printf("  %s: ", workdir->d_name);
158         }
159         if(!is_dir(entryname)) {
160             if(verbose)
161                 puts("skipping cruft file, perhaps you should delete it.");
162         } else if(!is_datestr(workdir->d_name)) {
163             if(verbose)
164                 puts("skipping cruft directory, perhaps you should delete it.");
165         } else {
166             holding_list = insert_sort_sl(holding_list, workdir->d_name);
167             if(verbose)
168                 puts("found Amanda directory.");
169         }
170     }
171     closedir(topdir);
172     amfree(entryname);
173     return holding_list;
174 }
175
176
177 sl_t *scan_holdingdir(holding_list, holdp, datestamp)
178 sl_t *holding_list;
179 holdingdisk_t *holdp;
180 char *datestamp;
181 {
182     DIR *workdir;
183     struct dirent *entry;
184     char *dirname = NULL;
185     char *destname = NULL;
186     disk_t *dp;
187     dumpfile_t file;
188
189     dirname = vstralloc(holdp->diskdir, "/", datestamp, NULL);
190     if((workdir = opendir(dirname)) == NULL) {
191         if(errno != ENOENT)
192             log_add(L_INFO, "%s: could not open dir: %s",
193                     dirname, strerror(errno));
194         amfree(dirname);
195         return holding_list;
196     }
197     chdir(dirname);
198     while((entry = readdir(workdir)) != NULL) {
199         if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
200             continue;
201
202         if(is_emptyfile(entry->d_name))
203             continue;
204
205         destname = newvstralloc(destname,
206                                 dirname, "/", entry->d_name,
207                                 NULL);
208         get_dumpfile(destname, &file);
209         if( file.type != F_DUMPFILE) {
210             if( file.type != F_CONT_DUMPFILE )
211                 log_add(L_INFO, "%s: ignoring cruft file.", entry->d_name);
212             continue;
213         }
214
215         dp = lookup_disk(file.name, file.disk);
216
217         if (dp == NULL) {
218             log_add(L_INFO, "%s: disk %s:%s not in database, skipping it.",
219                     entry->d_name, file.name, file.disk);
220             continue;
221         }
222
223         if(file.dumplevel < 0 || file.dumplevel > 9) {
224             log_add(L_INFO, "%s: ignoring file with bogus dump level %d.",
225                     entry->d_name, file.dumplevel);
226             continue;
227         }
228
229         holding_list = append_sl(holding_list, destname);
230     }
231     closedir(workdir);
232     amfree(dirname);
233     amfree(destname);
234     return holding_list;
235 }
236
237
238 sl_t *get_flush(dateargs, datestamp, amflush, verbose)
239 sl_t *dateargs;
240 char *datestamp;  /* don't do this date */
241 int amflush, verbose;
242 {
243     sl_t *holding_list;
244     sl_t *date_list;
245     sle_t *datearg;
246     sle_t *date, *next_date;
247     holdingdisk_t *hdisk;
248     char current_dir[1000];
249
250     getcwd(current_dir, 999);
251
252     holding_list = new_sl();
253
254     if(dateargs) {
255         int ok;
256
257         date_list = pick_all_datestamp(verbose);
258         for(date = date_list->first; date != NULL;) {
259             next_date = date->next;
260             ok = 0;
261             for(datearg=dateargs->first; datearg != NULL && ok==0;
262                 datearg = datearg->next) {
263                 ok = match_datestamp(datearg->name, date->name);
264             }
265             if(ok == 0) { /* remove dir */
266                 remove_sl(date_list, date);
267             }
268             date = next_date;
269         }
270     }
271     else if (amflush) {
272         date_list = pick_datestamp(verbose);
273     }
274     else {
275         date_list = pick_all_datestamp(verbose);
276     }
277
278     for(date = date_list->first; date !=NULL; date = date->next) {
279         if(!datestamp || strcmp(datestamp,date->name) != 0) {
280             for(hdisk = getconf_holdingdisks(); hdisk != NULL;
281                                                 hdisk = hdisk->next) {
282                 holding_list = scan_holdingdir(holding_list, hdisk, date->name);
283             }
284         }
285     }
286
287     free_sl(date_list);
288     date_list = NULL;
289     chdir(current_dir);
290     return(holding_list);
291 }
292
293
294 sl_t *pick_all_datestamp(verbose)
295 int verbose;
296 {
297     sl_t *holding_list = NULL;
298     holdingdisk_t *hdisk;
299
300     holding_list = new_sl();
301     for(hdisk = getconf_holdingdisks(); hdisk != NULL; hdisk = hdisk->next)
302         holding_list = scan_holdingdisk(holding_list, hdisk->diskdir, verbose);
303
304     return holding_list;
305 }
306
307
308 sl_t *pick_datestamp(verbose)
309 int verbose;
310 {
311     sl_t *holding_list;
312     sl_t *r_holding_list = NULL;
313     sle_t *dir;
314     char **directories = NULL;
315     int i;
316     char *answer = NULL;
317     char *a;
318     int ch;
319     char max_char = '\0', chupper = '\0';
320
321     holding_list = pick_all_datestamp(verbose);
322
323     if(holding_list->nb_element == 0) {
324         return holding_list;
325     }
326     else if(holding_list->nb_element == 1 || !verbose) {
327         return holding_list;
328     }
329     else {
330         directories = alloc((holding_list->nb_element) * sizeof(char *));
331         for(dir = holding_list->first, i=0; dir != NULL; dir = dir->next,i++) {
332             directories[i] = dir->name;
333         }
334
335         while(1) {
336             puts("\nMultiple Amanda directories, please pick one by letter:");
337             for(dir = holding_list->first, max_char = 'A';
338                 dir != NULL && max_char <= 'Z';
339                 dir = dir->next, max_char++) {
340                 printf("  %c. %s\n", max_char, dir->name);
341             }
342             max_char--;
343             printf("Select directories to flush [A..%c]: [ALL] ", max_char);
344             fflush(stdout); fflush(stderr);
345             amfree(answer);
346             if ((answer = agets(stdin)) == NULL) {
347                 clearerr(stdin);
348                 continue;
349             }
350             a = answer;
351             while ((ch = *a++) != '\0' && isspace(ch)) {}
352             if(ch == '\0' || strncasecmp(a, "ALL", 3) == 0) {
353                 break;
354             }
355             do {
356                 if (isspace(ch) || ch == ',') {
357                     continue;
358                 }
359                 chupper = toupper(ch);
360                 if (chupper < 'A' || chupper > max_char) {
361                     free_sl(r_holding_list);
362                     r_holding_list = NULL;
363                     break;
364                 }
365                 r_holding_list = append_sl(r_holding_list,
366                                            directories[chupper - 'A']);
367             } while ((ch = *a++) != '\0');
368             if (r_holding_list && ch == '\0') {
369                 free_sl(holding_list);
370                 holding_list = r_holding_list;
371                 break;
372             }
373         }
374     }
375     amfree(directories);
376     amfree(answer);
377     return holding_list;
378 }
379
380
381 filetype_t get_amanda_names(fname, hostname, diskname, level)
382 char *fname, **hostname, **diskname;
383 int *level;
384 {
385     dumpfile_t file;
386     char buffer[DISK_BLOCK_BYTES];
387     int fd;
388     *hostname = *diskname = NULL;
389
390     if((fd = open(fname, O_RDONLY)) == -1)
391         return F_UNKNOWN;
392
393     if(fullread(fd, buffer, sizeof(buffer)) != sizeof(buffer)) {
394         aclose(fd);
395         return F_UNKNOWN;
396     }
397     aclose(fd);
398
399     parse_file_header(buffer,&file,sizeof(buffer));
400     if(file.type != F_DUMPFILE && file.type != F_CONT_DUMPFILE) {
401         return file.type;
402     }
403     *hostname = stralloc(file.name);
404     *diskname = stralloc(file.disk);
405     *level = file.dumplevel;
406
407     return file.type;
408 }
409
410
411 void get_dumpfile(fname, file)
412 char *fname;
413 dumpfile_t *file;
414 {
415     char buffer[DISK_BLOCK_BYTES];
416     int fd;
417
418     fh_init(file);
419     file->type = F_UNKNOWN;
420     if((fd = open(fname, O_RDONLY)) == -1)
421         return;
422
423     if(fullread(fd, buffer, sizeof(buffer)) != sizeof(buffer)) {
424         aclose(fd);
425         return;
426     }
427     aclose(fd);
428
429     parse_file_header(buffer,file,sizeof(buffer));
430     return;
431 }
432
433
434 long size_holding_files(holding_file)
435 char *holding_file;
436 {
437     int fd;
438     int buflen;
439     char buffer[DISK_BLOCK_BYTES];
440     dumpfile_t file;
441     char *filename;
442     long size=0;
443     struct stat finfo;
444
445     filename = stralloc(holding_file);
446     while(filename != NULL && filename[0] != '\0') {
447         if((fd = open(filename,O_RDONLY)) == -1) {
448             fprintf(stderr,"size_holding_files: open of %s failed: %s\n",filename,strerror(errno));
449             amfree(filename);
450             return -1;
451         }
452         buflen = fullread(fd, buffer, sizeof(buffer));
453         parse_file_header(buffer, &file, buflen);
454         close(fd);
455         if(stat(filename, &finfo) == -1) {
456             printf("stat %s: %s\n", filename, strerror(errno));
457             finfo.st_size = 0;
458         }
459         size += (finfo.st_size+1023)/1024;
460         filename = newstralloc(filename, file.cont_filename);
461     }
462     amfree(filename);
463     return size;
464 }
465
466
467 int unlink_holding_files( holding_file )
468 char *holding_file;
469 {
470     int fd;
471     int buflen;
472     char buffer[DISK_BLOCK_BYTES];
473     dumpfile_t file;
474     char *filename;
475
476     filename = stralloc(holding_file);
477     while(filename != NULL && filename[0] != '\0') {
478         if((fd = open(filename,O_RDONLY)) == -1) {
479             fprintf(stderr,"unlink_holding_files: open of %s failed: %s\n",filename,strerror(errno));
480             amfree(filename);
481             return 0;
482         }
483         buflen = fullread(fd, buffer, sizeof(buffer));
484         parse_file_header(buffer, &file, buflen);
485         close(fd);
486         unlink(filename);
487         filename = newstralloc(filename,file.cont_filename);
488     }
489     amfree(filename);
490     return 1;
491 }
492
493
494 int rename_tmp_holding( holding_file, complete )
495 char *holding_file;
496 int complete;
497 {
498     int fd;
499     int buflen;
500     char buffer[DISK_BLOCK_BYTES];
501     dumpfile_t file;
502     char *filename;
503     char *filename_tmp = NULL;
504
505     filename = stralloc(holding_file);
506     while(filename != NULL && filename[0] != '\0') {
507         filename_tmp = newvstralloc(filename_tmp, filename, ".tmp", NULL);
508         if((fd = open(filename_tmp,O_RDONLY)) == -1) {
509             fprintf(stderr,"rename_tmp_holding: open of %s failed: %s\n",filename_tmp,strerror(errno));
510             amfree(filename);
511             amfree(filename_tmp);
512             return 0;
513         }
514         buflen = fullread(fd, buffer, sizeof(buffer));
515         close(fd);
516
517         if(rename(filename_tmp, filename) != 0) {
518             fprintf(stderr,
519                     "rename_tmp_holding: could not rename \"%s\" to \"%s\": %s",
520                     filename_tmp, filename, strerror(errno));
521         }
522
523         if (buflen == 0) {
524             fprintf(stderr,"rename_tmp_holding: %s: empty file?\n", filename);
525             amfree(filename);
526             amfree(filename_tmp);
527             return 0;
528         }
529         parse_file_header(buffer, &file, buflen);
530         if(complete == 0 ) {
531             if((fd = open(filename, O_RDWR)) == -1) {
532                 fprintf(stderr, "rename_tmp_holdingX: open of %s failed: %s\n",
533                         filename, strerror(errno));
534                 amfree(filename);
535                 amfree(filename_tmp);
536                 return 0;
537
538             }
539             file.is_partial = 1;
540             build_header(buffer, &file, sizeof(buffer));
541             fullwrite(fd, buffer, sizeof(buffer));
542             close(fd);
543         }
544         filename = newstralloc(filename, file.cont_filename);
545     }
546     amfree(filename);
547     amfree(filename_tmp);
548     return 1;
549 }
550
551
552 void cleanup_holdingdisk(diskdir, verbose)
553 char *diskdir;
554 int verbose;
555 {
556     DIR *topdir;
557     struct dirent *workdir;
558
559     if((topdir = opendir(diskdir)) == NULL) {
560         if(verbose && errno != ENOENT)
561             printf("Warning: could not open holding dir %s: %s\n",
562                    diskdir, strerror(errno));
563         return;
564    }
565
566     /* find all directories of the right format  */
567
568     if(verbose)
569         printf("Scanning %s...\n", diskdir);
570     chdir(diskdir);
571     while((workdir = readdir(topdir)) != NULL) {
572         if(is_dot_or_dotdot(workdir->d_name)
573            || strcmp(workdir->d_name, "lost+found") == 0) {
574             continue;
575         }
576         if(verbose)
577             printf("  %s: ", workdir->d_name);
578         if(!is_dir(workdir->d_name)) {
579             if(verbose)
580                 puts("skipping cruft file, perhaps you should delete it.");
581         }
582         else if(!is_datestr(workdir->d_name)) {
583             if(verbose)
584                 puts("skipping cruft directory, perhaps you should delete it.");
585         }
586         else if(rmdir(workdir->d_name) == 0) {
587             if(verbose)
588                 puts("deleted empty Amanda directory.");
589         }
590      }
591      closedir(topdir);
592 }
593
594
595 int mkholdingdir(diskdir)
596 char *diskdir;
597 {
598     struct stat stat_hdp;
599     int success = 1;
600
601     if (mkpdir(diskdir, 0770, (uid_t)-1, (gid_t)-1) != 0 && errno != EEXIST) {
602         log_add(L_WARNING, "WARNING: could not create parents of %s: %s",
603                 diskdir, strerror(errno));
604         success = 0;
605     }
606     else if (mkdir(diskdir, 0770) != 0 && errno != EEXIST) {
607         log_add(L_WARNING, "WARNING: could not create %s: %s",
608                 diskdir, strerror(errno));
609         success = 0;
610     }
611     else if (stat(diskdir, &stat_hdp) == -1) {
612         log_add(L_WARNING, "WARNING: could not stat %s: %s",
613                 diskdir, strerror(errno));
614         success = 0;
615     }
616     else {
617         if (!S_ISDIR((stat_hdp.st_mode))) {
618             log_add(L_WARNING, "WARNING: %s is not a directory",
619                     diskdir);
620             success = 0;
621         }
622         else if (access(diskdir,W_OK) != 0) {
623             log_add(L_WARNING, "WARNING: directory %s is not writable",
624                     diskdir);
625             success = 0;
626         }
627     }
628     return success;
629 }