]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/catreq.c
Fix update MD5 failure bug reported by Peter Keller
[bacula/bacula] / bacula / src / dird / catreq.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 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  *
21  *   Bacula Director -- catreq.c -- handles the message channel
22  *    catalog request from the Storage daemon.
23  *
24  *     Kern Sibbald, March MMI
25  *
26  *    This routine runs as a thread and must be thread reentrant.
27  *
28  *  Basic tasks done here:
29  *      Handle Catalog services.
30  *
31  */
32
33 #include "bacula.h"
34 #include "dird.h"
35 #include "findlib/find.h"
36
37 /*
38  * Handle catalog request
39  *  For now, we simply return next Volume to be used
40  */
41
42 /* Requests from the Storage daemon */
43 static char Find_media[] = "CatReq Job=%127s FindMedia=%d pool_name=%127s media_type=%127s vol_type=%d\n";
44 static char Get_Vol_Info[] = "CatReq Job=%127s GetVolInfo VolName=%127s write=%d\n";
45
46 static char Update_media[] = "CatReq Job=%127s UpdateMedia VolName=%s"
47    " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%lld VolABytes=%lld"
48    " VolHoleBytes=%lld VolHoles=%u VolMounts=%u"
49    " VolErrors=%u VolWrites=%lld MaxVolBytes=%lld EndTime=%lld VolStatus=%10s"
50    " Slot=%d relabel=%d InChanger=%d VolReadTime=%lld VolWriteTime=%lld"
51    " VolFirstWritten=%lld VolType=%u\n";
52
53 static char Create_jobmedia[] = "CatReq Job=%127s CreateJobMedia\n";
54
55 /* Responses  sent to Storage daemon */
56 static char OK_media[] = "1000 OK VolName=%s VolJobs=%u VolFiles=%u"
57    " VolBlocks=%u VolBytes=%s VolABytes=%s VolHoleBytes=%s VolHoles=%u"
58    " VolMounts=%u VolErrors=%u VolWrites=%s"
59    " MaxVolBytes=%s VolCapacityBytes=%s VolStatus=%s Slot=%d"
60    " MaxVolJobs=%u MaxVolFiles=%u InChanger=%d VolReadTime=%s"
61    " VolWriteTime=%s EndFile=%u EndBlock=%u VolType=%u LabelType=%d"
62    " MediaId=%s ScratchPoolId=%s\n";
63
64 static char OK_create[] = "1000 OK CreateJobMedia\n";
65
66
67 static int send_volume_info_to_storage_daemon(JCR *jcr, BSOCK *sd, MEDIA_DBR *mr)
68 {
69    int stat;
70    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50], ed7[50], ed8[50],
71         ed9[50], ed10[50];
72
73    jcr->MediaId = mr->MediaId;
74    pm_strcpy(jcr->VolumeName, mr->VolumeName);
75    bash_spaces(mr->VolumeName);
76    stat = sd->fsend(OK_media, mr->VolumeName, mr->VolJobs,
77       mr->VolFiles, mr->VolBlocks, edit_uint64(mr->VolBytes, ed1),
78       edit_uint64(mr->VolABytes, ed2),
79       edit_uint64(mr->VolHoleBytes, ed3),
80       mr->VolHoles, mr->VolMounts, mr->VolErrors,
81       edit_uint64(mr->VolWrites, ed4),
82       edit_uint64(mr->MaxVolBytes, ed5),
83       edit_uint64(mr->VolCapacityBytes, ed6),
84       mr->VolStatus, mr->Slot, mr->MaxVolJobs, mr->MaxVolFiles,
85       mr->InChanger,
86       edit_int64(mr->VolReadTime, ed7),
87       edit_int64(mr->VolWriteTime, ed8),
88       mr->EndFile, mr->EndBlock,
89       mr->VolType,
90       mr->LabelType,
91       edit_uint64(mr->MediaId, ed9),
92       edit_uint64(mr->ScratchPoolId, ed10));
93    unbash_spaces(mr->VolumeName);
94    Dmsg2(100, "Vol Info for %s: %s", jcr->Job, sd->msg);
95    return stat;
96 }
97
98 /* TODO: See if we want to let the FD do all kind
99  *       of catalog request/update
100  */
101 void catalog_request(JCR *jcr, BSOCK *bs)
102 {
103    MEDIA_DBR mr, sdmr;
104    JOBMEDIA_DBR jm;
105    char Job[MAX_NAME_LENGTH];
106    char pool_name[MAX_NAME_LENGTH];
107    int index, ok, label, writing;
108    POOLMEM *omsg;
109    POOL_DBR pr;
110    uint64_t MediaId;
111    utime_t VolFirstWritten;
112    utime_t VolLastWritten;
113    int n;
114
115    memset(&sdmr, 0, sizeof(sdmr));
116    memset(&jm, 0, sizeof(jm));
117    Dsm_check(100);
118
119    /*
120     * Request to find next appendable Volume for this Job
121     */
122    Dmsg1(200, "catreq %s", bs->msg);
123    if (!jcr->db) {
124       omsg = get_memory(bs->msglen+1);
125       pm_strcpy(omsg, bs->msg);
126       bs->fsend(_("1990 Invalid Catalog Request: %s"), omsg);
127       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request; DB not open: %s"), omsg);
128       free_memory(omsg);
129       return;
130    }
131    /*
132     * Find next appendable medium for SD
133     */
134    n = sscanf(bs->msg, Find_media, &Job, &index, &pool_name, &mr.MediaType, &mr.VolType);
135    if (n == 5) {
136       memset(&pr, 0, sizeof(pr));
137       bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
138       unbash_spaces(pr.Name);
139       ok = db_get_pool_record(jcr, jcr->db, &pr);
140       if (ok) {
141          mr.PoolId = pr.PoolId;
142          set_storageid_in_mr(jcr->wstore, &mr);
143          mr.ScratchPoolId = pr.ScratchPoolId;
144          ok = find_next_volume_for_append(jcr, &mr, index, fnv_create_vol, fnv_prune);
145          Dmsg3(050, "find_media ok=%d idx=%d vol=%s\n", ok, index, mr.VolumeName);
146       } else {
147          /* Report problem finding pool */
148          Jmsg1(jcr, M_WARNING, 0, _("Pool \"%s\" not found for SD find media request.\n"),
149             pr.Name);
150       }
151       /*
152        * Send Find Media response to Storage daemon
153        */
154       if (ok) {
155          send_volume_info_to_storage_daemon(jcr, bs, &mr);
156       } else {
157          bs->fsend(_("1901 No Media.\n"));
158          Dmsg0(500, "1901 No Media.\n");
159       }
160       goto ok_out;
161    }
162    Dmsg1(1000, "Tried find_media. fields wanted=4, got=%d\n", n);
163
164    /*
165     * Request to find specific Volume information
166     */
167    n = sscanf(bs->msg, Get_Vol_Info, &Job, &mr.VolumeName, &writing);
168    if (n == 3) {
169       Dmsg1(100, "CatReq GetVolInfo Vol=%s\n", mr.VolumeName);
170       /*
171        * Find the Volume
172        */
173       unbash_spaces(mr.VolumeName);
174       if (db_get_media_record(jcr, jcr->db, &mr)) {
175          const char *reason = NULL;           /* detailed reason for rejection */
176          /*
177           * If we are reading, accept any volume (reason == NULL)
178           * If we are writing, check if the Volume is valid
179           *   for this job, and do a recycle if necessary
180           */
181          if (writing) {
182             /*
183              * SD wants to write this Volume, so make
184              *   sure it is suitable for this job, i.e.
185              *   Pool matches, and it is either Append or Recycle
186              *   and Media Type matches and Pool allows any volume.
187              */
188             if (mr.PoolId != jcr->jr.PoolId) {
189                reason = _("not in Pool");
190             } else if (strcmp(mr.MediaType, jcr->wstore->media_type) != 0) {
191                reason = _("not correct MediaType");
192             } else {
193                /*
194                 * Now try recycling if necessary
195                 *   reason set non-NULL if we cannot use it
196                 */
197                check_if_volume_valid_or_recyclable(jcr, &mr, &reason);
198             }
199          }
200          if (!reason && mr.Enabled != 1) {
201             reason = _("is not Enabled");
202          }
203          if (reason == NULL) {
204             /*
205              * Send Find Media response to Storage daemon
206              */
207             send_volume_info_to_storage_daemon(jcr, bs, &mr);
208          } else {
209             /* Not suitable volume */
210             bs->fsend(_("1998 Volume \"%s\" catalog status is %s, %s.\n"), mr.VolumeName,
211                mr.VolStatus, reason);
212          }
213
214       } else {
215          bs->fsend(_("1997 Volume \"%s\" not in catalog.\n"), mr.VolumeName);
216          Dmsg1(100, "1997 Volume \"%s\" not in catalog.\n", mr.VolumeName);
217       }
218       goto ok_out;
219    }
220    Dmsg1(1000, "Tried get_vol_info. fields wanted=3, got=%d\n", n);
221
222
223    /*
224     * Request to update Media record. Comes typically at the end
225     *  of a Storage daemon Job Session, when labeling/relabeling a
226     *  Volume, or when an EOF mark is written.
227     */
228    n = sscanf(bs->msg, Update_media, &Job, &sdmr.VolumeName,
229       &sdmr.VolJobs, &sdmr.VolFiles, &sdmr.VolBlocks, &sdmr.VolBytes,
230       &sdmr.VolABytes, &sdmr.VolHoleBytes, &sdmr.VolHoles,
231       &sdmr.VolMounts, &sdmr.VolErrors, &sdmr.VolWrites, &sdmr.MaxVolBytes,
232       &VolLastWritten, &sdmr.VolStatus, &sdmr.Slot, &label, &sdmr.InChanger,
233       &sdmr.VolReadTime, &sdmr.VolWriteTime, &VolFirstWritten,
234       &sdmr.VolType);
235     if (n == 22) {
236       db_lock(jcr->db);
237       Dmsg3(400, "Update media %s oldStat=%s newStat=%s\n", sdmr.VolumeName,
238          mr.VolStatus, sdmr.VolStatus);
239       bstrncpy(mr.VolumeName, sdmr.VolumeName, sizeof(mr.VolumeName)); /* copy Volume name */
240       unbash_spaces(mr.VolumeName);
241       if (!db_get_media_record(jcr, jcr->db, &mr)) {
242          Jmsg(jcr, M_ERROR, 0, _("Unable to get Media record for Volume %s: ERR=%s\n"),
243               mr.VolumeName, db_strerror(jcr->db));
244          bs->fsend(_("1991 Catalog Request for vol=%s failed: %s"),
245             mr.VolumeName, db_strerror(jcr->db));
246          db_unlock(jcr->db);
247          return;
248       }
249       /* Set first written time if this is first job */
250       if (mr.FirstWritten == 0) {
251          if (VolFirstWritten == 0) {
252             mr.FirstWritten = jcr->start_time;   /* use Job start time as first write */
253          } else {
254             mr.FirstWritten = VolFirstWritten;
255          }
256          mr.set_first_written = true;
257       }
258       /* If we just labeled the tape set time */
259       if (label || mr.LabelDate == 0) {
260          mr.LabelDate = jcr->start_time;
261          mr.set_label_date = true;
262          if (mr.InitialWrite == 0) {
263             mr.InitialWrite = jcr->start_time;
264          }
265          Dmsg2(400, "label=%d labeldate=%d\n", label, mr.LabelDate);
266       } else {
267          /*
268           * Insanity check for VolFiles get set to a smaller value
269           */
270          if (sdmr.VolFiles < mr.VolFiles) {
271             Jmsg(jcr, M_INFO, 0, _("Attempt to set Volume Files from %u to %u"
272                  " for Volume \"%s\". Ignored.\n"),
273                mr.VolFiles, sdmr.VolFiles, mr.VolumeName);
274             sdmr.VolFiles = mr.VolFiles;  /* keep orginal value */
275          }
276       }
277       Dmsg2(400, "Update media: BefVolJobs=%u After=%u\n", mr.VolJobs, sdmr.VolJobs);
278
279       /*
280        * Check if the volume has been written by the job,
281        * and update the LastWritten field if needed.
282        */
283       if (mr.VolBlocks != sdmr.VolBlocks && VolLastWritten != 0) {
284          mr.LastWritten = VolLastWritten;
285       }
286
287       /*
288        * Update to point to the last device used to write the Volume.
289        *   However, do so only if we are writing the tape, i.e.
290        *   the number of VolWrites has increased.
291        */
292       if (jcr->wstore && sdmr.VolWrites > mr.VolWrites) {
293          Dmsg2(050, "Update StorageId old=%d new=%d\n",
294                mr.StorageId, jcr->wstore->StorageId);
295          /* Update StorageId after write */
296          set_storageid_in_mr(jcr->wstore, &mr);
297       } else {
298          /* Nothing written, reset same StorageId */
299          set_storageid_in_mr(NULL, &mr);
300       }
301
302       /* Copy updated values to original media record */
303       mr.VolJobs      = sdmr.VolJobs;
304       mr.VolFiles     = sdmr.VolFiles;
305       mr.VolBlocks    = sdmr.VolBlocks;
306       mr.VolBytes     = sdmr.VolBytes;
307       mr.VolABytes    = sdmr.VolABytes;
308       mr.VolHoleBytes = sdmr.VolHoleBytes;
309       mr.VolHoles     = sdmr.VolHoles;
310       mr.VolMounts    = sdmr.VolMounts;
311       mr.VolErrors    = sdmr.VolErrors;
312       mr.VolWrites    = sdmr.VolWrites;
313       mr.Slot         = sdmr.Slot;
314       mr.InChanger    = sdmr.InChanger;
315       mr.VolType     = sdmr.VolType;
316       bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus));
317       if (sdmr.VolReadTime >= 0) {
318          mr.VolReadTime  = sdmr.VolReadTime;
319       }
320       if (sdmr.VolWriteTime >= 0) {
321          mr.VolWriteTime = sdmr.VolWriteTime;
322       }
323
324       Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName);
325       /*
326        * Update the database, then before sending the response to the
327        *  SD, check if the Volume has expired.
328        */
329       if (!db_update_media_record(jcr, jcr->db, &mr)) {
330          Jmsg(jcr, M_FATAL, 0, _("Catalog error updating Media record. %s"),
331             db_strerror(jcr->db));
332          bs->fsend(_("1993 Update Media error\n"));
333          Pmsg0(000, "1993 Update Media error\n");
334       } else {
335          (void)has_volume_expired(jcr, &mr);
336          send_volume_info_to_storage_daemon(jcr, bs, &mr);
337       }
338       db_unlock(jcr->db);
339       goto ok_out;
340    }
341    Dmsg1(1000, "Tried update_media. fields wanted=20, got=%d\n", n);
342
343    /*
344     * Request to create a JobMedia record
345     */
346    if (sscanf(bs->msg, Create_jobmedia, &Job) == 1) {
347       if (jcr->wjcr) {
348          jm.JobId = jcr->wjcr->JobId;
349       } else {
350          jm.JobId = jcr->JobId;
351       }
352       ok = true;
353       db_lock(jcr->db);
354       db_start_transaction(jcr, jcr->db);
355       while (bs->recv() >= 0) {
356          if (ok && sscanf(bs->msg, "%u %u %u %u %u %u %lld\n",
357              &jm.FirstIndex, &jm.LastIndex, &jm.StartFile, &jm.EndFile,
358              &jm.StartBlock, &jm.EndBlock, &MediaId) != 7) {
359             ok = false;
360             continue;
361          }
362          if (ok) {
363             jm.MediaId = MediaId;
364             Dmsg6(400, "create_jobmedia JobId=%d MediaId=%d SF=%d EF=%d FI=%d LI=%d\n",
365               jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex);
366             ok = db_create_jobmedia_record(jcr, jcr->db, &jm);
367          }
368       }
369       db_end_transaction(jcr, jcr->db);
370       if (!ok) {
371          Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"),
372             db_strerror(jcr->db));
373          db_unlock(jcr->db);
374          bs->fsend(_("1992 Create JobMedia error\n"));
375          goto ok_out; 
376       }
377       db_unlock(jcr->db);
378       Dmsg0(400, "JobMedia record created\n");
379       bs->fsend(OK_create);
380       goto ok_out;
381    }
382
383    /* Handle snapshot catalog request */
384    if (snapshot_catreq(jcr, bs)) {
385       goto ok_out;
386    }
387
388    Dmsg1(1000, "Tried create_jobmedia. fields wanted=10, got=%d\n", n);
389
390    /* Everything failed. Send error message. */
391    omsg = get_memory(bs->msglen+1);
392    pm_strcpy(omsg, bs->msg);
393    bs->fsend(_("1990 Invalid Catalog Request: %s"), omsg);
394    Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request: %s"), omsg);
395    free_memory(omsg);
396
397 ok_out:
398    Dmsg1(400, ">CatReq response: %s", bs->msg);
399    Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr);
400    return;
401 }
402
403 /*
404  * Note, we receive the whole attribute record, but we select out only the stat
405  * packet, VolSessionId, VolSessionTime, FileIndex, file type, and file name to
406  * store in the catalog.
407  */
408 static void update_attribute(JCR *jcr, char *msg, int32_t msglen)
409 {
410    unser_declare;
411    uint32_t VolSessionId, VolSessionTime;
412    int32_t Stream;
413    uint32_t FileIndex;
414    char *p;
415    int len;
416    char *fname, *attr;
417    ATTR_DBR *ar = NULL;
418    uint32_t reclen;
419
420    /* Start transaction allocates jcr->attr and jcr->ar if needed */
421    db_start_transaction(jcr, jcr->db); /* start transaction if not already open */
422    ar = jcr->ar;
423
424    /*
425     * Start by scanning directly in the message buffer to get Stream
426     *  there may be a cached attr so we cannot yet write into
427     *  jcr->attr or jcr->ar
428     */
429    p = msg;
430    skip_nonspaces(&p);                /* UpdCat */
431    skip_spaces(&p);
432    skip_nonspaces(&p);                /* Job=nnn */
433    skip_spaces(&p);
434    skip_nonspaces(&p);                /* "FileAttributes" */
435    p += 1;
436    /* The following "SD header" fields are serialized */
437    unser_begin(p, 0);
438    unser_uint32(VolSessionId);        /* VolSessionId */
439    unser_uint32(VolSessionTime);      /* VolSessionTime */
440    unser_int32(FileIndex);            /* FileIndex */
441    unser_int32(Stream);               /* Stream */
442    unser_uint32(reclen);              /* Record length */
443    p += unser_length(p);              /* Raw record follows */
444
445    /**
446     * At this point p points to the raw record, which varies according
447     *  to what kind of a record (Stream) was sent.  Note, the integer
448     *  fields at the beginning of these "raw" records are in ASCII with
449     *  spaces between them so one can use scanf or manual scanning to
450     *  extract the fields.
451     *
452     * File Attributes
453     *   File_index
454     *   File type
455     *   Filename (full path)
456     *   Encoded attributes
457     *   Link name (if type==FT_LNK or FT_LNKSAVED)
458     *   Encoded extended-attributes (for Win32)
459     *   Delta sequence number (32 bit int)
460     *
461     * Restore Object
462     *   File_index
463     *   File_type
464     *   Object_index
465     *   Object_len (possibly compressed)
466     *   Object_full_len (not compressed)
467     *   Object_compression
468     *   Plugin_name
469     *   Object_name
470     *   Binary Object data
471     */
472
473    Dmsg1(400, "UpdCat msg=%s\n", msg);
474    Dmsg5(400, "UpdCat VolSessId=%d VolSessT=%d FI=%d Strm=%d reclen=%d\n",
475       VolSessionId, VolSessionTime, FileIndex, Stream, reclen);
476
477    if (Stream == STREAM_UNIX_ATTRIBUTES || Stream == STREAM_UNIX_ATTRIBUTES_EX) {
478       if (jcr->cached_attribute) {
479          Dmsg2(400, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname);
480          if (!db_create_attributes_record(jcr, jcr->db, ar)) {
481             Jmsg1(jcr, M_FATAL, 0, _("Attribute create error: ERR=%s"), db_strerror(jcr->db));
482          }
483          jcr->cached_attribute = false;
484       }
485       /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
486       jcr->attr = check_pool_memory_size(jcr->attr, msglen);
487       memcpy(jcr->attr, msg, msglen);
488       p = jcr->attr - msg + p;    /* point p into jcr->attr */
489       skip_nonspaces(&p);         /* skip FileIndex */
490       skip_spaces(&p);
491       ar->FileType = str_to_int32(p);
492       skip_nonspaces(&p);         /* skip FileType */
493       skip_spaces(&p);
494       fname = p;
495       len = strlen(fname);        /* length before attributes */
496       attr = &fname[len+1];
497       ar->DeltaSeq = 0;
498       if (ar->FileType == FT_REG) {
499          p = attr + strlen(attr) + 1;  /* point to link */
500          p = p + strlen(p) + 1;        /* point to extended attributes */
501          p = p + strlen(p) + 1;        /* point to delta sequence */
502          /*
503           * Older FDs don't have a delta sequence, so check if it is there
504           */
505          if (p - jcr->attr < msglen) {
506             ar->DeltaSeq = str_to_int32(p); /* delta_seq */
507          }
508       }
509
510       Dmsg2(400, "dird<stored: stream=%d %s\n", Stream, fname);
511       Dmsg1(400, "dird<stored: attr=%s\n", attr);
512       ar->attr = attr;
513       ar->fname = fname;
514       if (ar->FileType == FT_DELETED) {
515          ar->FileIndex = 0;     /* special value */
516       } else {
517          ar->FileIndex = FileIndex;
518       }
519       ar->Stream = Stream;
520       ar->link = NULL;
521       if (jcr->wjcr) {
522          ar->JobId = jcr->wjcr->JobId;
523          Dmsg1(100, "=== set JobId=%d\n", ar->JobId);
524       } else {
525          ar->JobId = jcr->JobId;
526       }
527       ar->Digest = NULL;
528       ar->DigestType = CRYPTO_DIGEST_NONE;
529       jcr->cached_attribute = true;
530
531       Dmsg2(400, "dird<filed: stream=%d %s\n", Stream, fname);
532       Dmsg1(400, "dird<filed: attr=%s\n", attr);
533
534    } else if (Stream == STREAM_RESTORE_OBJECT) {
535       ROBJECT_DBR ro;
536
537       memset(&ro, 0, sizeof(ro));
538       ro.Stream = Stream;
539       ro.FileIndex = FileIndex;
540       if (jcr->wjcr) {
541          ro.JobId = jcr->wjcr->JobId;
542          Dmsg1(100, "=== set JobId=%d\n", ar->JobId);
543       } else {
544          ro.JobId = jcr->JobId;
545       }
546
547       Dmsg1(100, "Robj=%s\n", p);
548
549       skip_nonspaces(&p);                  /* skip FileIndex */
550       skip_spaces(&p);
551       ro.FileType = str_to_int32(p);        /* FileType */
552       skip_nonspaces(&p);
553       skip_spaces(&p);
554       ro.object_index = str_to_int32(p);    /* Object Index */
555       skip_nonspaces(&p);
556       skip_spaces(&p);
557       ro.object_len = str_to_int32(p);      /* object length possibly compressed */
558       skip_nonspaces(&p);
559       skip_spaces(&p);
560       ro.object_full_len = str_to_int32(p); /* uncompressed object length */
561       skip_nonspaces(&p);
562       skip_spaces(&p);
563       ro.object_compression = str_to_int32(p); /* compression */
564       skip_nonspaces(&p);
565       skip_spaces(&p);
566
567       ro.plugin_name = p;                      /* point to plugin name */
568       len = strlen(ro.plugin_name);
569       ro.object_name = &ro.plugin_name[len+1]; /* point to object name */
570       len = strlen(ro.object_name);
571       ro.object = &ro.object_name[len+1];      /* point to object */
572       ro.object[ro.object_len] = 0;            /* add zero for those who attempt printing */
573       Dmsg7(100, "oname=%s stream=%d FT=%d FI=%d JobId=%d, obj_len=%d\nobj=\"%s\"\n",
574          ro.object_name, ro.Stream, ro.FileType, ro.FileIndex, ro.JobId,
575          ro.object_len, ro.object);
576       /* Send it */
577       if (!db_create_restore_object_record(jcr, jcr->db, &ro)) {
578          Jmsg1(jcr, M_FATAL, 0, _("Restore object create error. %s"), db_strerror(jcr->db));
579       }
580
581    } else if (crypto_digest_stream_type(Stream) != CRYPTO_DIGEST_NONE) {
582       fname = p;
583       if (ar->FileIndex != FileIndex) {
584          Jmsg3(jcr, M_WARNING, 0, _("%s not same File=%d as attributes=%d\n"),
585             stream_to_ascii(Stream), FileIndex, ar->FileIndex);
586       } else {
587          /* Update digest in catalog */
588          char digestbuf[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
589          int len = 0;
590          int type = CRYPTO_DIGEST_NONE;
591
592          switch(Stream) {
593          case STREAM_MD5_DIGEST:
594             len = CRYPTO_DIGEST_MD5_SIZE;
595             type = CRYPTO_DIGEST_MD5;
596             break;
597          case STREAM_SHA1_DIGEST:
598             len = CRYPTO_DIGEST_SHA1_SIZE;
599             type = CRYPTO_DIGEST_SHA1;
600             break;
601          case STREAM_SHA256_DIGEST:
602             len = CRYPTO_DIGEST_SHA256_SIZE;
603             type = CRYPTO_DIGEST_SHA256;
604             break;
605          case STREAM_SHA512_DIGEST:
606             len = CRYPTO_DIGEST_SHA512_SIZE;
607             type = CRYPTO_DIGEST_SHA512;
608             break;
609          default:
610             /* Never reached ... */
611             Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. Unsupported digest stream type: %d"),
612                  Stream);
613          }
614
615          bin_to_base64(digestbuf, sizeof(digestbuf), fname, len, true);
616          Dmsg3(400, "DigestLen=%d Digest=%s type=%d\n", strlen(digestbuf),
617                digestbuf, Stream);
618          if (jcr->cached_attribute) {
619             ar->Digest = digestbuf;
620             ar->DigestType = type;
621             Dmsg2(400, "Cached attr with digest. Stream=%d fname=%s\n",
622                   ar->Stream, ar->fname);
623
624             /* Update BaseFile table */
625             if (!db_create_attributes_record(jcr, jcr->db, ar)) {
626                Jmsg1(jcr, M_FATAL, 0, _("attribute create error. ERR=%s"),
627                         db_strerror(jcr->db));
628             }
629             jcr->cached_attribute = false;
630          } else {
631             if (!db_add_digest_to_file_record(jcr, jcr->db, ar->FileId, digestbuf, type)) {
632                Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. %s"),
633                   db_strerror(jcr->db));
634             }
635          }
636       }
637    }
638 }
639
640 /*
641  * Update File Attributes in the catalog with data
642  *  sent by the Storage daemon.
643  */
644 void catalog_update(JCR *jcr, BSOCK *bs)
645 {
646    if (!jcr->pool->catalog_files) {
647       return;                         /* user disabled cataloging */
648    }
649    if (jcr->is_job_canceled()) {
650       goto bail_out;
651    }
652    if (!jcr->db) {
653       POOLMEM *omsg = get_memory(bs->msglen+1);
654       pm_strcpy(omsg, bs->msg);
655       bs->fsend(_("1994 Invalid Catalog Update: %s"), omsg);
656       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog Update; DB not open: %s"), omsg);
657       free_memory(omsg);
658       goto bail_out;
659    }
660    update_attribute(jcr, bs->msg, bs->msglen);
661
662 bail_out:
663    if (jcr->is_job_canceled()) {
664       jcr->cached_attribute = false;
665       cancel_storage_daemon_job(jcr);
666    }
667 }
668
669 /*
670  * Update File Attributes in the catalog with data read from
671  * the storage daemon spool file. We receive the filename and
672  * we try to read it.
673  */
674 bool despool_attributes_from_file(JCR *jcr, const char *file)
675 {
676    bool ret=false;
677    int32_t pktsiz;
678    size_t nbytes;
679    ssize_t size = 0;
680    int32_t msglen;                    /* message length */
681    POOLMEM *msg = get_pool_memory(PM_MESSAGE);
682    FILE *spool_fd=NULL;
683
684    Dmsg1(100, "Begin despool_attributes_from_file\n", file);
685
686    if (jcr->is_job_canceled() || !jcr->pool->catalog_files || !jcr->db) {
687       goto bail_out;                  /* user disabled cataloging */
688    }
689
690    spool_fd = fopen(file, "rb");
691    if (!spool_fd) {
692       Dmsg0(100, "cancel despool_attributes_from_file\n");
693       /* send an error message */
694       goto bail_out;
695    }
696 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
697    posix_fadvise(fileno(spool_fd), 0, 0, POSIX_FADV_WILLNEED);
698 #endif
699
700    while (fread((char *)&pktsiz, 1, sizeof(int32_t), spool_fd) ==
701           sizeof(int32_t)) {
702       size += sizeof(int32_t);
703       msglen = ntohl(pktsiz);
704       if (msglen > 0) {
705          if (msglen > (int32_t) sizeof_pool_memory(msg)) {
706             msg = realloc_pool_memory(msg, msglen + 1);
707          }
708          nbytes = fread(msg, 1, msglen, spool_fd);
709          if (nbytes != (size_t) msglen) {
710             berrno be;
711             Dmsg2(400, "nbytes=%d msglen=%d\n", nbytes, msglen);
712             Qmsg1(jcr, M_FATAL, 0, _("fread attr spool error. ERR=%s\n"),
713                   be.bstrerror());
714             goto bail_out;
715          }
716          size += nbytes;
717       }
718       if (!jcr->is_job_canceled()) {
719          update_attribute(jcr, msg, msglen);
720          if (jcr->is_job_canceled() || (jcr->wjcr && jcr->wjcr->is_job_canceled())) {
721             goto bail_out;
722          }
723       }
724    }
725    if (ferror(spool_fd)) {
726       berrno be;
727       Qmsg1(jcr, M_FATAL, 0, _("fread attr spool error. ERR=%s\n"),
728             be.bstrerror());
729       Dmsg1(050, "fread attr spool error. ERR=%s\n", be.bstrerror());
730       goto bail_out;
731    }
732    ret = true;
733
734 bail_out:
735    if (spool_fd) {
736       fclose(spool_fd);
737    }
738
739    if (jcr->is_job_canceled()) {
740       jcr->cached_attribute = false;
741       cancel_storage_daemon_job(jcr);
742    }
743
744    free_pool_memory(msg);
745    Dmsg1(100, "End despool_attributes_from_file ret=%i\n", ret);
746    return ret;
747 }