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