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