1 Index: src/dird/fd_cmds.c
2 ===================================================================
3 --- src/dird/fd_cmds.c (revision 6450)
4 +++ src/dird/fd_cmds.c (working copy)
6 static char filesetcmd[] = "fileset%s\n"; /* set full fileset */
7 static char jobcmd[] = "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s\n";
8 /* Note, mtime_only is not used here -- implemented as file option */
9 -static char levelcmd[] = "level = %s%s mtime_only=%d\n";
10 +static char levelcmd[] = "level = %s%s%s mtime_only=%d\n";
11 static char runscript[] = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n";
12 static char runbeforenow[]= "RunBeforeNow\n";
17 stime = str_to_utime(jcr->stime);
18 - fd->fsend(levelcmd, NT_("since_utime "), edit_uint64(stime, ed1), 0);
19 + fd->fsend(levelcmd, "", NT_("since_utime "), edit_uint64(stime, ed1), 0);
20 while (bget_dirmsg(fd) >= 0) { /* allow him to poll us to sync clocks */
21 Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
24 bool send_level_command(JCR *jcr)
26 BSOCK *fd = jcr->file_bsock;
27 + const char *accurate=jcr->job->accurate?"accurate_":"";
29 * Send Level command to File daemon
31 switch (jcr->JobLevel) {
33 - fd->fsend(levelcmd, "base", " ", 0);
34 + fd->fsend(levelcmd, "", "base", " ", 0);
36 /* L_NONE is the console, sending something off to the FD */
39 - fd->fsend(levelcmd, "full", " ", 0);
40 + fd->fsend(levelcmd, "", "full", " ", 0);
43 - fd->fsend(levelcmd, "differential", " ", 0);
44 + fd->fsend(levelcmd, accurate, "differential", " ", 0);
48 - fd->fsend(levelcmd, "incremental", " ", 0);
49 + fd->fsend(levelcmd, accurate, "incremental", " ", 0);
53 Index: src/dird/backup.c
54 ===================================================================
55 --- src/dird/backup.c (revision 6450)
56 +++ src/dird/backup.c (working copy)
61 +#include "findlib/find.h"
63 /* Commands sent to File daemon */
64 static char backupcmd[] = "backup\n";
69 +static int get_int_handler(void *ctx, int num_fields, char **row)
71 + POOLMEM *ret = (POOLMEM *)ctx;
72 + if (num_fields == 1) {
74 + pm_strcat(ret, ",");
76 + pm_strcat(ret, row[0]);
81 +static int accurate_list_handler(void *ctx, int num_fields, char **row)
83 + JCR *jcr = (JCR *)ctx;
85 + if (job_canceled(jcr)) {
89 + if (row[0] > 0) { /* discard when file_index == 0 */
90 + jcr->file_bsock->fsend("%s%s%c%s", row[1], row[2], 0, row[3]);
96 + * Differential : get the last full id
97 + * Incremental : get the last full + last diff + last incr(s) ids
99 + * TODO: look and merge from ua_restore.c
101 +bool db_accurate_get_jobids(JCR *jcr, POOLMEM *jobids)
103 + char clientid[50], jobid[50], filesetid[50];
104 + char date[MAX_TIME_LENGTH];
106 + JOB_DBR *jr = &jcr->jr;
107 + POOLMEM *query = get_pool_memory(PM_FNAME);
108 + bstrutime(date, sizeof(date), time(NULL) + 1);
111 +"CREATE TEMPORARY TABLE btemp3%s AS ( "
112 + "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
113 + "FROM Job JOIN FileSet USING (FileSetId) "
114 + "WHERE ClientId = %s "
115 + "AND Level='F' AND JobStatus='T' AND Type='B' "
116 + "AND StartTime<'%s' "
117 + "AND FileSet.FileSet=(SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
118 + "ORDER BY Job.JobTDate DESC LIMIT 1) ",
119 + edit_uint64(jcr->JobId, jobid),
120 + edit_uint64(jr->ClientId, clientid),
122 + edit_uint64(jr->FileSetId, filesetid));
123 + db_sql_query(jcr->db, query, NULL, NULL);
125 + if (jcr->JobLevel == L_INCREMENTAL) {
128 +"INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
129 + "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
130 + "FROM Job JOIN FileSet USING (FileSetId) "
131 + "WHERE ClientId = %s "
132 + "AND Level='D' AND JobStatus='T' AND Type='B' "
133 + "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
134 + "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
135 + "ORDER BY Job.JobTDate DESC LIMIT 1 ",
140 + db_sql_query(jcr->db, query, NULL, NULL);
143 +"INSERT INTO btemp3%s (JobId, StartTime, EndTime, JobTDate, PurgedFiles) "
144 + "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
145 + "FROM Job JOIN FileSet USING (FileSetId) "
146 + "WHERE ClientId = %s "
147 + "AND Level='I' AND JobStatus='T' AND Type='B' "
148 + "AND StartTime > (SELECT EndTime FROM btemp3%s ORDER BY EndTime DESC LIMIT 1) "
149 + "AND FileSet.FileSet= (SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
150 + "ORDER BY Job.JobTDate DESC ",
155 + db_sql_query(jcr->db, query, NULL, NULL);
159 + Mmsg(query, "SELECT JobId FROM btemp3%s", jobid);
160 + db_sql_query(jcr->db, query, get_int_handler, jobids);
161 + Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids);
163 + Mmsg(query, "DROP TABLE btemp3%s", jobid);
164 + db_sql_query(jcr->db, query, NULL, NULL);
165 + free_pool_memory(query);
170 +bool send_accurate_current_files(JCR *jcr)
172 + char buf[MAXSTRING];
175 + if (jcr->accurate == false || job_canceled(jcr) || jcr->JobLevel == L_FULL) {
178 + POOLMEM *jobids = get_pool_memory(PM_FNAME);
179 + db_accurate_get_jobids(jcr, jobids);
181 + if (*jobids == 0) {
182 + free_pool_memory(jobids);
183 + Jmsg(jcr, M_ERROR_TERM, 0, _("Cannot find previous jobids.\n"));
187 + bsnprintf(buf, sizeof(buf),
188 + "CREATE TEMPORARY TABLE btemp2%s AS ( "
189 + "SELECT max(FileId) as FileId, PathId, FilenameId "
190 + "FROM (SELECT FileId, PathId, FilenameId "
191 + "FROM File WHERE JobId IN (%s)) AS F "
192 + "GROUP BY PathId, FilenameId ) ",
193 + edit_uint64(jcr->JobId, ed1),
195 + db_sql_query(jcr->db, buf, NULL, NULL);
197 + /* to be able to allocate the right size for htable */
198 + POOLMEM *nb = get_pool_memory(PM_FNAME);
199 + bsnprintf(buf, sizeof(buf), "SELECT count(1) FROM btemp2%s",ed1);
200 + db_sql_query(jcr->db, buf, get_int_handler, nb);
201 + jcr->file_bsock->fsend("accurate files=%s\n", nb);
203 + bsnprintf(buf, sizeof(buf),
204 + "SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat "
205 + "FROM btemp2%s JOIN Path USING (PathId) "
206 + "JOIN Filename USING (FilenameId) "
207 + "JOIN File USING (FileId) "
208 + "WHERE File.FileIndex > 0",
210 + db_sql_query(jcr->db, buf, accurate_list_handler, (void *)jcr);
212 + bsnprintf(buf, sizeof(buf), "DROP TABLE btemp2%s", ed1);
213 + free_pool_memory(jobids);
214 + free_pool_memory(nb);
217 + CREATE TEMPORARY TABLE btemp2 AS (
218 + SELECT max(FileId) as FileId, PathId, FilenameId
219 + FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (39867,40341)) AS F
220 + GROUP BY PathId, FilenameId )
222 + SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat
223 + FROM btemp2 JOIN Path USING (PathId) JOIN Filename USING (FilenameId)
224 + JOIN File USING (FileId)
225 + WHERE File.FileIndex > 0
230 +SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat
231 + FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (40341)
232 + ORDER BY PathId, FilenameId, JobId DESC
235 + jcr->file_bsock->signal(BNET_EOD);
240 * Do a backup of the specified FileSet
242 * Returns: false on failure
244 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
248 + * If backup is in accurate mode, FD will send the list of
251 + if (!send_accurate_current_files(jcr)) {
255 /* Send backup command */
256 fd->fsend(backupcmd);
257 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
259 /* Pickup Job termination data */
260 stat = wait_for_job_termination(jcr);
261 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
263 if (stat == JS_Terminated) {
264 backup_cleanup(jcr, stat);
266 Index: src/dird/inc_conf.c
267 ===================================================================
268 --- src/dird/inc_conf.c (revision 6450)
269 +++ src/dird/inc_conf.c (working copy)
271 * Items that are valid in an Options resource
273 static RES_ITEM options_items[] = {
274 + {"accurate", store_opts, {0}, 0, 0, 0},
275 {"compression", store_opts, {0}, 0, 0, 0},
276 {"signature", store_opts, {0}, 0, 0, 0},
277 {"verify", store_opts, {0}, 0, 0, 0},
289 * options given above.
291 static struct s_kw FS_option_kw[] = {
292 + {"accurate", INC_KW_ACCURATE},
293 {"compression", INC_KW_COMPRESSION},
294 {"signature", INC_KW_DIGEST},
295 {"encryption", INC_KW_ENCRYPTION},
297 {"no", INC_KW_ENHANCEDWILD, "0"},
298 {"yes", INC_KW_CHKCHANGES, "c"},
299 {"no", INC_KW_CHKCHANGES, "0"},
300 + {"yes", INC_KW_ACCURATE, "C"},
301 + {"no", INC_KW_ACCURATE, "0"},
305 Index: src/filed/backup.c
306 ===================================================================
307 --- src/filed/backup.c (revision 6450)
308 +++ src/filed/backup.c (working copy)
313 +#include "lib/htable.h"
315 /* Forward referenced functions */
316 int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level);
318 static bool crypto_session_start(JCR *jcr);
319 static void crypto_session_end(JCR *jcr);
320 static bool crypto_session_send(JCR *jcr, BSOCK *sd);
321 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname);
323 +typedef struct CurFile {
330 + * This function is called for each file seen in fileset.
332 + * If the file is skipped (saved=false), we will check if this
333 + * file have been backuped before. If not, we decide to backup it.
335 + * If the file have saved=true, we mark it as seen
338 +/* TODO: tweak verify code to use the same function */
339 +bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt, bool saved)
343 + struct stat statc; /* catalog stat */
344 + char *Opts_Digest = ff_pkt->VerifyOpts;
345 + char *fname = ff_pkt->fname;
350 + if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
354 + if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_DIRNOCHG) {
355 + fname = ff_pkt->link;
358 + elt = (CurFile *) jcr->file_list->lookup(fname);
361 + // TODO: we must backup it !
362 + Dmsg1(1, "accurate %s = yes (not found)\n", fname);
366 + if (saved || *elt->lstat == '\0') {
367 + Dmsg1(1, "accurate %s = no (already seen)\n", fname);
368 + *elt->lstat = '\0';
372 + decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */
373 +// *do_Digest = CRYPTO_DIGEST_NONE;
375 + for (p=Opts_Digest; *p; p++) {
376 + char ed1[30], ed2[30];
378 + case 'i': /* compare INODEs */
379 + if (statc.st_ino != ff_pkt->statp.st_ino) {
380 + Jmsg(jcr, M_INFO, 0, _(" st_ino differ. Cat: %s File: %s\n"),
381 + edit_uint64((uint64_t)statc.st_ino, ed1),
382 + edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2));
386 + case 'p': /* permissions bits */
387 + if (statc.st_mode != ff_pkt->statp.st_mode) {
388 + Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"),
389 + (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode);
393 + case 'n': /* number of links */
394 + if (statc.st_nlink != ff_pkt->statp.st_nlink) {
395 + Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"),
396 + (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink);
400 + case 'u': /* user id */
401 + if (statc.st_uid != ff_pkt->statp.st_uid) {
402 + Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"),
403 + (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid);
407 + case 'g': /* group id */
408 + if (statc.st_gid != ff_pkt->statp.st_gid) {
409 + Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"),
410 + (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid);
414 + case 's': /* size */
415 + if (statc.st_size != ff_pkt->statp.st_size) {
416 + Jmsg(jcr, M_INFO, 0, _(" st_size differ. Cat: %s File: %s\n"),
417 + edit_uint64((uint64_t)statc.st_size, ed1),
418 + edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
422 + case 'a': /* access time */
423 + if (statc.st_atime != ff_pkt->statp.st_atime) {
424 + Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n"));
429 + if (statc.st_mtime != ff_pkt->statp.st_mtime) {
430 + Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n"));
434 + case 'c': /* ctime */
435 + if (statc.st_ctime != ff_pkt->statp.st_ctime) {
436 + Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n"));
440 + case 'd': /* file size decrease */
441 + if (statc.st_size > ff_pkt->statp.st_size) {
442 + Jmsg(jcr, M_INFO, 0, _(" st_size decrease. Cat: %s File: %s\n"),
443 + edit_uint64((uint64_t)statc.st_size, ed1),
444 + edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
448 + case '5': /* compare MD5 */
449 + Dmsg1(500, "set Do_MD5 for %s\n", ff_pkt->fname);
450 +// *do_Digest = CRYPTO_DIGEST_MD5;
452 + case '1': /* compare SHA1 */
453 +// *do_Digest = CRYPTO_DIGEST_SHA1;
461 + *elt->lstat = '\0'; /* mark it as seen */
462 + Dmsg2(1, "accurate %s = %i\n", fname, stat);
467 + * This function doesn't work very well with smartalloc
469 +int accurate_get_current_file_list_cmd(JCR *jcr)
471 + BSOCK *dir = jcr->dir_bsock;
476 + if (jcr->accurate == false || job_canceled(jcr) || jcr->JobLevel == L_FULL) {
480 + if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
481 + dir->fsend(_("2991 Bad accurate command\n"));
485 + jcr->file_list = (htable *)malloc(sizeof(htable));
486 + jcr->file_list->init(elt, &elt->link, nb);
488 + /* get current files */
489 + while (dir->recv() >= 0) {
490 + len = strlen(dir->msg);
491 + if ((len+1) < dir->msglen) {
492 +// elt = (CurFile *)malloc(sizeof(CurFile));
493 +// elt->fname = (char *) malloc(dir->msglen+1);
495 + /* we store CurFile, fname and lstat in the same chunk */
496 + elt = (CurFile *)malloc(sizeof(CurFile)+dir->msglen+1);
497 + elt->fname = (char *) elt+sizeof(CurFile);
498 + memcpy(elt->fname, dir->msg, dir->msglen);
499 + elt->fname[dir->msglen]='\0';
500 + elt->lstat = elt->fname + len + 1;
501 + jcr->file_list->insert(elt->fname, elt);
502 + Dmsg2(1, "add fname=%s lstat=%s\n", elt->fname, elt->lstat);
506 +// jcr->file_list->stats();
507 + /* TODO: send a EOM ?
508 + dir->fsend("2000 OK accurate\n");
513 +bool accurate_send_deleted_list(JCR *jcr)
515 + if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
519 + if (jcr->file_list == NULL) { /* TODO: bug ? */
524 + foreach_htable (elt, jcr->file_list) {
525 + if (*elt->lstat != '\0') {
526 + Dmsg2(1, "deleted fname=%s lstat=%s\n", elt->fname, elt->lstat);
527 + encode_and_send_deleted_file(jcr, elt->fname);
529 +// free(elt->fname);
531 + jcr->file_list->destroy(); /* TODO: clean htable when this function is not reached ? */
532 + free(jcr->file_list);
533 + jcr->file_list = NULL;
538 * Find all the requested files and send them
539 * to the Storage daemon.
544 // TODO landonf: Allow user to specify encryption algorithm
546 sd = jcr->store_bsock;
548 set_jcr_job_status(jcr, JS_Running);
551 jcr->compress_buf_size = jcr->buf_size + ((jcr->buf_size+999) / 1000) + 30;
552 jcr->compress_buf = get_memory(jcr->compress_buf_size);
556 z_stream *pZlibStream = (z_stream*)malloc(sizeof(z_stream));
559 ok = false; /* error */
560 set_jcr_job_status(jcr, JS_ErrorTerminated);
562 + Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate);
564 + accurate_send_deleted_list(jcr); /* send deleted list to SD */
566 free_pool_memory(jcr->acl_text);
568 stop_heartbeat_monitor(jcr);
573 + /* TODO: in accurate mode, we have to change NOCHG attribute to FT_REG... */
574 +// if (!accurate_check_file(jcr, ff_pkt, false)) {
575 +// Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
578 + accurate_check_file(jcr, ff_pkt, false);
579 Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
582 + /* TODO: in accurate mode, we have to change NOCHG attribute to FT_REG... */
583 +// if (!accurate_check_file(jcr, ff_pkt, false)) {
584 +// Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname);
587 + accurate_check_file(jcr, ff_pkt, false);
588 Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname);
591 @@ -1118,6 +1348,9 @@
593 unstrip_path(ff_pkt);
595 + /* list backuped files */
596 + accurate_check_file(jcr, ff_pkt, true);
598 Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
600 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
601 @@ -1128,6 +1361,58 @@
605 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname)
607 + BSOCK *sd = jcr->store_bsock;
611 +#ifdef FD_NO_SEND_TEST
619 + * Send Attributes header to Storage daemon
620 + * <file-index> <stream> <info>
622 + if (!sd->fsend("%ld %d 0", 0, STREAM_UNIX_ATTRIBUTES)) {
623 + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
627 + Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
630 + * Send file attributes to Storage daemon
633 + * Filename (full path)
634 + * Encoded attributes
635 + * Link name (if type==FT_LNK or FT_LNKSAVED)
636 + * Encoded extended-attributes (for Win32)
638 + * For a directory, link is the same as fname, but with trailing
639 + * slash. For a linked file, link is the link.
641 + stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c",
643 + FT_NOSTAT /* FileType */,
644 + fname /* FileName */,
645 + 0, attribs, 0, 0, 0, attribsEx, 0);
647 + Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
649 + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
653 + sd->signal(BNET_EOD); /* indicate end of attributes data */
658 * Do in place strip of path
660 Index: src/filed/job.c
661 ===================================================================
662 --- src/filed/job.c (revision 6450)
663 +++ src/filed/job.c (working copy)
665 /* Imported functions */
666 extern int status_cmd(JCR *jcr);
667 extern int qstatus_cmd(JCR *jcr);
668 +extern int accurate_get_current_file_list_cmd(JCR *jcr);
670 /* Forward referenced functions */
671 static int backup_cmd(JCR *jcr);
673 {"RunBeforeJob", runbefore_cmd, 0},
674 {"RunAfterJob", runafter_cmd, 0},
675 {"Run", runscript_cmd, 0},
676 + {"accurate", accurate_get_current_file_list_cmd, 0},
677 {NULL, NULL} /* list terminator */
680 @@ -1087,6 +1089,9 @@
682 fo->flags |= FO_CHKCHANGES;
685 + fo->flags |= FO_ACCURATE;
688 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
690 @@ -1195,6 +1200,9 @@
692 level = get_memory(dir->msglen+1);
693 Dmsg1(110, "level_cmd: %s", dir->msg);
694 + if (strstr(dir->msg, "accurate")) {
695 + jcr->accurate = true;
697 if (sscanf(dir->msg, "level = %s ", level) != 1) {
700 @@ -1204,14 +1212,14 @@
701 /* Full backup requested? */
702 } else if (strcmp(level, "full") == 0) {
703 jcr->JobLevel = L_FULL;
704 - } else if (strcmp(level, "differential") == 0) {
705 + } else if (strstr(level, "differential")) {
706 jcr->JobLevel = L_DIFFERENTIAL;
709 - } else if (strcmp(level, "incremental") == 0) {
710 + } else if (strstr(level, "incremental")) {
711 jcr->JobLevel = L_INCREMENTAL;
716 * We get his UTC since time, then sync the clocks and correct it
717 * to agree with our clock.
718 Index: src/filed/restore.c
719 ===================================================================
720 --- src/filed/restore.c (revision 6450)
721 +++ src/filed/restore.c (working copy)
726 + /* TODO: manage deleted files */
727 + if (file_index == 0) { /* deleted file */
732 * Unpack attributes and do sanity check them
734 Index: src/stored/bextract.c
735 ===================================================================
736 --- src/stored/bextract.c (revision 6450)
737 +++ src/stored/bextract.c (working copy)
739 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
742 + /* handle deleted file
744 + if (rec->FileIndex == 0) {
745 + /* if file is included, remove it ? */
746 + Jmsg(jcr, M_INFO, 0, _("fname=%s is marked as deleted.\n"), attr->fname);
750 if (attr->file_index != rec->FileIndex) {
751 Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
752 rec->FileIndex, attr->file_index);
753 Index: src/stored/bscan.c
754 ===================================================================
755 --- src/stored/bscan.c (revision 6450)
756 +++ src/stored/bscan.c (working copy)
758 case STREAM_UNIX_ATTRIBUTES:
759 case STREAM_UNIX_ATTRIBUTES_EX:
761 + /* handle deleted file
763 + if (rec->FileIndex == 0) {
764 + create_file_attributes_record(db, mjcr, attr->fname, attr->lname,
765 + FT_NOSTAT, "", rec);
770 if (!unpack_attributes_record(bjcr, rec->Stream, rec->data, attr)) {
771 Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
773 Index: src/stored/append.c
774 ===================================================================
775 --- src/stored/append.c (revision 6450)
776 +++ src/stored/append.c (working copy)
779 /* Read Stream header from the File daemon.
780 * The stream header consists of the following:
781 - * file_index (sequential Bacula file index, base 1)
782 + * file_index (sequential Bacula file index, base 1, 0 for deleted files)
783 * stream (Bacula number to distinguish parts of data)
784 * info (Info for Storage daemon -- compressed, encryped, ...)
785 * info is not currently used, so is read, but ignored!
786 @@ -185,16 +185,18 @@
788 Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);
790 - if (!(file_index > 0 && (file_index == last_file_index ||
791 - file_index == last_file_index + 1))) {
792 - Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
795 + if (file_index != 0) { /* TODO: handle file_index == 0 */
796 + if (!(file_index > 0 && (file_index == last_file_index ||
797 + file_index == last_file_index + 1))) {
798 + Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
802 + if (file_index != last_file_index) {
803 + jcr->JobFiles = file_index;
804 + last_file_index = file_index;
807 - if (file_index != last_file_index) {
808 - jcr->JobFiles = file_index;
809 - last_file_index = file_index;
812 /* Read data stream from the File daemon.
813 * The data stream is just raw bytes
814 @@ -212,25 +214,26 @@
815 stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
818 - while (!write_record_to_block(dcr->block, &rec)) {
819 - Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
821 - if (!write_block_to_device(dcr)) {
822 - Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
823 - dev->print_name(), dev->bstrerror());
829 - Dmsg0(400, "Not OK\n");
832 - jcr->JobBytes += rec.data_len; /* increment bytes this job */
833 - Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
834 - FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
835 - stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
836 + while (!write_record_to_block(dcr->block, &rec)) {
837 + Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
839 + if (!write_block_to_device(dcr)) {
840 + Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
841 + dev->print_name(), dev->bstrerror());
847 + Dmsg0(400, "Not OK\n");
850 + jcr->JobBytes += rec.data_len; /* increment bytes this job */
851 + Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
852 + FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
853 + stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
856 /* Send attributes and digest to Director for Catalog */
857 if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX ||
858 crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
860 ===================================================================
861 --- src/jcr.h (revision 6450)
862 +++ src/jcr.h (working copy)
865 /* Forward referenced structures */
872 CRYPTO_CTX crypto; /* Crypto ctx */
873 DIRRES* director; /* Director resource */
874 bool VSS; /* VSS used by FD */
875 + htable *file_list; /* Previous file list (accurate mode) */
876 #endif /* FILE_DAEMON */
879 Index: src/lib/Makefile.in
880 ===================================================================
881 --- src/lib/Makefile.in (revision 6450)
882 +++ src/lib/Makefile.in (working copy)
884 res.c rwlock.c scan.c serial.c sha1.c \
885 signal.c smartall.c rblist.c tls.c tree.c \
886 util.c var.c watchdog.c workq.c btimers.c \
887 - address_conf.c pythonlib.c breg.c
888 + address_conf.c pythonlib.c breg.c htable.c
891 LIBOBJS = attr.o base64.o berrno.o bsys.o bget_msg.o \
893 res.o rwlock.o scan.o serial.o sha1.o \
894 signal.o smartall.o rblist.o tls.o tree.o \
895 util.o var.o watchdog.o workq.o btimers.o \
896 - address_conf.o pythonlib.o breg.o
897 + address_conf.o pythonlib.o breg.o htable.o
900 EXTRAOBJS = @OBJLIST@
901 Index: src/lib/htable.c
902 ===================================================================
903 --- src/lib/htable.c (revision 6450)
904 +++ src/lib/htable.c (working copy)
907 return false; /* already exists */
909 - sm_check(__FILE__, __LINE__, false);
910 ASSERT(index < buckets);
911 Dmsg2(100, "Insert: hash=0x%x index=%d\n", (unsigned)hash, index);
912 hp = (hlink *)(((char *)item)+loffset);
914 Dmsg2(100, "num_items=%d max_items=%d\n", num_items, max_items);
917 - sm_check(__FILE__, __LINE__, false);
918 Dmsg3(100, "Leave insert index=%d num_items=%d key=%s\n", index, num_items, key);
921 @@ -275,12 +273,13 @@
940 Dmsg0(100, "Done destroy.\n");
945 -#define NITEMS 10000
946 +#define NITEMS 1000000
951 Dmsg1(000, "Inserting %d items\n", NITEMS);
952 for (int i=0; i<NITEMS; i++) {
953 sprintf(mkey, "This is htable item %d", i);
954 - jcr = (MYJCR *)malloc(sizeof(MYJCR));
955 + jcr = (MYJCR *)malloc(sizeof(MYJCR)+strlen(mkey)+1);
956 Dmsg2(100, "link=0x%x jcr=0x%x\n", (unsigned)&jcr->link, (unsigned)jcr);
957 jcr->key = bstrdup(mkey);
959 Index: src/findlib/find.h
960 ===================================================================
961 --- src/findlib/find.h (revision 6450)
962 +++ src/findlib/find.h (working copy)
964 #define FO_ENHANCEDWILD (1<<23) /* Enhanced wild card processing */
965 #define FO_CHKCHANGES (1<<24) /* Check if file have been modified during backup */
966 #define FO_STRIPPATH (1<<25) /* Check for stripping path */
967 +#define FO_ACCURATE (1<<26) /* Accurate mode */
969 struct s_included_file {
970 struct s_included_file *next;