2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 Bacula® is a registered trademark of Kern Sibbald.
24 static int dbglvl=100;
26 typedef struct PrivateCurFile {
35 bool accurate_mark_file_as_seen(JCR *jcr, char *fname)
37 if (!jcr->accurate || !jcr->file_list) {
40 /* TODO: just use elt->seen = 1 */
41 CurFile *temp = (CurFile *)jcr->file_list->lookup(fname);
43 temp->seen = 1; /* records are in memory */
44 Dmsg1(dbglvl, "marked <%s> as seen\n", fname);
46 Dmsg1(dbglvl, "<%s> not found to be marked as seen\n", fname);
51 static bool accurate_mark_file_as_seen(JCR *jcr, CurFile *elt)
53 /* TODO: just use elt->seen = 1 */
54 CurFile *temp = (CurFile *)jcr->file_list->lookup(elt->fname);
56 temp->seen = 1; /* records are in memory */
61 static bool accurate_lookup(JCR *jcr, char *fname, CurFile *ret)
66 CurFile *temp = (CurFile *)jcr->file_list->lookup(fname);
68 memcpy(ret, temp, sizeof(CurFile));
70 Dmsg1(dbglvl, "lookup <%s> ok\n", fname);
76 static bool accurate_init(JCR *jcr, int nbfile)
79 jcr->file_list = (htable *)malloc(sizeof(htable));
80 jcr->file_list->init(elt, &elt->link, nbfile);
84 static bool accurate_send_base_file_list(JCR *jcr)
90 int stream = STREAM_UNIX_ATTRIBUTES;
92 if (!jcr->accurate || jcr->getJobLevel() != L_FULL) {
96 if (jcr->file_list == NULL) {
100 ff_pkt = init_find_files();
101 ff_pkt->type = FT_BASE;
103 foreach_htable(elt, jcr->file_list) {
105 Dmsg2(dbglvl, "base file fname=%s seen=%i\n", elt->fname, elt->seen);
106 /* TODO: skip the decode and use directly the lstat field */
107 decode_stat(elt->lstat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */
108 ff_pkt->fname = elt->fname;
109 ff_pkt->statp = statc;
110 encode_and_send_attributes(jcr, ff_pkt, stream);
115 term_find_files(ff_pkt);
120 /* This function is called at the end of backup
121 * We walk over all hash disk element, and we check
124 static bool accurate_send_deleted_list(JCR *jcr)
130 int stream = STREAM_UNIX_ATTRIBUTES;
132 if (!jcr->accurate) {
136 if (jcr->file_list == NULL) {
140 ff_pkt = init_find_files();
141 ff_pkt->type = FT_DELETED;
143 foreach_htable(elt, jcr->file_list) {
144 if (elt->seen || plugin_check_file(jcr, elt->fname)) {
147 Dmsg2(dbglvl, "deleted fname=%s seen=%i\n", elt->fname, elt->seen);
148 /* TODO: skip the decode and use directly the lstat field */
149 decode_stat(elt->lstat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */
150 ff_pkt->fname = elt->fname;
151 ff_pkt->statp.st_mtime = statc.st_mtime;
152 ff_pkt->statp.st_ctime = statc.st_ctime;
153 encode_and_send_attributes(jcr, ff_pkt, stream);
157 term_find_files(ff_pkt);
161 void accurate_free(JCR *jcr)
163 if (jcr->file_list) {
164 jcr->file_list->destroy();
165 free(jcr->file_list);
166 jcr->file_list = NULL;
170 /* Send the deleted or the base file list and cleanup */
171 bool accurate_finish(JCR *jcr)
175 if (jcr->is_canceled()) {
180 if (jcr->is_JobLevel(L_FULL)) {
181 if (!jcr->rerunning) {
182 ret = accurate_send_base_file_list(jcr);
185 ret = accurate_send_deleted_list(jcr);
188 if (jcr->is_JobLevel(L_FULL)) {
189 Jmsg(jcr, M_INFO, 0, _("Space saved with Base jobs: %lld MB\n"),
190 jcr->base_size/(1024*1024));
196 static bool accurate_add_file(JCR *jcr, uint32_t len,
197 char *fname, char *lstat, char *chksum,
203 /* we store CurFile, fname and ctime/mtime in the same chunk
204 * we need one extra byte to handle an empty chksum
206 item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+len+3);
209 /* TODO: see if we can optimize this part with memcpy instead of strcpy */
210 item->fname = (char *)item+sizeof(CurFile);
211 strcpy(item->fname, fname);
213 item->lstat = item->fname+strlen(item->fname)+1;
214 strcpy(item->lstat, lstat);
216 item->chksum = item->lstat+strlen(item->lstat)+1;
217 strcpy(item->chksum, chksum);
219 item->delta_seq = delta;
221 jcr->file_list->insert(item->fname, item);
223 Dmsg4(dbglvl, "add fname=<%s> lstat=%s delta_seq=%i chksum=%s\n",
224 fname, lstat, delta, chksum);
229 * This function is called for each file seen in fileset.
230 * We check in file_list hash if fname have been backuped
231 * the last time. After we can compare Lstat field.
232 * Full Lstat usage have been removed on 6612
234 * Returns: true if file has changed (must be backed up)
235 * false file not changed
237 bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt)
239 int digest_stream = STREAM_NONE;
240 DIGEST *digest = NULL;
249 ff_pkt->delta_seq = 0;
250 ff_pkt->accurate_found = false;
252 if (!jcr->accurate && !jcr->rerunning) {
256 if (!jcr->file_list) {
257 return true; /* Not initialized properly */
262 if (S_ISDIR(ff_pkt->statp.st_mode)) {
263 fname = ff_pkt->link;
265 fname = ff_pkt->fname;
268 if (!accurate_lookup(jcr, fname, &elt)) {
269 Dmsg1(dbglvl, "accurate %s (not found)\n", fname);
274 ff_pkt->accurate_found = true;
275 ff_pkt->delta_seq = elt.delta_seq;
277 decode_stat(elt.lstat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */
279 if (!jcr->rerunning && (jcr->getJobLevel() == L_FULL)) {
280 opts = ff_pkt->BaseJobOpts;
282 opts = ff_pkt->AccurateOpts;
286 * Loop over options supplied by user and verify the
287 * fields he requests.
289 for (char *p=opts; !stat && *p; p++) {
290 char ed1[30], ed2[30];
292 case 'i': /* compare INODEs */
293 if (statc.st_ino != ff_pkt->statp.st_ino) {
294 Dmsg3(dbglvl-1, "%s st_ino differ. Cat: %s File: %s\n",
296 edit_uint64((uint64_t)statc.st_ino, ed1),
297 edit_uint64((uint64_t)ff_pkt->statp.st_ino, ed2));
301 case 'p': /* permissions bits */
302 /* TODO: If something change only in perm, user, group
303 * Backup only the attribute stream
305 if (statc.st_mode != ff_pkt->statp.st_mode) {
306 Dmsg3(dbglvl-1, "%s st_mode differ. Cat: %x File: %x\n",
308 (uint32_t)statc.st_mode, (uint32_t)ff_pkt->statp.st_mode);
312 case 'n': /* number of links */
313 if (statc.st_nlink != ff_pkt->statp.st_nlink) {
314 Dmsg3(dbglvl-1, "%s st_nlink differ. Cat: %d File: %d\n",
316 (uint32_t)statc.st_nlink, (uint32_t)ff_pkt->statp.st_nlink);
320 case 'u': /* user id */
321 if (statc.st_uid != ff_pkt->statp.st_uid) {
322 Dmsg3(dbglvl-1, "%s st_uid differ. Cat: %u File: %u\n",
324 (uint32_t)statc.st_uid, (uint32_t)ff_pkt->statp.st_uid);
328 case 'g': /* group id */
329 if (statc.st_gid != ff_pkt->statp.st_gid) {
330 Dmsg3(dbglvl-1, "%s st_gid differ. Cat: %u File: %u\n",
332 (uint32_t)statc.st_gid, (uint32_t)ff_pkt->statp.st_gid);
337 if (statc.st_size != ff_pkt->statp.st_size) {
338 Dmsg3(dbglvl-1, "%s st_size differ. Cat: %s File: %s\n",
340 edit_uint64((uint64_t)statc.st_size, ed1),
341 edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
345 case 'a': /* access time */
346 if (statc.st_atime != ff_pkt->statp.st_atime) {
347 Dmsg1(dbglvl-1, "%s st_atime differs\n", fname);
351 case 'm': /* modification time */
352 if (statc.st_mtime != ff_pkt->statp.st_mtime) {
353 Dmsg1(dbglvl-1, "%s st_mtime differs\n", fname);
357 case 'c': /* ctime */
358 if (statc.st_ctime != ff_pkt->statp.st_ctime) {
359 Dmsg1(dbglvl-1, "%s st_ctime differs\n", fname);
363 case 'd': /* file size decrease */
364 if (statc.st_size > ff_pkt->statp.st_size) {
365 Dmsg3(dbglvl-1, "%s st_size decrease. Cat: %s File: %s\n",
367 edit_uint64((uint64_t)statc.st_size, ed1),
368 edit_uint64((uint64_t)ff_pkt->statp.st_size, ed2));
372 case 'A': /* Always backup a file */
375 /* TODO: cleanup and factorise this function with verify.c */
376 case '5': /* compare MD5 */
377 case '1': /* compare SHA1 */
379 * The remainder of the function is all about getting the checksum.
380 * First we initialise, then we read files, other streams and Finder Info.
382 if (!stat && ff_pkt->type != FT_LNKSAVED &&
383 (S_ISREG(ff_pkt->statp.st_mode) &&
384 ff_pkt->flags & (FO_MD5|FO_SHA1|FO_SHA256|FO_SHA512)))
387 if (!*elt.chksum && !jcr->rerunning) {
388 Jmsg(jcr, M_WARNING, 0, _("Cannot verify checksum for %s\n"),
395 * Create our digest context. If this fails, the digest will be set
396 * to NULL and not used.
398 if (ff_pkt->flags & FO_MD5) {
399 digest = crypto_digest_new(jcr, CRYPTO_DIGEST_MD5);
400 digest_stream = STREAM_MD5_DIGEST;
402 } else if (ff_pkt->flags & FO_SHA1) {
403 digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA1);
404 digest_stream = STREAM_SHA1_DIGEST;
406 } else if (ff_pkt->flags & FO_SHA256) {
407 digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA256);
408 digest_stream = STREAM_SHA256_DIGEST;
410 } else if (ff_pkt->flags & FO_SHA512) {
411 digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA512);
412 digest_stream = STREAM_SHA512_DIGEST;
415 /* Did digest initialization fail? */
416 if (digest_stream != STREAM_NONE && digest == NULL) {
417 Jmsg(jcr, M_WARNING, 0, _("%s digest initialization failed\n"),
418 stream_to_ascii(digest_stream));
421 /* compute MD5 or SHA1 hash */
423 char md[CRYPTO_DIGEST_MAX_SIZE];
428 if (digest_file(jcr, ff_pkt, digest) != 0) {
431 } else if (crypto_digest_finalize(digest, (uint8_t *)md, &size)) {
433 const char *digest_name;
435 digest_buf = (char *)malloc(BASE64_SIZE(size));
436 digest_name = crypto_digest_name(digest);
438 bin_to_base64(digest_buf, BASE64_SIZE(size), md, size, true);
440 if (strcmp(digest_buf, elt.chksum)) {
441 Dmsg4(dbglvl,"%s %s chksum diff. Cat: %s File: %s\n",
451 crypto_digest_free(digest);
464 /* In Incr/Diff accurate mode, we mark all files as seen
465 * When in Full+Base mode, we mark only if the file match exactly
467 if (jcr->getJobLevel() == L_FULL) {
469 /* compute space saved with basefile */
470 jcr->base_size += ff_pkt->statp.st_size;
471 accurate_mark_file_as_seen(jcr, &elt);
474 accurate_mark_file_as_seen(jcr, &elt);
478 unstrip_path(ff_pkt);
483 * TODO: use big buffer from htable
485 int accurate_cmd(JCR *jcr)
487 BSOCK *dir = jcr->dir_bsock;
488 int lstat_pos, chksum_pos;
492 if (job_canceled(jcr)) {
495 if (sscanf(dir->msg, "accurate files=%ld", &nb) != 1) {
496 dir->fsend(_("2991 Bad accurate command\n"));
500 jcr->accurate = true;
502 accurate_init(jcr, nb);
505 * buffer = sizeof(CurFile) + dirmsg
506 * dirmsg = fname + \0 + lstat + \0 + checksum + \0 + delta_seq + \0
508 /* get current files */
509 while (dir->recv() >= 0) {
510 lstat_pos = strlen(dir->msg) + 1;
511 if (lstat_pos < dir->msglen) {
512 chksum_pos = lstat_pos + strlen(dir->msg + lstat_pos) + 1;
514 if (chksum_pos >= dir->msglen) {
515 chksum_pos = lstat_pos - 1; /* tweak: no checksum, point to the last \0 */
518 delta_seq = str_to_int32(dir->msg +
520 strlen(dir->msg + chksum_pos) + 1);
523 accurate_add_file(jcr, dir->msglen,
525 dir->msg + lstat_pos, /* LStat */
526 dir->msg + chksum_pos, /* CheckSum */
527 delta_seq); /* Delta Sequence */
532 extern void *start_heap;
534 char b1[50], b2[50], b3[50], b4[50], b5[50];
535 Dmsg5(dbglvl," Heap: heap=%s smbytes=%s max_bytes=%s bufs=%s max_bufs=%s\n",
536 edit_uint64_with_commas((char *)sbrk(0)-(char *)start_heap, b1),
537 edit_uint64_with_commas(sm_bytes, b2),
538 edit_uint64_with_commas(sm_max_bytes, b3),
539 edit_uint64_with_commas(sm_buffers, b4),
540 edit_uint64_with_commas(sm_max_buffers, b5));