Imported Upstream version 2.5.1
[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.56 2006/06/09 23:07:26 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 "logfile.h"
37
38 static sl_t *scan_holdingdisk(sl_t *holding_list, char *diskdir, int verbose);
39 static sl_t *scan_holdingdir(sl_t *holding_list, holdingdisk_t *holdp, char *datestamp);
40
41 int
42 is_dir(
43     char *fname)
44 {
45     struct stat statbuf;
46
47     if(stat(fname, &statbuf) == -1) return 0;
48
49     return (statbuf.st_mode & S_IFDIR) == S_IFDIR;
50 }
51
52 int
53 is_emptyfile(
54     char *fname)
55 {
56     struct stat statbuf;
57
58     if(stat(fname, &statbuf) == -1) return 0;
59
60     return ((statbuf.st_mode & S_IFDIR) != S_IFDIR) &&
61                 (statbuf.st_size == (off_t)0);
62 }
63
64
65 /*
66  * sanity check on datestamp of the form YYYYMMDD or YYYYMMDDhhmmss
67  */
68
69 int
70 is_datestr(
71     char *fname)
72 {
73     char *cp;
74     int ch, num, date, year, month, hour, minute, second;
75     char ymd[9], hms[7];
76
77     /* must be 8 digits */
78     for(cp = fname; (ch = *cp) != '\0'; cp++) {
79         if(!isdigit(ch)) {
80             break;
81         }
82     }
83     if(ch != '\0' || (cp-fname != 8 && cp-fname != 14)) {
84         return 0;
85     }
86
87     /* sanity check year, month, and day */
88
89     strncpy(ymd, fname, 8);
90     ymd[8] = '\0';
91     num = atoi(ymd);
92     year = num / 10000;
93     month = (num / 100) % 100;
94     date = num % 100;
95     if(year<1990 || year>2100 || month<1 || month>12 || date<1 || date>31)
96         return 0;
97
98     if(cp-fname == 8)
99         return 1;
100
101     /* sanity check hour, minute, and second */
102     strncpy(hms, fname+8, 6);
103     hms[6] = '\0';
104     num = atoi(hms);
105     hour = num / 10000;
106     minute = (num / 100) % 100;
107     second = num % 100;
108     if(hour> 23 || minute>59 || second>59)
109         return 0;
110
111     /* yes, we passed all the checks */
112
113     return 1;
114 }
115
116
117 int
118 non_empty(
119     char *      fname)
120 {
121     DIR *dir;
122     struct dirent *entry;
123     int gotentry;
124
125     if((dir = opendir(fname)) == NULL)
126         return 0;
127
128     gotentry = 0;
129     while(!gotentry && (entry = readdir(dir)) != NULL) {
130         gotentry = !is_dot_or_dotdot(entry->d_name);
131     }
132
133     closedir(dir);
134     return gotentry;
135 }
136
137
138 static sl_t *
139 scan_holdingdisk(
140     sl_t *      holding_list,
141     char *      diskdir,
142     int         verbose)
143 {
144     DIR *topdir;
145     struct dirent *workdir;
146     char *entryname = NULL;
147
148     if((topdir = opendir(diskdir)) == NULL) {
149         if(verbose && errno != ENOENT)
150            printf("Warning: could not open holding dir %s: %s\n",
151                   diskdir, strerror(errno));
152         return holding_list;
153     }
154
155     /* find all directories of the right format  */
156
157     if(verbose)
158         printf("Scanning %s...\n", diskdir);
159     while((workdir = readdir(topdir)) != NULL) {
160         if(is_dot_or_dotdot(workdir->d_name)) {
161             continue;
162         }
163         entryname = newvstralloc(entryname,
164                                  diskdir, "/", workdir->d_name, NULL);
165         if(verbose) {
166             printf("  %s: ", workdir->d_name);
167         }
168         if(!is_dir(entryname)) {
169             if(verbose) {
170                 puts("skipping cruft file, perhaps you should delete it.");
171             }
172         } else if(!is_datestr(workdir->d_name)) {
173             if(verbose && (strcmp(workdir->d_name, "lost+found")!=0) ) {
174                 puts("skipping cruft directory, perhaps you should delete it.");
175             }
176         } else {
177             holding_list = insert_sort_sl(holding_list, workdir->d_name);
178             if(verbose) {
179                 puts("found Amanda directory.");
180             }
181         }
182     }
183     closedir(topdir);
184     amfree(entryname);
185     return holding_list;
186 }
187
188
189 static sl_t *
190 scan_holdingdir(
191     sl_t *              holding_list,
192     holdingdisk_t *     holdp,
193     char *              datestamp)
194 {
195     DIR *workdir;
196     struct dirent *entry;
197     char *dirname = NULL;
198     char *destname = NULL;
199     disk_t *dp;
200     dumpfile_t file;
201
202     dirname = vstralloc(holdingdisk_get_diskdir(holdp), "/", datestamp, NULL);
203     if((workdir = opendir(dirname)) == NULL) {
204         if(errno != ENOENT)
205             log_add(L_INFO, "%s: could not open dir: %s",
206                     dirname, strerror(errno));
207         amfree(dirname);
208         return holding_list;
209     }
210     if ((chdir(dirname)) == -1) {
211         log_add(L_INFO, "%s: could not chdir: %s",
212                     dirname, strerror(errno));
213         amfree(dirname);
214         return holding_list;
215     }
216
217     while((entry = readdir(workdir)) != NULL) {
218         if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
219             continue;
220
221         if(is_emptyfile(entry->d_name))
222             continue;
223
224         destname = newvstralloc(destname,
225                                 dirname, "/", entry->d_name,
226                                 NULL);
227         get_dumpfile(destname, &file);
228         if( file.type != F_DUMPFILE) {
229             if( file.type != F_CONT_DUMPFILE )
230                 log_add(L_INFO, "%s: ignoring cruft file.", entry->d_name);
231             continue;
232         }
233
234         dp = lookup_disk(file.name, file.disk);
235
236         if (dp == NULL) {
237             log_add(L_INFO, "%s: disk %s:%s not in database, skipping it.",
238                     entry->d_name, file.name, file.disk);
239             continue;
240         }
241
242         if(file.dumplevel < 0 || file.dumplevel > 9) {
243             log_add(L_INFO, "%s: ignoring file with bogus dump level %d.",
244                     entry->d_name, file.dumplevel);
245             continue;
246         }
247
248         holding_list = append_sl(holding_list, destname);
249     }
250     closedir(workdir);
251     amfree(dirname);
252     amfree(destname);
253     return holding_list;
254 }
255
256
257
258 sl_t *
259 get_flush(
260     sl_t *dateargs,
261     char *datestamp,  /* don't do this date */
262     int amflush,
263     int verbose)
264 {
265     sl_t *holding_list;
266     sl_t *date_list;
267     sle_t *datearg;
268     sle_t *date, *next_date;
269     holdingdisk_t *hdisk;
270     char current_dir[PATH_MAX];
271
272     holding_list = new_sl();
273
274     if (getcwd(current_dir, SIZEOF(current_dir)-1) == NULL) {
275         log_add(L_INFO, "get_flush: could get current working directory: %s",
276                     strerror(errno));
277         return holding_list;
278     }
279
280     if(dateargs) {
281         int ok;
282
283         date_list = pick_all_datestamp(verbose);
284         for(date = date_list->first; date != NULL;) {
285             next_date = date->next;
286             ok = 0;
287             for(datearg=dateargs->first; datearg != NULL && ok==0;
288                 datearg = datearg->next) {
289                 ok = match_datestamp(datearg->name, date->name);
290             }
291             if(ok == 0) { /* remove dir */
292                 remove_sl(date_list, date);
293             }
294             date = next_date;
295         }
296     }
297     else if (amflush) {
298         date_list = pick_datestamp(verbose);
299     }
300     else {
301         date_list = pick_all_datestamp(verbose);
302     }
303
304     for(date = date_list->first; date !=NULL; date = date->next) {
305         if(!datestamp || strcmp(datestamp,date->name) != 0) {
306             for(hdisk = getconf_holdingdisks(); hdisk != NULL;
307                                                 hdisk = hdisk->next) {
308                 holding_list = scan_holdingdir(holding_list, hdisk, date->name);
309             }
310         }
311     }
312
313     free_sl(date_list);
314     date_list = NULL;
315     if (chdir(current_dir) == -1) {
316         log_add(L_INFO, "%s: could not chdir: %s",
317                     current_dir, strerror(errno));
318     }
319     return(holding_list);
320 }
321
322
323 sl_t *
324 pick_all_datestamp(
325     int         verbose)
326 {
327     sl_t *holding_list = NULL;
328     holdingdisk_t *hdisk;
329
330     holding_list = new_sl();
331     for(hdisk = getconf_holdingdisks(); hdisk != NULL; hdisk = hdisk->next)
332         holding_list = scan_holdingdisk(holding_list, holdingdisk_get_diskdir(hdisk), verbose);
333
334     return holding_list;
335 }
336
337
338 sl_t *
339 pick_datestamp(
340     int         verbose)
341 {
342     sl_t *holding_list;
343     sl_t *r_holding_list = NULL;
344     sle_t *dir;
345     char **directories = NULL;
346     int i;
347     char *answer = NULL;
348     char *a = NULL;
349     int ch = 0;
350     char max_char = '\0', chupper = '\0';
351
352     holding_list = pick_all_datestamp(verbose);
353
354     if(holding_list->nb_element == 0) {
355         return holding_list;
356     }
357     else if(holding_list->nb_element == 1 || !verbose) {
358         return holding_list;
359     }
360     else {
361         directories = alloc((holding_list->nb_element) * SIZEOF(char *));
362         for(dir = holding_list->first, i=0; dir != NULL; dir = dir->next,i++) {
363             directories[i] = dir->name;
364         }
365
366         while(1) {
367             puts("\nMultiple Amanda directories, please pick one by letter:");
368             for(dir = holding_list->first, max_char = 'A';
369                 dir != NULL && max_char <= 'Z';
370                 dir = dir->next, max_char++) {
371                 printf("  %c. %s\n", max_char, dir->name);
372             }
373             max_char--;
374             printf("Select directories to flush [A..%c]: [ALL] ", max_char);
375             fflush(stdout); fflush(stderr);
376             amfree(answer);
377             if ((answer = agets(stdin)) == NULL) {
378                 clearerr(stdin);
379                 continue;
380             }
381
382             if (*answer == '\0' || strncasecmp(answer, "ALL", 3) == 0) {
383                 break;
384             }
385
386             a = answer;
387             while ((ch = *a++) != '\0') {
388                 if (!isspace(ch))
389                     break;
390             }
391
392             do {
393                 if (isspace(ch) || ch == ',') {
394                     continue;
395                 }
396                 chupper = (char)toupper(ch);
397                 if (chupper < 'A' || chupper > max_char) {
398                     free_sl(r_holding_list);
399                     r_holding_list = NULL;
400                     break;
401                 }
402                 r_holding_list = append_sl(r_holding_list,
403                                            directories[chupper - 'A']);
404             } while ((ch = *a++) != '\0');
405             if (r_holding_list && ch == '\0') {
406                 free_sl(holding_list);
407                 holding_list = r_holding_list;
408                 break;
409             }
410         }
411     }
412     amfree(directories);
413     amfree(answer);
414     return holding_list;
415 }
416
417
418 filetype_t
419 get_amanda_names( char *        fname,
420     char **     hostname,
421     char **     diskname,
422     int *       level)
423 {
424     dumpfile_t file;
425     char buffer[DISK_BLOCK_BYTES];
426     int fd;
427     *hostname = *diskname = NULL;
428
429     memset(buffer, 0, sizeof(buffer));
430     if((fd = open(fname, O_RDONLY)) == -1)
431         return F_UNKNOWN;
432
433     if(fullread(fd, buffer, SIZEOF(buffer)) != (ssize_t)sizeof(buffer)) {
434         aclose(fd);
435         return F_UNKNOWN;
436     }
437     aclose(fd);
438
439     parse_file_header(buffer, &file, SIZEOF(buffer));
440     if(file.type != F_DUMPFILE && file.type != F_CONT_DUMPFILE) {
441         return file.type;
442     }
443     *hostname = stralloc(file.name);
444     *diskname = stralloc(file.disk);
445     *level = file.dumplevel;
446
447     return file.type;
448 }
449
450
451 void
452 get_dumpfile(
453     char *      fname,
454     dumpfile_t *file)
455 {
456     char buffer[DISK_BLOCK_BYTES];
457     int fd;
458
459     memset(buffer, 0, sizeof(buffer));
460
461     fh_init(file);
462     file->type = F_UNKNOWN;
463     if((fd = open(fname, O_RDONLY)) == -1)
464         return;
465
466     if(fullread(fd, buffer, SIZEOF(buffer)) != (ssize_t)sizeof(buffer)) {
467         aclose(fd);
468         return;
469     }
470     aclose(fd);
471
472     parse_file_header(buffer, file, SIZEOF(buffer));
473     return;
474 }
475
476
477 off_t
478 size_holding_files(
479     char *      holding_file,
480     int         strip_headers)
481 {
482     int fd;
483     ssize_t buflen;
484     char buffer[DISK_BLOCK_BYTES];
485     dumpfile_t file;
486     char *filename;
487     off_t size = (off_t)0;
488     struct stat finfo;
489
490     memset(buffer, 0, sizeof(buffer));
491     filename = stralloc(holding_file);
492     while(filename != NULL && filename[0] != '\0') {
493         if((fd = open(filename,O_RDONLY)) == -1) {
494             fprintf(stderr,"size_holding_files: open of %s failed: %s\n",filename,strerror(errno));
495             amfree(filename);
496             return (off_t)-1;
497         }
498         if ((buflen = fullread(fd, buffer, SIZEOF(buffer))) > 0) {
499                 parse_file_header(buffer, &file, (size_t)buflen);
500         }
501         close(fd);
502         if(stat(filename, &finfo) == -1) {
503             printf("stat %s: %s\n", filename, strerror(errno));
504             finfo.st_size = (off_t)0;
505         }
506         size += (finfo.st_size+(off_t)1023)/(off_t)1024;
507         if(strip_headers)
508             size -= (off_t)(DISK_BLOCK_BYTES / 1024);
509         if(buflen > 0) {
510             filename = newstralloc(filename, file.cont_filename);
511         }
512         else {
513             amfree(filename);
514         }
515     }
516     amfree(filename);
517     return size;
518 }
519
520
521 int
522 unlink_holding_files(
523     char *      holding_file)
524 {
525     int fd;
526     ssize_t buflen;
527     char buffer[DISK_BLOCK_BYTES];
528     dumpfile_t file;
529     char *filename;
530
531     memset(buffer, 0, sizeof(buffer));
532     filename = stralloc(holding_file);
533     while(filename != NULL && filename[0] != '\0') {
534         if((fd = open(filename,O_RDONLY)) == -1) {
535             fprintf(stderr,"unlink_holding_files: open of %s failed: %s\n",filename,strerror(errno));
536             amfree(filename);
537             return 0;
538         }
539         if ((buflen = fullread(fd, buffer, SIZEOF(buffer))) > 0) {
540             parse_file_header(buffer, &file, (size_t)buflen);
541         }
542         close(fd);
543         unlink(filename);
544         if(buflen > 0) {
545             filename = newstralloc(filename, file.cont_filename);
546         }
547         else {
548             amfree(filename);
549         }
550     }
551     amfree(filename);
552     return 1;
553 }
554
555
556 int
557 rename_tmp_holding(
558     char *      holding_file,
559     int         complete)
560 {
561     int fd;
562     ssize_t buflen;
563     char buffer[DISK_BLOCK_BYTES];
564     dumpfile_t file;
565     char *filename;
566     char *filename_tmp = NULL;
567
568     memset(buffer, 0, sizeof(buffer));
569     filename = stralloc(holding_file);
570     while(filename != NULL && filename[0] != '\0') {
571         filename_tmp = newvstralloc(filename_tmp, filename, ".tmp", NULL);
572         if((fd = open(filename_tmp,O_RDONLY)) == -1) {
573             fprintf(stderr,"rename_tmp_holding: open of %s failed: %s\n",filename_tmp,strerror(errno));
574             amfree(filename);
575             amfree(filename_tmp);
576             return 0;
577         }
578         buflen = fullread(fd, buffer, SIZEOF(buffer));
579         close(fd);
580
581         if(rename(filename_tmp, filename) != 0) {
582             fprintf(stderr,
583                     "rename_tmp_holding: could not rename \"%s\" to \"%s\": %s",
584                     filename_tmp, filename, strerror(errno));
585         }
586
587         if (buflen <= 0) {
588             fprintf(stderr,"rename_tmp_holding: %s: empty file?\n", filename);
589             amfree(filename);
590             amfree(filename_tmp);
591             return 0;
592         }
593         parse_file_header(buffer, &file, (size_t)buflen);
594         if(complete == 0 ) {
595             if((fd = open(filename, O_RDWR)) == -1) {
596                 fprintf(stderr, "rename_tmp_holdingX: open of %s failed: %s\n",
597                         filename, strerror(errno));
598                 amfree(filename);
599                 amfree(filename_tmp);
600                 return 0;
601
602             }
603             file.is_partial = 1;
604             build_header(buffer, &file, SIZEOF(buffer));
605             fullwrite(fd, buffer, SIZEOF(buffer));
606             close(fd);
607         }
608         filename = newstralloc(filename, file.cont_filename);
609     }
610     amfree(filename);
611     amfree(filename_tmp);
612     return 1;
613 }
614
615
616 void
617 cleanup_holdingdisk(
618     char *      diskdir,
619     int         verbose)
620 {
621     DIR *topdir;
622     struct dirent *workdir;
623
624     if((topdir = opendir(diskdir)) == NULL) {
625         if(verbose && errno != ENOENT)
626             printf("Warning: could not open holding dir %s: %s\n",
627                    diskdir, strerror(errno));
628         return;
629    }
630
631     /* find all directories of the right format  */
632
633     if(verbose)
634         printf("Scanning %s...\n", diskdir);
635     if ((chdir(diskdir)) == -1) {
636         log_add(L_INFO, "%s: could not chdir: %s",
637                     diskdir, strerror(errno));
638     }
639     while((workdir = readdir(topdir)) != NULL) {
640         if(strcmp(workdir->d_name, ".") == 0
641            || strcmp(workdir->d_name, "..") == 0
642            || strcmp(workdir->d_name, "lost+found") == 0)
643             continue;
644
645         if(verbose)
646             printf("  %s: ", workdir->d_name);
647         if(!is_dir(workdir->d_name)) {
648             if(verbose)
649                 puts("skipping cruft file, perhaps you should delete it.");
650         }
651         else if(!is_datestr(workdir->d_name)) {
652             if(verbose && (strcmp(workdir->d_name, "lost+found")!=0) )
653                 puts("skipping cruft directory, perhaps you should delete it.");
654         }
655         else if(rmdir(workdir->d_name) == 0) {
656             if(verbose)
657                 puts("deleted empty Amanda directory.");
658         }
659      }
660      closedir(topdir);
661 }
662
663
664 int
665 mkholdingdir(
666     char *      diskdir)
667 {
668     struct stat stat_hdp;
669     int success = 1;
670
671     if (mkpdir(diskdir, 0770, (uid_t)-1, (gid_t)-1) != 0 && errno != EEXIST) {
672         log_add(L_WARNING, "WARNING: could not create parents of %s: %s",
673                 diskdir, strerror(errno));
674         success = 0;
675     }
676     else if (mkdir(diskdir, 0770) != 0 && errno != EEXIST) {
677         log_add(L_WARNING, "WARNING: could not create %s: %s",
678                 diskdir, strerror(errno));
679         success = 0;
680     }
681     else if (stat(diskdir, &stat_hdp) == -1) {
682         log_add(L_WARNING, "WARNING: could not stat %s: %s",
683                 diskdir, strerror(errno));
684         success = 0;
685     }
686     else {
687         if (!S_ISDIR((stat_hdp.st_mode))) {
688             log_add(L_WARNING, "WARNING: %s is not a directory",
689                     diskdir);
690             success = 0;
691         }
692         else if (access(diskdir,W_OK) != 0) {
693             log_add(L_WARNING, "WARNING: directory %s is not writable",
694                     diskdir);
695             success = 0;
696         }
697     }
698     return success;
699 }