X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Ffiled%2Fverify.c;h=427f711ea7e26417427f1cf4a9eba3d786733c16;hb=537d8957f060575d515ff6fef287b281db98aed8;hp=30dddf2cd259cf435b6fd0f48d09416896629d47;hpb=65e81a4da0d0175163ef1b8f67a6dd3d7b4fd06b;p=bacula%2Fbacula diff --git a/bacula/src/filed/verify.c b/bacula/src/filed/verify.c index 30dddf2cd2..427f711ea7 100644 --- a/bacula/src/filed/verify.c +++ b/bacula/src/filed/verify.c @@ -1,87 +1,83 @@ /* - * Bacula File Daemon verify.c Verify files. - * - * Kern Sibbald, October MM - * - * Version $Id$ - * - */ -/* - Copyright (C) 2000-2003 Kern Sibbald and John Walker + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2015 Kern Sibbald - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. - You should have received a copy of the GNU General Public - License along with this program; if not, write to the Free - Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA. + This notice must be preserved when any source code is + conveyed and/or propagated. + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +/* + * Bacula File Daemon verify.c Verify files. + * + * Kern Sibbald, October MM + * */ #include "bacula.h" #include "filed.h" -static int verify_file(FF_PKT *ff_pkt, void *my_pkt); +static int verify_file(JCR *jcr, FF_PKT *ff_pkt, bool); +static int read_digest(BFILE *bfd, DIGEST *digest, JCR *jcr); -/* +/* * Find all the requested files and send attributes * to the Director. - * + * */ void do_verify(JCR *jcr) { - set_jcr_job_status(jcr, JS_Running); - jcr->buf_size = MAX_NETWORK_BUFFER_SIZE; + jcr->setJobStatus(JS_Running); + jcr->buf_size = DEFAULT_NETWORK_BUFFER_SIZE; if ((jcr->big_buf = (char *) malloc(jcr->buf_size)) == NULL) { - Jmsg1(jcr, M_ABORT, 0, _("Cannot malloc %d network read buffer\n"), MAX_NETWORK_BUFFER_SIZE); + Jmsg1(jcr, M_ABORT, 0, _("Cannot malloc %d network read buffer\n"), + DEFAULT_NETWORK_BUFFER_SIZE); } - set_find_options((FF_PKT *)jcr->ff, jcr->incremental, jcr->mtime, jcr->mtime_only); + set_find_options((FF_PKT *)jcr->ff, jcr->incremental, jcr->mtime); Dmsg0(10, "Start find files\n"); /* Subroutine verify_file() is called for each file */ - find_files(jcr, (FF_PKT *)jcr->ff, verify_file, (void *)jcr); + find_files(jcr, (FF_PKT *)jcr->ff, verify_file, NULL); Dmsg0(10, "End find files\n"); if (jcr->big_buf) { free(jcr->big_buf); jcr->big_buf = NULL; } - set_jcr_job_status(jcr, JS_Terminated); -} + jcr->setJobStatus(JS_Terminated); +} -/* +/* * Called here by find() for each file. * * Find the file, compute the MD5 or SHA1 and send it back to the Director */ -static int verify_file(FF_PKT *ff_pkt, void *pkt) +static int verify_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) { char attribs[MAXSTRING]; - int32_t n; + char attribsEx[MAXSTRING]; + int digest_stream = STREAM_NONE; int stat; - BFILE bfd; - struct MD5Context md5c; - struct SHA1Context sha1c; - unsigned char signature[25]; /* large enough for either */ + DIGEST *digest = NULL; BSOCK *dir; - JCR *jcr = (JCR *)pkt; if (job_canceled(jcr)) { return 0; } - + dir = jcr->dir_bsock; - jcr->num_files_examined++; /* bump total file count */ + jcr->num_files_examined++; /* bump total file count */ switch (ff_pkt->type) { - case FT_LNKSAVED: /* Hard linked, file already saved */ + case FT_LNKSAVED: /* Hard linked, file already saved */ Dmsg2(30, "FT_LNKSAVED saving: %s => %s\n", ff_pkt->fname, ff_pkt->link); break; case FT_REGE: @@ -93,7 +89,12 @@ static int verify_file(FF_PKT *ff_pkt, void *pkt) case FT_LNK: Dmsg2(30, "FT_LNK saving: %s -> %s\n", ff_pkt->fname, ff_pkt->link); break; - case FT_DIR: + case FT_DIRBEGIN: + jcr->num_files_examined--; /* correct file count */ + return 1; /* ignored */ + case FT_REPARSE: + case FT_JUNCTION: + case FT_DIREND: Dmsg1(30, "FT_DIR saving: %s\n", ff_pkt->fname); break; case FT_SPEC: @@ -105,142 +106,270 @@ static int verify_file(FF_PKT *ff_pkt, void *pkt) case FT_FIFO: Dmsg1(30, "FT_FIFO saving: %s\n", ff_pkt->fname); break; - case FT_NOACCESS: - Jmsg(jcr, M_NOTSAVED, -1, _(" Could not access %s: ERR=%s\n"), ff_pkt->fname, strerror(ff_pkt->ff_errno)); - jcr->Errors++; + case FT_NOACCESS: { + berrno be; + be.set_errno(ff_pkt->ff_errno); + Jmsg(jcr, M_NOTSAVED, 1, _(" Could not access %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror()); + jcr->JobErrors++; return 1; - case FT_NOFOLLOW: - Jmsg(jcr, M_NOTSAVED, -1, _(" Could not follow link %s: ERR=%s\n"), ff_pkt->fname, strerror(ff_pkt->ff_errno)); - jcr->Errors++; + } + case FT_NOFOLLOW: { + berrno be; + be.set_errno(ff_pkt->ff_errno); + Jmsg(jcr, M_NOTSAVED, 1, _(" Could not follow link %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror()); + jcr->JobErrors++; return 1; - case FT_NOSTAT: - Jmsg(jcr, M_NOTSAVED, -1, _(" Could not stat %s: ERR=%s\n"), ff_pkt->fname, strerror(ff_pkt->ff_errno)); - jcr->Errors++; + } + case FT_NOSTAT: { + berrno be; + be.set_errno(ff_pkt->ff_errno); + Jmsg(jcr, M_NOTSAVED, 1, _(" Could not stat %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror()); + jcr->JobErrors++; return 1; + } case FT_DIRNOCHG: case FT_NOCHG: - Jmsg(jcr, M_INFO, -1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname); + Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname); return 1; case FT_ISARCH: - Jmsg(jcr, M_SKIPPED, -1, _(" Archive file skipped: %s\n"), ff_pkt->fname); + Jmsg(jcr, M_SKIPPED, 1, _(" Archive file skipped: %s\n"), ff_pkt->fname); return 1; case FT_NORECURSE: - Jmsg(jcr, M_SKIPPED, -1, _(" Recursion turned off. Directory skipped: %s\n"), ff_pkt->fname); - return 1; + Jmsg(jcr, M_SKIPPED, 1, _(" Recursion turned off. Directory skipped: %s\n"), ff_pkt->fname); + ff_pkt->type = FT_DIREND; /* directory entry was backed up */ + break; case FT_NOFSCHG: - Jmsg(jcr, M_SKIPPED, -1, _(" File system change prohibited. Directory skipped: %s\n"), ff_pkt->fname); + Jmsg(jcr, M_SKIPPED, 1, _(" File system change prohibited. Directory skipped: %s\n"), ff_pkt->fname); return 1; - case FT_NOOPEN: - Jmsg(jcr, M_NOTSAVED, -1, _(" Could not open directory %s: ERR=%s\n"), ff_pkt->fname, strerror(ff_pkt->ff_errno)); - jcr->Errors++; + case FT_PLUGIN_CONFIG: + case FT_RESTORE_FIRST: + return 1; /* silently skip */ + case FT_NOOPEN: { + berrno be; + be.set_errno(ff_pkt->ff_errno); + Jmsg(jcr, M_NOTSAVED, 1, _(" Could not open directory %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror()); + jcr->JobErrors++; return 1; + } default: Jmsg(jcr, M_NOTSAVED, 0, _(" Unknown file type %d: %s\n"), ff_pkt->type, ff_pkt->fname); - jcr->Errors++; + jcr->JobErrors++; return 1; } - binit(&bfd); - - if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) && - ff_pkt->statp.st_size > 0) || - ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO) { - if ((bopen(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0)) < 0) { - ff_pkt->ff_errno = errno; - Jmsg(jcr, M_NOTSAVED, -1, _(" Cannot open %s: ERR=%s.\n"), - ff_pkt->fname, berror(&bfd)); - jcr->Errors++; - return 1; - } - } + /* Encode attributes and possibly extend them */ + encode_stat(attribs, &ff_pkt->statp, sizeof(ff_pkt->statp), ff_pkt->LinkFI, 0); + encode_attribsEx(jcr, attribsEx, ff_pkt); - encode_stat(attribs, ff_pkt, 0); - - P(jcr->mutex); - jcr->JobFiles++; /* increment number of files sent */ - pm_strcpy(&jcr->last_fname, ff_pkt->fname); - V(jcr->mutex); + jcr->lock(); + jcr->JobFiles++; /* increment number of files sent */ + pm_strcpy(jcr->last_fname, ff_pkt->fname); + jcr->unlock(); - /* + /* * Send file attributes to Director - * File_index - * Stream - * Verify Options - * Filename (full path) - * Encoded attributes - * Link name (if type==FT_LNK) + * File_index + * Stream + * Verify Options + * Filename (full path) + * Encoded attributes + * Link name (if type==FT_LNK) * For a directory, link is the same as fname, but with trailing * slash. For a linked file, link is the link. */ /* Send file attributes to Director (note different format than for Storage) */ Dmsg2(400, "send ATTR inx=%d fname=%s\n", jcr->JobFiles, ff_pkt->fname); if (ff_pkt->type == FT_LNK || ff_pkt->type == FT_LNKSAVED) { - stat = bnet_fsend(dir, "%d %d %s %s%c%s%c%s%c", jcr->JobFiles, - STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname, - 0, attribs, 0, ff_pkt->link, 0); - } else if (ff_pkt->type == FT_DIR) { + stat = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles, + STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname, + 0, attribs, 0, ff_pkt->link, 0); + } else if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE || + ff_pkt->type == FT_JUNCTION) { /* Here link is the canonical filename (i.e. with trailing slash) */ - stat = bnet_fsend(dir,"%d %d %s %s%c%s%c%c", jcr->JobFiles, - STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->link, - 0, attribs, 0, 0); + stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles, + STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->link, + 0, attribs, 0, 0); } else { - stat = bnet_fsend(dir,"%d %d %s %s%c%s%c%c", jcr->JobFiles, - STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname, - 0, attribs, 0, 0); + stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles, + STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname, + 0, attribs, 0, 0); } Dmsg2(20, "bfiled>bdird: attribs len=%d: msg=%s\n", dir->msglen, dir->msg); if (!stat) { - Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), bnet_strerror(dir)); - if (is_bopen(&bfd)) { - bclose(&bfd); - } + Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), dir->bstrerror()); return 0; } - /* If file opened, compute MD5 or SHA1 hash */ - if (is_bopen(&bfd) && ff_pkt->flags & FO_MD5) { - char MD5buf[40]; /* 24 should do */ - MD5Init(&md5c); - while ((n=bread(&bfd, jcr->big_buf, jcr->buf_size)) > 0) { - MD5Update(&md5c, ((unsigned char *) jcr->big_buf), n); - jcr->JobBytes += n; - jcr->ReadBytes += n; - } - if (n < 0) { - Jmsg(jcr, M_ERROR, -1, _("Error reading file %s: ERR=%s\n"), - ff_pkt->fname, berror(&bfd)); - jcr->Errors++; + /* + * 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))) { + /* + * 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; } - MD5Final(signature, &md5c); - - bin_to_base64(MD5buf, (char *)signature, 16); /* encode 16 bytes */ - Dmsg2(400, "send inx=%d MD5=%s\n", jcr->JobFiles, MD5buf); - bnet_fsend(dir, "%d %d %s *MD5-%d*", jcr->JobFiles, STREAM_MD5_SIGNATURE, MD5buf, - jcr->JobFiles); - Dmsg2(20, "bfiled>bdird: MD5 len=%d: msg=%s\n", dir->msglen, dir->msg); - } else if (is_bopen(&bfd) && ff_pkt->flags & FO_SHA1) { - char SHA1buf[40]; /* 24 should do */ - SHA1Init(&sha1c); - while ((n=bread(&bfd, jcr->big_buf, jcr->buf_size)) > 0) { - SHA1Update(&sha1c, ((unsigned char *) jcr->big_buf), n); - jcr->JobBytes += n; - jcr->ReadBytes += n; + + /* 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)); } - if (n < 0) { - Jmsg(jcr, M_ERROR, -1, _("Error reading file %s: ERR=%s\n"), - ff_pkt->fname, berror(&bfd)); - jcr->Errors++; + + /* 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++; + goto good_rtn; + } + + 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); + Dmsg3(400, "send inx=%d %s=%s\n", jcr->JobFiles, digest_name, digest_buf); + dir->fsend("%d %d %s *%s-%d*", jcr->JobFiles, digest_stream, digest_buf, + digest_name, jcr->JobFiles); + Dmsg3(20, "bfiled>bdird: %s len=%d: msg=%s\n", digest_name, + dir->msglen, dir->msg); + + free(digest_buf); + } } - SHA1Final(&sha1c, signature); + } + +good_rtn: + if (digest) { + crypto_digest_free(digest); + } + return 1; +} + +/* + * Compute message digest for the file specified by ff_pkt. + * In case of errors we need the job control record and file name. + */ +int digest_file(JCR *jcr, FF_PKT *ff_pkt, DIGEST *digest) +{ + BFILE bfd; + + Dmsg0(50, "=== digest_file\n"); + binit(&bfd); - bin_to_base64(SHA1buf, (char *)signature, 20); /* encode 20 bytes */ - Dmsg2(400, "send inx=%d SHA1=%s\n", jcr->JobFiles, SHA1buf); - bnet_fsend(dir, "%d %d %s *SHA1-%d*", jcr->JobFiles, STREAM_SHA1_SIGNATURE, - SHA1buf, jcr->JobFiles); - Dmsg2(20, "bfiled>bdird: SHA1 len=%d: msg=%s\n", dir->msglen, dir->msg); + if (ff_pkt->statp.st_size > 0 || ff_pkt->type == FT_RAW + || ff_pkt->type == FT_FIFO) { + int noatime = ff_pkt->flags & FO_NOATIME ? O_NOATIME : 0; + if ((bopen(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0)) < 0) { + ff_pkt->ff_errno = errno; + berrno be; + be.set_errno(bfd.berrno); + Dmsg2(100, "Cannot open %s: ERR=%s\n", ff_pkt->fname, be.bstrerror()); + Jmsg(jcr, M_ERROR, 1, _(" Cannot open %s: ERR=%s.\n"), + ff_pkt->fname, be.bstrerror()); + return 1; + } + read_digest(&bfd, digest, jcr); + bclose(&bfd); } - if (is_bopen(&bfd)) { + +#ifdef HAVE_DARWIN_OS + /* Open resource fork if necessary */ + if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->hfsinfo.rsrclength > 0) { + if (bopen_rsrc(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) { + ff_pkt->ff_errno = errno; + berrno be; + Jmsg(jcr, M_ERROR, -1, _(" Cannot open resource fork for %s: ERR=%s.\n"), + ff_pkt->fname, be.bstrerror()); + if (is_bopen(&ff_pkt->bfd)) { + bclose(&ff_pkt->bfd); + } + return 1; + } + read_digest(&bfd, digest, jcr); bclose(&bfd); + } + if (digest && ff_pkt->flags & FO_HFSPLUS) { + crypto_digest_update(digest, (uint8_t *)ff_pkt->hfsinfo.fndrinfo, 32); + } +#endif + return 0; +} + +/* + * Read message digest of bfd, updating digest + * In case of errors we need the job control record and file name. + */ +static int read_digest(BFILE *bfd, DIGEST *digest, JCR *jcr) +{ + char buf[DEFAULT_NETWORK_BUFFER_SIZE]; + int64_t n; + int64_t bufsiz = (int64_t)sizeof(buf); + FF_PKT *ff_pkt = (FF_PKT *)jcr->ff; + uint64_t fileAddr = 0; /* file address */ + + + Dmsg0(50, "=== read_digest\n"); + while ((n=bread(bfd, buf, bufsiz)) > 0) { + /* Check for sparse blocks */ + if (ff_pkt->flags & FO_SPARSE) { + bool allZeros = false; + if ((n == bufsiz && + fileAddr+n < (uint64_t)ff_pkt->statp.st_size) || + ((ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO) && + (uint64_t)ff_pkt->statp.st_size == 0)) { + allZeros = is_buf_zero(buf, bufsiz); + } + fileAddr += n; /* update file address */ + /* Skip any block of all zeros */ + if (allZeros) { + continue; /* skip block of zeros */ + } + } + + crypto_digest_update(digest, (uint8_t *)buf, n); + + /* Can be used by BaseJobs or with accurate, update only for Verify + * jobs + */ + if (jcr->getJobType() == JT_VERIFY) { + jcr->JobBytes += n; + } + jcr->ReadBytes += n; } - return 1; + if (n < 0) { + berrno be; + be.set_errno(bfd->berrno); + Dmsg2(100, "Error reading file %s: ERR=%s\n", jcr->last_fname, be.bstrerror()); + Jmsg(jcr, M_ERROR, 1, _("Error reading file %s: ERR=%s\n"), + jcr->last_fname, be.bstrerror()); + jcr->JobErrors++; + return -1; + } + return 0; }