]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/verify.c
Reorganize regex code
[bacula/bacula] / bacula / src / filed / verify.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2010 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  */
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    set_jcr_job_status(jcr, 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    set_jcr_job_status(jcr, 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_DIREND:
112       Dmsg1(30, "FT_DIR saving: %s\n", ff_pkt->fname);
113       break;
114    case FT_SPEC:
115       Dmsg1(30, "FT_SPEC saving: %s\n", ff_pkt->fname);
116       break;
117    case FT_RAW:
118       Dmsg1(30, "FT_RAW saving: %s\n", ff_pkt->fname);
119       break;
120    case FT_FIFO:
121       Dmsg1(30, "FT_FIFO saving: %s\n", ff_pkt->fname);
122       break;
123    case FT_NOACCESS: {
124       berrno be;
125       be.set_errno(ff_pkt->ff_errno);
126       Jmsg(jcr, M_NOTSAVED, 1, _("     Could not access %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
127       jcr->JobErrors++;
128       return 1;
129    }
130    case FT_NOFOLLOW: {
131       berrno be;
132       be.set_errno(ff_pkt->ff_errno);
133       Jmsg(jcr, M_NOTSAVED, 1, _("     Could not follow link %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
134       jcr->JobErrors++;
135       return 1;
136    }
137    case FT_NOSTAT: {
138       berrno be;
139       be.set_errno(ff_pkt->ff_errno);
140       Jmsg(jcr, M_NOTSAVED, 1, _("     Could not stat %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
141       jcr->JobErrors++;
142       return 1;
143    }
144    case FT_DIRNOCHG:
145    case FT_NOCHG:
146       Jmsg(jcr, M_SKIPPED, 1, _("     Unchanged file skipped: %s\n"), ff_pkt->fname);
147       return 1;
148    case FT_ISARCH:
149       Jmsg(jcr, M_SKIPPED, 1, _("     Archive file skipped: %s\n"), ff_pkt->fname);
150       return 1;
151    case FT_NORECURSE:
152       Jmsg(jcr, M_SKIPPED, 1, _("     Recursion turned off. Directory skipped: %s\n"), ff_pkt->fname);
153       ff_pkt->type = FT_DIREND;     /* directory entry was backed up */
154       break;
155    case FT_NOFSCHG:
156       Jmsg(jcr, M_SKIPPED, 1, _("     File system change prohibited. Directory skipped: %s\n"), ff_pkt->fname);
157       return 1;
158    case FT_NOOPEN: {
159       berrno be;
160       be.set_errno(ff_pkt->ff_errno);
161       Jmsg(jcr, M_NOTSAVED, 1, _("     Could not open directory %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
162       jcr->JobErrors++;
163       return 1;
164    }
165    default:
166       Jmsg(jcr, M_NOTSAVED, 0, _("     Unknown file type %d: %s\n"), ff_pkt->type, ff_pkt->fname);
167       jcr->JobErrors++;
168       return 1;
169    }
170
171    /* Encode attributes and possibly extend them */
172    encode_stat(attribs, &ff_pkt->statp, ff_pkt->LinkFI, 0);
173    encode_attribsEx(jcr, attribsEx, ff_pkt);
174
175    jcr->lock();
176    jcr->JobFiles++;                  /* increment number of files sent */
177    pm_strcpy(jcr->last_fname, ff_pkt->fname);
178    jcr->unlock();
179
180    /*
181     * Send file attributes to Director
182     *   File_index
183     *   Stream
184     *   Verify Options
185     *   Filename (full path)
186     *   Encoded attributes
187     *   Link name (if type==FT_LNK)
188     * For a directory, link is the same as fname, but with trailing
189     * slash. For a linked file, link is the link.
190     */
191    /* Send file attributes to Director (note different format than for Storage) */
192    Dmsg2(400, "send ATTR inx=%d fname=%s\n", jcr->JobFiles, ff_pkt->fname);
193    if (ff_pkt->type == FT_LNK || ff_pkt->type == FT_LNKSAVED) {
194       stat = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles,
195                         STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
196                         0, attribs, 0, ff_pkt->link, 0);
197    } else if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE) {
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"), bnet_strerror(dir));
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  */
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 ||
296        ff_pkt->type == FT_RAW ||
297        ff_pkt->type == FT_FIFO) {
298       int noatime = ff_pkt->flags & FO_NOATIME ? O_NOATIME : 0;
299       if ((bopen(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0)) < 0) {
300          ff_pkt->ff_errno = errno;
301          berrno be;
302          be.set_errno(bfd.berrno);
303          Dmsg2(100, "Cannot open %s: ERR=%s\n", ff_pkt->fname, be.bstrerror());
304          Jmsg(jcr, M_ERROR, 1, _("     Cannot open %s: ERR=%s.\n"),
305                ff_pkt->fname, be.bstrerror());
306          return 1;
307       }
308       read_digest(&bfd, digest, jcr);
309       bclose(&bfd);
310    }
311
312    if (have_darwin_os) {
313       /* Open resource fork if necessary */
314       if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->hfsinfo.rsrclength > 0) {
315          if (bopen_rsrc(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) {
316             ff_pkt->ff_errno = errno;
317             berrno be;
318             Jmsg(jcr, M_ERROR, -1, _("     Cannot open resource fork for %s: ERR=%s.\n"),
319                   ff_pkt->fname, be.bstrerror());
320             if (is_bopen(&ff_pkt->bfd)) {
321                bclose(&ff_pkt->bfd);
322             }
323             return 1;
324          }
325          read_digest(&bfd, digest, jcr);
326          bclose(&bfd);
327       }
328
329       if (digest && ff_pkt->flags & FO_HFSPLUS) {
330          crypto_digest_update(digest, (uint8_t *)ff_pkt->hfsinfo.fndrinfo, 32);
331       }
332    }
333    return 0;
334 }
335
336 /*
337  * Read message digest of bfd, updating digest
338  * In case of errors we need the job control record and file name.
339  */
340 static int read_digest(BFILE *bfd, DIGEST *digest, JCR *jcr)
341 {
342    char buf[DEFAULT_NETWORK_BUFFER_SIZE];
343    int64_t n;
344    int64_t bufsiz = (int64_t)sizeof(buf);
345    FF_PKT *ff_pkt = (FF_PKT *)jcr->ff;
346    uint64_t fileAddr = 0;             /* file address */
347
348
349    Dmsg0(50, "=== read_digest\n");
350    while ((n=bread(bfd, buf, bufsiz)) > 0) {
351       /* Check for sparse blocks */
352       if (ff_pkt->flags & FO_SPARSE) {
353          bool allZeros = false;
354          if ((n == bufsiz &&
355               fileAddr+n < (uint64_t)ff_pkt->statp.st_size) ||
356              ((ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO) &&
357                (uint64_t)ff_pkt->statp.st_size == 0)) {
358             allZeros = is_buf_zero(buf, bufsiz);
359          }
360          fileAddr += n;               /* update file address */
361          /* Skip any block of all zeros */
362          if (allZeros) {
363             continue;                 /* skip block of zeros */
364          }
365       }
366       
367       crypto_digest_update(digest, (uint8_t *)buf, n);
368
369       /* Can be used by BaseJobs, update only for Verify jobs */
370       if (jcr->getJobLevel() != L_FULL) {
371          jcr->JobBytes += n;
372          jcr->ReadBytes += n;
373       }
374    }
375    if (n < 0) {
376       berrno be;
377       be.set_errno(bfd->berrno);
378       Dmsg2(100, "Error reading file %s: ERR=%s\n", jcr->last_fname, be.bstrerror());
379       Jmsg(jcr, M_ERROR, 1, _("Error reading file %s: ERR=%s\n"),
380             jcr->last_fname, be.bstrerror());
381       jcr->JobErrors++;
382       return -1;
383    }
384    return 0;
385 }