]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/verify_vol.c
Tweak leave SQL library links in rpm
[bacula/bacula] / bacula / src / filed / verify_vol.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 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-vol.c Verify files on a Volume
21  *    versus attributes in Catalog
22  *
23  *    Kern Sibbald, July MMII
24  *
25  */
26
27 #include "bacula.h"
28 #include "filed.h"
29 #include "findlib/win32filter.h"
30
31 #if   defined(HAVE_LIBZ)
32 const bool have_libz = true;
33 #else
34 const bool have_libz = false;
35 #endif
36
37 #ifdef HAVE_LZO
38 const bool have_lzo = true;
39 #else
40 const bool have_lzo = false;
41 #endif
42
43 /* Context used during Verify Data job. We use it in the
44  * verify loop to compute checksums and check attributes.
45  */
46 class v_ctx {
47 public:
48    JCR *jcr;
49    int32_t stream;              /* stream less new bits */
50    int32_t prev_stream;         /* previous stream */
51    int32_t full_stream;         /* full stream including new bits */
52    int32_t type;                /* file type FT_ */
53    int64_t size;                /* current file size */
54    ATTR *attr;                  /* Pointer to attributes */
55
56    bool check_size;             /* Check or not the size attribute */
57    bool check_chksum;           /* Check the checksum */
58
59    crypto_digest_t digesttype;
60    Win32Filter win32filter;
61    char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)]; /* current digest */
62
63    v_ctx(JCR *ajcr) :
64       jcr(ajcr), stream(0), prev_stream(0), full_stream(0), type(0), size(-1),
65       attr(new_attr(jcr)), check_size(false), check_chksum(false),
66       digesttype(CRYPTO_DIGEST_NONE), win32filter()
67    {
68       *digest = 0;
69       scan_fileset();
70    };
71    ~v_ctx() {
72       free_attr(attr);
73    };
74    /* Call this function when we change the file
75     * We check the st_size and we compute the digest
76     */
77    bool close_previous_stream();
78
79    /* Call when we have a sparse record */
80    void skip_sparse_header(char **data, uint32_t *length);
81
82    /* Scan the fileset to know if we want to check checksums or st_size */
83    void scan_fileset();
84
85    /* In cleanup, we reset the current file size to -1 */
86    void reset_size() {
87       size = -1;
88    };
89
90    /* Used for sparse files */
91    void set_size(int64_t val) {
92       size = MAX(size, val);
93    };
94
95    void update_size(int64_t val) {
96       if (size == -1) {
97          size = 0;
98       }
99       size += val;
100    };
101
102    void update_checksum(char *wbuf, int32_t wsize) {
103       if (wsize > 0 && check_chksum) {
104          if (!jcr->crypto.digest) {
105             jcr->crypto.digest = crypto_digest_new(jcr, digesttype);
106          }
107          crypto_digest_update(jcr->crypto.digest, (uint8_t *)wbuf, wsize);
108       }
109    };
110 };
111
112 /* Data received from Storage Daemon */
113 static char rec_header[] = "rechdr %ld %ld %ld %ld %ld";
114
115 /* Forward referenced functions */
116
117 /* We don't know in advance which digest mode is needed, we do not
118  * want to store files on disk either to check afterward. So, we read
119  * the fileset definition and we try to guess the digest that will be
120  * used. If the FileSet uses multiple digests, it will not work.
121  */
122 void v_ctx::scan_fileset()
123 {
124    findFILESET *fileset;
125
126    check_size = check_chksum = false;
127    digesttype = CRYPTO_DIGEST_NONE;
128
129    if (!jcr->ff || !jcr->ff->fileset) {
130       return;
131    }
132
133    fileset = jcr->ff->fileset;
134
135    for (int i=0; i<fileset->include_list.size(); i++) {
136       findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
137
138       for (int j=0; j<incexe->opts_list.size(); j++) {
139          findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
140          check_size = (strchr(fo->VerifyOpts, 's') != NULL);
141          if ((strchr(fo->VerifyOpts, '1') != NULL) ||
142              (strchr(fo->VerifyOpts, '5') != NULL))
143          {
144             check_chksum = true;
145          } 
146
147          if (fo->flags & FO_MD5) {
148             digesttype = CRYPTO_DIGEST_MD5;
149             return;
150          }
151          if (fo->flags & FO_SHA1) {
152             digesttype = CRYPTO_DIGEST_SHA1;
153             return;
154          }
155          if (fo->flags & FO_SHA256) {
156             digesttype = CRYPTO_DIGEST_SHA256;
157             return;
158          }
159          if (fo->flags & FO_SHA512) {
160             digesttype = CRYPTO_DIGEST_SHA512;
161             return;
162          }
163       }
164    }
165    digesttype = CRYPTO_DIGEST_NONE;
166    if (check_chksum) {
167       Jmsg(jcr, M_WARNING, 0, _("Checksum verification required in Verify FileSet option, but no Signature found in the FileSet\n"));
168       check_chksum = false;
169    }
170 }
171
172 /* Compute the file size for sparse records and adjust the data */
173 void v_ctx::skip_sparse_header(char **data, uint32_t *length)
174 {
175    unser_declare;
176    uint64_t faddr;
177    unser_begin(*data, OFFSET_FADDR_SIZE);
178    unser_uint64(faddr);
179
180    /* For sparse, we assume that the file is at least big as faddr */
181    set_size(faddr);
182    
183    *data += OFFSET_FADDR_SIZE;
184    *length -= OFFSET_FADDR_SIZE;
185 }
186
187 /*
188  * If extracting, close any previous stream
189  */
190 bool v_ctx::close_previous_stream()
191 {
192    bool rtn = true;
193    uint8_t buf[CRYPTO_DIGEST_MAX_SIZE];
194    uint32_t len = CRYPTO_DIGEST_MAX_SIZE;
195    char ed1[50], ed2[50];
196
197    /* Reset the win32 filter that strips header stream out of the file */
198    win32filter.init();
199
200    /* Check the size if possible */
201    if (check_size && size >= 0) {
202       if (attr->type == FT_REG && size != attr->statp.st_size) {
203          Dmsg1(50, "Size comparison failed for %s\n", jcr->last_fname);
204          Jmsg(jcr, M_INFO, 0,
205               _("   st_size  differs on \"%s\". Vol: %s File: %s\n"),
206               jcr->last_fname,
207               edit_int64(size, ed1),
208               edit_int64((int64_t)attr->statp.st_size, ed2));
209          jcr->setJobStatus(JS_Differences);
210       }
211       reset_size();
212    }
213
214    /* Compute the digest and store it */
215    *digest = 0;
216    if (jcr->crypto.digest) {
217       if (!crypto_digest_finalize(jcr->crypto.digest, buf, &len)) {
218          Dmsg1(50, "Unable to finalize digest for %s\n", jcr->last_fname);
219          rtn = false;
220
221       } else {
222          bin_to_base64(digest, sizeof(digest), (char *)buf, len, true);
223       }
224       crypto_digest_free(jcr->crypto.digest);
225       jcr->crypto.digest = NULL;
226    }
227    return rtn;
228 }
229
230 /*
231  * Verify attributes or data of the requested files on the Volume
232  *
233  */
234 void do_verify_volume(JCR *jcr)
235 {
236    BSOCK *sd, *dir;
237    uint32_t size;
238    uint32_t VolSessionId, VolSessionTime, file_index;
239    char digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
240    int stat;
241    char *wbuf;                        /* write buffer */
242    uint32_t wsize;                    /* write size */
243    uint32_t rsize;                    /* read size */
244    bool msg_encrypt = false;
245    bool do_chksum;
246    v_ctx vctx(jcr);
247    ATTR *attr = vctx.attr;
248
249    sd = jcr->store_bsock;
250    if (!sd) {
251       Jmsg(jcr, M_FATAL, 0, _("Storage command not issued before Verify.\n"));
252       jcr->setJobStatus(JS_FatalError);
253       return;
254    }
255    dir = jcr->dir_bsock;
256    jcr->setJobStatus(JS_Running);
257
258    LockRes();
259    CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
260    UnlockRes();
261    uint32_t buf_size;
262    if (client) {
263       buf_size = client->max_network_buffer_size;
264    } else {
265       buf_size = 0;                   /* use default */
266    }
267    if (!sd->set_buffer_size(buf_size, BNET_SETBUF_WRITE)) {
268       jcr->setJobStatus(JS_FatalError);
269       return;
270    }
271    jcr->buf_size = sd->msglen;
272
273    /* use the same buffer size to decompress both gzip and lzo */
274    if (have_libz || have_lzo) {
275       uint32_t compress_buf_size = jcr->buf_size + 12 + ((jcr->buf_size+999) / 1000) + 100;
276       jcr->compress_buf = get_memory(compress_buf_size);
277       jcr->compress_buf_size = compress_buf_size;
278    }
279
280    GetMsg *fdmsg = New(GetMsg(jcr, sd, rec_header, GETMSG_MAX_MSG_SIZE));
281    fdmsg->start_read_sock();
282    bmessage *bmsg = New(bmessage(GETMSG_MAX_MSG_SIZE));
283
284    /*
285     * Get a record from the Storage daemon
286     */
287    while (fdmsg->bget_msg(&bmsg) >= 0 && !job_canceled(jcr)) {
288       /* Remember previous stream type */
289       vctx.prev_stream = vctx.stream;
290
291       /*
292        * First we expect a Stream Record Header
293        */
294       if (sscanf(bmsg->rbuf, rec_header, &VolSessionId, &VolSessionTime, &file_index,
295           &vctx.full_stream, &size) != 5) {
296          Jmsg1(jcr, M_FATAL, 0, _("Record header scan error: %s\n"), bmsg->rbuf);
297          goto bail_out;
298       }
299       vctx.stream = vctx.full_stream & STREAMMASK_TYPE;
300       Dmsg4(30, "Got hdr: FilInx=%d FullStream=%d Stream=%d size=%d.\n",
301             file_index, vctx.full_stream, vctx.stream, size);
302
303       /*
304        * Now we expect the Stream Data
305        */
306       if (fdmsg->bget_msg(&bmsg) < 0) {
307          Jmsg1(jcr, M_FATAL, 0, _("Data record error. ERR=%s\n"), sd->bstrerror());
308          goto bail_out;
309       }
310       if (size != ((uint32_t)bmsg->origlen)) {
311          Jmsg2(jcr, M_FATAL, 0, _("Actual data size %d not same as header %d\n"), bmsg->origlen, size);
312          goto bail_out;
313       }
314       Dmsg2(30, "Got stream data %s, len=%d\n", stream_to_ascii(vctx.stream), bmsg->rbuflen);
315
316       /* File Attributes stream */
317       switch (vctx.stream) {
318       case STREAM_UNIX_ATTRIBUTES:
319       case STREAM_UNIX_ATTRIBUTES_EX:
320          Dmsg0(400, "Stream=Unix Attributes.\n");
321          if (!vctx.close_previous_stream()) {
322             goto bail_out;
323          }
324          /*
325           * Unpack attributes and do sanity check them
326           */
327          if (!unpack_attributes_record(jcr, vctx.stream,
328                                        bmsg->rbuf, bmsg->rbuflen, attr)) {
329             goto bail_out;
330          }
331
332          attr->data_stream = decode_stat(attr->attr, &attr->statp,
333                                          sizeof(attr->statp), &attr->LinkFI);
334
335          jcr->lock();
336          jcr->JobFiles++;
337          jcr->num_files_examined++;
338          pm_strcpy(jcr->last_fname, attr->fname); /* last file examined */
339          jcr->unlock();
340
341          if (jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
342             /*
343              * Send file attributes to Director
344              *   File_index
345              *   Stream
346              *   Verify Options
347              *   Filename (full path)
348              *   Encoded attributes
349              *   Link name (if type==FT_LNK)
350              * For a directory, link is the same as fname, but with trailing
351              * slash. For a linked file, link is the link.
352              */
353             /* Send file attributes to Director */
354             Dmsg2(200, "send ATTR inx=%d fname=%s\n", jcr->JobFiles, attr->fname);
355             if (attr->type == FT_LNK || attr->type == FT_LNKSAVED) {
356                stat = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles,
357                                  STREAM_UNIX_ATTRIBUTES, "pinsug5", attr->fname,
358                                  0, attr->attr, 0, attr->lname, 0);
359                /* for a deleted record, we set fileindex=0 */
360             } else if (attr->type == FT_DELETED)  {
361                stat = dir->fsend("%d %d %s %s%c%s%c%c", 0,
362                                  STREAM_UNIX_ATTRIBUTES, "pinsug5", attr->fname,
363                                  0, attr->attr, 0, 0);
364             } else {
365                stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles,
366                                  STREAM_UNIX_ATTRIBUTES, "pinsug5", attr->fname,
367                                  0, attr->attr, 0, 0);
368             }
369             Dmsg2(200, "bfiled>bdird: attribs len=%d: msg=%s\n", dir->msglen, dir->msg);
370             if (!stat) {
371                Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), dir->bstrerror());
372                goto bail_out;
373             }
374          }
375          break;
376
377          /*
378           * Restore stream object is counted, but not restored here
379           */
380       case STREAM_RESTORE_OBJECT:
381          jcr->lock();
382          jcr->JobFiles++;
383          jcr->num_files_examined++;
384          jcr->unlock();
385          break;
386
387       default:
388          break;
389       }
390
391       const char *digest_code = NULL;
392
393       switch(vctx.stream) {
394       case STREAM_MD5_DIGEST:
395          bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_MD5_SIZE, true);
396          digest_code = "MD5";
397          break;
398
399       case STREAM_SHA1_DIGEST:
400          bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA1_SIZE, true);
401          digest_code = "SHA1";
402          break;
403
404       case STREAM_SHA256_DIGEST:
405          bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA256_SIZE, true);
406          digest_code = "SHA256";
407          break;
408
409       case STREAM_SHA512_DIGEST:
410          bin_to_base64(digest, sizeof(digest), (char *)bmsg->rbuf, CRYPTO_DIGEST_SHA512_SIZE, true);
411          digest_code = "SHA512";
412          break;
413
414       default:
415          *digest = 0;
416          break;
417       }
418
419       if (digest_code && jcr->getJobLevel() == L_VERIFY_VOLUME_TO_CATALOG) {
420          dir->fsend("%d %d %s *%s-%d*", jcr->JobFiles, vctx.stream,
421                     digest, digest_code, jcr->JobFiles);
422
423       } else if (jcr->getJobLevel() == L_VERIFY_DATA) {
424
425          /* Compare digest */
426          if (vctx.check_chksum && *digest) {
427             /* probably an empty file, we can create an empty crypto session */
428             if (!jcr->crypto.digest) {
429                jcr->crypto.digest = crypto_digest_new(jcr, vctx.digesttype);
430             }
431             vctx.close_previous_stream();
432             if (strncmp(digest, vctx.digest,
433                         MIN(sizeof(digest), sizeof(vctx.digest))) != 0)
434             {
435                Jmsg(jcr, M_INFO, 0,
436                     _("   %s differs on \"%s\". File=%s Vol=%s\n"),
437                     stream_to_ascii(vctx.stream), jcr->last_fname,
438                     vctx.digest, digest);
439                jcr->setJobStatus(JS_Differences);
440                Dmsg3(50, "Signature verification failed for %s %s != %s\n",
441                      jcr->last_fname, digest, vctx.digest);
442             }
443          }
444
445          /* Compute size and checksum for level=Data */
446          switch (vctx.stream) {
447          case STREAM_ENCRYPTED_FILE_DATA:
448          case STREAM_ENCRYPTED_WIN32_DATA:
449          case STREAM_ENCRYPTED_FILE_GZIP_DATA:
450          case STREAM_ENCRYPTED_WIN32_GZIP_DATA:
451          case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA:
452          case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA:
453             if (!msg_encrypt) {
454                Jmsg(jcr, M_WARNING, 0,
455                   _("Verification of encrypted file data is not supported.\n"));
456                msg_encrypt = true;
457             }
458             break;
459
460          case STREAM_PLUGIN_DATA:
461          case STREAM_FILE_DATA:
462          case STREAM_SPARSE_DATA:
463          case STREAM_WIN32_DATA:
464          case STREAM_GZIP_DATA:
465          case STREAM_SPARSE_GZIP_DATA:
466          case STREAM_WIN32_GZIP_DATA:
467          case STREAM_COMPRESSED_DATA:
468          case STREAM_SPARSE_COMPRESSED_DATA:
469          case STREAM_WIN32_COMPRESSED_DATA:
470             do_chksum=true;
471             if (!(attr->type ==  FT_RAW || attr->type == FT_FIFO || attr->type == FT_REG)) {
472                break;
473             }
474
475             wbuf = bmsg->rbuf;
476             rsize = bmsg->rbuflen;
477             jcr->ReadBytes += rsize;
478             wsize = rsize;
479
480             if (vctx.stream == STREAM_SPARSE_DATA
481                 || vctx.stream == STREAM_SPARSE_COMPRESSED_DATA
482                 || vctx.stream == STREAM_SPARSE_GZIP_DATA) {
483                vctx.skip_sparse_header(&wbuf, &wsize);
484             }
485
486             /* On Windows, the checksum is computed after the compression
487              * On Unix, the checksum is computed before the compression
488              */
489             if (vctx.stream == STREAM_WIN32_GZIP_DATA
490                 || vctx.stream == STREAM_WIN32_DATA
491                 || vctx.stream == STREAM_WIN32_COMPRESSED_DATA
492                 || vctx.stream == STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA
493                 || vctx.stream == STREAM_ENCRYPTED_WIN32_GZIP_DATA)
494             {
495                do_chksum = false;
496                vctx.update_checksum(wbuf, wsize);
497             }
498
499             if (vctx.stream == STREAM_GZIP_DATA
500                 || vctx.stream == STREAM_SPARSE_GZIP_DATA
501                 || vctx.stream == STREAM_WIN32_GZIP_DATA
502                 || vctx.stream == STREAM_ENCRYPTED_FILE_GZIP_DATA
503                 || vctx.stream == STREAM_COMPRESSED_DATA
504                 || vctx.stream == STREAM_SPARSE_COMPRESSED_DATA
505                 || vctx.stream == STREAM_WIN32_COMPRESSED_DATA
506                 || vctx.stream == STREAM_ENCRYPTED_FILE_COMPRESSED_DATA
507                 || vctx.stream == STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA
508                 || vctx.stream == STREAM_ENCRYPTED_WIN32_GZIP_DATA) {
509
510                if (!decompress_data(jcr, vctx.stream, &wbuf, &wsize)) {
511                   dequeue_messages(jcr);
512                   goto bail_out;
513                }
514             }
515
516             /* Unix way to deal with checksums */
517             if (do_chksum) {
518                vctx.update_checksum(wbuf, wsize);
519             }
520
521             if (vctx.stream == STREAM_WIN32_GZIP_DATA
522                 || vctx.stream == STREAM_WIN32_DATA
523                 || vctx.stream == STREAM_WIN32_COMPRESSED_DATA
524                 || vctx.stream == STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA
525                 || vctx.stream == STREAM_ENCRYPTED_WIN32_GZIP_DATA) {
526
527                int64_t wbuf_len = wsize;
528                int64_t wsize64 = 0;
529                if (vctx.win32filter.have_data(&wbuf, &wbuf_len, &wsize64)) {
530                   wsize = wsize64;
531                }
532             }
533             jcr->JobBytes += wsize;
534             vctx.update_size(wsize);
535             break;
536
537             /* TODO: Handle data to compute checksums */
538             /* Ignore everything else */
539          default:
540             break;
541          }
542       } /* end switch */
543    } /* end while bnet_get */
544    if (!vctx.close_previous_stream()) {
545       goto bail_out;
546    }
547    jcr->setJobStatus(JS_Terminated);
548    goto ok_out;
549
550 bail_out:
551    jcr->setJobStatus(JS_ErrorTerminated);
552
553 ok_out:
554    Dmsg0(215, "wait BufferedMsg\n");
555    fdmsg->wait_read_sock();
556    delete bmsg;
557    free_GetMsg(fdmsg);
558    if (jcr->compress_buf) {
559       free_pool_memory(jcr->compress_buf);
560       jcr->compress_buf = NULL;
561    }
562    /* TODO: We probably want to mark the job as failed if we have errors */
563    Dmsg2(50, "End Verify-Vol. Files=%d Bytes=%" lld "\n", jcr->JobFiles,
564       jcr->JobBytes);
565 }