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