2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula Director -- catreq.c -- handles the message channel
21 * catalog request from the Storage daemon.
23 * Kern Sibbald, March MMI
25 * This routine runs as a thread and must be thread reentrant.
27 * Basic tasks done here:
28 * Handle Catalog services.
33 #include "findlib/find.h"
36 * Handle catalog request
37 * For now, we simply return next Volume to be used
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";
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";
52 static char Create_jobmedia[] = "CatReq JobId=%ld CreateJobMedia\n";
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";
64 static char OK_create[] = "1000 OK CreateJobMedia\n";
67 void remove_dummy_jobmedia_records(JCR *jcr)
69 if (jcr->dummy_jobmedia) {
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;
80 static int send_volume_info_to_storage_daemon(JCR *jcr, BSOCK *sd, MEDIA_DBR *mr)
83 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50], ed7[50], ed8[50],
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,
99 edit_int64(mr->VolReadTime, ed7),
100 edit_int64(mr->VolWriteTime, ed8),
101 mr->EndFile, mr->EndBlock,
104 edit_uint64(mr->MediaId, ed9),
105 edit_uint64(mr->ScratchPoolId, ed10),
110 unbash_spaces(mr->VolumeName);
111 Dmsg2(100, "Vol Info for %s: %s", jcr->Job, sd->msg);
115 /* TODO: See if we want to let the FD do all kind
116 * of catalog request/update
118 void catalog_request(JCR *jcr, BSOCK *bs)
122 char pool_name[MAX_NAME_LENGTH];
123 int index, ok, label, writing;
127 utime_t VolFirstWritten;
128 utime_t VolLastWritten;
133 memset(&sdmr, 0, sizeof(sdmr));
134 memset(&jm, 0, sizeof(jm));
138 * Request to find next appendable Volume for this Job
140 Dmsg1(200, "catreq %s", bs->msg);
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);
150 * Find next appendable medium for SD
152 n = sscanf(bs->msg, Find_media, &JobId, &index, &pool_name, &mr.MediaType, &mr.VolType);
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);
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);
165 /* Report problem finding pool */
166 Jmsg1(jcr, M_WARNING, 0, _("Pool \"%s\" not found for SD find media request.\n"),
170 * Send Find Media response to Storage daemon
173 send_volume_info_to_storage_daemon(jcr, bs, &mr);
175 bs->fsend(_("1901 No Media.\n"));
176 Dmsg0(500, "1901 No Media.\n");
180 Dmsg1(1000, "Tried find_media. fields wanted=4, got=%d\n", n);
183 * Request to find specific Volume information
185 n = sscanf(bs->msg, Get_Vol_Info, &JobId, &mr.VolumeName, &writing);
187 Dmsg1(100, "CatReq GetVolInfo Vol=%s\n", mr.VolumeName);
191 unbash_spaces(mr.VolumeName);
192 if (db_get_media_record(jcr, jcr->db, &mr)) {
193 const char *reason = NULL; /* detailed reason for rejection */
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
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.
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");
212 * Now try recycling if necessary
213 * reason set non-NULL if we cannot use it
215 check_if_volume_valid_or_recyclable(jcr, &mr, &reason);
218 if (!reason && mr.Enabled != 1) {
219 reason = _("is not Enabled");
221 if (reason == NULL) {
223 * Send Find Media response to Storage daemon
225 send_volume_info_to_storage_daemon(jcr, bs, &mr);
227 /* Not suitable volume */
228 bs->fsend(_("1998 Volume \"%s\" catalog status is %s, %s.\n"), mr.VolumeName,
229 mr.VolStatus, reason);
233 bs->fsend(_("1997 Volume \"%s\" not in catalog.\n"), mr.VolumeName);
234 Dmsg1(100, "1997 Volume \"%s\" not in catalog.\n", mr.VolumeName);
238 Dmsg1(1000, "Tried get_vol_info. fields wanted=3, got=%d\n", n);
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.
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);
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));
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 */
273 mr.FirstWritten = VolFirstWritten;
275 mr.set_first_written = true;
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;
284 Dmsg2(400, "label=%d labeldate=%d\n", label, mr.LabelDate);
287 * Insanity check for VolFiles get set to a smaller value
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 */
296 Dmsg2(400, "Update media: BefVolJobs=%u After=%u\n", mr.VolJobs, sdmr.VolJobs);
299 * Check if the volume has been written by the job,
300 * and update the LastWritten field if needed.
302 if (mr.VolBlocks != sdmr.VolBlocks && VolLastWritten != 0) {
303 mr.LastWritten = VolLastWritten;
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.
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);
317 /* Nothing written, reset same StorageId */
318 set_storageid_in_mr(NULL, &mr);
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;
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;
343 if (sdmr.VolWriteTime >= 0) {
344 mr.VolWriteTime = sdmr.VolWriteTime;
347 Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName);
349 * Update the database, then before sending the response to the
350 * SD, check if the Volume has expired.
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");
358 (void)has_volume_expired(jcr, &mr);
359 send_volume_info_to_storage_daemon(jcr, bs, &mr);
364 Dmsg1(1000, "Tried update_media. fields wanted=25, got=%d\n", n);
367 * Request to create a JobMedia record
369 if (sscanf(bs->msg, Create_jobmedia, &JobId) == 1) {
371 jm.JobId = jcr->wjcr->JobId;
373 jm.JobId = jcr->JobId;
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) {
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;
395 db_end_transaction(jcr, jcr->db);
397 Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"),
398 db_strerror(jcr->db));
400 bs->fsend(_("1992 Create JobMedia error\n"));
404 Dmsg0(400, "JobMedia record created\n");
405 bs->fsend(OK_create);
409 /* Handle snapshot catalog request */
410 if (snapshot_catreq(jcr, bs)) {
414 Dmsg1(1000, "Tried create_jobmedia. fields wanted=10, got=%d\n", n);
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);
424 Dmsg1(400, ">CatReq response: %s", bs->msg);
425 Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr);
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.
434 static void update_attribute(JCR *jcr, char *msg, int32_t msglen)
437 uint32_t VolSessionId, VolSessionTime;
446 /* Start transaction allocates jcr->attr and jcr->ar if needed */
447 db_start_transaction(jcr, jcr->db); /* start transaction if not already open */
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
456 skip_nonspaces(&p); /* UpdCat */
458 skip_nonspaces(&p); /* Job=nnn */
460 skip_nonspaces(&p); /* "FileAttributes" */
462 /* The following "SD header" fields are serialized */
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 */
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.
481 * Filename (full path)
483 * Link name (if type==FT_LNK or FT_LNKSAVED)
484 * Encoded extended-attributes (for Win32)
485 * Delta sequence number (32 bit int)
491 * Object_len (possibly compressed)
492 * Object_full_len (not compressed)
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);
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));
509 jcr->cached_attribute = false;
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 */
517 ar->FileType = str_to_int32(p);
518 skip_nonspaces(&p); /* skip FileType */
521 len = strlen(fname); /* length before attributes */
522 attr = &fname[len+1];
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 */
529 * Older FDs don't have a delta sequence, so check if it is there
531 if (p - jcr->attr < msglen) {
532 ar->DeltaSeq = str_to_int32(p); /* delta_seq */
536 Dmsg2(400, "dird<stored: stream=%d %s\n", Stream, fname);
537 Dmsg1(400, "dird<stored: attr=%s\n", attr);
540 if (ar->FileType == FT_DELETED) {
541 ar->FileIndex = 0; /* special value */
543 ar->FileIndex = FileIndex;
548 ar->JobId = jcr->wjcr->JobId;
549 Dmsg1(100, "=== set JobId=%d\n", ar->JobId);
551 ar->JobId = jcr->JobId;
554 ar->DigestType = CRYPTO_DIGEST_NONE;
555 jcr->cached_attribute = true;
557 Dmsg2(400, "dird<filed: stream=%d %s\n", Stream, fname);
558 Dmsg1(400, "dird<filed: attr=%s\n", attr);
560 } else if (Stream == STREAM_RESTORE_OBJECT) {
563 memset(&ro, 0, sizeof(ro));
565 ro.FileIndex = FileIndex;
567 ro.JobId = jcr->wjcr->JobId;
568 Dmsg1(100, "=== set JobId=%ld\n", ar->JobId);
570 ro.JobId = jcr->JobId;
573 Dmsg1(100, "Robj=%s\n", p);
575 skip_nonspaces(&p); /* skip FileIndex */
577 ro.FileType = str_to_int32(p); /* FileType */
580 ro.object_index = str_to_int32(p); /* Object Index */
583 ro.object_len = str_to_int32(p); /* object length possibly compressed */
586 ro.object_full_len = str_to_int32(p); /* uncompressed object length */
589 ro.object_compression = str_to_int32(p); /* compression */
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);
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));
607 } else if (crypto_digest_stream_type(Stream) != CRYPTO_DIGEST_NONE) {
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);
613 /* Update digest in catalog */
614 char digestbuf[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
616 int type = CRYPTO_DIGEST_NONE;
619 case STREAM_MD5_DIGEST:
620 len = CRYPTO_DIGEST_MD5_SIZE;
621 type = CRYPTO_DIGEST_MD5;
623 case STREAM_SHA1_DIGEST:
624 len = CRYPTO_DIGEST_SHA1_SIZE;
625 type = CRYPTO_DIGEST_SHA1;
627 case STREAM_SHA256_DIGEST:
628 len = CRYPTO_DIGEST_SHA256_SIZE;
629 type = CRYPTO_DIGEST_SHA256;
631 case STREAM_SHA512_DIGEST:
632 len = CRYPTO_DIGEST_SHA512_SIZE;
633 type = CRYPTO_DIGEST_SHA512;
636 /* Never reached ... */
637 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. Unsupported digest stream type: %d"),
642 bin_to_base64(digestbuf, sizeof(digestbuf), fname, len, true);
643 Dmsg3(400, "DigestLen=%d Digest=%s type=%d\n", strlen(digestbuf),
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);
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));
659 jcr->cached_attribute = false;
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));
671 * Update File Attributes in the catalog with data
672 * sent by the Storage daemon.
674 void catalog_update(JCR *jcr, BSOCK *bs)
676 if (!jcr->pool->catalog_files) {
677 return; /* user disabled cataloging */
679 if (jcr->is_job_canceled()) {
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);
690 update_attribute(jcr, bs->msg, bs->msglen);
693 if (jcr->is_job_canceled()) {
694 jcr->cached_attribute = false;
695 cancel_storage_daemon_job(jcr);
700 * Update File Attributes in the catalog with data read from
701 * the storage daemon spool file. We receive the filename and
704 bool despool_attributes_from_file(JCR *jcr, const char *file)
710 int32_t msglen; /* message length */
711 POOLMEM *msg = get_pool_memory(PM_MESSAGE);
715 Dmsg1(100, "Begin despool_attributes_from_file\n", file);
717 if (jcr->is_job_canceled() || !jcr->pool->catalog_files || !jcr->db) {
718 goto bail_out; /* user disabled cataloging */
721 spool_fd = bfopen(file, "rb");
722 //Dmsg1(000, "Open attr read file=%s\n", file);
724 Dmsg0(100, "cancel despool_attributes_from_file\n");
725 /* send an error message */
728 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
729 posix_fadvise(fileno(spool_fd), 0, 0, POSIX_FADV_WILLNEED);
733 * We read the attributes file or stream from the SD. It should
734 * be in the following format:
736 * 1. 4 bytes representing the record length
737 * 2. An attribute string starting with: UpdCat Job=nnn FileAttributes ...
740 nbytes = fread((char *)&pktsiz, 1, sizeof(int32_t), spool_fd);
741 if (nbytes == 0) { /* EOF */
744 if (nbytes != sizeof(int32_t)) {
745 Dmsg2(000, "Error: attr read status=%lld addr=%lld\n", nbytes, ftello(spool_fd));
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);
755 if (msglen > (int32_t)sizeof_pool_memory(msg)) {
756 msg = realloc_pool_memory(msg, msglen + 1);
758 nbytes = fread(msg, 1, msglen, spool_fd);
760 if (nbytes > 0 && strncmp(msg, "UpdCat Job", 10) != 0) {
761 Dmsg3(000, "Error: recnum=%ld nbytes=%lld msg=%s\n", recnum, nbytes, msg);
763 if (nbytes != (ssize_t)msglen) {
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());
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())) {
781 if (ferror(spool_fd)) {
783 Qmsg1(jcr, M_FATAL, 0, _("fread attr spool error. ERR=%s\n"),
785 Dmsg1(050, "fread attr spool error. ERR=%s\n", be.bstrerror());
792 //Dmsg1(000, "Close attr read file=%s\n", file);
796 if (jcr->is_job_canceled()) {
797 jcr->cached_attribute = false;
798 cancel_storage_daemon_job(jcr);
801 free_pool_memory(msg);
802 Dmsg1(100, "End despool_attributes_from_file ret=%i\n", ret);