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, "13");
89 +bool send_accurate_current_files(JCR *jcr)
91 + char buf[MAXSTRING];
92 + char ed1[50], ed2[50];
94 + if (jcr->accurate == false || job_canceled(jcr) || jcr->JobLevel == L_FULL) {
98 + POOLMEM *jobids = get_pool_memory(PM_FNAME);
99 + db_accurate_get_jobids(jcr, jobids);
101 + bsnprintf(buf, sizeof(buf),
102 + "CREATE TEMPORARY TABLE btemp2%s AS ( "
103 + "SELECT max(FileId) as FileId, PathId, FilenameId "
104 + "FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (%s)) AS F "
105 + "GROUP BY PathId, FilenameId ) ",
106 + edit_uint64(jcr->JobId, ed1),
108 + db_sql_query(jcr->db, buf, NULL, NULL);
110 + // TODO: compter le nombre de rows
111 + jcr->file_bsock->fsend("accurate files=%s\n", edit_uint64(5895, ed2)); /* TODO: change protocol to something like nb= */
113 + bsnprintf(buf, sizeof(buf),
114 + "SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat "
115 + "FROM btemp2%s JOIN Path USING (PathId) JOIN Filename USING (FilenameId) "
116 + "JOIN File USING (FileId) "
117 + "WHERE File.FileIndex > 0",
119 + db_sql_query(jcr->db, buf, accurate_list_handler, (void *)jcr);
121 + bsnprintf(buf, sizeof(buf), "DROP TABLE btemp2%s", ed1);
122 + free_pool_memory(jobids);
125 + CREATE TEMPORARY TABLE btemp2 AS (
126 + SELECT max(FileId) as FileId, PathId, FilenameId
127 + FROM (SELECT FileId, PathId, FilenameId FROM File WHERE JobId IN (39867,40341)) AS F
128 + GROUP BY PathId, FilenameId )
130 + SELECT File.FileIndex, Path.Path, Filename.Name, File.LStat
131 + FROM btemp2 JOIN Path USING (PathId) JOIN Filename USING (FilenameId)
132 + JOIN File USING (FileId)
133 + WHERE File.FileIndex > 0
138 +SELECT DISTINCT ON (PathId, FilenameId) FileIndex, Path, Name, LStat
139 + FROM File JOIN Filename USING (FilenameId) JOIN Path USING (PathId) WHERE JobId IN (40341)
140 + ORDER BY PathId, FilenameId, JobId DESC
143 + jcr->file_bsock->signal(BNET_EOD);
148 * Do a backup of the specified FileSet
150 * Returns: false on failure
152 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
156 + * If backup is in accurate mode, FD will send the list of
159 + if (!send_accurate_current_files(jcr)) {
163 /* Send backup command */
164 fd->fsend(backupcmd);
165 if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
167 /* Pickup Job termination data */
168 stat = wait_for_job_termination(jcr);
169 db_write_batch_file_records(jcr); /* used by bulk batch file insert */
171 if (stat == JS_Terminated) {
172 backup_cleanup(jcr, stat);
174 Index: src/dird/inc_conf.c
175 ===================================================================
176 --- src/dird/inc_conf.c (révision 6443)
177 +++ src/dird/inc_conf.c (copie de travail)
179 * Items that are valid in an Options resource
181 static RES_ITEM options_items[] = {
182 + {"accurate", store_opts, {0}, 0, 0, 0},
183 {"compression", store_opts, {0}, 0, 0, 0},
184 {"signature", store_opts, {0}, 0, 0, 0},
185 {"verify", store_opts, {0}, 0, 0, 0},
197 * options given above.
199 static struct s_kw FS_option_kw[] = {
200 + {"accurate", INC_KW_ACCURATE},
201 {"compression", INC_KW_COMPRESSION},
202 {"signature", INC_KW_DIGEST},
203 {"encryption", INC_KW_ENCRYPTION},
205 {"no", INC_KW_ENHANCEDWILD, "0"},
206 {"yes", INC_KW_CHKCHANGES, "c"},
207 {"no", INC_KW_CHKCHANGES, "0"},
208 + {"yes", INC_KW_ACCURATE, "C"},
209 + {"no", INC_KW_ACCURATE, "0"},
213 Index: src/filed/backup.c
214 ===================================================================
215 --- src/filed/backup.c (révision 6443)
216 +++ src/filed/backup.c (copie de travail)
218 static bool crypto_session_start(JCR *jcr);
219 static void crypto_session_end(JCR *jcr);
220 static bool crypto_session_send(JCR *jcr, BSOCK *sd);
221 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname);
223 +#include "lib/htable.c"
224 +typedef struct CurFile {
231 + * This function is called for each file seen in fileset.
233 + * If the file is skipped (attr=NULL), we will check if this
234 + * file have been backuped before. If not, we decide to backup it.
236 + * If the file have attr=AAA, we check attr with lstat
239 +/* TODO: tweak verify code to use the same function */
240 +bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt, bool saved)
244 + struct stat statc; /* catalog stat */
245 + char *Opts_Digest = ff_pkt->VerifyOpts;
246 + char *fname = ff_pkt->fname;
251 + if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
255 + if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_DIRNOCHG) {
256 + fname = ff_pkt->link;
259 + // TODO: check for /path/ and /path/file
260 + elt = (CurFile *) jcr->file_list->lookup(fname);
263 + // TODO: we must backup it !
264 + Dmsg1(1, "accurate %s = yes (not found)\n", fname);
268 + if (saved || *elt->lstat == '\0') {
269 + Dmsg1(1, "accurate %s = no (already seen)\n", fname);
270 + *elt->lstat = '\0';
274 + decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */
275 +// *do_Digest = CRYPTO_DIGEST_NONE;
277 + for (p=Opts_Digest; *p; p++) {
278 + char ed1[30], ed2[30];
280 + case 'i': /* compare INODEs */
281 + if (statc.st_ino != ff_pkt->statp.st_ino) {
282 + Jmsg(jcr, M_INFO, 0, _(" st_ino differ. Cat: %s File: %s\n"),
283 + edit_uint64((uint64_t)statc.st_ino, ed1),
284 + edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2));
288 + case 'p': /* permissions bits */
289 + if (statc.st_mode != ff_pkt->statp.st_mode) {
290 + Jmsg(jcr, M_INFO, 0, _(" st_mode differ. Cat: %x File: %x\n"),
291 + (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode);
295 + case 'n': /* number of links */
296 + if (statc.st_nlink != ff_pkt->statp.st_nlink) {
297 + Jmsg(jcr, M_INFO, 0, _(" st_nlink differ. Cat: %d File: %d\n"),
298 + (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink);
302 + case 'u': /* user id */
303 + if (statc.st_uid != ff_pkt->statp.st_uid) {
304 + Jmsg(jcr, M_INFO, 0, _(" st_uid differ. Cat: %u File: %u\n"),
305 + (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid);
309 + case 'g': /* group id */
310 + if (statc.st_gid != ff_pkt->statp.st_gid) {
311 + Jmsg(jcr, M_INFO, 0, _(" st_gid differ. Cat: %u File: %u\n"),
312 + (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid);
316 + case 's': /* size */
317 + if (statc.st_size != ff_pkt->statp.st_size) {
318 + Jmsg(jcr, M_INFO, 0, _(" st_size differ. Cat: %s File: %s\n"),
319 + edit_uint64((uint64_t)statc.st_size, ed1),
320 + edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
324 + case 'a': /* access time */
325 + if (statc.st_atime != ff_pkt->statp.st_atime) {
326 + Jmsg(jcr, M_INFO, 0, _(" st_atime differs\n"));
331 + if (statc.st_mtime != ff_pkt->statp.st_mtime) {
332 + Jmsg(jcr, M_INFO, 0, _(" st_mtime differs\n"));
336 + case 'c': /* ctime */
337 + if (statc.st_ctime != ff_pkt->statp.st_ctime) {
338 + Jmsg(jcr, M_INFO, 0, _(" st_ctime differs\n"));
342 + case 'd': /* file size decrease */
343 + if (statc.st_size > ff_pkt->statp.st_size) {
344 + Jmsg(jcr, M_INFO, 0, _(" st_size decrease. Cat: %s File: %s\n"),
345 + edit_uint64((uint64_t)statc.st_size, ed1),
346 + edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
350 + case '5': /* compare MD5 */
351 + Dmsg1(500, "set Do_MD5 for %s\n", ff_pkt->fname);
352 +// *do_Digest = CRYPTO_DIGEST_MD5;
354 + case '1': /* compare SHA1 */
355 +// *do_Digest = CRYPTO_DIGEST_SHA1;
363 + *elt->lstat = '\0'; /* mark it as deleted */
364 + Dmsg2(1, "accurate %s = %i\n", fname, stat);
368 +int accurate_get_current_file_list_cmd(JCR *jcr)
370 + BSOCK *dir = jcr->dir_bsock;
375 + if (jcr->accurate == false || job_canceled(jcr) || jcr->JobLevel == L_FULL) {
379 + if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
380 + dir->fsend(_("2991 Bad accurate command\n"));
384 + jcr->file_list = (htable *)malloc(sizeof(htable));
385 + jcr->file_list->init(elt, &elt->link, nb);
387 + /* get current files */
388 + while (dir->recv() >= 0) {
389 + len = strlen(dir->msg);
390 + if ((len+1) < dir->msglen) {
391 + elt = (CurFile *)malloc(sizeof(CurFile));
392 + elt->fname = (char *) malloc(dir->msglen+1);
393 + memcpy(elt->fname, dir->msg, dir->msglen);
394 + elt->fname[dir->msglen]='\0';
395 + elt->lstat = elt->fname + len + 1;
396 + Dmsg2(100, "hash[%s]=%s\n", elt->fname, elt->lstat);
397 + jcr->file_list->insert(elt->fname, elt);
401 + jcr->file_list->stats();
402 +// dir->fsend("2000 OK accurate\n");
407 +bool accurate_send_deleted_list(JCR *jcr)
409 + if (jcr->accurate == false || jcr->JobLevel == L_FULL) {
414 + foreach_htable (elt, jcr->file_list) {
415 + Dmsg3(100, "elt = 0x%x fname=%s lstat=%s\n", elt, elt->fname, elt->lstat);
416 + if (*elt->lstat != '\0') {
417 + encode_and_send_deleted_file(jcr, elt->fname);
421 + jcr->file_list->destroy(); /* TODO: clean htable when this function is not reached ? */
422 + free(jcr->file_list);
423 + jcr->file_list = NULL;
428 * Find all the requested files and send them
429 * to the Storage daemon.
434 // TODO landonf: Allow user to specify encryption algorithm
436 sd = jcr->store_bsock;
438 set_jcr_job_status(jcr, JS_Running);
440 ok = false; /* error */
441 set_jcr_job_status(jcr, JS_ErrorTerminated);
443 + Dmsg1(1, "jcr->accurate == %i\n", jcr->accurate);
445 + accurate_send_deleted_list(jcr); /* send deleted list to SD */
447 free_pool_memory(jcr->acl_text);
449 stop_heartbeat_monitor(jcr);
454 + /* TODO: in accurate mode, we have to change NOCHG attribute to FT_REG... */
455 +// if (!accurate_check_file(jcr, ff_pkt, false)) {
456 +// Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
459 Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
462 + /* TODO: in accurate mode, we have to change NOCHG attribute to FT_REG... */
463 +// if (!accurate_check_file(jcr, ff_pkt, false)) {
464 +// Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname);
467 Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname);
470 @@ -1118,6 +1335,9 @@
472 unstrip_path(ff_pkt);
474 + /* list backuped files */
475 + accurate_check_file(jcr, ff_pkt, true);
477 Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
479 Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
480 @@ -1128,6 +1348,58 @@
484 +static bool encode_and_send_deleted_file(JCR *jcr, char *fname)
486 + BSOCK *sd = jcr->store_bsock;
490 +#ifdef FD_NO_SEND_TEST
498 + * Send Attributes header to Storage daemon
499 + * <file-index> <stream> <info>
501 + if (!sd->fsend("%ld %d 0", 0, STREAM_UNIX_ATTRIBUTES)) {
502 + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
506 + Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
509 + * Send file attributes to Storage daemon
512 + * Filename (full path)
513 + * Encoded attributes
514 + * Link name (if type==FT_LNK or FT_LNKSAVED)
515 + * Encoded extended-attributes (for Win32)
517 + * For a directory, link is the same as fname, but with trailing
518 + * slash. For a linked file, link is the link.
520 + stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c",
522 + FT_NOSTAT /* FileType */,
523 + fname /* FileName */,
524 + 0, attribs, 0, 0, 0, attribsEx, 0);
526 + Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
528 + Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
532 + sd->signal(BNET_EOD); /* indicate end of attributes data */
537 * Do in place strip of path
539 Index: src/filed/job.c
540 ===================================================================
541 --- src/filed/job.c (révision 6443)
542 +++ src/filed/job.c (copie de travail)
544 /* Imported functions */
545 extern int status_cmd(JCR *jcr);
546 extern int qstatus_cmd(JCR *jcr);
547 +extern int accurate_get_current_file_list_cmd(JCR *jcr);
549 /* Forward referenced functions */
550 static int backup_cmd(JCR *jcr);
552 {"RunBeforeJob", runbefore_cmd, 0},
553 {"RunAfterJob", runafter_cmd, 0},
554 {"Run", runscript_cmd, 0},
555 + {"accurate", accurate_get_current_file_list_cmd, 0},
556 {NULL, NULL} /* list terminator */
559 @@ -1087,6 +1089,9 @@
561 fo->flags |= FO_CHKCHANGES;
564 + fo->flags |= FO_ACCURATE;
567 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
569 @@ -1195,6 +1200,9 @@
571 level = get_memory(dir->msglen+1);
572 Dmsg1(110, "level_cmd: %s", dir->msg);
573 + if (strstr(dir->msg, "accurate")) {
574 + jcr->accurate = true;
576 if (sscanf(dir->msg, "level = %s ", level) != 1) {
579 @@ -1204,14 +1212,14 @@
580 /* Full backup requested? */
581 } else if (strcmp(level, "full") == 0) {
582 jcr->JobLevel = L_FULL;
583 - } else if (strcmp(level, "differential") == 0) {
584 + } else if (strstr(level, "differential")) {
585 jcr->JobLevel = L_DIFFERENTIAL;
588 - } else if (strcmp(level, "incremental") == 0) {
589 + } else if (strstr(level, "incremental")) {
590 jcr->JobLevel = L_INCREMENTAL;
595 * We get his UTC since time, then sync the clocks and correct it
596 * to agree with our clock.
597 Index: src/stored/append.c
598 ===================================================================
599 --- src/stored/append.c (révision 6443)
600 +++ src/stored/append.c (copie de travail)
603 /* Read Stream header from the File daemon.
604 * The stream header consists of the following:
605 - * file_index (sequential Bacula file index, base 1)
606 + * file_index (sequential Bacula file index, base 1, 0 for deleted files)
607 * stream (Bacula number to distinguish parts of data)
608 * info (Info for Storage daemon -- compressed, encryped, ...)
609 * info is not currently used, so is read, but ignored!
610 @@ -185,16 +185,18 @@
612 Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);
614 - if (!(file_index > 0 && (file_index == last_file_index ||
615 - file_index == last_file_index + 1))) {
616 - Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
619 + if (file_index != 0) { /* TODO: handle file_index == 0 */
620 + if (!(file_index > 0 && (file_index == last_file_index ||
621 + file_index == last_file_index + 1))) {
622 + Jmsg0(jcr, M_FATAL, 0, _("File index from FD not positive or sequential\n"));
626 + if (file_index != last_file_index) {
627 + jcr->JobFiles = file_index;
628 + last_file_index = file_index;
631 - if (file_index != last_file_index) {
632 - jcr->JobFiles = file_index;
633 - last_file_index = file_index;
636 /* Read data stream from the File daemon.
637 * The data stream is just raw bytes
638 @@ -212,25 +214,26 @@
639 stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
642 - while (!write_record_to_block(dcr->block, &rec)) {
643 - Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
645 - if (!write_block_to_device(dcr)) {
646 - Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
647 - dev->print_name(), dev->bstrerror());
653 - Dmsg0(400, "Not OK\n");
656 - jcr->JobBytes += rec.data_len; /* increment bytes this job */
657 - Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
658 - FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
659 - stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
660 + while (!write_record_to_block(dcr->block, &rec)) {
661 + Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
663 + if (!write_block_to_device(dcr)) {
664 + Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
665 + dev->print_name(), dev->bstrerror());
671 + Dmsg0(400, "Not OK\n");
674 + jcr->JobBytes += rec.data_len; /* increment bytes this job */
675 + Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
676 + FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
677 + stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);
680 /* Send attributes and digest to Director for Catalog */
681 if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX ||
682 crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
684 ===================================================================
685 --- src/jcr.h (révision 6443)
686 +++ src/jcr.h (copie de travail)
689 /* Forward referenced structures */
696 CRYPTO_CTX crypto; /* Crypto ctx */
697 DIRRES* director; /* Director resource */
698 bool VSS; /* VSS used by FD */
699 + htable *file_list; /* Previous file list (accurate mode) */
700 #endif /* FILE_DAEMON */
703 Index: src/findlib/find.h
704 ===================================================================
705 --- src/findlib/find.h (révision 6443)
706 +++ src/findlib/find.h (copie de travail)
708 #define FO_ENHANCEDWILD (1<<23) /* Enhanced wild card processing */
709 #define FO_CHKCHANGES (1<<24) /* Check if file have been modified during backup */
710 #define FO_STRIPPATH (1<<25) /* Check for stripping path */
711 +#define FO_ACCURATE (1<<26) /* Accurate mode */
713 struct s_included_file {
714 struct s_included_file *next;