X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=server-src%2Fholding.c;h=15286bd3a432760d2577a2811aece707efce90b0;hb=b116e9366c7b2ea2c2eb53b0a13df4090e176235;hp=a158fecc7fc3913c05f83d1c46ec0be181e7b722;hpb=0de2ad0a86685398621fb8ffa6990c029681bb3a;p=debian%2Famanda diff --git a/server-src/holding.c b/server-src/holding.c index a158fec..15286bd 100644 --- a/server-src/holding.c +++ b/server-src/holding.c @@ -24,7 +24,7 @@ * file named AUTHORS, in the root directory of this distribution. */ /* - * $Id: holding.c,v 1.17.2.12.4.3.2.10.2.2 2004/10/21 13:12:29 martinea Exp $ + * $Id: holding.c,v 1.56 2006/06/09 23:07:26 martinea Exp $ * * Functions to access holding disk */ @@ -32,14 +32,41 @@ #include "amanda.h" #include "util.h" #include "holding.h" +#include "diskfile.h" #include "fileheader.h" -#include "util.h" #include "logfile.h" -static sl_t *scan_holdingdisk P((sl_t *holding_list, char *diskdir, int verbose)); +/* + * utilities */ + +/* Is fname a directory? + * + * @param fname: filename (fully qualified) + * @returns: boolean + */ +static int is_dir(char *fname); + +/* Is fname an empty file? + * + * @param fname: filename (fully qualified) + * @returns: boolean + */ +static int is_emptyfile(char *fname); -int is_dir(fname) -char *fname; +/* sanity check that datestamp is of the form YYYYMMDD or + * YYYYMMDDhhmmss + * + * @param fname: a filename (without directory) + * @returns: boolean + */ +static int is_datestr(char *fname); + +/* + * Static functions */ + +static int +is_dir( + char *fname) { struct stat statbuf; @@ -48,19 +75,21 @@ char *fname; return (statbuf.st_mode & S_IFDIR) == S_IFDIR; } -int is_emptyfile(fname) -char *fname; +static int +is_emptyfile( + char *fname) { struct stat statbuf; if(stat(fname, &statbuf) == -1) return 0; - return (statbuf.st_mode & S_IFDIR) != S_IFDIR && statbuf.st_size == 0; + return ((statbuf.st_mode & S_IFDIR) != S_IFDIR) && + (statbuf.st_size == (off_t)0); } -int is_datestr(fname) -char *fname; -/* sanity check on datestamp of the form YYYYMMDD or YYYYMMDDhhmmss*/ +static int +is_datestr( + char *fname) { char *cp; int ch, num, date, year, month, hour, minute, second; @@ -105,443 +134,824 @@ char *fname; return 1; } +/* + * Recursion functions + * + * These implement a general-purpose walk down the holding-* hierarchy. + */ -int non_empty(fname) -char *fname; +/* Perform a custom action for this holding element (disk, dir, file, chunk). + * + * If the element is not cruft, the next step into the tree will only take place + * if this function returns a nonzero value. + * + * The walk is depth-first, with the callback for an element invoked + * before entering that element. Callbacks may depend on this behavior. + * + * @param datap: generic user-data pointer + * @param base: the parent of the element being examined, or NULL for + * holding disks + * @param element: the name of the element being examined + * @param fqpath: fully qualified path to 'element' + * @param is_cruft: nonzero if this element doesn't belong here + * @returns: nonzero if the walk should descend into this element. + */ +typedef int (*holding_walk_fn)( + gpointer datap, + char *base, + char *element, + char *fqpath, + int is_cruft); + +typedef enum { + STOP_AT_DISK, + STOP_AT_DIR, + STOP_AT_FILE, + STOP_AT_CHUNK +} stop_at_t; + +/* Recurse over all holding chunks in a holding file. + * + * Call per_chunk_fn for each chunk of the given file + * + * datap is passed, unchanged, to all holding_walk_fns. + * + * @param hfile: holding file to examine (fully qualified path) + * @param datap: generic user-data pointer + * @param per_chunk_fn: function to call for each holding chunk + */ +static void holding_walk_file( + char *hfile, + gpointer datap, + holding_walk_fn per_chunk_fn) +{ + dumpfile_t file; + char *filename = NULL; + + /* Loop through all cont_filenames (subsequent chunks) */ + filename = stralloc(hfile); + while (filename != NULL && filename[0] != '\0') { + int is_cruft = 0; + + /* get the header to look for cont_filename */ + if (!holding_file_get_dumpfile(filename, &file)) { + is_cruft = 1; + } + + if (per_chunk_fn) + per_chunk_fn(datap, + hfile, + filename, + filename, + is_cruft); + amfree(filename); + + /* and go on to the next chunk if this wasn't cruft */ + if (!is_cruft) + filename = stralloc(file.cont_filename); + dumpfile_free_data(&file); + } + + amfree(filename); +} + +/* Recurse over all holding files in a holding directory. + * + * Call per_file_fn for each file, and so on, stopping at the level given by + * stop_at. + * + * datap is passed, unchanged, to all holding_walk_fns. + * + * @param hdir: holding directory to examine (fully qualified path) + * @param datap: generic user-data pointer + * @param stop_at: do not proceed beyond this level of the hierarchy + * @param per_file_fn: function to call for each holding file + * @param per_chunk_fn: function to call for each holding chunk + */ +static void holding_walk_dir( + char *hdir, + gpointer datap, + stop_at_t stop_at, + holding_walk_fn per_file_fn, + holding_walk_fn per_chunk_fn) { DIR *dir; - struct dirent *entry; - int gotentry; + struct dirent *workdir; + char *hfile = NULL; + dumpfile_t dumpf; + int dumpf_ok; + int proceed = 1; + + if ((dir = opendir(hdir)) == NULL) { + if (errno != ENOENT) + dbprintf(_("Warning: could not open holding dir %s: %s\n"), + hdir, strerror(errno)); + return; + } - if((dir = opendir(fname)) == NULL) - return 0; + while ((workdir = readdir(dir)) != NULL) { + int is_cruft = 0; - gotentry = 0; - while(!gotentry && (entry = readdir(dir)) != NULL) { - gotentry = !is_dot_or_dotdot(entry->d_name); + if (is_dot_or_dotdot(workdir->d_name)) + continue; /* expected cruft */ + + hfile = newvstralloc(hfile, + hdir, "/", workdir->d_name, + NULL); + + /* filter out various undesirables */ + if (is_emptyfile(hfile)) + is_cruft = 1; + + if (is_dir(hfile)) { + is_cruft= 1; + } + + if (!(dumpf_ok=holding_file_get_dumpfile(hfile, &dumpf)) || + dumpf.type != F_DUMPFILE) { + if (dumpf_ok && dumpf.type == F_CONT_DUMPFILE) + continue; /* silently skip expected file */ + + is_cruft = 1; + } + + if (dumpf.dumplevel < 0 || dumpf.dumplevel > 9) { + is_cruft = 1; + } + + if (per_file_fn) + proceed = per_file_fn(datap, + hdir, + workdir->d_name, + hfile, + is_cruft); + if (!is_cruft && proceed && stop_at != STOP_AT_FILE) + holding_walk_file(hfile, + datap, + per_chunk_fn); + dumpfile_free_data(&dumpf); } closedir(dir); - return gotentry; + amfree(hfile); } - -sl_t *scan_holdingdisk(holding_list, diskdir, verbose) -sl_t *holding_list; -char *diskdir; -int verbose; +/* Recurse over all holding directories in a holding disk. + * + * Call per_dir_fn for each dir, and so on, stopping at the level given by + * stop_at. + * + * datap is passed, unchanged, to all holding_walk_fns. + * + * @param hdisk: holding disk to examine (fully qualified path) + * @param datap: generic user-data pointer + * @param stop_at: do not proceed beyond this level of the hierarchy + * @param per_dir_fn: function to call for each holding dir + * @param per_file_fn: function to call for each holding file + * @param per_chunk_fn: function to call for each holding chunk + */ +static void +holding_walk_disk( + char *hdisk, + gpointer datap, + stop_at_t stop_at, + holding_walk_fn per_dir_fn, + holding_walk_fn per_file_fn, + holding_walk_fn per_chunk_fn) { - DIR *topdir; + DIR *dir; struct dirent *workdir; - char *entryname = NULL; + char *hdir = NULL; + int proceed = 1; + + if ((dir = opendir(hdisk)) == NULL) { + if (errno != ENOENT) + dbprintf(_("Warning: could not open holding disk %s: %s\n"), + hdisk, strerror(errno)); + return; + } - if((topdir = opendir(diskdir)) == NULL) { - if(verbose && errno != ENOENT) - printf("Warning: could not open holding dir %s: %s\n", - diskdir, strerror(errno)); - return holding_list; + while ((workdir = readdir(dir)) != NULL) { + int is_cruft = 0; + + if (is_dot_or_dotdot(workdir->d_name)) + continue; /* expected cruft */ + + hdir = newvstralloc(hdir, + hdisk, "/", workdir->d_name, + NULL); + + /* detect cruft */ + if (!is_dir(hdir)) { + is_cruft = 1; + } else if (!is_datestr(workdir->d_name)) { + /* EXT2/3 leave these in the root of each volume */ + if (strcmp(workdir->d_name, "lost+found") == 0) + continue; /* expected cruft */ + else + is_cruft = 1; /* unexpected */ + } + + if (per_dir_fn) + proceed = per_dir_fn(datap, + hdisk, + workdir->d_name, + hdir, + is_cruft); + if (!is_cruft && proceed && stop_at != STOP_AT_DIR) + holding_walk_dir(hdir, + datap, + stop_at, + per_file_fn, + per_chunk_fn); } - /* find all directories of the right format */ + closedir(dir); + amfree(hdir); +} - if(verbose) - printf("Scanning %s...\n", diskdir); - while((workdir = readdir(topdir)) != NULL) { - if(is_dot_or_dotdot(workdir->d_name) - || strcmp(workdir->d_name, "lost+found") == 0) { - continue; - } - entryname = newvstralloc(entryname, - diskdir, "/", workdir->d_name, NULL); - if(verbose) { - printf(" %s: ", workdir->d_name); - } - if(!is_dir(entryname)) { - if(verbose) - puts("skipping cruft file, perhaps you should delete it."); - } else if(!is_datestr(workdir->d_name)) { - if(verbose) - puts("skipping cruft directory, perhaps you should delete it."); - } else { - holding_list = insert_sort_sl(holding_list, workdir->d_name); - if(verbose) - puts("found Amanda directory."); - } +/* Recurse over all holding disks. + * + * Call per_disk_fn for each disk, per_dir_fn for each dir, and so on, stopping + * at the level given by stop_at. + * + * datap is passed, unchanged, to all holding_walk_fns. + * + * @param datap: generic user-data pointer + * @param stop_at: do not proceed beyond this level of the hierarchy + * @param per_disk_fn: function to call for each holding disk + * @param per_dir_fn: function to call for each holding dir + * @param per_file_fn: function to call for each holding file + * @param per_chunk_fn: function to call for each holding chunk + */ +static void +holding_walk( + gpointer datap, + stop_at_t stop_at, + holding_walk_fn per_disk_fn, + holding_walk_fn per_dir_fn, + holding_walk_fn per_file_fn, + holding_walk_fn per_chunk_fn) +{ + identlist_t il; + holdingdisk_t *hdisk_conf; + char *hdisk; + int proceed = 1; + + for (il = getconf_identlist(CNF_HOLDINGDISK); + il != NULL; + il = il->next) { + int is_cruft = 0; + hdisk_conf = lookup_holdingdisk(il->data); + + hdisk = holdingdisk_get_diskdir(hdisk_conf); + if (!is_dir(hdisk)) + is_cruft = 1; + + if (per_disk_fn) + proceed = per_disk_fn(datap, + NULL, + hdisk, + hdisk, + 0); + if (proceed && stop_at != STOP_AT_DISK) + holding_walk_disk(hdisk, + datap, + stop_at, + per_dir_fn, + per_file_fn, + per_chunk_fn); } - closedir(topdir); - amfree(entryname); - return holding_list; } +/* + * holding_get_* functions + */ +typedef struct { + GSList *result; + int fullpaths; +} holding_get_datap_t; -sl_t *scan_holdingdir(holding_list, holdp, datestamp) -sl_t *holding_list; -holdingdisk_t *holdp; -char *datestamp; +/* Functor for holding_get_*; adds 'element' or 'fqpath' to + * the result. + */ +static int +holding_get_walk_fn( + gpointer datap, + G_GNUC_UNUSED char *base, + char *element, + char *fqpath, + int is_cruft) { - DIR *workdir; - struct dirent *entry; - char *dirname = NULL; - char *destname = NULL; - disk_t *dp; - dumpfile_t file; + holding_get_datap_t *data = (holding_get_datap_t *)datap; + + /* ignore cruft */ + if (is_cruft) return 0; + + if (data->fullpaths) + data->result = g_slist_insert_sorted(data->result, + stralloc(fqpath), + g_compare_strings); + else + data->result = g_slist_insert_sorted(data->result, + stralloc(element), + g_compare_strings); + + /* don't proceed any deeper */ + return 0; +} + +GSList * +holding_get_disks(void) +{ + holding_get_datap_t data; + data.result = NULL; + data.fullpaths = 1; /* ignored anyway */ + + holding_walk((gpointer)&data, + STOP_AT_DISK, + holding_get_walk_fn, NULL, NULL, NULL); - dirname = vstralloc(holdp->diskdir, "/", datestamp, NULL); - if((workdir = opendir(dirname)) == NULL) { - if(errno != ENOENT) - log_add(L_INFO, "%s: could not open dir: %s", - dirname, strerror(errno)); - amfree(dirname); - return holding_list; + return data.result; +} + +GSList * +holding_get_files( + char *hdir, + int fullpaths) +{ + holding_get_datap_t data; + data.result = NULL; + data.fullpaths = fullpaths; + + if (hdir) { + holding_walk_dir(hdir, (gpointer)&data, + STOP_AT_FILE, + holding_get_walk_fn, NULL); + } else { + holding_walk((gpointer)&data, + STOP_AT_FILE, + NULL, NULL, holding_get_walk_fn, NULL); } - chdir(dirname); - while((entry = readdir(workdir)) != NULL) { - if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - if(is_emptyfile(entry->d_name)) - continue; + return data.result; +} - destname = newvstralloc(destname, - dirname, "/", entry->d_name, - NULL); - get_dumpfile(destname, &file); - if( file.type != F_DUMPFILE) { - if( file.type != F_CONT_DUMPFILE ) - log_add(L_INFO, "%s: ignoring cruft file.", entry->d_name); - continue; - } +GSList * +holding_get_file_chunks(char *hfile) +{ + holding_get_datap_t data; + data.result = NULL; + data.fullpaths = 1; - dp = lookup_disk(file.name, file.disk); + holding_walk_file(hfile, (gpointer)&data, + holding_get_walk_fn); + + return data.result; +} - if (dp == NULL) { - log_add(L_INFO, "%s: disk %s:%s not in database, skipping it.", - entry->d_name, file.name, file.disk); +GSList * +holding_get_files_for_flush( + GSList *dateargs) +{ + GSList *file_list, *file_elt; + GSList *date; + int date_matches; + dumpfile_t file; + GSList *result_list = NULL; + + /* loop over *all* files, checking each one's datestamp against the expressions + * in dateargs */ + file_list = holding_get_files(NULL, 1); + for (file_elt = file_list; file_elt != NULL; file_elt = file_elt->next) { + /* get info on that file */ + if (!holding_file_get_dumpfile((char *)file_elt->data, &file)) continue; + + if (file.type != F_DUMPFILE) { + dumpfile_free_data(&file); + continue; } - if(file.dumplevel < 0 || file.dumplevel > 9) { - log_add(L_INFO, "%s: ignoring file with bogus dump level %d.", - entry->d_name, file.dumplevel); - continue; + if (dateargs) { + date_matches = 0; + /* loop over date args, until we find a match */ + for (date = dateargs; date !=NULL; date = date->next) { + if (strcmp((char *)date->data, file.datestamp) == 0) { + date_matches = 1; + break; + } + } + } else { + /* if no date list was provided, then all dates match */ + date_matches = 1; + } + if (!date_matches) { + dumpfile_free_data(&file); + continue; } - holding_list = append_sl(holding_list, destname); + /* passed all tests -- we'll flush this file */ + result_list = g_slist_insert_sorted(result_list, + stralloc(file_elt->data), + g_compare_strings); + dumpfile_free_data(&file); } - closedir(workdir); - amfree(dirname); - amfree(destname); - return holding_list; -} + if (file_list) g_slist_free_full(file_list); -sl_t *get_flush(dateargs, datestamp, amflush, verbose) -sl_t *dateargs; -char *datestamp; /* don't do this date */ -int amflush, verbose; + return result_list; +} + +GSList * +holding_get_all_datestamps(void) { - sl_t *holding_list; - sl_t *date_list; - sle_t *datearg; - sle_t *date, *next_date; - holdingdisk_t *hdisk; - char current_dir[1000]; - - getcwd(current_dir, 999); - - holding_list = new_sl(); - - if(dateargs) { - int ok; - - date_list = pick_all_datestamp(verbose); - for(date = date_list->first; date != NULL;) { - next_date = date->next; - ok = 0; - for(datearg=dateargs->first; datearg != NULL && ok==0; - datearg = datearg->next) { - ok = match_datestamp(datearg->name, date->name); - } - if(ok == 0) { /* remove dir */ - remove_sl(date_list, date); - } - date = next_date; + GSList *all_files, *file; + GSList *datestamps = NULL; + + /* enumerate all files */ + all_files = holding_get_files(NULL, 1); + for (file = all_files; file != NULL; file = file->next) { + dumpfile_t dfile; + if (!holding_file_get_dumpfile((char *)file->data, &dfile)) + continue; + if (!g_slist_find_custom(datestamps, dfile.datestamp, + g_compare_strings)) { + datestamps = g_slist_insert_sorted(datestamps, + stralloc(dfile.datestamp), + g_compare_strings); } - } - else if (amflush) { - date_list = pick_datestamp(verbose); - } - else { - date_list = pick_all_datestamp(verbose); + dumpfile_free_data(&dfile); } - for(date = date_list->first; date !=NULL; date = date->next) { - if(!datestamp || strcmp(datestamp,date->name) != 0) { - for(hdisk = getconf_holdingdisks(); hdisk != NULL; - hdisk = hdisk->next) { - holding_list = scan_holdingdir(holding_list, hdisk, date->name); - } - } - } + g_slist_free_full(all_files); - free_sl(date_list); - date_list = NULL; - chdir(current_dir); - return(holding_list); + return datestamps; } - -sl_t *pick_all_datestamp(verbose) -int verbose; +off_t +holding_file_size( + char *hfile, + int strip_headers) { - sl_t *holding_list = NULL; - holdingdisk_t *hdisk; + dumpfile_t file; + char *filename; + off_t size = (off_t)0; + struct stat finfo; - holding_list = new_sl(); - for(hdisk = getconf_holdingdisks(); hdisk != NULL; hdisk = hdisk->next) - holding_list = scan_holdingdisk(holding_list, hdisk->diskdir, verbose); + /* (note: we don't use holding_get_file_chunks here because that would + * entail opening each file twice) */ - return holding_list; + /* Loop through all cont_filenames (subsequent chunks) */ + filename = stralloc(hfile); + while (filename != NULL && filename[0] != '\0') { + /* stat the file for its size */ + if (stat(filename, &finfo) == -1) { + dbprintf(_("stat %s: %s\n"), filename, strerror(errno)); + size = -1; + break; + } + size += (finfo.st_size+(off_t)1023)/(off_t)1024; + if (strip_headers) + size -= (off_t)(DISK_BLOCK_BYTES / 1024); + + /* get the header to look for cont_filename */ + if (!holding_file_get_dumpfile(filename, &file)) { + dbprintf(_("holding_file_size: open of %s failed.\n"), filename); + size = -1; + break; + } + + /* on to the next chunk */ + filename = newstralloc(filename, file.cont_filename); + dumpfile_free_data(&file); + } + amfree(filename); + return size; } -sl_t *pick_datestamp(verbose) -int verbose; +int +holding_file_unlink( + char *hfile) { - sl_t *holding_list; - sl_t *r_holding_list = NULL; - sle_t *dir; - char **directories = NULL; - int i; - char *answer = NULL; - char *a; - int ch; - char max_char = '\0', chupper = '\0'; - - holding_list = pick_all_datestamp(verbose); - - if(holding_list->nb_element == 0) { - return holding_list; - } - else if(holding_list->nb_element == 1 || !verbose) { - return holding_list; + GSList *chunklist; + GSList *chunk; + + chunklist = holding_get_file_chunks(hfile); + if (!chunklist) + return 0; + + for (chunk = chunklist; chunk != NULL; chunk = chunk->next) { + if (unlink((char *)chunk->data)<0) { + dbprintf(_("holding_file_unlink: could not unlink %s: %s\n"), + (char *)chunk->data, strerror(errno)); + return 0; + } } - else { - directories = alloc((holding_list->nb_element) * sizeof(char *)); - for(dir = holding_list->first, i=0; dir != NULL; dir = dir->next,i++) { - directories[i] = dir->name; - } - - while(1) { - puts("\nMultiple Amanda directories, please pick one by letter:"); - for(dir = holding_list->first, max_char = 'A'; - dir != NULL && max_char <= 'Z'; - dir = dir->next, max_char++) { - printf(" %c. %s\n", max_char, dir->name); - } - max_char--; - printf("Select directories to flush [A..%c]: [ALL] ", max_char); - fflush(stdout); fflush(stderr); - amfree(answer); - if ((answer = agets(stdin)) == NULL) { - clearerr(stdin); - continue; - } - a = answer; - while ((ch = *a++) != '\0' && isspace(ch)) {} - if(ch == '\0' || strncasecmp(a, "ALL", 3) == 0) { - break; - } - do { - if (isspace(ch) || ch == ',') { - continue; - } - chupper = toupper(ch); - if (chupper < 'A' || chupper > max_char) { - free_sl(r_holding_list); - r_holding_list = NULL; - break; - } - r_holding_list = append_sl(r_holding_list, - directories[chupper - 'A']); - } while ((ch = *a++) != '\0'); - if (r_holding_list && ch == '\0') { - free_sl(holding_list); - holding_list = r_holding_list; - break; - } - } - } - amfree(directories); - amfree(answer); - return holding_list; + return 1; } - -filetype_t get_amanda_names(fname, hostname, diskname, level) -char *fname, **hostname, **diskname; -int *level; +int +holding_file_get_dumpfile( + char * fname, + dumpfile_t *file) { - dumpfile_t file; char buffer[DISK_BLOCK_BYTES]; int fd; - *hostname = *diskname = NULL; - if((fd = open(fname, O_RDONLY)) == -1) - return F_UNKNOWN; + memset(buffer, 0, sizeof(buffer)); - if(fullread(fd, buffer, sizeof(buffer)) != sizeof(buffer)) { - aclose(fd); - return F_UNKNOWN; + fh_init(file); + file->type = F_UNKNOWN; + if((fd = robust_open(fname, O_RDONLY, 0)) == -1) + return 0; + + if(full_read(fd, buffer, SIZEOF(buffer)) != sizeof(buffer)) { + aclose(fd); + return 0; } aclose(fd); - parse_file_header(buffer,&file,sizeof(buffer)); - if(file.type != F_DUMPFILE && file.type != F_CONT_DUMPFILE) { - return file.type; + parse_file_header(buffer, file, SIZEOF(buffer)); + return 1; +} + +/* + * Cleanup + */ + +typedef struct { + corrupt_dle_fn corrupt_dle; + FILE *verbose_output; +} holding_cleanup_datap_t; + +static int +holding_cleanup_disk( + gpointer datap, + G_GNUC_UNUSED char *base, + G_GNUC_UNUSED char *element, + char *fqpath, + int is_cruft) +{ + holding_cleanup_datap_t *data = (holding_cleanup_datap_t *)datap; + + if (data->verbose_output) { + if (is_cruft) + g_fprintf(data->verbose_output, + _("Invalid holding disk '%s'\n"), fqpath); + else + g_fprintf(data->verbose_output, + _("Cleaning up holding disk '%s'\n"), fqpath); } - *hostname = stralloc(file.name); - *diskname = stralloc(file.disk); - *level = file.dumplevel; - return file.type; + return 1; } - -void get_dumpfile(fname, file) -char *fname; -dumpfile_t *file; +static int +holding_cleanup_dir( + gpointer datap, + G_GNUC_UNUSED char *base, + char *element, + char *fqpath, + int is_cruft) { - char buffer[DISK_BLOCK_BYTES]; - int fd; + holding_cleanup_datap_t *data = (holding_cleanup_datap_t *)datap; - fh_init(file); - file->type = F_UNKNOWN; - if((fd = open(fname, O_RDONLY)) == -1) - return; + if (is_cruft) { + if (data->verbose_output) + g_fprintf(data->verbose_output, + _("Invalid holding directory '%s'\n"), fqpath); + return 0; + } - if(fullread(fd, buffer, sizeof(buffer)) != sizeof(buffer)) { - aclose(fd); - return; + /* try removing it */ + if (rmdir(fqpath) == 0) { + /* success, so don't try to walk into it */ + if (data->verbose_output) + g_fprintf(data->verbose_output, + _(" ..removed empty directory '%s'\n"), element); + return 0; } - aclose(fd); - parse_file_header(buffer,file,sizeof(buffer)); - return; -} + if (data->verbose_output) + g_fprintf(data->verbose_output, + _(" ..cleaning up holding directory '%s'\n"), element); + return 1; +} -long size_holding_files(holding_file) -char *holding_file; +static int +holding_cleanup_file( + gpointer datap, + G_GNUC_UNUSED char *base, + char *element, + char *fqpath, + int is_cruft) { - int fd; - int buflen; - char buffer[DISK_BLOCK_BYTES]; + holding_cleanup_datap_t *data = (holding_cleanup_datap_t *)datap; + int stat; + int l; dumpfile_t file; - char *filename; - long size=0; - struct stat finfo; + disk_t *dp; - filename = stralloc(holding_file); - while(filename != NULL && filename[0] != '\0') { - if((fd = open(filename,O_RDONLY)) == -1) { - fprintf(stderr,"size_holding_files: open of %s failed: %s\n",filename,strerror(errno)); - amfree(filename); - return -1; - } - buflen = fullread(fd, buffer, sizeof(buffer)); - parse_file_header(buffer, &file, buflen); - close(fd); - if(stat(filename, &finfo) == -1) { - printf("stat %s: %s\n", filename, strerror(errno)); - finfo.st_size = 0; - } - size += (finfo.st_size+1023)/1024; - filename = newstralloc(filename, file.cont_filename); + if (is_cruft) { + if (data->verbose_output) + g_fprintf(data->verbose_output, + _("Invalid holding file '%s'\n"), element); + return 0; } - amfree(filename); - return size; -} -int unlink_holding_files( holding_file ) -char *holding_file; -{ - int fd; - int buflen; - char buffer[DISK_BLOCK_BYTES]; - dumpfile_t file; - char *filename; + stat = holding_file_get_dumpfile(fqpath, &file); - filename = stralloc(holding_file); - while(filename != NULL && filename[0] != '\0') { - if((fd = open(filename,O_RDONLY)) == -1) { - fprintf(stderr,"unlink_holding_files: open of %s failed: %s\n",filename,strerror(errno)); - amfree(filename); - return 0; + if (!stat) { + if (data->verbose_output) + g_fprintf(data->verbose_output, + _("Could not read read header from '%s'\n"), element); + dumpfile_free_data(&file); + return 0; + } + + if (file.type != F_DUMPFILE && file.type != F_CONT_DUMPFILE) { + if (data->verbose_output) + g_fprintf(data->verbose_output, + _("File '%s' is not a dump file\n"), element); + dumpfile_free_data(&file); + return 0; + } + + if(file.dumplevel < 0 || file.dumplevel > 9) { + if (data->verbose_output) + g_fprintf(data->verbose_output, + _("File '%s' has invalid level %d\n"), element, file.dumplevel); + dumpfile_free_data(&file); + return 0; + } + + dp = lookup_disk(file.name, file.disk); + + if (dp == NULL) { + if (data->verbose_output) + g_fprintf(data->verbose_output, + _("File '%s' is for '%s:%s', which is not in the disklist\n"), + element, file.name, file.disk); + dumpfile_free_data(&file); + return 0; + } + + if ((l = strlen(element)) >= 7 && strncmp(&fqpath[l-4],".tmp",4) == 0) { + char *destname; + + /* generate a name without '.tmp' */ + destname = stralloc(fqpath); + destname[strlen(destname) - 4] = '\0'; + + /* OK, it passes muster -- rename it to salvage some data, + * and mark the DLE as corrupted */ + if (data->verbose_output) + g_fprintf(data->verbose_output, + _("Processing partial holding file '%s'\n"), element); + + if(rename_tmp_holding(destname, 0)) { + if (data->corrupt_dle) + data->corrupt_dle(dp->host->hostname, dp->name); + } else { + dbprintf(_("rename_tmp_holding(%s) failed\n"), destname); + if (data->verbose_output) + g_fprintf(data->verbose_output, + _("Rename of '%s' to '%s' failed.\n"), element, destname); } - buflen = fullread(fd, buffer, sizeof(buffer)); - parse_file_header(buffer, &file, buflen); - close(fd); - unlink(filename); - filename = newstralloc(filename,file.cont_filename); + + amfree(destname); } - amfree(filename); + + dumpfile_free_data(&file); return 1; } +void +holding_cleanup( + corrupt_dle_fn corrupt_dle, + FILE *verbose_output) +{ + holding_cleanup_datap_t data; + data.corrupt_dle = corrupt_dle; + data.verbose_output = verbose_output; + + holding_walk((gpointer)&data, + STOP_AT_FILE, + holding_cleanup_disk, + holding_cleanup_dir, + holding_cleanup_file, + NULL); +} + +/* + * Application support + */ -int rename_tmp_holding( holding_file, complete ) -char *holding_file; -int complete; +void +holding_set_origsize( + char *holding_file, + off_t orig_size) +{ + int fd; + size_t buflen; + char buffer[DISK_BLOCK_BYTES]; + char *read_buffer; + dumpfile_t file; + + if((fd = robust_open(holding_file, O_RDWR, 0)) == -1) { + dbprintf(_("holding_set_origsize: open of %s failed: %s\n"), + holding_file, strerror(errno)); + return; + } + + buflen = full_read(fd, buffer, SIZEOF(buffer)); + if (buflen <= 0) { + dbprintf(_("holding_set_origsize: %s: empty file?\n"), holding_file); + return; + } + parse_file_header(buffer, &file, (size_t)buflen); + lseek(fd, (off_t)0, SEEK_SET); + file.orig_size = orig_size; + read_buffer = build_header(&file, NULL, DISK_BLOCK_BYTES); + full_write(fd, read_buffer, DISK_BLOCK_BYTES); + dumpfile_free_data(&file); + amfree(read_buffer); + close(fd); +} + +int +rename_tmp_holding( + char * holding_file, + int complete) { int fd; - int buflen; + size_t buflen; char buffer[DISK_BLOCK_BYTES]; dumpfile_t file; char *filename; char *filename_tmp = NULL; + memset(buffer, 0, sizeof(buffer)); filename = stralloc(holding_file); while(filename != NULL && filename[0] != '\0') { filename_tmp = newvstralloc(filename_tmp, filename, ".tmp", NULL); - if((fd = open(filename_tmp,O_RDONLY)) == -1) { - fprintf(stderr,"rename_tmp_holding: open of %s failed: %s\n",filename_tmp,strerror(errno)); + if((fd = robust_open(filename_tmp,O_RDONLY, 0)) == -1) { + dbprintf(_("rename_tmp_holding: open of %s failed: %s\n"),filename_tmp,strerror(errno)); amfree(filename); amfree(filename_tmp); return 0; } - buflen = fullread(fd, buffer, sizeof(buffer)); + buflen = full_read(fd, buffer, SIZEOF(buffer)); close(fd); if(rename(filename_tmp, filename) != 0) { - fprintf(stderr, - "rename_tmp_holding: could not rename \"%s\" to \"%s\": %s", + dbprintf(_("rename_tmp_holding: could not rename \"%s\" to \"%s\": %s"), filename_tmp, filename, strerror(errno)); } - if (buflen == 0) { - fprintf(stderr,"rename_tmp_holding: %s: empty file?\n", filename); + if (buflen <= 0) { + dbprintf(_("rename_tmp_holding: %s: empty file?\n"), filename); amfree(filename); amfree(filename_tmp); return 0; } - parse_file_header(buffer, &file, buflen); + parse_file_header(buffer, &file, (size_t)buflen); if(complete == 0 ) { - if((fd = open(filename, O_RDWR)) == -1) { - fprintf(stderr, "rename_tmp_holdingX: open of %s failed: %s\n", + char * header; + if((fd = robust_open(filename, O_RDWR, 0)) == -1) { + dbprintf(_("rename_tmp_holdingX: open of %s failed: %s\n"), filename, strerror(errno)); + dumpfile_free_data(&file); amfree(filename); amfree(filename_tmp); return 0; } file.is_partial = 1; - build_header(buffer, &file, sizeof(buffer)); - fullwrite(fd, buffer, sizeof(buffer)); + if (debug_holding > 1) + dump_dumpfile_t(&file); + header = build_header(&file, NULL, DISK_BLOCK_BYTES); + if (!header) /* this shouldn't happen */ + error(_("header does not fit in %zd bytes"), (size_t)DISK_BLOCK_BYTES); + if (full_write(fd, header, DISK_BLOCK_BYTES) != DISK_BLOCK_BYTES) { + dbprintf(_("rename_tmp_holding: writing new header failed: %s"), + strerror(errno)); + dumpfile_free_data(&file); + amfree(filename); + amfree(filename_tmp); + free(header); + close(fd); + return 0; + } + free(header); close(fd); } filename = newstralloc(filename, file.cont_filename); + dumpfile_free_data(&file); } amfree(filename); amfree(filename_tmp); @@ -549,78 +959,36 @@ int complete; } -void cleanup_holdingdisk(diskdir, verbose) -char *diskdir; -int verbose; -{ - DIR *topdir; - struct dirent *workdir; - - if((topdir = opendir(diskdir)) == NULL) { - if(verbose && errno != ENOENT) - printf("Warning: could not open holding dir %s: %s\n", - diskdir, strerror(errno)); - return; - } - - /* find all directories of the right format */ - - if(verbose) - printf("Scanning %s...\n", diskdir); - chdir(diskdir); - while((workdir = readdir(topdir)) != NULL) { - if(is_dot_or_dotdot(workdir->d_name) - || strcmp(workdir->d_name, "lost+found") == 0) { - continue; - } - if(verbose) - printf(" %s: ", workdir->d_name); - if(!is_dir(workdir->d_name)) { - if(verbose) - puts("skipping cruft file, perhaps you should delete it."); - } - else if(!is_datestr(workdir->d_name)) { - if(verbose) - puts("skipping cruft directory, perhaps you should delete it."); - } - else if(rmdir(workdir->d_name) == 0) { - if(verbose) - puts("deleted empty Amanda directory."); - } - } - closedir(topdir); -} - - -int mkholdingdir(diskdir) -char *diskdir; +int +mkholdingdir( + char * diskdir) { struct stat stat_hdp; int success = 1; if (mkpdir(diskdir, 0770, (uid_t)-1, (gid_t)-1) != 0 && errno != EEXIST) { - log_add(L_WARNING, "WARNING: could not create parents of %s: %s", + log_add(L_WARNING, _("WARNING: could not create parents of %s: %s"), diskdir, strerror(errno)); success = 0; } else if (mkdir(diskdir, 0770) != 0 && errno != EEXIST) { - log_add(L_WARNING, "WARNING: could not create %s: %s", + log_add(L_WARNING, _("WARNING: could not create %s: %s"), diskdir, strerror(errno)); success = 0; } else if (stat(diskdir, &stat_hdp) == -1) { - log_add(L_WARNING, "WARNING: could not stat %s: %s", + log_add(L_WARNING, _("WARNING: could not stat %s: %s"), diskdir, strerror(errno)); success = 0; } else { if (!S_ISDIR((stat_hdp.st_mode))) { - log_add(L_WARNING, "WARNING: %s is not a directory", + log_add(L_WARNING, _("WARNING: %s is not a directory"), diskdir); success = 0; } else if (access(diskdir,W_OK) != 0) { - log_add(L_WARNING, "WARNING: directory %s is not writable", + log_add(L_WARNING, _("WARNING: directory %s is not writable"), diskdir); success = 0; }