1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 * Bacula File Daemon verify.c Verify files.
21 *
22 * Kern Sibbald, October MM
23 *
24 */
25
26 #include "bacula.h"
27 #include "filed.h"
28
29 static int verify_file(JCR *jcr, FF_PKT *ff_pkt, bool);
30 static int read_digest(BFILE *bfd, DIGEST *digest, JCR *jcr);
31
32 /*
33 * Find all the requested files and send attributes
34 * to the Director.
35 *
36 */
do_verify(JCR * jcr)37 void do_verify(JCR *jcr)
38 {
39 jcr->setJobStatus(JS_Running);
40
41 LockRes();
42 CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
43 UnlockRes();
44
45 if (client) {
46 jcr->buf_size = client->max_network_buffer_size ?
47 client->max_network_buffer_size : DEFAULT_NETWORK_BUFFER_SIZE;
48 } else {
49 jcr->buf_size = DEFAULT_NETWORK_BUFFER_SIZE; /* use default */
50 }
51 if ((jcr->big_buf = (char *) malloc(jcr->buf_size)) == NULL) {
52 Jmsg1(jcr, M_ABORT, 0, _("Cannot malloc %d network read buffer\n"),
53 DEFAULT_NETWORK_BUFFER_SIZE);
54 }
55 set_find_options((FF_PKT *)jcr->ff, jcr->incremental, jcr->mtime);
56 Dmsg0(10, "Start find files\n");
57 /* Subroutine verify_file() is called for each file */
58 find_files(jcr, (FF_PKT *)jcr->ff, verify_file, NULL);
59 Dmsg0(10, "End find files\n");
60
61 if (jcr->big_buf) {
62 free(jcr->big_buf);
63 jcr->big_buf = NULL;
64 }
65 jcr->setJobStatus(JS_Terminated);
66 }
67
68 /*
69 * Called here by find() for each file.
70 *
71 * Find the file, compute the MD5 or SHA1 and send it back to the Director
72 */
verify_file(JCR * jcr,FF_PKT * ff_pkt,bool top_level)73 static int verify_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level)
74 {
75 char attribs[MAXSTRING];
76 char attribsEx[MAXSTRING];
77 int digest_stream = STREAM_NONE;
78 int stat;
79 DIGEST *digest = NULL;
80 BSOCK *dir;
81
82 if (job_canceled(jcr)) {
83 return 0;
84 }
85
86 dir = jcr->dir_bsock;
87 jcr->num_files_examined++; /* bump total file count */
88
89 switch (ff_pkt->type) {
90 case FT_LNKSAVED: /* Hard linked, file already saved */
91 Dmsg2(30, "FT_LNKSAVED saving: %s => %s\n", ff_pkt->fname, ff_pkt->link);
92 break;
93 case FT_REGE:
94 Dmsg1(30, "FT_REGE saving: %s\n", ff_pkt->fname);
95 break;
96 case FT_REG:
97 Dmsg1(30, "FT_REG saving: %s\n", ff_pkt->fname);
98 break;
99 case FT_LNK:
100 Dmsg2(30, "FT_LNK saving: %s -> %s\n", ff_pkt->fname, ff_pkt->link);
101 break;
102 case FT_DIRBEGIN:
103 jcr->num_files_examined--; /* correct file count */
104 return 1; /* ignored */
105 case FT_REPARSE:
106 case FT_JUNCTION:
107 case FT_DIREND:
108 Dmsg1(30, "FT_DIR saving: %s\n", ff_pkt->fname);
109 break;
110 case FT_SPEC:
111 Dmsg1(30, "FT_SPEC saving: %s\n", ff_pkt->fname);
112 break;
113 case FT_RAW:
114 Dmsg1(30, "FT_RAW saving: %s\n", ff_pkt->fname);
115 break;
116 case FT_FIFO:
117 Dmsg1(30, "FT_FIFO saving: %s\n", ff_pkt->fname);
118 break;
119 case FT_NOACCESS: {
120 berrno be;
121 be.set_errno(ff_pkt->ff_errno);
122 Jmsg(jcr, M_NOTSAVED, 1, _(" Could not access %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
123 jcr->JobErrors++;
124 return 1;
125 }
126 case FT_NOFOLLOW: {
127 berrno be;
128 be.set_errno(ff_pkt->ff_errno);
129 Jmsg(jcr, M_NOTSAVED, 1, _(" Could not follow link %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
130 jcr->JobErrors++;
131 return 1;
132 }
133 case FT_NOSTAT: {
134 berrno be;
135 be.set_errno(ff_pkt->ff_errno);
136 Jmsg(jcr, M_NOTSAVED, 1, _(" Could not stat %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
137 jcr->JobErrors++;
138 return 1;
139 }
140 case FT_DIRNOCHG:
141 case FT_NOCHG:
142 Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname);
143 return 1;
144 case FT_ISARCH:
145 Jmsg(jcr, M_SKIPPED, 1, _(" Archive file skipped: %s\n"), ff_pkt->fname);
146 return 1;
147 case FT_NORECURSE:
148 Jmsg(jcr, M_SKIPPED, 1, _(" Recursion turned off. Directory skipped: %s\n"), ff_pkt->fname);
149 ff_pkt->type = FT_DIREND; /* directory entry was backed up */
150 break;
151 case FT_NOFSCHG:
152 Jmsg(jcr, M_SKIPPED, 1, _(" File system change prohibited. Directory skipped: %s\n"), ff_pkt->fname);
153 return 1;
154 case FT_PLUGIN_CONFIG:
155 case FT_RESTORE_FIRST:
156 return 1; /* silently skip */
157 case FT_NOOPEN: {
158 berrno be;
159 be.set_errno(ff_pkt->ff_errno);
160 Jmsg(jcr, M_NOTSAVED, 1, _(" Could not open directory %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror());
161 jcr->JobErrors++;
162 return 1;
163 }
164 default:
165 Jmsg(jcr, M_NOTSAVED, 0, _(" Unknown file type %d: %s\n"), ff_pkt->type, ff_pkt->fname);
166 jcr->JobErrors++;
167 return 1;
168 }
169
170 /* Encode attributes and possibly extend them */
171 encode_stat(attribs, &ff_pkt->statp, sizeof(ff_pkt->statp), ff_pkt->LinkFI, 0);
172 encode_attribsEx(jcr, attribsEx, ff_pkt);
173
174 jcr->lock();
175 jcr->JobFiles++; /* increment number of files sent */
176 pm_strcpy(jcr->last_fname, ff_pkt->fname);
177 jcr->unlock();
178
179 /*
180 * Send file attributes to Director
181 * File_index
182 * Stream
183 * Verify Options
184 * Filename (full path)
185 * Encoded attributes
186 * Link name (if type==FT_LNK)
187 * For a directory, link is the same as fname, but with trailing
188 * slash. For a linked file, link is the link.
189 */
190 /* Send file attributes to Director (note different format than for Storage) */
191 Dmsg2(400, "send ATTR inx=%d fname=%s\n", jcr->JobFiles, ff_pkt->fname);
192 if (ff_pkt->type == FT_LNK || ff_pkt->type == FT_LNKSAVED) {
193 stat = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles,
194 STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname,
195 0, attribs, 0, ff_pkt->link, 0);
196 } else if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE ||
197 ff_pkt->type == FT_JUNCTION) {
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"), dir->bstrerror());
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 */
digest_file(JCR * jcr,FF_PKT * ff_pkt,DIGEST * digest)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 || ff_pkt->type == FT_RAW
296 || ff_pkt->type == FT_FIFO) {
297 int noatime = ff_pkt->flags & FO_NOATIME ? O_NOATIME : 0;
298 if ((bopen(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0)) < 0) {
299 ff_pkt->ff_errno = errno;
300 berrno be;
301 be.set_errno(bfd.berrno);
302 Dmsg2(100, "Cannot open %s: ERR=%s\n", ff_pkt->fname, be.bstrerror());
303 Jmsg(jcr, M_ERROR, 1, _(" Cannot open %s: ERR=%s.\n"),
304 ff_pkt->fname, be.bstrerror());
305 return 1;
306 }
307 read_digest(&bfd, digest, jcr);
308 bclose(&bfd);
309 }
310
311 #ifdef HAVE_DARWIN_OS
312 /* Open resource fork if necessary */
313 if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->hfsinfo.rsrclength > 0) {
314 if (bopen_rsrc(&bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) {
315 ff_pkt->ff_errno = errno;
316 berrno be;
317 Jmsg(jcr, M_ERROR, -1, _(" Cannot open resource fork for %s: ERR=%s.\n"),
318 ff_pkt->fname, be.bstrerror());
319 if (is_bopen(&ff_pkt->bfd)) {
320 bclose(&ff_pkt->bfd);
321 }
322 return 1;
323 }
324 read_digest(&bfd, digest, jcr);
325 bclose(&bfd);
326 }
327 if (digest && ff_pkt->flags & FO_HFSPLUS) {
328 crypto_digest_update(digest, (uint8_t *)ff_pkt->hfsinfo.fndrinfo, 32);
329 }
330 #endif
331 return 0;
332 }
333
334 /*
335 * Read message digest of bfd, updating digest
336 * In case of errors we need the job control record and file name.
337 */
read_digest(BFILE * bfd,DIGEST * digest,JCR * jcr)338 static int read_digest(BFILE *bfd, DIGEST *digest, JCR *jcr)
339 {
340 char *buf;
341 int64_t n;
342 int64_t bufsiz = jcr->buf_size;
343 FF_PKT *ff_pkt = (FF_PKT *)jcr->ff;
344 uint64_t fileAddr = 0; /* file address */
345
346 buf = (char *)malloc(bufsiz);
347 Dmsg0(50, "=== read_digest\n");
348 /* With this option, we read shorter blocks, so to not break the
349 * sparse block detection, we need to adjust the read size.
350 */
351 if (ff_pkt->flags & FO_SPARSE) {
352 bufsiz -= OFFSET_FADDR_SIZE;
353 }
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 free(buf);
382 if (n < 0) {
383 berrno be;
384 be.set_errno(bfd->berrno);
385 Dmsg2(100, "Error reading file %s: ERR=%s\n", jcr->last_fname, be.bstrerror());
386 Jmsg(jcr, M_ERROR, 1, _("Error reading file %s: ERR=%s\n"),
387 jcr->last_fname, be.bstrerror());
388 jcr->JobErrors++;
389 return -1;
390 }
391 return 0;
392 }
393