/* $Id: libmondo-filelist.c 3060 2012-11-10 04:05:37Z bruno $ */ /** * @file * Functions which create, chop, and edit the filelist. */ #include "my-stuff.h" #include "mondostructures.h" #include "lib-common-externs.h" #include "libmondo-filelist.h" #include "libmondo-string-EXT.h" #include "libmondo-files-EXT.h" #include "libmondo-fork-EXT.h" #include "libmondo-gui-EXT.h" #include "libmondo-tools-EXT.h" #include "mr_mem.h" #include "mr_str.h" #include #include #include #include #include #include #include extern ssize_t getline(char **lineptr, size_t * n, FILE * stream); extern char *MONDO_LOGFILE; /* Reference to global bkpinfo */ extern struct s_bkpinfo *bkpinfo; /*@unused@*/ //static char cvsid[] = "$Id: libmondo-filelist.c 3060 2012-11-10 04:05:37Z bruno $"; /** * Number of lines in the filelist last loaded. * @warning This implies that two filesets cannot be loaded at once. * @ingroup globalGroup */ long g_original_noof_lines_in_filelist = 0; /** * Number of filesets in the current backup. * @ingroup globalGroup */ long g_noof_sets = 0; extern bool g_text_mode; extern newtComponent g_progressForm; extern int g_currentY; extern int g_noof_rows; extern char *g_getfacl; extern char *g_getfattr; /** * @addtogroup filelistGroup * @{ */ /** * Call chop_filelist() to chop the filelist into sets. * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->image_devs * - @c bkpinfo->optimal_set_size * - @c bkpinfo->scratchdir * - @c bkpinfo->tmpdir * @see chop_filelist */ int call_filelist_chopper() { /*@ buffers *********************** */ char *dev = NULL; char *filelist = NULL; char *tempfile = NULL; long noof_sets; /*@ pointers ********************** */ char *ptr; FILE *fout; /*@ int *************************** */ int i, retval = 0; mvaddstr_and_log_it(g_currentY, 0, "Dividing filelist into sets"); log_to_screen("Dividing filelist into sets. Please wait."); i = 0; mr_asprintf(&filelist, "%s/archives/filelist.full", bkpinfo->scratchdir); if (!does_file_exist(filelist)) { log_it("filelist %s not found", filelist); mr_free(filelist); fatal_error("call_filelist_chopper() -- filelist not found!"); } noof_sets = chop_filelist(filelist, bkpinfo->tmpdir, bkpinfo->optimal_set_size); mr_free(filelist); estimate_noof_media_required(noof_sets); // for cosmetic purposes mr_asprintf(&tempfile, "%s/biggielist.txt", bkpinfo->tmpdir); if (!(fout = fopen(tempfile, "a"))) { log_OS_error("Cannot append to biggielist"); retval++; mr_free(tempfile); return (retval); } mr_free(tempfile); log_it(bkpinfo->image_devs); ptr = bkpinfo->image_devs; malloc_string(dev); while (ptr && *ptr) { strcpy(dev, ptr); log_it("Examining imagedev %s", dev); for (i = 0; i < (int) strlen(dev) && dev[i] != ' '; i++); dev[i] = '\0'; if (!strlen(dev)) { continue; } fprintf(fout, "%s\n", dev); log_it("Adding '%s' to biggielist", dev); if ((ptr = strchr(ptr, ' '))) { ptr++; } } paranoid_fclose(fout); mvaddstr_and_log_it(g_currentY++, 74, "Done."); paranoid_free(dev); return (retval); } int sort_file(char *orig_fname) { char *tmp_fname = NULL; char *command = NULL; int retval = 0; log_msg(5, "Sorting file %s", orig_fname); if (!does_file_exist(orig_fname)) { log_msg(2, "file %s empty", orig_fname); return (0); } // no sense in trying to sort an empty file mr_asprintf(&tmp_fname, "%s/sortfile", bkpinfo->tmpdir); mr_asprintf(&command, "sort %s > %s 2>> %s", orig_fname, tmp_fname, MONDO_LOGFILE); retval = system(command); mr_free(command); if (retval) { log_msg(2, "Failed to sort %s - oh dear", orig_fname); } else { log_msg(5, "Sorted %s --> %s OK. Copying it back to %s now", orig_fname, tmp_fname, orig_fname); mr_asprintf(&command, "mv -f %s %s", tmp_fname, orig_fname); retval += run_program_and_log_output(command, 5); mr_free(command); if (retval) { log_msg(2, "Failed to copy %s back to %s - oh dear", tmp_fname, orig_fname); } else { log_msg(5, "%s was sorted OK.", orig_fname); } } mr_free(tmp_fname); log_msg(5, "Finished sorting file %s", orig_fname); return (retval); } /** * Chop the filelist into sets. * Each fileset is a list of files whose total (uncompressed) size is usually * about X KB. Files bigger than 8X KB are placed in a "biggielist"; they will * be sliced and compressed separately from the regular files. * * @param filelist The big filelist (filelist.full) to chop up. * @param outdir The directory to place the files (filelist.N where N is * an integer, biggielist.txt, and LAST-FILELIST-NUMBER) created * @param maxsetsizeK Optimal size of a fileset (X above). * @return number of errors encountered (0 for success). */ int chop_filelist(char *filelist, char *outdir, long maxsetsizeK) { /*@ long ****************************************/ long lino = 0; long max_sane_size_for_a_file; long curr_set_size; long noof_lines; long siz; /*@ int **************************************** */ int i; long curr_set_no; /*@ buffers ************************************* */ char *outfname = NULL; char *biggie_fname = NULL; char *incoming; char *tmp = NULL; char *p = NULL; /*@ pointers *********************************** */ FILE *fin; FILE *fout; FILE *fbig; /*@ structures ********************************* */ struct stat buf; int err = 0; assert_string_is_neither_NULL_nor_zerolength(filelist); assert_string_is_neither_NULL_nor_zerolength(outdir); assert(maxsetsizeK > 0); max_sane_size_for_a_file = 64L * 1024L; // max_sane_size_for_a_file = maxsetsizeK*2; // if (max_sane_size_for_a_file > 32*1024) // { max_sane_size_for_a_file = 32*1024; } log_it("filelist=%s;", filelist); open_evalcall_form("Dividing filelist into sets"); noof_lines = count_lines_in_file(filelist); if (!(fin = fopen(filelist, "r"))) { log_OS_error("Cannot openin filelist"); return (0); } curr_set_no = 0; curr_set_size = 0; mr_asprintf(&outfname, "%s/filelist.%ld", outdir, curr_set_no); mr_asprintf(&biggie_fname, "%s/biggielist.txt", outdir); log_it("outfname=%s; biggie_fname=%s", outfname, biggie_fname); if (!(fbig = fopen(biggie_fname, "w"))) { log_OS_error("Cannot openout biggie_fname"); err++; mr_free(outfname); mr_free(biggie_fname); return (curr_set_no + 1); } if (!(fout = fopen(outfname, "w"))) { log_OS_error("Cannot openout outfname"); err++; mr_free(outfname); mr_free(biggie_fname); return (curr_set_no + 1); } incoming = malloc(MAX_STR_LEN * 2); p = fgets(incoming, MAX_STR_LEN * 2 - 1, fin); while (!feof(fin) && (p != NULL)) { lino++; i = strlen(incoming) - 1; if (i < 0) { i = 0; } if (i > MAX_STR_LEN - 1) { incoming[MAX_STR_LEN - 30] = '\0'; log_msg(1, "Warning - truncating file %s's name", incoming); err++; } if (incoming[i] < 32) { incoming[i] = '\0'; } if (!strncmp(incoming, "/dev/", 5)) { siz = 1; } else if (lstat(incoming, &buf) != 0) { siz = 0; } else { siz = (long) (buf.st_size >> 10); } if (siz > max_sane_size_for_a_file) { log_msg(10, "Adding %s to big files\n", incoming); fprintf(fbig, "%s\n", incoming); } else { curr_set_size += siz; log_msg(10, "Adding %s to filelist %d\n", incoming, curr_set_no); fprintf(fout, "%s\n", incoming); if (curr_set_size > maxsetsizeK) { paranoid_fclose(fout); sort_file(outfname); mr_free(outfname); curr_set_no++; curr_set_size = 0; mr_asprintf(&outfname, "%s/filelist.%ld", outdir, curr_set_no); if (!(fout = fopen(outfname, "w"))) { log_OS_error("Unable to openout outfname"); err++; mr_free(outfname); mr_free(biggie_fname); paranoid_free(incoming); return (curr_set_no + 1); } update_evalcall_form((int) (lino * 100 / noof_lines)); } } p = fgets(incoming, MAX_STR_LEN * 2 - 1, fin); } paranoid_free(incoming); paranoid_fclose(fin); paranoid_fclose(fout); paranoid_fclose(fbig); if (length_of_file(outfname) <= 2) { unlink(outfname); g_noof_sets--; } g_noof_sets = curr_set_no; sort_file(outfname); mr_free(outfname); sort_file(biggie_fname); mr_free(biggie_fname); mr_asprintf(&outfname, "%s/LAST-FILELIST-NUMBER", outdir); mr_asprintf(&tmp, "%ld", curr_set_no); if (write_one_liner_data_file(outfname, tmp)) { log_OS_error ("Unable to echo write one-liner to LAST-FILELIST-NUMBER"); err = 1; } mr_free(tmp); mr_free(outfname); if (curr_set_no == 0) { mr_asprintf(&tmp, "Only one fileset. Fine."); } else { mr_asprintf(&tmp, "Filelist divided into %ld sets", curr_set_no + 1); } log_msg(1, tmp); mr_free(tmp); close_evalcall_form(); /* This is to work around an obscure bug in Newt; open a form, close it, carry on... I don't know why it works but it works. If you don't do this then update_progress_form() won't show the "time taken / time remaining" line. The bug only crops up AFTER the call to chop_filelist(). Weird. */ #ifndef _XWIN if (!g_text_mode) { open_progress_form("", "", "", "", 100); newtPopHelpLine(); newtFormDestroy(g_progressForm); newtPopWindow(); } #endif return (err ? 0 : curr_set_no + 1); } /** * Free all the memory used by a filelist structure. * Since this may take a long time for large filelists, a progress bar will be displayed. * @param filelist The filelist to free. */ void free_filelist(struct s_node *filelist) { /*@ int's ******************************************************* */ static int depth = 0; int percentage; /*@ long's ****************************************************** */ static long i = 0; /*@ end vars **************************************************** */ assert(filelist != NULL); if (depth == 0) { open_evalcall_form("Freeing memory"); log_to_screen("Freeing memory formerly occupied by filelist"); } depth++; if (filelist->ch == '\0') { if (!(i++ % 1111)) { percentage = (int) (i * 100 / g_original_noof_lines_in_filelist); update_evalcall_form(percentage); } } if (filelist->right) { free_filelist(filelist->right); filelist->right = NULL; } if (filelist->down) { /* if (!(i++ %39999)) { update_evalcall_form(0); } */ free_filelist(filelist->down); filelist->down = NULL; } filelist->ch = '\0'; paranoid_free(filelist); depth--; if (depth == 0) { close_evalcall_form(); log_it("Finished freeing memory"); } } int call_exe_and_pipe_output_to_fd(char *syscall, FILE * pout) { FILE *pattr; char *tmp; char *p; pattr = popen(syscall, "r"); if (!pattr) { log_msg(1, "Failed to open fattr() %s", syscall); return (1); } if (feof(pattr)) { log_msg(1, "Failed to call fattr() %s", syscall); paranoid_pclose(pattr); return (2); } malloc_string(tmp); for (p = fgets(tmp, MAX_STR_LEN, pattr); !feof(pattr) && (p != NULL); p = fgets(tmp, MAX_STR_LEN, pattr)) { fputs(tmp, pout); } paranoid_pclose(pattr); paranoid_free(tmp); return (0); } int gen_aux_list(char *filelist, char *syscall_sprintf, char *auxlist_fname) { FILE *fin; FILE *pout; char *pout_command = NULL; char *syscall; char *file_to_analyze; char *strtmp = NULL; char *tmp = NULL; char *p = NULL; int i; if (!(fin = fopen(filelist, "r"))) { log_msg(1, "Cannot openin filelist %s", filelist); return (1); } mr_asprintf(&pout_command, "gzip -c1 > %s", auxlist_fname); if (!(pout = popen(pout_command, "w"))) { log_msg(1, "Cannot openout auxlist_fname %s", auxlist_fname); fclose(fin); mr_free(pout_command); return (4); } mr_free(pout_command); malloc_string(file_to_analyze); for (p = fgets(file_to_analyze, MAX_STR_LEN, fin); !feof(fin) && (p != NULL); p = fgets(file_to_analyze, MAX_STR_LEN, fin)) { i = strlen(file_to_analyze); if (i > 0 && file_to_analyze[i - 1] < 32) { file_to_analyze[i - 1] = '\0'; } log_msg(8, "Analyzing %s", file_to_analyze); tmp = mr_stresc(file_to_analyze, " `$\\\"(){}[]'*?&|!#~", '\\'); mr_asprintf(&strtmp, syscall_sprintf, tmp); paranoid_free(tmp); mr_asprintf(&syscall, "%s 2>> /dev/null", strtmp); // " MONDO_LOGFILE); paranoid_free(strtmp); log_msg(20,"calling %s\n",syscall); call_exe_and_pipe_output_to_fd(syscall, pout); paranoid_free(syscall); } paranoid_fclose(fin); paranoid_pclose(pout); paranoid_free(file_to_analyze); return (0); } int get_acl_list(char *filelist, char *facl_fname) { char *command = NULL; int retval = 0; if (g_getfacl != NULL) { mr_asprintf(&command, "touch \"%s\"", facl_fname); run_program_and_log_output(command, 8); mr_free(command); retval = gen_aux_list(filelist, "getfacl --all-effective -P \"%s\"", facl_fname); } return (retval); } int get_fattr_list(char *filelist, char *fattr_fname) { char *command; int retval = 0; if (g_getfattr != NULL) { mr_asprintf(&command, "touch \"%s\"", fattr_fname); run_program_and_log_output(command, 8); mr_free(command); retval = gen_aux_list(filelist, "getfattr --en=hex -m - -h -d \"%s\"", fattr_fname); } return (retval); } int set_EXAT_list(char *orig_msklist, char *original_exat_fname, char *executable) { const int my_depth = 8; char *command = NULL; char *syscall_pin = NULL; char *syscall_pout = NULL; char *incoming; char *current_subset_file, *current_master_file; char *masklist = NULL; int retval = 0; int i; char *p, *q, *r; char *tmp = NULL; FILE *pin, *pout, *faclin; log_msg(1, "set_EXAT_list(%s, %s, %s)", orig_msklist, original_exat_fname, executable); if (!orig_msklist || !orig_msklist[0] || !does_file_exist(orig_msklist)) { log_msg(1, "No masklist provided. I shall therefore set ALL attributes."); if (strstr(executable,"acl")) { /* setfacl needs no additional option for physical walk */ mr_asprintf(&tmp,""); } else { /* setfattr needs option -h for physical walk */ mr_asprintf(&tmp,"-h"); } mr_asprintf(&command, "gzip -dc %s | %s %s --restore - 2>> %s", original_exat_fname, executable, tmp, MONDO_LOGFILE); mr_free(tmp); log_msg(1, "command = %s", command); retval = system(command); mr_free(command); log_msg(1, "Returning w/ retval=%d", retval); return (retval); } if (length_of_file(original_exat_fname) <= 0) { log_msg(1, "original_exat_fname %s is empty or missing, so no need to set EXAT list", original_exat_fname); return (0); } malloc_string(incoming); malloc_string(current_subset_file); malloc_string(current_master_file); mr_asprintf(&masklist, "%s/masklist", bkpinfo->tmpdir); mr_asprintf(&command, "cp -f %s %s", orig_msklist, masklist); run_program_and_log_output(command, 1); mr_free(command); sort_file(masklist); current_subset_file[0] = current_master_file[0] = '\0'; mr_asprintf(&syscall_pout, "%s --restore - 2>> %s", executable, MONDO_LOGFILE); log_msg(1, "syscall_pout = %s", syscall_pout); pout = popen(syscall_pout, "w"); mr_free(syscall_pout); if (!pout) { log_it("Unable to openout to syscall_pout"); mr_free(masklist); return (1); } mr_asprintf(&syscall_pin, "gzip -dc %s", original_exat_fname); log_msg(1, "syscall_pin = %s", syscall_pin); pin = popen(syscall_pin, "r"); mr_free(syscall_pin); if (!pin) { pclose(pout); log_it("Unable to openin from syscall"); return (1); } faclin = fopen(masklist, "r"); if (!faclin) { pclose(pin); pclose(pout); log_it("Unable to openin masklist"); mr_free(masklist); return (1); } // printf("Hi there. Starting the loop\n"); r = fgets(current_subset_file, MAX_STR_LEN, faclin); r = fgets(incoming, MAX_STR_LEN, pin); while (!feof(pin) && !feof(faclin) && (r != NULL)) { // printf("incoming = %s", incoming); strcpy(current_master_file, incoming + 8); p = current_subset_file; if (*p == '/') { p++; } i = strlen(p); if (i > 0 && p[i - 1] < 32) { p[i - 1] = '\0'; } q = current_master_file; if (*q == '/') { q++; } i = strlen(q); if (i > 0 && q[i - 1] < 32) { q[i - 1] = '\0'; } i = strcmp(p, q); log_msg(my_depth, "'%s' v '%s' --> %d\n", p, q, i); // printf("%s v %s --> %d\n", p, q, i); if (i < 0) { // read another subset file in. log_msg(my_depth, "Reading next subset line in\n\n"); r = fgets(current_subset_file, MAX_STR_LEN, faclin); continue; } if (!i) { fputs(incoming, pout); } r = fgets(incoming, MAX_STR_LEN, pin); if (!i) { log_msg(my_depth, "Copying master %s", q); } // if (!i) { printf("Match --- %s\n", q); } while (!feof(pin) && strncmp(incoming, "# file: ", 8)) { if (!i) { // printf("%s", incoming); fputs(incoming, pout); } r = fgets(incoming, MAX_STR_LEN, pin); } if (!i) { r = fgets(current_subset_file, MAX_STR_LEN, faclin); } } while (!feof(pin)) { r = fgets(incoming, MAX_STR_LEN, pin); } fclose(faclin); pclose(pin); pclose(pout); // printf("OK, loop is done\n"); unlink(masklist); mr_free(masklist); paranoid_free(current_subset_file); paranoid_free(current_master_file); paranoid_free(incoming); return (retval); } int set_fattr_list(char *masklist, char *fattr_fname) { if (find_home_of_exe("setfattr")) { return (set_EXAT_list(masklist, fattr_fname, "setfattr")); } else { log_msg(1, "ERROR: set_EXAT_list: setfattr doesn't exist"); return(0); } } int set_acl_list(char *masklist, char *acl_fname) { if (find_home_of_exe("setfacl")) { return (set_EXAT_list(masklist, acl_fname, "setfacl")); } else { log_msg(1, "ERROR: set_EXAT_list: setfacl doesn't exist"); return(0); } } /** * Get the number of the last fileset in the backup. * @param bkpinfo The backup information structure. Only the @c bkpinfo->tmpdir field is used. * @return The last filelist number. * @note This function should only be called at restore-time. */ int get_last_filelist_number() { /*@ buffers ***************************************************** */ char val_sz[MAX_STR_LEN]; char *cfg_fname = NULL; /*@ long ******************************************************** */ int val_i; /*@ end vars **************************************************** */ assert(bkpinfo != NULL); strcpy(val_sz,""); mr_asprintf(&cfg_fname, "%s/mondo-restore.cfg", bkpinfo->tmpdir); read_cfg_var(cfg_fname, "last-filelist-number", val_sz); mr_free(cfg_fname); val_i = atoi(val_sz); if (val_i <= 0) { val_i = 500; } return (val_i); } /** * Add a string at @p startnode. * @param startnode The node to start at when searching for where to add the string. * @param string_to_add The string to add. * @return 0 for success, 1 for failure. * @bug I don't understand this function. Would someone care to explain it? */ int add_string_at_node(struct s_node *startnode, char *string_to_add) { /*@ int ******************************************************** */ int noof_chars; int i; int res; /*@ sturctures ************************************************* */ struct s_node *node, *newnode; /*@ char ****************************************************** */ char char_to_add; /*@ bools ****************************************************** */ const bool sosodef = FALSE; static int depth = 0; static char original_string[MAX_STR_LEN]; assert(startnode != NULL); assert(string_to_add != NULL); if (!depth) { strcpy(original_string, string_to_add); } noof_chars = strlen(string_to_add) + 1; /* we include the '\0' */ /* walk across tree if necessary */ node = startnode; char_to_add = string_to_add[0]; if (node->right != NULL && node->ch < char_to_add) { log_msg(7, "depth=%d --- going RIGHT ... %c-->%c", depth, char_to_add, node->ch, (node->right)->ch); return (add_string_at_node(node->right, string_to_add)); } /* walk down tree if appropriate */ if (node->down != NULL && node->ch == char_to_add) { log_msg(7, "depth=%d char=%c --- going DOWN", depth, char_to_add); depth++; res = add_string_at_node(node->down, string_to_add + 1); depth--; return (res); } if (char_to_add == '\0' && node->ch == '\0') { log_msg(6, "%s already in tree", original_string); return (1); } /* add here */ if (!(newnode = (struct s_node *) malloc(sizeof(struct s_node)))) { log_to_screen("failed to malloc"); depth--; return (1); } if (char_to_add < node->ch) // add to the left of node { log_msg(7, "depth=%d char=%c --- adding (left)", depth, char_to_add); memcpy((void *) newnode, (void *) node, sizeof(struct s_node)); node->right = newnode; } else if (char_to_add > node->ch) // add to the right of node { log_msg(7, "depth=%d char=%c --- adding (right)", depth, char_to_add); newnode->right = node->right; // newnode is to the RIGHT of node node->right = newnode; node = newnode; } // from now on, we're working on 'node' node->down = NULL; node->ch = char_to_add; node->expanded = node->selected = FALSE; if (char_to_add == '\0') { log_msg(6, "Added %s OK", original_string); return (0); } // add the rest log_msg(6, "Adding remaining chars ('%s')", string_to_add + 1); for (i = 1; i < noof_chars; i++) { if (! (node->down = (struct s_node *) malloc(sizeof(struct s_node)))) { log_to_screen("%s - failed to malloc", string_to_add); return (1); } node = node->down; char_to_add = string_to_add[i]; log_msg(6, "Adding '%c'", char_to_add); node->ch = char_to_add; node->right = node->down = NULL; node->expanded = node->selected = FALSE; if (!node->ch) { node->selected = sosodef; } } log_msg(6, "Finally - added %s OK", original_string); return (0); } /** * Load a filelist into a struct s_node. * When you are done with the filelist, call free_filelist(). * @param filelist_fname The file to load the filelist from. * @return A filelist tree structure. */ struct s_node *load_filelist(char *filelist_fname) { /*@ structures ************************************************* */ struct s_node *filelist; /*@ pointers *************************************************** */ FILE *pin; /*@ buffers **************************************************** */ char *command_to_open_fname = NULL; char fname[MAX_STR_LEN]; char tmp[MAX_STR_LEN]; char *tmp1 = NULL; int pos_in_fname; /*@ int ******************************************************** */ int percentage; /*@ long ******************************************************* */ long lines_in_filelist; long lino = 0; /*@ end vars *************************************************** */ assert_string_is_neither_NULL_nor_zerolength(filelist_fname); if (!does_file_exist(filelist_fname)) { fatal_error("filelist does not exist -- cannot load it"); } log_to_screen("Loading filelist"); mr_asprintf(&tmp1, "zcat %s | wc -l", filelist_fname); log_msg(6, "tmp1 = %s", tmp1); lines_in_filelist = atol(call_program_and_get_last_line_of_output(tmp1)); mr_free(tmp1); if (lines_in_filelist < 3) { log_to_screen("Warning - surprisingly short filelist."); } g_original_noof_lines_in_filelist = lines_in_filelist; if (!(filelist = (struct s_node *) malloc(sizeof(struct s_node)))) { return (NULL); } filelist->ch = '/'; filelist->right = NULL; filelist->down = malloc(sizeof(struct s_node)); filelist->expanded = filelist->selected = FALSE; (filelist->down)->ch = '\0'; (filelist->down)->right = (filelist->down)->down = FALSE; (filelist->down)->expanded = (filelist->down)->selected = FALSE; mr_asprintf(&command_to_open_fname, "gzip -dc %s", filelist_fname); if (!(pin = popen(command_to_open_fname, "r"))) { log_OS_error("Unable to openin filelist_fname"); mr_free(command_to_open_fname); return (NULL); } mr_free(command_to_open_fname); open_evalcall_form("Loading filelist from disk"); for (tmp1 = fgets(fname, MAX_STR_LEN, pin); !feof(pin); tmp1 = fgets(fname, MAX_STR_LEN, pin)) { if ((fname[strlen(fname) - 1] == 13 || fname[strlen(fname) - 1] == 10) && strlen(fname) > 0) { fname[strlen(fname) - 1] = '\0'; } // strip_spaces (fname); if (!strlen(fname)) { continue; } for (pos_in_fname = 0; fname[pos_in_fname] != '\0'; pos_in_fname++) { if (fname[pos_in_fname] != '/') { continue; } strcpy(tmp, fname); tmp[pos_in_fname] = '\0'; if (strlen(tmp)) { add_string_at_node(filelist, tmp); } } add_string_at_node(filelist, fname); if (!(++lino % 1111)) { percentage = (int) (lino * 100 / lines_in_filelist); update_evalcall_form(percentage); } } paranoid_pclose(pin); close_evalcall_form(); log_it("Finished loading filelist"); return (filelist); } /** * Log a list of files in @p node. * @param node The toplevel node to use. */ void show_filelist(struct s_node *node) { static int depth = 0; static char current_string[200]; if (depth == 0) { log_msg(0, "----------------show filelist--------------"); } current_string[depth] = node->ch; log_msg(3, "depth=%d", depth); if (node->down) { log_msg(3, "moving down"); depth++; show_filelist(node->down); depth--; } if (!node->ch) { log_msg(0, "%s\n", current_string); } if (node->right) { log_msg(3, "moving right"); show_filelist(node->right); } if (depth == 0) { log_msg(0, "----------------show filelist--------------"); } return; } /** * Reset the filelist to the state it was when it was loaded. This does not * touch the file on disk. * @param filelist The filelist tree structure. */ void reload_filelist(struct s_node *filelist) { assert(filelist != NULL); toggle_node_selection(filelist, FALSE); toggle_path_expandability(filelist, "/", FALSE); toggle_all_root_dirs_on(filelist); } /** * Save a filelist tree structure to disk. * @param filelist The filelist tree structure to save. * @param outfname Where to save it. */ void save_filelist(struct s_node *filelist, char *outfname) { /*@ int ********************************************************* */ static int percentage; static int depth = 0; /*@ buffers ***************************************************** */ static char str[MAX_STR_LEN]; /*@ structures ************************************************** */ struct s_node *node; /*@ pointers **************************************************** */ static FILE *fout = NULL; /*@ long ******************************************************** */ static long lines_in_filelist = 0; static long lino = 0; /*@ end vars *************************************************** */ assert(filelist != NULL); assert(outfname != NULL); // will be zerolength if save_filelist() is called by itself if (depth == 0) { log_to_screen("Saving filelist"); if (!(fout = fopen(outfname, "w"))) { fatal_error("Cannot openout/save filelist"); } lines_in_filelist = g_original_noof_lines_in_filelist; /* set by load_filelist() */ open_evalcall_form("Saving selection to disk"); } for (node = filelist; node != NULL; node = node->right) { str[depth] = node->ch; log_msg(5, "depth=%d ch=%c", depth, node->ch); if (!node->ch) { // if (node->selected) // { fprintf(fout, "%s\n", str); // } if (!(++lino % 1111)) { percentage = (int) (lino * 100 / lines_in_filelist); update_evalcall_form(percentage); } } if (node->down) { depth++; save_filelist(node->down, ""); depth--; } } if (depth == 0) { paranoid_fclose(fout); close_evalcall_form(); log_it("Finished saving filelist"); } } /** * Toggle all root dirs on. * @param filelist The filelist tree structure to operate on. * @bug I don't understand this function. Would someone care to explain it? */ void toggle_all_root_dirs_on(struct s_node *filelist) { /*@ structures ************************************************** */ struct s_node *node; /*@ int ********************************************************* */ static int depth = 0; static int root_dirs_expanded; /*@ buffers ***************************************************** */ static char filename[MAX_STR_LEN]; /*@ end vars *************************************************** */ assert(filelist != NULL); if (depth == 0) { log_it("Toggling all root dirs ON"); root_dirs_expanded = 0; } for (node = filelist; node != NULL; node = node->right) { filename[depth] = node->ch; if (node->ch == '\0' && strlen(filename) > 1 && (!strchr(filename + 1, '/'))) { node->selected = FALSE; node->expanded = TRUE; // log_it (filename); root_dirs_expanded++; } if (node->down) { depth++; toggle_all_root_dirs_on(node->down); depth--; } } if (depth == 0) { log_it("Finished toggling all root dirs ON"); } } /** * Toggle the expandability of a path. * @param filelist The filelist tree to operate on. * @param pathname The path to toggle expandability of. * @param on_or_off Whether to toggle it on or off. * @bug I don't understand this function. Would someone care to explain it? */ void toggle_path_expandability(struct s_node *filelist, char *pathname, bool on_or_off) { /*@ int ******************************************************** */ static int depth = 0; static int total_expanded; static int root_depth; int j; /*@ structures ************************************************* */ struct s_node *node; /*@ buffers **************************************************** */ static char current_filename[MAX_STR_LEN]; /* char tmp[MAX_STR_LEN+2]; */ /*@ end vars *************************************************** */ assert(filelist != NULL); assert_string_is_neither_NULL_nor_zerolength(pathname); if (depth == 0) { total_expanded = 0; // log_it ("Toggling path's expandability"); for (root_depth = (int) strlen(pathname); root_depth > 0 && pathname[root_depth - 1] != '/'; root_depth--); if (root_depth < 2) { root_depth = (int) strlen(pathname); } } for (node = filelist; node != NULL; node = node->right) { current_filename[depth] = node->ch; if (node->down) { depth++; toggle_path_expandability(node->down, pathname, on_or_off); depth--; } if (node->ch == '\0') { if (!strncmp(pathname, current_filename, strlen(pathname))) { for (j = root_depth; current_filename[j] != '/' && current_filename[j] != '\0'; j++); if (current_filename[j] != '\0') { for (j++; current_filename[j] != '/' && current_filename[j] != '\0'; j++); } if (current_filename[j] == '\0') { node->expanded = (!strcmp(pathname, current_filename) ? TRUE : on_or_off); } } } if (node->expanded) { if (total_expanded < ARBITRARY_MAXIMUM - 32 || !strrchr(current_filename + strlen(pathname), '/')) { total_expanded++; } else { node->expanded = FALSE; } } } if (depth == 0) { // log_it ("Finished toggling expandability"); } } /** * Toggle whether a path is selected. * @param filelist The filelist tree to operate on. * @param pathname The path to toggle selection of. * @param on_or_off Whether to toggle it on or off. * @bug I don't understand this function. Would someone care to explain it? */ void toggle_path_selection(struct s_node *filelist, char *pathname, bool on_or_off) { /*@ int ********************************************************* */ static int depth = 0; int j; /*@ structures ************************************************** */ struct s_node *node; /*@ buffers ***************************************************** */ static char current_filename[MAX_STR_LEN]; /*@ end vars *************************************************** */ assert(filelist != NULL); assert_string_is_neither_NULL_nor_zerolength(pathname); if (depth == 0) { log_it("Toggling path's selection"); } for (node = filelist; node != NULL; node = node->right) { current_filename[depth] = node->ch; if (node->down) { depth++; toggle_path_selection(node->down, pathname, on_or_off); depth--; } if (node->ch == '\0') { if (!strncmp(pathname, current_filename, strlen(pathname))) { for (j = 0; pathname[j] != '\0' && pathname[j] == current_filename[j]; j++); if (current_filename[j] == '/' || current_filename[j] == '\0') { node->selected = on_or_off; } } } } if (depth == 0) { log_it("Finished toggling selection"); } } /** * Toggle node selection of a filelist tree. * @param filelist The filelist tree to operate on. * @param on_or_off Whether to toggle selection on or off. * @bug I don't understand this function. Would someone care to explain it? */ void toggle_node_selection(struct s_node *filelist, bool on_or_off) { /*@ structure ************************************************** */ struct s_node *node; /*@ end vars *************************************************** */ assert(filelist != NULL); for (node = filelist; node != NULL; node = node->right) { if (node->ch == '/') { continue; } /* don't go deep */ if (node->ch == '\0') { node->selected = on_or_off; } if (node->down) { toggle_node_selection(node->down, on_or_off); } } } /** * The pathname to the skeleton filelist, used to give better progress reporting for mondo_makefilelist(). */ char *g_skeleton_filelist = NULL; /** * Get the next entry in the space-separated list in @p incoming. * So if @p incoming was '"one and two" three four', we would * return "one and two". * @param incoming The list to get the next entry from. * @return The first item in the list (respecting double quotes). * @note The returned string points to static data that will be overwritten with each call. */ char *next_entry(char *incoming) { char *sz_res; char *p; bool in_quotes = FALSE; mr_asprintf(&sz_res, "%s", incoming); p = sz_res; while ((*p != '|' || in_quotes) && *p != '\0') { if (*p == '\"') { in_quotes = !in_quotes; } p++; } *p = '\0'; return (sz_res); } /** * Number of entries in the skeleton filelist. */ long g_skeleton_entries = 0; /** * Recursively list all files in @p dir newer than @p time_of_last_full_backup to @p fout. * @param dir The directory to list to @p fout. * @param sth The directories to skip (exclude). * @param fout The file to write everything to. * @param time_of_last_full_backup Only backup files newer than this (0 to disable). * @return 0, always. * @bug Return value should be @c void. */ int open_and_list_dir(char *dir1, char *sth, FILE * fout, time_t time_of_last_full_backup) { const char delims[] = "|"; DIR *dip; struct dirent *dit; struct stat statbuf; char *new; char *tmp; char *dir = NULL; static int percentage = 0; char *skip_these; char *new_with_pipe; char *strtmp = NULL; char *token = NULL; char *find_excludes = NULL; static char *name_of_evalcall_form; int i; int lastpos = 0; static int depth = 0; char *p; static int counter = 0; static int uberctr = 0; static char *find_skeleton_marker; static long skeleton_lino = 0; static time_t last_time = 0; time_t this_time; /* dir is needed when we pass it to the shell */ dir = mr_stresc(dir1, "`$\\\"(){}'[]&*?|!#~", '\\'); p = strrchr(dir1, '/'); if (p) { if (!strcmp(p, "/.") || !strcmp(p, "/..")) { mr_free(dir); return (0); } } mr_asprintf(&find_excludes, " "); if (!depth) { malloc_string(name_of_evalcall_form); malloc_string(find_skeleton_marker); while((token = mr_strtok(sth, delims, &lastpos)) != NULL) { mr_strcat(find_excludes," -path %s -prune -o", token); mr_free(token); } /** * The maximum depth of directories to put in the skeleton filelist. * This is a balance between performance and a good progress indicator. */ #define MAX_SKEL_DEPTH 3 #if linux // 2.6 has /sys as a proc-type thing -- must be excluded mr_asprintf(&strtmp, "find '%s' -maxdepth %d -fstype mvfs -prune -o -fstype devpts -prune -o -fstype tmpfs -prune -o -fstype devtmpfs -prune -o -fstype proc -prune -o -fstype selinuxfs -prune -o -fstype securityfs -prune -o -fstype hugetlbfs -o -fstype cgroup -prune -prune -o -fstype mqueue -prune -o -fstype debugfs -prune -o -fstype sysfs -prune -o -fstype rpc_pipefs -prune -o -fstype none -prune -o %s -type d -print > %s 2> /dev/null", dir, MAX_SKEL_DEPTH, find_excludes, g_skeleton_filelist); #else // On BSD, for example, /sys is the kernel sources -- don't exclude mr_asprintf(&strtmp, "find '%s' -maxdepth %d -fstype mvfs -prune -o -path /proc -prune -o %s -type d -print > %s 2> /dev/null", dir, MAX_SKEL_DEPTH, find_excludes, g_skeleton_filelist); #endif paranoid_free(find_excludes); log_msg(5, "find command = %s", strtmp); paranoid_system(strtmp); paranoid_free(strtmp); mr_asprintf(&tmp, "wc -l %s | awk '{print $1;}'", g_skeleton_filelist); g_skeleton_entries = 1 + atol(call_program_and_get_last_line_of_output(tmp)); paranoid_free(tmp); sprintf(name_of_evalcall_form, "Making catalog of %s", dir1); open_evalcall_form(name_of_evalcall_form); find_skeleton_marker[0] = '\0'; skeleton_lino = 1; log_msg(5, "entries = %ld", g_skeleton_entries); percentage = 0; } else if (depth <= MAX_SKEL_DEPTH) // update evalcall form if appropriate { sprintf(find_skeleton_marker, "grep -Fv '%s' %s > %s.new 2> /dev/null", dir, g_skeleton_filelist, g_skeleton_filelist); // log_msg(0, "fsm = %s", find_skeleton_marker); if (!system(find_skeleton_marker)) { percentage = (int) (skeleton_lino * 100 / g_skeleton_entries); skeleton_lino++; // log_msg(5, "Found %s", dir); // log_msg(2, "Incrementing skeleton_lino; now %ld/%ld (%d%%)", skeleton_lino, g_skeleton_entries, percentage); sprintf(find_skeleton_marker, "mv -f %s.new %s", g_skeleton_filelist, g_skeleton_filelist); // log_msg(6, "fsm = %s", find_skeleton_marker); paranoid_system(find_skeleton_marker); time(&this_time); if (this_time != last_time) { last_time = this_time; #ifndef _XWIN if (!g_text_mode) { int cols, rows; newtGetScreenSize(&cols, &rows); mr_asprintf(&tmp, "Reading %-*s", cols, dir1); newtDrawRootText(0, g_noof_rows - 3, tmp); mr_free(tmp); } #endif update_evalcall_form(percentage); } } } depth++; // log_msg(0, "Cataloguing %s", dir); mr_asprintf(&skip_these, "|%s|", sth); mr_asprintf(&new_with_pipe, "|%s|", dir1); if ((dip = opendir(dir1)) == NULL) { mr_asprintf(&tmp,"opendir %s", dir1); log_OS_error(tmp); paranoid_free(tmp); } else if (strstr(skip_these, new_with_pipe)) { log_msg(10, "Found dir ***%s**** excluded", dir1); fprintf(fout, "%s\n", dir1); // if excluded dir then print dir ONLY } else { log_msg(10, "Found dir ***%s**** parsed", dir1); fprintf(fout, "%s\n", dir1); while ((dit = readdir(dip)) != NULL) { i++; if (strcmp(dir1, "/")) { mr_asprintf(&new,"%s/%s",dir1,dit->d_name); } else { mr_asprintf(&new,"%s%s",dir1,dit->d_name); } paranoid_free(new_with_pipe); mr_asprintf(&new_with_pipe, "|%s|", new); if (strstr(skip_these, new_with_pipe)) { fprintf(fout, "%s\n", new); log_msg(10, "Found child dir ***%s**** excluded", new); paranoid_free(new_with_pipe); } else { paranoid_free(new_with_pipe); if (!lstat(new, &statbuf)) { if (!S_ISLNK(statbuf.st_mode) && S_ISDIR(statbuf.st_mode)) { log_msg(10, "Found child dir ***%s**** parsed", new); open_and_list_dir(new, skip_these, fout, time_of_last_full_backup); } else { if (time_of_last_full_backup == 0 || time_of_last_full_backup < statbuf.st_ctime) { log_msg(10, "Found child file ***%s**** parsed", new); fprintf(fout, "%s\n", new); if ((counter++) > 128) { counter = 0; uberctr++; mr_asprintf(&tmp, " %c ", special_dot_char(uberctr)); #ifndef _XWIN if (!g_text_mode) { newtDrawRootText(77, g_noof_rows - 3, tmp); newtRefresh(); } #endif paranoid_free(tmp); } } } } } paranoid_free(new); } } paranoid_free(new_with_pipe); paranoid_free(skip_these); mr_free(dir); if (dip) { if (closedir(dip) == -1) { log_OS_error("closedir"); } } depth--; if (!depth) { close_evalcall_form(); paranoid_free(name_of_evalcall_form); paranoid_free(find_skeleton_marker); unlink(g_skeleton_filelist); log_msg(5, "g_skeleton_entries = %ld", g_skeleton_entries); } return (0); } /** * Create the filelist for the backup. It will be stored in [scratchdir]/archives/filelist.full. * @param logfile Unused. * @param tmpdir The tmpdir of the backup. * @param scratchdir The scratchdir of the backup. * @param include_paths The paths to back up, or NULL if you're using a user-defined filelist. * @param excp The paths to NOT back up. * @param differential The differential level (currently only 0 and 1 are supported). * @param userdef_filelist The user-defined filelist, or NULL if you're using @p include_paths. * @return 0, always. * @bug @p logfile is unused. * @bug Return value is meaningless. */ int mondo_makefilelist(char *logfile, char *tmpdir, char *scratchdir, char *include_paths, char *excp, int differential, char *userdef_filelist) { char *p, *q; char *sz_datefile; char *sz_filelist, *tmp; char *exclude_paths = NULL; FILE *fout; char *command = NULL; time_t time_of_last_full_backup = 0; struct stat statbuf; char *tmp1 = NULL; char *tmp2 = NULL; malloc_string(tmp); malloc_string(g_skeleton_filelist); mr_asprintf(&sz_datefile,MONDO_CACHE"/difflevel.%d" , 0); if (!include_paths && !userdef_filelist) { fatal_error ("Please supply either include_paths or userdef_filelist"); } // make hole for filelist mr_asprintf(&command, "mkdir -p %s/archives", scratchdir); paranoid_system(command); mr_free(command); mr_asprintf(&sz_filelist, "%s/tmpfs/filelist.full", tmpdir); make_hole_for_file(sz_filelist); if (differential == 0) { // restore last good datefile if it exists mr_asprintf(&command, "cp -f %s.aborted %s", sz_datefile, sz_datefile); run_program_and_log_output(command, 3); mr_free(command); // backup last known good datefile just in case :) if (does_file_exist(sz_datefile)) { mr_asprintf(&command, "mv -f %s %s.aborted", sz_datefile, sz_datefile); paranoid_system(command); mr_free(command); } make_hole_for_file(sz_datefile); write_one_liner_data_file(sz_datefile, call_program_and_get_last_line_of_output ("date +%s")); } else if (lstat(sz_datefile, &statbuf)) { log_msg(2, "Warning - unable to find date of previous backup. Full backup instead."); differential = 0; time_of_last_full_backup = 0; } else { time_of_last_full_backup = statbuf.st_mtime; log_msg(2, "Differential backup. Yay."); } paranoid_free(sz_datefile); // use user-specified filelist (if specified) if (userdef_filelist) { log_msg(1, "Using the user-specified filelist - %s - instead of calculating one", userdef_filelist); mr_asprintf(&command, "cp -f %s %s", userdef_filelist, sz_filelist); if (run_program_and_log_output(command, 3)) { mr_free(command); fatal_error("Failed to copy user-specified filelist"); } mr_free(command); } else { log_msg(2, "include_paths = '%s'", include_paths); log_msg(1, "Calculating filelist"); mr_asprintf(&tmp2, "%s", call_program_and_get_last_line_of_output("mount | grep -Ew 'ntfs|ntfs-3g|fat|vfat|dos' | awk '{print $3}'")); if (strlen(tmp2) < 1) { mr_asprintf(&tmp1," "); } else { log_msg(2, "Found windows FS: %s",tmp2); mr_asprintf(&tmp1, "find %s -name '/win386.swp' -o -name '/hiberfil.sys' -o -name '/pagefile.sys' 2> /dev/null\n",tmp2); paranoid_free(tmp2); mr_asprintf(&tmp2, "%s", call_program_and_get_last_line_of_output(tmp1)); log_msg(2, "Found windows files: %s",tmp2); } paranoid_free(tmp1); mr_asprintf(&exclude_paths, MONDO_CACHE"|%s|%s|%s|.|..|"MNT_CDROM"|"MNT_FLOPPY"|/media|/tmp|/proc|/sys|/run|/dev/shm|"MINDI_CACHE, tmp2, (tmpdir[0] == '/' && tmpdir[1] == '/') ? (tmpdir + 1) : tmpdir, (scratchdir[0] == '/' && scratchdir[1] == '/') ? (scratchdir + 1) : scratchdir); if (excp != NULL) { mr_strcat(exclude_paths,"|%s",excp); } paranoid_free(tmp2); log_msg(2, "Excluding paths = '%s'", exclude_paths); log_msg(2, "Generating skeleton filelist so that we can track our progress"); sprintf(g_skeleton_filelist, "%s/tmpfs/skeleton.txt", tmpdir); make_hole_for_file(g_skeleton_filelist); log_msg(4, "g_skeleton_entries = %ld", g_skeleton_entries); log_msg(2, "Opening out filelist to %s", sz_filelist); if (!(fout = fopen(sz_filelist, "w"))) { fatal_error("Cannot openout to sz_filelist"); } if (strlen(include_paths) == 0) { log_msg(1, "Including only '/' in %s", sz_filelist); open_and_list_dir("/", exclude_paths, fout, time_of_last_full_backup); } else { p = include_paths; while (*p) { q = next_entry(p); log_msg(1, "Including %s in filelist %s", q, sz_filelist); open_and_list_dir(q, exclude_paths, fout, time_of_last_full_backup); p += strlen(q); paranoid_free(q); while (*p == '|') { p++; } } } paranoid_fclose(fout); } log_msg(2, "Copying new filelist to scratchdir"); mr_asprintf(&command, "mkdir -p %s/archives", scratchdir); paranoid_system(command); mr_free(command); mr_asprintf(&command, "cp -f %s %s/archives/", sz_filelist, scratchdir); paranoid_system(command); mr_free(command); mr_asprintf(&command, "mv -f %s %s", sz_filelist, tmpdir); paranoid_system(command); mr_free(command); paranoid_free(sz_filelist); log_msg(2, "Freeing variables"); paranoid_free(exclude_paths); paranoid_free(tmp); paranoid_free(g_skeleton_filelist); log_msg(2, "Exiting"); return (0); } /** * Wrapper around mondo_makefilelist(). * @param bkpinfo The backup information structure. Fields used: * - @c bkpinfo->differential * - @c bkpinfo->exclude_paths * - @c bkpinfo->include_paths * - @c bkpinfo->make_filelist * - @c bkpinfo->scratchdir * - @c bkpinfo->tmpdir * @return 0 for success, nonzero for failure. * @see mondo_makefilelist */ int prepare_filelist() { /*@ int **************************************************** */ int res = 0; assert(bkpinfo != NULL); log_it("tmpdir=%s; scratchdir=%s", bkpinfo->tmpdir, bkpinfo->scratchdir); if (bkpinfo->make_filelist) { mvaddstr_and_log_it(g_currentY, 0, "Making catalog of files to be backed up"); } else { mvaddstr_and_log_it(g_currentY, 0, "Using supplied catalog of files to be backed up"); } if (bkpinfo->make_filelist) { res = mondo_makefilelist(MONDO_LOGFILE, bkpinfo->tmpdir, bkpinfo->scratchdir, bkpinfo->include_paths, bkpinfo->exclude_paths, bkpinfo->differential, NULL); } else { res = mondo_makefilelist(MONDO_LOGFILE, bkpinfo->tmpdir, bkpinfo->scratchdir, NULL, bkpinfo->exclude_paths, bkpinfo->differential, bkpinfo->include_paths); } if (res) { log_OS_error("Call to mondo_makefilelist failed"); mvaddstr_and_log_it(g_currentY++, 74, "Failed."); } else { mvaddstr_and_log_it(g_currentY++, 74, "Done."); } return (res); } /** * Locate the string @p string_to_find in the tree rooted at @p startnode. * @param startnode The node containing the root of the directory tree. * @param string_to_find The string to look for at @p startnode. * @return The node containing the last element of @p string_to_find, or NULL if * it was not found. */ struct s_node *find_string_at_node(struct s_node *startnode, char *string_to_find) { /*@ int ******************************************************** */ static int depth = 0; static char original_string[MAX_STR_LEN]; /*@ sturctures ************************************************* */ struct s_node *node; /*@ char ****************************************************** */ char char_to_find; /*@ bools ****************************************************** */ if (!depth) { strcpy(original_string, string_to_find); } assert(startnode != NULL); assert(string_to_find != NULL); log_msg(7, "starting --- str=%s", string_to_find); /* walk across tree if necessary */ node = startnode; char_to_find = string_to_find[0]; if (node->right != NULL && node->ch < char_to_find) { log_msg(7, "depth=%d --- going RIGHT ... %c-->%c", depth, char_to_find, node->ch, (node->right)->ch); return (find_string_at_node(node->right, string_to_find)); } /* walk down tree if appropriate */ if (node->down != NULL && node->ch == char_to_find) { log_msg(7, "depth=%d char=%c --- going DOWN", depth, char_to_find); depth++; node = find_string_at_node(node->down, string_to_find + 1); depth--; return (node); } if (char_to_find == '\0' && node->ch == '\0') { log_msg(7, "%s is in tree", original_string); return (node); } else { log_msg(7, "%s is NOT in tree", original_string); return (NULL); } } /** * Write all entries in @p needles_list_fname which are also in * @p filelist to @p matches_list_fname. * @param needles_list_fname A file containing strings to look for, 1 per line. * @param filelist The node for the root of the directory structure to search in. * @param matches_list_fname The filename where we should put the matches. * @return The number of matches found. */ long save_filelist_entries_in_common(char *needles_list_fname, struct s_node *filelist, char *matches_list_fname, bool use_star) { int retval = 0; struct s_node *found_node; FILE *fin; FILE *fout; char *fname; char *tmp; size_t len = 0; // Scrub's patch doesn't work without that // log_msg(1, "use_star = %s", (use_star)?"TRUE":"FALSE"); malloc_string(fname); malloc_string(tmp); log_msg(5, "starting"); log_msg(5, "needles_list_fname = %s", needles_list_fname); log_msg(5, "matches_list_fname = %s", matches_list_fname); if (!(fin = fopen(needles_list_fname, "r"))) { fatal_error("Cannot openin needles_list_fname"); } if (!(fout = fopen(matches_list_fname, "w"))) { fatal_error("Cannot openout matches_list_fname"); } while (!feof(fin)) { // fscanf(fin, "%s\n", fname); len = MAX_STR_LEN - 1; if (getline(&fname, &len, fin)) { // FIXME } if (!use_star) { if (fname[0] == '/') { strcpy(tmp, fname); } else { tmp[0] = '/'; strcpy(tmp + 1, fname); } strcpy(fname, tmp); } while (strlen(fname) > 0 && fname[strlen(fname) - 1] < 32) { fname[strlen(fname) - 1] = '\0'; } log_msg(5, "Looking for '%s'", fname); found_node = find_string_at_node(filelist, fname); if (found_node) { if (found_node->selected) { if (fname[0] == '/') { strcpy(tmp, fname + 1); strcpy(fname, tmp); } log_msg(5, "Found '%s'", fname); turn_wildcard_chars_into_literal_chars(tmp, fname); fprintf(fout, "%s\n", tmp); retval++; } } } paranoid_fclose(fout); paranoid_fclose(fin); paranoid_free(fname); paranoid_free(tmp); return (retval); } /** * Add all files listed in @p list_of_files_fname to the directory structure rooted at * @p filelist. * @param filelist The top node of the directory structure to add the files to. * @param list_of_files_fname The file containing the files to add, 1 per line. * @param flag_em If TRUE, then flag the added files for restoration. * @return 0 for success, nonzero for failure. */ int add_list_of_files_to_filelist(struct s_node *filelist, char *list_of_files_fname, bool flag_em) { FILE *fin; char *tmp; char *p; struct s_node *nod; malloc_string(tmp); log_msg(3, "Adding %s to filelist", list_of_files_fname); if (!(fin = fopen(list_of_files_fname, "r"))) { log_it("%s",list_of_files_fname); return (1); } for (p = fgets(tmp, MAX_STR_LEN, fin); !feof(fin) && (p != NULL); p = fgets(tmp, MAX_STR_LEN, fin)) { if (!tmp[0]) { continue; } if ((tmp[strlen(tmp) - 1] == 13 || tmp[strlen(tmp) - 1] == 10) && strlen(tmp) > 0) { tmp[strlen(tmp) - 1] = '\0'; } log_msg(2, "tmp = '%s'", tmp); if (!tmp[0]) { continue; } if ((nod = find_string_at_node(filelist, tmp))) { log_msg(5, "Found '%s' in filelist already. Cool.", tmp); } else { add_string_at_node(filelist, tmp); nod = find_string_at_node(filelist, tmp); } if (nod && flag_em) { toggle_path_selection(filelist, tmp, TRUE); log_msg(5, "Flagged '%s'", tmp); } } paranoid_fclose(fin); paranoid_free(tmp); return (0); } /* @} - end of filelistGroup */