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

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