source: branches/3.0/mondo/src/common/libmondo-filelist.c

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