X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Ffiled%2Faccurate.c;h=404756aa6c20e16e42b1880c2848877abc97e145;hb=f5e7d95ce741ea5c296d605b2f411805a65462d6;hp=1541fa889d7684ad60f7f698fc9b1fb5d62e3e4b;hpb=a7b934393e3722d1b5c67be8e9b579979846cbe0;p=bacula%2Fbacula diff --git a/bacula/src/filed/accurate.c b/bacula/src/filed/accurate.c index 1541fa889d..404756aa6c 100644 --- a/bacula/src/filed/accurate.c +++ b/bacula/src/filed/accurate.c @@ -1,12 +1,12 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2009 Free Software Foundation Europe e.V. + Copyright (C) 2000-2011 Free Software Foundation Europe e.V. The main author of Bacula is Kern Sibbald, with contributions from many others, a complete list can be found in the file AUTHORS. This program is Free Software; you can redistribute it and/or - modify it under the terms of version two of the GNU General Public + modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. @@ -15,7 +15,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Affero General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -39,6 +39,8 @@ typedef struct PrivateCurFile { hlink link; char *fname; char *lstat; + char *chksum; + int32_t delta_seq; bool seen; } CurFile; @@ -99,7 +101,7 @@ static bool accurate_send_base_file_list(JCR *jcr) FF_PKT *ff_pkt; int stream = STREAM_UNIX_ATTRIBUTES; - if (!jcr->accurate || jcr->get_JobLevel() != L_FULL) { + if (!jcr->accurate || jcr->getJobLevel() != L_FULL) { return true; } @@ -114,7 +116,7 @@ static bool accurate_send_base_file_list(JCR *jcr) if (elt->seen) { Dmsg2(dbglvl, "base file fname=%s seen=%i\n", elt->fname, elt->seen); /* TODO: skip the decode and use directly the lstat field */ - decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */ + decode_stat(elt->lstat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */ ff_pkt->fname = elt->fname; ff_pkt->statp = statc; encode_and_send_attributes(jcr, ff_pkt, stream); @@ -156,7 +158,7 @@ static bool accurate_send_deleted_list(JCR *jcr) } Dmsg2(dbglvl, "deleted fname=%s seen=%i\n", elt->fname, elt->seen); /* TODO: skip the decode and use directly the lstat field */ - decode_stat(elt->lstat, &statc, &LinkFIc); /* decode catalog stat */ + decode_stat(elt->lstat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */ ff_pkt->fname = elt->fname; ff_pkt->statp.st_mtime = statc.st_mtime; ff_pkt->statp.st_ctime = statc.st_ctime; @@ -180,40 +182,58 @@ void accurate_free(JCR *jcr) /* Send the deleted or the base file list and cleanup */ bool accurate_finish(JCR *jcr) { - bool ret=true; + bool ret = true; + + if (jcr->is_canceled() || jcr->is_incomplete()) { + accurate_free(jcr); + return ret; + } if (jcr->accurate) { - if (jcr->get_JobLevel() == L_FULL) { - ret = accurate_send_base_file_list(jcr); + if (jcr->is_JobLevel(L_FULL)) { + if (!jcr->rerunning) { + ret = accurate_send_base_file_list(jcr); + } } else { ret = accurate_send_deleted_list(jcr); } - accurate_free(jcr); - if (jcr->get_JobLevel() == L_FULL) { - Dmsg1(0, "Space saved with Base jobs: %lld MB\n", - jcr->base_size/(1024*1024)); + if (jcr->is_JobLevel(L_FULL)) { + Jmsg(jcr, M_INFO, 0, _("Space saved with Base jobs: %lld MB\n"), + jcr->base_size/(1024*1024)); } } return ret; } -static bool accurate_add_file(JCR *jcr, char *fname, char *lstat) +static bool accurate_add_file(JCR *jcr, uint32_t len, + char *fname, char *lstat, char *chksum, + int32_t delta) { bool ret = true; - CurFile elt; - elt.seen = 0; - CurFile *item; - /* we store CurFile, fname and ctime/mtime in the same chunk */ - item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+strlen(fname)+strlen(lstat)+2); - memcpy(item, &elt, sizeof(CurFile)); + + /* we store CurFile, fname and ctime/mtime in the same chunk + * we need one extra byte to handle an empty chksum + */ + item = (CurFile *)jcr->file_list->hash_malloc(sizeof(CurFile)+len+1); + item->seen = 0; + + /* TODO: see if we can optimize this part with memcpy instead of strcpy */ item->fname = (char *)item+sizeof(CurFile); strcpy(item->fname, fname); + item->lstat = item->fname+strlen(item->fname)+1; strcpy(item->lstat, lstat); + + item->chksum = item->lstat+strlen(item->lstat)+1; + strcpy(item->chksum, chksum); + + item->delta_seq = delta; + jcr->file_list->insert(item->fname, item); - Dmsg2(dbglvl, "add fname=<%s> lstat=%s\n", fname, lstat); + Dmsg4(dbglvl, "add fname=<%s> lstat=%s delta_seq=%i chksum=%s\n", + fname, lstat, delta, chksum); return ret; } @@ -228,13 +248,19 @@ static bool accurate_add_file(JCR *jcr, char *fname, char *lstat) */ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt) { + int digest_stream = STREAM_NONE; + DIGEST *digest = NULL; + struct stat statc; int32_t LinkFIc; bool stat = false; + char *opts; char *fname; CurFile elt; - if (!jcr->accurate) { + ff_pkt->delta_seq = 0; + + if (!jcr->accurate && !jcr->rerunning) { return true; } @@ -252,19 +278,26 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt) goto bail_out; } + ff_pkt->delta_seq = elt.delta_seq; + if (elt.seen) { /* file has been seen ? */ Dmsg1(dbglvl, "accurate %s (already seen)\n", fname); goto bail_out; } - decode_stat(elt.lstat, &statc, &LinkFIc); /* decode catalog stat */ + decode_stat(elt.lstat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */ + + if (!jcr->rerunning && (jcr->getJobLevel() == L_FULL)) { + opts = ff_pkt->BaseJobOpts; + } else { + opts = ff_pkt->AccurateOpts; + } -//#if 0 /* * Loop over options supplied by user and verify the * fields he requests. */ - for (char *p=ff_pkt->AccurateOpts; *p; p++) { + for (char *p=opts; !stat && *p; p++) { char ed1[30], ed2[30]; switch (*p) { case 'i': /* compare INODEs */ @@ -277,6 +310,9 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt) } break; case 'p': /* permissions bits */ + /* TODO: If something change only in perm, user, group + * Backup only the attribute stream + */ if (statc.st_mode != ff_pkt->statp.st_mode) { Dmsg3(dbglvl-1, "%s st_mode differ. Cat: %x File: %x\n", fname, @@ -323,7 +359,7 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt) stat = true; } break; - case 'm': + case 'm': /* modification time */ if (statc.st_mtime != ff_pkt->statp.st_mtime) { Dmsg1(dbglvl-1, "%s st_mtime differs\n", fname); stat = true; @@ -331,7 +367,7 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt) break; case 'c': /* ctime */ if (statc.st_ctime != ff_pkt->statp.st_ctime) { - Dmsg1(dbglvl-1, " st_ctime differs\n", fname); + Dmsg1(dbglvl-1, "%s st_ctime differs\n", fname); stat = true; } break; @@ -344,44 +380,102 @@ bool accurate_check_file(JCR *jcr, FF_PKT *ff_pkt) stat = true; } break; - case '5': /* compare MD5 */ + case 'A': /* Always backup a file */ + stat = true; break; - case '1': /* compare SHA1 */ + /* TODO: cleanup and factorise this function with verify.c */ + case '5': /* compare MD5 */ + case '1': /* compare SHA1 */ + /* + * The remainder of the function is all about getting the checksum. + * First we initialise, then we read files, other streams and Finder Info. + */ + if (!stat && ff_pkt->type != FT_LNKSAVED && + (S_ISREG(ff_pkt->statp.st_mode) && + ff_pkt->flags & (FO_MD5|FO_SHA1|FO_SHA256|FO_SHA512))) + { + + if (!*elt.chksum && !jcr->rerunning) { + Jmsg(jcr, M_WARNING, 0, _("Cannot verify checksum for %s\n"), + ff_pkt->fname); + stat = true; + break; + } + + /* + * Create our digest context. If this fails, the digest will be set + * to NULL and not used. + */ + if (ff_pkt->flags & FO_MD5) { + digest = crypto_digest_new(jcr, CRYPTO_DIGEST_MD5); + digest_stream = STREAM_MD5_DIGEST; + + } else if (ff_pkt->flags & FO_SHA1) { + digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA1); + digest_stream = STREAM_SHA1_DIGEST; + + } else if (ff_pkt->flags & FO_SHA256) { + digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA256); + digest_stream = STREAM_SHA256_DIGEST; + + } else if (ff_pkt->flags & FO_SHA512) { + digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA512); + digest_stream = STREAM_SHA512_DIGEST; + } + + /* Did digest initialization fail? */ + if (digest_stream != STREAM_NONE && digest == NULL) { + Jmsg(jcr, M_WARNING, 0, _("%s digest initialization failed\n"), + stream_to_ascii(digest_stream)); + } + + /* compute MD5 or SHA1 hash */ + if (digest) { + char md[CRYPTO_DIGEST_MAX_SIZE]; + uint32_t size; + + size = sizeof(md); + + if (digest_file(jcr, ff_pkt, digest) != 0) { + jcr->JobErrors++; + + } else if (crypto_digest_finalize(digest, (uint8_t *)md, &size)) { + char *digest_buf; + const char *digest_name; + + digest_buf = (char *)malloc(BASE64_SIZE(size)); + digest_name = crypto_digest_name(digest); + + bin_to_base64(digest_buf, BASE64_SIZE(size), md, size, true); + + if (strcmp(digest_buf, elt.chksum)) { + Dmsg4(dbglvl,"%s %s chksum diff. Cat: %s File: %s\n", + fname, + digest_name, + elt.chksum, + digest_buf); + stat = true; + } + + free(digest_buf); + } + crypto_digest_free(digest); + } + } + break; case ':': + case 'J': case 'C': default: break; - } - } -//#endif -#if 0 - /* - * We check only mtime/ctime like with the normal - * incremental/differential mode - */ - if (statc.st_mtime != ff_pkt->statp.st_mtime) { -// Jmsg(jcr, M_SAVED, 0, _("%s st_mtime differs\n"), fname); - Dmsg3(dbglvl, "%s st_mtime differs (%lld!=%lld)\n", - fname, statc.st_mtime, (utime_t)ff_pkt->statp.st_mtime); - stat = true; - } else if (!(ff_pkt->flags & FO_MTIMEONLY) - && (statc.st_ctime != ff_pkt->statp.st_ctime)) { -// Jmsg(jcr, M_SAVED, 0, _("%s st_ctime differs\n"), fname); - Dmsg1(dbglvl, "%s st_ctime differs\n", fname); - stat = true; - - } else if (statc.st_size != ff_pkt->statp.st_size) { -// Jmsg(jcr, M_SAVED, 0, _("%s st_size differs\n"), fname); - Dmsg1(dbglvl, "%s st_size differs\n", fname); - stat = true; + } } -#endif /* In Incr/Diff accurate mode, we mark all files as seen * When in Full+Base mode, we mark only if the file match exactly */ - if (jcr->get_JobLevel() == L_FULL) { + if (jcr->getJobLevel() == L_FULL) { if (!stat) { /* compute space saved with basefile */ jcr->base_size += ff_pkt->statp.st_size; @@ -402,8 +496,9 @@ bail_out: int accurate_cmd(JCR *jcr) { BSOCK *dir = jcr->dir_bsock; - int len; + int lstat_pos, chksum_pos; int32_t nb; + uint16_t delta_seq; if (job_canceled(jcr)) { return true; @@ -419,13 +514,28 @@ int accurate_cmd(JCR *jcr) /* * buffer = sizeof(CurFile) + dirmsg - * dirmsg = fname + \0 + lstat + * dirmsg = fname + \0 + lstat + \0 + checksum + \0 + delta_seq + \0 */ /* get current files */ while (dir->recv() >= 0) { - len = strlen(dir->msg) + 1; - if (len < dir->msglen) { - accurate_add_file(jcr, dir->msg, dir->msg + len); + lstat_pos = strlen(dir->msg) + 1; + if (lstat_pos < dir->msglen) { + chksum_pos = lstat_pos + strlen(dir->msg + lstat_pos) + 1; + + if (chksum_pos >= dir->msglen) { + chksum_pos = lstat_pos - 1; /* tweak: no checksum, point to the last \0 */ + delta_seq = 0; + } else { + delta_seq = str_to_int32(dir->msg + + chksum_pos + + strlen(dir->msg + chksum_pos) + 1); + } + + accurate_add_file(jcr, dir->msglen, + dir->msg, /* Path */ + dir->msg + lstat_pos, /* LStat */ + dir->msg + chksum_pos, /* CheckSum */ + delta_seq); /* Delta Sequence */ } }