1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
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.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *  Bacula File Daemon  verify.c  Verify files.
21  *
22  *    Kern Sibbald, October MM
23  *
24  */
25 
26 #include "bacula.h"
27 #include "filed.h"
28 
29 static int verify_file(JCR *jcr, FF_PKT *ff_pkt, bool);
30 static int read_digest(BFILE *bfd, DIGEST *digest, JCR *jcr);
31 
32 /*
33  * Find all the requested files and send attributes
34  * to the Director.
35  *
36  */
do_verify(JCR * jcr)37 void do_verify(JCR *jcr)
38 {
39    jcr->setJobStatus(JS_Running);
40 
41    LockRes();
42    CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
43    UnlockRes();
44 
45    if (client) {
46       jcr->buf_size = client->max_network_buffer_size ?
47          client->max_network_buffer_size : DEFAULT_NETWORK_BUFFER_SIZE;
48    } else {
49       jcr->buf_size = DEFAULT_NETWORK_BUFFER_SIZE; /* use default */
50    }
51    if ((jcr->big_buf = (char *) malloc(jcr->buf_size)) == NULL) {
52       Jmsg1(jcr, M_ABORT, 0, _("Cannot malloc %d network read buffer\n"),
53          DEFAULT_NETWORK_BUFFER_SIZE);
54    }
55    set_find_options((FF_PKT *)jcr->ff, jcr->incremental, jcr->mtime);
56    Dmsg0(10, "Start find files\n");
57    /* Subroutine verify_file() is called for each file */
58    find_files(jcr, (FF_PKT *)jcr->ff, verify_file, NULL);
59    Dmsg0(10, "End find files\n");
60 
61    if (jcr->big_buf) {
62       free(jcr->big_buf);
63       jcr->big_buf = NULL;
64    }
65    jcr->setJobStatus(JS_Terminated);
66 }
67 
68 /*
69  * Called here by find() for each file.
70  *
71  *  Find the file, compute the MD5 or SHA1 and send it back to the Director
72  */
verify_file(JCR * jcr,FF_PKT * ff_pkt,bool top_level)73 static int verify_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
74 {
75    char attribs[MAXSTRING];
76    char attribsEx[MAXSTRING];
77    int digest_stream = STREAM_NONE;
78    int stat;
79    DIGEST *digest = NULL;
80    BSOCK *dir;
81 
82    if (job_canceled(jcr)) {
83       return 0;
84    }
85 
86    dir = jcr->dir_bsock;
87    jcr->num_files_examined++;         /* bump total file count */
88 
89    switch (ff_pkt->type) {
90    case FT_LNKSAVED:                  /* Hard linked, file already saved */
91       Dmsg2(30, "FT_LNKSAVED saving: %s => %s\n", ff_pkt->fname, ff_pkt->link);
92       break;
93    case FT_REGE:
94       Dmsg1(30, "FT_REGE saving: %s\n", ff_pkt->fname);
95       break;
96    case FT_REG:
97       Dmsg1(30, "FT_REG saving: %s\n", ff_pkt->fname);
98       break;
99    case FT_LNK:
100       Dmsg2(30, "FT_LNK saving: %s -> %s\n", ff_pkt->fname, ff_pkt->link);
101       break;
102    case FT_DIRBEGIN:
103       jcr->num_files_examined--;      /* correct file count */
104       return 1;                       /* ignored */
105    case FT_REPARSE:
106    case FT_JUNCTION:
107    case FT_DIREND:
108       Dmsg1(30, "FT_DIR saving: %s\n", ff_pkt->fname);
109       break;
110    case FT_SPEC:
111       Dmsg1(30, "FT_SPEC saving: %s\n", ff_pkt->fname);
112       break;
113    case FT_RAW:
114       Dmsg1(30, "FT_RAW saving: %s\n", ff_pkt->fname);
115       break;
116    case FT_FIFO:
117       Dmsg1(30, "FT_FIFO saving: %s\n", ff_pkt->fname);
118       break;
119    case FT_NOACCESS: {
120       berrno be;
121       be.set_errno(ff_pkt->ff_errno);
122       Jmsg(jcr, M_NOTSAVED, 1, _("     Could not access %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
123       jcr->JobErrors++;
124       return 1;
125    }
126    case FT_NOFOLLOW: {
127       berrno be;
128       be.set_errno(ff_pkt->ff_errno);
129       Jmsg(jcr, M_NOTSAVED, 1, _("     Could not follow link %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
130       jcr->JobErrors++;
131       return 1;
132    }
133    case FT_NOSTAT: {
134       berrno be;
135       be.set_errno(ff_pkt->ff_errno);
136       Jmsg(jcr, M_NOTSAVED, 1, _("     Could not stat %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
137       jcr->JobErrors++;
138       return 1;
139    }
140    case FT_DIRNOCHG:
141    case FT_NOCHG:
142       Jmsg(jcr, M_SKIPPED, 1, _("     Unchanged file skipped: %s\n"), ff_pkt->fname);
143       return 1;
144    case FT_ISARCH:
145       Jmsg(jcr, M_SKIPPED, 1, _("     Archive file skipped: %s\n"), ff_pkt->fname);
146       return 1;
147    case FT_NORECURSE:
148       Jmsg(jcr, M_SKIPPED, 1, _("     Recursion turned off. Directory skipped: %s\n"), ff_pkt->fname);
149       ff_pkt->type = FT_DIREND;     /* directory entry was backed up */
150       break;
151    case FT_NOFSCHG:
152       Jmsg(jcr, M_SKIPPED, 1, _("     File system change prohibited. Directory skipped: %s\n"), ff_pkt->fname);
153       return 1;
154    case FT_PLUGIN_CONFIG:
155    case FT_RESTORE_FIRST:
156       return 1;                       /* silently skip */
157    case FT_NOOPEN: {
158       berrno be;
159       be.set_errno(ff_pkt->ff_errno);
160       Jmsg(jcr, M_NOTSAVED, 1, _("     Could not open directory %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
161       jcr->JobErrors++;
162       return 1;
163    }
164    default:
165       Jmsg(jcr, M_NOTSAVED, 0, _("     Unknown file type %d: %s\n"), ff_pkt->type, ff_pkt->fname);
166       jcr->JobErrors++;
167       return 1;
168    }
169 
170    /* Encode attributes and possibly extend them */
171    encode_stat(attribs, &ff_pkt->statp, sizeof(ff_pkt->statp), ff_pkt->LinkFI, 0);
172    encode_attribsEx(jcr, attribsEx, ff_pkt);
173 
174    jcr->lock();
175    jcr->JobFiles++;                  /* increment number of files sent */
176    pm_strcpy(jcr->last_fname, ff_pkt->fname);
177    jcr->unlock();
178 
179    /*
180     * Send file attributes to Director
181     *   File_index
182     *   Stream
183     *   Verify Options
184     *   Filename (full path)
185     *   Encoded attributes
186     *   Link name (if type==FT_LNK)
187     * For a directory, link is the same as fname, but with trailing
188     * slash. For a linked file, link is the link.
189     */
190    /* Send file attributes to Director (note different format than for Storage) */
191    Dmsg2(400, "send ATTR inx=%d fname=%s\n", jcr->JobFiles, ff_pkt->fname);
192    if (ff_pkt->type == FT_LNK || ff_pkt->type == FT_LNKSAVED) {
193       stat = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles,
194                         STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
195                         0, attribs, 0, ff_pkt->link, 0);
196    } else if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE ||
197               ff_pkt->type == FT_JUNCTION) {
198       /* Here link is the canonical filename (i.e. with trailing slash) */
199       stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles,
200                         STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->link,
201                         0, attribs, 0, 0);
202    } else {
203       stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles,
204                         STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
205                         0, attribs, 0, 0);
206    }
207    Dmsg2(20, "bfiled>bdird: attribs len=%d: msg=%s\n", dir->msglen, dir->msg);
208    if (!stat) {
209       Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), dir->bstrerror());
210       return 0;
211    }
212 
213    /*
214     * The remainder of the function is all about getting the checksum.
215     * First we initialise, then we read files, other streams and Finder Info.
216     */
217    if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) &&
218             ff_pkt->flags & (FO_MD5|FO_SHA1|FO_SHA256|FO_SHA512))) {
219       /*
220        * Create our digest context. If this fails, the digest will be set to NULL
221        * and not used.
222        */
223       if (ff_pkt->flags & FO_MD5) {
224          digest = crypto_digest_new(jcr, CRYPTO_DIGEST_MD5);
225          digest_stream = STREAM_MD5_DIGEST;
226 
227       } else if (ff_pkt->flags & FO_SHA1) {
228          digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA1);
229          digest_stream = STREAM_SHA1_DIGEST;
230 
231       } else if (ff_pkt->flags & FO_SHA256) {
232          digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA256);
233          digest_stream = STREAM_SHA256_DIGEST;
234 
235       } else if (ff_pkt->flags & FO_SHA512) {
236          digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA512);
237          digest_stream = STREAM_SHA512_DIGEST;
238       }
239 
240       /* Did digest initialization fail? */
241       if (digest_stream != STREAM_NONE && digest == NULL) {
242          Jmsg(jcr, M_WARNING, 0, _("%s digest initialization failed\n"),
243               stream_to_ascii(digest_stream));
244       }
245 
246       /* compute MD5 or SHA1 hash */
247       if (digest) {
248          char md[CRYPTO_DIGEST_MAX_SIZE];
249          uint32_t size;
250 
251          size = sizeof(md);
252 
253          if (digest_file(jcr, ff_pkt, digest) != 0) {
254             jcr->JobErrors++;
255             goto good_rtn;
256          }
257 
258          if (crypto_digest_finalize(digest, (uint8_t *)md, &size)) {
259             char *digest_buf;
260             const char *digest_name;
261 
262             digest_buf = (char *)malloc(BASE64_SIZE(size));
263             digest_name = crypto_digest_name(digest);
264 
265             bin_to_base64(digest_buf, BASE64_SIZE(size), md, size, true);
266             Dmsg3(400, "send inx=%d %s=%s\n", jcr->JobFiles, digest_name, digest_buf);
267             dir->fsend("%d %d %s *%s-%d*", jcr->JobFiles, digest_stream, digest_buf,
268                        digest_name, jcr->JobFiles);
269             Dmsg3(20, "bfiled>bdird: %s len=%d: msg=%s\n", digest_name,
270                   dir->msglen, dir->msg);
271 
272             free(digest_buf);
273          }
274       }
275    }
276 
277 good_rtn:
278    if (digest) {
279       crypto_digest_free(digest);
280    }
281    return 1;
282 }
283 
284 /*
285  * Compute message digest for the file specified by ff_pkt.
286  * In case of errors we need the job control record and file name.
287  */
digest_file(JCR * jcr,FF_PKT * ff_pkt,DIGEST * digest)288 int digest_file(JCR *jcr, FF_PKT *ff_pkt, DIGEST *digest)
289 {
290    BFILE bfd;
291 
292    Dmsg0(50, "=== digest_file\n");
293    binit(&bfd);
294 
295    if (ff_pkt->statp.st_size > 0 || ff_pkt->type == FT_RAW
296          || ff_pkt->type == FT_FIFO) {
297       int noatime = ff_pkt->flags & FO_NOATIME ? O_NOATIME : 0;
298       if ((bopen(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0)) < 0) {
299          ff_pkt->ff_errno = errno;
300          berrno be;
301          be.set_errno(bfd.berrno);
302          Dmsg2(100, "Cannot open %s: ERR=%s\n", ff_pkt->fname, be.bstrerror());
303          Jmsg(jcr, M_ERROR, 1, _("     Cannot open %s: ERR=%s.\n"),
304                ff_pkt->fname, be.bstrerror());
305          return 1;
306       }
307       read_digest(&bfd, digest, jcr);
308       bclose(&bfd);
309    }
310 
311 #ifdef HAVE_DARWIN_OS
312    /* Open resource fork if necessary */
313    if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->hfsinfo.rsrclength > 0) {
314       if (bopen_rsrc(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) {
315          ff_pkt->ff_errno = errno;
316          berrno be;
317          Jmsg(jcr, M_ERROR, -1, _("     Cannot open resource fork for %s: ERR=%s.\n"),
318                ff_pkt->fname, be.bstrerror());
319          if (is_bopen(&ff_pkt->bfd)) {
320             bclose(&ff_pkt->bfd);
321          }
322          return 1;
323       }
324       read_digest(&bfd, digest, jcr);
325       bclose(&bfd);
326    }
327    if (digest && ff_pkt->flags & FO_HFSPLUS) {
328       crypto_digest_update(digest, (uint8_t *)ff_pkt->hfsinfo.fndrinfo, 32);
329    }
330 #endif
331    return 0;
332 }
333 
334 /*
335  * Read message digest of bfd, updating digest
336  * In case of errors we need the job control record and file name.
337  */
read_digest(BFILE * bfd,DIGEST * digest,JCR * jcr)338 static int read_digest(BFILE *bfd, DIGEST *digest, JCR *jcr)
339 {
340    char  *buf;
341    int64_t n;
342    int64_t bufsiz = jcr->buf_size;
343    FF_PKT *ff_pkt = (FF_PKT *)jcr->ff;
344    uint64_t fileAddr = 0;             /* file address */
345 
346    buf = (char *)malloc(bufsiz);
347    Dmsg0(50, "=== read_digest\n");
348    /* With this option, we read shorter blocks, so to not break the
349     * sparse block detection, we need to adjust the read size.
350     */
351    if (ff_pkt->flags & FO_SPARSE) {
352       bufsiz -= OFFSET_FADDR_SIZE;
353    }
354    while ((n=bread(bfd, buf, bufsiz)) > 0) {
355       /* Check for sparse blocks */
356       if (ff_pkt->flags & FO_SPARSE) {
357          bool allZeros = false;
358          if ((n == bufsiz &&
359               fileAddr+n < (uint64_t)ff_pkt->statp.st_size) ||
360              ((ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO) &&
361                (uint64_t)ff_pkt->statp.st_size == 0)) {
362             allZeros = is_buf_zero(buf, bufsiz);
363          }
364          fileAddr += n;               /* update file address */
365          /* Skip any block of all zeros */
366          if (allZeros) {
367             continue;                 /* skip block of zeros */
368          }
369       }
370 
371       crypto_digest_update(digest, (uint8_t *)buf, n);
372 
373       /* Can be used by BaseJobs or with accurate, update only for Verify
374        * jobs
375        */
376       if (jcr->getJobType() == JT_VERIFY) {
377          jcr->JobBytes += n;
378       }
379       jcr->ReadBytes += n;
380    }
381    free(buf);
382    if (n < 0) {
383       berrno be;
384       be.set_errno(bfd->berrno);
385       Dmsg2(100, "Error reading file %s: ERR=%s\n", jcr->last_fname, be.bstrerror());
386       Jmsg(jcr, M_ERROR, 1, _("Error reading file %s: ERR=%s\n"),
387             jcr->last_fname, be.bstrerror());
388       jcr->JobErrors++;
389       return -1;
390    }
391    return 0;
392 }
393