source: branches/2.2.8/mondo/src/common/libmondo-filelist.c @ 2813

Last change on this file since 2813 was 2813, checked in by Bruno Cornec, 9 years ago

r2185@localhost (orig r2184): bruno | 2009-04-30 17:55:43 +0200
-Remove the maxdepth param in find to take in account deeply mounted proc file systems. Shouldn't slowdown stuff too much on local disks, maybe more on net FS, but net FS should nearly always be excluded.


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