]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/dvd.c
- Modify DVD code so that it keeps a state flag that indicates
[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 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 edit_device_codes_dev(DEVICE *dev, POOL_MEM &omsg, const char *imsg);
30 static bool do_mount_dev(DEVICE* dev, int mount, int dotimeout);
31 static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name);
32
33 /* 
34  * Write the current volume/part filename to archive_name.
35  */
36 void make_mounted_dvd_filename(DEVICE *dev, POOL_MEM &archive_name) 
37 {
38    pm_strcpy(archive_name, dev->device->mount_point);
39    add_file_and_part_name(dev, archive_name);
40    dev->set_part_spooled(false);
41 }
42
43 void make_spooled_dvd_filename(DEVICE *dev, POOL_MEM &archive_name)
44 {
45    /* Use the working directory if spool directory is not defined */
46    if (dev->device->spool_directory) {
47       pm_strcpy(archive_name, dev->device->spool_directory);
48    } else {
49       pm_strcpy(archive_name, working_directory);
50    }
51    add_file_and_part_name(dev, archive_name);
52    dev->set_part_spooled(true);
53 }      
54
55 static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name)
56 {
57    char partnumber[20];
58    if (archive_name.c_str()[strlen(archive_name.c_str())-1] != '/') {
59       pm_strcat(archive_name, "/");
60    }
61
62    pm_strcat(archive_name, dev->VolCatInfo.VolCatName);
63    /* if part > 1, append .# to the filename (where # is the part number) */
64    if (dev->part > 0) {
65       pm_strcat(archive_name, ".");
66       bsnprintf(partnumber, sizeof(partnumber), "%d", dev->part);
67       pm_strcat(archive_name, partnumber);
68    }
69    Dmsg1(100, "Exit make_dvd_filename: arch=%s\n", archive_name.c_str());
70 }  
71
72 /* Mount the device.
73  * If timeout, wait until the mount command returns 0.
74  * If !timeout, try to mount the device only once.
75  */
76 bool mount_dev(DEVICE* dev, int timeout) 
77 {
78    Dmsg0(90, "Enter mount_dev\n");
79    if (dev->is_mounted()) {
80       return true;
81    } else if (dev->requires_mount()) {
82       return do_mount_dev(dev, 1, timeout);
83    }       
84    return true;
85 }
86
87 /* Unmount the device
88  * If timeout, wait until the unmount command returns 0.
89  * If !timeout, try to unmount the device only once.
90  */
91 bool unmount_dev(DEVICE *dev, int timeout) 
92 {
93    Dmsg0(90, "Enter unmount_dev\n");
94    if (dev->is_mounted()) {
95       return do_mount_dev(dev, 0, timeout);
96    }
97    return true;
98 }
99
100 /* (Un)mount the device */
101 static bool do_mount_dev(DEVICE* dev, int mount, int dotimeout) 
102 {
103    POOL_MEM ocmd(PM_FNAME);
104    POOLMEM *results;
105    char *icmd;
106    int status, timeout;
107    
108    sm_check(__FILE__, __LINE__, false);
109    if (mount) {
110       if (dev->is_mounted()) {
111          Dmsg0(200, "======= DVD mount=1\n");
112          return true;
113       }
114       icmd = dev->device->mount_command;
115    } else {
116       if (!dev->is_mounted()) {
117          Dmsg0(200, "======= DVD mount=0\n");
118          return true;
119       }
120       icmd = dev->device->unmount_command;
121    }
122    
123    edit_device_codes_dev(dev, ocmd, icmd);
124    
125    Dmsg2(200, "do_mount_dev: cmd=%s mounted=%d\n", ocmd.c_str(), !!dev->is_mounted());
126
127    if (dotimeout) {
128       /* Try at most 1 time to (un)mount the device. This should perhaps be configurable. */
129       timeout = 1;
130    } else {
131       timeout = 0;
132    }
133    results = get_memory(2000);
134    results[0] = 0;
135    /* If busy retry each second */
136    while ((status = run_program_full_output(ocmd.c_str(), 
137                        dev->max_open_wait/2, results)) != 0) {
138       /* Doesn't work with internationalisation (This is not a problem) */
139       if (fnmatch("*is already mounted on", results, 0) == 0) {
140          break;
141       }
142       if (timeout-- > 0) {
143          /* Sometimes the device cannot be mounted because it is already mounted.
144           * Try to unmount it, then remount it */
145          if (mount) {
146             Dmsg1(400, "Trying to unmount the device %s...\n", dev->print_name());
147             do_mount_dev(dev, 0, 0);
148          }
149          bmicrosleep(1, 0);
150          continue;
151       }
152       Dmsg2(40, "Device %s cannot be mounted. ERR=%s\n", dev->print_name(), results);
153       Mmsg(dev->errmsg, _("Device %s cannot be mounted. ERR=%s\n"), 
154            dev->print_name(), results);
155       /*
156        * Now, just to be sure it is not mounted, try to read the
157        *  filesystem.
158        */
159       DIR* dp;
160       struct dirent *entry, *result;
161       int name_max;
162       int count = 0;
163       
164       name_max = pathconf(".", _PC_NAME_MAX);
165       if (name_max < 1024) {
166          name_max = 1024;
167       }
168          
169       if (!(dp = opendir(dev->device->mount_point))) {
170          berrno be;
171          dev->dev_errno = errno;
172          Dmsg3(29, "open_mounted_dev: failed to open dir %s (dev=%s), ERR=%s\n", 
173                dev->device->mount_point, dev->print_name(), be.strerror());
174          goto get_out;
175       }
176       
177       entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
178       while (1) {
179          if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
180             dev->dev_errno = EIO;
181             Dmsg2(129, "open_mounted_dev: failed to find suitable file in dir %s (dev=%s)\n", 
182                   dev->device->mount_point, dev->print_name());
183             break;
184          }
185          count++;
186       }
187       free(entry);
188       closedir(dp);
189       
190       Dmsg1(29, "open_mounted_dev: got %d files in the mount point\n", count);
191       
192       if (count > 2) {
193          mount = 1;                      /* If we got more than . and .. */
194          break;                          /*   there must be something mounted */
195       }
196 get_out:
197       dev->set_mounted(false);
198       sm_check(__FILE__, __LINE__, false);
199       free_pool_memory(results);
200       Dmsg0(200, "============ DVD mount=0\n");
201       return false;
202    }
203    
204    dev->set_mounted(mount);              /* set/clear mounted flag */
205    free_pool_memory(results);
206    /* Do not check free space when unmounting (otherwise it will mount it again) */
207    if (mount) {
208       update_free_space_dev(dev);
209    }
210    Dmsg1(200, "============ DVD mount=%d\n", mount);
211    return true;
212 }
213
214 /* Update the free space on the device */
215 void update_free_space_dev(DEVICE* dev) 
216 {
217    POOL_MEM ocmd(PM_FNAME);
218    POOLMEM* results;
219    char* icmd;
220    int timeout;
221    long long int free;
222    char ed1[50];
223    
224    /* The device must be mounted in order to dvd-freespace to work */
225    mount_dev(dev, 1);
226    
227    sm_check(__FILE__, __LINE__, false);
228    icmd = dev->device->free_space_command;
229    
230    if (!icmd) {
231       dev->free_space = 0;
232       dev->free_space_errno = 0;
233       dev->clear_freespace_ok();   /* No valid freespace */
234       dev->clear_media();
235       Dmsg2(29, "update_free_space_dev: free_space=%s, free_space_errno=%d (!icmd)\n", 
236             edit_uint64(dev->free_space, ed1), dev->free_space_errno);
237       return;
238    }
239    
240    edit_device_codes_dev(dev, ocmd, icmd);
241    
242    Dmsg1(29, "update_free_space_dev: cmd=%s\n", ocmd.c_str());
243
244    results = get_pool_memory(PM_MESSAGE);
245    
246    /* Try at most 3 times to get the free space on the device. This should perhaps be configurable. */
247    timeout = 3;
248    
249    while (1) {
250       if (run_program_full_output(ocmd.c_str(), dev->max_open_wait/2, results) == 0) {
251          Dmsg1(100, "Free space program run : %s\n", results);
252          free = str_to_int64(results);
253          if (free >= 0) {
254             dev->free_space = free;
255             dev->free_space_errno = 0;
256             dev->set_freespace_ok();     /* have valid freespace */
257             dev->set_media();
258             Mmsg0(dev->errmsg, "");
259             break;
260          }
261       }
262       dev->free_space = 0;
263       dev->free_space_errno = EPIPE;
264       dev->clear_freespace_ok();         /* no valid freespace */
265       Mmsg1(dev->errmsg, _("Cannot run free space command (%s)\n"), results);
266       
267       if (--timeout > 0) {
268          Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
269             "free_space_errno=%d ERR=%s\n", dev->print_name(), 
270                edit_uint64(dev->free_space, ed1), dev->free_space_errno, 
271                dev->errmsg);
272          bmicrosleep(1, 0);
273          continue;
274       }
275
276       dev->dev_errno = dev->free_space_errno;
277       Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
278          "free_space_errno=%d ERR=%s\n",
279             dev->print_name(), edit_uint64(dev->free_space, ed1),
280             dev->free_space_errno, dev->errmsg);
281       break;
282    }
283    
284    free_pool_memory(results);
285    Dmsg4(29, "update_free_space_dev: free_space=%s freespace_ok=%d free_space_errno=%d have_media=%d\n", 
286       edit_uint64(dev->free_space, ed1), !!dev->is_freespace_ok(), dev->free_space_errno, !!dev->have_media());
287    sm_check(__FILE__, __LINE__, false);
288    return;
289 }
290
291 /*
292  * Write a part (Vol, Vol.1, ...) from the spool to the DVD   
293  * This routine does not update the part number, so normally, you
294  *  should call open_next_part()
295  * It is also called from truncate_dvd_dev to "blank" the medium, as
296  *  well as from block.c when the DVD is full to write the last part.
297  */
298 bool dvd_write_part(DCR *dcr) 
299 {
300    DEVICE *dev = dcr->dev;
301    POOL_MEM ocmd(PM_FNAME);
302    char* icmd;
303    int status;
304    int timeout;
305    char ed1[50];
306    
307    sm_check(__FILE__, __LINE__, false);
308    Dmsg3(29, "dvd_write_part: device is %s, part is %d, is_mounted=%d\n", dev->print_name(), dev->part, dev->is_mounted());
309    icmd = dev->device->write_part_command;
310    
311    edit_device_codes_dev(dev, ocmd, icmd);
312       
313    /*
314     * Wait at most the time a maximum size part is written in DVD 0.5x speed
315     * FIXME: Minimum speed should be in device configuration 
316     */
317    timeout = dev->max_open_wait + (dev->max_part_size/(1350*1024/2));
318    
319    Dmsg2(29, "dvd_write_part: cmd=%s timeout=%d\n", ocmd.c_str(), timeout);
320       
321    {
322       POOL_MEM results(PM_MESSAGE);
323       sm_check(__FILE__, __LINE__, false);
324       status = run_program_full_output(ocmd.c_str(), timeout, results.c_str());
325       sm_check(__FILE__, __LINE__, false);
326       if (status != 0) {
327          Mmsg1(dev->errmsg, _("Error while writing current part to the DVD: %s"), 
328                results.c_str());
329          Dmsg1(000, "%s", dev->errmsg);
330          dev->dev_errno = EIO;
331          mark_volume_in_error(dcr);
332          return false;
333       }
334       sm_check(__FILE__, __LINE__, false);
335    }
336
337    {
338       POOL_MEM archive_name(PM_FNAME);
339       /* Delete spool file */
340       make_spooled_dvd_filename(dev, archive_name);
341       unlink(archive_name.c_str());
342       Dmsg1(29, "unlink(%s)\n", archive_name.c_str());
343       sm_check(__FILE__, __LINE__, false);
344    }
345    
346    /* growisofs umounted the device, so remount it (it will update the free space) */
347    dev->clear_mounted();
348    mount_dev(dev, 1);
349    Jmsg(dcr->jcr, M_INFO, 0, _("Remaining free space %s on %s\n"), 
350       edit_uint64_with_commas(dev->free_space, ed1), dev->print_name());
351    sm_check(__FILE__, __LINE__, false);
352    return true;
353 }
354
355 /* Open the next part file.
356  *  - Close the fd
357  *  - Increment part number 
358  *  - Reopen the device
359  */
360 int dvd_open_next_part(DCR *dcr)
361 {
362    DEVICE *dev = dcr->dev;
363
364    Dmsg6(29, "Enter: ==== open_next_part part=%d npart=%d dev=%s vol=%s mode=%d file_addr=%d\n", 
365       dev->part, dev->num_parts, dev->print_name(),
366          dev->VolCatInfo.VolCatName, dev->openmode, dev->file_addr);
367    if (!dev->is_dvd()) {
368       Dmsg1(000, "Device %s is not dvd!!!!\n", dev->print_name()); 
369       return -1;
370    }
371    
372    /* When appending, do not open a new part if the current is empty */
373    if (dev->can_append() && (dev->part >= dev->num_parts) && 
374        (dev->part_size == 0)) {
375       Dmsg0(29, "open_next_part exited immediately (dev->part_size == 0).\n");
376       return dev->fd;
377    }
378    
379    if (dev->fd >= 0) {
380       close(dev->fd);
381    }
382    
383    dev->fd = -1;
384    dev->clear_opened();
385    
386    /*
387     * If we have a part open for write, then write it to
388     *  DVD before opening the next part.
389     */
390    if (dev->is_dvd() && (dev->part >= dev->num_parts) && dev->can_append()) {
391       if (!dvd_write_part(dcr)) {
392          return -1;
393       }
394    }
395      
396    if (dev->part > dev->num_parts) {
397       Dmsg2(000, "In open_next_part: part=%d nump=%d\n", dev->part, dev->num_parts);
398       ASSERT(dev->part <= dev->num_parts);
399    }
400    dev->part_start += dev->part_size;
401    dev->part++;
402    
403    Dmsg2(29, "part=%d num_parts=%d\n", dev->part, dev->num_parts);
404    /* I think this dev->can_append() should not be there */
405    if ((dev->num_parts < dev->part) && dev->can_append()) {
406       POOL_MEM archive_name(PM_FNAME);
407       struct stat buf;
408       /* 
409        * First check what is on DVD.  If our part is there, we
410        *   are in trouble, so bail out.
411        * NB: This is however not a problem if we are writing the first part.
412        * It simply means that we are overriding an existing volume...
413        */
414       if (dev->num_parts > 0) {
415          make_mounted_dvd_filename(dev, archive_name);   /* makes dvd name */
416          if (stat(archive_name.c_str(), &buf) == 0) {
417             /* bad news bail out */
418             Mmsg1(&dev->errmsg, _("Next Volume part already exists on DVD. Cannot continue: %s\n"),
419                archive_name.c_str());
420             return -1;
421          }
422       }
423
424       Dmsg2(100, "Set npart=%d to part=%d\n", dev->num_parts, dev->part);
425       dev->num_parts = dev->part;
426       dev->VolCatInfo.VolCatParts = dev->part;
427       make_spooled_dvd_filename(dev, archive_name);   /* makes spool name */
428       
429       /* Check if the next part exists in spool directory . */
430       if ((stat(archive_name.c_str(), &buf) == 0) || (errno != ENOENT)) {
431          Dmsg1(29, "open_next_part %s is in the way, moving it away...\n", archive_name.c_str());
432          /* Then try to unlink it */
433          if (unlink(archive_name.c_str()) < 0) {
434             berrno be;
435             dev->dev_errno = errno;
436             Mmsg2(dev->errmsg, _("open_next_part can't unlink existing part %s, ERR=%s\n"), 
437                    archive_name.c_str(), be.strerror());
438             return -1;
439          }
440       }
441    }
442    if (dev->num_parts < dev->part) {
443       Dmsg2(100, "Set npart=%d to part=%d\n", dev->num_parts, dev->part);
444       dev->num_parts = dev->part;
445       dev->VolCatInfo.VolCatParts = dev->part;
446    }
447    Dmsg2(50, "Call dev->open(vol=%s, mode=%d\n", dev->VolCatInfo.VolCatName, 
448          dev->openmode);
449    /* Open next part */
450    
451    int append = dev->can_append();
452    if (dev->open(dcr, dev->openmode) < 0) {
453       return -1;
454    } 
455    dev->set_labeled();          /* all next parts are "labeled" */
456    if (append && (dev->part == dev->num_parts)) { /* If needed, set the append flag back */
457       dev->set_append();
458    }
459    
460    return dev->fd;
461 }
462
463 /* Open the first part file.
464  *  - Close the fd
465  *  - Reopen the device
466  */
467 int dvd_open_first_part(DCR *dcr, int mode)
468 {
469    DEVICE *dev = dcr->dev;
470
471    Dmsg4(29, "Enter: ==== open_first_part dev=%s Vol=%s mode=%d num_parts=%d\n", dev->print_name(), 
472          dev->VolCatInfo.VolCatName, dev->openmode, dev->num_parts);
473
474    if (dev->fd >= 0) {
475       close(dev->fd);
476    }
477    dev->fd = -1;
478    dev->clear_opened();
479    
480    dev->part_start = 0;
481    dev->part = 0;
482    
483    Dmsg2(50, "Call dev->open(vol=%s, mode=%d)\n", dcr->VolCatInfo.VolCatName, 
484          mode);
485    if (dev->open(dcr, mode) < 0) {
486       Dmsg0(50, "open dev() failed\n");
487       return -1;
488    }
489    Dmsg1(50, "Leave open_first_part state=%s\n", dev->is_open()?"open":"not open");
490    return dev->fd;
491 }
492
493
494 /* Protected version of lseek, which opens the right part if necessary */
495 off_t lseek_dev(DEVICE *dev, off_t offset, int whence)
496 {
497    DCR *dcr;
498    off_t pos;
499    char ed1[50], ed2[50];
500    
501    Dmsg3(100, "Enter lseek_dev fd=%d part=%d nparts=%d\n", dev->fd,
502       dev->part, dev->num_parts);
503    if (!dev->is_dvd()) { 
504       Dmsg0(100, "Using sys lseek\n");
505       return lseek(dev->fd, offset, whence);
506    }
507       
508    dcr = (DCR *)dev->attached_dcrs->first();  /* any dcr will do */
509    switch(whence) {
510    case SEEK_SET:
511       Dmsg2(100, "lseek_dev SEEK_SET to %s (part_start=%s)\n",
512          edit_uint64(offset, ed1), edit_uint64(dev->part_start, ed2));
513       if ((uint64_t)offset >= dev->part_start) {
514          if (((uint64_t)offset == dev->part_start) || ((uint64_t)offset < (dev->part_start+dev->part_size))) {
515             /* We are staying in the current part, just seek */
516             if ((pos = lseek(dev->fd, offset-dev->part_start, SEEK_SET)) < 0) {
517                return pos;
518             } else {
519                return pos + dev->part_start;
520             }
521          } else {
522             /* Load next part, and start again */
523             if (dvd_open_next_part(dcr) < 0) {
524                Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
525                return -1;
526             }
527             return lseek_dev(dev, offset, SEEK_SET);
528          }
529       } else {
530          /*
531           * pos < dev->part_start :
532           * We need to access a previous part, 
533           * so just load the first one, and seek again
534           * until the right one is loaded
535           */
536          if (dvd_open_first_part(dcr, dev->openmode) < 0) {
537             Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
538             return -1;
539          }
540          return lseek_dev(dev, offset, SEEK_SET);
541       }
542       break;
543    case SEEK_CUR:
544       Dmsg1(100, "lseek_dev SEEK_CUR to %s\n", edit_uint64(offset, ed1));
545       if ((pos = lseek(dev->fd, (off_t)0, SEEK_CUR)) < 0) {
546          return pos;   
547       }
548       pos += dev->part_start;
549       if (offset == 0) {
550          Dmsg1(100, "lseek_dev SEEK_CUR returns %s\n", edit_uint64(pos, ed1));
551          return pos;
552       } else { /* Not used in Bacula, but should work */
553          return lseek_dev(dev, pos, SEEK_SET);
554       }
555       break;
556    case SEEK_END:
557       Dmsg1(100, "lseek_dev SEEK_END to %s\n", edit_uint64(offset, ed1));
558       /*
559        * Bacula does not use offsets for SEEK_END
560        *  Also, Bacula uses seek_end only when it wants to
561        *  append to the volume, so for a dvd that means
562        *  that the volume must be spooled since the DVD
563        *  itself is read-only (as currently implemented).
564        */
565       if (offset > 0) { /* Not used by bacula */
566          Dmsg1(100, "lseek_dev SEEK_END called with an invalid offset %s\n", 
567             edit_uint64(offset, ed1));
568          errno = EINVAL;
569          return -1;
570       }
571       /* If we are already on a spooled part and have the
572        *  right part number, simply seek
573        */
574       if (dev->is_part_spooled() && dev->part == dev->num_parts) {
575          if ((pos = lseek(dev->fd, (off_t)0, SEEK_END)) < 0) {
576             return pos;   
577          } else {
578             Dmsg1(100, "lseek_dev SEEK_END returns %s\n", 
579                   edit_uint64(pos + dev->part_start, ed1));
580             return pos + dev->part_start;
581          }
582       } else {
583          /*
584           * Load the first part, then load the next until we reach the last one.
585           * This is the only way to be sure we compute the right file address.
586           *
587           * Save previous openmode, and open all but last part read-only 
588           * (useful for DVDs) 
589           */
590          int modesave = dev->openmode;
591          /* Works because num_parts > 0. */
592          if (dvd_open_first_part(dcr, OPEN_READ_ONLY) < 0) {
593             Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
594             return -1;
595          }
596          if (dev->num_parts > 0) {
597             while (dev->part < (dev->num_parts-1)) {
598                if (dvd_open_next_part(dcr) < 0) {
599                   Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
600                   return -1;
601                }
602             }
603             dev->openmode = modesave;
604             if (dvd_open_next_part(dcr) < 0) {
605                Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
606                return -1;
607             }
608          }
609          return lseek_dev(dev, 0, SEEK_END);
610       }
611       break;
612    default:
613       errno = EINVAL;
614       return -1;
615    }
616 }
617
618 bool dvd_close_job(DCR *dcr)
619 {
620    DEVICE *dev = dcr->dev;
621    JCR *jcr = dcr->jcr;
622    bool ok = true;
623
624    /* If the device is a dvd and WritePartAfterJob
625     * is set to yes, open the next part, so, in case of a device
626     * that requires mount, it will be written to the device.
627     */
628    if (dev->is_dvd() && jcr->write_part_after_job && (dev->part_size > 0)) {
629       Dmsg1(100, "Writing last part=%d write_partafter_job is set.\n",
630          dev->part);
631       if (dev->part < dev->num_parts) {
632          Jmsg3(jcr, M_FATAL, 0, _("Error while writing, current part number is less than the total number of parts (%d/%d, device=%s)\n"),
633                dev->part, dev->num_parts, dev->print_name());
634          dev->dev_errno = EIO;
635          ok = false;
636       }
637       
638       /* This should be !dvd_write_part(dcr)
639          NB: No! If you call dvd_write_part, the part number is not updated.
640          You must open the next part, it will automatically write the part and
641          update the part number. */
642       if (ok && (dvd_open_next_part(dcr) < 0)) {
643          Jmsg2(jcr, M_FATAL, 0, _("Unable to write part %s: ERR=%s\n"),
644                dev->print_name(), strerror_dev(dev));
645          dev->dev_errno = EIO;
646          ok = false;
647       }
648    }
649    Dmsg1(200, "Set VolCatParts=%d\n", dev->num_parts);
650    dev->VolCatInfo.VolCatParts = dev->num_parts;
651    return ok;
652 }
653
654 bool truncate_dvd_dev(DCR *dcr) {
655    DEVICE* dev = dcr->dev;
656
657    /* Set num_parts to zero (on disk) */
658    dev->num_parts = 0;
659    dcr->VolCatInfo.VolCatParts = 0;
660    dev->VolCatInfo.VolCatParts = 0;
661    
662    Dmsg0(100, "truncate_dvd_dev: Opening first part (1)...\n");
663    
664    dev->truncating = true;
665    if (dvd_open_first_part(dcr, OPEN_READ_WRITE) < 0) {
666       Dmsg0(100, "truncate_dvd_dev: Error while opening first part (1).\n");
667       dev->truncating = false;
668       return false;
669    }
670    dev->truncating = false;
671
672    Dmsg0(100, "truncate_dvd_dev: Truncating...\n");
673
674    /* If necessary, truncate it. */
675    if (ftruncate(dev->fd, 0) != 0) {
676       berrno be;
677       Mmsg2(dev->errmsg, _("Unable to truncate device %s. ERR=%s\n"), 
678          dev->print_name(), be.strerror());
679       return false;
680    }
681    
682    close(dev->fd);
683    dev->fd = -1;
684    dev->clear_opened();
685    
686    Dmsg0(100, "truncate_dvd_dev: Opening first part (2)...\n");
687    
688    if (!dvd_write_part(dcr)) {
689       Dmsg0(100, "truncate_dvd_dev: Error while writing to DVD.\n");
690       return false;
691    }
692    
693    if (dvd_open_first_part(dcr, OPEN_READ_WRITE) < 0) {
694       Dmsg0(100, "truncate_dvd_dev: Error while opening first part (2).\n");
695       return false;
696    }
697
698    return true;
699 }
700
701 /* Checks if we can write on a non-blank DVD: meaning that it just have been
702  * truncated (there is only one zero-sized file on the DVD, with the right
703  * volume name). */
704 bool check_can_write_on_non_blank_dvd(DCR *dcr) {
705    DEVICE* dev = dcr->dev;
706    DIR* dp;
707    struct dirent *entry, *result;
708    int name_max;
709    int count = 0;
710    int matched = 0; /* We found an empty file with the right name. */
711    struct stat filestat;
712       
713    name_max = pathconf(".", _PC_NAME_MAX);
714    if (name_max < 1024) {
715       name_max = 1024;
716    }
717          
718    if (!(dp = opendir(dev->device->mount_point))) {
719       berrno be;
720       dev->dev_errno = errno;
721       Dmsg3(29, "check_can_write_on_non_blank_dvd: failed to open dir %s (dev=%s), ERR=%s\n", 
722             dev->device->mount_point, dev->print_name(), be.strerror());
723       return false;
724    }
725    
726    entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
727    while (1) {
728       if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
729          dev->dev_errno = EIO;
730          Dmsg2(129, "check_can_write_on_non_blank_dvd: failed to find suitable file in dir %s (dev=%s)\n", 
731                dev->device->mount_point, dev->print_name());
732          break;
733       }
734       else {
735          Dmsg2(99, "check_can_write_on_non_blank_dvd: found %s (versus %s)\n", 
736                result->d_name, dev->VolCatInfo.VolCatName);
737          if (strcmp(result->d_name, dev->VolCatInfo.VolCatName) == 0) {
738             /* Found the file, checking it is empty */
739             POOL_MEM filename(PM_FNAME);
740             pm_strcpy(filename, dev->device->mount_point);
741             if (filename.c_str()[strlen(filename.c_str())-1] != '/') {
742                pm_strcat(filename, "/");
743             }
744             pm_strcat(filename, dev->VolCatInfo.VolCatName);
745             if (stat(filename.c_str(), &filestat) < 0) {
746                berrno be;
747                dev->dev_errno = errno;
748                Dmsg2(29, "check_can_write_on_non_blank_dvd: cannot stat file (file=%s), ERR=%s\n", 
749                   filename.c_str(), be.strerror());
750                return false;
751             }
752             Dmsg2(99, "check_can_write_on_non_blank_dvd: size of %s is %d\n", 
753                filename.c_str(), filestat.st_size);
754             matched = (filestat.st_size == 0);
755          }
756       }
757       count++;
758    }
759    free(entry);
760    closedir(dp);
761    
762    Dmsg2(29, "check_can_write_on_non_blank_dvd: got %d files in the mount point (matched=%d)\n", count, matched);
763    
764    if (count != 3) {
765       /* There is more than 3 files (., .., and the volume file) */
766       return false;
767    }
768    
769    return matched;
770 }
771
772 /*
773  * Edit codes into (Un)MountCommand, Write(First)PartCommand
774  *  %% = %
775  *  %a = archive device name
776  *  %e = erase (set if cannot mount and first part)
777  *  %n = part number
778  *  %m = mount point
779  *  %v = last part name
780  *
781  *  omsg = edited output message
782  *  imsg = input string containing edit codes (%x)
783  *
784  */
785 static void edit_device_codes_dev(DEVICE* dev, POOL_MEM &omsg, const char *imsg)
786 {
787    const char *p;
788    const char *str;
789    char add[20];
790    
791    POOL_MEM archive_name(PM_FNAME);
792
793    omsg.c_str()[0] = 0;
794    Dmsg1(800, "edit_device_codes: %s\n", imsg);
795    for (p=imsg; *p; p++) {
796       if (*p == '%') {
797          switch (*++p) {
798          case '%':
799             str = "%";
800             break;
801          case 'a':
802             str = dev->dev_name;
803             break;
804          case 'e':
805             if (dev->num_parts == 0) {
806                str = "1";
807             } else {
808                str = "0";
809             }
810             break;
811          case 'n':
812             bsnprintf(add, sizeof(add), "%d", dev->part);
813             str = add;
814             break;
815          case 'm':
816             str = dev->device->mount_point;
817             break;
818          case 'v':
819             make_spooled_dvd_filename(dev, archive_name);
820             str = archive_name.c_str();
821             break;
822          default:
823             add[0] = '%';
824             add[1] = *p;
825             add[2] = 0;
826             str = add;
827             break;
828          }
829       } else {
830          add[0] = *p;
831          add[1] = 0;
832          str = add;
833       }
834       Dmsg1(1900, "add_str %s\n", str);
835       pm_strcat(omsg, (char *)str);
836       Dmsg1(1800, "omsg=%s\n", omsg.c_str());
837    }
838 }