}
+/* Helper method to calculate actual file's checksum and compare it with the old one.
+ * Return statuses:
+ * 0 - checksum did not change
+ * 1 - checksum changed
+ * -1 - error
+ */
+static int check_checksum_diff(JCR *jcr, FF_PKT *ff_pkt, CurFile *elt)
+{
+ int ret = 0;
+ int digest_stream = STREAM_NONE;
+ DIGEST *digest = NULL;
+ char *fname;
+
+ if (S_ISDIR(ff_pkt->statp.st_mode)) {
+ fname = ff_pkt->link;
+ } else {
+ fname = ff_pkt->fname;
+ }
+
+ /*
+ * The remainder of the function is all about getting the checksum.
+ * First we initialise, then we read files, other streams and Finder Info.
+ */
+ if (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);
+ ret = -1;
+ goto bail_out;
+ }
+
+ /*
+ * 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); /* TODO: With FIPS, MD5 is disabled */
+ 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);
+ ret = 1;
+ }
+ free(digest_buf);
+ }
+ }
+ }
+
+bail_out:
+ if (digest) {
+ crypto_digest_free(digest);
+ }
+
+ return ret;
+}
+
/*
* This function is called for each file seen in fileset.
* We check in file_list hash if fname have been backuped
*/
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;
+ bool only_changed = false, checksum = false;
CurFile elt;
+ int ret;
ff_pkt->delta_seq = 0;
ff_pkt->accurate_found = false;
stat = true;
break;
/* TODO: cleanup and factorise this function with verify.c */
+ case ':':
+ case 'J':
+ case 'C':
+ default:
+ break;
+ }
+ }
+
+ /* Go through opts once again, this time check only for checksum-related opts */
+ for (char *p=opts; *p; p++) {
+ switch (*p) {
case '5': /* compare MD5 */
case '1': /* compare SHA1 */
case '2': /* compare SHA256 */
case '3': /* compare SHA512 */
- /*
- * 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); /* TODO: With FIPS, MD5 is disabled */
- 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);
- }
+ if (ff_pkt->type != FT_LNKSAVED &&
+ (S_ISREG(ff_pkt->statp.st_mode) &&
+ ff_pkt->flags & (FO_MD5|FO_SHA1|FO_SHA256|FO_SHA512))) {
+ checksum = true;
}
-
break;
- case ':':
- case 'J':
- case 'C':
+ case 'o':
+ only_changed = true;
+ break;
default:
break;
}
}
+ /* Check if user specified any of the checksum accurate opts */
+ if (checksum) {
+ if (only_changed) {
+ /* User wants to calculate checksum only for the files with changed metadata */
+ if (stat) {
+ /* Any of metadata member specified in accurate options has changed so we need to calculate
+ * and compare file's checksum and decide if we want to backup file or only metadata based on
+ * checksum comparison with the 'old' file*/
+ ret = check_checksum_diff(jcr, ff_pkt, &elt);
+ if (ret == 1) {
+ // checksum has changed, backup file normally
+ stat = true;
+ } else if (ret == -1){
+ stat = false;
+ goto bail_out;
+ } else {
+ /* Checksum hasn't changed, we can backup only meta */
+ ff_pkt->stat_update = true;
+ }
+ }
+ } else if (!only_changed && !stat) {
+ /* User did not specified the 'calculate checksum only when metadata change' option,
+ * and we know that specified metadata did not change at that point so we need to calculate it
+ * and base our backup decision on the result of comparing it with the one we had before */
+ ret = check_checksum_diff(jcr, ff_pkt, &elt);
+ if (ret == 1) {
+ stat = true;
+ } else if (ret == -1){
+ stat = false;
+ goto bail_out;
+ }
+ }
+ }
+
/* In Incr/Diff accurate mode, we mark all files as seen
* When in Full+Base mode, we mark only if the file match exactly
*/
goto bail_out;
}
- /** Meta data only for restore object */
- if (IS_FT_OBJECT(ff_pkt->type)) {
- goto good_rtn;
- }
- /** Meta data only for deleted files */
- if (ff_pkt->type == FT_DELETED) {
+ if (IS_FT_OBJECT(ff_pkt->type) || /* Meta data only for restore object */
+ ff_pkt->type == FT_DELETED || /* Meta data only for deleted files */
+ bctx.ff_pkt->stat_update) { /* Only metadata changed for file */
goto good_rtn;
}
+
/** Set up the encryption context and send the session data to the SD */
if (has_file_data && jcr->crypto.pki_encrypt) {
if (!crypto_session_send(jcr, sd)) {
attr_stream = STREAM_RESTORE_OBJECT;
} else if (ff_pkt->type == FT_PLUGIN_OBJECT) {
attr_stream = STREAM_PLUGIN_OBJECT;
+ } else if (ff_pkt->stat_update) {
+ attr_stream = STREAM_UNIX_ATTRIBUTE_UPDATE;
} else {
attribsEx = attribsExBuf;
attr_stream = encode_attribsEx(jcr, attribsEx, ff_pkt);
case FT_REG:
stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%d%c", jcr->JobFiles,
ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, attribsEx, 0,
- ff_pkt->delta_seq, 0);
+ /*TODO we may want to increment the delta_seq number intead of hardcode it to 1,
+ * when at some point we start to generate delta sequences also for regular files */
+ ff_pkt->stat_update ? 1 : ff_pkt->delta_seq, 0);
break;
default:
stat = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles,