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