1 Index: src/dird/fd_cmds.c
2 ===================================================================
3 --- src/dird/fd_cmds.c (révision 6443)
4 +++ src/dird/fd_cmds.c (copie de travail)
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 (révision 6443)
56 +++ src/dird/backup.c (copie de travail)
61 +#include "findlib/find.h"
63 /* Commands sent to File daemon */
64 static char backupcmd[] = "backup\n";
69 +static int accurate_list_handler(void *ctx, int num_fields, char **row)
71 + JCR *jcr = (JCR *)ctx;
73 + if (job_canceled(jcr)) {
78 + jcr->file_bsock->fsend("%s%s%c%s", row[1], row[2], 0, row[3]);
83 +bool db_accurate_get_jobids(JCR *jcr, POOLMEM *jobids)
85 + pm_strcpy(jobids, "1");
89 +bool send_accurate_current_files(JCR *jcr)
91 + char buf[MAXSTRING];
92 + char ed1[50], ed2[50];
94 + POOLMEM *jobids = get_pool_memory(PM_FNAME);
95 + db_accurate_get_jobids(jcr, jobids);
97 + bsnprintf(buf, sizeof(buf),
98 + "CREATE TEMPORARY TABLE btemp2%s AS ( "
99 + "SELECT max(FileId) as FileId, PathId, FilenameId "
100 + "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F "
101 + "GROUP BY PathId, FilenameId ) ",
102 + edit_uint64(jcr->JobId, ed1),
104 + db_sql_query(jcr->db, buf, NULL, NULL);
106 + // TODO: compter le nombre de rows
107 + jcr->file_bsock->fsend("accurate files=%s", edit_uint64(5895, ed2)); /* TODO: change protocol to something like nb= */
109 + bsnprintf(buf, sizeof(buf),
110 + "SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat "
111 + "FROM btemp2%s JOIN Path USING (PathId) JOIN Filename USING (FilenameId) "
112 + "JOIN File USING (FileId) "
113 + "WHERE File.FileIndex > 0",
115 + db_sql_query(jcr->db, buf, accurate_list_handler, (void *)jcr);
117 + bsnprintf(buf, sizeof(buf), "DROP TABLE btemp2%s", ed1);
118 + free_pool_memory(jobids);
121 + CREATE TEMPORARY TABLE btemp2 AS (
122 + SELECT max(FileId) as FileId, PathId, FilenameId
123 + FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (39867,40341)) AS F
124 + GROUP BY PathId, FilenameId )
126 + SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat
127 + FROM btemp2 JOIN Path USING (PathId) JOIN Filename USING (FilenameId)
128 + JOIN File USING (FileId)
129 + WHERE File.FileIndex > 0
134 +SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat
135 + FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (40341)
136 + ORDER BY PathId, FilenameId, JobId DESC
139 + jcr->file_bsock->signal(BNET_EOD);
144 * Do a backup of the specified FileSet
146 * Returns: false on failure
148 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
152 + * If backup is in accurate mode, FD will send the list of
155 + if (!send_accurate_current_files(jcr)) {
159 /* Send backup command */
160 fd->fsend(backupcmd);
161 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
163 /* Pickup Job termination data */
164 stat = wait_for_job_termination(jcr);
165 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
167 if (stat == JS_Terminated) {
168 backup_cleanup(jcr, stat);
170 Index: src/dird/inc_conf.c
171 ===================================================================
172 --- src/dird/inc_conf.c (révision 6443)
173 +++ src/dird/inc_conf.c (copie de travail)
175 * Items that are valid in an Options resource
177 static RES_ITEM options_items[] = {
178 + {"accurate", store_opts, {0}, 0, 0, 0},
179 {"compression", store_opts, {0}, 0, 0, 0},
180 {"signature", store_opts, {0}, 0, 0, 0},
181 {"verify", store_opts, {0}, 0, 0, 0},
193 * options given above.
195 static struct s_kw FS_option_kw[] = {
196 + {"accurate", INC_KW_ACCURATE},
197 {"compression", INC_KW_COMPRESSION},
198 {"signature", INC_KW_DIGEST},
199 {"encryption", INC_KW_ENCRYPTION},
201 {"no", INC_KW_ENHANCEDWILD, "0"},
202 {"yes", INC_KW_CHKCHANGES, "c"},
203 {"no", INC_KW_CHKCHANGES, "0"},
204 + {"yes", INC_KW_ACCURATE, "C"},
205 + {"no", INC_KW_ACCURATE, "0"},
209 Index: src/filed/backup.c
210 ===================================================================
211 --- src/filed/backup.c (révision 6443)
212 +++ src/filed/backup.c (copie de travail)
214 static bool crypto_session_start(JCR *jcr);
215 static void crypto_session_end(JCR *jcr);
216 static bool crypto_session_send(JCR *jcr, BSOCK *sd);
217 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname);
221 + * We are called here for each record that matches the above
222 + * SQL query -- that is for each file contained in the Catalog
223 + * that was not marked earlier. This means that the file in
224 + * question is a missing file (in the Catalog but not on Disk).
226 +/* TODO: tweak verify code to use the same function */
227 +bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt, char *attr)
231 + struct stat statf; /* file stat */
232 + struct stat statc; /* catalog stat */
233 + char *Opts_Digest = ff_pkt->VerifyOpts;
236 + int32_t LinkFIf, LinkFIc;
238 + if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
239 + return attr != NULL; /* if null => don't backup */
242 + return attr != true;
244 + decode_stat(attr, &statf, &LinkFIf); /* decode file stat packet */
246 + // TODO: check for /path/ and /path/file
247 + lstat = (char *) jcr->file_list->lookup(ff_pkt->fname);
250 + // TODO: we must backup it !
254 + decode_stat(lstat, &statc, &LinkFIc); /* decode catalog stat */
255 +// *do_Digest = CRYPTO_DIGEST_NONE;
257 + for (p=Opts_Digest; *p; p++) {
258 + char ed1[30], ed2[30];
260 + case 'i': /* compare INODEs */
261 + if (statc.st_ino != statf.st_ino) {
262 + Jmsg(jcr, M_INFO, 0, _(" st_ino differ. Cat: %s File: %s\n"),
263 + edit_uint64((uint64_t)statc.st_ino, ed1),
264 + edit_uint64((uint64_t)statf.st_ino, ed2));
268 + case 'p': /* permissions bits */
269 + if (statc.st_mode != statf.st_mode) {
270 + Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"),
271 + (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
275 + case 'n': /* number of links */
276 + if (statc.st_nlink != statf.st_nlink) {
277 + Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"),
278 + (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
282 + case 'u': /* user id */
283 + if (statc.st_uid != statf.st_uid) {
284 + Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"),
285 + (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
289 + case 'g': /* group id */
290 + if (statc.st_gid != statf.st_gid) {
291 + Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"),
292 + (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
296 + case 's': /* size */
297 + if (statc.st_size != statf.st_size) {
298 + Jmsg(jcr, M_INFO, 0, _(" st_size differ. Cat: %s File: %s\n"),
299 + edit_uint64((uint64_t)statc.st_size, ed1),
300 + edit_uint64((uint64_t)statf.st_size, ed2));
304 + case 'a': /* access time */
305 + if (statc.st_atime != statf.st_atime) {
306 + Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n"));
311 + if (statc.st_mtime != statf.st_mtime) {
312 + Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n"));
316 + case 'c': /* ctime */
317 + if (statc.st_ctime != statf.st_ctime) {
318 + Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n"));
322 + case 'd': /* file size decrease */
323 + if (statc.st_size > statf.st_size) {
324 + Jmsg(jcr, M_INFO, 0, _(" st_size decrease. Cat: %s File: %s\n"),
325 + edit_uint64((uint64_t)statc.st_size, ed1),
326 + edit_uint64((uint64_t)statf.st_size, ed2));
330 + case '5': /* compare MD5 */
331 + Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
332 +// *do_Digest = CRYPTO_DIGEST_MD5;
334 + case '1': /* compare SHA1 */
335 +// *do_Digest = CRYPTO_DIGEST_SHA1;
343 + *lstat = '\0'; /* mark it as deleted */
347 +#include "lib/htable.c"
348 +int accurate_get_current_file_list_cmd(JCR *jcr)
350 + BSOCK *dir = jcr->dir_bsock;
355 + if (jcr->accurate == false || job_canceled(jcr)) {
359 + if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
360 + dir->fsend(_("2991 Bad accurate command\n"));
364 + jcr->file_list = (htable *)malloc(sizeof(htable));
365 + jcr->file_list->init(jcr, &jcr->link, nb);
367 + /* get current files */
368 + while (dir->recv() >= 0) {
369 + len = strlen(dir->msg);
370 + if ((len+1) < dir->msglen) {
371 + char *elt = (char *) malloc(dir->msglen+1);
372 + memcpy(elt, dir->msg, dir->msglen+1);
373 + lstat = elt + len + 1;
374 + Dmsg2(5, "hash[%s]=%s\n", elt, lstat);
375 + jcr->file_list->insert(elt, lstat);
379 + jcr->file_list->stats();
380 +// dir->fsend("2000 OK accurate\n");
386 * Find all the requested files and send them
387 * to the Storage daemon.
392 // TODO landonf: Allow user to specify encryption algorithm
394 sd = jcr->store_bsock;
396 set_jcr_job_status(jcr, JS_Running);
398 ok = false; /* error */
399 set_jcr_job_status(jcr, JS_ErrorTerminated);
401 + Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate);
403 + if (jcr->accurate) {
404 + //accurate_send_deleted_list(jcr); /* send deleted list to SD */
407 free_pool_memory(jcr->acl_text);
409 stop_heartbeat_monitor(jcr);
413 Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
414 + accurate_check_file(jcr, ff_pkt, NULL); /* list skipped files */
417 Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname);
418 + accurate_check_file(jcr, ff_pkt, NULL); /* list skipped files */
422 @@ -1118,6 +1291,9 @@
424 unstrip_path(ff_pkt);
426 + /* list backuped files */
427 + accurate_check_file(jcr, ff_pkt, attribs);
429 Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
431 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
432 @@ -1128,6 +1304,58 @@
436 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname)
438 + BSOCK *sd = jcr->store_bsock;
442 +#ifdef FD_NO_SEND_TEST
450 + * Send Attributes header to Storage daemon
451 + * <file-index> <stream> <info>
453 + if (!sd->fsend("%ld %d 0", 0, STREAM_UNIX_ATTRIBUTES)) {
454 + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
458 + Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
461 + * Send file attributes to Storage daemon
464 + * Filename (full path)
465 + * Encoded attributes
466 + * Link name (if type==FT_LNK or FT_LNKSAVED)
467 + * Encoded extended-attributes (for Win32)
469 + * For a directory, link is the same as fname, but with trailing
470 + * slash. For a linked file, link is the link.
472 + stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c",
474 + FT_NOSTAT /* FileType */,
475 + fname /* FileName */,
476 + 0, attribs, 0, 0, 0, attribsEx, 0);
478 + Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
480 + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
484 + sd->signal(BNET_EOD); /* indicate end of attributes data */
489 * Do in place strip of path
491 Index: src/filed/job.c
492 ===================================================================
493 --- src/filed/job.c (révision 6443)
494 +++ src/filed/job.c (copie de travail)
496 /* Imported functions */
497 extern int status_cmd(JCR *jcr);
498 extern int qstatus_cmd(JCR *jcr);
499 +extern int accurate_get_current_file_list_cmd(JCR *jcr);
501 /* Forward referenced functions */
502 static int backup_cmd(JCR *jcr);
504 {"RunBeforeJob", runbefore_cmd, 0},
505 {"RunAfterJob", runafter_cmd, 0},
506 {"Run", runscript_cmd, 0},
507 + {"accurate", accurate_get_current_file_list_cmd, 0},
508 {NULL, NULL} /* list terminator */
511 @@ -1087,6 +1089,9 @@
513 fo->flags |= FO_CHKCHANGES;
516 + fo->flags |= FO_ACCURATE;
519 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
521 @@ -1195,6 +1200,9 @@
523 level = get_memory(dir->msglen+1);
524 Dmsg1(110, "level_cmd: %s", dir->msg);
525 + if (strstr(dir->msg, "accurate")) {
526 + jcr->accurate = true;
528 if (sscanf(dir->msg, "level = %s ", level) != 1) {
531 @@ -1204,14 +1212,14 @@
532 /* Full backup requested? */
533 } else if (strcmp(level, "full") == 0) {
534 jcr->JobLevel = L_FULL;
535 - } else if (strcmp(level, "differential") == 0) {
536 + } else if (strstr(level, "differential")) {
537 jcr->JobLevel = L_DIFFERENTIAL;
540 - } else if (strcmp(level, "incremental") == 0) {
541 + } else if (strstr(level, "incremental")) {
542 jcr->JobLevel = L_INCREMENTAL;
547 * We get his UTC since time, then sync the clocks and correct it
548 * to agree with our clock.
549 Index: src/cats/sql_update.c
550 ===================================================================
551 --- src/cats/sql_update.c (révision 6443)
552 +++ src/cats/sql_update.c (copie de travail)
557 +int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId)
560 + char ed1[50], ed2[50];
562 + Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE FileId=%s AND BackupId=%s",
563 + edit_int64(FileId, ed1), edit_int64(BackupId, ed2));
564 + stat = INSERT_DB(jcr, mdb, mdb->cmd);
569 +int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, JobId_t JobId)
574 + /* TODO: mdb->esc_xxx are already ok but it's more smart to recompute it */
575 +// mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*len+2);
576 +// mdb->esc_name = db_escape_string(jcr, mdb, mdb->esc_name, fname, len);
577 + Mmsg(mdb->cmd, "INSERT INTO ToBackup%s (name) VALUES ('%s%s')", edit_int64(JobId, ed1), mdb->esc_path, mdb->esc_name);
578 + stat = INSERT_DB(jcr, mdb, mdb->cmd);
583 +int db_accurate_cleanup_currentfile(JCR *jcr, B_DB *mdb, JobId_t BackupId)
588 + Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE BackupId=%s", edit_int64(BackupId, ed1));
589 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
594 +int db_accurate_update_currentfile(JCR *jcr, B_DB *mdb, JobId_t JobId, int JobLevel, JobId_t BackupId)
597 + char ed1[50], ed2[50], ed3[50];
599 + edit_int64(JobId, ed2);
601 + "INSERT INTO CurrentFile (FileId, BackupId, FullMark, MarkId) "
602 + " (SELECT FileId, %s, '%c', %s FROM File WHERE JobId=%s)",
603 + edit_int64(BackupId, ed1),
604 + JobLevel, ed2, ed2);
605 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
610 +int db_accurate_create_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
615 + Mmsg(mdb->cmd, "CREATE TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
616 +// Mmsg(mdb->cmd, "CREATE TEMPORARY TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
617 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
622 +int db_accurate_drop_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
627 +// Mmsg(mdb->cmd, "DROP TABLE ToBackup%s", edit_int64(JobId, ed1));
628 +// stat = QUERY_DB(jcr, mdb, mdb->cmd);
634 +/* Mark the file record as being visited during database
635 + * accurate compare. Stuff JobId into the MarkId field
637 +int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId)
640 + char ed1[50], ed2[50], ed3[50];
643 + Mmsg(mdb->cmd, "UPDATE CurrentFile SET MarkId=%s WHERE FileId=%s AND BackupId=%s",
644 + edit_int64(JobId, ed1), edit_int64(FileId, ed2), edit_int64(BackupId, ed3));
645 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
646 + if (!stat || sql_affected_rows(mdb) != 1) {
654 * Update the Job record at start of Job
656 Index: src/cats/drop_postgresql_tables.in
657 ===================================================================
658 --- src/cats/drop_postgresql_tables.in (révision 6443)
659 +++ src/cats/drop_postgresql_tables.in (copie de travail)
664 -$bindir/psql -f - -d ${db_name} $* <<END-OF-DATA
665 +$bindir/psql -f - -U regress -d ${db_name} $* <<END-OF-DATA
666 drop table unsavedfiles;
667 drop table basefiles;
669 Index: src/cats/make_postgresql_tables.in
670 ===================================================================
671 --- src/cats/make_postgresql_tables.in (révision 6443)
672 +++ src/cats/make_postgresql_tables.in (copie de travail)
677 -$bindir/psql -f - -d ${db_name} $* <<END-OF-DATA
678 +$bindir/psql -f - -U regress -d ${db_name} $* <<END-OF-DATA
680 CREATE TABLE filename
683 CREATE INDEX file_jobid_idx on file (jobid);
684 CREATE INDEX file_fp_idx on file (filenameid, pathid);
686 +CREATE TABLE CurrentBackupId
688 + BackupId serial not null,
689 + ClientId integer not null,
690 + JobName text not null,
691 + FileSetId integer not null,
692 + primary key (BackupId)
695 +-- Serait bien de prendre la meme table pour
696 +-- les File et le CurrentBackup...
697 +-- Mais y'a des problemes pour les prunes
699 +CREATE TABLE CurrentFile
701 + FileId integer not null,
702 + BackupId integer not null,
703 + FullMark char(1) default 0,
704 + MarkId integer default 0,
705 + primary key (FileId)
708 +CREATE INDEX currentfile_fileid on CurrentFile (BackupId);
710 +-- CREATE TEMPORARY TABLE batch (fileindex int,
717 +-- -- On batch insert dans la table temporaire
719 +-- il faut trouver les fichiers manquant
720 +-- INSERT des nouveaux, UPDATE des anciens, SELECT pour trouver les deletes
723 +-- il faut trouver les fichiers modifies
724 +-- Le champs LStat n'est plus le meme
726 +-- FROM CurrentBackup,
727 +-- batch JOIN Path USING (Path) JOIN Filename USING (Name)
728 +-- WHERE Path.PathId = CurrentBackup.PathId
729 +-- AND Filename.FilenameId = CurrentBackup.FilenameId
730 +-- AND CurrentBackup.LStat != batch.LStat
732 +-- il faut mettre a jour la liste des fichiers
740 -- Possibly add one or more of the following indexes
741 -- if your Verifies are too slow.
742 Index: src/cats/protos.h
743 ===================================================================
744 --- src/cats/protos.h (révision 6443)
745 +++ src/cats/protos.h (copie de travail)
748 int db_delete_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pool_dbr);
749 int db_delete_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
750 +int db_accurate_clean_deleted_files(JCR *jcr, B_DB *mdb, JobId_t JobId, JobId_t BackupId);
753 bool db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime);
754 bool db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr);
755 +JobId_t db_accurate_find_backupid(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
756 int db_find_next_volume(JCR *jcr, B_DB *mdb, int index, bool InChanger, MEDIA_DBR *mr);
757 bool db_find_failed_job_since(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *stime, int &JobLevel);
760 +int db_accurate_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JobId_t backupid, FILE_DBR *fdbr);
761 bool db_get_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pdbr);
762 int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr);
763 bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
765 int db_update_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
766 int db_add_digest_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *digest, int type);
767 int db_mark_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t JobId);
768 +int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, FileId_t JobId);
769 +int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId);
770 +int db_accurate_drop_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
771 +int db_accurate_create_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
772 +int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId);
773 void db_make_inchanger_unique(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
774 +int db_accurate_cleanup_currentfile(JCR *jcr, B_DB *mdb, JobId_t BackupId);
775 +int db_accurate_update_currentfile(JCR *jcr, B_DB *mdb, JobId_t JobId, int JobLevel, JobId_t BackupId);
778 #endif /* __SQL_PROTOS_H */
779 Index: src/cats/sql_find.c
780 ===================================================================
781 --- src/cats/sql_find.c (révision 6443)
782 +++ src/cats/sql_find.c (copie de travail)
789 * Find JobId of last job that ran. E.g. for
790 * VERIFY_CATALOG we want the JobId of the last INIT.
791 Index: src/cats/sql_delete.c
792 ===================================================================
793 --- src/cats/sql_delete.c (révision 6443)
794 +++ src/cats/sql_delete.c (copie de travail)
800 + * Purge delete file from CurrentFile table. This table contains only
803 +int db_accurate_clean_deleted_files(JCR *jcr, B_DB *mdb, JobId_t JobId, JobId_t BackupId)
806 + char ed1[50], ed2[50];
808 + Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE MarkId!=%s AND BackupId=%s",
809 + edit_int64(JobId, ed1), edit_int64(BackupId, ed2));
810 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
817 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/
818 Index: src/cats/sql_create.c
819 ===================================================================
820 --- src/cats/sql_create.c (révision 6443)
821 +++ src/cats/sql_create.c (copie de travail)
826 +bool db_accurate_insert(JCR *jcr, B_DB *mdb, bool saved, const char *fname, struct stat *stat)
829 + split_path_and_file(jcr, mdb, fname);
830 + /* make like in Verify code */
835 * Create File record in B_DB
837 Index: src/cats/sql_get.c
838 ===================================================================
839 --- src/cats/sql_get.c (révision 6443)
840 +++ src/cats/sql_get.c (copie de travail)
843 * Returns: 0 on failure
844 * 1 on success with the File record in FILE_DBR
846 + * TODO: optimize this with only one query
848 int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr)
857 * Returns: 0 on failure
858 Index: src/stored/append.c
859 ===================================================================
860 --- src/stored/append.c (révision 6443)
861 +++ src/stored/append.c (copie de travail)
864 /* Read Stream header from the File daemon.
865 * The stream header consists of the following:
866 - * file_index (sequential Bacula file index, base 1)
867 + * file_index (sequential Bacula file index, base 1, 0 for deleted files)
868 * stream (Bacula number to distinguish parts of data)
869 * info (Info for Storage daemon -- compressed, encryped, ...)
870 * info is not currently used, so is read, but ignored!
871 @@ -185,16 +185,18 @@
873 Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);
875 - if (!(file_index > 0 && (file_index == last_file_index ||
876 - file_index == last_file_index + 1))) {
877 - Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
880 + if (file_index != 0) { /* TODO: handle file_index == 0 */
881 + if (!(file_index > 0 && (file_index == last_file_index ||
882 + file_index == last_file_index + 1))) {
883 + Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
887 + if (file_index != last_file_index) {
888 + jcr->JobFiles = file_index;
889 + last_file_index = file_index;
892 - if (file_index != last_file_index) {
893 - jcr->JobFiles = file_index;
894 - last_file_index = file_index;
897 /* Read data stream from the File daemon.
898 * The data stream is just raw bytes
899 @@ -212,25 +214,26 @@
900 stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
903 - while (!write_record_to_block(dcr->block, &rec)) {
904 - Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
906 - if (!write_block_to_device(dcr)) {
907 - Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
908 - dev->print_name(), dev->bstrerror());
914 - Dmsg0(400, "Not OK\n");
917 - jcr->JobBytes += rec.data_len; /* increment bytes this job */
918 - Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
919 - FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
920 - stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
921 + while (!write_record_to_block(dcr->block, &rec)) {
922 + Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
924 + if (!write_block_to_device(dcr)) {
925 + Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
926 + dev->print_name(), dev->bstrerror());
932 + Dmsg0(400, "Not OK\n");
935 + jcr->JobBytes += rec.data_len; /* increment bytes this job */
936 + Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
937 + FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
938 + stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
941 /* Send attributes and digest to Director for Catalog */
942 if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX ||
943 crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
945 ===================================================================
946 --- src/jcr.h (révision 6443)
947 +++ src/jcr.h (copie de travail)
950 /* Forward referenced structures */
957 CRYPTO_CTX crypto; /* Crypto ctx */
958 DIRRES* director; /* Director resource */
959 bool VSS; /* VSS used by FD */
960 + htable *file_list; /* Previous file list (accurate mode) */
961 #endif /* FILE_DAEMON */
964 Index: src/findlib/find.h
965 ===================================================================
966 --- src/findlib/find.h (révision 6443)
967 +++ src/findlib/find.h (copie de travail)
969 #define FO_ENHANCEDWILD (1<<23) /* Enhanced wild card processing */
970 #define FO_CHKCHANGES (1<<24) /* Check if file have been modified during backup */
971 #define FO_STRIPPATH (1<<25) /* Check for stripping path */
972 +#define FO_ACCURATE (1<<26) /* Accurate mode */
974 struct s_included_file {
975 struct s_included_file *next;