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