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

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