]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/dvd.c
- Mark DVD volume in error if part cannot be written.
[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_media();
234       Dmsg2(29, "update_free_space_dev: free_space=%d, free_space_errno=%d (!icmd)\n", dev->free_space, dev->free_space_errno);
235       return;
236    }
237    
238    edit_device_codes_dev(dev, ocmd, icmd);
239    
240    Dmsg1(29, "update_free_space_dev: cmd=%s\n", ocmd.c_str());
241
242    results = get_pool_memory(PM_MESSAGE);
243    
244    /* Try at most 3 times to get the free space on the device. This should perhaps be configurable. */
245    timeout = 3;
246    
247    while (1) {
248       if (run_program_full_output(ocmd.c_str(), dev->max_open_wait/2, results) == 0) {
249          Dmsg1(100, "Free space program run : %s\n", results);
250          free = str_to_int64(results);
251          if (free >= 0) {
252             dev->free_space = free;
253             dev->free_space_errno = 1;
254             dev->set_media();
255             Mmsg0(dev->errmsg, "");
256             break;
257          }
258       }
259       dev->free_space = 0;
260       dev->free_space_errno = -EPIPE;
261       Mmsg1(dev->errmsg, _("Cannot run free space command (%s)\n"), results);
262       
263       if (--timeout > 0) {
264          Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
265             "free_space_errno=%d ERR=%s\n", dev->print_name(), 
266                edit_uint64(dev->free_space, ed1), dev->free_space_errno, 
267                dev->errmsg);
268          bmicrosleep(1, 0);
269          continue;
270       }
271
272       dev->dev_errno = -dev->free_space_errno;
273       Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
274          "free_space_errno=%d ERR=%s\n",
275             dev->print_name(), edit_uint64(dev->free_space, ed1),
276             dev->free_space_errno, dev->errmsg);
277       break;
278    }
279    
280    free_pool_memory(results);
281    Dmsg3(29, "update_free_space_dev: free_space=%s, free_space_errno=%d have_media=%d\n", 
282       edit_uint64(dev->free_space, ed1), dev->free_space_errno, dev->have_media());
283    sm_check(__FILE__, __LINE__, false);
284    return;
285 }
286
287 /*
288  * Write a part (Vol, Vol.1, ...) from the spool to the DVD   
289  * This MUST only be called from open_next_part. Otherwise the part number
290  * is not updated.
291  * It is also called from truncate_dvd_dev to "blank" the medium.
292  */
293 static bool dvd_write_part(DCR *dcr) 
294 {
295    DEVICE *dev = dcr->dev;
296    POOL_MEM ocmd(PM_FNAME);
297    char* icmd;
298    int status;
299    int timeout;
300    char ed1[50];
301    
302    sm_check(__FILE__, __LINE__, false);
303    Dmsg3(29, "dvd_write_part: device is %s, part is %d, is_mounted=%d\n", dev->print_name(), dev->part, dev->is_mounted());
304    icmd = dev->device->write_part_command;
305    
306    edit_device_codes_dev(dev, ocmd, icmd);
307       
308    /*
309     * Wait at most the time a maximum size part is written in DVD 0.5x speed
310     * FIXME: Minimum speed should be in device configuration 
311     */
312    timeout = dev->max_open_wait + (dev->max_part_size/(1350*1024/2));
313    
314    Dmsg2(29, "dvd_write_part: cmd=%s timeout=%d\n", ocmd.c_str(), timeout);
315       
316    {
317       POOL_MEM results(PM_MESSAGE);
318       sm_check(__FILE__, __LINE__, false);
319       status = run_program_full_output(ocmd.c_str(), timeout, results.c_str());
320       sm_check(__FILE__, __LINE__, false);
321       if (status != 0) {
322          Mmsg1(dev->errmsg, _("Error while writing current part to the DVD: %s"), 
323                results.c_str());
324          Dmsg1(000, "%s", dev->errmsg);
325          dev->dev_errno = EIO;
326          mark_volume_in_error(dcr);
327          return false;
328       }
329       sm_check(__FILE__, __LINE__, false);
330    }
331
332    {
333       POOL_MEM archive_name(PM_FNAME);
334       /* Delete spool file */
335       make_spooled_dvd_filename(dev, archive_name);
336       unlink(archive_name.c_str());
337       Dmsg1(29, "unlink(%s)\n", archive_name.c_str());
338       sm_check(__FILE__, __LINE__, false);
339    }
340    
341    /* growisofs umount the device, so remount it (it will update the free space) */
342    dev->clear_mounted();
343    mount_dev(dev, 1);
344    Jmsg(dcr->jcr, M_INFO, 0, _("Remaining free space %s on %s\n"), 
345       edit_uint64_with_commas(dev->free_space, ed1), dev->print_name());
346    sm_check(__FILE__, __LINE__, false);
347    return true;
348 }
349
350 /* Open the next part file.
351  *  - Close the fd
352  *  - Increment part number 
353  *  - Reopen the device
354  */
355 int open_next_part(DCR *dcr)
356 {
357    DEVICE *dev = dcr->dev;
358
359    Dmsg6(29, "Enter: ==== open_next_part part=%d npart=%d dev=%s vol=%s mode=%d file_addr=%d\n", 
360       dev->part, dev->num_parts, dev->print_name(),
361          dev->VolCatInfo.VolCatName, dev->openmode, dev->file_addr);
362    if (!dev->is_dvd()) {
363       Dmsg1(000, "Device %s is not dvd!!!!\n", dev->print_name()); 
364       return -1;
365    }
366    
367    /* When appending, do not open a new part if the current is empty */
368    if (dev->can_append() && (dev->part >= dev->num_parts) && 
369        (dev->part_size == 0)) {
370       Dmsg0(29, "open_next_part exited immediately (dev->part_size == 0).\n");
371       return dev->fd;
372    }
373    
374    if (dev->fd >= 0) {
375       close(dev->fd);
376    }
377    
378    dev->fd = -1;
379    dev->clear_opened();
380    
381    /*
382     * If we have a part open for write, then write it to
383     *  DVD before opening the next part.
384     */
385    if (dev->is_dvd() && (dev->part >= dev->num_parts) && dev->can_append()) {
386       if (!dvd_write_part(dcr)) {
387          return -1;
388       }
389    }
390      
391    if (dev->part > dev->num_parts) {
392       Dmsg2(000, "In open_next_part: part=%d nump=%d\n", dev->part, dev->num_parts);
393       ASSERT(dev->part <= dev->num_parts);
394    }
395    dev->part_start += dev->part_size;
396    dev->part++;
397    
398    Dmsg2(29, "part=%d num_parts=%d\n", dev->part, dev->num_parts);
399    /* I think this dev->can_append() should not be there */
400    if ((dev->num_parts < dev->part) && dev->can_append()) {
401       POOL_MEM archive_name(PM_FNAME);
402       struct stat buf;
403       /* 
404        * First check what is on DVD.  If our part is there, we
405        *   are in trouble, so bail out.
406        * NB: This is however not a problem if we are writing the first part.
407        * It simply means that we are overriding an existing volume...
408        */
409       if (dev->num_parts > 0) {
410          make_mounted_dvd_filename(dev, archive_name);   /* makes dvd name */
411          if (stat(archive_name.c_str(), &buf) == 0) {
412             /* bad news bail out */
413             Mmsg1(&dev->errmsg, _("Next Volume part already exists on DVD. Cannot continue: %s\n"),
414                archive_name.c_str());
415             return -1;
416          }
417       }
418
419       Dmsg2(100, "Set npart=%d to part=%d\n", dev->num_parts, dev->part);
420       dev->num_parts = dev->part;
421       dev->VolCatInfo.VolCatParts = dev->part;
422       make_spooled_dvd_filename(dev, archive_name);   /* makes spool name */
423       
424       /* Check if the next part exists in spool directory . */
425       if ((stat(archive_name.c_str(), &buf) == 0) || (errno != ENOENT)) {
426          Dmsg1(29, "open_next_part %s is in the way, moving it away...\n", archive_name.c_str());
427          /* Then try to unlink it */
428          if (unlink(archive_name.c_str()) < 0) {
429             berrno be;
430             dev->dev_errno = errno;
431             Mmsg2(dev->errmsg, _("open_next_part can't unlink existing part %s, ERR=%s\n"), 
432                    archive_name.c_str(), be.strerror());
433             return -1;
434          }
435       }
436    }
437    if (dev->num_parts < dev->part) {
438       Dmsg2(100, "Set npart=%d to part=%d\n", dev->num_parts, dev->part);
439       dev->num_parts = dev->part;
440       dev->VolCatInfo.VolCatParts = dev->part;
441    }
442    Dmsg2(50, "Call dev->open(vol=%s, mode=%d\n", dev->VolCatInfo.VolCatName, 
443          dev->openmode);
444    /* Open next part */
445    
446    int append = dev->can_append();
447    if (dev->open(dcr, dev->openmode) < 0) {
448       return -1;
449    } 
450    dev->set_labeled();          /* all next parts are "labeled" */
451    if (append && (dev->part == dev->num_parts)) { /* If needed, set the append flag back */
452       dev->set_append();
453    }
454    
455    return dev->fd;
456 }
457
458 /* Open the first part file.
459  *  - Close the fd
460  *  - Reopen the device
461  */
462 int open_first_part(DCR *dcr, int mode)
463 {
464    DEVICE *dev = dcr->dev;
465
466    Dmsg4(29, "Enter: ==== open_first_part dev=%s Vol=%s mode=%d num_parts=%d\n", dev->print_name(), 
467          dev->VolCatInfo.VolCatName, dev->openmode, dev->num_parts);
468
469    if (dev->fd >= 0) {
470       close(dev->fd);
471    }
472    dev->fd = -1;
473    dev->clear_opened();
474    
475    dev->part_start = 0;
476    dev->part = 0;
477    
478    Dmsg2(50, "Call dev->open(vol=%s, mode=%d)\n", dcr->VolCatInfo.VolCatName, 
479          mode);
480    if (dev->open(dcr, mode) < 0) {
481       Dmsg0(50, "open dev() failed\n");
482       return -1;
483    }
484    Dmsg1(50, "Leave open_first_part state=%s\n", dev->is_open()?"open":"not open");
485    return dev->fd;
486 }
487
488
489 /* Protected version of lseek, which opens the right part if necessary */
490 off_t lseek_dev(DEVICE *dev, off_t offset, int whence)
491 {
492    DCR *dcr;
493    off_t pos;
494    
495    Dmsg3(100, "Enter lseek_dev fd=%d part=%d nparts=%d\n", dev->fd,
496       dev->part, dev->num_parts);
497    if (!dev->is_dvd()) { 
498       Dmsg0(100, "Using sys lseek\n");
499       return lseek(dev->fd, offset, whence);
500    }
501       
502    dcr = (DCR *)dev->attached_dcrs->first();  /* any dcr will do */
503    switch(whence) {
504    case SEEK_SET:
505       Dmsg2(100, "lseek_dev SEEK_SET to %d (part_start=%d)\n", (int)offset, (int)dev->part_start);
506       if ((uint64_t)offset >= dev->part_start) {
507          if (((uint64_t)offset == dev->part_start) || ((uint64_t)offset < (dev->part_start+dev->part_size))) {
508             /* We are staying in the current part, just seek */
509             if ((pos = lseek(dev->fd, offset-dev->part_start, SEEK_SET)) < 0) {
510                return pos;
511             } else {
512                return pos + dev->part_start;
513             }
514          } else {
515             /* Load next part, and start again */
516             if (open_next_part(dcr) < 0) {
517                Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
518                return -1;
519             }
520             return lseek_dev(dev, offset, SEEK_SET);
521          }
522       } else {
523          /*
524           * pos < dev->part_start :
525           * We need to access a previous part, 
526           * so just load the first one, and seek again
527           * until the right one is loaded
528           */
529          if (open_first_part(dcr, dev->openmode) < 0) {
530             Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
531             return -1;
532          }
533          return lseek_dev(dev, offset, SEEK_SET);
534       }
535       break;
536    case SEEK_CUR:
537       Dmsg1(100, "lseek_dev SEEK_CUR to %d\n", (int)offset);
538       if ((pos = lseek(dev->fd, (off_t)0, SEEK_CUR)) < 0) {
539          return pos;   
540       }
541       pos += dev->part_start;
542       if (offset == 0) {
543          Dmsg1(100, "lseek_dev SEEK_CUR returns %d\n", pos);
544          return pos;
545       } else { /* Not used in Bacula, but should work */
546          return lseek_dev(dev, pos, SEEK_SET);
547       }
548       break;
549    case SEEK_END:
550       Dmsg1(100, "lseek_dev SEEK_END to %d\n", (int)offset);
551       /*
552        * Bacula does not use offsets for SEEK_END
553        *  Also, Bacula uses seek_end only when it wants to
554        *  append to the volume, so for a dvd that means
555        *  that the volume must be spooled since the DVD
556        *  itself is read-only (as currently implemented).
557        */
558       if (offset > 0) { /* Not used by bacula */
559          Dmsg1(100, "lseek_dev SEEK_END called with an invalid offset %d\n", (int)offset);
560          errno = EINVAL;
561          return -1;
562       }
563       /* If we are already on a spooled part and have the
564        *  right part number, simply seek
565        */
566       if (dev->is_part_spooled() && dev->part == dev->num_parts) {
567          if ((pos = lseek(dev->fd, (off_t)0, SEEK_END)) < 0) {
568             return pos;   
569          } else {
570             Dmsg1(100, "lseek_dev SEEK_END returns %d\n", pos + dev->part_start);
571             return pos + dev->part_start;
572          }
573       } else {
574          /*
575           * Load the first part, then load the next until we reach the last one.
576           * This is the only way to be sure we compute the right file address.
577           *
578           * Save previous openmode, and open all but last part read-only 
579           * (useful for DVDs) 
580           */
581          int modesave = dev->openmode;
582          /* Works because num_parts > 0. */
583          if (open_first_part(dcr, OPEN_READ_ONLY) < 0) {
584             Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
585             return -1;
586          }
587          if (dev->num_parts > 0) {
588             while (dev->part < (dev->num_parts-1)) {
589                if (open_next_part(dcr) < 0) {
590                   Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
591                   return -1;
592                }
593             }
594             dev->openmode = modesave;
595             if (open_next_part(dcr) < 0) {
596                Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
597                return -1;
598             }
599          }
600          return lseek_dev(dev, 0, SEEK_END);
601       }
602       break;
603    default:
604       errno = EINVAL;
605       return -1;
606    }
607 }
608
609 bool dvd_close_job(DCR *dcr)
610 {
611    DEVICE *dev = dcr->dev;
612    JCR *jcr = dcr->jcr;
613    bool ok = true;
614
615    /* If the device is a dvd and WritePartAfterJob
616     * is set to yes, open the next part, so, in case of a device
617     * that requires mount, it will be written to the device.
618     */
619    if (dev->is_dvd() && jcr->write_part_after_job && (dev->part_size > 0)) {
620       Dmsg1(100, "Writing last part=%d write_partafter_job is set.\n",
621          dev->part);
622       if (dev->part < dev->num_parts) {
623          Jmsg3(jcr, M_FATAL, 0, _("Error while writing, current part number is less than the total number of parts (%d/%d, device=%s)\n"),
624                dev->part, dev->num_parts, dev->print_name());
625          dev->dev_errno = EIO;
626          ok = false;
627       }
628       
629       /* This should be !dvd_write_part(dcr)
630          NB: No! If you call dvd_write_part, the part number is not updated.
631          You must open the next part, it will automatically write the part and
632          update the part number. */
633       if (ok && (open_next_part(dcr) < 0)) {
634          Jmsg2(jcr, M_FATAL, 0, _("Unable to write part %s: ERR=%s\n"),
635                dev->print_name(), strerror_dev(dev));
636          dev->dev_errno = EIO;
637          ok = false;
638       }
639    }
640    Dmsg1(200, "Set VolCatParts=%d\n", dev->num_parts);
641    dev->VolCatInfo.VolCatParts = dev->num_parts;
642    return ok;
643 }
644
645 bool truncate_dvd_dev(DCR *dcr) {
646    DEVICE* dev = dcr->dev;
647
648    /* Set num_parts to zero (on disk) */
649    dev->num_parts = 0;
650    dcr->VolCatInfo.VolCatParts = 0;
651    dev->VolCatInfo.VolCatParts = 0;
652    
653    Dmsg0(100, "truncate_dvd_dev: Opening first part (1)...\n");
654    
655    dev->truncating = true;
656    if (open_first_part(dcr, OPEN_READ_WRITE) < 0) {
657       Dmsg0(100, "truncate_dvd_dev: Error while opening first part (1).\n");
658       dev->truncating = false;
659       return false;
660    }
661    dev->truncating = false;
662
663    Dmsg0(100, "truncate_dvd_dev: Truncating...\n");
664
665    /* If necessary, truncate it. */
666    if (ftruncate(dev->fd, 0) != 0) {
667       berrno be;
668       Mmsg2(dev->errmsg, _("Unable to truncate device %s. ERR=%s\n"), 
669          dev->print_name(), be.strerror());
670       return false;
671    }
672    
673    close(dev->fd);
674    dev->fd = -1;
675    dev->clear_opened();
676    
677    Dmsg0(100, "truncate_dvd_dev: Opening first part (2)...\n");
678    
679    if (!dvd_write_part(dcr)) {
680       Dmsg0(100, "truncate_dvd_dev: Error while writing to DVD.\n");
681       return false;
682    }
683    
684    if (open_first_part(dcr, OPEN_READ_WRITE) < 0) {
685       Dmsg0(100, "truncate_dvd_dev: Error while opening first part (2).\n");
686       return false;
687    }
688
689    return true;
690 }
691
692 /* Checks if we can write on a non-blank DVD: meaning that it just have been
693  * truncated (there is only one zero-sized file on the DVD, with the right
694  * volume name). */
695 bool check_can_write_on_non_blank_dvd(DCR *dcr) {
696    DEVICE* dev = dcr->dev;
697    DIR* dp;
698    struct dirent *entry, *result;
699    int name_max;
700    int count = 0;
701    int matched = 0; /* We found an empty file with the right name. */
702    struct stat filestat;
703       
704    name_max = pathconf(".", _PC_NAME_MAX);
705    if (name_max < 1024) {
706       name_max = 1024;
707    }
708          
709    if (!(dp = opendir(dev->device->mount_point))) {
710       berrno be;
711       dev->dev_errno = errno;
712       Dmsg3(29, "check_can_write_on_non_blank_dvd: failed to open dir %s (dev=%s), ERR=%s\n", 
713             dev->device->mount_point, dev->print_name(), be.strerror());
714       return false;
715    }
716    
717    entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
718    while (1) {
719       if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
720          dev->dev_errno = EIO;
721          Dmsg2(129, "check_can_write_on_non_blank_dvd: failed to find suitable file in dir %s (dev=%s)\n", 
722                dev->device->mount_point, dev->print_name());
723          break;
724       }
725       else {
726          Dmsg2(99, "check_can_write_on_non_blank_dvd: found %s (versus %s)\n", 
727                result->d_name, dev->VolCatInfo.VolCatName);
728          if (strcmp(result->d_name, dev->VolCatInfo.VolCatName) == 0) {
729             /* Found the file, checking it is empty */
730             POOL_MEM filename(PM_FNAME);
731             pm_strcpy(filename, dev->device->mount_point);
732             if (filename.c_str()[strlen(filename.c_str())-1] != '/') {
733                pm_strcat(filename, "/");
734             }
735             pm_strcat(filename, dev->VolCatInfo.VolCatName);
736             if (stat(filename.c_str(), &filestat) < 0) {
737                berrno be;
738                dev->dev_errno = errno;
739                Dmsg2(29, "check_can_write_on_non_blank_dvd: cannot stat file (file=%s), ERR=%s\n", 
740                   filename.c_str(), be.strerror());
741                return false;
742             }
743             Dmsg2(99, "check_can_write_on_non_blank_dvd: size of %s is %d\n", 
744                filename.c_str(), filestat.st_size);
745             matched = (filestat.st_size == 0);
746          }
747       }
748       count++;
749    }
750    free(entry);
751    closedir(dp);
752    
753    Dmsg2(29, "check_can_write_on_non_blank_dvd: got %d files in the mount point (matched=%d)\n", count, matched);
754    
755    if (count != 3) {
756       /* There is more than 3 files (., .., and the volume file) */
757       return false;
758    }
759    
760    return matched;
761 }
762
763 /*
764  * Edit codes into (Un)MountCommand, Write(First)PartCommand
765  *  %% = %
766  *  %a = archive device name
767  *  %e = erase (set if cannot mount and first part)
768  *  %m = mount point
769  *  %v = last part name
770  *
771  *  omsg = edited output message
772  *  imsg = input string containing edit codes (%x)
773  *
774  */
775 static void edit_device_codes_dev(DEVICE* dev, POOL_MEM &omsg, const char *imsg)
776 {
777    const char *p;
778    const char *str;
779    char add[20];
780    
781    POOL_MEM archive_name(PM_FNAME);
782
783    omsg.c_str()[0] = 0;
784    Dmsg1(800, "edit_device_codes: %s\n", imsg);
785    for (p=imsg; *p; p++) {
786       if (*p == '%') {
787          switch (*++p) {
788          case '%':
789             str = "%";
790             break;
791          case 'a':
792             str = dev->dev_name;
793             break;
794          case 'e':
795             if (dev->part == 0) {
796                str = "1";
797             } else {
798                str = "0";
799             }
800             break;
801          case 'n':
802             bsnprintf(add, sizeof(add), "%d", dev->part);
803             str = add;
804             break;
805          case 'm':
806             str = dev->device->mount_point;
807             break;
808          case 'v':
809             make_spooled_dvd_filename(dev, archive_name);
810             str = archive_name.c_str();
811             break;
812          default:
813             add[0] = '%';
814             add[1] = *p;
815             add[2] = 0;
816             str = add;
817             break;
818          }
819       } else {
820          add[0] = *p;
821          add[1] = 0;
822          str = add;
823       }
824       Dmsg1(1900, "add_str %s\n", str);
825       pm_strcat(omsg, (char *)str);
826       Dmsg1(1800, "omsg=%s\n", omsg.c_str());
827    }
828 }