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