source: MondoRescue/branches/stable/mondo/src/common/libmondo-filelist.c@ 1164

Last change on this file since 1164 was 1164, checked in by Bruno Cornec, 17 years ago

Compiler warning solved (M. Loiseleur again !)
Code displaced to ease memeory management in case of exit in libmondo-archive.c

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