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