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