]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/dvd.c
Replace explicit checks for "/" with calls to IsPathSeparator, strchr with first_path...
[bacula/bacula] / bacula / src / stored / dvd.c
1 /*
2  *
3  *   dvd.c  -- Routines specific to DVD devices (and
4  *             possibly other removable hard media). 
5  *
6  *    Nicolas Boichat, MMV
7  *
8  *   Version $Id$
9  */
10 /*
11    Copyright (C) 2005-2006 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"
26 #include "stored.h"
27
28 /* Forward referenced functions */
29 static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name);
30
31 /* 
32  * Write the current volume/part filename to archive_name.
33  */
34 void make_mounted_dvd_filename(DEVICE *dev, POOL_MEM &archive_name) 
35 {
36    pm_strcpy(archive_name, dev->device->mount_point);
37    add_file_and_part_name(dev, archive_name);
38 }
39
40 void make_spooled_dvd_filename(DEVICE *dev, POOL_MEM &archive_name)
41 {
42    /* Use the working directory if spool directory is not defined */
43    if (dev->device->spool_directory) {
44       pm_strcpy(archive_name, dev->device->spool_directory);
45    } else {
46       pm_strcpy(archive_name, working_directory);
47    }
48    add_file_and_part_name(dev, archive_name);
49 }      
50
51 static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name)
52 {
53    char partnumber[20];
54
55    if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) {
56       pm_strcat(archive_name, "/");
57    }
58
59    pm_strcat(archive_name, dev->VolCatInfo.VolCatName);
60    /* if part > 1, append .# to the filename (where # is the part number) */
61    if (dev->part > 1) {
62       pm_strcat(archive_name, ".");
63       bsnprintf(partnumber, sizeof(partnumber), "%d", dev->part);
64       pm_strcat(archive_name, partnumber);
65    }
66    Dmsg2(400, "Exit add_file_part_name: arch=%s, part=%d\n",
67                   archive_name.c_str(), dev->part);
68 }  
69
70 /* Update the free space on the device */
71 bool DEVICE::update_freespace() 
72 {
73    POOL_MEM ocmd(PM_FNAME);
74    POOLMEM* results;
75    char* icmd;
76    int timeout;
77    uint64_t free;
78    char ed1[50];
79    bool ok = false;
80    int status;
81
82    if (!is_dvd() || is_freespace_ok()) {
83       return true;
84    }
85    
86    /* The device must be mounted in order to dvd-freespace to work */
87    mount(1);
88    
89    sm_check(__FILE__, __LINE__, false);
90    icmd = device->free_space_command;
91    
92    if (!icmd) {
93       free_space = 0;
94       free_space_errno = 0;
95       clear_freespace_ok();              /* No valid freespace */
96       clear_media();
97       Dmsg2(29, "ERROR: update_free_space_dev: free_space=%s, free_space_errno=%d (!icmd)\n", 
98             edit_uint64(free_space, ed1), free_space_errno);
99       Mmsg(errmsg, _("No FreeSpace command defined.\n"));
100       return false;
101    }
102    
103    edit_mount_codes(ocmd, icmd);
104    
105    Dmsg1(29, "update_freespace: cmd=%s\n", ocmd.c_str());
106
107    results = get_pool_memory(PM_MESSAGE);
108    
109    /* Try at most 3 times to get the free space on the device. This should perhaps be configurable. */
110    timeout = 3;
111    
112    while (1) {
113       berrno be;
114       Dmsg1(20, "Run freespace prog=%s\n", ocmd.c_str());
115       status = run_program_full_output(ocmd.c_str(), max_open_wait/2, results);
116       Dmsg2(500, "Freespace status=%d result=%s\n", status, results);
117       if (status == 0) {
118          free = str_to_int64(results);
119          Dmsg1(400, "Free space program run: Freespace=%s\n", results);
120          if (free >= 0) {
121             free_space = free;
122             free_space_errno = 0;
123             set_freespace_ok();     /* have valid freespace */
124             set_media();
125             Mmsg(errmsg, "");
126             ok = true;
127             break;
128          }
129       }
130       free_space = 0;
131       free_space_errno = EPIPE;
132       clear_freespace_ok();         /* no valid freespace */
133       Mmsg2(errmsg, _("Cannot run free space command. Results=%s ERR=%s\n"), 
134             results, be.strerror(status));
135       
136       if (--timeout > 0) {
137          Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
138             "free_space_errno=%d ERR=%s\n", print_name(), 
139                edit_uint64(free_space, ed1), free_space_errno, 
140                errmsg);
141          bmicrosleep(1, 0);
142          continue;
143       }
144
145       dev_errno = free_space_errno;
146       Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
147          "free_space_errno=%d ERR=%s\n",
148             print_name(), edit_uint64(free_space, ed1),
149             free_space_errno, errmsg);
150       break;
151    }
152    
153    free_pool_memory(results);
154    Dmsg4(29, "leave update_freespace: free_space=%s freespace_ok=%d free_space_errno=%d have_media=%d\n", 
155       edit_uint64(free_space, ed1), !!is_freespace_ok(), free_space_errno, !!have_media());
156    sm_check(__FILE__, __LINE__, false);
157    return ok;
158 }
159
160 /*
161  * Note!!!! Part numbers now begin at 1. The part number is
162  *  suppressed from the first part, which is just the Volume
163  *  name. Each subsequent part is the Volumename.partnumber.
164  *
165  * Write a part (Vol, Vol.2, ...) from the spool to the DVD   
166  * This routine does not update the part number, so normally, you
167  *  should call open_next_part()
168  *
169  * It is also called from truncate_dvd to "blank" the medium, as
170  *  well as from block.c when the DVD is full to write the last part.
171  */
172 bool dvd_write_part(DCR *dcr)
173 {
174    DEVICE *dev = dcr->dev;
175    POOL_MEM archive_name(PM_FNAME);
176    
177    /*
178     * Don't write empty part files.
179     * This is only useful when growisofs does not support write beyond
180     * the 4GB boundary.
181     * Example :
182     *   - 3.9 GB on the volume, dvd-freespace reports 0.4 GB free
183     *   - Write 0.2 GB on the volume, Bacula thinks it could still
184     *     append data, it creates a new empty part.
185     *   - dvd-freespace reports 0 GB free, as the 4GB boundary has
186     *     been crossed
187     *   - Bacula thinks he must finish to write to the device, so it
188     *     tries to write the last part (0-byte), but dvd-writepart fails...
189     *
190     * There is one exception: when recycling a volume, we write a blank part
191     * file, so, then, we need to accept to write it.
192     */
193    if (dev->part_size == 0 && !dev->truncating) {
194       Dmsg2(29, "dvd_write_part: device is %s, won't write blank part %d\n", dev->print_name(), dev->part);
195       /* Delete spool file */
196       make_spooled_dvd_filename(dev, archive_name);
197       unlink(archive_name.c_str());
198       dev->set_part_spooled(false);
199       Dmsg1(29, "========= unlink(%s)\n", archive_name.c_str());
200       sm_check(__FILE__, __LINE__, false);
201       return true;
202    }
203    
204    POOL_MEM ocmd(PM_FNAME);
205    POOL_MEM results(PM_MESSAGE);
206    char* icmd;
207    int status;
208    int timeout;
209    char ed1[50];
210    
211    dev->clear_freespace_ok();             /* need to update freespace */
212
213    sm_check(__FILE__, __LINE__, false);
214    Dmsg3(29, "dvd_write_part: device is %s, part is %d, is_mounted=%d\n", dev->print_name(), dev->part, dev->is_mounted());
215    icmd = dev->device->write_part_command;
216    
217    dev->edit_mount_codes(ocmd, icmd);
218       
219    /*
220     * original line follows
221     * timeout = dev->max_open_wait + (dev->max_part_size/(1350*1024/2));
222     * I modified this for a longer timeout; pre-formatting, blanking and
223     * writing can take quite a while
224     */
225
226    /* Explanation of the timeout value, when writing the first part,
227     *  by Arno Lehmann :
228     * 9 GB, write speed 1x: 6990 seconds (almost 2 hours...)
229     * Overhead: 900 seconds (starting, initializing, finalizing,probably 
230     *   reloading 15 minutes)
231     * Sum: 15780.
232     * A reasonable last-exit timeout would be 16000 seconds. Quite long - 
233     * almost 4.5 hours, but hopefully, that timeout will only ever be needed 
234     * in case of a serious emergency.
235     */
236
237    if (dev->part == 1) {
238       timeout = 16000;
239    } else {
240       timeout = dev->max_open_wait + (dev->part_size/(1350*1024/4));
241    }
242
243    Dmsg2(20, "Write part: cmd=%s timeout=%d\n", ocmd.c_str(), timeout);
244    status = run_program_full_output(ocmd.c_str(), timeout, results.c_str());
245    Dmsg2(20, "Write part status=%d result=%s\n", status, results.c_str());
246
247    dev->blank_dvd = false;
248    if (status != 0) {
249       Jmsg2(dcr->jcr, M_FATAL, 0, _("Error writing part %d to the DVD: ERR=%s\n"),
250          dev->part, results.c_str());
251       Mmsg1(dev->errmsg, _("Error while writing current part to the DVD: %s"), 
252             results.c_str());
253       Dmsg1(100, "%s\n", dev->errmsg);
254       dev->dev_errno = EIO;
255       if (!dev->truncating) {
256          mark_volume_in_error(dcr);
257       }
258       sm_check(__FILE__, __LINE__, false);
259       return false;
260    }
261    Jmsg(dcr->jcr, M_INFO, 0, _("Part %d (%lld bytes) written to DVD.\n"), dev->part, dev->part_size);
262    Dmsg3(400, "dvd_write_part: Part %d (%lld bytes) written to DVD\nResults: %s\n",
263             dev->part, dev->part_size, results.c_str());
264     
265    dev->num_dvd_parts++;            /* there is now one more part on DVD */
266    dev->VolCatInfo.VolCatParts = dev->num_dvd_parts;
267    dcr->VolCatInfo.VolCatParts = dev->num_dvd_parts;
268    Dmsg1(100, "Update num_parts=%d\n", dev->num_dvd_parts);
269
270    /* Delete spool file */
271    make_spooled_dvd_filename(dev, archive_name);
272    unlink(archive_name.c_str());
273    dev->set_part_spooled(false);
274    Dmsg1(29, "========= unlink(%s)\n", archive_name.c_str());
275    sm_check(__FILE__, __LINE__, false);
276    
277    /* growisofs umounted the device, so remount it (it will update the free space) */
278    dev->clear_mounted();
279    dev->mount(1);
280    Jmsg(dcr->jcr, M_INFO, 0, _("Remaining free space %s on %s\n"), 
281       edit_uint64_with_commas(dev->free_space, ed1), dev->print_name());
282    sm_check(__FILE__, __LINE__, false);
283    return true;
284 }
285
286 /*
287  * Open the next part file.
288  *  - Close the fd
289  *  - Increment part number 
290  *  - Reopen the device
291  */
292 int dvd_open_next_part(DCR *dcr)
293 {
294    DEVICE *dev = dcr->dev;
295
296    Dmsg6(29, "Enter: == open_next_part part=%d npart=%d dev=%s vol=%s mode=%d file_addr=%d\n", 
297       dev->part, dev->num_dvd_parts, dev->print_name(),
298          dev->VolCatInfo.VolCatName, dev->openmode, dev->file_addr);
299    if (!dev->is_dvd()) {
300       Dmsg1(100, "Device %s is not dvd!!!!\n", dev->print_name()); 
301       return -1;
302    }
303    
304    /* When appending, do not open a new part if the current is empty */
305    if (dev->can_append() && (dev->part > dev->num_dvd_parts) && 
306        (dev->part_size == 0)) {
307       Dmsg0(29, "open_next_part exited immediately (dev->part_size == 0).\n");
308       return dev->fd;
309    }
310
311    dev->close_part(dcr);               /* close current part */
312    
313    /*
314     * If we have a spooled part open, write it to the
315     *  DVD before opening the next part.
316     */
317    if (dev->is_part_spooled()) {
318       Dmsg2(100, "Before open next write previous. part=%d num_parts=%d\n",
319          dev->part, dev->num_dvd_parts);
320       if (!dvd_write_part(dcr)) {
321          Dmsg0(29, "Error in dvd_write part.\n");
322          return -1;
323       }
324    }
325      
326    dev->part_start += dev->part_size;
327    dev->part++;
328    Dmsg2(29, "Inc part=%d num_dvd_parts=%d\n", dev->part, dev->num_dvd_parts);
329
330    /* Are we working on a part past what is written in the DVD? */
331    if (dev->num_dvd_parts < dev->part) {
332       POOL_MEM archive_name(PM_FNAME);
333       struct stat buf;
334       /* 
335        * First check what is on DVD.  If our part is there, we
336        *   are in trouble, so bail out.
337        * NB: This is however not a problem if we are writing the first part.
338        * It simply means that we are over writing an existing volume...
339        */
340       if (dev->num_dvd_parts > 0) {
341          make_mounted_dvd_filename(dev, archive_name);   /* makes dvd name */
342          Dmsg1(100, "Check if part on DVD: %s\n", archive_name.c_str());
343          if (stat(archive_name.c_str(), &buf) == 0) {
344             /* bad news bail out */
345             dev->set_part_spooled(false);
346             Mmsg1(&dev->errmsg, _("Next Volume part already exists on DVD. Cannot continue: %s\n"),
347                archive_name.c_str());
348             return -1;
349          }
350       }
351
352 #ifdef neeeded
353       Dmsg2(400, "num_dvd_parts=%d part=%d\n", dev->num_dvd_parts, dev->part);
354       make_spooled_dvd_filename(dev, archive_name);   /* makes spool name */
355       
356       /* Check if the next part exists in spool directory . */
357       Dmsg1(100, "Check if part on spool: %s\n", archive_name.c_str());
358       if ((stat(archive_name.c_str(), &buf) == 0) || (errno != ENOENT)) {
359          Dmsg1(29, "======= Part %s is in the way, deleting it...\n", archive_name.c_str());
360          /* Then try to unlink it */
361          if (unlink(archive_name.c_str()) < 0) {
362             berrno be;
363             dev->set_part_spooled(false);
364             dev->dev_errno = errno;
365             Mmsg2(dev->errmsg, _("open_next_part can't unlink existing part %s, ERR=%s\n"), 
366                    archive_name.c_str(), be.strerror());
367             return -1;
368          }
369       }
370 #endif
371    }
372
373    Dmsg2(400, "Call dev->open(vol=%s, mode=%d)\n", dcr->VolCatInfo.VolCatName, 
374          dev->openmode);
375
376    /* Open next part.  Note, this sets part_size for part opened. */
377    if (dev->open(dcr, OPEN_READ_ONLY) < 0) {
378       return -1;
379    } 
380    dev->set_labeled();                   /* all next parts are "labeled" */
381    
382    return dev->fd;
383 }
384
385 /*
386  * Open the first part file.
387  *  - Close the fd
388  *  - Reopen the device
389  */
390 static bool dvd_open_first_part(DCR *dcr, int mode)
391 {
392    DEVICE *dev = dcr->dev;
393
394    Dmsg5(29, "Enter: ==== open_first_part dev=%s Vol=%s mode=%d num_dvd_parts=%d append=%d\n", dev->print_name(), 
395          dev->VolCatInfo.VolCatName, dev->openmode, dev->num_dvd_parts, dev->can_append());
396
397
398    dev->close_part(dcr);
399
400    Dmsg2(400, "Call dev->open(vol=%s, mode=%d)\n", dcr->VolCatInfo.VolCatName, 
401          mode);
402    Dmsg0(100, "Set part=1\n");
403    dev->part = 1;
404    dev->part_start = 0;
405
406    if (dev->open(dcr, mode) < 0) {
407       Dmsg0(400, "open dev() failed\n");
408       return false;
409    }
410    Dmsg2(400, "Leave open_first_part state=%s append=%d\n", dev->is_open()?"open":"not open", dev->can_append());
411    
412    return true;
413 }
414
415
416 /* 
417  * Do an lseek on a DVD handling all the different parts
418  */
419 boffset_t lseek_dvd(DCR *dcr, boffset_t offset, int whence)
420 {
421    DEVICE *dev = dcr->dev;
422    boffset_t pos;
423    char ed1[50], ed2[50];
424    
425    Dmsg5(400, "Enter lseek_dvd fd=%d off=%s w=%d part=%d nparts=%d\n", dev->fd,
426       edit_int64(offset, ed1), whence, dev->part, dev->num_dvd_parts);
427
428    switch(whence) {
429    case SEEK_SET:
430       Dmsg2(400, "lseek_dvd SEEK_SET to %s (part_start=%s)\n",
431          edit_int64(offset, ed1), edit_uint64(dev->part_start, ed2));
432       if ((uint64_t)offset >= dev->part_start) {
433          if ((uint64_t)offset == dev->part_start || 
434              (uint64_t)offset < dev->part_start+dev->part_size) {
435             /* We are staying in the current part, just seek */
436 #if defined(HAVE_WIN32)
437             pos = _lseeki64(dev->fd, offset-dev->part_start, SEEK_SET);
438 #else
439             pos = lseek(dev->fd, offset-dev->part_start, SEEK_SET);
440 #endif
441             if (pos < 0) {
442                return pos;
443             } else {
444                return pos + dev->part_start;
445             }
446          } else {
447             /* Load next part, and start again */
448             Dmsg0(100, "lseek open next part\n");
449             if (dvd_open_next_part(dcr) < 0) {
450                Dmsg0(400, "lseek_dvd failed while trying to open the next part\n");
451                return -1;
452             }
453             Dmsg2(100, "Recurse lseek after open next part=%d num_part=%d\n",
454                dev->part, dev->num_dvd_parts);
455             return lseek_dvd(dcr, offset, SEEK_SET);
456          }
457       } else {
458          /*
459           * pos < dev->part_start :
460           * We need to access a previous part, 
461           * so just load the first one, and seek again
462           * until the right one is loaded
463           */
464          Dmsg0(100, "lseek open first part\n");
465          if (!dvd_open_first_part(dcr, dev->openmode)) {
466             Dmsg0(400, "lseek_dvd failed while trying to open the first part\n");
467             return -1;
468          }
469          Dmsg2(100, "Recurse lseek after open first part=%d num_part=%d\n",
470                dev->part, dev->num_dvd_parts);
471          return lseek_dvd(dcr, offset, SEEK_SET); /* system lseek */
472       }
473       break;
474    case SEEK_CUR:
475       Dmsg1(400, "lseek_dvd SEEK_CUR to %s\n", edit_int64(offset, ed1));
476       if ((pos = lseek(dev->fd, (off_t)0, SEEK_CUR)) < 0) {
477          Dmsg0(400, "Seek error.\n");
478          return pos;                  
479       }
480       pos += dev->part_start;
481       if (offset == 0) {
482          Dmsg1(400, "lseek_dvd SEEK_CUR returns %s\n", edit_uint64(pos, ed1));
483          return pos;
484       } else { 
485          Dmsg1(400, "do lseek_dvd SEEK_SET %s\n", edit_uint64(pos, ed1));
486          return lseek_dvd(dcr, pos, SEEK_SET);
487       }
488       break;
489    case SEEK_END:
490       Dmsg1(400, "lseek_dvd SEEK_END to %s\n", edit_int64(offset, ed1));
491       /*
492        * Bacula does not use offsets for SEEK_END
493        *  Also, Bacula uses seek_end only when it wants to
494        *  append to the volume, so for a dvd that means
495        *  that the volume must be spooled since the DVD
496        *  itself is read-only (as currently implemented).
497        */
498       if (offset > 0) { /* Not used by bacula */
499          Dmsg1(400, "lseek_dvd SEEK_END called with an invalid offset %s\n", 
500             edit_uint64(offset, ed1));
501          errno = EINVAL;
502          return -1;
503       }
504       /* If we are already on a spooled part and have the
505        *  right part number, simply seek
506        */
507       if (dev->is_part_spooled() && dev->part > dev->num_dvd_parts) {
508          if ((pos = lseek(dev->fd, (off_t)0, SEEK_END)) < 0) {
509             return pos;   
510          } else {
511             Dmsg1(400, "lseek_dvd SEEK_END returns %s\n", 
512                   edit_uint64(pos + dev->part_start, ed1));
513             return pos + dev->part_start;
514          }
515       } else {
516          /*
517           * Load the first part, then load the next until we reach the last one.
518           * This is the only way to be sure we compute the right file address.
519           *
520           * Save previous openmode, and open all but last part read-only 
521           * (useful for DVDs) 
522           */
523          int modesave = dev->openmode;
524          if (!dvd_open_first_part(dcr, OPEN_READ_ONLY)) {
525             Dmsg0(400, "lseek_dvd failed while trying to open the first part\n");
526             return -1;
527          }
528          if (dev->num_dvd_parts > 0) {
529             while (dev->part < dev->num_dvd_parts) {
530                if (dvd_open_next_part(dcr) < 0) {
531                   Dmsg0(400, "lseek_dvd failed while trying to open the next part\n");
532                   return -1;
533                }
534             }
535             dev->openmode = modesave;
536             if (dvd_open_next_part(dcr) < 0) {
537                Dmsg0(400, "lseek_dvd failed while trying to open the next part\n");
538                return -1;
539             }
540          }
541          return lseek_dvd(dcr, 0, SEEK_END);
542       }
543       break;
544    default:
545       Dmsg0(400, "Seek call error.\n");
546       errno = EINVAL;
547       return -1;
548    }
549 }
550
551 bool dvd_close_job(DCR *dcr)
552 {
553    DEVICE *dev = dcr->dev;
554    JCR *jcr = dcr->jcr;
555    bool ok = true;
556
557    /*
558     * If the device is a dvd and WritePartAfterJob
559     * is set to yes, open the next part, so, in case of a device
560     * that requires mount, it will be written to the device.
561     */
562    if (dev->is_dvd() && jcr->write_part_after_job && (dev->part_size > 0)) {
563       Dmsg1(400, "Writing last part=%d write_partafter_job is set.\n",
564          dev->part);
565       if (dev->part < dev->num_dvd_parts+1) {
566          Jmsg3(jcr, M_FATAL, 0, _("Error writing. Current part less than total number of parts (%d/%d, device=%s)\n"),
567                dev->part, dev->num_dvd_parts, dev->print_name());
568          dev->dev_errno = EIO;
569          ok = false;
570       }
571       
572       if (ok && !dvd_write_part(dcr)) {
573          Jmsg2(jcr, M_FATAL, 0, _("Unable to write last on %s: ERR=%s\n"),
574                dev->print_name(), dev->bstrerror());
575          dev->dev_errno = EIO;
576          ok = false;
577       }
578    }
579    return ok;
580 }
581
582 void dvd_remove_empty_part(DCR *dcr) 
583 {
584    DEVICE *dev = dcr->dev;
585
586    /* Remove the last part file if it is empty */
587    if (dev->is_dvd() && dev->num_dvd_parts > 0) {
588       struct stat statp;
589       uint32_t part_save = dev->part;
590       POOL_MEM archive_name(PM_FNAME);
591       int status;
592
593       dev->part = dev->num_dvd_parts;
594       make_spooled_dvd_filename(dev, archive_name);
595       /* Check that the part file is empty */
596       status = stat(archive_name.c_str(), &statp);
597       if (status == 0 && statp.st_size == 0) {
598          Dmsg3(100, "Unlink empty part in close call make_dvd_filename. part=%d num=%d vol=%s\n", 
599                 part_save, dev->num_dvd_parts, dev->VolCatInfo.VolCatName);
600          Dmsg1(100, "unlink(%s)\n", archive_name.c_str());
601          unlink(archive_name.c_str());
602          if (part_save == dev->part) {
603             dev->set_part_spooled(false);  /* no spooled part left */
604          }
605       } else if (status < 0) {                         
606          if (part_save == dev->part) {
607             dev->set_part_spooled(false);  /* spool doesn't exit */
608          }
609       }       
610       dev->part = part_save;               /* restore part number */
611    }
612 }
613
614 bool truncate_dvd(DCR *dcr) 
615 {
616    DEVICE* dev = dcr->dev;
617
618    dev->clear_freespace_ok();             /* need to update freespace */
619    dev->close_part(dcr);
620
621    if (!dev->unmount(1)) {
622       Dmsg0(400, "truncate_dvd: Failed to unmount DVD\n");
623       return false;
624    }
625
626    /* If necessary, delete its spool file. */
627    if (dev->is_part_spooled()) {
628       POOL_MEM archive_name(PM_FNAME);
629       /* Delete spool file */
630       make_spooled_dvd_filename(dev, archive_name);
631       unlink(archive_name.c_str());
632       dev->set_part_spooled(false);
633    }
634
635    /* Set num_dvd_parts to zero (on disk) */
636    dev->part = 0;
637    dev->num_dvd_parts = 0;
638    dcr->VolCatInfo.VolCatParts = 0;
639    dev->VolCatInfo.VolCatParts = 0;
640    
641    Dmsg0(400, "truncate_dvd: Opening first part (1)...\n");
642    
643    dev->truncating = true;
644    /* This creates a zero length spool file and sets part=1 */
645    if (!dvd_open_first_part(dcr, CREATE_READ_WRITE)) {
646       Dmsg0(400, "truncate_dvd: Error while opening first part (1).\n");
647       dev->truncating = false;
648       return false;
649    }
650
651    dev->close_part(dcr);
652    
653    Dmsg0(400, "truncate_dvd: Opening first part (2)...\n");
654    
655    /* 
656     * Now actually truncate the DVD which is done by writing
657     *  a zero length part to the DVD/
658     */
659    if (!dvd_write_part(dcr)) {
660       Dmsg0(400, "truncate_dvd: Error while writing to DVD.\n");
661       dev->truncating = false;
662       return false;
663    }
664    dev->truncating = false;
665    
666    /* Set num_dvd_parts to zero (on disk) */
667    dev->part = 0;
668    dev->num_dvd_parts = 0;
669    dcr->VolCatInfo.VolCatParts = 0;
670    dev->VolCatInfo.VolCatParts = 0;
671    /* Clear the size of the volume */
672    dev->VolCatInfo.VolCatBytes = 0;
673    dcr->VolCatInfo.VolCatBytes = 0;
674
675    /* Update catalog */
676    if (!dir_update_volume_info(dcr, false)) {
677       return false;
678    }
679    
680    return true;
681 }
682
683 /*
684  * Checks if we can write on a non-blank DVD: meaning that it just have been
685  * truncated (there is only one zero-sized file on the DVD).
686  *  
687  * Note!  Normally if we can mount the device, which should be the case
688  *   when we get here, it is not a blank DVD.  Hence we check if
689  *   if all files are of zero length (i.e. no data), in which case we allow it.
690  *
691  */
692 bool check_can_write_on_non_blank_dvd(DCR *dcr) 
693 {
694    DEVICE* dev = dcr->dev;
695    DIR* dp;
696    struct dirent *entry, *result;
697    int name_max;
698    struct stat filestat;
699    bool ok = true;
700       
701    name_max = pathconf(".", _PC_NAME_MAX);
702    if (name_max < 1024) {
703       name_max = 1024;
704    }
705    
706    if (!(dp = opendir(dev->device->mount_point))) {
707       berrno be;
708       dev->dev_errno = errno;
709       Dmsg3(29, "check_can_write_on_non_blank_dvd: failed to open dir %s (dev=%s), ERR=%s\n", 
710             dev->device->mount_point, dev->print_name(), be.strerror());
711       return false;
712    }
713    
714    entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
715    for ( ;; ) {
716       if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
717          dev->dev_errno = EIO;
718          Dmsg2(129, "check_can_write_on_non_blank_dvd: no more files in dir %s (dev=%s)\n", 
719                dev->device->mount_point, dev->print_name());
720          break;
721       } else {
722          Dmsg2(99, "check_can_write_on_non_blank_dvd: found %s (versus %s)\n", 
723                result->d_name, dev->VolCatInfo.VolCatName);
724          if (strcmp(result->d_name, ".") && strcmp(result->d_name, "..") &&
725              strcmp(result->d_name, ".keep")) {
726             /* Found a file, checking it is empty */
727             POOL_MEM filename(PM_FNAME);
728             pm_strcpy(filename, dev->device->mount_point);
729             if (!IsPathSeparator(filename.c_str()[strlen(filename.c_str())-1])) {
730                pm_strcat(filename, "/");
731             }
732             pm_strcat(filename, result->d_name);
733             if (stat(filename.c_str(), &filestat) < 0) {
734                berrno be;
735                dev->dev_errno = errno;
736                Dmsg2(29, "check_can_write_on_non_blank_dvd: cannot stat file (file=%s), ERR=%s\n", 
737                   filename.c_str(), be.strerror());
738                ok = false;
739                break;
740             }
741             Dmsg2(99, "check_can_write_on_non_blank_dvd: size of %s is %lld\n", 
742                filename.c_str(), filestat.st_size);
743             if (filestat.st_size != 0) {
744                ok = false;
745                break;
746             }
747          }
748       }
749    }
750    free(entry);
751    closedir(dp);
752    
753    Dmsg1(29, "OK  can_write_on_non_blank_dvd: OK=%d\n", ok);
754    return ok;
755 }
756
757 /* 
758  * Mount a DVD device, then scan to find out how many parts
759  *  there are.
760  */
761 int find_num_dvd_parts(DCR *dcr)
762 {
763    DEVICE *dev = dcr->dev;
764    int num_parts = 0;
765
766    if (!dev->is_dvd()) {
767       return 0;
768    }
769    
770    if (dev->mount(1)) {
771       DIR* dp;
772       struct dirent *entry, *result;
773       int name_max;
774       int len = strlen(dcr->VolCatInfo.VolCatName);
775
776       /* Now count the number of parts */
777       name_max = pathconf(".", _PC_NAME_MAX);
778       if (name_max < 1024) {
779          name_max = 1024;
780       }
781          
782       if (!(dp = opendir(dev->device->mount_point))) {
783          berrno be;
784          dev->dev_errno = errno;
785          Dmsg3(29, "find_num_dvd_parts: failed to open dir %s (dev=%s), ERR=%s\n", 
786                dev->device->mount_point, dev->print_name(), be.strerror());
787          goto get_out;
788       }
789       
790       entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
791
792       Dmsg1(100, "Looking for Vol=%s\n", dcr->VolCatInfo.VolCatName);
793       for ( ;; ) {
794          int flen;
795          bool ignore;
796          if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
797             dev->dev_errno = EIO;
798             Dmsg2(129, "find_num_dvd_parts: failed to find suitable file in dir %s (dev=%s)\n", 
799                   dev->device->mount_point, dev->print_name());
800             break;
801          }
802          flen = strlen(result->d_name);
803          ignore = true;
804          if (flen >= len) {
805             result->d_name[len] = 0;
806             if (strcmp(dcr->VolCatInfo.VolCatName, result->d_name) == 0) {
807                num_parts++;
808                Dmsg1(100, "find_num_dvd_parts: found part: %s\n", result->d_name);
809                ignore = false;
810             }
811          }
812          if (ignore) {
813             Dmsg2(129, "find_num_dvd_parts: ignoring %s in %s\n", 
814                   result->d_name, dev->device->mount_point);
815          }
816       }
817       free(entry);
818       closedir(dp);
819       Dmsg1(29, "find_num_dvd_parts = %d\n", num_parts);
820    }
821    
822 get_out:
823    dev->set_freespace_ok();
824    if (dev->is_mounted()) {
825       dev->unmount(0);
826    }
827    return num_parts;
828 }