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