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