]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/catreq.c
Add more debug code for bug 1885
[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        * Update to point to the last device used to write the Volume.
280        *   However, do so only if we are writing the tape, i.e.
281        *   the number of VolWrites has increased.
282        */
283       if (jcr->wstore && sdmr.VolWrites > mr.VolWrites) {
284          Dmsg2(050, "Update StorageId old=%d new=%d\n",
285                mr.StorageId, jcr->wstore->StorageId);
286          /* Update StorageId after write */
287          set_storageid_in_mr(jcr->wstore, &mr);
288       } else {
289          /* Nothing written, reset same StorageId */
290          set_storageid_in_mr(NULL, &mr);
291       }
292
293       /* Copy updated values to original media record */
294       mr.VolJobs      = sdmr.VolJobs;
295       mr.VolFiles     = sdmr.VolFiles;
296       mr.VolBlocks    = sdmr.VolBlocks;
297       mr.VolBytes     = sdmr.VolBytes;
298       mr.VolMounts    = sdmr.VolMounts;
299       mr.VolErrors    = sdmr.VolErrors;
300       mr.VolWrites    = sdmr.VolWrites;
301       mr.Slot         = sdmr.Slot;
302       mr.InChanger    = sdmr.InChanger;
303       mr.VolParts     = sdmr.VolParts;
304       bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus));
305       if (sdmr.VolReadTime >= 0) { 
306          mr.VolReadTime  = sdmr.VolReadTime;
307       }
308       if (sdmr.VolWriteTime >= 0) {
309          mr.VolWriteTime = sdmr.VolWriteTime;
310       }
311
312       Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName);
313       /*
314        * Update the database, then before sending the response to the
315        *  SD, check if the Volume has expired.
316        */
317       if (!db_update_media_record(jcr, jcr->db, &mr)) {
318          Jmsg(jcr, M_FATAL, 0, _("Catalog error updating Media record. %s"),
319             db_strerror(jcr->db));
320          bs->fsend(_("1993 Update Media error\n"));
321          Dmsg0(400, "send error\n");
322       } else {
323          (void)has_volume_expired(jcr, &mr);
324          send_volume_info_to_storage_daemon(jcr, bs, &mr);
325       }
326       db_unlock(jcr->db);
327
328    /*
329     * Request to create a JobMedia record
330     */
331    } else if (sscanf(bs->msg, Create_job_media, &Job,
332       &jm.FirstIndex, &jm.LastIndex, &jm.StartFile, &jm.EndFile,
333       &jm.StartBlock, &jm.EndBlock, &Copy, &Stripe, &MediaId) == 10) {
334
335       if (jcr->mig_jcr) {
336          jm.JobId = jcr->mig_jcr->JobId;
337       } else {
338          jm.JobId = jcr->JobId;
339       }
340       jm.MediaId = MediaId;
341       Dmsg6(400, "create_jobmedia JobId=%d MediaId=%d SF=%d EF=%d FI=%d LI=%d\n",
342          jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex);
343       if (!db_create_jobmedia_record(jcr, jcr->db, &jm)) {
344          Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"),
345             db_strerror(jcr->db));
346          bs->fsend(_("1992 Create JobMedia error\n"));
347       } else {
348          Dmsg0(400, "JobMedia record created\n");
349          bs->fsend(OK_create);
350       }
351
352    } else {
353       omsg = get_memory(bs->msglen+1);
354       pm_strcpy(omsg, bs->msg);
355       bs->fsend(_("1990 Invalid Catalog Request: %s"), omsg);
356       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request: %s"), omsg);
357       free_memory(omsg);
358    }
359
360    Dmsg1(400, ">CatReq response: %s", bs->msg);
361    Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr);
362    return;
363 }
364
365 /*
366  * Note, we receive the whole attribute record, but we select out only the stat
367  * packet, VolSessionId, VolSessionTime, FileIndex, file type, and file name to
368  * store in the catalog.
369  */
370 static void update_attribute(JCR *jcr, char *msg, int32_t msglen)
371 {
372    unser_declare;
373    uint32_t VolSessionId, VolSessionTime;
374    int32_t Stream;
375    uint32_t FileIndex;
376    char *p;
377    int len;
378    char *fname, *attr;
379    ATTR_DBR *ar = NULL;
380    uint32_t reclen;
381
382    /* Start transaction allocates jcr->attr and jcr->ar if needed */
383    db_start_transaction(jcr, jcr->db);     /* start transaction if not already open */
384    ar = jcr->ar;      
385
386    /*
387     * Start by scanning directly in the message buffer to get Stream   
388     *  there may be a cached attr so we cannot yet write into
389     *  jcr->attr or jcr->ar  
390     */
391    p = msg;
392    skip_nonspaces(&p);                /* UpdCat */
393    skip_spaces(&p);
394    skip_nonspaces(&p);                /* Job=nnn */
395    skip_spaces(&p);
396    skip_nonspaces(&p);                /* "FileAttributes" */
397    p += 1;
398    /* The following "SD header" fields are serialized */
399    unser_begin(p, 0);
400    unser_uint32(VolSessionId);        /* VolSessionId */
401    unser_uint32(VolSessionTime);      /* VolSessionTime */
402    unser_int32(FileIndex);            /* FileIndex */
403    unser_int32(Stream);               /* Stream */
404    unser_uint32(reclen);              /* Record length */
405    p += unser_length(p);              /* Raw record follows */
406
407    /**
408     * At this point p points to the raw record, which varies according
409     *  to what kind of a record (Stream) was sent.  Note, the integer
410     *  fields at the beginning of these "raw" records are in ASCII with
411     *  spaces between them so one can use scanf or manual scanning to
412     *  extract the fields.
413     *
414     * File Attributes
415     *   File_index
416     *   File type
417     *   Filename (full path)
418     *   Encoded attributes
419     *   Link name (if type==FT_LNK or FT_LNKSAVED)
420     *   Encoded extended-attributes (for Win32)
421     *   Delta sequence number (32 bit int)
422     *
423     * Restore Object
424     *   File_index
425     *   File_type
426     *   Object_index
427     *   Object_len (possibly compressed)
428     *   Object_full_len (not compressed)
429     *   Object_compression
430     *   Plugin_name
431     *   Object_name
432     *   Binary Object data
433     */
434
435    Dmsg1(400, "UpdCat msg=%s\n", msg);
436    Dmsg5(400, "UpdCat VolSessId=%d VolSessT=%d FI=%d Strm=%d reclen=%d\n",
437       VolSessionId, VolSessionTime, FileIndex, Stream, reclen);
438
439    if (Stream == STREAM_UNIX_ATTRIBUTES || Stream == STREAM_UNIX_ATTRIBUTES_EX) {
440       if (jcr->cached_attribute) {
441          Dmsg2(400, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname);
442          if (!db_create_attributes_record(jcr, jcr->db, ar)) {
443             Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
444          }
445          jcr->cached_attribute = false;
446       }
447       /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
448       jcr->attr = check_pool_memory_size(jcr->attr, msglen);
449       memcpy(jcr->attr, msg, msglen);
450       p = jcr->attr - msg + p;    /* point p into jcr->attr */
451       skip_nonspaces(&p);         /* skip FileIndex */
452       skip_spaces(&p);
453       ar->FileType = str_to_int32(p); 
454       skip_nonspaces(&p);         /* skip FileType */
455       skip_spaces(&p);
456       fname = p;
457       len = strlen(fname);        /* length before attributes */
458       attr = &fname[len+1];
459       ar->DeltaSeq = 0;
460       if (ar->FileType == FT_REG) {
461          p = attr + strlen(attr) + 1;  /* point to link */
462          p = p + strlen(p) + 1;        /* point to extended attributes */
463          p = p + strlen(p) + 1;        /* point to delta sequence */
464          /*
465           * Older FDs don't have a delta sequence, so check if it is there 
466           */
467          if (p - jcr->attr < msglen) {
468             ar->DeltaSeq = str_to_int32(p); /* delta_seq */
469          }
470       }
471
472       Dmsg2(400, "dird<stored: stream=%d %s\n", Stream, fname);
473       Dmsg1(400, "dird<stored: attr=%s\n", attr);
474       ar->attr = attr;
475       ar->fname = fname;
476       if (ar->FileType == FT_DELETED) {
477          ar->FileIndex = 0;     /* special value */
478       } else {
479          ar->FileIndex = FileIndex;
480       }
481       ar->Stream = Stream;
482       ar->link = NULL;
483       if (jcr->mig_jcr) {
484          ar->JobId = jcr->mig_jcr->JobId;
485       } else {
486          ar->JobId = jcr->JobId;
487       }
488       ar->Digest = NULL;
489       ar->DigestType = CRYPTO_DIGEST_NONE;
490       jcr->cached_attribute = true;
491
492       Dmsg2(400, "dird<filed: stream=%d %s\n", Stream, fname);
493       Dmsg1(400, "dird<filed: attr=%s\n", attr);
494
495    } else if (Stream == STREAM_RESTORE_OBJECT) {
496       ROBJECT_DBR ro;
497
498       memset(&ro, 0, sizeof(ro));
499       ro.Stream = Stream;
500       ro.FileIndex = FileIndex;
501       if (jcr->mig_jcr) {
502          ro.JobId = jcr->mig_jcr->JobId;
503       } else {
504          ro.JobId = jcr->JobId;
505       }
506
507       Dmsg1(100, "Robj=%s\n", p);
508       
509       skip_nonspaces(&p);                  /* skip FileIndex */
510       skip_spaces(&p);
511       ro.FileType = str_to_int32(p);        /* FileType */
512       skip_nonspaces(&p);
513       skip_spaces(&p);
514       ro.object_index = str_to_int32(p);    /* Object Index */
515       skip_nonspaces(&p);
516       skip_spaces(&p);
517       ro.object_len = str_to_int32(p);      /* object length possibly compressed */
518       skip_nonspaces(&p);                  
519       skip_spaces(&p);
520       ro.object_full_len = str_to_int32(p); /* uncompressed object length */
521       skip_nonspaces(&p);
522       skip_spaces(&p);
523       ro.object_compression = str_to_int32(p); /* compression */
524       skip_nonspaces(&p);
525       skip_spaces(&p);
526
527       ro.plugin_name = p;                      /* point to plugin name */
528       len = strlen(ro.plugin_name);
529       ro.object_name = &ro.plugin_name[len+1]; /* point to object name */
530       len = strlen(ro.object_name);
531       ro.object = &ro.object_name[len+1];      /* point to object */
532       ro.object[ro.object_len] = 0;            /* add zero for those who attempt printing */
533       Dmsg7(100, "oname=%s stream=%d FT=%d FI=%d JobId=%d, obj_len=%d\nobj=\"%s\"\n",
534          ro.object_name, ro.Stream, ro.FileType, ro.FileIndex, ro.JobId,
535          ro.object_len, ro.object);
536       /* Send it */
537       if (!db_create_restore_object_record(jcr, jcr->db, &ro)) {
538          Jmsg1(jcr, M_FATAL, 0, _("Restore object create error. %s"), db_strerror(jcr->db));
539       }
540
541    } else if (crypto_digest_stream_type(Stream) != CRYPTO_DIGEST_NONE) {
542       fname = p;
543       if (ar->FileIndex != FileIndex) {
544          Jmsg3(jcr, M_WARNING, 0, _("%s not same File=%d as attributes=%d\n"), 
545             stream_to_ascii(Stream), FileIndex, ar->FileIndex);
546       } else {
547          /* Update digest in catalog */
548          char digestbuf[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
549          int len = 0;
550          int type = CRYPTO_DIGEST_NONE;
551
552          switch(Stream) {
553          case STREAM_MD5_DIGEST:
554             len = CRYPTO_DIGEST_MD5_SIZE;
555             type = CRYPTO_DIGEST_MD5;
556             break;
557          case STREAM_SHA1_DIGEST:
558             len = CRYPTO_DIGEST_SHA1_SIZE;
559             type = CRYPTO_DIGEST_SHA1;
560             break;
561          case STREAM_SHA256_DIGEST:
562             len = CRYPTO_DIGEST_SHA256_SIZE;
563             type = CRYPTO_DIGEST_SHA256;
564             break;
565          case STREAM_SHA512_DIGEST:
566             len = CRYPTO_DIGEST_SHA512_SIZE;
567             type = CRYPTO_DIGEST_SHA512;
568             break;
569          default:
570             /* Never reached ... */
571             Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. Unsupported digest stream type: %d"),
572                  Stream);
573          }
574
575          bin_to_base64(digestbuf, sizeof(digestbuf), fname, len, true);
576          Dmsg3(400, "DigestLen=%d Digest=%s type=%d\n", strlen(digestbuf),
577                digestbuf, Stream);
578          if (jcr->cached_attribute) {
579             ar->Digest = digestbuf;
580             ar->DigestType = type;
581             Dmsg2(400, "Cached attr with digest. Stream=%d fname=%s\n",
582                   ar->Stream, ar->fname);
583
584             /* Update BaseFile table */
585             if (!db_create_attributes_record(jcr, jcr->db, ar)) {
586                Jmsg1(jcr, M_FATAL, 0, _("attribute create error. %s"),
587                         db_strerror(jcr->db));
588             }
589             jcr->cached_attribute = false; 
590          } else {
591             if (!db_add_digest_to_file_record(jcr, jcr->db, ar->FileId, digestbuf, type)) {
592                Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. %s"),
593                   db_strerror(jcr->db));
594             }
595          }
596       }
597    }
598 }
599
600 /*
601  * Update File Attributes in the catalog with data
602  *  sent by the Storage daemon.
603  */
604 void catalog_update(JCR *jcr, BSOCK *bs)
605 {
606    if (!jcr->pool->catalog_files) {
607       return;                         /* user disabled cataloging */
608    }
609    if (jcr->is_job_canceled()) {
610       goto bail_out;
611    }
612    if (!jcr->db) {
613       POOLMEM *omsg = get_memory(bs->msglen+1);
614       pm_strcpy(omsg, bs->msg);
615       bs->fsend(_("1994 Invalid Catalog Update: %s"), omsg);    
616       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog Update; DB not open: %s"), omsg);
617       free_memory(omsg);
618       goto bail_out;
619    }
620    update_attribute(jcr, bs->msg, bs->msglen);
621
622 bail_out:
623    if (jcr->is_job_canceled()) {
624       cancel_storage_daemon_job(jcr);
625    }
626 }
627
628 /*
629  * Update File Attributes in the catalog with data read from
630  * the storage daemon spool file. We receive the filename and
631  * we try to read it.
632  */
633 bool despool_attributes_from_file(JCR *jcr, const char *file)
634 {
635    bool ret=false;
636    int32_t pktsiz;
637    size_t nbytes;
638    ssize_t size = 0;
639    int32_t msglen;                    /* message length */
640    POOLMEM *msg = get_pool_memory(PM_MESSAGE);
641    FILE *spool_fd=NULL;
642
643    Dmsg0(100, "Begin despool_attributes_from_file\n");
644
645    if (jcr->is_job_canceled() || !jcr->pool->catalog_files || !jcr->db) {
646       goto bail_out;                  /* user disabled cataloging */
647    }
648
649    spool_fd = fopen(file, "rb");
650    if (!spool_fd) {
651       Dmsg0(100, "cancel despool_attributes_from_file\n");
652       /* send an error message */
653       goto bail_out;
654    }
655 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
656    posix_fadvise(fileno(spool_fd), 0, 0, POSIX_FADV_WILLNEED);
657 #endif
658
659    while (fread((char *)&pktsiz, 1, sizeof(int32_t), spool_fd) ==
660           sizeof(int32_t)) {
661       size += sizeof(int32_t);
662       msglen = ntohl(pktsiz);
663       if (msglen > 0) {
664          if (msglen > (int32_t) sizeof_pool_memory(msg)) {
665             msg = realloc_pool_memory(msg, msglen + 1);
666          }
667          nbytes = fread(msg, 1, msglen, spool_fd);
668          if (nbytes != (size_t) msglen) {
669             berrno be;
670             Dmsg2(400, "nbytes=%d msglen=%d\n", nbytes, msglen);
671             Qmsg1(jcr, M_FATAL, 0, _("fread attr spool error. ERR=%s\n"),
672                   be.bstrerror());
673             goto bail_out;
674          }
675          size += nbytes;
676       }
677       if (!jcr->is_job_canceled()) {
678          update_attribute(jcr, msg, msglen);
679          if (jcr->is_job_canceled()) {
680             goto bail_out;
681          }
682       }
683    }
684    if (ferror(spool_fd)) {
685       berrno be;
686       Qmsg1(jcr, M_FATAL, 0, _("fread attr spool error. ERR=%s\n"),
687             be.bstrerror());
688       goto bail_out;
689    }
690    ret = true;
691
692 bail_out:
693    if (spool_fd) {
694       fclose(spool_fd);
695    }
696
697    if (jcr->is_job_canceled()) {
698       cancel_storage_daemon_job(jcr);
699    }
700
701    free_pool_memory(msg);
702    Dmsg1(100, "End despool_attributes_from_file ret=%i\n", ret);
703    return ret;
704 }