1 Index: src/dird/fd_cmds.c
2 ===================================================================
3 --- src/dird/fd_cmds.c (révision 6372)
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 6372)
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 + * We are called here for each record that matches the above
70 + * SQL query -- that is for each file contained in the Catalog
71 + * that was not marked earlier. This means that the file in
72 + * question is a missing file (in the Catalog but not on Disk).
74 +static int missing_handler(void *ctx, int num_fields, char **row)
76 + JCR *jcr = (JCR *)ctx;
78 + if (job_canceled(jcr)) {
82 + /* TODO: return the list to the FD */
83 + if (num_fields == 2)
84 + Qmsg(jcr, M_INFO, 0, " %s%s\n", row[0]?row[0]:"", row[1]?row[1]:"");
86 + Qmsg(jcr, M_INFO, 0, " %s\n", row[0]?row[0]:"");
91 +/* TODO: tweak verify code to use the same function */
92 +bool accurate_check_file(JCR *jcr, FILE_DBR *fdbr, char *attr, char *Opts_Digest, int *do_Digest)
96 + struct stat statf; /* file stat */
97 + struct stat statc; /* catalog stat */
99 + int32_t LinkFIf, LinkFIc;
101 + decode_stat(attr, &statf, &LinkFIf); /* decode file stat packet */
102 + decode_stat(fdbr->LStat, &statc, &LinkFIc); /* decode catalog stat */
103 + *do_Digest = CRYPTO_DIGEST_NONE;
105 + for (p=Opts_Digest; *p; p++) {
106 + char ed1[30], ed2[30];
108 + case 'i': /* compare INODEs */
109 + if (statc.st_ino != statf.st_ino) {
110 + Jmsg(jcr, M_INFO, 0, _(" st_ino differ. Cat: %s File: %s\n"),
111 + edit_uint64((uint64_t)statc.st_ino, ed1),
112 + edit_uint64((uint64_t)statf.st_ino, ed2));
116 + case 'p': /* permissions bits */
117 + if (statc.st_mode != statf.st_mode) {
118 + Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"),
119 + (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
123 + case 'n': /* number of links */
124 + if (statc.st_nlink != statf.st_nlink) {
125 + Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"),
126 + (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
130 + case 'u': /* user id */
131 + if (statc.st_uid != statf.st_uid) {
132 + Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"),
133 + (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
137 + case 'g': /* group id */
138 + if (statc.st_gid != statf.st_gid) {
139 + Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"),
140 + (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
144 + case 's': /* size */
145 + if (statc.st_size != statf.st_size) {
146 + Jmsg(jcr, M_INFO, 0, _(" st_size differ. Cat: %s File: %s\n"),
147 + edit_uint64((uint64_t)statc.st_size, ed1),
148 + edit_uint64((uint64_t)statf.st_size, ed2));
152 + case 'a': /* access time */
153 + if (statc.st_atime != statf.st_atime) {
154 + Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n"));
159 + if (statc.st_mtime != statf.st_mtime) {
160 + Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n"));
164 + case 'c': /* ctime */
165 + if (statc.st_ctime != statf.st_ctime) {
166 + Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n"));
170 + case 'd': /* file size decrease */
171 + if (statc.st_size > statf.st_size) {
172 + Jmsg(jcr, M_INFO, 0, _(" st_size decrease. Cat: %s File: %s\n"),
173 + edit_uint64((uint64_t)statc.st_size, ed1),
174 + edit_uint64((uint64_t)statf.st_size, ed2));
178 + case '5': /* compare MD5 */
179 + Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
180 + *do_Digest = CRYPTO_DIGEST_MD5;
182 + case '1': /* compare SHA1 */
183 + *do_Digest = CRYPTO_DIGEST_SHA1;
195 + * This function is called at EOJ.
196 + * For a Full backup, we remove old one, and we add all entries
197 + * For an Incremental, we add all entries (delete have been before)
198 + * For a Differential, we add all entries (delete have been before)
203 +bool accurate_update_current_files(JCR *jcr)
207 + if (jcr->accurate == false) {
211 + backupid = db_accurate_find_backupid(jcr, jcr->db, &jcr->jr);
213 + Dmsg1(1, "backupid = %i\n", backupid);
216 + return false; /* something goes wrong */
219 + if (jcr->JobLevel == L_FULL) {
220 + db_accurate_cleanup_currentfile(jcr, jcr->db, backupid);
223 + db_accurate_update_currentfile(jcr, jcr->db, jcr->JobId,
224 + jcr->JobLevel, backupid);
229 + * We are called here for each record that matches the above
230 + * SQL query -- that is for each file contained in the Catalog
231 + * that was not marked earlier. This means that the file in
232 + * question is a missing file (in the Catalog but not on Disk).
234 +static int accurate_handler(void *ctx, int num_fields, char **row)
236 + JCR *jcr = (JCR *)ctx;
238 + if (job_canceled(jcr)) {
241 + if (num_fields == 2) { /* deleted files */
242 + jcr->file_bsock->fsend("D %s%s", row[0]?row[0]:"", row[1]?row[1]:"");
243 + } else if (num_fields == 1) { /* files to backup */
244 + jcr->file_bsock->fsend("S %s", row[0]?row[0]:"");
250 + * Send deleted files and files to backup in accurate mode
253 +static int accurate_send_missing_and_deleted_files(JCR *jcr, JobId_t BackupId)
255 + char buf[MAXSTRING];
256 + char ed1[50], ed2[50];
258 + bsnprintf(buf, sizeof(buf),
259 + "SELECT Path.Path,Filename.Name "
260 + "FROM CurrentFile "
261 + "JOIN File USING (FileId) "
262 + "JOIN Path USING (PathId) "
263 + "JOIN Filename USING (FilenameId) "
264 + "WHERE CurrentFile.BackupId=%s "
265 + "AND CurrentFile.MarkId!=%s ",
266 + edit_uint64(BackupId, ed1), edit_uint64(jcr->JobId, ed2));
267 + /* missing_handler is called for each file found */
268 + Dmsg1(2, "display deleted files cmd=%s\n", buf);
269 + db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
270 + jcr->file_bsock->signal(BNET_EOD);
272 + bsnprintf(buf, sizeof(buf),
273 + "SELECT Name FROM ToBackup%s",
274 + edit_uint64(jcr->JobId, ed2));
275 + /* missing_handler is called for each file found */
276 + Dmsg1(2, "display files to backup cmd=%s\n", buf);
277 + db_sql_query(jcr->db, buf, accurate_handler, (void *)jcr);
278 + jcr->file_bsock->signal(BNET_EOD);
284 + * Accurate backup mode
285 + * 1. Receive the list of all files including those backed up to the Dir
286 + * 2. Dir computes files and deleted files.
287 + * 3. Dir sends list of additional files (new files) to backup, and list of files
290 + * Cleanup attributes (don't use atime, inode etc..)
291 + * Need to insert file and attributes to temp table ?
292 + * Batch compare files and attributes ?
294 + * If file have file_index=0, they are discarded by FD
296 + * TODO: send deleted list and new list to client
297 + * tweak SD with file_index=-1
299 +bool accurate_compute_files(JCR *jcr)
302 + char buf[MAXSTRING];
305 + POOLMEM *fname = get_pool_memory(PM_MESSAGE);
306 + int do_Digest = CRYPTO_DIGEST_NONE;
307 + int32_t file_index = 0;
308 + JobId_t JobId=0; /* TODO: compute the job key in new table */
309 + JobId_t backupid=0;
311 + memset(&fdbr, 0, sizeof(FILE_DBR));
312 + fd = jcr->file_bsock;
313 + fdbr.JobId = JobId;
314 + jcr->FileIndex = 0;
316 + if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
320 + backupid = db_accurate_find_backupid(jcr, jcr->db, &jcr->jr);
322 + Jmsg(jcr, M_ERROR, 0, _("Can't use Accurate mode ERR=Can't find BackupId\n"));
325 + db_accurate_create_tobackup_table(jcr, jcr->db, jcr->JobId);
326 + Dmsg0(1, "bdird: waiting to receive file attributes\n");
328 + * Get Attributes and Signature from File daemon
332 + * Options or Digest (MD5/SHA1)
337 + while ((n=bget_dirmsg(fd)) >= 0 && !job_canceled(jcr)) {
339 + char *attr, *p, *fn;
340 + char Opts_Digest[MAXSTRING]; /* Verify Opts or MD5/SHA1 digest */
342 + if (job_canceled(jcr)) {
345 + fname = check_pool_memory_size(fname, fd->msglen);
346 + jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
347 + Dmsg1(1, "Atts+Digest=%s\n", fd->msg);
348 + if ((len = sscanf(fd->msg, "%ld %d %100s", &file_index, &stream,
350 + Jmsg3(jcr, M_FATAL, 0, _("bird<filed: bad attributes, expected 3 fields got %d\n"
351 +" mslen=%d msg=%s\n"), len, fd->msglen, fd->msg);
355 + * We read the Options or Signature into fname
356 + * to prevent overrun, now copy it to proper location.
358 + bstrncpy(Opts_Digest, fname, sizeof(Opts_Digest));
360 + skip_nonspaces(&p); /* skip FileIndex */
362 + skip_nonspaces(&p); /* skip Stream */
364 + skip_nonspaces(&p); /* skip Opts_Digest */
365 + p++; /* skip space */
368 + *fn++ = *p++; /* copy filename */
370 + *fn = *p++; /* term filename and point to attribs */
373 + * Got attributes stream, decode it
375 + if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
377 + Dmsg2(400, "file_index=%d attr=%s\n", file_index, attr);
379 + jcr->FileIndex = file_index; /* remember attribute file_index */
380 + do_Digest = CRYPTO_DIGEST_NONE;
381 + pm_strcpy(jcr->fname, fname); /* move filename into JCR */
385 + * Find equivalent record in the database
388 + if (db_accurate_get_file_attributes_record(jcr, jcr->db, jcr->fname,
391 + Dmsg2(1, "get_file ok fname=%s fileid=%i\n", jcr->fname, fdbr.FileId);
392 + if (fdbr.MarkId != jcr->JobId) { /* Already visited ? */
393 + if (file_index == 0) { /* file not saved */
394 + changed = accurate_check_file(jcr, &fdbr, attr, Opts_Digest, &do_Digest);
395 + Dmsg1(1, "check_file changed=%i\n", changed);
397 + if (changed == true) {
398 + db_accurate_mark_file_for_backup(jcr, jcr->db, jcr->fname, jcr->JobId);
399 + db_accurate_delete_file_record(jcr, jcr->db, fdbr.FileId, backupid);
401 + db_accurate_mark_file_record(jcr, jcr->db, backupid,
402 + fdbr.FileId, jcr->JobId);
404 + } else { /* file_index != 0 file have be backuped */
405 + db_accurate_delete_file_record(jcr, jcr->db, fdbr.FileId, backupid);
408 + Dmsg2(1, "already saved fname=%s fileid=%i\n", jcr->fname, fdbr.FileId);
410 + } else if (file_index == 0) {
411 + Dmsg1(1, "mark_for_backup fname=%s\n", jcr->fname);
412 + db_accurate_mark_file_for_backup(jcr, jcr->db, jcr->fname, jcr->JobId);
416 + * Got Digest Signature from Storage daemon
417 + * It came across in the Opts_Digest field.
420 + } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
421 + Dmsg2(400, "stream=Digest inx=%d Digest=%s\n", file_index, Opts_Digest);
423 + * When ever we get a digest it MUST have been
424 + * preceded by an attributes record, which sets attr_file_index
426 + if (jcr->FileIndex != (uint32_t)file_index) {
427 + Jmsg2(jcr, M_FATAL, 0, _("MD5/SHA1 index %d not same as attributes %d\n"),
428 + file_index, jcr->FileIndex);
431 + if (do_Digest != CRYPTO_DIGEST_NONE) {
432 + db_escape_string(jcr, jcr->db, buf, Opts_Digest, strlen(Opts_Digest));
433 + if (strcmp(buf, fdbr.Digest) != 0) {
434 + if (debug_level >= 10) {
435 + Jmsg(jcr, M_INFO, 0, _(" %d not same. File=%s Cat=%s\n"),
436 + stream, buf, fdbr.Digest);
438 + Jmsg(jcr, M_INFO, 0, _(" %d differs.\n"),
441 + //stat = JS_Differences;
443 + do_Digest = CRYPTO_DIGEST_NONE;
446 +// jcr->JobFiles = file_index;
448 + if (is_bnet_error(fd)) {
450 + Jmsg2(jcr, M_FATAL, 0, _("bdird<filed: bad attributes from filed n=%d : %s\n"),
451 + n, be.bstrerror());
456 +CREATE VIEW cf AS SELECT path.path || filename.name as filename,
457 + jobid, currentfile.markid, backupid
458 + FROM File join currentfile using (fileid) join filename using (filenameid) join path using (pathid)
461 + accurate_send_missing_and_deleted_files(jcr, backupid);
463 + db_accurate_clean_deleted_files(jcr, jcr->db, jcr->JobId, backupid);
465 + db_accurate_drop_tobackup_table(jcr, jcr->db, jcr->JobId);
467 + free_pool_memory(fname);
471 + db_accurate_drop_tobackup_table(jcr, jcr->db, jcr->JobId);
476 * Do a backup of the specified FileSet
478 * Returns: false on failure
484 + * If backup is in accurate mode, FD will send the list of
485 + * all files. We have to store it, and compute witch files
486 + * have been deleted and witch files have to be backuped.
488 + accurate_compute_files(jcr);
490 /* Pickup Job termination data */
491 stat = wait_for_job_termination(jcr);
492 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
493 + accurate_update_current_files(jcr);
495 if (stat == JS_Terminated) {
496 backup_cleanup(jcr, stat);
498 Index: src/dird/job.c
499 ===================================================================
500 --- src/dird/job.c (révision 6372)
501 +++ src/dird/job.c (copie de travail)
503 jcr->spool_data = job->spool_data;
504 jcr->spool_size = job->spool_size;
505 jcr->write_part_after_job = job->write_part_after_job;
506 + jcr->accurate = job->accurate;
507 if (jcr->RestoreBootstrap) {
508 free(jcr->RestoreBootstrap);
509 jcr->RestoreBootstrap = NULL;
510 Index: src/dird/inc_conf.c
511 ===================================================================
512 --- src/dird/inc_conf.c (révision 6372)
513 +++ src/dird/inc_conf.c (copie de travail)
515 * Items that are valid in an Options resource
517 static RES_ITEM options_items[] = {
518 + {"accurate", store_opts, {0}, 0, 0, 0},
519 {"compression", store_opts, {0}, 0, 0, 0},
520 {"signature", store_opts, {0}, 0, 0, 0},
521 {"verify", store_opts, {0}, 0, 0, 0},
533 * options given above.
535 static struct s_kw FS_option_kw[] = {
536 + {"accurate", INC_KW_ACCURATE},
537 {"compression", INC_KW_COMPRESSION},
538 {"signature", INC_KW_DIGEST},
539 {"encryption", INC_KW_ENCRYPTION},
541 {"no", INC_KW_ENHANCEDWILD, "0"},
542 {"yes", INC_KW_CHKCHANGES, "c"},
543 {"no", INC_KW_CHKCHANGES, "0"},
544 + {"yes", INC_KW_ACCURATE, "C"},
545 + {"no", INC_KW_ACCURATE, "0"},
549 Index: src/dird/dird_conf.c
550 ===================================================================
551 --- src/dird/dird_conf.c (révision 6372)
552 +++ src/dird/dird_conf.c (copie de travail)
554 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
555 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
556 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
557 + {"accurate", store_bool, ITEM(res_job.accurate), 0,0,0},
558 {NULL, NULL, {0}, 0, 0, 0}
562 if (res->res_job.spool_size) {
563 sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1));
565 + if (res->res_job.JobType == JT_BACKUP) {
566 + sendit(sock, _(" Accurate=%d\n"), res->res_job.accurate);
568 if (res->res_job.JobType == JT_MIGRATE) {
569 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
571 Index: src/dird/dird_conf.h
572 ===================================================================
573 --- src/dird/dird_conf.h (révision 6372)
574 +++ src/dird/dird_conf.h (copie de travail)
576 bool write_part_after_job; /* Set to write part after job in SD */
577 bool enabled; /* Set if job enabled */
578 bool OptimizeJobScheduling; /* Set if we should optimize Job scheduling */
579 + bool accurate; /* Set if it is an accurate backup job */
581 MSGS *messages; /* How and where to send messages */
582 SCHED *schedule; /* When -- Automatic schedule */
583 Index: src/filed/backup.c
584 ===================================================================
585 --- src/filed/backup.c (révision 6372)
586 +++ src/filed/backup.c (copie de travail)
588 static bool crypto_session_send(JCR *jcr, BSOCK *sd);
591 + * Called by save_file when accept/discard file for backup
593 + * TODO: we could add MD5/SHAX digest, but we have to compute it
596 +static bool accurate_add_file(JCR *jcr, FF_PKT *ff_pkt, char *stats)
599 + char attribs[MAXSTRING];
600 + uint32_t file_index=jcr->JobFiles;
601 + BSOCK *dir = jcr->dir_bsock;
604 + if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
608 + if (!stats) { /* TODO: don't always compute attribute */
610 + encode_stat(attribs, ff_pkt, 0);
614 + switch (ff_pkt->type) {
615 + case FT_LNKSAVED: /* Hard linked, file already saved */
617 + stat = dir->fsend("%d %d %s %s%c%s%c%s%c", file_index,
618 + STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
619 + 0, a, 0, ff_pkt->link, 0);
627 + stat = dir->fsend("%d %d %s %s%c%s%c%c", file_index,
628 + STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
636 + stat = dir->fsend("%d %d %s %s%c%s%c%c", file_index,
637 + STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->link,
641 + Dmsg2(1, _("Fname=%s Type=%i\n"), ff_pkt->fname, ff_pkt->type);
646 + Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), bnet_strerror(dir));
653 +/* build a fileset with new files from director */
654 +static bool accurate_get_new_and_deleted_file_list(JCR *jcr)
656 + BSOCK *dir = jcr->dir_bsock;
657 + if (jcr->accurate == false || job_canceled(jcr)) {
661 + /* get deleted files */
662 + while (dir->recv() >= 0) {
663 + Dmsg1(1, "deleted = %s\n", dir->msg);
665 + /* get missing files */
666 + while (dir->recv() >= 0) {
667 + Dmsg1(1, "missing = %s\n", dir->msg);
673 +/* send deleted file list to stored */
674 +static bool accurate_send_deleted_list(JCR *jcr)
676 + if (jcr->accurate == false || job_canceled(jcr)) {
682 +static bool accurate_send_file_list(JCR *jcr)
684 + if (jcr->accurate == false || job_canceled(jcr)) {
687 + Dmsg0(1, "Sending BNET_EOD\n");
688 + jcr->dir_bsock->signal(BNET_EOD); /* end of sending data */
694 * Find all the requested files and send them
695 * to the Storage daemon.
700 // TODO landonf: Allow user to specify encryption algorithm
702 sd = jcr->store_bsock;
704 set_jcr_job_status(jcr, JS_Running);
706 ok = false; /* error */
707 set_jcr_job_status(jcr, JS_ErrorTerminated);
709 + Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate);
710 + /* start accurate stuffs */
711 + if (jcr->accurate) {
712 + /* TODO: test job_canceled() */
713 + accurate_send_file_list(jcr); /* send all files to DIR */
714 + accurate_get_new_and_deleted_file_list(jcr); /* get a new incr fileset from DIR */
715 +// set_find_options((FF_PKT *)jcr->ff, 0, 0); /* we backup all that director wants */
716 +// if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file)) {
717 +// ok = false; /* error */
718 +// set_jcr_job_status(jcr, JS_ErrorTerminated);
720 +// accurate_send_file_list(jcr); /* send all new files to DIR */
721 + accurate_send_deleted_list(jcr); /* send deleted list to SD */
724 free_pool_memory(jcr->acl_text);
729 Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
730 + accurate_add_file(jcr, ff_pkt, NULL); /* list skipped files */
733 Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname);
734 + accurate_add_file(jcr, ff_pkt, NULL); /* list skipped files */
738 @@ -1111,6 +1229,9 @@
740 unstrip_path(ff_pkt);
742 + /* list backuped files */
743 + accurate_add_file(jcr, ff_pkt, attribs);
745 Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
747 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
748 Index: src/filed/job.c
749 ===================================================================
750 --- src/filed/job.c (révision 6372)
751 +++ src/filed/job.c (copie de travail)
752 @@ -1087,6 +1087,9 @@
754 fo->flags |= FO_CHKCHANGES;
757 + fo->flags |= FO_ACCURATE;
760 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
762 @@ -1195,6 +1198,9 @@
764 level = get_memory(dir->msglen+1);
765 Dmsg1(110, "level_cmd: %s", dir->msg);
766 + if (strstr(dir->msg, "accurate")) {
767 + jcr->accurate = true;
769 if (sscanf(dir->msg, "level = %s ", level) != 1) {
772 @@ -1204,14 +1210,14 @@
773 /* Full backup requested? */
774 } else if (strcmp(level, "full") == 0) {
775 jcr->JobLevel = L_FULL;
776 - } else if (strcmp(level, "differential") == 0) {
777 + } else if (strstr(level, "differential")) {
778 jcr->JobLevel = L_DIFFERENTIAL;
781 - } else if (strcmp(level, "incremental") == 0) {
782 + } else if (strstr(level, "incremental")) {
783 jcr->JobLevel = L_INCREMENTAL;
788 * We get his UTC since time, then sync the clocks and correct it
789 * to agree with our clock.
790 Index: src/cats/sql_update.c
791 ===================================================================
792 --- src/cats/sql_update.c (révision 6372)
793 +++ src/cats/sql_update.c (copie de travail)
798 +int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId)
801 + char ed1[50], ed2[50];
803 + Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE FileId=%s AND BackupId=%s",
804 + edit_int64(FileId, ed1), edit_int64(BackupId, ed2));
805 + stat = INSERT_DB(jcr, mdb, mdb->cmd);
810 +int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, JobId_t JobId)
815 + /* TODO: mdb->esc_xxx are already ok but it's more smart to recompute it */
816 +// mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*len+2);
817 +// mdb->esc_name = db_escape_string(jcr, mdb, mdb->esc_name, fname, len);
818 + Mmsg(mdb->cmd, "INSERT INTO ToBackup%s (name) VALUES ('%s%s')", edit_int64(JobId, ed1), mdb->esc_path, mdb->esc_name);
819 + stat = INSERT_DB(jcr, mdb, mdb->cmd);
824 +int db_accurate_cleanup_currentfile(JCR *jcr, B_DB *mdb, JobId_t BackupId)
829 + Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE BackupId=%s", edit_int64(BackupId, ed1));
830 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
835 +int db_accurate_update_currentfile(JCR *jcr, B_DB *mdb, JobId_t JobId, int JobLevel, JobId_t BackupId)
838 + char ed1[50], ed2[50], ed3[50];
840 + edit_int64(JobId, ed2);
842 + "INSERT INTO CurrentFile (FileId, BackupId, FullMark, MarkId) "
843 + " (SELECT FileId, %s, '%c', %s FROM File WHERE JobId=%s)",
844 + edit_int64(BackupId, ed1),
845 + JobLevel, ed2, ed2);
846 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
851 +int db_accurate_create_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
856 + Mmsg(mdb->cmd, "CREATE TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
857 +// Mmsg(mdb->cmd, "CREATE TEMPORARY TABLE ToBackup%s (name text)", edit_int64(JobId, ed1));
858 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
863 +int db_accurate_drop_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId)
868 +// Mmsg(mdb->cmd, "DROP TABLE ToBackup%s", edit_int64(JobId, ed1));
869 +// stat = QUERY_DB(jcr, mdb, mdb->cmd);
875 +/* Mark the file record as being visited during database
876 + * accurate compare. Stuff JobId into the MarkId field
878 +int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId)
881 + char ed1[50], ed2[50], ed3[50];
884 + Mmsg(mdb->cmd, "UPDATE CurrentFile SET MarkId=%s WHERE FileId=%s AND BackupId=%s",
885 + edit_int64(JobId, ed1), edit_int64(FileId, ed2), edit_int64(BackupId, ed3));
886 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
887 + if (!stat || sql_affected_rows(mdb) != 1) {
895 * Update the Job record at start of Job
897 Index: src/cats/drop_postgresql_tables.in
898 ===================================================================
899 --- src/cats/drop_postgresql_tables.in (révision 6372)
900 +++ src/cats/drop_postgresql_tables.in (copie de travail)
905 -$bindir/psql -f - -d ${db_name} $* <<END-OF-DATA
906 +$bindir/psql -f - -U regress -d ${db_name} $* <<END-OF-DATA
907 drop table unsavedfiles;
908 drop table basefiles;
910 Index: src/cats/make_postgresql_tables.in
911 ===================================================================
912 --- src/cats/make_postgresql_tables.in (révision 6372)
913 +++ src/cats/make_postgresql_tables.in (copie de travail)
918 -$bindir/psql -f - -d ${db_name} $* <<END-OF-DATA
919 +$bindir/psql -f - -U regress -d ${db_name} $* <<END-OF-DATA
921 CREATE TABLE filename
924 CREATE INDEX file_jobid_idx on file (jobid);
925 CREATE INDEX file_fp_idx on file (filenameid, pathid);
927 +CREATE TABLE CurrentBackupId
929 + BackupId serial not null,
930 + ClientId integer not null,
931 + JobName text not null,
932 + FileSetId integer not null,
933 + primary key (BackupId)
936 +-- Serait bien de prendre la meme table pour
937 +-- les File et le CurrentBackup...
938 +-- Mais y'a des problemes pour les prunes
940 +CREATE TABLE CurrentFile
942 + FileId integer not null,
943 + BackupId integer not null,
944 + FullMark char(1) default 0,
945 + MarkId integer default 0,
946 + primary key (FileId)
949 +CREATE INDEX currentfile_fileid on CurrentFile (BackupId);
951 +-- CREATE TEMPORARY TABLE batch (fileindex int,
958 +-- -- On batch insert dans la table temporaire
960 +-- il faut trouver les fichiers manquant
961 +-- INSERT des nouveaux, UPDATE des anciens, SELECT pour trouver les deletes
964 +-- il faut trouver les fichiers modifies
965 +-- Le champs LStat n'est plus le meme
967 +-- FROM CurrentBackup,
968 +-- batch JOIN Path USING (Path) JOIN Filename USING (Name)
969 +-- WHERE Path.PathId = CurrentBackup.PathId
970 +-- AND Filename.FilenameId = CurrentBackup.FilenameId
971 +-- AND CurrentBackup.LStat != batch.LStat
973 +-- il faut mettre a jour la liste des fichiers
981 -- Possibly add one or more of the following indexes
982 -- if your Verifies are too slow.
983 Index: src/cats/protos.h
984 ===================================================================
985 --- src/cats/protos.h (révision 6372)
986 +++ src/cats/protos.h (copie de travail)
989 int db_delete_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pool_dbr);
990 int db_delete_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
991 +int db_accurate_clean_deleted_files(JCR *jcr, B_DB *mdb, JobId_t JobId, JobId_t BackupId);
994 bool db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime);
995 bool db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr);
996 +JobId_t db_accurate_find_backupid(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
997 int db_find_next_volume(JCR *jcr, B_DB *mdb, int index, bool InChanger, MEDIA_DBR *mr);
998 bool db_find_failed_job_since(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *stime, int &JobLevel);
1001 +int db_accurate_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JobId_t backupid, FILE_DBR *fdbr);
1002 bool db_get_pool_record(JCR *jcr, B_DB *db, POOL_DBR *pdbr);
1003 int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr);
1004 bool db_get_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr);
1005 @@ -129,6 +132,14 @@
1006 int db_update_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
1007 int db_add_digest_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *digest, int type);
1008 int db_mark_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t JobId);
1009 +int db_accurate_mark_file_for_backup(JCR *jcr, B_DB *mdb, char *fname, FileId_t JobId);
1010 +int db_accurate_mark_file_record(JCR *jcr, B_DB *mdb, JobId_t BackupId, FileId_t FileId, JobId_t JobId);
1011 +int db_accurate_drop_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
1012 +int db_accurate_create_tobackup_table(JCR *jcr, B_DB *mdb, JobId_t JobId);
1013 +int db_accurate_delete_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, JobId_t BackupId);
1014 void db_make_inchanger_unique(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr);
1015 +int db_accurate_cleanup_currentfile(JCR *jcr, B_DB *mdb, JobId_t BackupId);
1016 +int db_accurate_update_currentfile(JCR *jcr, B_DB *mdb, JobId_t JobId, int JobLevel, JobId_t BackupId);
1019 #endif /* __SQL_PROTOS_H */
1020 Index: src/cats/sql_find.c
1021 ===================================================================
1022 --- src/cats/sql_find.c (révision 6372)
1023 +++ src/cats/sql_find.c (copie de travail)
1024 @@ -190,7 +190,60 @@
1029 + * Find BackupId of last job that ran. E.g. for
1031 + * Returns: Last backuip
1035 +db_accurate_find_backupid(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
1038 + char ed1[50],ed2[50];
1039 + JobId_t backupid=0;
1041 + /* Find backupid */
1043 + Dmsg2(100, "JobLevel=%d JobType=%d\n", jcr->JobLevel, jcr->JobType);
1045 +"SELECT BackupId FROM CurrentBackupId WHERE JobName='%s' AND "
1046 +"ClientId=%s AND FileSetId=%s ORDER BY BackupId DESC LIMIT 1",
1048 + edit_int64(jr->ClientId, ed1),
1049 + edit_int64(jr->FileSetId, ed2));
1051 + Dmsg1(100, "Query: %s\n", mdb->cmd);
1052 + if (!QUERY_DB(jcr, mdb, mdb->cmd)) {
1056 + if ((row = sql_fetch_row(mdb)) == NULL) {
1057 + sql_free_result(mdb);
1058 + if (jcr->JobLevel == L_FULL) {
1060 + "INSERT INTO CurrentBackupId (JobName, ClientId, FileSetId) VALUES ('%s', %s, %s)",
1061 + jr->Name, ed1, ed2);
1062 + if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
1066 + backupid = sql_insert_id(mdb, NT_("CurrentBackupId"));
1068 + Mmsg1(&mdb->errmsg, _("No Job found for: %s.\n"), mdb->cmd);
1072 + backupid = str_to_int64(row[0]);
1075 + sql_free_result(mdb);
1083 * Find JobId of last job that ran. E.g. for
1084 * VERIFY_CATALOG we want the JobId of the last INIT.
1085 Index: src/cats/sql_delete.c
1086 ===================================================================
1087 --- src/cats/sql_delete.c (révision 6372)
1088 +++ src/cats/sql_delete.c (copie de travail)
1089 @@ -236,5 +236,22 @@
1094 + * Purge delete file from CurrentFile table. This table contains only
1097 +int db_accurate_clean_deleted_files(JCR *jcr, B_DB *mdb, JobId_t JobId, JobId_t BackupId)
1100 + char ed1[50], ed2[50];
1102 + Mmsg(mdb->cmd, "DELETE FROM CurrentFile WHERE MarkId!=%s AND BackupId=%s",
1103 + edit_int64(JobId, ed1), edit_int64(BackupId, ed2));
1104 + stat = QUERY_DB(jcr, mdb, mdb->cmd);
1111 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/
1112 Index: src/cats/sql_create.c
1113 ===================================================================
1114 --- src/cats/sql_create.c (révision 6372)
1115 +++ src/cats/sql_create.c (copie de travail)
1116 @@ -829,6 +829,14 @@
1120 +bool db_accurate_insert(JCR *jcr, B_DB *mdb, bool saved, const char *fname, struct stat *stat)
1123 + split_path_and_file(jcr, mdb, fname);
1124 + /* make like in Verify code */
1129 * Create File record in B_DB
1131 Index: src/cats/sql_get.c
1132 ===================================================================
1133 --- src/cats/sql_get.c (révision 6372)
1134 +++ src/cats/sql_get.c (copie de travail)
1137 * Returns: 0 on failure
1138 * 1 on success with the File record in FILE_DBR
1140 + * TODO: optimize this with only one query
1142 int db_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname, JOB_DBR *jr, FILE_DBR *fdbr)
1149 + * Given a full filename (with path), look up the File record
1150 + * (with attributes) in the database.
1152 + * Returns: 0 on failure
1153 + * 1 on success with the File record in FILE_DBR
1155 +int db_accurate_get_file_attributes_record(JCR *jcr, B_DB *mdb, char *fname,
1156 + JobId_t backupid, FILE_DBR *fdbr)
1163 + split_path_and_file(jcr, mdb, fname);
1165 + mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
1166 + db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
1168 + mdb->esc_path = check_pool_memory_size(mdb->esc_path, 2*mdb->pnl+2);
1169 + db_escape_string(jcr, mdb, mdb->esc_path, mdb->path, mdb->pnl);
1171 + /* A file could be present more than one time in the same backup, so we use LIMIT 1 */
1173 +"SELECT FileId, LStat, MD5, FilenameId, PathId, FileIndex, CurrentFile.MarkId, JobId "
1174 + "FROM File JOIN CurrentFile USING (FileId) "
1175 + "JOIN Filename USING (FilenameId) "
1176 + "JOIN Path USING (PathId) "
1177 + "WHERE Path.Path='%s' "
1178 + "AND Filename.Name='%s' "
1179 + "AND BackupId=%s "
1180 + "ORDER BY FileId DESC LIMIT 1",
1183 + edit_int64(backupid, ed1));
1185 + Dmsg1(2,"get_file %s\n", mdb->cmd);
1187 + if (QUERY_DB(jcr, mdb, mdb->cmd)) {
1189 + mdb->num_rows = sql_num_rows(mdb);
1190 + if (mdb->num_rows == 1) {
1191 + if ((row = sql_fetch_row(mdb)) == NULL) {
1192 + Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
1194 + fdbr->FileId = str_to_int64(row[0]);
1195 + bstrncpy(fdbr->LStat, row[1], sizeof(fdbr->LStat));
1196 + bstrncpy(fdbr->Digest, row[2], sizeof(fdbr->Digest));
1197 + fdbr->FilenameId = str_to_int64(row[3]);
1198 + fdbr->PathId = str_to_int64(row[4]);
1199 + fdbr->FileIndex = str_to_int64(row[5]);
1200 + fdbr->MarkId = str_to_int64(row[6]);
1201 + fdbr->JobId = str_to_int64(row[7]);
1205 + sql_free_result(mdb);
1207 + Mmsg(mdb->errmsg, _("File record: %s not found in Catalog for BackupId=%s.\n"), fname, ed1);
1217 * Returns: 0 on failure
1219 ===================================================================
1220 --- src/jcr.h (révision 6372)
1221 +++ src/jcr.h (copie de travail)
1223 B_DB *db_batch; /* database pointer for batch insert */
1224 ATTR_DBR *ar; /* DB attribute record */
1225 guid_list *id_list; /* User/group id to name list */
1226 + bool accurate; /* true if job is accurate */
1228 void *plugin_ctx_list; /* list of contexts for plugins */
1229 void *plugin_ctx; /* current plugin context */
1230 Index: src/findlib/find.h
1231 ===================================================================
1232 --- src/findlib/find.h (révision 6372)
1233 +++ src/findlib/find.h (copie de travail)
1235 #define FO_ENHANCEDWILD (1<<23) /* Enhanced wild card processing */
1236 #define FO_CHKCHANGES (1<<24) /* Check if file have been modified during backup */
1237 #define FO_STRIPPATH (1<<25) /* Check for stripping path */
1238 +#define FO_ACCURATE (1<<26) /* Accurate mode */
1240 struct s_included_file {
1241 struct s_included_file *next;