]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/verify.c
As it seems Linux doesn't comply to its own manpages.
[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_RESTORE_FIRST:
160       return 1;                       /* silently skip */
161    case FT_NOOPEN: {
162       berrno be;
163       be.set_errno(ff_pkt->ff_errno);
164       Jmsg(jcr, M_NOTSAVED, 1, _("     Could not open directory %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
165       jcr->JobErrors++;
166       return 1;
167    }
168    default:
169       Jmsg(jcr, M_NOTSAVED, 0, _("     Unknown file type %d: %s\n"), ff_pkt->type, ff_pkt->fname);
170       jcr->JobErrors++;
171       return 1;
172    }
173
174    /* Encode attributes and possibly extend them */
175    encode_stat(attribs, &ff_pkt->statp, sizeof(ff_pkt->statp), ff_pkt->LinkFI, 0);
176    encode_attribsEx(jcr, attribsEx, ff_pkt);
177
178    jcr->lock();
179    jcr->JobFiles++;                  /* increment number of files sent */
180    pm_strcpy(jcr->last_fname, ff_pkt->fname);
181    jcr->unlock();
182
183    /*
184     * Send file attributes to Director
185     *   File_index
186     *   Stream
187     *   Verify Options
188     *   Filename (full path)
189     *   Encoded attributes
190     *   Link name (if type==FT_LNK)
191     * For a directory, link is the same as fname, but with trailing
192     * slash. For a linked file, link is the link.
193     */
194    /* Send file attributes to Director (note different format than for Storage) */
195    Dmsg2(400, "send ATTR inx=%d fname=%s\n", jcr->JobFiles, ff_pkt->fname);
196    if (ff_pkt->type == FT_LNK || ff_pkt->type == FT_LNKSAVED) {
197       stat = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles,
198                         STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
199                         0, attribs, 0, ff_pkt->link, 0);
200    } else if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE ||
201               ff_pkt->type == FT_JUNCTION) {
202       /* Here link is the canonical filename (i.e. with trailing slash) */
203       stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles,
204                         STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->link,
205                         0, attribs, 0, 0);
206    } else {
207       stat = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles,
208                         STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
209                         0, attribs, 0, 0);
210    }
211    Dmsg2(20, "bfiled>bdird: attribs len=%d: msg=%s\n", dir->msglen, dir->msg);
212    if (!stat) {
213       Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), bnet_strerror(dir));
214       return 0;
215    }
216
217    /*
218     * The remainder of the function is all about getting the checksum.
219     * First we initialise, then we read files, other streams and Finder Info.
220     */
221    if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) &&
222             ff_pkt->flags & (FO_MD5|FO_SHA1|FO_SHA256|FO_SHA512))) {
223       /*
224        * Create our digest context. If this fails, the digest will be set to NULL
225        * and not used.
226        */
227       if (ff_pkt->flags & FO_MD5) {
228          digest = crypto_digest_new(jcr, CRYPTO_DIGEST_MD5);
229          digest_stream = STREAM_MD5_DIGEST;
230
231       } else if (ff_pkt->flags & FO_SHA1) {
232          digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA1);
233          digest_stream = STREAM_SHA1_DIGEST;
234
235       } else if (ff_pkt->flags & FO_SHA256) {
236          digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA256);
237          digest_stream = STREAM_SHA256_DIGEST;
238
239       } else if (ff_pkt->flags & FO_SHA512) {
240          digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA512);
241          digest_stream = STREAM_SHA512_DIGEST;
242       }
243
244       /* Did digest initialization fail? */
245       if (digest_stream != STREAM_NONE && digest == NULL) {
246          Jmsg(jcr, M_WARNING, 0, _("%s digest initialization failed\n"),
247               stream_to_ascii(digest_stream));
248       }
249
250       /* compute MD5 or SHA1 hash */
251       if (digest) {
252          char md[CRYPTO_DIGEST_MAX_SIZE];
253          uint32_t size;
254
255          size = sizeof(md);
256          
257          if (digest_file(jcr, ff_pkt, digest) != 0) {
258             jcr->JobErrors++;
259             goto good_rtn;
260          }
261
262          if (crypto_digest_finalize(digest, (uint8_t *)md, &size)) {
263             char *digest_buf;
264             const char *digest_name;
265             
266             digest_buf = (char *)malloc(BASE64_SIZE(size));
267             digest_name = crypto_digest_name(digest);
268
269             bin_to_base64(digest_buf, BASE64_SIZE(size), md, size, true);
270             Dmsg3(400, "send inx=%d %s=%s\n", jcr->JobFiles, digest_name, digest_buf);
271             dir->fsend("%d %d %s *%s-%d*", jcr->JobFiles, digest_stream, digest_buf,
272                        digest_name, jcr->JobFiles);
273             Dmsg3(20, "bfiled>bdird: %s len=%d: msg=%s\n", digest_name,
274                   dir->msglen, dir->msg);
275
276             free(digest_buf);
277          }
278       }
279    }
280
281 good_rtn:
282    if (digest) {
283       crypto_digest_free(digest);
284    }
285    return 1;
286 }
287
288 /*
289  * Compute message digest for the file specified by ff_pkt.
290  * In case of errors we need the job control record and file name.
291  */
292 int digest_file(JCR *jcr, FF_PKT *ff_pkt, DIGEST *digest)
293 {
294    BFILE bfd;
295
296    Dmsg0(50, "=== digest_file\n");
297    binit(&bfd);
298
299    if (ff_pkt->statp.st_size > 0 ||
300        ff_pkt->type == FT_RAW ||
301        ff_pkt->type == FT_FIFO) {
302       int noatime = ff_pkt->flags & FO_NOATIME ? O_NOATIME : 0;
303       if ((bopen(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0)) < 0) {
304          ff_pkt->ff_errno = errno;
305          berrno be;
306          be.set_errno(bfd.berrno);
307          Dmsg2(100, "Cannot open %s: ERR=%s\n", ff_pkt->fname, be.bstrerror());
308          Jmsg(jcr, M_ERROR, 1, _("     Cannot open %s: ERR=%s.\n"),
309                ff_pkt->fname, be.bstrerror());
310          return 1;
311       }
312       read_digest(&bfd, digest, jcr);
313       bclose(&bfd);
314    }
315
316    if (have_darwin_os) {
317       /* Open resource fork if necessary */
318       if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->hfsinfo.rsrclength > 0) {
319          if (bopen_rsrc(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) {
320             ff_pkt->ff_errno = errno;
321             berrno be;
322             Jmsg(jcr, M_ERROR, -1, _("     Cannot open resource fork for %s: ERR=%s.\n"),
323                   ff_pkt->fname, be.bstrerror());
324             if (is_bopen(&ff_pkt->bfd)) {
325                bclose(&ff_pkt->bfd);
326             }
327             return 1;
328          }
329          read_digest(&bfd, digest, jcr);
330          bclose(&bfd);
331       }
332
333       if (digest && ff_pkt->flags & FO_HFSPLUS) {
334          crypto_digest_update(digest, (uint8_t *)ff_pkt->hfsinfo.fndrinfo, 32);
335       }
336    }
337    return 0;
338 }
339
340 /*
341  * Read message digest of bfd, updating digest
342  * In case of errors we need the job control record and file name.
343  */
344 static int read_digest(BFILE *bfd, DIGEST *digest, JCR *jcr)
345 {
346    char buf[DEFAULT_NETWORK_BUFFER_SIZE];
347    int64_t n;
348    int64_t bufsiz = (int64_t)sizeof(buf);
349    FF_PKT *ff_pkt = (FF_PKT *)jcr->ff;
350    uint64_t fileAddr = 0;             /* file address */
351
352
353    Dmsg0(50, "=== read_digest\n");
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    if (n < 0) {
382       berrno be;
383       be.set_errno(bfd->berrno);
384       Dmsg2(100, "Error reading file %s: ERR=%s\n", jcr->last_fname, be.bstrerror());
385       Jmsg(jcr, M_ERROR, 1, _("Error reading file %s: ERR=%s\n"),
386             jcr->last_fname, be.bstrerror());
387       jcr->JobErrors++;
388       return -1;
389    }
390    return 0;
391 }