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