1 Index: src/dird/backup.c
2 ===================================================================
3 --- src/dird/backup.c (révision 6374)
4 +++ src/dird/backup.c (copie de travail)
9 +#include "findlib/find.h"
11 /* Commands sent to File daemon */
12 static char backupcmd[] = "backup\n";
17 + * We are called here for each record that matches the above
18 + * SQL query -- that is for each file contained in the Catalog
19 + * that was not marked earlier. This means that the file in
20 + * question is a missing file (in the Catalog but not on Disk).
22 +static int missing_handler(void *ctx, int num_fields, char **row)
24 + JCR *jcr = (JCR *)ctx;
26 + if (job_canceled(jcr)) {
30 + /* TODO: return the list to the FD */
31 + if (num_fields == 2)
32 + Qmsg(jcr, M_INFO, 0, " %s%s\n", row[0]?row[0]:"", row[1]?row[1]:"");
34 + Qmsg(jcr, M_INFO, 0, " %s\n", row[0]?row[0]:"");
39 +/* TODO: tweak verify code to use the same function */
40 +bool accurate_check_file(JCR *jcr, FILE_DBR *fdbr, char *attr, char *Opts_Digest, int *do_Digest)
44 + struct stat statf; /* file stat */
45 + struct stat statc; /* catalog stat */
47 + int32_t LinkFIf, LinkFIc;
49 + decode_stat(attr, &statf, &LinkFIf); /* decode file stat packet */
50 + decode_stat(fdbr->LStat, &statc, &LinkFIc); /* decode catalog stat */
51 + *do_Digest = CRYPTO_DIGEST_NONE;
53 + for (p=Opts_Digest; *p; p++) {
54 + char ed1[30], ed2[30];
56 + case 'i': /* compare INODEs */
57 + if (statc.st_ino != statf.st_ino) {
58 + Jmsg(jcr, M_INFO, 0, _(" st_ino differ. Cat: %s File: %s\n"),
59 + edit_uint64((uint64_t)statc.st_ino, ed1),
60 + edit_uint64((uint64_t)statf.st_ino, ed2));
64 + case 'p': /* permissions bits */
65 + if (statc.st_mode != statf.st_mode) {
66 + Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"),
67 + (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
71 + case 'n': /* number of links */
72 + if (statc.st_nlink != statf.st_nlink) {
73 + Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"),
74 + (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
78 + case 'u': /* user id */
79 + if (statc.st_uid != statf.st_uid) {
80 + Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"),
81 + (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
85 + case 'g': /* group id */
86 + if (statc.st_gid != statf.st_gid) {
87 + Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"),
88 + (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
92 + case 's': /* size */
93 + if (statc.st_size != statf.st_size) {
94 + Jmsg(jcr, M_INFO, 0, _(" st_size differ. Cat: %s File: %s\n"),
95 + edit_uint64((uint64_t)statc.st_size, ed1),
96 + edit_uint64((uint64_t)statf.st_size, ed2));
100 + case 'a': /* access time */
101 + if (statc.st_atime != statf.st_atime) {
102 + Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n"));
107 + if (statc.st_mtime != statf.st_mtime) {
108 + Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n"));
112 + case 'c': /* ctime */
113 + if (statc.st_ctime != statf.st_ctime) {
114 + Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n"));
118 + case 'd': /* file size decrease */
119 + if (statc.st_size > statf.st_size) {
120 + Jmsg(jcr, M_INFO, 0, _(" st_size decrease. Cat: %s File: %s\n"),
121 + edit_uint64((uint64_t)statc.st_size, ed1),
122 + edit_uint64((uint64_t)statf.st_size, ed2));
126 + case '5': /* compare MD5 */
127 + Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
128 + *do_Digest = CRYPTO_DIGEST_MD5;
130 + case '1': /* compare SHA1 */
131 + *do_Digest = CRYPTO_DIGEST_SHA1;
143 + * This function is called at EOJ.
144 + * For a Full backup, we remove old one, and we add all entries
145 + * For an Incremental, we add all entries (delete have been before)
146 + * For a Differential, we add all entries (delete have been before)
151 +bool accurate_update_current_files(JCR *jcr)
155 + if (jcr->accurate == false) {
159 + backupid = db_accurate_find_backupid(jcr, jcr->db, &jcr->jr);
161 + Dmsg1(1, "backupid = %i\n", backupid);
164 + return false; /* something goes wrong */
167 + if (jcr->JobLevel == L_FULL) {
168 + db_accurate_cleanup_currentfile(jcr, jcr->db, backupid);
171 + db_accurate_update_currentfile(jcr, jcr->db, jcr->JobId,
172 + jcr->JobLevel, backupid);
177 + * We are called here for each record that matches the above
178 + * SQL query -- that is for each file contained in the Catalog
179 + * that was not marked earlier. This means that the file in
180 + * question is a missing file (in the Catalog but not on Disk).
182 +static int accurate_handler(void *ctx, int num_fields, char **row)
184 + JCR *jcr = (JCR *)ctx;
186 + if (job_canceled(jcr)) {
189 + if (num_fields == 2) { /* deleted files */
190 + jcr->file_bsock->fsend("D %s%s", row[0]?row[0]:"", row[1]?row[1]:"");
191 + } else if (num_fields == 1) { /* files to backup */
192 + jcr->file_bsock->fsend("S %s", row[0]?row[0]:"");
198 + * Send deleted files and files to backup in accurate mode
201 +static int accurate_send_missing_and_deleted_files(JCR *jcr, JobId_t BackupId)
203 + char buf[MAXSTRING];
204 + char ed1[50], ed2[50];
206 + bsnprintf(buf, sizeof(buf),
207 + "SELECT Path.Path,Filename.Name "
208 + "FROM CurrentFile "
209 + "JOIN File USING (FileId) "
210 + "JOIN Path USING (PathId) "
211 + "JOIN Filename USING (FilenameId) "
212 + "WHERE CurrentFile.BackupId=%s "
213 + "AND CurrentFile.MarkId!=%s ",
214 + edit_uint64(BackupId, ed1), edit_uint64(jcr->JobId, ed2));
215 + /* missing_handler is called for each file found */
216 + Dmsg1(2, "display deleted files cmd=%s\n", buf);
217 + db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
218 + jcr->file_bsock->signal(BNET_EOD);
220 + bsnprintf(buf, sizeof(buf),
221 + "SELECT Name FROM ToBackup%s",
222 + edit_uint64(jcr->JobId, ed2));
223 + /* missing_handler is called for each file found */
224 + Dmsg1(2, "display files to backup cmd=%s\n", buf);
225 + db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
226 + jcr->file_bsock->signal(BNET_EOD);
232 + * Accurate backup mode
233 + * 1. Receive the list of all files including those backed up to the Dir
234 + * 2. Dir computes files and deleted files.
235 + * 3. Dir sends list of additional files (new files) to backup, and list of files
238 + * Cleanup attributes (don't use atime, inode etc..)
239 + * Need to insert file and attributes to temp table ?
240 + * Batch compare files and attributes ?
242 + * If file have file_index=0, they are discarded by FD
244 + * TODO: send deleted list and new list to client
245 + * tweak SD with file_index=-1
247 +bool accurate_compute_files(JCR *jcr)
250 + char buf[MAXSTRING];
253 + POOLMEM *fname = get_pool_memory(PM_MESSAGE);
254 + int do_Digest = CRYPTO_DIGEST_NONE;
255 + int32_t file_index = 0;
256 + JobId_t JobId=0; /* TODO: compute the job key in new table */
257 + JobId_t backupid=0;
259 + memset(&fdbr, 0, sizeof(FILE_DBR));
260 + fd = jcr->file_bsock;
261 + fdbr.JobId = JobId;
262 + jcr->FileIndex = 0;
264 + if (/*jcr->accurate == false ||*/ jcr->JobLevel == L_FULL) {
268 + backupid = db_accurate_find_backupid(jcr, jcr->db, &jcr->jr);
270 + Jmsg(jcr, M_ERROR, 0, _("Can't use Accurate mode ERR=Can't find BackupId\n"));
273 + db_accurate_create_tobackup_table(jcr, jcr->db, jcr->JobId);
274 + Dmsg0(1, "bdird: waiting to receive file attributes\n");
276 + * Get Attributes and Signature from File daemon
280 + * Options or Digest (MD5/SHA1)
285 + while ((n=bget_dirmsg(fd)) >= 0 && !job_canceled(jcr)) {
287 + char *attr, *p, *fn;
288 + char Opts_Digest[MAXSTRING]; /* Verify Opts or MD5/SHA1 digest */
290 + if (job_canceled(jcr)) {
293 + fname = check_pool_memory_size(fname, fd->msglen);
294 + jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
295 + Dmsg1(1, "Atts+Digest=%s\n", fd->msg);
296 + if ((len = sscanf(fd->msg, "%ld %d %100s", &file_index, &stream,
298 + Jmsg3(jcr, M_FATAL, 0, _("bird<filed: bad attributes, expected 3 fields got %d\n"
299 +" mslen=%d msg=%s\n"), len, fd->msglen, fd->msg);
303 + * We read the Options or Signature into fname
304 + * to prevent overrun, now copy it to proper location.
306 + bstrncpy(Opts_Digest, fname, sizeof(Opts_Digest));
308 + skip_nonspaces(&p); /* skip FileIndex */
310 + skip_nonspaces(&p); /* skip Stream */
312 + skip_nonspaces(&p); /* skip Opts_Digest */
313 + p++; /* skip space */
316 + *fn++ = *p++; /* copy filename */
318 + *fn = *p++; /* term filename and point to attribs */
321 + * Got attributes stream, decode it
323 + if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
325 + Dmsg2(400, "file_index=%d attr=%s\n", file_index, attr);
327 + jcr->FileIndex = file_index; /* remember attribute file_index */
328 + do_Digest = CRYPTO_DIGEST_NONE;
329 + pm_strcpy(jcr->fname, fname); /* move filename into JCR */
333 + * Find equivalent record in the database
336 + if (db_accurate_get_file_attributes_record(jcr, jcr->db, jcr->fname,
339 + Dmsg2(1, "get_file ok fname=%s fileid=%i\n", jcr->fname, fdbr.FileId);
340 + if (fdbr.MarkId != jcr->JobId) { /* Already visited ? */
341 + if (file_index == 0) { /* file not saved */
342 + changed = accurate_check_file(jcr, &fdbr, attr, Opts_Digest, &do_Digest);
343 + Dmsg1(1, "check_file changed=%i\n", changed);
345 + if (changed == true) {
346 + db_accurate_mark_file_for_backup(jcr, jcr->db, jcr->fname, jcr->JobId);
347 + db_accurate_delete_file_record(jcr, jcr->db, fdbr.FileId, backupid);
349 + db_accurate_mark_file_record(jcr, jcr->db, backupid,
350 + fdbr.FileId, jcr->JobId);
352 + } else { /* file_index != 0 file have be backuped */
353 + db_accurate_delete_file_record(jcr, jcr->db, fdbr.FileId, backupid);
356 + } else if (file_index == 0) {
357 + Dmsg1(1, "mark_for_backup fname=%s\n", jcr->fname);
358 + db_accurate_mark_file_for_backup(jcr, jcr->db, jcr->fname, jcr->JobId);
362 + * Got Digest Signature from Storage daemon
363 + * It came across in the Opts_Digest field.
366 + } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
367 + Dmsg2(400, "stream=Digest inx=%d Digest=%s\n", file_index, Opts_Digest);
369 + * When ever we get a digest it MUST have been
370 + * preceded by an attributes record, which sets attr_file_index
372 + if (jcr->FileIndex != (uint32_t)file_index) {
373 + Jmsg2(jcr, M_FATAL, 0, _("MD5/SHA1 index %d not same as attributes %d\n"),
374 + file_index, jcr->FileIndex);
377 + if (do_Digest != CRYPTO_DIGEST_NONE) {
378 + db_escape_string(jcr, jcr->db, buf, Opts_Digest, strlen(Opts_Digest));
379 + if (strcmp(buf, fdbr.Digest) != 0) {
380 + if (debug_level >= 10) {
381 + Jmsg(jcr, M_INFO, 0, _(" %d not same. File=%s Cat=%s\n"),
382 + stream, buf, fdbr.Digest);
384 + Jmsg(jcr, M_INFO, 0, _(" %d differs.\n"),
387 + //stat = JS_Differences;
389 + do_Digest = CRYPTO_DIGEST_NONE;
392 +// jcr->JobFiles = file_index;
394 + if (is_bnet_error(fd)) {
396 + Jmsg2(jcr, M_FATAL, 0, _("bdird<filed: bad attributes from filed n=%d : %s\n"),
397 + n, be.bstrerror());
402 +CREATE VIEW cf AS SELECT path.path || filename.name as filename,
403 + jobid, currentfile.markid, backupid
404 + FROM File join currentfile using (fileid) join filename using (filenameid) join path using (pathid)
407 + accurate_send_missing_and_deleted_files(jcr, backupid);
409 + db_accurate_clean_deleted_files(jcr, jcr->db, jcr->JobId, backupid);
411 + db_accurate_drop_tobackup_table(jcr, jcr->db, jcr->JobId);
413 + free_pool_memory(fname);
417 + db_accurate_drop_tobackup_table(jcr, jcr->db, jcr->JobId);
422 * Do a backup of the specified FileSet
424 * Returns: false on failure
430 + * If backup is in accurate mode, FD will send the list of
431 + * all files. We have to store it, and compute witch files
432 + * have been deleted and witch files have to be backuped.
434 + accurate_compute_files(jcr);
436 /* Pickup Job termination data */
437 stat = wait_for_job_termination(jcr);
438 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
439 + accurate_update_current_files(jcr);
441 if (stat == JS_Terminated) {
442 backup_cleanup(jcr, stat);
444 Index: src/dird/inc_conf.c
445 ===================================================================
446 --- src/dird/inc_conf.c (révision 6374)
447 +++ src/dird/inc_conf.c (copie de travail)
449 * Items that are valid in an Options resource
451 static RES_ITEM options_items[] = {
452 + {"accurate", store_opts, {0}, 0, 0, 0},
453 {"compression", store_opts, {0}, 0, 0, 0},
454 {"signature", store_opts, {0}, 0, 0, 0},
455 {"verify", store_opts, {0}, 0, 0, 0},
467 * options given above.
469 static struct s_kw FS_option_kw[] = {
470 + {"accurate", INC_KW_ACCURATE},
471 {"compression", INC_KW_COMPRESSION},
472 {"signature", INC_KW_DIGEST},
473 {"encryption", INC_KW_ENCRYPTION},
475 {"no", INC_KW_ENHANCEDWILD, "0"},
476 {"yes", INC_KW_CHKCHANGES, "c"},
477 {"no", INC_KW_CHKCHANGES, "0"},
478 + {"yes", INC_KW_ACCURATE, "C"},
479 + {"no", INC_KW_ACCURATE, "0"},
483 Index: src/dird/dird_conf.c
484 ===================================================================
485 --- src/dird/dird_conf.c (révision 6374)
486 +++ src/dird/dird_conf.c (copie de travail)
488 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
489 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
490 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
491 + {"accuratebackup", store_bool, ITEM(res_job.accurate), 0,0,0},
492 {NULL, NULL, {0}, 0, 0, 0}
496 if (res->res_job.spool_size) {
497 sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1));
499 + if (res->res_job.JobType == JT_BACKUP) {
500 + sendit(sock, _(" Accurate=%d\n"), res->res_job.accurate);
502 if (res->res_job.JobType == JT_MIGRATE) {
503 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
505 Index: src/dird/dird_conf.h
506 ===================================================================
507 --- src/dird/dird_conf.h (révision 6374)
508 +++ src/dird/dird_conf.h (copie de travail)
510 bool write_part_after_job; /* Set to write part after job in SD */
511 bool enabled; /* Set if job enabled */
512 bool OptimizeJobScheduling; /* Set if we should optimize Job scheduling */
513 + bool accurate; /* Set if it is an accurate backup job */
515 MSGS *messages; /* How and where to send messages */
516 SCHED *schedule; /* When -- Automatic schedule */
517 Index: src/filed/backup.c
518 ===================================================================
519 --- src/filed/backup.c (révision 6374)
520 +++ src/filed/backup.c (copie de travail)
522 static bool crypto_session_send(JCR *jcr, BSOCK *sd);
525 + * Called by save_file when accept/discard file for backup
527 + * TODO: we could add MD5/SHAX digest, but we have to compute it
530 +static bool accurate_add_file(JCR *jcr, FF_PKT *ff_pkt, char *stats)
533 + char attribs[MAXSTRING];
534 + uint32_t file_index=jcr->JobFiles;
535 + BSOCK *dir = jcr->dir_bsock;
538 + if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
542 + if (!stats) { /* TODO: don't always compute attribute */
544 + encode_stat(attribs, ff_pkt, 0);
548 + switch (ff_pkt->type) {
549 + case FT_LNKSAVED: /* Hard linked, file already saved */
551 + stat = dir->fsend("%d %d %s %s%c%s%c%s%c", file_index,
552 + STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
553 + 0, a, 0, ff_pkt->link, 0);
561 + stat = dir->fsend("%d %d %s %s%c%s%c%c", file_index,
562 + STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
570 + stat = dir->fsend("%d %d %s %s%c%s%c%c", file_index,
571 + STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->link,
575 + Dmsg2(1, _("Fname=%s Type=%i\n"), ff_pkt->fname, ff_pkt->type);
580 + Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), bnet_strerror(dir));
587 +/* build a fileset with new files from director */
588 +static bool accurate_get_new_and_deleted_file_list(JCR *jcr)
590 + BSOCK *dir = jcr->dir_bsock;
591 + if (jcr->accurate == false || job_canceled(jcr)) {
595 + /* get deleted files */
596 + while (dir->recv() >= 0) {
597 + Dmsg1(1, "deleted = %s\n", dir->msg);
599 + /* get missing files */
600 + while (dir->recv() >= 0) {
601 + Dmsg1(1, "missing = %s\n", dir->msg);
607 +/* send deleted file list to stored */
608 +static bool accurate_send_deleted_list(JCR *jcr)
610 + if (jcr->accurate == false || job_canceled(jcr)) {
616 +static bool accurate_send_file_list(JCR *jcr)
618 + if (jcr->accurate == false || job_canceled(jcr)) {
621 + Dmsg0(1, "Sending BNET_EOD\n");
622 + jcr->dir_bsock->signal(BNET_EOD); /* end of sending data */
628 * Find all the requested files and send them
629 * to the Storage daemon.
634 // TODO landonf: Allow user to specify encryption algorithm
635 + if (jcr->JobLevel != L_FULL) {
636 + jcr->accurate=true; /* TODO: remove that */
639 sd = jcr->store_bsock;
642 ok = false; /* error */
643 set_jcr_job_status(jcr, JS_ErrorTerminated);
645 + Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate);
646 + /* start accurate stuffs */
647 + if (jcr->accurate) {
648 + /* TODO: test job_canceled() */
649 + accurate_send_file_list(jcr); /* send all files to DIR */
650 + accurate_get_new_and_deleted_file_list(jcr); /* get a new incr fileset from DIR */
651 +// set_find_options((FF_PKT *)jcr->ff, 0, 0); /* we backup all that director wants */
652 +// if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file, (void *)jcr)) {
653 +// ok = false; /* error */
654 +// set_jcr_job_status(jcr, JS_ErrorTerminated);
656 +// accurate_send_file_list(jcr); /* send all new files to DIR */
657 + accurate_send_deleted_list(jcr); /* send deleted list to SD */
660 free_pool_memory(jcr->acl_text);
665 Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
666 + accurate_add_file(jcr, ff_pkt, NULL); /* list skipped files */
669 Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname);
670 + accurate_add_file(jcr, ff_pkt, NULL); /* list skipped files */
674 @@ -1111,6 +1233,9 @@
676 unstrip_path(ff_pkt);
678 + /* list backuped files */
679 + accurate_add_file(jcr, ff_pkt, attribs);
681 Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
683 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
684 Index: src/filed/job.c
685 ===================================================================
686 --- src/filed/job.c (révision 6374)
687 +++ src/filed/job.c (copie de travail)
688 @@ -1087,6 +1087,9 @@
690 fo->flags |= FO_CHKCHANGES;
693 + fo->flags |= FO_ACCURATE;
696 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
698 Index: src/cats/sql_update.c
699 ===================================================================
700 --- src/cats/sql_update.c (révision 6374)
701 +++ src/cats/sql_update.c (copie de travail)
706 +int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId)
709 + char ed1[50], ed2[50];
711 + Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE FileId=%s AND BackupId=%s",
712 + edit_int64(FileId, ed1), edit_int64(BackupId, ed2));
713 + stat = INSERT_DB(jcr, mdb, mdb->cmd);
718 +int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, JobId_t JobId)
723 + /* TODO: mdb->esc_xxx are already ok but it's more smart to recompute it */
724 +// mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*len+2);
725 +// mdb->esc_name = db_escape_string(jcr, mdb, mdb->esc_name, fname, len);
726 + Mmsg(mdb->cmd, "INSERT INTO ToBackup%s (name) VALUES ('%s%s')", edit_int64(JobId, ed1), mdb->esc_path, mdb->esc_name);
727 + stat = INSERT_DB(jcr, mdb, mdb->cmd);
732 +int db_accurate_cleanup_currentfile(JCR *jcr, B_DB *mdb, JobId_t BackupId)
737 + Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE BackupId=%s", edit_int64(BackupId, ed1));
738 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
743 +int db_accurate_update_currentfile(JCR *jcr, B_DB *mdb, JobId_t JobId, int JobLevel, JobId_t BackupId)
746 + char ed1[50], ed2[50], ed3[50];
748 + edit_int64(JobId, ed2);
750 + "INSERT INTO CurrentFile (FileId, BackupId, FullMark, MarkId) "
751 + " (SELECT FileId, %s, '%c', %s FROM File WHERE JobId=%s)",
752 + edit_int64(BackupId, ed1),
753 + JobLevel, ed2, ed2);
754 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
759 +int db_accurate_create_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
764 + Mmsg(mdb->cmd, "CREATE TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
765 +// Mmsg(mdb->cmd, "CREATE TEMPORARY TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
766 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
771 +int db_accurate_drop_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
776 +// Mmsg(mdb->cmd, "DROP TABLE ToBackup%s", edit_int64(JobId, ed1));
777 +// stat = QUERY_DB(jcr, mdb, mdb->cmd);
783 +/* Mark the file record as being visited during database
784 + * accurate compare. Stuff JobId into the MarkId field
786 +int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId)
789 + char ed1[50], ed2[50], ed3[50];
792 + Mmsg(mdb->cmd, "UPDATE CurrentFile SET MarkId=%s WHERE FileId=%s AND BackupId=%s",
793 + edit_int64(JobId, ed1), edit_int64(FileId, ed2), edit_int64(BackupId, ed3));
794 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
795 + if (!stat || sql_affected_rows(mdb) != 1) {
803 * Update the Job record at start of Job
805 Index: src/cats/make_postgresql_tables.in
806 ===================================================================
807 --- src/cats/make_postgresql_tables.in (révision 6374)
808 +++ src/cats/make_postgresql_tables.in (copie de travail)
810 CREATE INDEX file_jobid_idx on file (jobid);
811 CREATE INDEX file_fp_idx on file (filenameid, pathid);
813 +CREATE TABLE CurrentBackupId
815 + BackupId serial not null,
816 + ClientId integer not null,
817 + JobName text not null,
818 + FileSetId integer not null,
819 + primary key (BackupId)
822 +-- Serait bien de prendre la meme table pour
823 +-- les File et le CurrentBackup...
824 +-- Mais y'a des problemes pour les prunes
826 +CREATE TABLE CurrentFile
828 + FileId integer not null,
829 + BackupId integer not null,
830 + FullMark char(1) default 0,
831 + MarkId integer default 0,
832 + primary key (FileId)
835 +CREATE INDEX currentfile_fileid on CurrentFile (BackupId);
837 +-- CREATE TEMPORARY TABLE batch (fileindex int,
844 +-- -- On batch insert dans la table temporaire
846 +-- il faut trouver les fichiers manquant
847 +-- INSERT des nouveaux, UPDATE des anciens, SELECT pour trouver les deletes
850 +-- il faut trouver les fichiers modifies
851 +-- Le champs LStat n'est plus le meme
853 +-- FROM CurrentBackup,
854 +-- batch JOIN Path USING (Path) JOIN Filename USING (Name)
855 +-- WHERE Path.PathId = CurrentBackup.PathId
856 +-- AND Filename.FilenameId = CurrentBackup.FilenameId
857 +-- AND CurrentBackup.LStat != batch.LStat
859 +-- il faut mettre a jour la liste des fichiers
867 -- Possibly add one or more of the following indexes
868 -- if your Verifies are too slow.
869 Index: src/cats/protos.h
870 ===================================================================
871 --- src/cats/protos.h (révision 6374)
872 +++ src/cats/protos.h (copie de travail)
875 int db_delete_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pool_dbr);
876 int db_delete_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
877 +int db_accurate_clean_deleted_files(JCR *jcr, B_DB *mdb, JobId_t JobId, JobId_t BackupId);
880 bool db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime);
881 bool db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr);
882 +JobId_t db_accurate_find_backupid(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
883 int db_find_next_volume(JCR *jcr, B_DB *mdb, int index, bool InChanger, MEDIA_DBR *mr);
884 bool db_find_failed_job_since(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *stime, int &JobLevel);
887 +int db_accurate_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JobId_t backupid, FILE_DBR *fdbr);
888 bool db_get_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pdbr);
889 int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr);
890 bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
892 int db_update_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
893 int db_add_digest_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *digest, int type);
894 int db_mark_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t JobId);
895 +int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, FileId_t JobId);
896 +int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId);
897 +int db_accurate_drop_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
898 +int db_accurate_create_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
899 +int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId);
900 void db_make_inchanger_unique(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
901 +int db_accurate_cleanup_currentfile(JCR *jcr, B_DB *mdb, JobId_t BackupId);
902 +int db_accurate_update_currentfile(JCR *jcr, B_DB *mdb, JobId_t JobId, int JobLevel, JobId_t BackupId);
905 #endif /* __SQL_PROTOS_H */
906 Index: src/cats/sql_find.c
907 ===================================================================
908 --- src/cats/sql_find.c (révision 6374)
909 +++ src/cats/sql_find.c (copie de travail)
915 + * Find BackupId of last job that ran. E.g. for
917 + * Returns: Last backuip
921 +db_accurate_find_backupid(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
924 + char ed1[50],ed2[50];
925 + JobId_t backupid=0;
927 + /* Find backupid */
929 + Dmsg2(100, "JobLevel=%d JobType=%d\n", jcr->JobLevel, jcr->JobType);
931 +"SELECT BackupId FROM CurrentBackupId WHERE JobName='%s' AND "
932 +"ClientId=%s AND FileSetId=%s ORDER BY BackupId DESC LIMIT 1",
934 + edit_int64(jr->ClientId, ed1),
935 + edit_int64(jr->FileSetId, ed2));
937 + Dmsg1(100, "Query: %s\n", mdb->cmd);
938 + if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
942 + if ((row = sql_fetch_row(mdb)) == NULL) {
943 + sql_free_result(mdb);
944 + if (jcr->JobLevel == L_FULL) {
946 + "INSERT INTO CurrentBackupId (JobName, ClientId, FileSetId) VALUES ('%s', %s, %s)",
947 + jr->Name, ed1, ed2);
948 + if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
952 + backupid = sql_insert_id(mdb, NT_("CurrentBackupId"));
954 + Mmsg1(&mdb->errmsg, _("No Job found for: %s.\n"), mdb->cmd);
958 + backupid = str_to_int64(row[0]);
961 + sql_free_result(mdb);
969 * Find JobId of last job that ran. E.g. for
970 * VERIFY_CATALOG we want the JobId of the last INIT.
971 Index: src/cats/sql_delete.c
972 ===================================================================
973 --- src/cats/sql_delete.c (révision 6374)
974 +++ src/cats/sql_delete.c (copie de travail)
980 + * Purge delete file from CurrentFile table. This table contains only
983 +int db_accurate_clean_deleted_files(JCR *jcr, B_DB *mdb, JobId_t JobId, JobId_t BackupId)
986 + char ed1[50], ed2[50];
988 + Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE MarkId!=%s AND BackupId=%s",
989 + edit_int64(JobId, ed1), edit_int64(BackupId, ed2));
990 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
997 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/
998 Index: src/cats/sql_create.c
999 ===================================================================
1000 --- src/cats/sql_create.c (révision 6374)
1001 +++ src/cats/sql_create.c (copie de travail)
1002 @@ -829,6 +829,14 @@
1006 +bool db_accurate_insert(JCR *jcr, B_DB *mdb, bool saved, const char *fname, struct stat *stat)
1009 + split_path_and_file(jcr, mdb, fname);
1010 + /* make like in Verify code */
1015 * Create File record in B_DB
1017 Index: src/cats/sql_get.c
1018 ===================================================================
1019 --- src/cats/sql_get.c (révision 6374)
1020 +++ src/cats/sql_get.c (copie de travail)
1023 * Returns: 0 on failure
1024 * 1 on success with the File record in FILE_DBR
1026 + * TODO: optimize this with only one query
1028 int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr)
1035 + * Given a full filename (with path), look up the File record
1036 + * (with attributes) in the database.
1038 + * Returns: 0 on failure
1039 + * 1 on success with the File record in FILE_DBR
1041 +int db_accurate_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JobId_t backupid, FILE_DBR *fdbr)
1048 + split_path_and_file(jcr, mdb, fname);
1050 + mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
1051 + db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
1053 + mdb->esc_path = check_pool_memory_size(mdb->esc_path, 2*mdb->pnl+2);
1054 + db_escape_string(jcr, mdb, mdb->esc_path, mdb->path, mdb->pnl);
1057 +"SELECT FileId, LStat, MD5, FilenameId, PathId, FileIndex, CurrentFile.MarkId, JobId "
1058 + "FROM File JOIN CurrentFile USING (FileId) "
1059 + "JOIN Filename USING (FilenameId) "
1060 + "JOIN Path USING (PathId) "
1061 + "WHERE Path.Path='%s' "
1062 + "AND Filename.Name='%s' "
1063 + "AND BackupId=%s ",
1066 + edit_int64(backupid, ed1));
1068 + Dmsg1(100,"get_file %s\n", mdb->cmd);
1070 + if (QUERY_DB(jcr, mdb, mdb->cmd)) {
1072 + mdb->num_rows = sql_num_rows(mdb);
1073 + if (mdb->num_rows == 1) {
1074 + if ((row = sql_fetch_row(mdb)) == NULL) {
1075 + Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
1077 + fdbr->FileId = str_to_int64(row[0]);
1078 + bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
1079 + bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
1080 + fdbr->FilenameId = str_to_int64(row[3]);
1081 + fdbr->PathId = str_to_int64(row[4]);
1082 + fdbr->FileIndex = str_to_int64(row[5]);
1083 + fdbr->MarkId = str_to_int64(row[6]);
1084 + fdbr->JobId = str_to_int64(row[7]);
1087 + } else if (mdb->num_rows > 1) {
1088 + Mmsg2(mdb->errmsg, _("Get DB File record %s failed num=%i\n"),fname,mdb->num_rows);
1089 + Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
1091 + sql_free_result(mdb);
1093 + Mmsg(mdb->errmsg, _("File record: %s not found in Catalog for BackupId=%s.\n"), fname, ed1);
1103 * Returns: 0 on failure
1105 ===================================================================
1106 --- src/jcr.h (révision 6374)
1107 +++ src/jcr.h (copie de travail)
1109 B_DB *db_batch; /* database pointer for batch insert */
1110 ATTR_DBR *ar; /* DB attribute record */
1111 guid_list *id_list; /* User/group id to name list */
1112 + bool accurate; /* true if job is accurate */
1114 void *plugin_ctx_list; /* list of contexts for plugins */
1115 void *plugin_ctx; /* current plugin context */
1116 Index: src/findlib/find.h
1117 ===================================================================
1118 --- src/findlib/find.h (révision 6374)
1119 +++ src/findlib/find.h (copie de travail)
1121 #define FO_ENHANCEDWILD (1<<23) /* Enhanced wild card processing */
1122 #define FO_CHKCHANGES (1<<24) /* Check if file have been modified during backup */
1123 #define FO_STRIPPATH (1<<25) /* Check for stripping path */
1124 +#define FO_ACCURATE (1<<26) /* Accurate mode */
1126 struct s_included_file {
1127 struct s_included_file *next;