source: MondoRescue/branches/3.3/mondo/src/common/libmondo-filelist.c@ 3891

Last change on this file since 3891 was 3879, checked in by Bruno Cornec, 4 months ago

Fix all remaining compiler errors

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