source: MondoRescue/branches/2.2.10/mondo/src/common/libmondo-filelist.c @ 2327

Last change on this file since 2327 was 2327, checked in by Bruno Cornec, 12 years ago

r3338@localhost: bruno | 2009-08-11 23:03:30 +0200
bkpinfo->zip_suffix, bkpinfo->image_devs and bkpinfo->restore_path are now allocated dynmically

  • Property svn:keywords set to Id
File size: 49.4 KB
Line 
1/*
2   $Id: libmondo-filelist.c 2327 2009-08-18 13:20:37Z bruno $
3*/
4
5/**
6 * @file
7 * Functions which create, chop, and edit the filelist.
8 */
9
10#include "my-stuff.h"
11#include "mondostructures.h"
12#include "lib-common-externs.h"
13#include "libmondo-filelist.h"
14#include "libmondo-string-EXT.h"
15#include "libmondo-files-EXT.h"
16#include "libmondo-fork-EXT.h"
17#include "libmondo-gui-EXT.h"
18#include "libmondo-tools-EXT.h"
19#include "mr_mem.h"
20#include "mr_str.h"
21
22#include <time.h>
23#include <stdio.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <dirent.h>
27#include <errno.h>
28#include <stdio.h>
29
30
31extern ssize_t getline(char **lineptr, size_t * n, FILE * stream);
32extern char *MONDO_LOGFILE;
33
34/* Reference to global bkpinfo */
35extern struct s_bkpinfo *bkpinfo;
36
37
38int mondo_makefilelist(char *logfile, char *include_paths, char *excp, int differential, char *userdef_filelist);
39
40
41/*@unused@*/
42//static char cvsid[] = "$Id: libmondo-filelist.c 2327 2009-08-18 13:20:37Z bruno $";
43
44/**
45 * Number of lines in the filelist last loaded.
46 * @warning This implies that two filesets cannot be loaded at once.
47 * @ingroup globalGroup
48 */
49long g_original_noof_lines_in_filelist = 0;
50
51/**
52 * Number of filesets in the current backup.
53 * @ingroup globalGroup
54 */
55long g_noof_sets = 0;
56
57extern bool g_text_mode;
58extern newtComponent g_progressForm;
59extern int g_currentY;
60extern int g_noof_rows;
61
62extern char *g_getfacl;
63extern char *g_getfattr;
64
65
66
67/**
68 * @addtogroup filelistGroup
69 * @{
70 */
71/**
72 * Call chop_filelist() to chop the filelist into sets.
73 * @param bkpinfo The backup information structure. Fields used:
74 * - @c bkpinfo->image_devs
75 * - @c bkpinfo->optimal_set_size
76 * - @c bkpinfo->scratchdir
77 * - @c bkpinfo->tmpdir
78 * @see chop_filelist
79 */
80int call_filelist_chopper()
81{
82    /*@ buffers *********************** */
83    char *dev = NULL;
84    char *filelist = NULL;
85    char *tempfile = NULL;
86    long noof_sets;
87
88    /*@ pointers ********************** */
89    char *ptr = NULL;
90    FILE *fout;
91
92    /*@ int *************************** */
93    int i, retval = 0;
94
95    mvaddstr_and_log_it(g_currentY, 0, "Dividing filelist into sets");
96
97    log_to_screen("Dividing filelist into sets. Please wait.");
98    i = 0;
99    mr_asprintf(filelist, "%s/archives/filelist.full", bkpinfo->scratchdir);
100    if (!does_file_exist(filelist)) {
101        log_it("filelist %s not found", filelist);
102        mr_free(filelist);
103        fatal_error("call_filelist_chopper() -- filelist not found!");
104    }
105
106    noof_sets = chop_filelist(filelist, bkpinfo->optimal_set_size);
107    mr_free(filelist);
108    estimate_noof_media_required(noof_sets);    // for cosmetic purposes
109
110    mr_asprintf(tempfile, "%s/biggielist.txt", bkpinfo->tmpdir);
111    if (!(fout = fopen(tempfile, "a"))) {
112        log_OS_error("Cannot append to biggielist");
113        retval++;
114        mr_free(tempfile);
115        return (retval);
116    }
117    mr_free(tempfile);
118
119    if (bkpinfo->image_devs) {
120        log_it("image_devs : %s", bkpinfo->image_devs);
121
122        ptr = bkpinfo->image_devs;
123
124        while (ptr && *ptr) {
125            mr_asprintf(dev, "%s", ptr);
126            log_it("Examining imagedev %s", dev);
127            for (i = 0; i < (int) strlen(dev) && dev[i] != ' '; i++);
128            dev[i] = '\0';
129            if (!strlen(dev)) {
130                mr_free(dev);
131                continue;
132            }
133            fprintf(fout, "%s\n", dev);
134            log_it("Adding '%s' to biggielist", dev);
135            if ((ptr = strchr(ptr, ' '))) {
136                ptr++;
137            }
138            mr_free(dev);
139        }
140    }
141    paranoid_fclose(fout);
142    mvaddstr_and_log_it(g_currentY++, 74, "Done.");
143
144    return (retval);
145}
146
147
148int sort_file(char *orig_fname)
149{
150    char *tmp_fname = NULL;
151    char *command = NULL;
152    int retval = 0;
153
154    log_msg(5, "Sorting file %s", orig_fname);
155
156    if (!does_file_exist(orig_fname)) {
157        log_msg(2, "file %s empty", orig_fname);
158        return (0);
159    }                           // no sense in trying to sort an empty file
160
161    mr_asprintf(tmp_fname, "%s/sortfile", bkpinfo->tmpdir);
162    mr_asprintf(command, "sort %s > %s 2>> %s", orig_fname, tmp_fname, MONDO_LOGFILE);
163    retval = system(command);
164    mr_free(command);
165
166    if (retval) {
167        log_msg(2, "Failed to sort %s - oh dear", orig_fname);
168    } else {
169        log_msg(5, "Sorted %s --> %s OK. Copying it back to %s now", orig_fname, tmp_fname, orig_fname);
170        mr_asprintf(command, "mv -f %s %s", tmp_fname, orig_fname);
171        retval += run_program_and_log_output(command, 5);
172        mr_free(command);
173
174        if (retval) {
175            log_msg(2, "Failed to copy %s back to %s - oh dear", tmp_fname, orig_fname);
176        } else {
177            log_msg(5, "%s was sorted OK.", orig_fname);
178        }
179    }
180    mr_free(tmp_fname);
181    log_msg(5, "Finished sorting file %s", orig_fname);
182    return (retval);
183}
184
185
186
187/**
188 * Chop the filelist into sets.
189 * Each fileset is a list of files whose total (uncompressed) size is usually
190 * about X KB. Files bigger than 8X KB are placed in a "biggielist"; they will
191 * be sliced and compressed separately from the regular files.
192 *
193 * @param filelist The big filelist (filelist.full) to chop up.
194 * @param maxsetsizeK Optimal size of a fileset (X above).
195 * @return number of errors encountered (0 for success).
196 */
197int chop_filelist(char *filelist, long maxsetsizeK)
198{
199/*@ long ****************************************/
200    long lino = 0;
201    long max_sane_size_for_a_file;
202    long curr_set_size;
203    long noof_lines;
204    long siz;
205
206    /*@ int **************************************** */
207    int i;
208    long curr_set_no;
209
210    /*@ buffers ************************************* */
211    char *outfname = NULL;
212    char *biggie_fname = NULL;
213    char *incoming;
214    char *tmp = NULL;
215
216    /*@ pointers *********************************** */
217    FILE *fin;
218    FILE *fout;
219    FILE *fbig;
220
221    /*@ structures ********************************* */
222    struct stat buf;
223    int err = 0;
224
225    assert_string_is_neither_NULL_nor_zerolength(filelist);
226    assert(maxsetsizeK > 0);
227
228    max_sane_size_for_a_file = 64L * 1024L;
229// max_sane_size_for_a_file = maxsetsizeK*2;
230//  if (max_sane_size_for_a_file > 32*1024)
231//    { max_sane_size_for_a_file = 32*1024; }
232
233    log_it("filelist=%s;", filelist);
234    open_evalcall_form("Dividing filelist into sets");
235    noof_lines = count_lines_in_file(filelist);
236    if (!(fin = fopen(filelist, "r"))) {
237        log_OS_error("Cannot openin filelist");
238        return (0);
239    }
240    curr_set_no = 0;
241    curr_set_size = 0;
242    mr_asprintf(outfname, "%s/filelist.%ld", bkpinfo->tmpdir, curr_set_no);
243    mr_asprintf(biggie_fname, "%s/biggielist.txt", bkpinfo->tmpdir);
244    log_it("outfname=%s; biggie_fname=%s", outfname, biggie_fname);
245    if (!(fbig = fopen(biggie_fname, "w"))) {
246        log_OS_error("Cannot openout biggie_fname");
247        err++;
248        mr_free(outfname);
249        mr_free(biggie_fname);
250        return (curr_set_no + 1);
251    }
252    if (!(fout = fopen(outfname, "w"))) {
253        log_OS_error("Cannot openout outfname");
254        err++;
255        mr_free(outfname);
256        mr_free(biggie_fname);
257        return (curr_set_no + 1);
258    }
259    incoming = malloc(MAX_STR_LEN * 2);
260
261    (void) fgets(incoming, MAX_STR_LEN * 2 - 1, fin);
262    while (!feof(fin)) {
263        lino++;
264        i = strlen(incoming) - 1;
265        if (i < 0) {
266            i = 0;
267        }
268        if (i > MAX_STR_LEN - 1) {
269            incoming[MAX_STR_LEN - 30] = '\0';
270            log_msg(1, "Warning - truncating file %s's name", incoming);
271            err++;
272        }
273        if (incoming[i] < 32) {
274            incoming[i] = '\0';
275        }
276        if (!strncmp(incoming, "/dev/", 5)) {
277            siz = 1;
278        } else if (lstat(incoming, &buf) != 0) {
279            siz = 0;
280        } else {
281            siz = (long) (buf.st_size >> 10);
282        }
283        if (siz > max_sane_size_for_a_file) {
284            fprintf(fbig, "%s\n", incoming);
285        } else {
286            curr_set_size += siz;
287            fprintf(fout, "%s\n", incoming);
288            if (curr_set_size > maxsetsizeK) {
289                paranoid_fclose(fout);
290                sort_file(outfname);
291                mr_free(outfname);
292                curr_set_no++;
293                curr_set_size = 0;
294
295                mr_asprintf(outfname, "%s/filelist.%ld", bkpinfo->tmpdir, curr_set_no);
296                if (!(fout = fopen(outfname, "w"))) {
297                    log_OS_error("Unable to openout outfname");
298                    err++;
299                    mr_free(outfname);
300                    mr_free(biggie_fname);
301                    paranoid_free(incoming);
302                    return (curr_set_no + 1);
303                }
304                update_evalcall_form((int) (lino * 100 / noof_lines));
305            }
306        }
307        (void) fgets(incoming, MAX_STR_LEN * 2 - 1, fin);
308    }
309    paranoid_free(incoming);
310    paranoid_fclose(fin);
311    paranoid_fclose(fout);
312    paranoid_fclose(fbig);
313
314    if (length_of_file(outfname) <= 2) {
315        unlink(outfname);
316        g_noof_sets--;
317    }
318    g_noof_sets = curr_set_no;
319    sort_file(outfname);
320    mr_free(outfname);
321
322    sort_file(biggie_fname);
323    mr_free(biggie_fname);
324
325    mr_asprintf(outfname, "%s/LAST-FILELIST-NUMBER", bkpinfo->tmpdir);
326    mr_asprintf(tmp, "%ld", curr_set_no);
327    if (write_one_liner_data_file(outfname, tmp)) {
328        log_OS_error
329            ("Unable to echo write one-liner to LAST-FILELIST-NUMBER");
330        err = 1;
331    }
332    mr_free(tmp);
333    mr_free(outfname);
334
335    if (curr_set_no == 0) {
336        log_msg(1, "Only one fileset. Fine.");
337    } else {
338        log_msg(1, "Filelist divided into %ld sets", curr_set_no + 1);
339    }
340    close_evalcall_form();
341    /* This is to work around an obscure bug in Newt; open a form, close it,
342       carry on... I don't know why it works but it works. If you don't do this
343       then update_progress_form() won't show the "time taken / time remaining"
344       line. The bug only crops up AFTER the call to chop_filelist(). Weird. */
345#ifndef _XWIN
346    if (!g_text_mode) {
347        open_progress_form("", "", "", "", 100);
348        newtPopHelpLine();
349        newtFormDestroy(g_progressForm);
350        newtPopWindow();
351    }
352#endif
353    return (err ? 0 : curr_set_no + 1);
354}
355
356
357
358
359
360/**
361 * Free all the memory used by a filelist structure.
362 * Since this may take a long time for large filelists, a progress bar will be displayed.
363 * @param filelist The filelist to free.
364 */
365void free_filelist(struct s_node *filelist)
366{
367    /*@ int's ******************************************************* */
368    static int depth = 0;
369    int percentage;
370
371    /*@ long's ****************************************************** */
372    static long i = 0;
373
374    /*@ end vars **************************************************** */
375
376    assert(filelist != NULL);
377    if (depth == 0) {
378        open_evalcall_form("Freeing memory");
379        log_to_screen("Freeing memory formerly occupied by filelist");
380    }
381    depth++;
382
383    if (filelist->ch == '\0') {
384        if (!(i++ % 1111)) {
385            percentage =
386                (int) (i * 100 / g_original_noof_lines_in_filelist);
387            update_evalcall_form(percentage);
388
389        }
390    }
391
392    if (filelist->right) {
393        free_filelist(filelist->right);
394        filelist->right = NULL;
395    }
396    if (filelist->down) {
397/*      if (!(i++ %39999)) { update_evalcall_form(0); } */
398        free_filelist(filelist->down);
399        filelist->down = NULL;
400    }
401    filelist->ch = '\0';
402    paranoid_free(filelist);
403    depth--;
404    if (depth == 0) {
405        close_evalcall_form();
406        log_it("Finished freeing memory");
407    }
408}
409
410
411int call_exe_and_pipe_output_to_fd(char *syscall, FILE * pout)
412{
413    FILE *pattr;
414    char *tmp;
415    pattr = popen(syscall, "r");
416    if (!pattr) {
417        log_msg(1, "Failed to open fattr() %s", syscall);
418        return (1);
419    }
420    if (feof(pattr)) {
421        log_msg(1, "Failed to call fattr() %s", syscall);
422        paranoid_pclose(pattr);
423        return (2);
424    }
425    malloc_string(tmp);
426    for ((void)fgets(tmp, MAX_STR_LEN, pattr); !feof(pattr);
427         (void)fgets(tmp, MAX_STR_LEN, pattr)) {
428        fputs(tmp, pout);
429    }
430    paranoid_pclose(pattr);
431    paranoid_free(tmp);
432    return (0);
433}
434
435
436
437int gen_aux_list(char *filelist, char *syscall_sprintf,
438                 char *auxlist_fname)
439{
440    FILE *fin;
441    FILE *pout;
442    char *pout_command = NULL;
443    char *syscall;
444    char *file_to_analyze;
445    char *strtmp = NULL;
446    char *tmp = NULL;
447    int i;
448
449    if (!(fin = fopen(filelist, "r"))) {
450        log_msg(1, "Cannot openin filelist %s", filelist);
451        return (1);
452    }
453    mr_asprintf(pout_command, "gzip -c1 > %s", auxlist_fname);
454    if (!(pout = popen(pout_command, "w"))) {
455        log_msg(1, "Cannot openout auxlist_fname %s", auxlist_fname);
456        fclose(fin);
457        mr_free(pout_command);
458        return (4);
459    }
460    mr_free(pout_command);
461
462    malloc_string(file_to_analyze);
463    for ((void)fgets(file_to_analyze, MAX_STR_LEN, fin); !feof(fin);
464         (void)fgets(file_to_analyze, MAX_STR_LEN, fin)) {
465        i = strlen(file_to_analyze);
466        if (i > 0 && file_to_analyze[i - 1] < 32) {
467            file_to_analyze[i - 1] = '\0';
468        }
469        log_msg(8, "Analyzing %s", file_to_analyze);
470        tmp = mr_stresc(file_to_analyze, "`$\\\"(){}[]'*?&|!#~", '\\');
471        mr_asprintf(strtmp, syscall_sprintf, tmp);
472        paranoid_free(tmp);
473        mr_asprintf(syscall, "%s 2>> /dev/null", strtmp);   // " MONDO_LOGFILE);
474        mr_free(strtmp);
475        call_exe_and_pipe_output_to_fd(syscall, pout);
476        mr_free(syscall);
477    }
478    paranoid_fclose(fin);
479    paranoid_pclose(pout);
480    paranoid_free(file_to_analyze);
481    return (0);
482}
483
484
485int get_acl_list(char *filelist, char *facl_fname)
486{
487    char *command = NULL;
488    int retval = 0;
489
490    if (g_getfacl != NULL) {
491        mr_asprintf(command, "touch %s", facl_fname);
492        run_program_and_log_output(command, 8);
493        mr_free(command);
494
495        mr_asprintf(command, "getfacl --all-effective -P %s 2>> %s | gzip -c1 > %s 2>> %s", filelist, MONDO_LOGFILE, facl_fname, MONDO_LOGFILE);
496        log_it("%s",command);
497        retval = system(command);
498        mr_free(command);
499    }
500    return (retval);
501}
502
503
504int get_fattr_list(char *filelist, char *fattr_fname)
505{
506    char *command;
507    int retval = 0;
508
509    if (g_getfattr != NULL) {
510        mr_asprintf(command, "touch %s", fattr_fname);
511        run_program_and_log_output(command, 8);
512        mr_free(command);
513        retval =
514            gen_aux_list(filelist, "getfattr --en=hex -P -d \"%s\"",
515                         fattr_fname);
516    }
517    return (retval);
518}
519
520
521int set_EXAT_list(char *orig_msklist, char *original_exat_fname,
522                  char *executable)
523{
524    const int my_depth = 8;
525    char *command = NULL; 
526    char *syscall_pin = NULL;
527    char *syscall_pout = NULL;
528    char *incoming;
529    char *current_subset_file, *current_master_file;
530    char *masklist = NULL;
531    int retval = 0;
532    int i;
533    char *p, *q;
534    FILE *pin, *pout, *faclin;
535
536    log_msg(1, "set_EXAT_list(%s, %s, %s)", orig_msklist, original_exat_fname, executable);
537    if (!orig_msklist || !orig_msklist[0]
538        || !does_file_exist(orig_msklist)) {
539        log_msg(1, "No masklist provided. I shall therefore set ALL attributes.");
540        mr_asprintf(command, "gzip -dc %s | %s --restore - 2>> %s", original_exat_fname, executable, MONDO_LOGFILE);
541        log_msg(1, "command = %s", command);
542        retval = system(command);
543        mr_free(command);
544        log_msg(1, "Returning w/ retval=%d", retval);
545        return (retval);
546    }
547    if (length_of_file(original_exat_fname) <= 0) {
548        log_msg(1, "original_exat_fname %s is empty or missing, so no need to set EXAT list", original_exat_fname);
549        return (0);
550    }
551    malloc_string(incoming);
552    malloc_string(current_subset_file);
553    malloc_string(current_master_file);
554    mr_asprintf(masklist, "%s/masklist", bkpinfo->tmpdir);
555    mr_asprintf(command, "cp -f %s %s", orig_msklist, masklist);
556    run_program_and_log_output(command, 1);
557    mr_free(command);
558
559    sort_file(masklist);
560    current_subset_file[0] = current_master_file[0] = '\0';
561
562    mr_asprintf(syscall_pout, "%s --restore - 2>> %s", executable, MONDO_LOGFILE);
563    log_msg(1, "syscall_pout = %s", syscall_pout);
564    pout = popen(syscall_pout, "w");
565    mr_free(syscall_pout);
566
567    if (!pout) {
568        log_it("Unable to openout to syscall_pout");
569        mr_free(masklist);
570        return (1);
571    }
572
573    mr_asprintf(syscall_pin, "gzip -dc %s", original_exat_fname);
574    log_msg(1, "syscall_pin = %s", syscall_pin);
575    pin = popen(syscall_pin, "r");
576    mr_free(syscall_pin);
577
578    if (!pin) {
579        pclose(pout);
580        log_it("Unable to openin from syscall");
581        return (1);
582    }
583    faclin = fopen(masklist, "r");
584    if (!faclin) {
585        pclose(pin);
586        pclose(pout);
587        log_it("Unable to openin masklist");
588        mr_free(masklist);
589        return (1);
590    }
591//  printf("Hi there. Starting the loop\n");
592
593    (void)fgets(current_subset_file, MAX_STR_LEN, faclin);
594    (void)fgets(incoming, MAX_STR_LEN, pin);
595    while (!feof(pin) && !feof(faclin)) {
596//      printf("incoming = %s", incoming);
597
598        strcpy(current_master_file, incoming + 8);
599
600        p = current_subset_file;
601        if (*p == '/') {
602            p++;
603        }
604        i = strlen(p);
605        if (i > 0 && p[i - 1] < 32) {
606            p[i - 1] = '\0';
607        }
608
609
610        q = current_master_file;
611        if (*q == '/') {
612            q++;
613        }
614        i = strlen(q);
615        if (i > 0 && q[i - 1] < 32) {
616            q[i - 1] = '\0';
617        }
618
619        i = strcmp(p, q);
620        log_msg(my_depth, "'%s' v '%s' --> %d\n", p, q, i);
621
622//      printf("%s v %s --> %d\n", p, q, i);
623
624        if (i < 0) {            // read another subset file in.
625            log_msg(my_depth, "Reading next subset line in\n\n");
626            (void)fgets(current_subset_file, MAX_STR_LEN, faclin);
627            continue;
628        }
629
630        if (!i) {
631            fputs(incoming, pout);
632        }
633        (void)fgets(incoming, MAX_STR_LEN, pin);
634        if (!i) {
635            log_msg(my_depth, "Copying master %s", q);
636        }
637//      if (!i) { printf("Match --- %s\n", q); }
638
639        while (!feof(pin) && strncmp(incoming, "# file: ", 8)) {
640            if (!i) {
641
642//    printf("%s", incoming);
643
644                fputs(incoming, pout);
645            }
646            (void)fgets(incoming, MAX_STR_LEN, pin);
647        }
648        if (!i) {
649            (void)fgets(current_subset_file, MAX_STR_LEN, faclin);
650        }
651    }
652    while (!feof(pin)) {
653        (void)fgets(incoming, MAX_STR_LEN, pin);
654    }
655    fclose(faclin);
656    pclose(pin);
657    pclose(pout);
658
659//  printf("OK, loop is done\n");
660
661    unlink(masklist);
662    mr_free(masklist);
663
664    paranoid_free(current_subset_file);
665    paranoid_free(current_master_file);
666    paranoid_free(incoming);
667    return (retval);
668}
669
670
671int set_fattr_list(char *masklist, char *fattr_fname)
672{
673    if (find_home_of_exe("setfattr")) {
674        return (set_EXAT_list(masklist, fattr_fname, "setfattr"));
675    } else {
676        log_msg(1, "ERROR: set_EXAT_list: setfattr doesn't exist");
677        return(0);
678    }
679}
680
681
682
683int set_acl_list(char *masklist, char *acl_fname)
684{
685    if (find_home_of_exe("setfacl")) {
686        return (set_EXAT_list(masklist, acl_fname, "setfacl"));
687    } else {
688        log_msg(1, "ERROR: set_EXAT_list: setfacl doesn't exist");
689        return(0);
690    }
691}
692
693
694/**
695 * Get the number of the last fileset in the backup.
696 * @return The last filelist number.
697 * @note This function should only be called at restore-time.
698 */
699int get_last_filelist_number()
700{
701    /*@ buffers ***************************************************** */
702    char *val_sz = NULL;
703    char *cfg_fname = NULL;
704
705    /*@ long ******************************************************** */
706    int val_i;
707
708    /*@ end vars **************************************************** */
709
710    assert(bkpinfo != NULL);
711
712    mr_asprintf(cfg_fname, "%s/mondo-restore.cfg", bkpinfo->tmpdir);
713    val_sz = read_cfg_var(cfg_fname, "last-filelist-number");
714    mr_free(cfg_fname);
715
716    if (val_sz == NULL) {
717        mr_asprintf(val_sz, "");
718    }
719    val_i = atoi(val_sz);
720    mr_free(val_sz);
721
722    if (val_i <= 0) {
723        val_i = 500;
724    }
725    return (val_i);
726}
727
728
729/**
730 * Add a string at @p startnode.
731 * @param startnode The node to start at when searching for where to add the string.
732 * @param string_to_add The string to add.
733 * @return 0 for success, 1 for failure.
734 * @bug I don't understand this function. Would someone care to explain it?
735 */
736int add_string_at_node(struct s_node *startnode, char *string_to_add)
737{
738
739
740    /*@ int ******************************************************** */
741    int noof_chars;
742    int i;
743    int res;
744
745    /*@ sturctures ************************************************* */
746    struct s_node *node, *newnode;
747
748    /*@ char  ****************************************************** */
749    char char_to_add;
750
751    /*@ bools ****************************************************** */
752
753    const bool sosodef = FALSE;
754
755    static int depth = 0;
756    static char original_string[MAX_STR_LEN];
757
758    assert(startnode != NULL);
759    assert(string_to_add != NULL);
760
761    if (!depth) {
762        strcpy(original_string, string_to_add);
763    }
764
765    noof_chars = strlen(string_to_add) + 1; /* we include the '\0' */
766
767/* walk across tree if necessary */
768    node = startnode;
769    char_to_add = string_to_add[0];
770    if (node->right != NULL && node->ch < char_to_add) {
771        log_msg(7, "depth=%d --- going RIGHT ... %c-->%c", depth,
772                char_to_add, node->ch, (node->right)->ch);
773        return (add_string_at_node(node->right, string_to_add));
774    }
775
776/* walk down tree if appropriate */
777    if (node->down != NULL && node->ch == char_to_add) {
778        log_msg(7, "depth=%d char=%c --- going DOWN", depth, char_to_add);
779        depth++;
780        res = add_string_at_node(node->down, string_to_add + 1);
781        depth--;
782        return (res);
783    }
784
785    if (char_to_add == '\0' && node->ch == '\0') {
786        log_msg(6, "%s already in tree", original_string);
787        return (1);
788    }
789
790/* add here */
791    if (!(newnode = (struct s_node *) malloc(sizeof(struct s_node)))) {
792        log_to_screen("failed to malloc");
793        depth--;
794        return (1);
795    }
796    if (char_to_add < node->ch) // add to the left of node
797    {
798        log_msg(7, "depth=%d char=%c --- adding (left)", depth,
799                char_to_add);
800        memcpy((void *) newnode, (void *) node, sizeof(struct s_node));
801        node->right = newnode;
802    } else if (char_to_add > node->ch)  // add to the right of node
803    {
804        log_msg(7, "depth=%d char=%c --- adding (right)", depth,
805                char_to_add);
806        newnode->right = node->right;   // newnode is to the RIGHT of node
807        node->right = newnode;
808        node = newnode;
809    }
810    // from now on, we're working on 'node'
811    node->down = NULL;
812    node->ch = char_to_add;
813    node->expanded = node->selected = FALSE;
814    if (char_to_add == '\0') {
815        log_msg(6, "Added %s OK", original_string);
816        return (0);
817    }
818// add the rest
819    log_msg(6, "Adding remaining chars ('%s')", string_to_add + 1);
820    for (i = 1; i < noof_chars; i++) {
821        if (!
822            (node->down =
823             (struct s_node *) malloc(sizeof(struct s_node)))) {
824            log_to_screen("%s - failed to malloc", string_to_add);
825            return (1);
826        }
827        node = node->down;
828        char_to_add = string_to_add[i];
829        log_msg(6, "Adding '%c'", char_to_add);
830        node->ch = char_to_add;
831        node->right = node->down = NULL;
832        node->expanded = node->selected = FALSE;
833        if (!node->ch) {
834            node->selected = sosodef;
835        }
836    }
837    log_msg(6, "Finally - added %s OK", original_string);
838    return (0);
839}
840
841
842
843
844/**
845 * Load a filelist into a <tt>struct s_node</tt>.
846 * When you are done with the filelist, call free_filelist().
847 * @param filelist_fname The file to load the filelist from.
848 * @return A filelist tree structure.
849 */
850struct s_node *load_filelist(char *filelist_fname)
851{
852
853    /*@ structures ************************************************* */
854    struct s_node *filelist;
855
856    /*@ pointers *************************************************** */
857    FILE *pin;
858
859    /*@ buffers **************************************************** */
860    char *command_to_open_fname = NULL;
861    char fname[MAX_STR_LEN];
862    char tmp[MAX_STR_LEN];
863    char *tmp1 = NULL;
864    int pos_in_fname;
865    /*@ int ******************************************************** */
866    int percentage;
867
868    /*@ long ******************************************************* */
869    long lines_in_filelist;
870    long lino = 0;
871    /*@ end vars *************************************************** */
872
873    assert_string_is_neither_NULL_nor_zerolength(filelist_fname);
874
875    if (!does_file_exist(filelist_fname)) {
876        fatal_error("filelist does not exist -- cannot load it");
877    }
878    log_to_screen("Loading filelist");
879    mr_asprintf(tmp1, "zcat %s | wc -l", filelist_fname);
880    log_msg(6, "tmp1 = %s", tmp1);
881    lines_in_filelist = atol(call_program_and_get_last_line_of_output(tmp1));
882    mr_free(tmp1);
883
884    if (lines_in_filelist < 3) {
885        log_to_screen("Warning - surprisingly short filelist.");
886    }
887    g_original_noof_lines_in_filelist = lines_in_filelist;
888    if (!(filelist = (struct s_node *) malloc(sizeof(struct s_node)))) {
889        return (NULL);
890    }
891    filelist->ch = '/';
892    filelist->right = NULL;
893    filelist->down = malloc(sizeof(struct s_node));
894    filelist->expanded = filelist->selected = FALSE;
895    (filelist->down)->ch = '\0';
896    (filelist->down)->right = (filelist->down)->down = FALSE;
897    (filelist->down)->expanded = (filelist->down)->selected = FALSE;
898
899    mr_asprintf(command_to_open_fname, "gzip -dc %s", filelist_fname);
900    if (!(pin = popen(command_to_open_fname, "r"))) {
901        log_OS_error("Unable to openin filelist_fname");
902        mr_free(command_to_open_fname);
903        return (NULL);
904    }
905    mr_free(command_to_open_fname);
906
907    open_evalcall_form("Loading filelist from disk");
908    for ((void)fgets(fname, MAX_STR_LEN, pin); !feof(pin);
909         (void)fgets(fname, MAX_STR_LEN, pin)) {
910        if ((fname[strlen(fname) - 1] == 13
911             || fname[strlen(fname) - 1] == 10) && strlen(fname) > 0) {
912            fname[strlen(fname) - 1] = '\0';
913        }
914//      strip_spaces (fname);
915        if (!strlen(fname)) {
916            continue;
917        }
918        for (pos_in_fname = 0; fname[pos_in_fname] != '\0'; pos_in_fname++) {
919            if (fname[pos_in_fname] != '/') {
920                continue;
921            }
922            strcpy(tmp, fname);
923            tmp[pos_in_fname] = '\0';
924            if (strlen(tmp)) {
925                add_string_at_node(filelist, tmp);
926            }
927        }
928        add_string_at_node(filelist, fname);
929        if (!(++lino % 1111)) {
930            percentage = (int) (lino * 100 / lines_in_filelist);
931            update_evalcall_form(percentage);
932        }
933    }
934    paranoid_pclose(pin);
935    close_evalcall_form();
936    log_it("Finished loading filelist");
937    return (filelist);
938}
939
940
941/**
942 * Log a list of files in @p node.
943 * @param node The toplevel node to use.
944 */
945void show_filelist(struct s_node *node)
946{
947    static int depth = 0;
948    static char current_string[200];
949
950    if (depth == 0) {
951        log_msg(0, "----------------show filelist--------------");
952    }
953    current_string[depth] = node->ch;
954
955    log_msg(3, "depth=%d", depth);
956    if (node->down) {
957        log_msg(3, "moving down");
958        depth++;
959        show_filelist(node->down);
960        depth--;
961    }
962
963    if (!node->ch) {
964        log_msg(0, "%s\n", current_string);
965    }
966
967    if (node->right) {
968        log_msg(3, "moving right");
969        show_filelist(node->right);
970    }
971    if (depth == 0) {
972        log_msg(0, "----------------show filelist--------------");
973    }
974    return;
975}
976
977
978
979
980/**
981 * Reset the filelist to the state it was when it was loaded. This does not
982 * touch the file on disk.
983 * @param filelist The filelist tree structure.
984 */
985void reload_filelist(struct s_node *filelist)
986{
987    assert(filelist != NULL);
988    toggle_node_selection(filelist, FALSE);
989    toggle_path_expandability(filelist, "/", FALSE);
990    toggle_all_root_dirs_on(filelist);
991}
992
993
994
995/**
996 * Save a filelist tree structure to disk.
997 * @param filelist The filelist tree structure to save.
998 * @param outfname Where to save it.
999 */
1000void save_filelist(struct s_node *filelist, char *outfname)
1001{
1002    /*@ int ********************************************************* */
1003    static int percentage;
1004    static int depth = 0;
1005
1006    /*@ buffers ***************************************************** */
1007    static char str[MAX_STR_LEN];
1008
1009    /*@ structures ************************************************** */
1010    struct s_node *node;
1011
1012    /*@ pointers **************************************************** */
1013    static FILE *fout = NULL;
1014
1015    /*@ long ******************************************************** */
1016    static long lines_in_filelist = 0;
1017    static long lino = 0;
1018
1019    /*@ end vars *************************************************** */
1020
1021    assert(filelist != NULL);
1022    assert(outfname != NULL);   // will be zerolength if save_filelist() is called by itself
1023    if (depth == 0) {
1024        log_to_screen("Saving filelist");
1025        if (!(fout = fopen(outfname, "w"))) {
1026            fatal_error("Cannot openout/save filelist");
1027        }
1028        lines_in_filelist = g_original_noof_lines_in_filelist;  /* set by load_filelist() */
1029        open_evalcall_form("Saving selection to disk");
1030    }
1031    for (node = filelist; node != NULL; node = node->right) {
1032        str[depth] = node->ch;
1033        log_msg(5, "depth=%d ch=%c", depth, node->ch);
1034        if (!node->ch) {
1035//    if (node->selected)
1036//      {
1037            fprintf(fout, "%s\n", str);
1038//      }
1039            if (!(++lino % 1111)) {
1040                percentage = (int) (lino * 100 / lines_in_filelist);
1041                update_evalcall_form(percentage);
1042            }
1043        }
1044        if (node->down) {
1045            depth++;
1046            save_filelist(node->down, "");
1047            depth--;
1048        }
1049    }
1050    if (depth == 0) {
1051        paranoid_fclose(fout);
1052        close_evalcall_form();
1053        log_it("Finished saving filelist");
1054    }
1055}
1056
1057
1058
1059/**
1060 * Toggle all root dirs on.
1061 * @param filelist The filelist tree structure to operate on.
1062 * @bug I don't understand this function. Would someone care to explain it?
1063 */
1064void toggle_all_root_dirs_on(struct s_node *filelist)
1065{
1066    /*@ structures ************************************************** */
1067    struct s_node *node;
1068
1069    /*@ int ********************************************************* */
1070    static int depth = 0;
1071    static int root_dirs_expanded;
1072
1073    /*@ buffers ***************************************************** */
1074    static char filename[MAX_STR_LEN];
1075
1076    /*@ end vars *************************************************** */
1077
1078    assert(filelist != NULL);
1079    if (depth == 0) {
1080        log_it("Toggling all root dirs ON");
1081        root_dirs_expanded = 0;
1082    }
1083    for (node = filelist; node != NULL; node = node->right) {
1084        filename[depth] = node->ch;
1085        if (node->ch == '\0' && strlen(filename) > 1
1086            && (!strchr(filename + 1, '/'))) {
1087            node->selected = FALSE;
1088            node->expanded = TRUE;
1089//    log_it (filename);
1090            root_dirs_expanded++;
1091        }
1092        if (node->down) {
1093            depth++;
1094            toggle_all_root_dirs_on(node->down);
1095            depth--;
1096        }
1097    }
1098    if (depth == 0) {
1099        log_it("Finished toggling all root dirs ON");
1100    }
1101}
1102
1103
1104/**
1105 * Toggle the expandability of a path.
1106 * @param filelist The filelist tree to operate on.
1107 * @param pathname The path to toggle expandability of.
1108 * @param on_or_off Whether to toggle it on or off.
1109 * @bug I don't understand this function. Would someone care to explain it?
1110 */
1111void
1112toggle_path_expandability(struct s_node *filelist, char *pathname,
1113                          bool on_or_off)
1114{
1115
1116    /*@ int ******************************************************** */
1117    static int depth = 0;
1118    static int total_expanded;
1119    static int root_depth;
1120    int j;
1121    /*@ structures ************************************************* */
1122    struct s_node *node;
1123
1124    /*@ buffers **************************************************** */
1125    static char current_filename[MAX_STR_LEN];
1126
1127/*  char tmp[MAX_STR_LEN+2]; */
1128
1129    /*@ end vars *************************************************** */
1130
1131    assert(filelist != NULL);
1132    assert_string_is_neither_NULL_nor_zerolength(pathname);
1133    if (depth == 0) {
1134        total_expanded = 0;
1135//      log_it ("Toggling path's expandability");
1136        for (root_depth = (int) strlen(pathname);
1137             root_depth > 0 && pathname[root_depth - 1] != '/';
1138             root_depth--);
1139        if (root_depth < 2) {
1140            root_depth = (int) strlen(pathname);
1141        }
1142    }
1143    for (node = filelist; node != NULL; node = node->right) {
1144        current_filename[depth] = node->ch;
1145        if (node->down) {
1146            depth++;
1147            toggle_path_expandability(node->down, pathname, on_or_off);
1148            depth--;
1149        }
1150        if (node->ch == '\0') {
1151            if (!strncmp(pathname, current_filename, strlen(pathname))) {
1152                for (j = root_depth;
1153                     current_filename[j] != '/'
1154                     && current_filename[j] != '\0'; j++);
1155                if (current_filename[j] != '\0') {
1156                    for (j++;
1157                         current_filename[j] != '/'
1158                         && current_filename[j] != '\0'; j++);
1159                }
1160                if (current_filename[j] == '\0') {
1161                    node->expanded =
1162                        (!strcmp(pathname, current_filename) ? TRUE :
1163                         on_or_off);
1164                }
1165            }
1166        }
1167        if (node->expanded) {
1168            if (total_expanded < ARBITRARY_MAXIMUM - 32
1169                || !strrchr(current_filename + strlen(pathname), '/')) {
1170                total_expanded++;
1171            } else {
1172                node->expanded = FALSE;
1173            }
1174        }
1175    }
1176    if (depth == 0) {
1177//      log_it ("Finished toggling expandability");
1178    }
1179}
1180
1181/**
1182 * Toggle whether a path is selected.
1183 * @param filelist The filelist tree to operate on.
1184 * @param pathname The path to toggle selection of.
1185 * @param on_or_off Whether to toggle it on or off.
1186 * @bug I don't understand this function. Would someone care to explain it?
1187 */
1188void
1189toggle_path_selection(struct s_node *filelist, char *pathname,
1190                      bool on_or_off)
1191{
1192    /*@ int ********************************************************* */
1193    static int depth = 0;
1194    int j;
1195
1196    /*@ structures ************************************************** */
1197    struct s_node *node;
1198
1199    /*@ buffers ***************************************************** */
1200    static char current_filename[MAX_STR_LEN];
1201
1202    /*@ end vars *************************************************** */
1203    assert(filelist != NULL);
1204    assert_string_is_neither_NULL_nor_zerolength(pathname);
1205    if (depth == 0) {
1206        log_it("Toggling path's selection");
1207    }
1208    for (node = filelist; node != NULL; node = node->right) {
1209        current_filename[depth] = node->ch;
1210        if (node->down) {
1211            depth++;
1212            toggle_path_selection(node->down, pathname, on_or_off);
1213            depth--;
1214        }
1215        if (node->ch == '\0') {
1216            if (!strncmp(pathname, current_filename, strlen(pathname))) {
1217                for (j = 0;
1218                     pathname[j] != '\0'
1219                     && pathname[j] == current_filename[j]; j++);
1220                    if (current_filename[j] == '/'
1221                        || current_filename[j] == '\0') {
1222                        node->selected = on_or_off;
1223                }
1224            }
1225        }
1226    }
1227    if (depth == 0) {
1228        log_it("Finished toggling selection");
1229    }
1230}
1231
1232
1233/**
1234 * Toggle node selection of a filelist tree.
1235 * @param filelist The filelist tree to operate on.
1236 * @param on_or_off Whether to toggle selection on or off.
1237 * @bug I don't understand this function. Would someone care to explain it?
1238 */
1239void toggle_node_selection(struct s_node *filelist, bool on_or_off)
1240{
1241    /*@ structure ************************************************** */
1242    struct s_node *node;
1243
1244    /*@ end vars *************************************************** */
1245    assert(filelist != NULL);
1246    for (node = filelist; node != NULL; node = node->right) {
1247        if (node->ch == '/') {
1248            continue;
1249        }                       /* don't go deep */
1250        if (node->ch == '\0') {
1251            node->selected = on_or_off;
1252        }
1253        if (node->down) {
1254            toggle_node_selection(node->down, on_or_off);
1255        }
1256    }
1257}
1258
1259
1260
1261
1262
1263
1264
1265/**
1266 * The pathname to the skeleton filelist, used to give better progress reporting for mondo_makefilelist().
1267 */
1268char *g_skeleton_filelist = NULL;
1269
1270/**
1271 * Number of entries in the skeleton filelist.
1272 */
1273long g_skeleton_entries = 0;
1274
1275/**
1276 * Wrapper around mondo_makefilelist().
1277 * @param bkpinfo The backup information structure. Fields used:
1278 * - @c bkpinfo->differential
1279 * - @c bkpinfo->exclude_paths
1280 * - @c bkpinfo->include_paths
1281 * - @c bkpinfo->make_filelist
1282 * - @c bkpinfo->scratchdir
1283 * - @c bkpinfo->tmpdir
1284 * @return 0 for success, nonzero for failure.
1285 * @see mondo_makefilelist
1286 */
1287int prepare_filelist()
1288{
1289
1290    /*@ int **************************************************** */
1291    int res = 0;
1292
1293    assert(bkpinfo != NULL);
1294    log_it("tmpdir=%s; scratchdir=%s", bkpinfo->tmpdir, bkpinfo->scratchdir);
1295    if (bkpinfo->make_filelist) {
1296        mvaddstr_and_log_it(g_currentY, 0,
1297                            "Making catalog of files to be backed up");
1298    } else {
1299        mvaddstr_and_log_it(g_currentY, 0,
1300                            "Using supplied catalog of files to be backed up");
1301    }
1302
1303    if (bkpinfo->make_filelist) {
1304        res =
1305            mondo_makefilelist(MONDO_LOGFILE, bkpinfo->include_paths, bkpinfo->exclude_paths, bkpinfo->differential, NULL);
1306    } else {
1307        res =
1308            mondo_makefilelist(MONDO_LOGFILE, NULL, bkpinfo->exclude_paths, bkpinfo->differential, bkpinfo->include_paths);
1309    }
1310
1311    if (res) {
1312        log_OS_error("Call to mondo_makefilelist failed");
1313        mvaddstr_and_log_it(g_currentY++, 74, "Failed.");
1314    } else {
1315        mvaddstr_and_log_it(g_currentY++, 74, "Done.");
1316    }
1317    return (res);
1318}
1319
1320
1321/**
1322 * Recursively list all files in @p dir newer than @p time_of_last_full_backup to @p fout.
1323 * @param dir The directory to list to @p fout.
1324 * @param sth The directories to skip (exclude).
1325 * @param fout The file to write everything to.
1326 * @param time_of_last_full_backup Only backup files newer than this (0 to disable).
1327 * @return 0, always.
1328 * @bug Return value should be @c void.
1329 */
1330int open_and_list_dir(char *dir1, char *sth, FILE * fout,
1331                      time_t time_of_last_full_backup)
1332{
1333    const char delims[] = " ";
1334
1335    DIR *dip;
1336    struct dirent *dit;
1337    struct stat statbuf;
1338    char *new;
1339    char *tmp;
1340    char *dir = NULL;
1341    static int percentage = 0;
1342    char *skip_these;
1343    char *new_with_spaces;
1344    char *strtmp;
1345    char *token;
1346    char *find_excludes;
1347    static char *name_of_evalcall_form;
1348    int i;
1349    int lastpos = 0;
1350    static int depth = 0;
1351    char *p;
1352    static int counter = 0;
1353    static int uberctr = 0;
1354    static char *find_skeleton_marker;
1355    static long skeleton_lino = 0;
1356    static time_t last_time = 0;
1357    time_t this_time;
1358
1359    dir = mr_stresc(dir1, "`$\\\"(){}'[]&*?|!#~", '\\');
1360    p = strrchr(dir, '/');
1361    if (p) {
1362        if (!strcmp(p, "/.") || !strcmp(p, "/..")) {
1363            mr_free(dir);
1364            return (0);
1365        }
1366    }
1367
1368    if (!depth) {
1369        malloc_string(name_of_evalcall_form);
1370        malloc_string(find_skeleton_marker);
1371        mr_asprintf(find_excludes, " ");
1372        if (sth != NULL) {
1373            while((token = mr_strtok(sth, delims, &lastpos))) {
1374                mr_strcat(find_excludes," -path %s -prune -o", token);
1375                mr_free(token);
1376            }
1377        }
1378#if linux
1379        // 2.6 has /sys as a proc-type thing -- must be excluded
1380        mr_asprintf(strtmp, "find '%s' -fstype mvfs -prune -o -fstype devpts -prune -o -fstype tmpfs -prune -o -fstype proc -prune -o -fstype sysfs -prune -o %s -type d -print > %s 2> /dev/null", dir, find_excludes, g_skeleton_filelist);
1381#else
1382        // On BSD, for example, /sys is the kernel sources -- don't exclude
1383        mr_asprintf(strtmp, "find '%s' -fstype mvfs -prune -o -path /proc -prune -o %s -type d -print > %s 2> /dev/null", dir, find_excludes, g_skeleton_filelist);
1384#endif
1385        mr_free(find_excludes);
1386
1387        log_msg(5, "find command = %s", strtmp);
1388        (void)system(strtmp);
1389        mr_free(strtmp);
1390
1391        mr_asprintf(tmp, "wc -l %s | awk '{print $1;}'", g_skeleton_filelist);
1392        g_skeleton_entries = 1 + atol(call_program_and_get_last_line_of_output(tmp));
1393        mr_free(tmp);
1394
1395        sprintf(name_of_evalcall_form, "Making catalog of %s", dir);
1396        open_evalcall_form(name_of_evalcall_form);
1397        find_skeleton_marker[0] = '\0';
1398        skeleton_lino = 1;
1399        log_msg(5, "entries = %ld", g_skeleton_entries);
1400        percentage = 0;
1401    } else  // update evalcall form if appropriate
1402    {
1403        sprintf(find_skeleton_marker,
1404                "grep -Fv '%s' %s > %s.new 2> /dev/null", dir,
1405                g_skeleton_filelist, g_skeleton_filelist);
1406        if (!system(find_skeleton_marker)) {
1407            percentage = (int) (skeleton_lino * 100 / g_skeleton_entries);
1408            skeleton_lino++;
1409            sprintf(find_skeleton_marker, "mv -f %s.new %s",
1410                    g_skeleton_filelist, g_skeleton_filelist);
1411            run_program_and_log_output(find_skeleton_marker, 8);
1412            time(&this_time);
1413            if (this_time != last_time) {
1414                last_time = this_time;
1415#ifndef _XWIN
1416                if (!g_text_mode) {
1417                    mr_asprintf(tmp, "Reading %-68s", dir);
1418                    newtDrawRootText(0, g_noof_rows - 3, tmp);
1419                    mr_free(tmp);
1420                }
1421#endif
1422                update_evalcall_form(percentage);
1423            }
1424        }
1425    }
1426
1427    depth++;
1428
1429//  log_msg(0, "Cataloguing %s", dir);
1430    if (sth[0] == ' ') {
1431        mr_asprintf(skip_these, "%s", sth);
1432    } else {
1433        mr_asprintf(skip_these, " %s ", sth);
1434    }
1435    mr_asprintf(new_with_spaces, " %s ", dir);
1436    if ((dip = opendir(dir)) == NULL) {
1437        mr_asprintf(tmp,"opendir %s", dir);
1438        log_OS_error(tmp);
1439        mr_free(tmp);
1440    } else if (strstr(skip_these, new_with_spaces)) {
1441        fprintf(fout, "%s\n", dir); // if excluded dir then print dir ONLY
1442    } else {
1443        fprintf(fout, "%s\n", dir);
1444        while ((dit = readdir(dip)) != NULL) {
1445            i++;
1446            if (strcmp(dir, "/")) {
1447                mr_asprintf(new,"%s/%s",dir,dit->d_name);
1448            } else {
1449                mr_asprintf(new,"%s%s",dir,dit->d_name);
1450            }
1451            paranoid_free(new_with_spaces);
1452            mr_asprintf(new_with_spaces, " %s ", new);
1453            if (strstr(skip_these, new_with_spaces)) {
1454                fprintf(fout, "%s\n", new);
1455                mr_free(new_with_spaces);
1456            } else {
1457                mr_free(new_with_spaces);
1458                if (!lstat(new, &statbuf)) {
1459                    if (!S_ISLNK(statbuf.st_mode)
1460                        && S_ISDIR(statbuf.st_mode)) {
1461                        open_and_list_dir(new, skip_these, fout,
1462                                          time_of_last_full_backup);
1463                    } else {
1464                        if (time_of_last_full_backup == 0
1465                            || time_of_last_full_backup <
1466                            statbuf.st_ctime) {
1467                            fprintf(fout, "%s\n", new);
1468                            if ((counter++) > 128) {
1469                                counter = 0;
1470                                uberctr++;
1471                                mr_asprintf(tmp, " %c ", special_dot_char(uberctr));
1472#ifndef _XWIN
1473                                if (!g_text_mode) {
1474                                    newtDrawRootText(77, g_noof_rows - 3, tmp);
1475                                    newtRefresh();
1476                                }
1477#endif
1478                                mr_free(tmp);
1479                            }
1480                        }
1481                    }
1482                }
1483            }
1484            paranoid_free(new);
1485        }
1486    }
1487    paranoid_free(new_with_spaces);
1488    paranoid_free(skip_these);
1489    mr_free(dir);
1490
1491    if (dip) {
1492        if (closedir(dip) == -1) {
1493            log_OS_error("closedir");
1494        }
1495    }
1496    depth--;
1497    if (!depth) {
1498        close_evalcall_form();
1499        paranoid_free(name_of_evalcall_form);
1500        paranoid_free(find_skeleton_marker);
1501        unlink(g_skeleton_filelist);
1502        log_msg(5, "g_skeleton_entries = %ld", g_skeleton_entries);
1503    }
1504    return (0);
1505}
1506
1507
1508
1509/**
1510 * Get the next entry in the space-separated list in @p incoming.
1511 * So if @p incoming was '"one and two" three four', we would
1512 * return "one and two".
1513 * @param incoming The list to get the next entry from.
1514 * @return The first item in the list (respecting double quotes).
1515 * @note The returned string points to static data that will be overwritten with each call.
1516 */
1517char *next_entry(char *incoming)
1518{
1519    char *sz_res;
1520    char *p;
1521    bool in_quotes = FALSE;
1522
1523    mr_asprintf(sz_res, "%s", incoming);
1524    p = sz_res;
1525    while ((*p != ' ' || in_quotes) && *p != '\0') {
1526        if (*p == '\"') {
1527            in_quotes = !in_quotes;
1528        }
1529        p++;
1530    }
1531    *p = '\0';
1532    return (sz_res);
1533}
1534
1535
1536
1537/**
1538 * Create the filelist for the backup. It will be stored in [scratchdir]/archives/filelist.full.
1539 * @param logfile Unused.
1540 * @param include_paths The paths to back up, or NULL if you're using a user-defined filelist.
1541 * @param excp The paths to NOT back up.
1542 * @param differential The differential level (currently only 0 and 1 are supported).
1543 * @param userdef_filelist The user-defined filelist, or NULL if you're using @p include_paths.
1544 * @return 0, always.
1545 * @bug @p logfile is unused.
1546 * @bug Return value is meaningless.
1547 */
1548int mondo_makefilelist(char *logfile, char *include_paths, char *excp, int differential, char *userdef_filelist)
1549{
1550    char *p, *q;
1551    char *sz_datefile;
1552    char *sz_filelist;
1553    char *exclude_paths = NULL;
1554    char *tmp;
1555    int i;
1556    FILE *fout;
1557    char *command = NULL;
1558    time_t time_of_last_full_backup = 0;
1559    struct stat statbuf;
1560    char *tmp1 = NULL;
1561    char *tmp2 = NULL;
1562
1563    malloc_string(tmp);
1564    malloc_string(g_skeleton_filelist);
1565    mr_asprintf(sz_datefile,MONDO_CACHE"/difflevel.%d" , 0);
1566    if (!include_paths && !userdef_filelist) {
1567        fatal_error("Please supply either include_paths or userdef_filelist");
1568    }
1569    // make hole for filelist
1570    mr_asprintf(command, "mkdir -p %s/archives", bkpinfo->scratchdir);
1571    paranoid_system(command);
1572    mr_free(command);
1573
1574    mr_asprintf(sz_filelist, "%s/tmpfs/filelist.full", bkpinfo->tmpdir);
1575    make_hole_for_file(sz_filelist);
1576
1577    if (differential == 0) {
1578        // restore last good datefile if it exists
1579        mr_asprintf(command, "cp -f %s.aborted %s", sz_datefile, sz_datefile);
1580        run_program_and_log_output(command, 3);
1581        mr_free(command);
1582
1583        // backup last known good datefile just in case :)
1584        if (does_file_exist(sz_datefile)) {
1585            mr_asprintf(command, "mv -f %s %s.aborted", sz_datefile, sz_datefile);
1586            paranoid_system(command);
1587            mr_free(command);
1588        }
1589        make_hole_for_file(sz_datefile);
1590        write_one_liner_data_file(sz_datefile,
1591                                  call_program_and_get_last_line_of_output
1592                                  ("date +%s"));
1593    } else if (lstat(sz_datefile, &statbuf)) {
1594        log_msg(2,
1595                "Warning - unable to find date of previous backup. Full backup instead.");
1596        differential = 0;
1597        time_of_last_full_backup = 0;
1598    } else {
1599        time_of_last_full_backup = statbuf.st_mtime;
1600        log_msg(2, "Differential backup. Yay.");
1601    }
1602    paranoid_free(sz_datefile);
1603
1604// use user-specified filelist (if specified)
1605    if (userdef_filelist) {
1606        log_msg(1,
1607                "Using the user-specified filelist - %s - instead of calculating one",
1608                userdef_filelist);
1609        mr_asprintf(command, "cp -f %s %s", userdef_filelist, sz_filelist);
1610        if (run_program_and_log_output(command, 3)) {
1611            mr_free(command);
1612            fatal_error("Failed to copy user-specified filelist");
1613        }
1614        mr_free(command);
1615    } else {
1616        if (include_paths) {
1617            log_msg(2, "include_paths = '%s'", include_paths);
1618        }
1619        log_msg(1, "Calculating filelist");
1620        mr_asprintf(tmp2, "%s", call_program_and_get_last_line_of_output("mount | grep -Ew 'ntfs|ntfs-3g|fat|vfat|dos' | awk '{print $3}'"));
1621        if (strlen(tmp2) < 1) {
1622            mr_asprintf(tmp1," ");
1623        } else {
1624            log_msg(2, "Found windows FS: %s",tmp2);
1625            mr_asprintf(tmp1, "find %s -name '/win386.swp' -o -name '/hiberfil.sys' -o -name '/pagefile.sys' 2> /dev/null\n",tmp2);
1626            mr_free(tmp2);
1627            mr_asprintf(tmp2, "%s", call_program_and_get_last_line_of_output(tmp1));
1628            log_msg(2, "Found windows files: %s",tmp2);
1629        }
1630        mr_free(tmp1);
1631
1632        mr_asprintf(exclude_paths, " %s %s %s %s %s . ..  " MNT_CDROM " " MNT_FLOPPY " /media /tmp  /proc /sys " MINDI_CACHE, MONDO_CACHE, (excp == NULL) ? "" : excp, tmp2, (bkpinfo->tmpdir[0] == '/' && bkpinfo->tmpdir[1] == '/') ? (bkpinfo->tmpdir + 1) : bkpinfo->tmpdir, (bkpinfo->scratchdir[0] == '/' && bkpinfo->scratchdir[1] == '/') ? (bkpinfo->scratchdir + 1) : bkpinfo->scratchdir);
1633        mr_free(tmp2);
1634
1635        log_msg(2, "Excluding paths = '%s'", exclude_paths);
1636        log_msg(2, "Generating skeleton filelist so that we can track our progress");
1637        sprintf(g_skeleton_filelist, "%s/tmpfs/skeleton.txt", bkpinfo->tmpdir);
1638        make_hole_for_file(g_skeleton_filelist);
1639        log_msg(4, "g_skeleton_entries = %ld", g_skeleton_entries);
1640        log_msg(2, "Opening out filelist to %s", sz_filelist);
1641        if (!(fout = fopen(sz_filelist, "w"))) {
1642            fatal_error("Cannot openout to sz_filelist");
1643        }
1644        i = 0;
1645        if ((!include_paths) || (strlen(include_paths) == 0)) {
1646            log_msg(1, "Including only '/' in %s", sz_filelist);
1647            open_and_list_dir("/", exclude_paths, fout,
1648                              time_of_last_full_backup);
1649        } else {
1650            p = include_paths;
1651            while (*p) {
1652                q = next_entry(p);
1653                log_msg(1, "Including %s in filelist %s", q, sz_filelist);
1654                open_and_list_dir(q, exclude_paths, fout,
1655                                  time_of_last_full_backup);
1656                p += strlen(q);
1657                paranoid_free(q);
1658                while (*p == ' ') {
1659                    p++;
1660                }
1661            }
1662        }
1663        mr_free(exclude_paths);
1664        paranoid_fclose(fout);
1665    }
1666    log_msg(2, "Copying new filelist to scratchdir");
1667    mr_asprintf(command, "mkdir -p %s/archives", bkpinfo->scratchdir);
1668    paranoid_system(command);
1669    mr_free(command);
1670
1671    mr_asprintf(command, "cp -f %s %s/archives/", sz_filelist, bkpinfo->scratchdir);
1672    paranoid_system(command);
1673    mr_free(command);
1674
1675    mr_asprintf(command, "mv -f %s %s", sz_filelist, bkpinfo->tmpdir);
1676    paranoid_system(command);
1677    mr_free(command);
1678
1679    paranoid_free(sz_filelist);
1680    log_msg(2, "Freeing variables");
1681    paranoid_free(tmp);
1682    paranoid_free(g_skeleton_filelist);
1683    log_msg(2, "Exiting");
1684    return (0);
1685}
1686
1687
1688
1689
1690/**
1691 * Locate the string @p string_to_find in the tree rooted at @p startnode.
1692 * @param startnode The node containing the root of the directory tree.
1693 * @param string_to_find The string to look for at @p startnode.
1694 * @return The node containing the last element of @p string_to_find, or NULL if
1695 * it was not found.
1696 */
1697struct s_node *find_string_at_node(struct s_node *startnode,
1698                                   char *string_to_find)
1699{
1700    /*@ int ******************************************************** */
1701    int noof_chars;
1702    static int depth = 0;
1703    static char original_string[MAX_STR_LEN];
1704
1705    /*@ sturctures ************************************************* */
1706    struct s_node *node;
1707
1708    /*@ char  ****************************************************** */
1709    char char_to_find;
1710
1711    /*@ bools ****************************************************** */
1712
1713    if (!depth) {
1714        strcpy(original_string, string_to_find);
1715    }
1716
1717    assert(startnode != NULL);
1718    assert(string_to_find != NULL);
1719
1720    noof_chars = strlen(string_to_find) + 1;    /* we include the '\0' */
1721
1722    log_msg(7, "starting --- str=%s", string_to_find);
1723
1724/* walk across tree if necessary */
1725    node = startnode;
1726    char_to_find = string_to_find[0];
1727    if (node->right != NULL && node->ch < char_to_find) {
1728        log_msg(7, "depth=%d --- going RIGHT ... %c-->%c", depth,
1729                char_to_find, node->ch, (node->right)->ch);
1730        return (find_string_at_node(node->right, string_to_find));
1731    }
1732
1733/* walk down tree if appropriate */
1734    if (node->down != NULL && node->ch == char_to_find) {
1735        log_msg(7, "depth=%d char=%c --- going DOWN", depth, char_to_find);
1736        depth++;
1737        node = find_string_at_node(node->down, string_to_find + 1);
1738        depth--;
1739        return (node);
1740    }
1741
1742    if (char_to_find == '\0' && node->ch == '\0') {
1743        log_msg(7, "%s is in tree", original_string);
1744        return (node);
1745    } else {
1746        log_msg(7, "%s is NOT in tree", original_string);
1747        return (NULL);
1748    }
1749}
1750
1751
1752
1753/**
1754 * Write all entries in @p needles_list_fname which are also in
1755 * @p filelist to @p matches_list_fname.
1756 * @param needles_list_fname A file containing strings to look for, 1 per line.
1757 * @param filelist The node for the root of the directory structure to search in.
1758 * @param matches_list_fname The filename where we should put the matches.
1759 * @return The number of matches found.
1760 */
1761long save_filelist_entries_in_common(char *needles_list_fname,
1762                                     struct s_node *filelist,
1763                                     char *matches_list_fname,
1764                                     bool use_star)
1765{
1766    int retval = 0;
1767    struct s_node *found_node;
1768    FILE *fin;
1769    FILE *fout;
1770    char *fname;
1771    char *tmp;
1772    size_t len = 0;             // Scrub's patch doesn't work without that
1773
1774//  log_msg(1, "use_star = %s", (use_star)?"TRUE":"FALSE");
1775    malloc_string(fname);
1776    malloc_string(tmp);
1777    log_msg(5, "starting");
1778    log_msg(5, "needles_list_fname = %s", needles_list_fname);
1779    log_msg(5, "matches_list_fname = %s", matches_list_fname);
1780    if (!(fin = fopen(needles_list_fname, "r"))) {
1781        fatal_error("Cannot openin needles_list_fname");
1782    }
1783    if (!(fout = fopen(matches_list_fname, "w"))) {
1784        fatal_error("Cannot openout matches_list_fname");
1785    }
1786    while (!feof(fin)) {
1787//      fscanf(fin, "%s\n", fname);
1788        len = MAX_STR_LEN - 1;
1789        (void)getline(&fname, &len, fin);   // patch by Scrub
1790        if (!use_star) {
1791            if (fname[0] == '/') {
1792                strcpy(tmp, fname);
1793            } else {
1794                tmp[0] = '/';
1795                strcpy(tmp + 1, fname);
1796            }
1797            strcpy(fname, tmp);
1798        }
1799        while (strlen(fname) > 0 && fname[strlen(fname) - 1] < 32) {
1800            fname[strlen(fname) - 1] = '\0';
1801        }
1802
1803        log_msg(5, "Looking for '%s'", fname);
1804        found_node = find_string_at_node(filelist, fname);
1805        if (found_node) {
1806            if (found_node->selected) {
1807                if (fname[0] == '/') {
1808                    strcpy(tmp, fname + 1);
1809                    strcpy(fname, tmp);
1810                }
1811                log_msg(5, "Found '%s'", fname);
1812                turn_wildcard_chars_into_literal_chars(tmp, fname);
1813                fprintf(fout, "%s\n", tmp);
1814                retval++;
1815            }
1816        }
1817    }
1818    paranoid_fclose(fout);
1819    paranoid_fclose(fin);
1820    paranoid_free(fname);
1821    paranoid_free(tmp);
1822    return (retval);
1823}
1824
1825
1826
1827
1828
1829
1830/**
1831 * Add all files listed in @p list_of_files_fname to the directory structure rooted at
1832 * @p filelist.
1833 * @param filelist The top node of the directory structure to add the files to.
1834 * @param list_of_files_fname The file containing the files to add, 1 per line.
1835 * @param flag_em If TRUE, then flag the added files for restoration.
1836 * @return 0 for success, nonzero for failure.
1837 */
1838int add_list_of_files_to_filelist(struct s_node *filelist,
1839                                  char *list_of_files_fname, bool flag_em)
1840{
1841    FILE *fin;
1842    char *tmp;
1843    struct s_node *nod;
1844
1845    malloc_string(tmp);
1846    log_msg(3, "Adding %s to filelist", list_of_files_fname);
1847    if (!(fin = fopen(list_of_files_fname, "r"))) {
1848        log_it("%s",list_of_files_fname);
1849        return (1);
1850    }
1851    for ((void)fgets(tmp, MAX_STR_LEN, fin); !feof(fin);
1852         (void)fgets(tmp, MAX_STR_LEN, fin)) {
1853        if (!tmp[0]) {
1854            continue;
1855        }
1856        if ((tmp[strlen(tmp) - 1] == 13 || tmp[strlen(tmp) - 1] == 10)
1857            && strlen(tmp) > 0) {
1858            tmp[strlen(tmp) - 1] = '\0';
1859        }
1860        log_msg(2, "tmp = '%s'", tmp);
1861        if (!tmp[0]) {
1862            continue;
1863        }
1864        if ((nod = find_string_at_node(filelist, tmp))) {
1865            log_msg(5, "Found '%s' in filelist already. Cool.", tmp);
1866        } else {
1867            add_string_at_node(filelist, tmp);
1868            nod = find_string_at_node(filelist, tmp);
1869        }
1870
1871        if (nod && flag_em) {
1872            toggle_path_selection(filelist, tmp, TRUE);
1873            log_msg(5, "Flagged '%s'", tmp);
1874        }
1875    }
1876    paranoid_fclose(fin);
1877    paranoid_free(tmp);
1878    return (0);
1879}
1880
1881/* @} - end of filelistGroup */
Note: See TracBrowser for help on using the repository browser.