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