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

Last change on this file since 3185 was 3185, checked in by bruno, 6 years ago

Simplify the interface of mr_getline and mr_asprintf. With 3.1 compatibility now will allow backports from this branch into 3.0

  • Property svn:keywords set to Id
File size: 50.9 KB
Line 
1/*
2   $Id: libmondo-filelist.c 3185 2013-09-20 12:29:57Z 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 3185 2013-09-20 12:29:57Z 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.