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