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