2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: holding.c,v 1.56 2006/06/09 23:07:26 martinea Exp $
29 * Functions to access holding disk
35 #include "fileheader.h"
41 /* Is fname a directory?
43 * @param fname: filename (fully qualified)
46 static int is_dir(char *fname);
48 /* sanity check that datestamp is of the form YYYYMMDD or
51 * @param fname: a filename (without directory)
54 static int is_datestr(char *fname);
58 static int verbose = 0;
69 if(stat(fname, &statbuf) == -1) return 0;
71 return (statbuf.st_mode & S_IFDIR) == S_IFDIR;
80 if(stat(fname, &statbuf) == -1) return 0;
82 return ((statbuf.st_mode & S_IFDIR) != S_IFDIR) &&
83 (statbuf.st_size == (off_t)0);
91 int ch, num, date, year, month, hour, minute, second;
94 /* must be 8 digits */
95 for(cp = fname; (ch = *cp) != '\0'; cp++) {
100 if(ch != '\0' || (cp-fname != 8 && cp-fname != 14)) {
104 /* sanity check year, month, and day */
106 strncpy(ymd, fname, 8);
110 month = (num / 100) % 100;
112 if(year<1990 || year>2100 || month<1 || month>12 || date<1 || date>31)
118 /* sanity check hour, minute, and second */
119 strncpy(hms, fname+8, 6);
123 minute = (num / 100) % 100;
125 if(hour> 23 || minute>59 || second>59)
128 /* yes, we passed all the checks */
137 holding_set_verbosity(int v)
145 * Holding directories
149 holding_get_directories_per_disk(
156 struct dirent *workdir;
161 if ((dir = opendir(hdisk)) == NULL) {
162 if (verbose && errno != ENOENT)
163 printf(_("Warning: could not open holding disk %s: %s\n"),
164 hdisk, strerror(errno));
169 printf(_("Scanning %s...\n"), hdisk);
171 while ((workdir = readdir(dir)) != NULL) {
172 if (is_dot_or_dotdot(workdir->d_name))
176 printf(" %s: ", workdir->d_name);
178 hdir = newvstralloc(hdir,
179 hdisk, "/", workdir->d_name,
182 /* filter out various undesirables */
185 puts(_("skipping cruft file, perhaps you should delete it."));
186 } else if (!is_datestr(workdir->d_name)) {
187 /* EXT2/3 leave these in the root of each volume */
188 if (strcmp(workdir->d_name, "lost+found")==0)
189 puts(_("skipping system directory"));
191 puts(_("skipping cruft directory, perhaps you should delete it."));
193 /* found a holding directory -- keep it */
196 for (dl= date_list->first; dl != NULL; dl = dl->next) {
197 if (strcmp(dl->name, workdir->d_name) == 0) {
205 if (date_found == 1) {
207 rv = insert_sort_sl(rv, hdir);
209 rv = insert_sort_sl(rv, workdir->d_name);
211 puts(_("found Amanda directory."));
217 if (hdir) amfree(hdir);
222 holding_get_directories(
227 holdingdisk_t *hdisk_conf;
235 /* call _per_disk for the hdisk we were given, or for all
236 * hdisks if we were given NULL */
238 holding_get_directories_per_disk(hdisk, date_list, fullpaths, rv);
240 for (hdisk_conf = getconf_holdingdisks();
242 hdisk_conf = hdisk_conf->next) {
243 hdisk = holdingdisk_get_diskdir(hdisk_conf);
244 holding_get_directories_per_disk(hdisk, date_list, fullpaths, rv);
255 holding_get_files_per_dir(
261 struct dirent *workdir;
266 if ((dir = opendir(hdir)) == NULL) {
267 if (verbose && errno != ENOENT)
268 printf(_("Warning: could not open holding dir %s: %s\n"),
269 hdir, strerror(errno));
274 printf(_("Scanning %s...\n"), hdir);
276 while ((workdir = readdir(dir)) != NULL) {
277 if (is_dot_or_dotdot(workdir->d_name))
280 hfile = newvstralloc(hfile,
281 hdir, "/", workdir->d_name,
284 /* filter out various undesirables */
285 if (is_emptyfile(hfile))
290 printf(_("%s: ignoring directory\n"), hfile);
294 if (!(dumpf_ok=holding_file_get_dumpfile(hfile, &dumpf)) ||
295 dumpf.type != F_DUMPFILE) {
296 if (dumpf_ok && dumpf.type == F_CONT_DUMPFILE)
297 continue; /* silently skip expected file */
299 printf(_("%s: not a dumpfile\n"), hfile);
303 if (dumpf.dumplevel < 0 || dumpf.dumplevel > 9) {
305 printf(_("%s: ignoring file with bogus dump level %d.\n"),
306 hfile, dumpf.dumplevel);
310 /* found a holding file -- keep it */
312 rv = insert_sort_sl(rv, hfile);
314 rv = insert_sort_sl(rv, workdir->d_name);
317 if (hfile) amfree(hfile);
335 /* call _per_dir for the hdir we were given, or for all
336 * hdir if we were given NULL */
338 holding_get_files_per_dir(hdir, fullpaths, rv);
340 hdirs = holding_get_directories(NULL, date_list, 1);
341 for (e = hdirs->first; e != NULL; e = e->next) {
342 holding_get_files_per_dir(e->name, fullpaths, rv);
350 holding_get_files_for_flush(
358 sle_t *date, *next_date;
366 /* make date_list the intersection of available holding directories and
367 * the dateargs parameter. */
371 date_list = pick_all_datestamp(verbose);
372 for (date = date_list->first; date != NULL;) {
373 next_date = date->next;
375 for(datearg=dateargs->first; datearg != NULL && ok==0;
376 datearg = datearg->next) {
377 ok = match_datestamp(datearg->name, date->name);
379 if(ok == 0) { /* remove dir */
380 remove_sl(date_list, date);
386 /* no date args were provided, so use everything */
388 date_list = pick_datestamp(verbose);
390 date_list = pick_all_datestamp(verbose);
393 result_list = new_sl();
398 /* loop over *all* files, checking each one */
399 file_list = holding_get_files(NULL, date_list, 1);
400 for (file_elt = file_list->first; file_elt != NULL; file_elt = file_elt->next) {
401 /* get info on that file */
402 filetype = holding_file_read_header(file_elt->name, &host, &disk, NULL, &datestamp);
403 if (filetype != F_DUMPFILE)
406 /* check that the hostname and disk are in the disklist */
407 dp = lookup_disk(host, disk);
410 printf(_("%s: disk %s:%s not in database, skipping it."),
411 file_elt->name, host, disk);
415 /* passed all tests -- we'll flush this file */
416 result_list = insert_sort_sl(result_list, file_elt->name);
419 if (date_list) free_sl(date_list);
420 if (file_list) free_sl(file_list);
426 holding_get_file_chunks(char *hfile)
436 /* Loop through all cont_filenames (subsequent chunks) */
437 filename = stralloc(hfile);
438 while (filename != NULL && filename[0] != '\0') {
439 /* get the header to look for cont_filename */
440 if (!holding_file_get_dumpfile(filename, &file)) {
442 printf(_("holding_get_file_chunks: open of %s failed.\n"), filename);
447 /* add the file to the results */
448 insert_sort_sl(rv, filename);
450 /* and go on to the next chunk */
451 filename = newstralloc(filename, file.cont_filename);
464 off_t size = (off_t)0;
467 /* (note: we don't use holding_get_file_chunks here because that would
468 * entail opening each file twice) */
470 /* Loop through all cont_filenames (subsequent chunks) */
471 filename = stralloc(hfile);
472 while (filename != NULL && filename[0] != '\0') {
473 /* stat the file for its size */
474 if (stat(filename, &finfo) == -1) {
476 printf(_("stat %s: %s\n"), filename, strerror(errno));
479 size += (finfo.st_size+(off_t)1023)/(off_t)1024;
481 size -= (off_t)(DISK_BLOCK_BYTES / 1024);
483 /* get the header to look for cont_filename */
484 if (!holding_file_get_dumpfile(filename, &file)) {
486 printf(_("holding_file_size: open of %s failed.\n"), filename);
491 /* on to the next chunk */
492 filename = newstralloc(filename, file.cont_filename);
506 chunklist = holding_get_file_chunks(hfile);
510 for (chunk = chunklist->first; chunk != NULL; chunk = chunk->next) {
511 if (unlink(chunk->name)<0) {
513 printf(_("holding_file_unlink: could not unlink %s: %s\n"),
514 chunk->name, strerror(errno));
523 holding_file_read_header(
532 if (hostname) *hostname = NULL;
533 if (diskname) *diskname = NULL;
534 if (datestamp) *datestamp = NULL;
536 if (!holding_file_get_dumpfile(fname, &file)) {
540 if(file.type != F_DUMPFILE && file.type != F_CONT_DUMPFILE) {
544 if (hostname) *hostname = stralloc(file.name);
545 if (diskname) *diskname = stralloc(file.disk);
546 if (level) *level = file.dumplevel;
547 if (datestamp) *datestamp = stralloc(file.datestamp);
554 holding_file_get_dumpfile(
558 char buffer[DISK_BLOCK_BYTES];
561 memset(buffer, 0, sizeof(buffer));
564 file->type = F_UNKNOWN;
565 if((fd = open(fname, O_RDONLY)) == -1)
568 if(fullread(fd, buffer, SIZEOF(buffer)) != (ssize_t)sizeof(buffer)) {
574 parse_file_header(buffer, file, SIZEOF(buffer));
579 * Interactive functions
586 int old_verbose = holding_set_verbosity(v);
589 /* get all holding directories, without full paths -- this
590 * will be datestamps only */
591 rv = holding_get_directories(NULL, NULL, 0);
593 holding_set_verbosity(old_verbose);
602 sl_t *r_holding_list = NULL;
604 char **directories = NULL;
609 char max_char = '\0', chupper = '\0';
611 holding_list = pick_all_datestamp(verbose);
613 if(holding_list->nb_element == 0) {
616 else if(holding_list->nb_element == 1 || !verbose) {
620 directories = alloc((holding_list->nb_element) * SIZEOF(char *));
621 for(dir = holding_list->first, i=0; dir != NULL; dir = dir->next,i++) {
622 directories[i] = dir->name;
626 puts(_("\nMultiple Amanda directories, please pick one by letter:"));
627 for(dir = holding_list->first, max_char = 'A';
628 dir != NULL && max_char <= 'Z';
629 dir = dir->next, max_char++) {
630 printf(" %c. %s\n", max_char, dir->name);
633 printf(_("Select directories to flush [A..%c]: [ALL] "), max_char);
634 fflush(stdout); fflush(stderr);
636 if ((answer = agets(stdin)) == NULL) {
641 if (*answer == '\0' || strncasecmp(answer, "ALL", 3) == 0) {
646 while ((ch = *a++) != '\0') {
652 if (isspace(ch) || ch == ',') {
655 chupper = (char)toupper(ch);
656 if (chupper < 'A' || chupper > max_char) {
657 free_sl(r_holding_list);
658 r_holding_list = NULL;
661 r_holding_list = append_sl(r_holding_list,
662 directories[chupper - 'A']);
663 } while ((ch = *a++) != '\0');
664 if (r_holding_list && ch == '\0') {
665 free_sl(holding_list);
666 holding_list = r_holding_list;
677 * Application support
687 char buffer[DISK_BLOCK_BYTES];
690 char *filename_tmp = NULL;
692 memset(buffer, 0, sizeof(buffer));
693 filename = stralloc(holding_file);
694 while(filename != NULL && filename[0] != '\0') {
695 filename_tmp = newvstralloc(filename_tmp, filename, ".tmp", NULL);
696 if((fd = open(filename_tmp,O_RDONLY)) == -1) {
697 fprintf(stderr,_("rename_tmp_holding: open of %s failed: %s\n"),filename_tmp,strerror(errno));
699 amfree(filename_tmp);
702 buflen = fullread(fd, buffer, SIZEOF(buffer));
705 if(rename(filename_tmp, filename) != 0) {
707 _("rename_tmp_holding: could not rename \"%s\" to \"%s\": %s"),
708 filename_tmp, filename, strerror(errno));
712 fprintf(stderr,_("rename_tmp_holding: %s: empty file?\n"), filename);
714 amfree(filename_tmp);
717 parse_file_header(buffer, &file, (size_t)buflen);
719 if((fd = open(filename, O_RDWR)) == -1) {
720 fprintf(stderr, _("rename_tmp_holdingX: open of %s failed: %s\n"),
721 filename, strerror(errno));
723 amfree(filename_tmp);
728 build_header(buffer, &file, SIZEOF(buffer));
729 fullwrite(fd, buffer, SIZEOF(buffer));
732 filename = newstralloc(filename, file.cont_filename);
735 amfree(filename_tmp);
745 struct dirent *workdir;
747 if((topdir = opendir(diskdir)) == NULL) {
748 if(verbose && errno != ENOENT)
749 printf(_("Warning: could not open holding dir %s: %s\n"),
750 diskdir, strerror(errno));
754 /* find all directories of the right format */
757 printf(_("Scanning %s...\n"), diskdir);
758 if ((chdir(diskdir)) == -1) {
759 log_add(L_INFO, _("%s: could not chdir: %s"),
760 diskdir, strerror(errno));
762 while((workdir = readdir(topdir)) != NULL) {
763 if(strcmp(workdir->d_name, ".") == 0
764 || strcmp(workdir->d_name, "..") == 0
765 || strcmp(workdir->d_name, "lost+found") == 0)
769 printf(" %s: ", workdir->d_name);
770 if(!is_dir(workdir->d_name)) {
772 puts(_("skipping cruft file, perhaps you should delete it."));
774 else if(!is_datestr(workdir->d_name)) {
775 if(verbose && (strcmp(workdir->d_name, "lost+found")!=0) )
776 puts(_("skipping cruft directory, perhaps you should delete it."));
778 else if(rmdir(workdir->d_name) == 0) {
780 puts(_("deleted empty Amanda directory."));
791 struct stat stat_hdp;
794 if (mkpdir(diskdir, 0770, (uid_t)-1, (gid_t)-1) != 0 && errno != EEXIST) {
795 log_add(L_WARNING, _("WARNING: could not create parents of %s: %s"),
796 diskdir, strerror(errno));
799 else if (mkdir(diskdir, 0770) != 0 && errno != EEXIST) {
800 log_add(L_WARNING, _("WARNING: could not create %s: %s"),
801 diskdir, strerror(errno));
804 else if (stat(diskdir, &stat_hdp) == -1) {
805 log_add(L_WARNING, _("WARNING: could not stat %s: %s"),
806 diskdir, strerror(errno));
810 if (!S_ISDIR((stat_hdp.st_mode))) {
811 log_add(L_WARNING, _("WARNING: %s is not a directory"),
815 else if (access(diskdir,W_OK) != 0) {
816 log_add(L_WARNING, _("WARNING: directory %s is not writable"),