]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/acquire.c
Replace explicit checks for "/" with calls to IsPathSeparator, strchr with first_path...
[bacula/bacula] / bacula / src / stored / acquire.c
1 /*
2  *  Routines to acquire and release a device for read/write
3  *
4  *   Kern Sibbald, August MMII
5  *
6  *   Version $Id$
7  */
8 /*
9    Copyright (C) 2002-2006 Kern Sibbald
10
11    This program is free software; you can redistribute it and/or
12    modify it under the terms of the GNU General Public License
13    version 2 as amended with additional clauses defined in the
14    file LICENSE in the main source directory.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
19    the file LICENSE for additional details.
20
21  */
22
23 #include "bacula.h"                   /* pull in global headers */
24 #include "stored.h"                   /* pull in Storage Deamon headers */
25
26 /* Forward referenced functions */
27 static void attach_dcr_to_dev(DCR *dcr);
28
29
30 /*********************************************************************
31  * Acquire device for reading. 
32  *  The drive should have previously been reserved by calling 
33  *  reserve_device_for_read(). We read the Volume label from the block and
34  *  leave the block pointers just after the label.
35  *
36  *  Returns: NULL if failed for any reason
37  *           dcr  if successful
38  */
39 bool acquire_device_for_read(DCR *dcr)
40 {
41    DEVICE *dev = dcr->dev;
42    JCR *jcr = dcr->jcr;
43    bool ok = false;
44    bool tape_previously_mounted;
45    bool tape_initially_mounted;
46    VOL_LIST *vol;
47    bool try_autochanger = true;
48    int i;
49    int vol_label_status;
50    int retry = 0;
51    
52    Dmsg1(50, "jcr->dcr=%p\n", jcr->dcr);
53    dev->block(BST_DOING_ACQUIRE);
54
55    if (dev->num_writers > 0) {
56       Jmsg2(jcr, M_FATAL, 0, _("Num_writers=%d not zero. Job %d canceled.\n"), 
57          dev->num_writers, jcr->JobId);
58       goto get_out;
59    }
60
61    /* Find next Volume, if any */
62    vol = jcr->VolList;
63    if (!vol) {
64       char ed1[50];
65       Jmsg(jcr, M_FATAL, 0, _("No volumes specified. Job %s canceled.\n"), 
66          edit_int64(jcr->JobId, ed1));
67       goto get_out;
68    }
69    jcr->CurVolume++;
70    for (i=1; i<jcr->CurVolume; i++) {
71       vol = vol->next;
72    }
73    if (!vol) {
74       Jmsg(jcr, M_FATAL, 0, _("Logic error: no next volume. Numvol=%d Curvol=%d\n"),
75          jcr->NumVolumes, jcr->CurVolume);
76       goto get_out;                   /* should not happen */   
77    }
78    bstrncpy(dcr->VolumeName, vol->VolumeName, sizeof(dcr->VolumeName));
79    bstrncpy(dcr->media_type, vol->MediaType, sizeof(dcr->media_type));
80    dcr->VolCatInfo.Slot = vol->Slot;
81     
82    /*
83     * If the MediaType requested for this volume is not the
84     *  same as the current drive, we attempt to find the same
85     *  device that was used to write the orginal volume.  If
86     *  found, we switch to using that device.
87     */
88    Dmsg2(100, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
89    if (dcr->media_type[0] && strcmp(dcr->media_type, dev->device->media_type) != 0) {
90       RCTX rctx;
91       DIRSTORE *store;
92       int stat;
93       DCR *dcr_save = jcr->dcr;
94
95       lock_reservations();
96       jcr->dcr = NULL;
97       memset(&rctx, 0, sizeof(RCTX));
98       rctx.jcr = jcr;
99       jcr->reserve_msgs = New(alist(10, not_owned_by_alist));
100       rctx.any_drive = true;
101       rctx.device_name = vol->device;
102       store = new DIRSTORE;
103       memset(store, 0, sizeof(DIRSTORE));
104       store->name[0] = 0; /* No dir name */
105       bstrncpy(store->media_type, vol->MediaType, sizeof(store->media_type));
106       bstrncpy(store->pool_name, dcr->pool_name, sizeof(store->pool_name));
107       bstrncpy(store->pool_type, dcr->pool_type, sizeof(store->pool_type));
108       store->append = false;
109       rctx.store = store;
110       
111       /*
112        * Note, if search_for_device() succeeds, we get a new_dcr,
113        *  which we do not use except for the dev info.
114        */
115       stat = search_res_for_device(rctx);
116       release_msgs(jcr);              /* release queued messages */
117       unlock_reservations();
118       if (stat == 1) {
119          DCR *new_dcr = jcr->read_dcr;
120          dev->unblock();
121          detach_dcr_from_dev(dcr);    /* release old device */
122          /* Copy important info from the new dcr */
123          dev = dcr->dev = new_dcr->dev; 
124          jcr->read_dcr = dcr; 
125          dcr->device = new_dcr->device;
126          dcr->max_job_spool_size = dcr->device->max_job_spool_size;
127          attach_dcr_to_dev(dcr);
128          new_dcr->VolumeName[0] = 0;
129          free_dcr(new_dcr);
130          dev->block(BST_DOING_ACQUIRE); 
131          Jmsg(jcr, M_INFO, 0, _("Media Type change.  New device %s chosen.\n"),
132             dev->print_name());
133          bstrncpy(dcr->VolumeName, vol->VolumeName, sizeof(dcr->VolumeName));
134          bstrncpy(dcr->media_type, vol->MediaType, sizeof(dcr->media_type));
135          dcr->VolCatInfo.Slot = vol->Slot;
136          bstrncpy(dcr->pool_name, store->pool_name, sizeof(dcr->pool_name));
137          bstrncpy(dcr->pool_type, store->pool_type, sizeof(dcr->pool_type));
138       } else if (stat == 0) {   /* device busy */
139          Dmsg1(000, "Device %s is busy.\n", vol->device);
140       } else {
141          /* error */
142          Jmsg1(jcr, M_FATAL, 0, _("No suitable device found to read Volume \"%s\"\n"),
143             vol->VolumeName);
144          jcr->dcr = dcr_save;
145          goto get_out;
146       }
147       jcr->dcr = dcr_save;
148    }
149
150
151    init_device_wait_timers(dcr);
152
153    tape_previously_mounted = dev->can_read() || dev->can_append() ||
154                              dev->is_labeled();
155    tape_initially_mounted = tape_previously_mounted;
156
157
158    /* Volume info is always needed because of VolParts */
159    Dmsg0(200, "dir_get_volume_info\n");
160    if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_READ)) {
161       Jmsg1(jcr, M_WARNING, 0, "%s", jcr->errmsg);
162    }
163    
164    for ( ;; ) {
165       /* If not polling limit retries */
166       if (!dev->poll && retry++ > 10) {
167          break;
168       }
169       dev->clear_labeled();              /* force reread of label */
170       if (job_canceled(jcr)) {
171          char ed1[50];
172          Mmsg1(dev->errmsg, _("Job %s canceled.\n"), edit_int64(jcr->JobId, ed1));
173          Jmsg(jcr, M_INFO, 0, dev->errmsg);
174          goto get_out;                /* error return */
175       }
176
177       autoload_device(dcr, 0, NULL);
178
179       /*
180        * This code ensures that the device is ready for
181        * reading. If it is a file, it opens it.
182        * If it is a tape, it checks the volume name
183        */
184       Dmsg1(100, "bstored: open vol=%s\n", dcr->VolumeName);
185       if (dev->open(dcr, OPEN_READ_ONLY) < 0) {
186         Jmsg3(jcr, M_WARNING, 0, _("Read open device %s Volume \"%s\" failed: ERR=%s\n"),
187               dev->print_name(), dcr->VolumeName, dev->bstrerror());
188          goto default_path;
189       }
190       Dmsg1(100, "opened dev %s OK\n", dev->print_name());
191       
192       /* Read Volume Label */
193       
194       Dmsg0(200, "calling read-vol-label\n");
195       vol_label_status = read_dev_volume_label(dcr);
196       switch (vol_label_status) {
197       case VOL_OK:
198          ok = true;
199          memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
200          break;                    /* got it */
201       case VOL_IO_ERROR:
202          /*
203           * Send error message generated by read_dev_volume_label()
204           *  only we really had a tape mounted. This supresses superfluous
205           *  error messages when nothing is mounted.
206           */
207          if (tape_previously_mounted) {
208             Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
209          }
210          goto default_path;
211       case VOL_NAME_ERROR:
212          if (tape_initially_mounted) {
213             tape_initially_mounted = false;
214             goto default_path;
215          }
216          /* If polling and got a previous bad name, ignore it */
217          if (dev->poll && strcmp(dev->BadVolName, dev->VolHdr.VolumeName) == 0) {
218             goto default_path;
219          } else {
220              bstrncpy(dev->BadVolName, dev->VolHdr.VolumeName, sizeof(dev->BadVolName));
221          }
222          /* Fall through */
223       default:
224          Jmsg1(jcr, M_WARNING, 0, "%s", jcr->errmsg);
225 default_path:
226          tape_previously_mounted = true;
227          
228          /*
229           * If the device requires mount, close it, so the device can be ejected.
230           */
231          if (dev->requires_mount()) {
232             dev->close();
233          }
234          
235          /* Call autochanger only once unless ask_sysop called */
236          if (try_autochanger) {
237             int stat;
238             Dmsg2(200, "calling autoload Vol=%s Slot=%d\n",
239                dcr->VolumeName, dcr->VolCatInfo.Slot);
240             stat = autoload_device(dcr, 0, NULL);
241             if (stat > 0) {
242                try_autochanger = false;
243                continue;              /* try reading volume mounted */
244             }
245          }
246          
247          /* Mount a specific volume and no other */
248          Dmsg0(200, "calling dir_ask_sysop\n");
249          if (!dir_ask_sysop_to_mount_volume(dcr)) {
250             goto get_out;             /* error return */
251          }
252          try_autochanger = true;      /* permit using autochanger again */
253          continue;                    /* try reading again */
254       } /* end switch */
255       break;
256    } /* end for loop */
257    if (!ok) {
258       Jmsg1(jcr, M_FATAL, 0, _("Too many errors trying to mount device %s.\n"),
259             dev->print_name());
260       goto get_out;
261    }
262
263    dev->clear_append();
264    dev->set_read();
265    set_jcr_job_status(jcr, JS_Running);
266    dir_send_job_status(jcr);
267    Jmsg(jcr, M_INFO, 0, _("Ready to read from volume \"%s\" on device %s.\n"),
268       dcr->VolumeName, dev->print_name());
269
270 get_out:
271    P(dev->mutex);
272    if (dcr->reserved_device) {
273       dev->reserved_device--;
274       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
275       dcr->reserved_device = false;
276    }
277    V(dev->mutex);
278    dev->unblock();
279    Dmsg1(50, "jcr->dcr=%p\n", jcr->dcr);
280    return ok;
281 }
282
283
284 /*
285  * Acquire device for writing. We permit multiple writers.
286  *  If this is the first one, we read the label.
287  *
288  *  Returns: NULL if failed for any reason
289  *           dcr if successful.
290  *   Note, normally reserve_device_for_append() is called
291  *   before this routine.
292  */
293 DCR *acquire_device_for_append(DCR *dcr)
294 {
295    bool release = false;
296    bool recycle = false;
297    bool do_mount = false;
298    DEVICE *dev = dcr->dev;
299    JCR *jcr = dcr->jcr;
300
301    init_device_wait_timers(dcr);
302
303    dev->block(BST_DOING_ACQUIRE);
304    Dmsg1(190, "acquire_append device is %s\n", dev->is_tape()?"tape":
305         (dev->is_dvd()?"DVD":"disk"));
306
307    /*
308     * With the reservation system, this should not happen
309     */
310    if (dev->can_read()) {
311       Jmsg1(jcr, M_FATAL, 0, _("Device %s is busy reading.\n"), dev->print_name());
312       Dmsg1(200, "Device %s is busy reading.\n", dev->print_name());
313       goto get_out;
314    }
315
316    if (dev->can_append()) {
317       Dmsg0(190, "device already in append.\n");
318       /*
319        * Device already in append mode
320        *
321        * Check if we have the right Volume mounted
322        *   OK if current volume info OK
323        *   OK if next volume matches current volume
324        *   otherwise mount desired volume obtained from
325        *    dir_find_next_appendable_volume
326        *  dev->VolHdr.VolumeName is what is in the drive
327        *  dcr->VolumeName is what we pass into the routines, or
328        *    get back from the subroutines.
329        */
330       bstrncpy(dcr->VolumeName, dev->VolHdr.VolumeName, sizeof(dcr->VolumeName));
331       if (!dir_get_volume_info(dcr, GET_VOL_INFO_FOR_WRITE) &&
332           !(dir_find_next_appendable_volume(dcr) &&
333             strcmp(dev->VolHdr.VolumeName, dcr->VolumeName) == 0)) { /* wrong tape mounted */
334          Dmsg2(190, "Wrong tape mounted: %s. wants:%s\n", dev->VolHdr.VolumeName,
335             dcr->VolumeName);
336          /* Release volume reserved by dir_find_next_appendable_volume() */
337          if (dcr->VolumeName[0]) {
338             free_unused_volume(dcr);
339          }
340          if (dev->num_writers != 0) {
341             Jmsg3(jcr, M_FATAL, 0, _("Wanted Volume \"%s\", but device %s is busy writing on \"%s\" .\n"), 
342                  dcr->VolumeName, dev->print_name(), dev->VolHdr.VolumeName);
343             Dmsg3(200, "Wanted Volume \"%s\", but device %s is busy writing on \"%s\" .\n",  
344                  dcr->VolumeName, dev->print_name(), dev->VolHdr.VolumeName);
345             goto get_out;
346          }
347          /* Wrong tape mounted, release it, then fall through to get correct one */
348          Dmsg0(190, "Wrong tape mounted, release and try mount.\n");
349          release = true;
350          do_mount = true;
351       } else {
352          /*
353           * At this point, the correct tape is already mounted, so
354           *   we do not need to do mount_next_write_volume(), unless
355           *   we need to recycle the tape.
356           */
357           recycle = strcmp(dcr->VolCatInfo.VolCatStatus, "Recycle") == 0;
358           Dmsg1(190, "Correct tape mounted. recycle=%d\n", recycle);
359           if (recycle && dev->num_writers != 0) {
360              Jmsg(jcr, M_FATAL, 0, _("Cannot recycle volume \"%s\""
361                   " on device %s because it is in use by another job.\n"),
362                   dev->VolHdr.VolumeName, dev->print_name());
363              goto get_out;
364           }
365           if (dev->num_writers == 0) {
366              memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
367           }
368       }
369    } else {
370       /* Not already in append mode, so mount the device */
371       Dmsg0(190, "Not in append mode, try mount.\n");
372       ASSERT(dev->num_writers == 0);
373       do_mount = true;
374    }
375
376    if (do_mount || recycle) {
377       Dmsg0(190, "Do mount_next_write_vol\n");
378       bool mounted = mount_next_write_volume(dcr, release);
379       if (!mounted) {
380          if (!job_canceled(jcr)) {
381             /* Reduce "noise" -- don't print if job canceled */
382             Jmsg(jcr, M_FATAL, 0, _("Could not ready device %s for append.\n"),
383                dev->print_name());
384             Dmsg1(200, "Could not ready device %s for append.\n", 
385                dev->print_name());
386          }
387          goto get_out;
388       }
389       Dmsg2(190, "Output pos=%u:%u\n", dcr->dev->file, dcr->dev->block_num);
390    }
391
392    dev->num_writers++;                /* we are now a writer */
393    if (jcr->NumVolumes == 0) {
394       jcr->NumVolumes = 1;
395    }
396    P(dev->mutex);
397    if (dcr->reserved_device) {
398       dev->reserved_device--;
399       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
400       dcr->reserved_device = false;
401    }
402    V(dev->mutex);
403    dev->unblock();
404    return dcr;
405
406 /*
407  * Error return
408  */
409 get_out:
410    P(dev->mutex);
411    if (dcr->reserved_device) {
412       dev->reserved_device--;
413       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
414       dcr->reserved_device = false;
415    }
416    V(dev->mutex);
417    dev->unblock();
418    return NULL;
419 }
420
421
422 /*
423  * This job is done, so release the device. From a Unix standpoint,
424  *  the device remains open.
425  *
426  * Note, if we are spooling, we may enter with the device locked.
427  * However, in all cases, unlock the device when leaving.
428  *
429  */
430 bool release_device(DCR *dcr)
431 {
432    JCR *jcr = dcr->jcr;
433    DEVICE *dev = dcr->dev;
434    bool ok = true;
435
436    /* lock only if not already locked by this thread */
437    if (!dcr->dev_locked) {
438       lock_device(dev);
439    }
440    Dmsg2(100, "release_device device %s is %s\n", dev->print_name(), dev->is_tape()?"tape":"disk");
441
442    /* if device is reserved, job never started, so release the reserve here */
443    if (dcr->reserved_device) {
444       dev->reserved_device--;
445       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
446       dcr->reserved_device = false;
447    }
448
449    if (dev->can_read()) {
450       dev->clear_read();              /* clear read bit */
451
452       /******FIXME**** send read volume usage statistics to director */
453
454    } else if (dev->num_writers > 0) {
455       /* 
456        * Note if WEOT is set, we are at the end of the tape
457        *   and may not be positioned correctly, so the
458        *   job_media_record and update_vol_info have already been
459        *   done, which means we skip them here.
460        */
461       dev->num_writers--;
462       Dmsg1(100, "There are %d writers in release_device\n", dev->num_writers);
463       if (dev->is_labeled()) {
464          Dmsg0(100, "dir_create_jobmedia_record. Release\n");
465          if (!dev->at_weot() && !dir_create_jobmedia_record(dcr)) {
466             Jmsg(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
467                dcr->VolCatInfo.VolCatName, jcr->Job);
468          }
469          /* If no more writers, write an EOF */
470          if (!dev->num_writers && dev->can_write()) {
471             dev->weof(1);
472             write_ansi_ibm_labels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolumeName);
473          }
474          if (!dev->at_weot()) {
475             dev->VolCatInfo.VolCatFiles = dev->file;   /* set number of files */
476             dev->VolCatInfo.VolCatJobs++;              /* increment number of jobs */
477             /* Note! do volume update before close, which zaps VolCatInfo */
478             Dmsg0(100, "dir_update_vol_info. Release0\n");
479             dir_update_volume_info(dcr, false); /* send Volume info to Director */
480          }
481       }
482
483    } else {
484       /*                
485        * If we reach here, it is most likely because the job
486        *   has failed, since the device is not in read mode and
487        *   there are no writers. It was probably reserved.
488        */
489    }
490
491    /* If no writers, close if file or !CAP_ALWAYS_OPEN */
492    if (dev->num_writers == 0 && (!dev->is_tape() || !dev_cap(dev, CAP_ALWAYSOPEN))) {
493       dvd_remove_empty_part(dcr);        /* get rid of any empty spool part */
494       dev->close();
495    }
496
497    /* Fire off Alert command and include any output */
498    if (!job_canceled(jcr) && dcr->device->alert_command) {
499       POOLMEM *alert;
500       int status = 1;
501       BPIPE *bpipe;
502       char line[MAXSTRING];
503       alert = get_pool_memory(PM_FNAME);
504       alert = edit_device_codes(dcr, alert, dcr->device->alert_command, "");
505       bpipe = open_bpipe(alert, 0, "r");
506       if (bpipe) {
507          while (fgets(line, sizeof(line), bpipe->rfd)) {
508             Jmsg(jcr, M_ALERT, 0, _("Alert: %s"), line);
509          }
510          status = close_bpipe(bpipe);
511       } else {
512          status = errno;
513       }
514       if (status != 0) {
515          berrno be;
516          Jmsg(jcr, M_ALERT, 0, _("3997 Bad alert command: %s: ERR=%s.\n"),
517               alert, be.strerror(status));
518       }
519
520       Dmsg1(400, "alert status=%d\n", status);
521       free_pool_memory(alert);
522    }
523    dcr->dev_locked = false;              /* set no longer locked */
524    unlock_device(dev);
525    if (jcr->read_dcr == dcr) {
526       jcr->read_dcr = NULL;
527    }
528    if (jcr->dcr == dcr) {
529       jcr->dcr = NULL;
530    }
531    free_dcr(dcr);
532    return ok;
533 }
534
535 /*
536  * Create a new Device Control Record and attach
537  *   it to the device (if this is a real job).
538  */
539 DCR *new_dcr(JCR *jcr, DEVICE *dev)
540 {
541    DCR *dcr = (DCR *)malloc(sizeof(DCR));
542    memset(dcr, 0, sizeof(DCR));
543    dcr->jcr = jcr;
544    if (dev) {
545       dcr->dev = dev;
546       dcr->device = dev->device;
547       dcr->block = new_block(dev);
548       dcr->rec = new_record();
549       dcr->max_job_spool_size = dev->device->max_job_spool_size;
550       attach_dcr_to_dev(dcr);
551    }
552    dcr->spool_fd = -1;
553    return dcr;
554 }
555
556 /*
557  * Search the dcrs list for the given dcr. If it is found,
558  *  as it should be, then remove it. Also zap the jcr pointer
559  *  to the dcr if it is the same one.
560  */
561 #ifdef needed
562 static void remove_dcr_from_dcrs(DCR *dcr)
563 {
564    JCR *jcr = dcr->jcr;
565    if (jcr->dcrs) {
566       int i = 0;
567       DCR *ldcr;
568       int num = jcr->dcrs->size();
569       for (i=0; i < num; i++) {
570          ldcr = (DCR *)jcr->dcrs->get(i);
571          if (ldcr == dcr) {
572             jcr->dcrs->remove(i);
573             if (jcr->dcr == dcr) {
574                jcr->dcr = NULL;
575             }
576          }
577       }
578    }
579 }
580 #endif
581
582 static void attach_dcr_to_dev(DCR *dcr)
583 {
584    DEVICE *dev = dcr->dev;
585    JCR *jcr = dcr->jcr;
586
587    if (!dcr->attached_to_dev && dev->is_open() && jcr && jcr->JobType != JT_SYSTEM) {
588       dev->attached_dcrs->append(dcr);  /* attach dcr to device */
589       dcr->attached_to_dev = true;
590    }
591 }
592
593 void detach_dcr_from_dev(DCR *dcr)
594 {
595    DEVICE *dev = dcr->dev;
596
597    if (dcr->reserved_device) {
598       dcr->reserved_device = false;
599       lock_device(dev);
600       dev->reserved_device--;
601       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
602       dcr->reserved_device = false;
603       /* If we set read mode in reserving, remove it */
604       if (dev->can_read()) {
605          dev->clear_read();
606       }
607       if (dev->num_writers < 0) {
608          Jmsg1(dcr->jcr, M_ERROR, 0, _("Hey! num_writers=%d!!!!\n"), dev->num_writers);
609          dev->num_writers = 0;
610       }
611       unlock_device(dev);
612    }
613
614    /* Detach this dcr only if attached */
615    if (dcr->attached_to_dev) {
616       dcr->dev->attached_dcrs->remove(dcr);  /* detach dcr from device */
617       dcr->attached_to_dev = false;
618 //    remove_dcr_from_dcrs(dcr);      /* remove dcr from jcr list */
619    }
620    free_unused_volume(dcr);           /* free unused vols attached to this dcr */
621    pthread_cond_broadcast(&dcr->dev->wait_next_vol);
622    pthread_cond_broadcast(&wait_device_release);
623 }
624
625 /*
626  * Free up all aspects of the given dcr -- i.e. dechain it,
627  *  release allocated memory, zap pointers, ...
628  */
629 void free_dcr(DCR *dcr)
630 {
631
632    detach_dcr_from_dev(dcr);
633
634    if (dcr->block) {
635       free_block(dcr->block);
636    }
637    if (dcr->rec) {
638       free_record(dcr->rec);
639    }
640    if (dcr->jcr) {
641       dcr->jcr->dcr = NULL;
642    }
643    free(dcr);
644 }