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