]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/label.c
29Jul05
[bacula/bacula] / bacula / src / stored / label.c
1 /*
2  *
3  *  label.c  Bacula routines to handle labels
4  *
5  *   Kern Sibbald, MM
6  *
7  *
8  *   Version $Id$
9  */
10 /*
11    Copyright (C) 2000-2005 Kern Sibbald
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License
15    version 2 as amended with additional clauses defined in the
16    file LICENSE in the main source directory.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
21    the file LICENSE for additional details.
22
23  */
24
25 #include "bacula.h"                   /* pull in global headers */
26 #include "stored.h"                   /* pull in Storage Deamon headers */
27
28 /* Forward referenced functions */
29 static void create_volume_label_record(DCR *dcr, DEV_RECORD *rec);
30
31 extern char my_name[];
32 extern int debug_level;
33
34 /*
35  * Read the volume label
36  *
37  *  If dcr->VolumeName == NULL, we accept any Bacula Volume
38  *  If dcr->VolumeName[0] == 0, we accept any Bacula Volume
39  *  otherwise dcr->VolumeName must match the Volume.
40  *
41  *  If VolName given, ensure that it matches
42  *
43  *  Returns VOL_  code as defined in record.h
44  *    VOL_NOT_READ
45  *    VOL_OK                          good label found
46  *    VOL_NO_LABEL                    volume not labeled
47  *    VOL_IO_ERROR                    I/O error reading tape
48  *    VOL_NAME_ERROR                  label has wrong name
49  *    VOL_CREATE_ERROR                Error creating label
50  *    VOL_VERSION_ERROR               label has wrong version
51  *    VOL_LABEL_ERROR                 bad label type
52  *    VOL_NO_MEDIA                    no media in drive
53  *
54  *  The dcr block is emptied on return, and the Volume is
55  *    rewound.
56  */
57 int read_dev_volume_label(DCR *dcr)
58 {
59    JCR *jcr = dcr->jcr;
60    DEVICE *dev = dcr->dev;
61    char *VolName = dcr->VolumeName;
62    DEV_RECORD *record;
63    bool ok = false;
64    DEV_BLOCK *block = dcr->block;
65    int stat;
66    bool want_ansi_label;
67    bool have_ansi_label = false;
68
69    Dmsg3(100, "Enter read_volume_label device=%s vol=%s dev_Vol=%s\n",
70       dev->print_name(), VolName, dev->VolHdr.VolumeName[0]?dev->VolHdr.VolumeName:
71       "*NULL*");
72
73    if (!dev->is_open()) {
74       Emsg0(M_ABORT, 0, _("BAD call to read_dev_volume_label\n"));
75    }
76    if (dev->is_labeled()) {              /* did we already read label? */
77       /* Compare Volume Names allow special wild card */
78       if (VolName && *VolName && *VolName != '*' && strcmp(dev->VolHdr.VolumeName, VolName) != 0) {
79          Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"),
80             dev->print_name(), VolName, dev->VolHdr.VolumeName);
81          /*
82           * Cancel Job if too many label errors
83           *  => we are in a loop
84           */
85          if (!dev->poll && jcr->label_errors++ > 100) {
86             Jmsg(jcr, M_FATAL, 0, "Too many tries: %s", jcr->errmsg);
87          }
88          Dmsg0(100, "return VOL_NAME_ERROR\n");
89          stat = VOL_NAME_ERROR;
90          goto bail_out;
91       }
92       Dmsg0(30, "Leave read_volume_label() VOL_OK\n");
93       return VOL_OK;       /* label already read */
94    }
95
96    dev->clear_labeled();
97    dev->clear_append();
98    dev->clear_read();
99    dev->label_type = B_BACULA_LABEL;
100
101    if (!rewind_dev(dev)) {
102       Mmsg(jcr->errmsg, _("Couldn't rewind device %s: ERR=%s\n"), 
103          dev->print_name(), strerror_dev(dev));
104       Dmsg1(30, "return VOL_NO_MEDIA: %s", jcr->errmsg);
105       return VOL_NO_MEDIA;
106    }
107    bstrncpy(dev->VolHdr.Id, "**error**", sizeof(dev->VolHdr.Id));
108
109   /* Read ANSI/IBM label if so requested */
110   
111   want_ansi_label = dcr->VolCatInfo.LabelType != B_BACULA_LABEL ||
112                     dcr->device->label_type != B_BACULA_LABEL;
113   if (want_ansi_label || dev_cap(dev, CAP_CHECKLABELS)) {
114       stat = read_ansi_ibm_label(dcr);            
115       /* If we want a label and didn't find it, return error */
116       if (want_ansi_label && stat != VOL_OK) {
117          goto bail_out;
118       }
119       if (stat == VOL_NAME_ERROR || stat == VOL_LABEL_ERROR) {
120          Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"),
121               dev->print_name(), VolName, dev->VolHdr.VolumeName);
122          if (!dev->poll && jcr->label_errors++ > 100) {
123             Jmsg(jcr, M_FATAL, 0, "Too many tries: %s", jcr->errmsg);
124          }
125          goto bail_out;
126       }
127       if (stat != VOL_OK) {           /* Not an ANSI/IBM label, so re-read */
128          rewind_dev(dev);
129       } else {
130          have_ansi_label = true;
131       }
132    }
133   
134    /* Read the Bacula Volume label block */
135    record = new_record();
136    empty_block(block);
137
138    Dmsg0(90, "Big if statement in read_volume_label\n");
139    if (!read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK)) {
140       Mmsg(jcr->errmsg, _("Requested Volume \"%s\" on %s is not a Bacula "
141            "labeled Volume, because: ERR=%s"), NPRT(VolName), 
142            dev->print_name(), strerror_dev(dev));
143       Dmsg1(30, "%s", jcr->errmsg);
144    } else if (!read_record_from_block(block, record)) {
145       Mmsg(jcr->errmsg, _("Could not read Volume label from block.\n"));
146       Dmsg1(30, "%s", jcr->errmsg);
147    } else if (!unser_volume_label(dev, record)) {
148       Mmsg(jcr->errmsg, _("Could not unserialize Volume label: ERR=%s\n"),
149          strerror_dev(dev));
150       Dmsg1(30, "%s", jcr->errmsg);
151    } else if (strcmp(dev->VolHdr.Id, BaculaId) != 0 &&
152               strcmp(dev->VolHdr.Id, OldBaculaId) != 0) {
153       Mmsg(jcr->errmsg, _("Volume Header Id bad: %s\n"), dev->VolHdr.Id);
154       Dmsg1(30, "%s", jcr->errmsg);
155    } else {
156       ok = true;
157    }
158    free_record(record);               /* finished reading Volume record */
159
160    if (!ok) {
161       if (forge_on || jcr->ignore_label_errors) {
162          dev->set_labeled();         /* set has Bacula label */
163          Jmsg(jcr, M_ERROR, 0, "%s", jcr->errmsg);
164          empty_block(block);
165          return VOL_OK;
166       }
167       stat = VOL_NO_LABEL;
168       goto bail_out;
169    }
170
171    /* At this point, we have read the first Bacula block, and
172     * then read the Bacula Volume label. Now we need to
173     * make sure we have the right Volume.
174     */
175
176
177    if (dev->VolHdr.VerNum != BaculaTapeVersion &&
178        dev->VolHdr.VerNum != OldCompatibleBaculaTapeVersion1 &&
179        dev->VolHdr.VerNum != OldCompatibleBaculaTapeVersion2) {
180       Mmsg(jcr->errmsg, _("Volume on %s has wrong Bacula version. Wanted %d got %d\n"),
181          dev->print_name(), BaculaTapeVersion, dev->VolHdr.VerNum);
182       Dmsg1(30, "VOL_VERSION_ERROR: %s", jcr->errmsg);
183       stat = VOL_VERSION_ERROR;
184       goto bail_out;
185    }
186
187    /* We are looking for either an unused Bacula tape (PRE_LABEL) or
188     * a Bacula volume label (VOL_LABEL)
189     */
190    if (dev->VolHdr.LabelType != PRE_LABEL && dev->VolHdr.LabelType != VOL_LABEL) {
191       Mmsg(jcr->errmsg, _("Volume on %s has bad Bacula label type: %x\n"),
192           dev->print_name(), dev->VolHdr.LabelType);
193       Dmsg1(30, "%s", jcr->errmsg);
194       if (!dev->poll && jcr->label_errors++ > 100) {
195          Jmsg(jcr, M_FATAL, 0, "Too many tries: %s", jcr->errmsg);
196       }
197       Dmsg0(100, "return VOL_LABEL_ERROR\n");
198       stat = VOL_LABEL_ERROR;
199       goto bail_out;
200    }
201
202    dev->set_labeled();               /* set has Bacula label */
203    new_volume(dcr, dev->VolHdr.VolumeName);
204
205    /* Compare Volume Names */
206    Dmsg2(30, "Compare Vol names: VolName=%s hdr=%s\n", VolName?VolName:"*", dev->VolHdr.VolumeName);
207    if (VolName && *VolName && *VolName != '*' && strcmp(dev->VolHdr.VolumeName, VolName) != 0) {
208       Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"),
209            dev->print_name(), VolName, dev->VolHdr.VolumeName);
210       Dmsg1(30, "%s", jcr->errmsg);
211       /*
212        * Cancel Job if too many label errors
213        *  => we are in a loop
214        */
215       if (!dev->poll && jcr->label_errors++ > 100) {
216          Jmsg(jcr, M_FATAL, 0, "Too many tries: %s", jcr->errmsg);
217       }
218       Dmsg0(100, "return VOL_NAME_ERROR\n");
219       stat = VOL_NAME_ERROR;
220       goto bail_out;
221    }
222    Dmsg1(30, "Copy vol_name=%s\n", dev->VolHdr.VolumeName);
223
224    if (debug_level >= 10) {
225       dump_volume_label(dev);
226    }
227    Dmsg0(30, "Leave read_volume_label() VOL_OK\n");
228    /* If we are a streaming device, we only get one chance to read */
229    if (!dev_cap(dev, CAP_STREAM)) {
230       rewind_dev(dev);
231       if (have_ansi_label) {
232          stat = read_ansi_ibm_label(dcr);            
233          /* If we want a label and didn't find it, return error */
234          if (stat != VOL_OK) {
235             goto bail_out;
236          }
237       }
238    }
239    empty_block(block);
240    return VOL_OK;
241
242 bail_out:
243    empty_block(block);
244    rewind_dev(dev);
245    Dmsg1(100, "return %d\n", stat);
246    return stat;
247 }
248
249 /*
250  * Put a volume label into the block
251  *
252  *  Returns: false on failure
253  *           true  on success
254  */
255 bool write_volume_label_to_block(DCR *dcr)
256 {
257    DEV_RECORD rec;
258    DEVICE *dev = dcr->dev;
259    JCR *jcr = dcr->jcr;
260    DEV_BLOCK *block = dcr->block;
261
262    Dmsg0(20, "write Label in write_volume_label_to_block()\n");
263    memset(&rec, 0, sizeof(rec));
264    rec.data = get_memory(SER_LENGTH_Volume_Label);
265    empty_block(block);                /* Volume label always at beginning */
266
267    create_volume_label_record(dcr, &rec);
268
269    block->BlockNumber = 0;
270    if (!write_record_to_block(block, &rec)) {
271       free_pool_memory(rec.data);
272       Jmsg1(jcr, M_FATAL, 0, _("Cannot write Volume label to block for device %s\n"),
273          dev->print_name());
274       return false;
275    } else {
276       Dmsg1(90, "Wrote label of %d bytes to block\n", rec.data_len);
277    }
278    free_pool_memory(rec.data);
279    return true;
280 }
281
282
283 /*
284  * Write a Volume Label
285  *  !!! Note, this is ONLY used for writing
286  *            a fresh volume label.  Any data
287  *            after the label will be destroyed,
288  *            in fact, we write the label 5 times !!!!
289  *
290  *  This routine expects that open_device() was previously called.
291  *
292  *  This routine should be used only when labeling a blank tape.
293  */
294 bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName, const char *PoolName)
295 {
296    DEVICE *dev = dcr->dev;
297
298
299    Dmsg0(99, "write_volume_label()\n");
300    empty_block(dcr->block);
301
302    Dmsg1(100, "Label type=%d\n", dev->label_type);
303    if (!rewind_dev(dev)) {
304       memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
305       Dmsg2(30, "Bad status on %s from rewind: ERR=%s\n", dev->print_name(), strerror_dev(dev));
306       if (!forge_on) {
307          goto bail_out;
308       }
309    }
310
311    /* Create PRE_LABEL */
312    create_volume_label(dev, VolName, PoolName);
313
314    /*
315     * If we have already detected an ANSI label, re-read it
316     *   to skip past it. Otherwise, we write a new one if 
317     *   so requested.  
318     */
319    if (dev->label_type != B_BACULA_LABEL) {
320       if (read_ansi_ibm_label(dcr) != VOL_OK) {
321          rewind_dev(dev);
322          goto bail_out;
323       }
324    } else if (!write_ansi_ibm_labels(dcr, ANSI_VOL_LABEL, VolName)) {
325       goto bail_out;
326    }
327
328    create_volume_label_record(dcr, dcr->rec);
329    dcr->rec->Stream = 0;
330
331    /* Temporarily mark in append state to enable writing */
332    dev->set_append();
333    if (!write_record_to_block(dcr->block, dcr->rec)) {
334       Dmsg2(30, "Bad Label write on %s: ERR=%s\n", dev->print_name(), strerror_dev(dev));
335       goto bail_out;
336    } else {
337       Dmsg2(30, "Wrote label of %d bytes to %s\n", dcr->rec->data_len, dev->print_name());
338    }
339
340    Dmsg0(99, "Call write_block_to_dev()\n");
341    if (!write_block_to_dev(dcr)) {
342       Dmsg2(30, "Bad Label write on %s: ERR=%s\n", dev->print_name(), strerror_dev(dev));
343       goto bail_out;
344    }
345    Dmsg0(99, " Wrote block to device\n");
346
347    if (weof_dev(dev, 1) == 0) {
348       dev->set_labeled();
349       write_ansi_ibm_labels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolumeName);
350    }
351
352    if (debug_level >= 20)  {
353       dump_volume_label(dev);
354    }
355    dev->clear_append();               /* remove append since this is PRE_LABEL */
356    return true;
357
358 bail_out:
359    memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
360    dev->clear_append();               /* remove append since this is PRE_LABEL */
361    return false;
362 }
363
364 /*
365  * Write a volume label. This is ONLY called if we have a valid Bacula
366  *   label of type PRE_LABEL;
367  *  Returns: true if OK
368  *           false if unable to write it
369  */
370 bool rewrite_volume_label(DCR *dcr, bool recycle)
371 {
372    DEVICE *dev = dcr->dev;
373    JCR *jcr = dcr->jcr;
374
375    Dmsg2(190, "set append found freshly labeled volume. fd=%d dev=%x\n", dev->fd, dev);
376    dev->VolHdr.LabelType = VOL_LABEL; /* set Volume label */
377    dev->set_append();
378    if (!write_volume_label_to_block(dcr)) {
379       Dmsg0(200, "Error from write volume label.\n");
380       return false;
381    }
382    /*
383     * If we are not dealing with a streaming device,
384     *  write the block now to ensure we have write permission.
385     *  It is better to find out now rather than later.
386     * We do not write the block now if this is an ANSI label. This
387     *  avoids re-writing the ANSI label, which we do not want to do.
388     */
389    if (!dev_cap(dev, CAP_STREAM)) {
390       if (!rewind_dev(dev)) {
391          Jmsg2(jcr, M_WARNING, 0, _("Rewind error on device %s: ERR=%s\n"),
392                dev->print_name(), strerror_dev(dev));
393       }
394       if (recycle) {
395          if (!truncate_dev(dcr)) {
396             Jmsg2(jcr, M_WARNING, 0, _("Truncate error on device %s: ERR=%s\n"),
397                   dev->print_name(), strerror_dev(dev));
398          }
399       }
400
401       /*
402        * If we have already detected an ANSI label, re-read it
403        *   to skip past it. Otherwise, we write a new one if 
404        *   so requested.  
405        */
406       if (dev->label_type != B_BACULA_LABEL) {
407          if (read_ansi_ibm_label(dcr) != VOL_OK) {
408             rewind_dev(dev);
409             return false;
410          }
411       } else if (!write_ansi_ibm_labels(dcr, ANSI_VOL_LABEL, dev->VolHdr.VolumeName)) {
412          return false;
413       }
414
415       /* Attempt write to check write permission */
416       Dmsg1(200, "Attempt to write to device fd=%d.\n", dev->fd);
417       if (!write_block_to_dev(dcr)) {
418          Jmsg2(jcr, M_ERROR, 0, _("Unable to write device %s: ERR=%s\n"),
419             dev->print_name(), strerror_dev(dev));
420          Dmsg0(200, "===ERROR write block to dev\n");
421          return false;
422       }
423    }
424    /* Set or reset Volume statistics */
425    dev->VolCatInfo.VolCatJobs = 0;
426    dev->VolCatInfo.VolCatFiles = 0;
427    dev->VolCatInfo.VolCatBytes = 1;
428    dev->VolCatInfo.VolCatErrors = 0;
429    dev->VolCatInfo.VolCatBlocks = 0;
430    dev->VolCatInfo.VolCatRBytes = 0;
431    if (recycle) {
432       dev->VolCatInfo.VolCatMounts++;
433       dev->VolCatInfo.VolCatRecycles++;
434    } else {
435       dev->VolCatInfo.VolCatMounts = 1;
436       dev->VolCatInfo.VolCatRecycles = 0;
437       dev->VolCatInfo.VolCatWrites = 1;
438       dev->VolCatInfo.VolCatReads = 1;
439    }
440    Dmsg0(100, "dir_update_vol_info. Set Append\n");
441    bstrncpy(dev->VolCatInfo.VolCatStatus, "Append", sizeof(dev->VolCatInfo.VolCatStatus));
442    if (!dir_update_volume_info(dcr, true)) {  /* indicate doing relabel */
443       return false;
444    }
445    if (recycle) {
446       Jmsg(jcr, M_INFO, 0, _("Recycled volume \"%s\" on device %s, all previous data lost.\n"),
447          dcr->VolumeName, dev->print_name());
448    } else {
449       Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume \"%s\" on device %s\n"),
450          dcr->VolumeName, dev->print_name());
451    }
452    /*
453     * End writing real Volume label (from pre-labeled tape), or recycling
454     *  the volume.
455     */
456    Dmsg0(200, "OK from rewite vol label.\n");
457    return true;
458 }
459
460
461 /*
462  *  create_volume_label_record
463  *   Serialize label (from dev->VolHdr structure) into device record.
464  *   Assumes that the dev->VolHdr structure is properly
465  *   initialized.
466 */
467 static void create_volume_label_record(DCR *dcr, DEV_RECORD *rec)
468 {
469    ser_declare;
470    struct date_time dt;
471    DEVICE *dev = dcr->dev;
472    JCR *jcr = dcr->jcr;
473
474    /* Serialize the label into the device record. */
475
476    rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Volume_Label);
477    ser_begin(rec->data, SER_LENGTH_Volume_Label);
478    ser_string(dev->VolHdr.Id);
479
480    ser_uint32(dev->VolHdr.VerNum);
481
482    if (dev->VolHdr.VerNum >= 11) {
483       ser_btime(dev->VolHdr.label_btime);
484       dev->VolHdr.write_btime = get_current_btime();
485       ser_btime(dev->VolHdr.write_btime);
486       dev->VolHdr.write_date = 0;
487       dev->VolHdr.write_time = 0;
488    } else {
489       /* OLD WAY DEPRECATED */
490       ser_float64(dev->VolHdr.label_date);
491       ser_float64(dev->VolHdr.label_time);
492       get_current_time(&dt);
493       dev->VolHdr.write_date = dt.julian_day_number;
494       dev->VolHdr.write_time = dt.julian_day_fraction;
495    }
496    ser_float64(dev->VolHdr.write_date);   /* 0 if VerNum >= 11 */
497    ser_float64(dev->VolHdr.write_time);   /* 0  if VerNum >= 11 */
498
499    ser_string(dev->VolHdr.VolumeName);
500    ser_string(dev->VolHdr.PrevVolumeName);
501    ser_string(dev->VolHdr.PoolName);
502    ser_string(dev->VolHdr.PoolType);
503    ser_string(dev->VolHdr.MediaType);
504
505    ser_string(dev->VolHdr.HostName);
506    ser_string(dev->VolHdr.LabelProg);
507    ser_string(dev->VolHdr.ProgVersion);
508    ser_string(dev->VolHdr.ProgDate);
509
510    ser_end(rec->data, SER_LENGTH_Volume_Label);
511    rec->data_len = ser_length(rec->data);
512    rec->FileIndex = dev->VolHdr.LabelType;
513    rec->VolSessionId = jcr->VolSessionId;
514    rec->VolSessionTime = jcr->VolSessionTime;
515    rec->Stream = jcr->NumVolumes;
516    Dmsg2(100, "Created Vol label rec: FI=%s len=%d\n", FI_to_ascii(rec->FileIndex),
517       rec->data_len);
518 }
519
520
521 /*
522  * Create a volume label in memory
523  */
524 void create_volume_label(DEVICE *dev, const char *VolName, const char *PoolName)
525 {
526    DEVRES *device = (DEVRES *)dev->device;
527
528    Dmsg0(90, "Start create_volume_label()\n");
529
530    ASSERT(dev != NULL);
531
532    memset(&dev->VolHdr, 0, sizeof(dev->VolHdr));
533
534    bstrncpy(dev->VolHdr.Id, BaculaId, sizeof(dev->VolHdr.Id));
535    dev->VolHdr.VerNum = BaculaTapeVersion;
536    dev->VolHdr.LabelType = PRE_LABEL;  /* Mark tape as unused */
537    bstrncpy(dev->VolHdr.VolumeName, VolName, sizeof(dev->VolHdr.VolumeName));
538    bstrncpy(dev->VolHdr.PoolName, PoolName, sizeof(dev->VolHdr.PoolName));
539    bstrncpy(dev->VolHdr.MediaType, device->media_type, sizeof(dev->VolHdr.MediaType));
540
541    bstrncpy(dev->VolHdr.PoolType, "Backup", sizeof(dev->VolHdr.PoolType));
542
543    dev->VolHdr.label_btime = get_current_btime();
544    dev->VolHdr.label_date = 0;
545    dev->VolHdr.label_time = 0;
546
547    if (gethostname(dev->VolHdr.HostName, sizeof(dev->VolHdr.HostName)) != 0) {
548       dev->VolHdr.HostName[0] = 0;
549    }
550    bstrncpy(dev->VolHdr.LabelProg, my_name, sizeof(dev->VolHdr.LabelProg));
551    sprintf(dev->VolHdr.ProgVersion, "Ver. %s %s", VERSION, BDATE);
552    sprintf(dev->VolHdr.ProgDate, "Build %s %s", __DATE__, __TIME__);
553    dev->set_labeled();               /* set has Bacula label */
554    if (debug_level >= 90) {
555       dump_volume_label(dev);
556    }
557 }
558
559 /*
560  * Create session label
561  *  The pool memory must be released by the calling program
562  */
563 void create_session_label(DCR *dcr, DEV_RECORD *rec, int label)
564 {
565    JCR *jcr = dcr->jcr;
566    ser_declare;
567
568    rec->VolSessionId   = jcr->VolSessionId;
569    rec->VolSessionTime = jcr->VolSessionTime;
570    rec->Stream         = jcr->JobId;
571
572    rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Session_Label);
573    ser_begin(rec->data, SER_LENGTH_Session_Label);
574    ser_string(BaculaId);
575    ser_uint32(BaculaTapeVersion);
576
577    ser_uint32(jcr->JobId);
578
579    /* Changed in VerNum 11 */
580    ser_btime(get_current_btime());
581    ser_float64(0);
582
583    ser_string(dcr->pool_name);
584    ser_string(dcr->pool_type);
585    ser_string(jcr->job_name);         /* base Job name */
586    ser_string(jcr->client_name);
587
588    /* Added in VerNum 10 */
589    ser_string(jcr->Job);              /* Unique name of this Job */
590    ser_string(jcr->fileset_name);
591    ser_uint32(jcr->JobType);
592    ser_uint32(jcr->JobLevel);
593    /* Added in VerNum 11 */
594    ser_string(jcr->fileset_md5);
595
596    if (label == EOS_LABEL) {
597       ser_uint32(jcr->JobFiles);
598       ser_uint64(jcr->JobBytes);
599       ser_uint32(dcr->StartBlock);
600       ser_uint32(dcr->EndBlock);
601       ser_uint32(dcr->StartFile);
602       ser_uint32(dcr->EndFile);
603       ser_uint32(jcr->JobErrors);
604
605       /* Added in VerNum 11 */
606       ser_uint32(jcr->JobStatus);
607    }
608    ser_end(rec->data, SER_LENGTH_Session_Label);
609    rec->data_len = ser_length(rec->data);
610 }
611
612 /* Write session label
613  *  Returns: false on failure
614  *           true  on success
615  */
616 bool write_session_label(DCR *dcr, int label)
617 {
618    JCR *jcr = dcr->jcr;
619    DEVICE *dev = dcr->dev;
620    DEV_RECORD *rec;
621    DEV_BLOCK *block = dcr->block;
622
623    rec = new_record();
624    Dmsg1(90, "session_label record=%x\n", rec);
625    switch (label) {
626    case SOS_LABEL:
627       if (dev->is_tape()) {
628          dcr->StartBlock = dev->block_num;
629          dcr->StartFile  = dev->file;
630       } else {
631          dcr->StartBlock = (uint32_t)dev->file_addr;
632          dcr->StartFile = (uint32_t)(dev->file_addr >> 32);
633       }
634       break;
635    case EOS_LABEL:
636       if (dev->is_tape()) {
637          dcr->EndBlock = dev->EndBlock;
638          dcr->EndFile  = dev->EndFile;
639       } else {
640          dcr->EndBlock = (uint32_t)dev->file_addr;
641          dcr->EndFile = (uint32_t)(dev->file_addr >> 32);
642       }
643       break;
644    default:
645       Jmsg1(jcr, M_ABORT, 0, _("Bad session label = %d\n"), label);
646       break;
647    }
648    create_session_label(dcr, rec, label);
649    rec->FileIndex = label;
650
651    /*
652     * We guarantee that the session record can totally fit
653     *  into a block. If not, write the block, and put it in
654     *  the next block. Having the sesssion record totally in
655     *  one block makes reading them much easier (no need to
656     *  read the next block).
657     */
658    if (!can_write_record_to_block(block, rec)) {
659       Dmsg0(100, "Cannot write session label to block.\n");
660       if (!write_block_to_device(dcr)) {
661          Dmsg0(90, "Got session label write_block_to_dev error.\n");
662          /* ****FIXME***** errno is not set here */
663          Jmsg(jcr, M_FATAL, 0, _("Error writing Session label to %s: %s\n"),
664                            dev_vol_name(dev), strerror(errno));
665          free_record(rec);
666          return false;
667       }
668    }
669    if (!write_record_to_block(block, rec)) {
670       Jmsg(jcr, M_FATAL, 0, _("Error writing Session label to %s: %s\n"),
671                         dev_vol_name(dev), strerror(errno));
672       free_record(rec);
673       return false;
674    }
675
676    Dmsg6(20, "Write sesson_label record JobId=%d FI=%s SessId=%d Strm=%s len=%d "
677              "remainder=%d\n", jcr->JobId,
678       FI_to_ascii(rec->FileIndex), rec->VolSessionId,
679       stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len,
680       rec->remainder);
681
682    free_record(rec);
683    Dmsg2(20, "Leave write_session_label Block=%d File=%d\n",
684       dev->block_num, dev->file);
685    return true;
686 }
687
688 /*  unser_volume_label
689  *
690  * Unserialize the Bacula Volume label into the device Volume_Label
691  * structure.
692  *
693  * Assumes that the record is already read.
694  *
695  * Returns: false on error
696  *          true  on success
697 */
698
699 bool unser_volume_label(DEVICE *dev, DEV_RECORD *rec)
700 {
701    ser_declare;
702
703    if (rec->FileIndex != VOL_LABEL && rec->FileIndex != PRE_LABEL) {
704       Mmsg3(dev->errmsg, _("Expecting Volume Label, got FI=%s Stream=%s len=%d\n"),
705               FI_to_ascii(rec->FileIndex),
706               stream_to_ascii(rec->Stream, rec->FileIndex),
707               rec->data_len);
708       if (!forge_on) {
709          return false;
710       }
711    }
712
713    dev->VolHdr.LabelType = rec->FileIndex;
714    dev->VolHdr.LabelSize = rec->data_len;
715
716
717    /* Unserialize the record into the Volume Header */
718    rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Volume_Label);
719    ser_begin(rec->data, SER_LENGTH_Volume_Label);
720    unser_string(dev->VolHdr.Id);
721    unser_uint32(dev->VolHdr.VerNum);
722
723    if (dev->VolHdr.VerNum >= 11) {
724       unser_btime(dev->VolHdr.label_btime);
725       unser_btime(dev->VolHdr.write_btime);
726    } else { /* old way */
727       unser_float64(dev->VolHdr.label_date);
728       unser_float64(dev->VolHdr.label_time);
729    }
730    unser_float64(dev->VolHdr.write_date);    /* Unused with VerNum >= 11 */
731    unser_float64(dev->VolHdr.write_time);    /* Unused with VerNum >= 11 */
732
733    unser_string(dev->VolHdr.VolumeName);
734    unser_string(dev->VolHdr.PrevVolumeName);
735    unser_string(dev->VolHdr.PoolName);
736    unser_string(dev->VolHdr.PoolType);
737    unser_string(dev->VolHdr.MediaType);
738
739    unser_string(dev->VolHdr.HostName);
740    unser_string(dev->VolHdr.LabelProg);
741    unser_string(dev->VolHdr.ProgVersion);
742    unser_string(dev->VolHdr.ProgDate);
743
744    ser_end(rec->data, SER_LENGTH_Volume_Label);
745    Dmsg0(90, "unser_vol_label\n");
746    if (debug_level >= 90) {
747       dump_volume_label(dev);
748    }
749    return true;
750 }
751
752
753 bool unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec)
754 {
755    ser_declare;
756
757    rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Session_Label);
758    unser_begin(rec->data, SER_LENGTH_Session_Label);
759    unser_string(label->Id);
760    unser_uint32(label->VerNum);
761    unser_uint32(label->JobId);
762    if (label->VerNum >= 11) {
763       unser_btime(label->write_btime);
764    } else {
765       unser_float64(label->write_date);
766    }
767    unser_float64(label->write_time);
768    unser_string(label->PoolName);
769    unser_string(label->PoolType);
770    unser_string(label->JobName);
771    unser_string(label->ClientName);
772    if (label->VerNum >= 10) {
773       unser_string(label->Job);          /* Unique name of this Job */
774       unser_string(label->FileSetName);
775       unser_uint32(label->JobType);
776       unser_uint32(label->JobLevel);
777    }
778    if (label->VerNum >= 11) {
779       unser_string(label->FileSetMD5);
780    } else {
781       label->FileSetMD5[0] = 0;
782    }
783    if (rec->FileIndex == EOS_LABEL) {
784       unser_uint32(label->JobFiles);
785       unser_uint64(label->JobBytes);
786       unser_uint32(label->StartBlock);
787       unser_uint32(label->EndBlock);
788       unser_uint32(label->StartFile);
789       unser_uint32(label->EndFile);
790       unser_uint32(label->JobErrors);
791       if (label->VerNum >= 11) {
792          unser_uint32(label->JobStatus);
793       } else {
794          label->JobStatus = JS_Terminated; /* kludge */
795       }
796    }
797    return true;
798 }
799
800 void dump_volume_label(DEVICE *dev)
801 {
802    int dbl = debug_level;
803    uint32_t File;
804    const char *LabelType;
805    char buf[30];
806    struct tm tm;
807    struct date_time dt;
808
809    debug_level = 1;
810    File = dev->file;
811    switch (dev->VolHdr.LabelType) {
812    case PRE_LABEL:
813       LabelType = "PRE_LABEL";
814       break;
815    case VOL_LABEL:
816       LabelType = "VOL_LABEL";
817       break;
818    case EOM_LABEL:
819       LabelType = "EOM_LABEL";
820       break;
821    case SOS_LABEL:
822       LabelType = "SOS_LABEL";
823       break;
824    case EOS_LABEL:
825       LabelType = "EOS_LABEL";
826       break;
827    case EOT_LABEL:
828       goto bail_out;
829    default:
830       LabelType = buf;
831       sprintf(buf, "Unknown %d", dev->VolHdr.LabelType);
832       break;
833    }
834
835    Pmsg11(-1, "\nVolume Label:\n"
836 "Id                : %s"
837 "VerNo             : %d\n"
838 "VolName           : %s\n"
839 "PrevVolName       : %s\n"
840 "VolFile           : %d\n"
841 "LabelType         : %s\n"
842 "LabelSize         : %d\n"
843 "PoolName          : %s\n"
844 "MediaType         : %s\n"
845 "PoolType          : %s\n"
846 "HostName          : %s\n"
847 "",
848              dev->VolHdr.Id, dev->VolHdr.VerNum,
849              dev->VolHdr.VolumeName, dev->VolHdr.PrevVolumeName,
850              File, LabelType, dev->VolHdr.LabelSize,
851              dev->VolHdr.PoolName, dev->VolHdr.MediaType,
852              dev->VolHdr.PoolType, dev->VolHdr.HostName);
853
854    if (dev->VolHdr.VerNum >= 11) {
855       char dt[50];
856       bstrftime(dt, sizeof(dt), btime_to_unix(dev->VolHdr.label_btime));
857       Pmsg1(-1, "Date label written: %s\n", dt);
858    } else {
859    dt.julian_day_number   = dev->VolHdr.label_date;
860    dt.julian_day_fraction = dev->VolHdr.label_time;
861    tm_decode(&dt, &tm);
862    Pmsg5(-1,
863 "Date label written: %04d-%02d-%02d at %02d:%02d\n",
864       tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min);
865    }
866
867 bail_out:
868    debug_level = dbl;
869 }
870
871
872 static void dump_session_label(DEV_RECORD *rec, const char *type)
873 {
874    int dbl;
875    struct date_time dt;
876    struct tm tm;
877    SESSION_LABEL label;
878    char ec1[30], ec2[30], ec3[30], ec4[30], ec5[30], ec6[30], ec7[30];
879
880    unser_session_label(&label, rec);
881    dbl = debug_level;
882    debug_level = 1;
883    Pmsg7(-1, "\n%s Record:\n"
884 "JobId             : %d\n"
885 "VerNum            : %d\n"
886 "PoolName          : %s\n"
887 "PoolType          : %s\n"
888 "JobName           : %s\n"
889 "ClientName        : %s\n"
890 "",    type, label.JobId, label.VerNum,
891       label.PoolName, label.PoolType,
892       label.JobName, label.ClientName);
893
894    if (label.VerNum >= 10) {
895       Pmsg4(-1, ""
896 "Job (unique name) : %s\n"
897 "FileSet           : %s\n"
898 "JobType           : %c\n"
899 "JobLevel          : %c\n"
900 "", label.Job, label.FileSetName, label.JobType, label.JobLevel);
901    }
902
903    if (rec->FileIndex == EOS_LABEL) {
904       Pmsg8(-1, ""
905 "JobFiles          : %s\n"
906 "JobBytes          : %s\n"
907 "StartBlock        : %s\n"
908 "EndBlock          : %s\n"
909 "StartFile         : %s\n"
910 "EndFile           : %s\n"
911 "JobErrors         : %s\n"
912 "JobStatus         : %c\n"
913 "",
914          edit_uint64_with_commas(label.JobFiles, ec1),
915          edit_uint64_with_commas(label.JobBytes, ec2),
916          edit_uint64_with_commas(label.StartBlock, ec3),
917          edit_uint64_with_commas(label.EndBlock, ec4),
918          edit_uint64_with_commas(label.StartFile, ec5),
919          edit_uint64_with_commas(label.EndFile, ec6),
920          edit_uint64_with_commas(label.JobErrors, ec7),
921          label.JobStatus);
922    }
923    if (label.VerNum >= 11) {
924       char dt[50];
925       bstrftime(dt, sizeof(dt), btime_to_unix(label.write_btime));
926       Pmsg1(-1, _("Date written      : %s\n"), dt);
927    } else {
928       dt.julian_day_number   = label.write_date;
929       dt.julian_day_fraction = label.write_time;
930       tm_decode(&dt, &tm);
931       Pmsg5(-1, _(""
932 "Date written      : %04d-%02d-%02d at %02d:%02d\n"),
933       tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min);
934    }
935
936    debug_level = dbl;
937 }
938
939 void dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose)
940 {
941    const char *type;
942    int dbl;
943
944    if (rec->FileIndex == 0 && rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
945       return;
946    }
947    dbl = debug_level;
948    debug_level = 1;
949    switch (rec->FileIndex) {
950    case PRE_LABEL:
951       type = _("Fresh Volume");
952       break;
953    case VOL_LABEL:
954       type = _("Volume");
955       break;
956    case SOS_LABEL:
957       type = _("Begin Job Session");
958       break;
959    case EOS_LABEL:
960       type = _("End Job Session");
961       break;
962    case EOM_LABEL:
963       type = _("End of Media");
964       break;
965    case EOT_LABEL:
966       type = ("End of Tape");
967       break;
968    default:
969       type = _("Unknown");
970       break;
971    }
972    if (verbose) {
973       switch (rec->FileIndex) {
974       case PRE_LABEL:
975       case VOL_LABEL:
976          unser_volume_label(dev, rec);
977          dump_volume_label(dev);
978          break;
979       case SOS_LABEL:
980          dump_session_label(rec, type);
981          break;
982       case EOS_LABEL:
983          dump_session_label(rec, type);
984          break;
985       case EOM_LABEL:
986          Pmsg7(-1, "%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d DataLen=%d\n",
987             type, dev->file, dev->block_num, rec->VolSessionId, 
988             rec->VolSessionTime, rec->Stream, rec->data_len);
989          break;
990       case EOT_LABEL:
991          Pmsg0(-1, _("End of physical tape.\n"));
992          break;
993       default:
994          Pmsg7(-1, "%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d DataLen=%d\n",
995             type, dev->file, dev->block_num, rec->VolSessionId, 
996             rec->VolSessionTime, rec->Stream, rec->data_len);
997          break;
998       }
999    } else {
1000       SESSION_LABEL label;
1001       char dt[50];
1002       switch (rec->FileIndex) {
1003       case SOS_LABEL:
1004          unser_session_label(&label, rec);
1005          bstrftimes(dt, sizeof(dt), btime_to_unix(label.write_btime));
1006          Pmsg6(-1, "%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d\n",
1007             type, dev->file, dev->block_num, rec->VolSessionId, rec->VolSessionTime, label.JobId);
1008          Pmsg4(-1, "   Job=%s Date=%s Level=%c Type=%c\n",
1009             label.Job, dt, label.JobLevel, label.JobType);
1010          break;
1011       case EOS_LABEL:
1012          char ed1[30], ed2[30];
1013          unser_session_label(&label, rec);
1014          bstrftimes(dt, sizeof(dt), btime_to_unix(label.write_btime));
1015          Pmsg6(-1, "%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d\n",
1016             type, dev->file, dev->block_num, rec->VolSessionId, rec->VolSessionTime, label.JobId);
1017          Pmsg7(-1, "   Date=%s Level=%c Type=%c Files=%s Bytes=%s Errors=%d Status=%c\n",
1018             dt, label.JobLevel, label.JobType,
1019             edit_uint64_with_commas(label.JobFiles, ed1),
1020             edit_uint64_with_commas(label.JobBytes, ed2),
1021             label.JobErrors, (char)label.JobStatus);
1022          break;
1023       case EOM_LABEL:
1024       case PRE_LABEL:
1025       case VOL_LABEL:
1026       default:
1027          Pmsg7(-1, "%s Record: File:blk=%u:%u SessId=%d SessTime=%d JobId=%d DataLen=%d\n",
1028             type, dev->file, dev->block_num, rec->VolSessionId, rec->VolSessionTime, 
1029             rec->Stream, rec->data_len);
1030          break;
1031       case EOT_LABEL:
1032          break;
1033       }
1034    }
1035    debug_level = dbl;
1036 }