]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/catreq.c
- ========= Remove Accept Any Volume ========= directive.
[bacula/bacula] / bacula / src / dird / catreq.c
1 /*
2  *
3  *   Bacula Director -- catreq.c -- handles the message channel
4  *    catalog request from the Storage daemon.
5  *
6  *     Kern Sibbald, March MMI
7  *
8  *    This routine runs as a thread and must be thread reentrant.
9  *
10  *  Basic tasks done here:
11  *      Handle Catalog services.
12  *
13  *   Version $Id$
14  */
15 /*
16    Copyright (C) 2001-2006 Kern Sibbald
17
18    This program is free software; you can redistribute it and/or
19    modify it under the terms of the GNU General Public License
20    version 2 as amended with additional clauses defined in the
21    file LICENSE in the main source directory.
22
23    This program is distributed in the hope that it will be useful,
24    but WITHOUT ANY WARRANTY; without even the implied warranty of
25    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
26    the file LICENSE for additional details.
27
28  */
29
30 #include "bacula.h"
31 #include "dird.h"
32 #include "findlib/find.h"
33
34 /*
35  * Handle catalog request
36  *  For now, we simply return next Volume to be used
37  */
38
39 /* Requests from the Storage daemon */
40 static char Find_media[] = "CatReq Job=%127s FindMedia=%d pool_name=%127s media_type=%127s\n";
41 static char Get_Vol_Info[] = "CatReq Job=%127s GetVolInfo VolName=%127s write=%d\n";
42
43 static char Update_media[] = "CatReq Job=%127s UpdateMedia VolName=%s"
44    " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%" lld " VolMounts=%u"
45    " VolErrors=%u VolWrites=%u MaxVolBytes=%" lld " EndTime=%d VolStatus=%10s"
46    " Slot=%d relabel=%d InChanger=%d VolReadTime=%" lld " VolWriteTime=%" lld
47    " VolParts=%u\n";
48
49 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
50    " FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
51    " StartBlock=%u EndBlock=%u Copy=%d Strip=%d\n";
52
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 VolMounts=%u VolErrors=%u VolWrites=%u"
57    " MaxVolBytes=%s VolCapacityBytes=%s VolStatus=%s Slot=%d"
58    " MaxVolJobs=%u MaxVolFiles=%u InChanger=%d VolReadTime=%s"
59    " VolWriteTime=%s EndFile=%u EndBlock=%u VolParts=%u LabelType=%d\n";
60
61 static char OK_create[] = "1000 OK CreateJobMedia\n";
62
63
64 static int send_volume_info_to_storage_daemon(JCR *jcr, BSOCK *sd, MEDIA_DBR *mr)
65 {
66    int stat;
67    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
68
69    jcr->MediaId = mr->MediaId;
70    pm_strcpy(jcr->VolumeName, mr->VolumeName);
71    bash_spaces(mr->VolumeName);
72    stat = bnet_fsend(sd, OK_media, mr->VolumeName, mr->VolJobs,
73       mr->VolFiles, mr->VolBlocks, edit_uint64(mr->VolBytes, ed1),
74       mr->VolMounts, mr->VolErrors, mr->VolWrites,
75       edit_uint64(mr->MaxVolBytes, ed2),
76       edit_uint64(mr->VolCapacityBytes, ed3),
77       mr->VolStatus, mr->Slot, mr->MaxVolJobs, mr->MaxVolFiles,
78       mr->InChanger,
79       edit_uint64(mr->VolReadTime, ed4),
80       edit_uint64(mr->VolWriteTime, ed5),
81       mr->EndFile, mr->EndBlock,
82       mr->VolParts,
83       mr->LabelType);
84    unbash_spaces(mr->VolumeName);
85    Dmsg2(100, "Vol Info for %s: %s", jcr->Job, sd->msg);
86    return stat;
87 }
88
89 void catalog_request(JCR *jcr, BSOCK *bs)
90 {
91    MEDIA_DBR mr, sdmr;
92    JOBMEDIA_DBR jm;
93    char Job[MAX_NAME_LENGTH];
94    char pool_name[MAX_NAME_LENGTH];
95    int index, ok, label, writing;
96    POOLMEM *omsg;
97    POOL_DBR pr;
98    uint32_t Stripe;
99
100    memset(&mr, 0, sizeof(mr));
101    memset(&sdmr, 0, sizeof(sdmr));
102    memset(&jm, 0, sizeof(jm));
103
104    /*
105     * Request to find next appendable Volume for this Job
106     */
107    Dmsg1(100, "catreq %s", bs->msg);
108    if (!jcr->db) {
109       omsg = get_memory(bs->msglen+1);
110       pm_strcpy(omsg, bs->msg);
111       bnet_fsend(bs, _("1990 Invalid Catalog Request: %s"), omsg);    
112       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request; DB not open: %s"), omsg);
113       free_memory(omsg);
114       return;
115    }
116    /*
117     * Find next appendable medium for SD
118     */
119    if (sscanf(bs->msg, Find_media, &Job, &index, &pool_name, &mr.MediaType) == 4) {
120       memset(&pr, 0, sizeof(pr));
121       bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
122       unbash_spaces(pr.Name);
123       ok = db_get_pool_record(jcr, jcr->db, &pr);
124       if (ok) {
125          mr.PoolId = pr.PoolId;
126          mr.StorageId = jcr->store->StorageId;
127          ok = find_next_volume_for_append(jcr, &mr, index, true /*permit create new vol*/);
128          Dmsg3(100, "find_media idx=%d ok=%d vol=%s\n", index, ok, mr.VolumeName);
129       }
130       /*
131        * Send Find Media response to Storage daemon
132        */
133       if (ok) {
134          send_volume_info_to_storage_daemon(jcr, bs, &mr);
135       } else {
136          bnet_fsend(bs, _("1901 No Media.\n"));
137          Dmsg0(500, "1901 No Media.\n");
138       }
139
140    /*
141     * Request to find specific Volume information
142     */
143    } else if (sscanf(bs->msg, Get_Vol_Info, &Job, &mr.VolumeName, &writing) == 3) {
144       Dmsg1(100, "CatReq GetVolInfo Vol=%s\n", mr.VolumeName);
145       /*
146        * Find the Volume
147        */
148       unbash_spaces(mr.VolumeName);
149       if (db_get_media_record(jcr, jcr->db, &mr)) {
150          const char *reason = NULL;           /* detailed reason for rejection */
151          /*
152           * If we are reading, accept any volume (reason == NULL)
153           * If we are writing, check if the Volume is valid
154           *   for this job, and do a recycle if necessary
155           */
156          if (writing) {
157             /*
158              * SD wants to write this Volume, so make
159              *   sure it is suitable for this job, i.e.
160              *   Pool matches, and it is either Append or Recycle
161              *   and Media Type matches and Pool allows any volume.
162              */
163             if (mr.PoolId != jcr->jr.PoolId) {
164                reason = _("not in Pool");
165             } else if (strcmp(mr.MediaType, jcr->store->media_type) != 0) {
166                reason = _("not correct MediaType");
167             } else {
168                /*
169                 * Now try recycling if necessary
170                 *   reason set non-NULL if we cannot use it
171                 */
172                check_if_volume_valid_or_recyclable(jcr, &mr, &reason);
173             }
174          }
175          if (reason == NULL) {
176             /*
177              * Send Find Media response to Storage daemon
178              */
179             send_volume_info_to_storage_daemon(jcr, bs, &mr);
180          } else {
181             /* Not suitable volume */
182             bnet_fsend(bs, _("1998 Volume \"%s\" status is %s, %s.\n"), mr.VolumeName,
183                mr.VolStatus, reason);
184          }
185
186       } else {
187          bnet_fsend(bs, _("1997 Volume \"%s\" not in catalog.\n"), mr.VolumeName);
188          Dmsg1(100, "1997 Volume \"%s\" not in catalog.\n", mr.VolumeName);
189       }
190
191    /*
192     * Request to update Media record. Comes typically at the end
193     *  of a Storage daemon Job Session, when labeling/relabeling a
194     *  Volume, or when an EOF mark is written.
195     */
196    } else if (sscanf(bs->msg, Update_media, &Job, &sdmr.VolumeName,
197       &sdmr.VolJobs, &sdmr.VolFiles, &sdmr.VolBlocks, &sdmr.VolBytes,
198       &sdmr.VolMounts, &sdmr.VolErrors, &sdmr.VolWrites, &sdmr.MaxVolBytes,
199       &sdmr.LastWritten, &sdmr.VolStatus, &sdmr.Slot, &label, &sdmr.InChanger,
200       &sdmr.VolReadTime, &sdmr.VolWriteTime, &sdmr.VolParts) == 18) {
201
202       db_lock(jcr->db);
203       Dmsg3(400, "Update media %s oldStat=%s newStat=%s\n", sdmr.VolumeName,
204          mr.VolStatus, sdmr.VolStatus);
205       bstrncpy(mr.VolumeName, sdmr.VolumeName, sizeof(mr.VolumeName)); /* copy Volume name */
206       unbash_spaces(mr.VolumeName);
207       if (!db_get_media_record(jcr, jcr->db, &mr)) {
208          Jmsg(jcr, M_ERROR, 0, _("Unable to get Media record for Volume %s: ERR=%s\n"),
209               mr.VolumeName, db_strerror(jcr->db));
210          bnet_fsend(bs, _("1991 Catalog Request for vol=%s failed: %s"),
211             mr.VolumeName, db_strerror(jcr->db));
212          db_unlock(jcr->db);
213          return;
214       }
215       /* Set first written time if this is first job */
216       if (mr.FirstWritten == 0) {
217          mr.FirstWritten = jcr->start_time;   /* use Job start time as first write */
218          mr.set_first_written = true;
219       }
220       /* If we just labeled the tape set time */
221       if (label || mr.LabelDate == 0) {
222          mr.LabelDate = jcr->start_time;
223          mr.set_label_date = true;
224          if (mr.InitialWrite == 0) {
225             mr.InitialWrite = jcr->start_time;
226          }
227          Dmsg2(400, "label=%d labeldate=%d\n", label, mr.LabelDate);
228       } else {
229          /*
230           * Insanity check for VolFiles get set to a smaller value
231           */
232          if (sdmr.VolFiles < mr.VolFiles) {
233             Jmsg(jcr, M_FATAL, 0, _("Volume Files at %u being set to %u"
234                  " for Volume \"%s\". This is incorrect.\n"),
235                mr.VolFiles, sdmr.VolFiles, mr.VolumeName);
236             bnet_fsend(bs, _("1992 Update Media error. VolFiles=%u, CatFiles=%u\n"),
237                sdmr.VolFiles, mr.VolFiles);
238             db_unlock(jcr->db);
239             return;
240          }
241       }
242       Dmsg2(400, "Update media: BefVolJobs=%u After=%u\n", mr.VolJobs, sdmr.VolJobs);
243       /* Copy updated values to original media record */
244       mr.VolJobs      = sdmr.VolJobs;
245       mr.VolFiles     = sdmr.VolFiles;
246       mr.VolBlocks    = sdmr.VolBlocks;
247       mr.VolBytes     = sdmr.VolBytes;
248       mr.VolMounts    = sdmr.VolMounts;
249       mr.VolErrors    = sdmr.VolErrors;
250       mr.VolWrites    = sdmr.VolWrites;
251       mr.LastWritten  = sdmr.LastWritten;
252       mr.Slot         = sdmr.Slot;
253       mr.InChanger    = sdmr.InChanger;
254       mr.VolReadTime  = sdmr.VolReadTime;
255       mr.VolWriteTime = sdmr.VolWriteTime;
256       mr.VolParts     = sdmr.VolParts;
257       bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus));
258       if (jcr->store->StorageId) {
259          mr.StorageId = jcr->store->StorageId;
260       }
261
262       Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName);
263       /*
264        * Update the database, then before sending the response to the
265        *  SD, check if the Volume has expired.
266        */
267       if (!db_update_media_record(jcr, jcr->db, &mr)) {
268          Jmsg(jcr, M_FATAL, 0, _("Catalog error updating Media record. %s"),
269             db_strerror(jcr->db));
270          bnet_fsend(bs, _("1993 Update Media error\n"));
271          Dmsg0(400, "send error\n");
272       } else {
273          (void)has_volume_expired(jcr, &mr);
274          send_volume_info_to_storage_daemon(jcr, bs, &mr);
275       }
276       db_unlock(jcr->db);
277
278    /*
279     * Request to create a JobMedia record
280     */
281    } else if (sscanf(bs->msg, Create_job_media, &Job,
282       &jm.FirstIndex, &jm.LastIndex, &jm.StartFile, &jm.EndFile,
283       &jm.StartBlock, &jm.EndBlock, &jm.Copy, &Stripe) == 9) {
284
285       if (jcr->previous_jcr) {
286          jm.JobId = jcr->previous_jcr->JobId;
287          jm.MediaId = jcr->MediaId;
288       } else {
289          jm.JobId = jcr->JobId;
290          jm.MediaId = jcr->MediaId;
291       }
292       Dmsg6(400, "create_jobmedia JobId=%d MediaId=%d SF=%d EF=%d FI=%d LI=%d\n",
293          jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex);
294       if (!db_create_jobmedia_record(jcr, jcr->db, &jm)) {
295          Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"),
296             db_strerror(jcr->db));
297          bnet_fsend(bs, _("1991 Update JobMedia error\n"));
298       } else {
299          Dmsg0(400, "JobMedia record created\n");
300          bnet_fsend(bs, OK_create);
301       }
302
303    } else {
304       omsg = get_memory(bs->msglen+1);
305       pm_strcpy(omsg, bs->msg);
306       bnet_fsend(bs, _("1990 Invalid Catalog Request: %s"), omsg);
307       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request: %s"), omsg);
308       free_memory(omsg);
309    }
310    Dmsg1(400, ">CatReq response: %s", bs->msg);
311    Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr);
312    return;
313 }
314
315 /*
316  * Update File Attributes in the catalog with data
317  *  sent by the Storage daemon.  Note, we receive the whole
318  *  attribute record, but we select out only the stat packet,
319  *  VolSessionId, VolSessionTime, FileIndex, and file name
320  *  to store in the catalog.
321  */
322 void catalog_update(JCR *jcr, BSOCK *bs)
323 {
324    unser_declare;
325    uint32_t VolSessionId, VolSessionTime;
326    int32_t Stream;
327    uint32_t FileIndex;
328    uint32_t data_len;
329    char *p;
330    int len;
331    char *fname, *attr;
332    ATTR_DBR *ar = NULL;
333    POOLMEM *omsg;
334
335    if (!jcr->pool->catalog_files) {
336       return;                         /* user disabled cataloging */
337    }
338    if (!jcr->db) {
339       omsg = get_memory(bs->msglen+1);
340       pm_strcpy(omsg, bs->msg);
341       bnet_fsend(bs, _("1991 Invalid Catalog Update: %s"), omsg);    
342       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog Update; DB not open: %s"), omsg);
343       free_memory(omsg);
344       return;
345    }
346
347    /* Start transaction allocates jcr->attr and jcr->ar if needed */
348    db_start_transaction(jcr, jcr->db);     /* start transaction if not already open */
349    ar = jcr->ar;      
350
351    /* Start by scanning directly in the message buffer to get Stream   
352     *  there may be a cached attr so we cannot yet write into
353     *  jcr->attr or jcr->ar  
354     */
355    p = bs->msg;
356    skip_nonspaces(&p);                /* UpdCat */
357    skip_spaces(&p);
358    skip_nonspaces(&p);                /* Job=nnn */
359    skip_spaces(&p);
360    skip_nonspaces(&p);                /* FileAttributes */
361    p += 1;
362    unser_begin(p, 0);
363    unser_uint32(VolSessionId);
364    unser_uint32(VolSessionTime);
365    unser_int32(FileIndex);
366    unser_int32(Stream);
367    unser_uint32(data_len);
368    p += unser_length(p);
369
370    Dmsg1(400, "UpdCat msg=%s\n", bs->msg);
371    Dmsg5(400, "UpdCat VolSessId=%d VolSessT=%d FI=%d Strm=%d data_len=%d\n",
372       VolSessionId, VolSessionTime, FileIndex, Stream, data_len);
373
374    if (Stream == STREAM_UNIX_ATTRIBUTES || Stream == STREAM_UNIX_ATTRIBUTES_EX) {
375       if (jcr->cached_attribute) {
376          Dmsg2(400, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname);
377          if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
378             Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
379          }
380       }
381       /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
382       jcr->attr = check_pool_memory_size(jcr->attr, bs->msglen);
383       memcpy(jcr->attr, bs->msg, bs->msglen);
384       p = jcr->attr - bs->msg + p;    /* point p into jcr->attr */
385       skip_nonspaces(&p);             /* skip FileIndex */
386       skip_spaces(&p);
387       skip_nonspaces(&p);             /* skip FileType */
388       skip_spaces(&p);
389       fname = p;
390       len = strlen(fname);        /* length before attributes */
391       attr = &fname[len+1];
392
393       Dmsg2(400, "dird<stored: stream=%d %s\n", Stream, fname);
394       Dmsg1(400, "dird<stored: attr=%s\n", attr);
395       ar->attr = attr;
396       ar->fname = fname;
397       ar->FileIndex = FileIndex;
398       ar->Stream = Stream;
399       ar->link = NULL;
400       if (jcr->previous_jcr) {
401          ar->JobId = jcr->previous_jcr->JobId;
402       } else {
403          ar->JobId = jcr->JobId;
404       }
405       ar->Digest = NULL;
406       ar->DigestType = CRYPTO_DIGEST_NONE;
407       jcr->cached_attribute = true;
408
409       Dmsg2(400, "dird<filed: stream=%d %s\n", Stream, fname);
410       Dmsg1(400, "dird<filed: attr=%s\n", attr);
411
412    } else if (crypto_digest_stream_type(Stream) != CRYPTO_DIGEST_NONE) {
413       fname = p;
414       if (ar->FileIndex != FileIndex) {
415          Jmsg(jcr, M_WARNING, 0, _("Got %s but not same File as attributes\n"), stream_to_ascii(Stream));
416       } else {
417          /* Update digest in catalog */
418          char digestbuf[CRYPTO_DIGEST_MAX_SIZE];
419          int len = 0;
420          int type = CRYPTO_DIGEST_NONE;
421
422          switch(Stream) {
423          case STREAM_MD5_DIGEST:
424             len = CRYPTO_DIGEST_MD5_SIZE;
425             type = CRYPTO_DIGEST_MD5;
426             break;
427          case STREAM_SHA1_DIGEST:
428             len = CRYPTO_DIGEST_SHA1_SIZE;
429             type = CRYPTO_DIGEST_SHA1;
430             break;
431          case STREAM_SHA256_DIGEST:
432             len = CRYPTO_DIGEST_SHA256_SIZE;
433             type = CRYPTO_DIGEST_SHA256;
434             break;
435          case STREAM_SHA512_DIGEST:
436             len = CRYPTO_DIGEST_SHA512_SIZE;
437             type = CRYPTO_DIGEST_SHA512;
438             break;
439          default:
440             /* Never reached ... */
441             Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. Unsupported digest stream type: %d"),
442                  Stream);
443          }
444
445          bin_to_base64(digestbuf, fname, len);
446          Dmsg3(400, "DigestLen=%d Digest=%s type=%d\n", strlen(digestbuf), digestbuf, Stream);
447          if (jcr->cached_attribute) {
448             ar->Digest = digestbuf;
449             ar->DigestType = type;
450             Dmsg2(400, "Cached attr with digest. Stream=%d fname=%s\n", ar->Stream, ar->fname);
451             if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
452                Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
453             }
454             jcr->cached_attribute = false; 
455          } else {
456             if (!db_add_digest_to_file_record(jcr, jcr->db, ar->FileId, digestbuf, type)) {
457                Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. %s"),
458                   db_strerror(jcr->db));
459             }
460          }
461       }
462    }
463 }