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