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

Last change on this file since 3137 was 3137, checked in by Bruno Cornec, 11 years ago

r5336@localhost: bruno | 2013-06-12 15:37:29 +0200

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