Imported Upstream version 3.1.0
[debian/amanda] / server-src / holding.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: holding.c,v 1.56 2006/06/09 23:07:26 martinea Exp $
28  *
29  * Functions to access holding disk
30  */
31
32 #include "amanda.h"
33 #include "util.h"
34 #include "holding.h"
35 #include "fileheader.h"
36 #include "logfile.h"
37
38 /*
39  * utilities */
40
41 /* Is fname a directory?
42  *
43  * @param fname: filename (fully qualified)
44  * @returns: boolean
45  */
46 static int is_dir(char *fname);
47
48 /* Is fname an empty file?
49  *
50  * @param fname: filename (fully qualified)
51  * @returns: boolean
52  */
53 static int is_emptyfile(char *fname);
54
55 /* sanity check that datestamp is of the form YYYYMMDD or 
56  * YYYYMMDDhhmmss
57  *
58  * @param fname: a filename (without directory)
59  * @returns: boolean
60  */
61 static int is_datestr(char *fname);
62
63 /*
64  * Static functions */
65
66 static int
67 is_dir(
68     char *fname)
69 {
70     struct stat statbuf;
71
72     if(stat(fname, &statbuf) == -1) return 0;
73
74     return (statbuf.st_mode & S_IFDIR) == S_IFDIR;
75 }
76
77 static int
78 is_emptyfile(
79     char *fname)
80 {
81     struct stat statbuf;
82
83     if(stat(fname, &statbuf) == -1) return 0;
84
85     return ((statbuf.st_mode & S_IFDIR) != S_IFDIR) &&
86                 (statbuf.st_size == (off_t)0);
87 }
88
89 static int
90 is_datestr(
91     char *fname)
92 {
93     char *cp;
94     int ch, num, date, year, month, hour, minute, second;
95     char ymd[9], hms[7];
96
97     /* must be 8 digits */
98     for(cp = fname; (ch = *cp) != '\0'; cp++) {
99         if(!isdigit(ch)) {
100             break;
101         }
102     }
103     if(ch != '\0' || (cp-fname != 8 && cp-fname != 14)) {
104         return 0;
105     }
106
107     /* sanity check year, month, and day */
108
109     strncpy(ymd, fname, 8);
110     ymd[8] = '\0';
111     num = atoi(ymd);
112     year = num / 10000;
113     month = (num / 100) % 100;
114     date = num % 100;
115     if(year<1990 || year>2100 || month<1 || month>12 || date<1 || date>31)
116         return 0;
117
118     if(cp-fname == 8)
119         return 1;
120
121     /* sanity check hour, minute, and second */
122     strncpy(hms, fname+8, 6);
123     hms[6] = '\0';
124     num = atoi(hms);
125     hour = num / 10000;
126     minute = (num / 100) % 100;
127     second = num % 100;
128     if(hour> 23 || minute>59 || second>59)
129         return 0;
130
131     /* yes, we passed all the checks */
132
133     return 1;
134 }
135
136 /*
137  * Recursion functions
138  *
139  * These implement a general-purpose walk down the holding-* hierarchy.
140  */
141
142 /* Perform a custom action for this holding element (disk, dir, file, chunk).
143  *
144  * If the element is not cruft, the next step into the tree will only take place 
145  * if this function returns a nonzero value.
146  *
147  * The walk is depth-first, with the callback for an element invoked
148  * before entering that element.  Callbacks may depend on this behavior.
149  *
150  * @param datap: generic user-data pointer
151  * @param base: the parent of the element being examined, or NULL for 
152  * holding disks
153  * @param element: the name of the element being examined
154  * @param fqpath: fully qualified path to 'element'
155  * @param is_cruft: nonzero if this element doesn't belong here
156  * @returns: nonzero if the walk should descend into this element.
157  */
158 typedef int (*holding_walk_fn)(
159     gpointer datap,
160     char *base,
161     char *element,
162     char *fqpath,
163     int is_cruft);
164
165 typedef enum {
166     STOP_AT_DISK,
167     STOP_AT_DIR,
168     STOP_AT_FILE,
169     STOP_AT_CHUNK
170 } stop_at_t;
171
172 /* Recurse over all holding chunks in a holding file.
173  *
174  * Call per_chunk_fn for each chunk of the given file
175  *
176  * datap is passed, unchanged, to all holding_walk_fns.
177  *
178  * @param hfile: holding file to examine (fully qualified path)
179  * @param datap: generic user-data pointer
180  * @param per_chunk_fn: function to call for each holding chunk
181  */
182 static void holding_walk_file(
183     char *hfile,
184     gpointer datap,
185     holding_walk_fn per_chunk_fn)
186 {
187     dumpfile_t file;
188     char *filename = NULL;
189
190     /* Loop through all cont_filenames (subsequent chunks) */
191     filename = stralloc(hfile);
192     while (filename != NULL && filename[0] != '\0') {
193         int is_cruft = 0;
194
195         /* get the header to look for cont_filename */
196         if (!holding_file_get_dumpfile(filename, &file)) {
197             is_cruft = 1;
198         }
199
200         if (per_chunk_fn) 
201             per_chunk_fn(datap, 
202                         hfile, 
203                         filename, 
204                         filename, 
205                         is_cruft);
206         amfree(filename);
207
208         /* and go on to the next chunk if this wasn't cruft */
209         if (!is_cruft)
210             filename = stralloc(file.cont_filename);
211         dumpfile_free_data(&file);
212     }
213
214     amfree(filename);
215 }
216
217 /* Recurse over all holding files in a holding directory.
218  *
219  * Call per_file_fn for each file, and so on, stopping at the level given by 
220  * stop_at.
221  *
222  * datap is passed, unchanged, to all holding_walk_fns.
223  *
224  * @param hdir: holding directory to examine (fully qualified path)
225  * @param datap: generic user-data pointer
226  * @param stop_at: do not proceed beyond this level of the hierarchy
227  * @param per_file_fn: function to call for each holding file
228  * @param per_chunk_fn: function to call for each holding chunk
229  */
230 static void holding_walk_dir(
231     char *hdir,
232     gpointer datap,
233     stop_at_t stop_at,
234     holding_walk_fn per_file_fn,
235     holding_walk_fn per_chunk_fn)
236 {
237     DIR *dir;
238     struct dirent *workdir;
239     char *hfile = NULL;
240     dumpfile_t dumpf;
241     int dumpf_ok;
242     int proceed = 1;
243
244     if ((dir = opendir(hdir)) == NULL) {
245         if (errno != ENOENT)
246            dbprintf(_("Warning: could not open holding dir %s: %s\n"),
247                   hdir, strerror(errno));
248         return;
249     }
250
251     while ((workdir = readdir(dir)) != NULL) {
252         int is_cruft = 0;
253
254         if (is_dot_or_dotdot(workdir->d_name))
255             continue; /* expected cruft */
256
257         hfile = newvstralloc(hfile,
258                      hdir, "/", workdir->d_name,
259                      NULL);
260
261         /* filter out various undesirables */
262         if (is_emptyfile(hfile))
263             is_cruft = 1;
264
265         if (is_dir(hfile)) {
266             is_cruft= 1;
267         }
268
269         if (!(dumpf_ok=holding_file_get_dumpfile(hfile, &dumpf)) ||
270             dumpf.type != F_DUMPFILE) {
271             if (dumpf_ok && dumpf.type == F_CONT_DUMPFILE)
272                 continue; /* silently skip expected file */
273
274             is_cruft = 1;
275         }
276
277         if (dumpf.dumplevel < 0 || dumpf.dumplevel > 9) {
278             is_cruft = 1;
279         }
280
281         if (per_file_fn) 
282             proceed = per_file_fn(datap, 
283                         hdir, 
284                         workdir->d_name, 
285                         hfile, 
286                         is_cruft);
287         if (!is_cruft && proceed && stop_at != STOP_AT_FILE)
288             holding_walk_file(hfile,
289                     datap,
290                     per_chunk_fn);
291         dumpfile_free_data(&dumpf);
292     }
293
294     closedir(dir);
295     amfree(hfile);
296 }
297
298 /* Recurse over all holding directories in a holding disk.
299  *
300  * Call per_dir_fn for each dir, and so on, stopping at the level given by 
301  * stop_at.
302  *
303  * datap is passed, unchanged, to all holding_walk_fns.
304  *
305  * @param hdisk: holding disk to examine (fully qualified path)
306  * @param datap: generic user-data pointer
307  * @param stop_at: do not proceed beyond this level of the hierarchy
308  * @param per_dir_fn: function to call for each holding dir
309  * @param per_file_fn: function to call for each holding file
310  * @param per_chunk_fn: function to call for each holding chunk
311  */
312 static void 
313 holding_walk_disk(
314     char *hdisk,
315     gpointer datap,
316     stop_at_t stop_at,
317     holding_walk_fn per_dir_fn,
318     holding_walk_fn per_file_fn,
319     holding_walk_fn per_chunk_fn)
320 {
321     DIR *dir;
322     struct dirent *workdir;
323     char *hdir = NULL;
324     int proceed = 1;
325
326     if ((dir = opendir(hdisk)) == NULL) {
327         if (errno != ENOENT)
328            dbprintf(_("Warning: could not open holding disk %s: %s\n"),
329                   hdisk, strerror(errno));
330         return;
331     }
332
333     while ((workdir = readdir(dir)) != NULL) {
334         int is_cruft = 0;
335
336         if (is_dot_or_dotdot(workdir->d_name))
337             continue; /* expected cruft */
338
339         hdir = newvstralloc(hdir,
340                      hdisk, "/", workdir->d_name,
341                      NULL);
342
343         /* detect cruft */
344         if (!is_dir(hdir)) {
345             is_cruft = 1;
346         } else if (!is_datestr(workdir->d_name)) {
347             /* EXT2/3 leave these in the root of each volume */
348             if (strcmp(workdir->d_name, "lost+found") == 0)
349                 continue; /* expected cruft */
350             else
351                 is_cruft = 1; /* unexpected */
352         }
353
354         if (per_dir_fn) 
355             proceed = per_dir_fn(datap, 
356                         hdisk, 
357                         workdir->d_name, 
358                         hdir, 
359                         is_cruft);
360         if (!is_cruft && proceed && stop_at != STOP_AT_DIR)
361             holding_walk_dir(hdir,
362                     datap,
363                     stop_at,
364                     per_file_fn,
365                     per_chunk_fn);
366     }
367
368     closedir(dir);
369     amfree(hdir);
370 }
371
372 /* Recurse over all holding disks.
373  *
374  * Call per_disk_fn for each disk, per_dir_fn for each dir, and so on, stopping
375  * at the level given by stop_at.
376  *
377  * datap is passed, unchanged, to all holding_walk_fns.
378  *
379  * @param datap: generic user-data pointer
380  * @param stop_at: do not proceed beyond this level of the hierarchy
381  * @param per_disk_fn: function to call for each holding disk
382  * @param per_dir_fn: function to call for each holding dir
383  * @param per_file_fn: function to call for each holding file
384  * @param per_chunk_fn: function to call for each holding chunk
385  */
386 static void 
387 holding_walk(
388     gpointer datap,
389     stop_at_t stop_at,
390     holding_walk_fn per_disk_fn,
391     holding_walk_fn per_dir_fn,
392     holding_walk_fn per_file_fn,
393     holding_walk_fn per_chunk_fn)
394 {
395     identlist_t    il;
396     holdingdisk_t *hdisk_conf;
397     char *hdisk;
398     int proceed = 1;
399
400     for (il = getconf_identlist(CNF_HOLDINGDISK);
401                 il != NULL;
402                 il = il->next) {
403         int is_cruft = 0;
404         hdisk_conf = lookup_holdingdisk(il->data);
405
406         hdisk = holdingdisk_get_diskdir(hdisk_conf);
407         if (!is_dir(hdisk))
408             is_cruft = 1;
409
410         if (per_disk_fn) 
411             proceed = per_disk_fn(datap, 
412                         NULL, 
413                         hdisk, 
414                         hdisk, 
415                         0);
416         if (proceed && stop_at != STOP_AT_DISK)
417             holding_walk_disk(hdisk,
418                     datap,
419                     stop_at,
420                     per_dir_fn,
421                     per_file_fn,
422                     per_chunk_fn);
423     }
424 }
425
426 /*
427  * holding_get_* functions
428  */
429 typedef struct {
430     GSList *result;
431     int fullpaths;
432 } holding_get_datap_t;
433
434 /* Functor for holding_get_*; adds 'element' or 'fqpath' to
435  * the result.
436  */
437 static int
438 holding_get_walk_fn(
439     gpointer datap,
440     G_GNUC_UNUSED char *base,
441     char *element,
442     char *fqpath,
443     int is_cruft)
444 {
445     holding_get_datap_t *data = (holding_get_datap_t *)datap;
446
447     /* ignore cruft */
448     if (is_cruft) return 0;
449
450     if (data->fullpaths)
451         data->result = g_slist_insert_sorted(data->result,
452                 stralloc(fqpath), 
453                 g_compare_strings);
454     else
455         data->result = g_slist_insert_sorted(data->result, 
456                 stralloc(element), 
457                 g_compare_strings);
458
459     /* don't proceed any deeper */
460     return 0;
461 }
462
463 GSList *
464 holding_get_disks(void)
465 {
466     holding_get_datap_t data;
467     data.result = NULL;
468     data.fullpaths = 1; /* ignored anyway */
469
470     holding_walk((gpointer)&data,
471         STOP_AT_DISK,
472         holding_get_walk_fn, NULL, NULL, NULL);
473
474     return data.result;
475 }
476
477 GSList *
478 holding_get_files(
479     char *hdir,
480     int fullpaths)
481 {
482     holding_get_datap_t data;
483     data.result = NULL;
484     data.fullpaths = fullpaths;
485
486     if (hdir) {
487         holding_walk_dir(hdir, (gpointer)&data,
488             STOP_AT_FILE,
489             holding_get_walk_fn, NULL);
490     } else {
491         holding_walk((gpointer)&data,
492             STOP_AT_FILE,
493             NULL, NULL, holding_get_walk_fn, NULL);
494     }
495
496     return data.result;
497 }
498
499 GSList *
500 holding_get_file_chunks(char *hfile)
501 {
502     holding_get_datap_t data;
503     data.result = NULL;
504     data.fullpaths = 1;
505
506     holding_walk_file(hfile, (gpointer)&data,
507         holding_get_walk_fn);
508
509     return data.result;
510 }
511
512 GSList *
513 holding_get_files_for_flush(
514     GSList *dateargs)
515 {
516     GSList *file_list, *file_elt;
517     GSList *date;
518     int date_matches;
519     disk_t *dp;
520     dumpfile_t file;
521     GSList *result_list = NULL;
522
523     /* loop over *all* files, checking each one's datestamp against the expressions
524      * in dateargs */
525     file_list = holding_get_files(NULL, 1);
526     for (file_elt = file_list; file_elt != NULL; file_elt = file_elt->next) {
527         /* get info on that file */
528         if (!holding_file_get_dumpfile((char *)file_elt->data, &file))
529             continue;
530
531         if (file.type != F_DUMPFILE) {
532             dumpfile_free_data(&file);
533             continue;
534         }
535
536         if (dateargs) {
537             date_matches = 0;
538             /* loop over date args, until we find a match */
539             for (date = dateargs; date !=NULL; date = date->next) {
540                 if (strcmp((char *)date->data, file.datestamp) == 0) {
541                     date_matches = 1;
542                     break;
543                 }
544             }
545         } else {
546             /* if no date list was provided, then all dates match */
547             date_matches = 1;
548         }
549         if (!date_matches) {
550             dumpfile_free_data(&file);
551             continue;
552         }
553
554         /* check that the hostname and disk are in the disklist */
555         dp = lookup_disk(file.name, file.disk);
556         if (dp == NULL) {
557             dbprintf(_("%s: disk %s:%s not in database, skipping it."),
558                         (char *)file_elt->data, file.name, file.disk);
559             dumpfile_free_data(&file);
560             continue;
561         }
562
563         /* passed all tests -- we'll flush this file */
564         result_list = g_slist_insert_sorted(result_list, 
565             stralloc(file_elt->data), 
566             g_compare_strings);
567         dumpfile_free_data(&file);
568     }
569
570     if (file_list) g_slist_free_full(file_list);
571
572     return result_list;
573 }
574
575 GSList *
576 holding_get_all_datestamps(void)
577 {
578     GSList *all_files, *file;
579     GSList *datestamps = NULL;
580
581     /* enumerate all files */
582     all_files = holding_get_files(NULL, 1);
583     for (file = all_files; file != NULL; file = file->next) {
584         dumpfile_t dfile;
585         if (!holding_file_get_dumpfile((char *)file->data, &dfile))
586             continue;
587         if (!g_slist_find_custom(datestamps, dfile.datestamp,
588                                  g_compare_strings)) {
589             datestamps = g_slist_insert_sorted(datestamps, 
590                                                stralloc(dfile.datestamp), 
591                                                g_compare_strings);
592         }
593         dumpfile_free_data(&dfile);
594     }
595
596     g_slist_free_full(all_files);
597
598     return datestamps;
599 }
600
601 off_t
602 holding_file_size(
603     char *hfile,
604     int strip_headers)
605 {
606     dumpfile_t file;
607     char *filename;
608     off_t size = (off_t)0;
609     struct stat finfo;
610
611     /* (note: we don't use holding_get_file_chunks here because that would
612      * entail opening each file twice) */
613
614     /* Loop through all cont_filenames (subsequent chunks) */
615     filename = stralloc(hfile);
616     while (filename != NULL && filename[0] != '\0') {
617         /* stat the file for its size */
618         if (stat(filename, &finfo) == -1) {
619             dbprintf(_("stat %s: %s\n"), filename, strerror(errno));
620             size = -1;
621             break;
622         }
623         size += (finfo.st_size+(off_t)1023)/(off_t)1024;
624         if (strip_headers)
625             size -= (off_t)(DISK_BLOCK_BYTES / 1024);
626
627         /* get the header to look for cont_filename */
628         if (!holding_file_get_dumpfile(filename, &file)) {
629             dbprintf(_("holding_file_size: open of %s failed.\n"), filename);
630             size = -1;
631             break;
632         }
633
634         /* on to the next chunk */
635         filename = newstralloc(filename, file.cont_filename);
636         dumpfile_free_data(&file);
637     }
638     amfree(filename);
639     return size;
640 }
641
642
643 int
644 holding_file_unlink(
645     char *hfile)
646 {
647     GSList *chunklist;
648     GSList *chunk;
649
650     chunklist = holding_get_file_chunks(hfile);
651     if (!chunklist)
652         return 0;
653
654     for (chunk = chunklist; chunk != NULL; chunk = chunk->next) {
655         if (unlink((char *)chunk->data)<0) {
656             dbprintf(_("holding_file_unlink: could not unlink %s: %s\n"),
657                     (char *)chunk->data, strerror(errno));
658             return 0;
659         }
660     }
661     return 1;
662 }
663
664 int
665 holding_file_get_dumpfile(
666     char *      fname,
667     dumpfile_t *file)
668 {
669     char buffer[DISK_BLOCK_BYTES];
670     int fd;
671
672     memset(buffer, 0, sizeof(buffer));
673
674     fh_init(file);
675     file->type = F_UNKNOWN;
676     if((fd = robust_open(fname, O_RDONLY, 0)) == -1)
677         return 0;
678
679     if(full_read(fd, buffer, SIZEOF(buffer)) != sizeof(buffer)) {
680         aclose(fd);
681         return 0;
682     }
683     aclose(fd);
684
685     parse_file_header(buffer, file, SIZEOF(buffer));
686     return 1;
687 }
688
689 /*
690  * Cleanup
691  */
692
693 typedef struct {
694     corrupt_dle_fn corrupt_dle;
695     FILE *verbose_output;
696 } holding_cleanup_datap_t;
697
698 static int
699 holding_cleanup_disk(
700     gpointer datap,
701     G_GNUC_UNUSED char *base,
702     G_GNUC_UNUSED char *element,
703     char *fqpath,
704     int is_cruft)
705 {
706     holding_cleanup_datap_t *data = (holding_cleanup_datap_t *)datap;
707
708     if (data->verbose_output) {
709         if (is_cruft)
710             g_fprintf(data->verbose_output, 
711                 _("Invalid holding disk '%s'\n"), fqpath);
712         else
713             g_fprintf(data->verbose_output, 
714                 _("Cleaning up holding disk '%s'\n"), fqpath);
715     }
716
717     return 1;
718 }
719
720 static int
721 holding_cleanup_dir(
722     gpointer datap,
723     G_GNUC_UNUSED char *base,
724     char *element,
725     char *fqpath,
726     int is_cruft)
727 {
728     holding_cleanup_datap_t *data = (holding_cleanup_datap_t *)datap;
729
730     if (is_cruft) {
731         if (data->verbose_output)
732             g_fprintf(data->verbose_output, 
733                 _("Invalid holding directory '%s'\n"), fqpath);
734         return 0;
735     }
736
737     /* try removing it */
738     if (rmdir(fqpath) == 0) {
739         /* success, so don't try to walk into it */
740         if (data->verbose_output)
741             g_fprintf(data->verbose_output,
742                 _(" ..removed empty directory '%s'\n"), element);
743         return 0;
744     }
745
746     if (data->verbose_output)
747         g_fprintf(data->verbose_output, 
748             _(" ..cleaning up holding directory '%s'\n"), element);
749
750     return 1;
751 }
752
753 static int
754 holding_cleanup_file(
755     gpointer datap,
756     G_GNUC_UNUSED char *base,
757     char *element,
758     char *fqpath,
759     int is_cruft)
760 {
761     holding_cleanup_datap_t *data = (holding_cleanup_datap_t *)datap;
762     int stat;
763     int l;
764     dumpfile_t file;
765     disk_t *dp;
766
767     if (is_cruft) {
768         if (data->verbose_output)
769             g_fprintf(data->verbose_output, 
770                 _("Invalid holding file '%s'\n"), element);
771         return 0;
772     }
773
774
775     stat = holding_file_get_dumpfile(fqpath, &file);
776
777     if (!stat) {
778         if (data->verbose_output)
779             g_fprintf(data->verbose_output, 
780                 _("Could not read read header from '%s'\n"), element);
781         dumpfile_free_data(&file);
782         return 0;
783     }
784
785     if (file.type != F_DUMPFILE && file.type != F_CONT_DUMPFILE) {
786         if (data->verbose_output)
787             g_fprintf(data->verbose_output, 
788                 _("File '%s' is not a dump file\n"), element);
789         dumpfile_free_data(&file);
790         return 0;
791     }
792
793     if(file.dumplevel < 0 || file.dumplevel > 9) {
794         if (data->verbose_output)
795             g_fprintf(data->verbose_output, 
796                 _("File '%s' has invalid level %d\n"), element, file.dumplevel);
797         dumpfile_free_data(&file);
798         return 0;
799     }
800
801     dp = lookup_disk(file.name, file.disk);
802
803     if (dp == NULL) {
804         if (data->verbose_output)
805             g_fprintf(data->verbose_output, 
806                 _("File '%s' is for '%s:%s', which is not in the disklist\n"), 
807                     element, file.name, file.disk);
808         dumpfile_free_data(&file);
809         return 0;
810     }
811
812     if ((l = strlen(element)) >= 7 && strncmp(&fqpath[l-4],".tmp",4) == 0) {
813         char *destname;
814
815         /* generate a name without '.tmp' */
816         destname = stralloc(fqpath);
817         destname[strlen(destname) - 4] = '\0';
818
819         /* OK, it passes muster -- rename it to salvage some data,
820          * and mark the DLE as corrupted */
821         if (data->verbose_output)
822             g_fprintf(data->verbose_output, 
823                 _("Processing partial holding file '%s'\n"), element);
824
825         if(rename_tmp_holding(destname, 0)) {
826             if (data->corrupt_dle)
827                 data->corrupt_dle(dp->host->hostname, dp->name);
828         } else {
829             dbprintf(_("rename_tmp_holding(%s) failed\n"), destname);
830             if (data->verbose_output)
831                 g_fprintf(data->verbose_output, 
832                     _("Rename of '%s' to '%s' failed.\n"), element, destname);
833         }
834
835         amfree(destname);
836     }
837
838     dumpfile_free_data(&file);
839     return 1;
840 }
841
842 void
843 holding_cleanup(
844     corrupt_dle_fn corrupt_dle,
845     FILE *verbose_output)
846 {
847     holding_cleanup_datap_t data;
848     data.corrupt_dle = corrupt_dle;
849     data.verbose_output = verbose_output;
850
851     holding_walk((gpointer)&data,
852         STOP_AT_FILE,
853         holding_cleanup_disk,
854         holding_cleanup_dir,
855         holding_cleanup_file,
856         NULL);
857 }
858
859 /*
860  * Application support
861  */
862
863 void
864 holding_set_origsize(
865     char  *holding_file,
866     off_t  orig_size)
867 {
868     int         fd;
869     size_t      buflen;
870     char        buffer[DISK_BLOCK_BYTES];
871     char       *read_buffer;
872     dumpfile_t  file;
873
874     if((fd = robust_open(holding_file, O_RDWR, 0)) == -1) {
875         dbprintf(_("holding_set_origsize: open of %s failed: %s\n"),
876                  holding_file, strerror(errno));
877         return;
878     }
879
880     buflen = full_read(fd, buffer, SIZEOF(buffer));
881     if (buflen <= 0) {
882         dbprintf(_("holding_set_origsize: %s: empty file?\n"), holding_file);
883         return;
884     }
885     parse_file_header(buffer, &file, (size_t)buflen);
886     lseek(fd, (off_t)0, SEEK_SET);
887     file.orig_size = orig_size;
888     read_buffer = build_header(&file, NULL, DISK_BLOCK_BYTES);
889     full_write(fd, read_buffer, DISK_BLOCK_BYTES);
890     dumpfile_free_data(&file);
891     amfree(read_buffer);
892     close(fd);
893 }
894
895 int
896 rename_tmp_holding(
897     char *      holding_file,
898     int         complete)
899 {
900     int fd;
901     size_t buflen;
902     char buffer[DISK_BLOCK_BYTES];
903     dumpfile_t file;
904     char *filename;
905     char *filename_tmp = NULL;
906
907     memset(buffer, 0, sizeof(buffer));
908     filename = stralloc(holding_file);
909     while(filename != NULL && filename[0] != '\0') {
910         filename_tmp = newvstralloc(filename_tmp, filename, ".tmp", NULL);
911         if((fd = robust_open(filename_tmp,O_RDONLY, 0)) == -1) {
912             dbprintf(_("rename_tmp_holding: open of %s failed: %s\n"),filename_tmp,strerror(errno));
913             amfree(filename);
914             amfree(filename_tmp);
915             return 0;
916         }
917         buflen = full_read(fd, buffer, SIZEOF(buffer));
918         close(fd);
919
920         if(rename(filename_tmp, filename) != 0) {
921             dbprintf(_("rename_tmp_holding: could not rename \"%s\" to \"%s\": %s"),
922                     filename_tmp, filename, strerror(errno));
923         }
924
925         if (buflen <= 0) {
926             dbprintf(_("rename_tmp_holding: %s: empty file?\n"), filename);
927             amfree(filename);
928             amfree(filename_tmp);
929             return 0;
930         }
931         parse_file_header(buffer, &file, (size_t)buflen);
932         if(complete == 0 ) {
933             char * header;
934             if((fd = robust_open(filename, O_RDWR, 0)) == -1) {
935                 dbprintf(_("rename_tmp_holdingX: open of %s failed: %s\n"),
936                         filename, strerror(errno));
937                 dumpfile_free_data(&file);
938                 amfree(filename);
939                 amfree(filename_tmp);
940                 return 0;
941
942             }
943             file.is_partial = 1;
944             if (debug_holding > 1)
945                 dump_dumpfile_t(&file);
946             header = build_header(&file, NULL, DISK_BLOCK_BYTES);
947             if (!header) /* this shouldn't happen */
948                 error(_("header does not fit in %zd bytes"), (size_t)DISK_BLOCK_BYTES);
949             if (full_write(fd, header, DISK_BLOCK_BYTES) != DISK_BLOCK_BYTES) {
950                 dbprintf(_("rename_tmp_holding: writing new header failed: %s"),
951                         strerror(errno));
952                 dumpfile_free_data(&file);
953                 amfree(filename);
954                 amfree(filename_tmp);
955                 close(fd);
956                 return 0;
957             }
958             close(fd);
959         }
960         filename = newstralloc(filename, file.cont_filename);
961         dumpfile_free_data(&file);
962     }
963     amfree(filename);
964     amfree(filename_tmp);
965     return 1;
966 }
967
968
969 int
970 mkholdingdir(
971     char *      diskdir)
972 {
973     struct stat stat_hdp;
974     int success = 1;
975
976     if (mkpdir(diskdir, 0770, (uid_t)-1, (gid_t)-1) != 0 && errno != EEXIST) {
977         log_add(L_WARNING, _("WARNING: could not create parents of %s: %s"),
978                 diskdir, strerror(errno));
979         success = 0;
980     }
981     else if (mkdir(diskdir, 0770) != 0 && errno != EEXIST) {
982         log_add(L_WARNING, _("WARNING: could not create %s: %s"),
983                 diskdir, strerror(errno));
984         success = 0;
985     }
986     else if (stat(diskdir, &stat_hdp) == -1) {
987         log_add(L_WARNING, _("WARNING: could not stat %s: %s"),
988                 diskdir, strerror(errno));
989         success = 0;
990     }
991     else {
992         if (!S_ISDIR((stat_hdp.st_mode))) {
993             log_add(L_WARNING, _("WARNING: %s is not a directory"),
994                     diskdir);
995             success = 0;
996         }
997         else if (access(diskdir,W_OK) != 0) {
998             log_add(L_WARNING, _("WARNING: directory %s is not writable"),
999                     diskdir);
1000             success = 0;
1001         }
1002     }
1003     return success;
1004 }