]> git.sur5r.net Git - bacula/bacula/commitdiff
- Add some changes submitted by a user for HP client build.
authorKern Sibbald <kern@sibbald.com>
Tue, 22 Feb 2005 07:52:16 +0000 (07:52 +0000)
committerKern Sibbald <kern@sibbald.com>
Tue, 22 Feb 2005 07:52:16 +0000 (07:52 +0000)
  Not all changes accepted.
- Rework code in filed/backup.c to ease #ifdefing and make
  program flow more obvious.
- Split DVD code out of dev.c into dvd.c

git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@1838 91ce42f0-d328-0410-95d8-f526ca767f89

bacula/autoconf/configure.in
bacula/configure
bacula/kernstodo
bacula/src/console/conio.c
bacula/src/filed/backup.c
bacula/src/lib/bsys.c
bacula/src/stored/Makefile.in
bacula/src/stored/askdir.c
bacula/src/stored/dev.c
bacula/src/stored/dvd.c [new file with mode: 0644]
bacula/src/version.h

index cd22084c4ae17d4283668fb103e91e3877295f4d..846afa936d550ab9bb27e91fdffd7b3e5f0e0537 100644 (file)
@@ -1766,6 +1766,7 @@ hpux)
        CFLAGS="$(CFLAGS) -D_XOPEN_SOURCE_EXTENDED=1"
        DISTVER=`uname -r`
        TAPEDRIVE="/dev/rmt/0hnb"
+       PTHREAD_LIB="-lpthread"
   ;;
 irix)
        DISTVER=`uname -r`
index 1c1693a48ee4a711159a20b7ea3bd5f8eb78e7c9..df29d69ca6fa0c979ff52815c95fab74917efdea 100755 (executable)
@@ -21323,6 +21323,7 @@ hpux)
        CFLAGS="$(CFLAGS) -D_XOPEN_SOURCE_EXTENDED=1"
        DISTVER=`uname -r`
        TAPEDRIVE="/dev/rmt/0hnb"
+       PTHREAD_LIB="-lpthread"
   ;;
 irix)
        DISTVER=`uname -r`
index 229c8bc80a5895b2c2dd2567bbbec7856d8e77ef..2348d10e62430827f2246f458b4f6b361bb11940 100644 (file)
@@ -30,6 +30,8 @@ Suggestions for Preben:
 - Optimized bootstrap.
 
 For 1.37:
+- Implement "update device" from SD so that DIR will
+  always have current version of device.
 - Add disk seeking on restore.  
 - Add Python writable variable for changing the Priority,
     Client, Storage, JobStatus (error), ...
@@ -1218,4 +1220,3 @@ Block Position: 0
 - Try to open a device on each Job if it was not opened
   when the SD started.
 - Add dump of VolSessionId/Time and FileIndex with bls.
-
index 19f10bde3fda4567e31709a1439e2f57d1f84c84..94ffeaea1c820688f1e09a3c3536a67baa0c8407 100755 (executable)
@@ -59,6 +59,8 @@ extern "C" int tgetent(void *, const char *);
 extern "C" int tgetnum(const char *);
 extern "C" char *tgetstr (const char*, char**);
 extern "C" char *tgoto (const char *, int, int);
+#elif HAVE_HPUX_OS
+#include <term.h>
 #else
 #include <termcap.h>
 #endif
@@ -66,8 +68,13 @@ extern "C" char *tgoto (const char *, int, int);
 
 
 /* From termios library */
+#ifdef HAVE_HPUX_OS
+static char *BC;
+static char *UP;
+#else
 extern char *BC;
 extern char *UP;
+#endif
 
 /* Forward referenced functions */
 extern "C" {
@@ -362,7 +369,7 @@ static void dump_stab()
          for (j=0; j<tstab->len; j++) {
             c = tstab->str[j];
             if (c < 0x20 || c > 0x7F) {
-               sprintf(buf, " 0x%x ", c);
+                sprintf(buf, " 0x%x ", c);
                t_send(buf);
             } else {
                buf[0] = c;
@@ -370,7 +377,7 @@ static void dump_stab()
                t_sendl(buf, 1);
             }
          }
-         sprintf(buf, " func=%d len=%d\n\r", tstab->func, tstab->len);
+          sprintf(buf, " func=%d len=%d\n\r", tstab->func, tstab->len);
          t_send(buf);
        }
     }
@@ -460,7 +467,7 @@ input_line(char *string, int length)
        }
        switch (c=input_char()) {
        case F_RETURN:               /* CR */
-          t_sendl("\r\n", 2);       /* yes, print it and */
+           t_sendl("\r\n", 2);       /* yes, print it and */
           goto done;                /* get out */
        case F_CLRSCRN:              /* clear screen */
          asclrs();
@@ -496,7 +503,7 @@ input_line(char *string, int length)
           backup(curline);
           delchr(1, curline, sizeof(curline));
           if (cp == 0) {
-             t_char(' ');
+              t_char(' ');
              t_char(0x8);
           }
           break;
@@ -534,7 +541,7 @@ input_line(char *string, int length)
           t_clrline(0, t_width);     /* erase line */
           cp = 0;
           cl = 0;                   /* reset cursor counter */
-          t_char(' ');
+           t_char(' ');
           t_char(0x8);
           break;
        case F_SOL:
@@ -873,7 +880,7 @@ dump(struct lstr *ptr, char *msg)
     printf("%s buf=%x nextl=%x prevl=%x len=%d used=%d\n",
        msg,ptr,ptr->nextl,ptr->prevl,ptr->len,ptr->used);
     if (ptr->used)
-       printf("line=%s\n",&ptr->line);
+        printf("line=%s\n",&ptr->line);
 }
 #endif /* DEBUGOUT */
 
index de5de2e02ec457197ed0c617b69ee4f80fc03184..545ae8e3a65d889793afd264432c9d6dfc5f9a85 100644 (file)
 #include "bacula.h"
 #include "filed.h"
 
+/* Forward referenced functions */
 static int save_file(FF_PKT *ff_pkt, void *pkt);
 static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, struct CHKSUM *chksum);
-#ifdef HAVE_ACL
-static int read_and_send_acl(JCR *jcr, int acltype, int stream);
-#endif
+static bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream);
+static bool read_and_send_acl(JCR *jcr, int acltype, int stream);
 
 /*
  * Find all the requested files and send them
@@ -129,9 +129,7 @@ bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
  */
 static int save_file(FF_PKT *ff_pkt, void *vjcr)
 {
-   char attribs[MAXSTRING];
-   char attribsEx[MAXSTRING];
-   int stat, attr_stream, data_stream;
+   int stat, data_stream;
    struct CHKSUM chksum;
    BSOCK *sd;
    JCR *jcr = (JCR *)vjcr;
@@ -227,65 +225,9 @@ static int save_file(FF_PKT *ff_pkt, void *vjcr)
 
    Dmsg1(130, "bfiled: sending %s to stored\n", ff_pkt->fname);
 
-   /* Find what data stream we will use, then encode the attributes */
-   data_stream = select_data_stream(ff_pkt);
-   encode_stat(attribs, ff_pkt, data_stream);
-
-   /* Now possibly extend the attributes */
-   attr_stream = encode_attribsEx(jcr, attribsEx, ff_pkt);
-
-   Dmsg3(300, "File %s\nattribs=%s\nattribsEx=%s\n", ff_pkt->fname, attribs, attribsEx);
-
-   P(jcr->mutex);
-   jcr->JobFiles++;                   /* increment number of files sent */
-   ff_pkt->FileIndex = jcr->JobFiles;  /* return FileIndex */
-   pm_strcpy(jcr->last_fname, ff_pkt->fname);
-   V(jcr->mutex);
-
-   /*
-    * Send Attributes header to Storage daemon
-    *   <file-index> <stream> <info>
-    */
-   if (!bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, attr_stream)) {
-      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-           bnet_strerror(sd));
-      return 0;
-   }
-   Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
-
-   /*
-    * Send file attributes to Storage daemon
-    *  File_index
-    *  File type
-    *  Filename (full path)
-    *  Encoded attributes
-    *  Link name (if type==FT_LNK or FT_LNKSAVED)
-    *  Encoded extended-attributes (for Win32)
-    *
-    * For a directory, link is the same as fname, but with trailing
-    * slash. For a linked file, link is the link.
-    */
-   if (ff_pkt->type == FT_LNK || ff_pkt->type == FT_LNKSAVED) {
-      Dmsg2(300, "Link %s to %s\n", ff_pkt->fname, ff_pkt->link);
-      stat = bnet_fsend(sd, "%ld %d %s%c%s%c%s%c%s%c", jcr->JobFiles,
-              ff_pkt->type, ff_pkt->fname, 0, attribs, 0, ff_pkt->link, 0,
-              attribsEx, 0);
-   } else if (ff_pkt->type == FT_DIREND) {
-      /* Here link is the canonical filename (i.e. with trailing slash) */
-      stat = bnet_fsend(sd, "%ld %d %s%c%s%c%c%s%c", jcr->JobFiles,
-              ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0, attribsEx, 0);
-   } else {
-      stat = bnet_fsend(sd, "%ld %d %s%c%s%c%c%s%c", jcr->JobFiles,
-              ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, attribsEx, 0);
-   }
-
-   Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
-   if (!stat) {
-      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
-           bnet_strerror(sd));
+   if (!encode_and_send_attributes(jcr, ff_pkt, data_stream)) {
       return 0;
    }
-   bnet_sig(sd, BNET_EOD);           /* indicate end of attributes data */
 
    /*
     * Setup for signature handling.
@@ -378,7 +320,6 @@ static int save_file(FF_PKT *ff_pkt, void *vjcr)
    }
 #endif
 
-#ifdef HAVE_ACL
    if (ff_pkt->flags & FO_ACL) {
       /* Read access ACLs for files, dirs and links */
       if (!read_and_send_acl(jcr, BACL_TYPE_ACCESS, STREAM_UNIX_ATTRIBUTES_ACCESS_ACL)) {
@@ -391,7 +332,6 @@ static int save_file(FF_PKT *ff_pkt, void *vjcr)
         }
       }
    }
-#endif
 
    /* Terminate any signature and send it to Storage daemon and the Director */
    if (chksum.updated) {
@@ -571,12 +511,12 @@ int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, struct CHKSUM *chksum)
    return 1;
 }
 
-#ifdef HAVE_ACL
 /*
  * Read and send an ACL for the last encountered file.
  */
-static int read_and_send_acl(JCR *jcr, int acltype, int stream)
+static bool read_and_send_acl(JCR *jcr, int acltype, int stream)
 {
+#ifdef HAVE_ACL
    BSOCK *sd = jcr->store_bsock;
    POOLMEM *msgsave;
    int len;
@@ -584,17 +524,17 @@ static int read_and_send_acl(JCR *jcr, int acltype, int stream)
    len = bacl_get(jcr, acltype);
    if (len < 0) {
       Jmsg1(jcr, M_WARNING, 0, "Error reading ACL of %s\n", jcr->last_fname);
-      return 1;
+      return true; 
    }
    if (len == 0) {
-      return 1;                      /* no ACL */
+      return true;                   /* no ACL */
    }
 
    /* Send header */
    if (!bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, stream)) {
       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
            bnet_strerror(sd));
-      return 0;
+      return false;
    }
 
    /* Send the buffer to the storage deamon */
@@ -607,7 +547,7 @@ static int read_and_send_acl(JCR *jcr, int acltype, int stream)
       sd->msglen = 0;
       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
            bnet_strerror(sd));
-      return 0;
+      return false;
    }
 
    jcr->JobBytes += sd->msglen;
@@ -615,10 +555,83 @@ static int read_and_send_acl(JCR *jcr, int acltype, int stream)
    if (!bnet_sig(sd, BNET_EOD)) {
       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
            bnet_strerror(sd));
-      return 0;
+      return false;
    }
 
    Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname);
-   return 1;
+#endif
+   return true;
 }
+
+static bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) 
+{
+   BSOCK *sd = jcr->store_bsock;
+   char attribs[MAXSTRING];
+   char attribsEx[MAXSTRING];
+   int attr_stream;
+   int stat;
+#ifdef FD_NO_SEND_TEST
+   return true;
 #endif
+
+   /* Find what data stream we will use, then encode the attributes */
+   data_stream = select_data_stream(ff_pkt);
+   encode_stat(attribs, ff_pkt, data_stream);
+
+   /* Now possibly extend the attributes */
+   attr_stream = encode_attribsEx(jcr, attribsEx, ff_pkt);
+
+   Dmsg3(300, "File %s\nattribs=%s\nattribsEx=%s\n", ff_pkt->fname, attribs, attribsEx);
+
+   P(jcr->mutex);
+   jcr->JobFiles++;                   /* increment number of files sent */
+   ff_pkt->FileIndex = jcr->JobFiles;  /* return FileIndex */
+   pm_strcpy(jcr->last_fname, ff_pkt->fname);
+   V(jcr->mutex);
+
+   /*
+    * Send Attributes header to Storage daemon
+    *   <file-index> <stream> <info>
+    */
+   if (!bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, attr_stream)) {
+      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
+           bnet_strerror(sd));
+      return false;
+   }
+   Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
+
+   /*
+    * Send file attributes to Storage daemon
+    *  File_index
+    *  File type
+    *  Filename (full path)
+    *  Encoded attributes
+    *  Link name (if type==FT_LNK or FT_LNKSAVED)
+    *  Encoded extended-attributes (for Win32)
+    *
+    * For a directory, link is the same as fname, but with trailing
+    * slash. For a linked file, link is the link.
+    */
+   if (ff_pkt->type == FT_LNK || ff_pkt->type == FT_LNKSAVED) {
+      Dmsg2(300, "Link %s to %s\n", ff_pkt->fname, ff_pkt->link);
+      stat = bnet_fsend(sd, "%ld %d %s%c%s%c%s%c%s%c", jcr->JobFiles,
+              ff_pkt->type, ff_pkt->fname, 0, attribs, 0, ff_pkt->link, 0,
+              attribsEx, 0);
+   } else if (ff_pkt->type == FT_DIREND) {
+      /* Here link is the canonical filename (i.e. with trailing slash) */
+      stat = bnet_fsend(sd, "%ld %d %s%c%s%c%c%s%c", jcr->JobFiles,
+              ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0, attribsEx, 0);
+   } else {
+      stat = bnet_fsend(sd, "%ld %d %s%c%s%c%c%s%c", jcr->JobFiles,
+              ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, attribsEx, 0);
+   }
+
+   Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
+   if (!stat) {
+      Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
+           bnet_strerror(sd));
+      return false;
+   }
+   bnet_sig(sd, BNET_EOD);           /* indicate end of attributes data */
+   return true;
+}
index ba14452c0e0ae7c2fbe4bd5cf1e176470dd014a2..a66914b08511dd247708c73e8eff8d4deec95722 100644 (file)
@@ -8,7 +8,7 @@
  *   Version $Id$
  */
 /*
-   Copyright (C) 2000-2004 Kern Sibbald
+   Copyright (C) 2000-2005 Kern Sibbald
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
index 782721c1f8ac54a73e7772a2249f297f8c417668..bb3165c2dab0d1737931fbe537a104a9106403f1 100644 (file)
@@ -22,7 +22,7 @@ SVRSRCS = stored.c ansi_label.c \
          autochanger.c acquire.c append.c \
          askdir.c authenticate.c \
          block.c butil.c dev.c \
-         device.c dircmd.c fd_cmds.c job.c \
+         device.c dircmd.c dvd.c fd_cmds.c job.c \
          label.c match_bsr.c mount.c parse_bsr.c \
          python.c \
          read.c read_record.c record.c \
@@ -31,7 +31,7 @@ SVROBJS = stored.o ansi_label.o \
          autochanger.o acquire.o append.o \
          askdir.o authenticate.o \
          block.o butil.o dev.o \
-         device.o dircmd.o fd_cmds.o job.o \
+         device.o dircmd.o dvd.o fd_cmds.o job.o \
          label.o match_bsr.o mount.o parse_bsr.o \
          python.o \
          read.o read_record.o record.o \
@@ -39,35 +39,35 @@ SVROBJS = stored.o ansi_label.o \
 
 # btape
 TAPESRCS = btape.c block.c butil.c dev.c device.c label.c \
-          ansi_label.c \
+          ansi_label.c dvd.c \
           acquire.c mount.c record.c read_record.c \
           stored_conf.c match_bsr.c parse_bsr.c spool.c
 TAPEOBJS = btape.o block.o butil.o dev.o device.o label.o \
-          ansi_label.o \
+          ansi_label.o dvd.o \
           autochanger.o acquire.o mount.o record.o read_record.o \
           stored_conf.o match_bsr.o parse_bsr.o spool.o
 
 # bls
 BLSOBJS = bls.o block.o butil.o device.o dev.o label.o match_bsr.o \
-         ansi_label.o \
+         ansi_label.o dvd.o \
          autochanger.o acquire.o mount.o parse_bsr.o record.o  \
          read_record.o stored_conf.o spool.o
 
 # bextract
 BEXTOBJS = bextract.o block.o device.o dev.o label.o record.o \
-          ansi_label.o \
+          ansi_label.o dvd.o \
           autochanger.o acquire.o mount.o match_bsr.o parse_bsr.o butil.o \
           read_record.o stored_conf.o spool.o
 
 # bscan
 SCNOBJS = bscan.o block.o device.o dev.o label.o \
-         ansi_label.o \
+         ansi_label.o dvd.o \
          autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \
          butil.o read_record.o stored_conf.o spool.o
 
 # bcopy
 COPYOBJS = bcopy.o block.o device.o dev.o label.o \
-          ansi_label.o \
+          ansi_label.o dvd.o \
           autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \
           butil.o read_record.o stored_conf.o spool.o
 
index 7706e0458acaccb93f21b6bd6526b34a637e386f..ac170c3bfa31a6c83fcf6504b771f84690a2ea44 100644 (file)
@@ -307,6 +307,10 @@ bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
    BSOCK *dir = jcr->dir_bsock;
    ser_declare;
 
+#ifdef NO_ATTRIBUTES_TEST
+   return true;
+#endif
+
    dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
    dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
                sizeof(DEV_RECORD) + rec->data_len);
index 339007acdbd398cf2c16b85d02554c14d668dbff..326f09e991e5e96370e6c3ee5ec6954a1020cc4e 100644 (file)
 #include "bacula.h"
 #include "stored.h"
 
+/* Functions in dvd.c */ 
+void get_filename(DEVICE *dev, char *VolName, POOL_MEM& archive_name);
+int mount_dev(DEVICE* dev, int timeout);
+int unmount_dev(DEVICE *dev, int timeout);
+void update_free_space_dev(DEVICE* dev);
+
+
 /* Forward referenced functions */
 void set_os_device_parameters(DEVICE *dev);
-int mount_dev(DEVICE* dev, int timeout);
-int unmount_dev(DEVICE* dev, int timeout);
-int write_part(DEVICE *dev);
-char *edit_device_codes_dev(DEVICE* dev, char *omsg, const char *imsg);
-static void get_filename(DEVICE *dev, char *VolName, POOL_MEM& archive_name);
-static void update_free_space_dev(DEVICE* dev);
+static bool dev_get_os_pos(DEVICE *dev, struct mtget *mt_stat);
 
 /*
  * Allocate and initialize the DEVICE structure
@@ -252,42 +254,6 @@ init_dev(JCR *jcr, DEVICE *dev, DEVRES *device)
    return dev;
 }
 
-/* 
- * Write the current volume/part filename to archive_name.
- */
-static void get_filename(DEVICE *dev, char *VolName, POOL_MEM& archive_name) 
-{
-   char partnumber[20];
-   
-   if (dev->is_dvd()) {
-        /* If we try to open the last part, just open it from disk, 
-        * otherwise, open it from the spooling directory */
-      if (dev->part < dev->num_parts) {
-        pm_strcpy(archive_name, dev->device->mount_point);
-      } else {
-        /* Use the working directory if spool directory is not defined */
-        if (dev->device->spool_directory) {
-           pm_strcpy(archive_name, dev->device->spool_directory);
-        } else {
-           pm_strcpy(archive_name, working_directory);
-        }
-      }
-   } else {
-      pm_strcpy(archive_name, dev->dev_name);
-   }
-      
-   if (archive_name.c_str()[strlen(archive_name.c_str())-1] != '/') {
-      pm_strcat(archive_name, "/");
-   }
-   pm_strcat(archive_name, VolName);
-   /* if part != 0, append .# to the filename (where # is the part number) */
-   if (dev->is_dvd() && dev->part != 0) {
-      pm_strcat(archive_name, ".");
-      bsnprintf(partnumber, sizeof(partnumber), "%d", dev->part);
-      pm_strcat(archive_name, partnumber);
-   }
-}  
-
 /*
  * Open the device with the operating system and
  * initialize buffer pointers.
@@ -406,14 +372,12 @@ open_dev(DEVICE *dev, char *VolName, int mode)
       }
       get_filename(dev, VolName, archive_name);
 
-      if (dev->is_dvd()) {
-        if (mount_dev(dev, 1) < 0) {
-            Mmsg(dev->errmsg, _("Could not mount archive device %s.\n"),
-                dev->dev_name);
-           Emsg0(M_FATAL, 0, dev->errmsg);
-           dev->fd = -1;
-           return dev->fd;
-        }
+      if (mount_dev(dev, 1) < 0) {
+         Mmsg(dev->errmsg, _("Could not mount archive device %s.\n"),
+             dev->dev_name);
+        Emsg0(M_FATAL, 0, dev->errmsg);
+        dev->fd = -1;
+        return dev->fd;
       }
            
       Dmsg2(29, "open_dev: device is disk %s (mode:%d)\n", archive_name.c_str(), mode);
@@ -465,562 +429,6 @@ open_dev(DEVICE *dev, char *VolName, int mode)
    return dev->fd;
 }
 
-/* (Un)mount the device */
-int do_mount_dev(DEVICE* dev, int mount, int dotimeout) {
-   POOL_MEM ocmd(PM_FNAME);
-   POOLMEM* results;
-   results = get_pool_memory(PM_MESSAGE);
-   char* icmd;
-   int status, timeout;
-   
-   if (mount) {
-      icmd = dev->device->mount_command;
-   }
-   else {
-      icmd = dev->device->unmount_command;
-   }
-   
-   edit_device_codes_dev(dev, ocmd.c_str(), icmd);
-   
-   Dmsg2(29, "do_mount_dev: cmd=%s state=%d\n", ocmd.c_str(), dev->state & ST_MOUNTED);
-
-   if (dotimeout) {
-      /* Try at most 5 times to (un)mount the device. This should perhaps be configurable. */
-      timeout = 5;
-   }
-   else {
-      timeout = 0;
-   }
-   /* If busy retry each second */
-   while ((status = run_program_full_output(ocmd.c_str(), dev->max_open_wait/2, results)) != 0) {
-      if (--timeout > 0) {
-         Dmsg2(40, "Device %s cannot be (un)mounted. Retrying... ERR=%s\n", dev->dev_name, results);
-        /* Sometimes the device cannot be mounted because it is already mounted.
-         * Try to unmount it, then remount it */
-        if (mount) {
-            Dmsg1(40, "Trying to unmount the device %s...\n", dev->dev_name);
-           do_mount_dev(dev, 0, 0);
-        }
-        bmicrosleep(1, 0);
-        continue;
-      }
-      free_pool_memory(results);
-      Dmsg2(40, "Device %s cannot be mounted. ERR=%s\n", dev->dev_name, results);
-      return -1;
-   }
-   
-   if (mount) {
-     dev->state |= ST_MOUNTED;
-   }
-   else {
-     dev->state &= ~ST_MOUNTED;
-   }
-   free_pool_memory(results);
-   
-   Dmsg1(29, "do_mount_dev: end_state=%d\n", dev->state & ST_MOUNTED);
-   return 0;
-}
-
-/* Only for devices that requires mount.
- * Try to find the volume name of the loaded device, and open the
- * first part of this volume. 
- *
- * Returns 0 if read_dev_volume_label can now read the label,
- * -1 if an error occured, and read_dev_volume_label_guess must abort with an IO_ERROR.
- *
- * To guess the device name, it lists all the files on the DVD, and searches for a 
- * file which has a minimum size (500 bytes). If this file has a numeric extension,
- * like part files, try to open the file which has no extension (e.g. the first
- * part file).
- * So, if the DVD does not contains a Bacula volume, a random file is opened,
- * and no valid label could be read from this file.
- *
- * It is useful, so the operator can be told that a wrong volume is mounted, with
- * the label name of the current volume. We can also check that the currently
- * mounted disk is writable. (See also read_dev_volume_label_guess in label.c).
- *
- * Note that if the right volume is mounted, open_guess_name_dev returns the same
- * result as an usual open_dev.
- */
-int open_guess_name_dev(DEVICE *dev) 
-{
-   Dmsg1(29, "open_guess_name_dev: dev=%s\n", dev->dev_name);
-   POOL_MEM guessedname(PM_FNAME);
-   DIR* dp;
-   struct dirent *entry, *result;
-   struct stat statp;
-   int index;
-   int name_max;
-   
-   if (!dev->is_dvd()) {
-      Dmsg1(100, "open_guess_name_dev: device does not require mount, returning 0. dev=%s\n", dev->dev_name);
-      return 0;
-   }
-
-#ifndef HAVE_DIRENT_H
-   Dmsg0(29, "open_guess_name_dev: readdir not available, cannot guess volume name\n");
-   return 0; 
-#endif
-   
-   update_free_space_dev(dev);
-
-   if (mount_dev(dev, 1) < 0) {
-      /* If the device cannot be mounted, check if it is writable */
-      if (dev->free_space_errno >= 0) {
-         Dmsg1(100, "open_guess_name_dev: device cannot be mounted, but it seems to be writable, returning 0. dev=%s\n", dev->dev_name);
-        return 0;
-      } else {
-         Dmsg1(100, "open_guess_name_dev: device cannot be mounted, and is not writable, returning 0. dev=%s\n", dev->dev_name);
-        /* read_dev_volume_label_guess must now check dev->free_space_errno to understand that the media is not writable. */
-        return 0;
-      }
-   }
-      
-   name_max = pathconf(".", _PC_NAME_MAX);
-   if (name_max < 1024) {
-      name_max = 1024;
-   }
-      
-   if (!(dp = opendir(dev->device->mount_point))) {
-      berrno be;
-      dev->dev_errno = errno;
-      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());
-      return -1;
-   }
-   
-   entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
-   while (1) {
-      if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
-        dev->dev_errno = ENOENT;
-         Dmsg2(29, "open_guess_name_dev: failed to find suitable file in dir %s (dev=%s)\n", dev->device->mount_point, dev->dev_name);
-        closedir(dp);
-        return -1;
-      }
-      
-      ASSERT(name_max+1 > (int)sizeof(struct dirent) + (int)NAMELEN(entry));
-      
-      if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
-        continue;
-      }
-      
-      pm_strcpy(guessedname, dev->device->mount_point);
-      if (guessedname.c_str()[strlen(guessedname.c_str())-1] != '/') {
-         pm_strcat(guessedname, "/");
-      }
-      pm_strcat(guessedname, entry->d_name);
-      
-      if (stat(guessedname.c_str(), &statp) < 0) {
-        berrno be;
-         Dmsg3(29, "open_guess_name_dev: failed to stat %s (dev=%s), ERR=%s\n",
-              guessedname.c_str(), dev->dev_name, be.strerror());
-        continue;
-      }
-      
-      if (!S_ISREG(statp.st_mode) || (statp.st_size < 500)) {
-         Dmsg2(100, "open_guess_name_dev: %s is not a regular file, or less than 500 bytes (dev=%s)\n", 
-              guessedname.c_str(), dev->dev_name);
-        continue;
-      }
-      
-      /* Ok, we found a good file, remove the part extension if possible. */
-      for (index = strlen(guessedname.c_str())-1; index >= 0; index--) {
-         if ((guessedname.c_str()[index] == '/') || 
-             (guessedname.c_str()[index] < '0') || 
-             (guessedname.c_str()[index] > '9')) {
-           break;
-        }
-         if (guessedname.c_str()[index] == '.') {
-            guessedname.c_str()[index] = '\0';
-           break;
-        }
-      }
-      
-      if ((stat(guessedname.c_str(), &statp) < 0) || (statp.st_size < 500)) {
-        /* The file with extension truncated does not exists or is too small, so use it with its extension. */
-        berrno be;
-         Dmsg3(100, "open_guess_name_dev: failed to stat %s (dev=%s), using the file with its extension, ERR=%s\n", 
-              guessedname.c_str(), dev->dev_name, be.strerror());
-        pm_strcpy(guessedname, dev->device->mount_point);
-         if (guessedname.c_str()[strlen(guessedname.c_str())-1] != '/') {
-            pm_strcat(guessedname, "/");
-        }
-        pm_strcat(guessedname, entry->d_name);
-        continue;
-      }
-      break;
-   }
-   
-   closedir(dp);
-   
-   if (dev->fd >= 0) {
-      close(dev->fd);
-   }
-     
-   if ((dev->fd = open(guessedname.c_str(), O_RDONLY | O_BINARY)) < 0) {
-      berrno be;
-      dev->dev_errno = errno;
-      Dmsg3(29, "open_guess_name_dev: failed to open %s (dev=%s), ERR=%s\n", 
-           guessedname.c_str(), dev->dev_name, be.strerror());
-      if (open_first_part(dev) < 0) {
-        berrno be;
-        dev->dev_errno = errno;
-         Mmsg1(&dev->errmsg, _("Could not open_first_part, ERR=%s\n"), be.strerror());
-        Emsg0(M_FATAL, 0, dev->errmsg);         
-      }
-      return -1;
-   }
-   dev->part_start = 0;
-   dev->part_size = statp.st_size;
-   dev->part = 0;
-   dev->state |= ST_OPENED;
-   dev->use_count = 1;
-   
-   Dmsg2(29, "open_guess_name_dev: %s opened (dev=%s)\n", guessedname.c_str(), dev->dev_name);
-   
-   return 0;
-}
-
-/* Mount the device.
- * If timeout, wait until the mount command returns 0.
- * If !timeout, try to mount the device only once.
- */
-int mount_dev(DEVICE* dev, int timeout) 
-{
-   if (dev->state & ST_MOUNTED) {
-      Dmsg0(100, "mount_dev: Device already mounted\n");
-      return 0;
-   } else {
-      return do_mount_dev(dev, 1, timeout);
-   }
-}
-
-/* Unmount the device
- * If timeout, wait until the unmount command returns 0.
- * If !timeout, try to unmount the device only once.
- */
-int unmount_dev(DEVICE* dev, int timeout) 
-{
-   if (dev->state & ST_MOUNTED) {
-      return do_mount_dev(dev, 0, timeout);
-   } else {
-      Dmsg0(100, "mount_dev: Device already unmounted\n");
-      return 0;
-   }
-}
-
-/* Update the free space on the device */
-static void update_free_space_dev(DEVICE* dev) 
-{
-   POOL_MEM ocmd(PM_FNAME);
-   POOLMEM* results;
-   char* icmd;
-   int timeout;
-   long long int free;
-   
-   icmd = dev->device->free_space_command;
-   
-   if (!icmd) {
-      dev->free_space = 0;
-      dev->free_space_errno = 0;
-      Dmsg2(29, "update_free_space_dev: free_space=%d, free_space_errno=%d (!icmd)\n", dev->free_space, dev->free_space_errno);
-      return;
-   }
-   
-   edit_device_codes_dev(dev, ocmd.c_str(), icmd);
-   
-   Dmsg1(29, "update_free_space_dev: cmd=%s\n", ocmd.c_str());
-
-   results = get_pool_memory(PM_MESSAGE);
-   
-   /* Try at most 3 times to get the free space on the device. This should perhaps be configurable. */
-   timeout = 3;
-   
-   while (1) {
-      char ed1[50];
-      if (run_program_full_output(ocmd.c_str(), dev->max_open_wait/2, results) == 0) {
-         Dmsg1(100, "Free space program run : %s\n", results);
-        free = str_to_int64(results);
-        if (free >= 0) {
-           dev->free_space = free;
-           dev->free_space_errno = 1;
-            Mmsg0(dev->errmsg, "");
-           break;
-        }
-      }
-      dev->free_space = 0;
-      dev->free_space_errno = -EPIPE;
-      Mmsg1(dev->errmsg, "Cannot run free space command (%s)\n", results);
-      
-      if (--timeout > 0) {
-         Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
-            "free_space_errno=%d ERR=%s\n", dev->dev_name, 
-              edit_uint64(dev->free_space, ed1), dev->free_space_errno, 
-              dev->errmsg);
-        bmicrosleep(1, 0);
-        continue;
-      }
-
-      dev->dev_errno = -dev->free_space_errno;
-      Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
-         "free_space_errno=%d ERR=%s\n",
-           dev->dev_name, edit_uint64(dev->free_space, ed1),
-           dev->free_space_errno, dev->errmsg);
-      break;
-   }
-   
-   free_pool_memory(results);
-   Dmsg2(29, "update_free_space_dev: free_space=%lld, free_space_errno=%d\n", dev->free_space, dev->free_space_errno);
-   return;
-}
-
-int write_part(DEVICE *dev) 
-{
-   Dmsg1(29, "write_part: device is %s\n", dev->dev_name);
-   
-   if (unmount_dev(dev, 1) < 0) {
-      Dmsg0(29, "write_part: unable to unmount the device\n");
-   }
-   
-   POOL_MEM ocmd(PM_FNAME);
-   POOLMEM *results;
-   results = get_pool_memory(PM_MESSAGE);
-   char* icmd;
-   int status;
-   int timeout;
-   
-   icmd = dev->device->write_part_command;
-   
-   edit_device_codes_dev(dev, ocmd.c_str(), icmd);
-      
-   /* Wait at most the time a maximum size part is written in DVD 0.5x speed
-    * FIXME: Minimum speed should be in device configuration 
-    */
-   timeout = dev->max_open_wait + (dev->max_part_size/(1350*1024/2));
-   
-   Dmsg2(29, "write_part: cmd=%s timeout=%d\n", ocmd.c_str(), timeout);
-      
-   status = run_program_full_output(ocmd.c_str(), timeout, results);
-   if (status != 0) {
-      Mmsg1(dev->errmsg, "Error while writing current part to the DVD: %s", results);
-      dev->dev_errno = EIO;
-      free_pool_memory(results);
-      return -1;
-   }
-   else {
-      Dmsg1(29, "write_part: command output=%s\n", results);
-      POOL_MEM archive_name(PM_FNAME);
-      get_filename(dev, dev->VolCatInfo.VolCatName, archive_name);
-      unlink(archive_name.c_str());
-      free_pool_memory(results);
-      return 0;
-   }
-}
-
-/* Open the next part file.
- *  - Close the fd
- *  - Increment part number 
- *  - Reopen the device
- */
-int open_next_part(DEVICE *dev) {
-   int state;
-      
-   Dmsg3(29, "open_next_part %s %s %d\n", dev->dev_name, dev->VolCatInfo.VolCatName, dev->openmode);
-   /* When appending, do not open a new part if the current is empty */
-   if (dev->can_append() && (dev->part == dev->num_parts) && 
-       (dev->part_size == 0)) {
-      Dmsg0(29, "open_next_part exited immediately (dev->part_size == 0).\n");
-      return dev->fd;
-   }
-   
-   if (dev->fd >= 0) {
-      close(dev->fd);
-   }
-   
-   dev->fd = -1;
-   
-   state = dev->state;
-   dev->state &= ~ST_OPENED;
-   
-   if (dev->is_dvd() && (dev->part == dev->num_parts) && dev->can_append()) {
-      if (write_part(dev) < 0) {
-        return -1;
-      }
-   }
-     
-   dev->part_start += dev->part_size;
-   dev->part++;
-   
-   if ((dev->num_parts < dev->part) && (dev->state & ST_APPEND)) {
-      dev->num_parts = dev->part;
-      
-      /* Check that the next part file does not exists.
-       * If it does, move it away... */
-      POOL_MEM archive_name(PM_FNAME);
-      POOL_MEM archive_bkp_name(PM_FNAME);
-      struct stat buf;
-      
-      get_filename(dev, dev->VolCatInfo.VolCatName, archive_name);
-      
-      /* Check if the next part exists. */
-      if ((stat(archive_name.c_str(), &buf) == 0) || (errno != ENOENT)) {
-         Dmsg1(29, "open_next_part %s is in the way, moving it away...\n", archive_name.c_str());
-        pm_strcpy(archive_bkp_name, archive_name.c_str());
-         pm_strcat(archive_bkp_name, ".bak");
-        unlink(archive_bkp_name.c_str()); 
-        
-        /* First try to rename it */
-        if (rename(archive_name.c_str(), archive_bkp_name.c_str()) < 0) {
-           berrno be;
-            Dmsg3(29, "open_next_part can't rename %s to %s, ERR=%s\n", 
-                 archive_name.c_str(), archive_bkp_name.c_str(), be.strerror());
-           /* Then try to unlink it */
-           if (unlink(archive_name.c_str()) < 0) {
-              berrno be;
-              dev->dev_errno = errno;
-               Mmsg2(&dev->errmsg, _("open_next_part can't unlink existing part %s, ERR=%s\n"), 
-                     archive_name.c_str(), be.strerror());
-              Emsg0(M_FATAL, 0, dev->errmsg);
-              return -1;
-           }
-        }
-      }
-   }
-   
-   if (open_dev(dev, dev->VolCatInfo.VolCatName, dev->openmode) < 0) {
-      return -1;
-   } else {
-      dev->state = state;
-      return dev->fd;
-   }
-}
-
-/* Open the first part file.
- *  - Close the fd
- *  - Reopen the device
- */
-int open_first_part(DEVICE *dev) {
-   int state;
-      
-   Dmsg3(29, "open_first_part %s %s %d\n", dev->dev_name, dev->VolCatInfo.VolCatName, dev->openmode);
-   if (dev->fd >= 0) {
-      close(dev->fd);
-   }
-   
-   dev->fd = -1;
-   state = dev->state;
-   dev->state &= ~ST_OPENED;
-   
-   dev->part_start = 0;
-   dev->part = 0;
-   
-   if (open_dev(dev, dev->VolCatInfo.VolCatName, dev->openmode)) {
-      dev->state = state;
-      return dev->fd;
-   } else {
-      return 0;
-   }
-}
-
-
-/* Protected version of lseek, which opens the right part if necessary */
-off_t lseek_dev(DEVICE *dev, off_t offset, int whence)
-{
-   int pos, openmode;
-   
-   if (dev->num_parts == 0) { /* If there is only one part, simply call lseek. */
-      return lseek(dev->fd, offset, whence);
-   }
-      
-   switch(whence) {
-   case SEEK_SET:
-      Dmsg1(100, "lseek_dev SEEK_SET called %d\n", offset);
-      if ((uint64_t)offset >= dev->part_start) {
-        if ((uint64_t)(offset - dev->part_start) < dev->part_size) {
-           /* We are staying in the current part, just seek */
-           if ((pos = lseek(dev->fd, (off_t)(offset-dev->part_start), SEEK_SET)) < 0) {
-              return pos;   
-           } else {
-              return pos + dev->part_start;
-           }
-        } else {
-           /* Load next part, and start again */
-           if (open_next_part(dev) < 0) {
-               Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
-              return -1;
-           }
-           return lseek_dev(dev, offset, SEEK_SET);
-        }
-      } else {
-        /* pos < dev->part_start :
-         * We need to access a previous part, 
-         * so just load the first one, and seek again
-         * until the right one is loaded */
-        if (open_first_part(dev) < 0) {
-            Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
-           return -1;
-        }
-        return lseek_dev(dev, offset, SEEK_SET);
-      }
-      break;
-   case SEEK_CUR:
-      Dmsg1(100, "lseek_dev SEEK_CUR called %d\n", offset);
-      if ((pos = lseek(dev->fd, (off_t)0, SEEK_CUR)) < 0) {
-        return pos;   
-      }
-      pos += dev->part_start;
-      if (offset == 0) {
-        return pos;
-      }
-      else { /* Not used in Bacula, but should work */
-        return lseek_dev(dev, pos, SEEK_SET);
-      }
-      break;
-   case SEEK_END:
-      Dmsg1(100, "lseek_dev SEEK_END called %d\n", offset);
-      if (offset > 0) { /* Not used by bacula */
-         Dmsg1(100, "lseek_dev SEEK_END called with an invalid offset %d\n", offset);
-        errno = EINVAL;
-        return -1;
-      }
-      
-      if (dev->part == dev->num_parts) { /* The right part is already loaded */
-        if ((pos = lseek(dev->fd, (off_t)0, SEEK_END)) < 0) {
-           return pos;   
-        } else {
-           return pos + dev->part_start;
-        }
-      } else {
-        /* Load the first part, then load the next until we reach the last one.
-         * This is the only way to be sure we compute the right file address. */
-        /* Save previous openmode, and open all but last part read-only (useful for DVDs) */
-        openmode = dev->openmode;
-        dev->openmode = OPEN_READ_ONLY;
-        
-        /* Works because num_parts > 0. */
-        if (open_first_part(dev) < 0) {
-            Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
-           return -1;
-        }
-        while (dev->part < (dev->num_parts-1)) {
-           if (open_next_part(dev) < 0) {
-               Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
-              return -1;
-           }
-        }
-        dev->openmode = openmode;
-        if (open_next_part(dev) < 0) {
-            Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
-           return -1;
-        }
-        return lseek_dev(dev, 0, SEEK_END);
-      }
-      break;
-   default:
-      errno = EINVAL;
-      return -1;
-   }
-}
-
 #ifdef debug_tracing
 #undef rewind_dev
 bool _rewind_dev(char *file, int line, DEVICE *dev)
@@ -1151,12 +559,10 @@ eod_dev(DEVICE *dev)
       return 0;
    }
 #ifdef MTEOM
-
-   if (dev_cap(dev, CAP_MTIOCGET) && dev_cap(dev, CAP_FASTFSF) && 
-      !dev_cap(dev, CAP_EOM)) {
-      struct mtget mt_stat;
+   if (dev_cap(dev, CAP_FASTFSF) && !dev_cap(dev, CAP_EOM)) {
       Dmsg0(100,"Using FAST FSF for EOM\n");
-      if (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 && mt_stat.mt_fileno <= 0) {
+      /* If unknown position, rewind */
+      if (!dev_get_os_pos(dev, &mt_stat)) {
        if (!rewind_dev(dev)) {
          return 0;
        }
@@ -1189,7 +595,7 @@ eod_dev(DEVICE *dev)
         return 0;
       }
 
-      if (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) < 0) {
+      if (!dev_get_os_pos(dev, &mt_stat)) {
         berrno be;
         clrerror_dev(dev, -1);
          Mmsg2(&dev->errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"),
@@ -1200,13 +606,13 @@ eod_dev(DEVICE *dev)
       dev->set_eof();
       dev->file = mt_stat.mt_fileno;
 
-   /*
-    * Rewind then use FSF until EOT reached
-    */
    } else {
 #else
    {
 #endif
+      /*
+       * Rewind then use FSF until EOT reached
+       */
       if (!rewind_dev(dev)) {
         return 0;
       }
@@ -1227,8 +633,7 @@ eod_dev(DEVICE *dev)
         if (file_num == (int)dev->file) {
            struct mtget mt_stat;
             Dmsg1(100, "fsf_dev did not advance from file %d\n", file_num);
-           if (dev_cap(dev, CAP_MTIOCGET) && ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 &&
-                     mt_stat.mt_fileno >= 0) {
+           if (dev_get_os_pos(dev, &mt_stat)) {
                Dmsg2(100, "Adjust file from %d to %d\n", dev->file , mt_stat.mt_fileno);
               dev->set_eof();
               dev->file = mt_stat.mt_fileno;
@@ -1248,7 +653,7 @@ eod_dev(DEVICE *dev)
       /* Backup over EOF */
       stat = bsf_dev(dev, 1);
       /* If BSF worked and fileno is known (not -1), set file */
-      if (dev_cap(dev, CAP_MTIOCGET) && ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 && mt_stat.mt_fileno >= 0) {
+      if (dev_get_os_pos(dev, &mt_stat)) {
          Dmsg2(100, "BSFATEOF adjust file from %d to %d\n", dev->file , mt_stat.mt_fileno);
         dev->file = mt_stat.mt_fileno;
       } else {
@@ -1536,7 +941,7 @@ fsf_dev(DEVICE *dev, int num)
       mt_com.mt_op = MTFSF;
       mt_com.mt_count = num;
       stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com);
-      if (stat < 0 || ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) < 0) {
+      if (stat < 0 || !dev_get_os_pos(dev, &mt_stat)) {
         berrno be;
         dev->state |= ST_EOT;
          Dmsg0(200, "Set ST_EOT\n");
@@ -1726,7 +1131,7 @@ fsr_dev(DEVICE *dev, int num)
       struct mtget mt_stat;
       clrerror_dev(dev, MTFSR);
       Dmsg1(100, "FSF fail: ERR=%s\n", be.strerror());
-      if (dev_cap(dev, CAP_MTIOCGET) && ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 && mt_stat.mt_fileno >= 0) {
+      if (dev_get_os_pos(dev, &mt_stat)) {
          Dmsg4(100, "Adjust from %d:%d to %d:%d\n", dev->file,
            dev->block_num, mt_stat.mt_fileno, mt_stat.mt_blkno);
         dev->file = mt_stat.mt_fileno;
@@ -2047,10 +1452,9 @@ static void do_close(DEVICE *dev)
    if (dev->fd >= 0) {
       close(dev->fd);
    }
-   if (dev_cap(dev, CAP_REQMOUNT)) {
-      if (unmount_dev(dev, 1) < 0) {
-         Dmsg1(0, "Cannot unmount device %s.\n", dev->dev_name);
-      }
+
+   if (unmount_dev(dev, 1) < 0) {
+      Dmsg1(0, "Cannot unmount device %s.\n", dev->dev_name);
    }
    
    /* Remove the last part file if it is empty */
@@ -2345,62 +1749,9 @@ void set_os_device_parameters(DEVICE *dev)
 #endif
 }
 
-/*
- * Edit codes into (Un)MountCommand, Write(First)PartCommand
- *  %% = %
- *  %a = archive device name
- *  %m = mount point
- *  %v = last part name
- *
- *  omsg = edited output message
- *  imsg = input string containing edit codes (%x)
- *
- */
-char *edit_device_codes_dev(DEVICE* dev, char *omsg, const char *imsg)
+static bool dev_get_os_pos(DEVICE *dev, struct mtget *mt_stat)
 {
-   const char *p;
-   const char *str;
-   char add[20];
-   
-   POOL_MEM archive_name(PM_FNAME);
-   get_filename(dev, dev->VolCatInfo.VolCatName, archive_name);
-
-   *omsg = 0;
-   Dmsg1(800, "edit_device_codes: %s\n", imsg);
-   for (p=imsg; *p; p++) {
-      if (*p == '%') {
-        switch (*++p) {
-            case '%':
-               str = "%";
-              break;
-            case 'n':
-               bsnprintf(add, sizeof(add), "%d", dev->part);
-              str = add;
-              break;
-            case 'a':
-              str = dev->dev_name;
-              break;
-            case 'm':
-              str = dev->device->mount_point;
-              break;
-            case 'v':
-              str = archive_name.c_str();
-              break;
-           default:
-               add[0] = '%';
-              add[1] = *p;
-              add[2] = 0;
-              str = add;
-              break;
-        }
-      } else {
-        add[0] = *p;
-        add[1] = 0;
-        str = add;
-      }
-      Dmsg1(900, "add_str %s\n", str);
-      pm_strcat(&omsg, (char *)str);
-      Dmsg1(800, "omsg=%s\n", omsg);
-   }
-   return omsg;
+   return dev_cap(dev, CAP_MTIOCGET) && 
+         ioctl(dev->fd, MTIOCGET, (char *)mt_stat) == 0 &&
+         mt_stat->mt_fileno >= 0;
 }
diff --git a/bacula/src/stored/dvd.c b/bacula/src/stored/dvd.c
new file mode 100644 (file)
index 0000000..7d46115
--- /dev/null
@@ -0,0 +1,695 @@
+/*
+ *
+ *   dvd.c  -- Routines specific to DVD devices (and
+ *            possibly other removable hard media). 
+ *
+ *    Nicolas Boichat, MMV
+ *
+ *   Version $Id$
+ */
+/*
+   Copyright (C) 2005 Kern Sibbald
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public
+   License along with this program; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA.
+
+ */
+
+#include "bacula.h"
+#include "stored.h"
+
+int mount_dev(DEVICE *dev, int timeout);
+int unmount_dev(DEVICE *dev, int timeout);
+void update_free_space_dev(DEVICE *dev);
+void get_filename(DEVICE *dev, char *VolName, POOL_MEM& archive_name);
+
+/* Forward referenced functions */
+static char *edit_device_codes_dev(DEVICE *dev, char *omsg, const char *imsg);
+static int do_mount_dev(DEVICE* dev, int mount, int dotimeout);
+static int write_part(DEVICE *dev);
+
+
+/* 
+ * Write the current volume/part filename to archive_name.
+ */
+void get_filename(DEVICE *dev, char *VolName, POOL_MEM& archive_name) 
+{
+   char partnumber[20];
+   
+   if (dev->is_dvd()) {
+        /* If we try to open the last part, just open it from disk, 
+        * otherwise, open it from the spooling directory */
+      if (dev->part < dev->num_parts) {
+        pm_strcpy(archive_name, dev->device->mount_point);
+      } else {
+        /* Use the working directory if spool directory is not defined */
+        if (dev->device->spool_directory) {
+           pm_strcpy(archive_name, dev->device->spool_directory);
+        } else {
+           pm_strcpy(archive_name, working_directory);
+        }
+      }
+   } else {
+      pm_strcpy(archive_name, dev->dev_name);
+   }
+      
+   if (archive_name.c_str()[strlen(archive_name.c_str())-1] != '/') {
+      pm_strcat(archive_name, "/");
+   }
+   pm_strcat(archive_name, VolName);
+   /* if part != 0, append .# to the filename (where # is the part number) */
+   if (dev->is_dvd() && dev->part != 0) {
+      pm_strcat(archive_name, ".");
+      bsnprintf(partnumber, sizeof(partnumber), "%d", dev->part);
+      pm_strcat(archive_name, partnumber);
+   }
+}  
+
+/* Mount the device.
+ * If timeout, wait until the mount command returns 0.
+ * If !timeout, try to mount the device only once.
+ */
+int mount_dev(DEVICE* dev, int timeout) 
+{
+   if (dev->state & ST_MOUNTED) {
+      Dmsg0(100, "mount_dev: Device already mounted\n");
+      return 0;
+   } else if (dev_cap(dev, CAP_REQMOUNT)) {
+      return do_mount_dev(dev, 1, timeout);
+   }      
+   return 0;
+}
+
+/* Unmount the device
+ * If timeout, wait until the unmount command returns 0.
+ * If !timeout, try to unmount the device only once.
+ */
+int unmount_dev(DEVICE *dev, int timeout) 
+{
+   if (dev->state & ST_MOUNTED) {
+      return do_mount_dev(dev, 0, timeout);
+   }
+   Dmsg0(100, "mount_dev: Device already unmounted\n");
+   return 0;
+}
+
+/* (Un)mount the device */
+static int do_mount_dev(DEVICE* dev, int mount, int dotimeout) {
+   POOL_MEM ocmd(PM_FNAME);
+   POOLMEM* results;
+   results = get_pool_memory(PM_MESSAGE);
+   char* icmd;
+   int status, timeout;
+   
+   if (mount) {
+      icmd = dev->device->mount_command;
+   }
+   else {
+      icmd = dev->device->unmount_command;
+   }
+   
+   edit_device_codes_dev(dev, ocmd.c_str(), icmd);
+   
+   Dmsg2(29, "do_mount_dev: cmd=%s state=%d\n", ocmd.c_str(), dev->state & ST_MOUNTED);
+
+   if (dotimeout) {
+      /* Try at most 5 times to (un)mount the device. This should perhaps be configurable. */
+      timeout = 5;
+   }
+   else {
+      timeout = 0;
+   }
+   /* If busy retry each second */
+   while ((status = run_program_full_output(ocmd.c_str(), dev->max_open_wait/2, results)) != 0) {
+      if (--timeout > 0) {
+         Dmsg2(40, "Device %s cannot be (un)mounted. Retrying... ERR=%s\n", dev->dev_name, results);
+        /* Sometimes the device cannot be mounted because it is already mounted.
+         * Try to unmount it, then remount it */
+        if (mount) {
+            Dmsg1(40, "Trying to unmount the device %s...\n", dev->dev_name);
+           do_mount_dev(dev, 0, 0);
+        }
+        bmicrosleep(1, 0);
+        continue;
+      }
+      free_pool_memory(results);
+      Dmsg2(40, "Device %s cannot be mounted. ERR=%s\n", dev->dev_name, results);
+      return -1;
+   }
+   
+   if (mount) {
+     dev->state |= ST_MOUNTED;
+   } else {
+     dev->state &= ~ST_MOUNTED;
+   }
+   free_pool_memory(results);
+   
+   Dmsg1(29, "do_mount_dev: end_state=%d\n", dev->state & ST_MOUNTED);
+   return 0;
+}
+
+/* Only for devices that require a mount.
+ * Try to find the volume name of the loaded device, and open the
+ * first part of this volume. 
+ *
+ * Returns 0 if read_dev_volume_label can now read the label,
+ * -1 if an error occured, and read_dev_volume_label_guess must abort with an IO_ERROR.
+ *
+ * To guess the device name, it lists all the files on the DVD, and searches for a 
+ * file which has a minimum size (500 bytes). If this file has a numeric extension,
+ * like part files, try to open the file which has no extension (e.g. the first
+ * part file).
+ * So, if the DVD does not contains a Bacula volume, a random file is opened,
+ * and no valid label could be read from this file.
+ *
+ * It is useful, so the operator can be told that a wrong volume is mounted, with
+ * the label name of the current volume. We can also check that the currently
+ * mounted disk is writable. (See also read_dev_volume_label_guess in label.c).
+ *
+ * Note that if the right volume is mounted, open_guess_name_dev returns the same
+ * result as an usual open_dev.
+ */
+int open_guess_name_dev(DEVICE *dev) 
+{
+   Dmsg1(29, "open_guess_name_dev: dev=%s\n", dev->dev_name);
+   POOL_MEM guessedname(PM_FNAME);
+   DIR* dp;
+   struct dirent *entry, *result;
+   struct stat statp;
+   int index;
+   int name_max;
+   
+   if (!dev->is_dvd()) {
+      Dmsg1(100, "open_guess_name_dev: device does not require mount, returning 0. dev=%s\n", dev->dev_name);
+      return 0;
+   }
+
+#ifndef HAVE_DIRENT_H
+   Dmsg0(29, "open_guess_name_dev: readdir not available, cannot guess volume name\n");
+   return 0; 
+#endif
+   
+   update_free_space_dev(dev);
+
+   if (mount_dev(dev, 1) < 0) {
+      /* If the device cannot be mounted, check if it is writable */
+      if (dev->free_space_errno >= 0) {
+         Dmsg1(100, "open_guess_name_dev: device cannot be mounted, but it seems to be writable, returning 0. dev=%s\n", dev->dev_name);
+        return 0;
+      } else {
+         Dmsg1(100, "open_guess_name_dev: device cannot be mounted, and is not writable, returning 0. dev=%s\n", dev->dev_name);
+        /* read_dev_volume_label_guess must now check dev->free_space_errno to understand that the media is not writable. */
+        return 0;
+      }
+   }
+      
+   name_max = pathconf(".", _PC_NAME_MAX);
+   if (name_max < 1024) {
+      name_max = 1024;
+   }
+      
+   if (!(dp = opendir(dev->device->mount_point))) {
+      berrno be;
+      dev->dev_errno = errno;
+      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());
+      return -1;
+   }
+   
+   entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
+   while (1) {
+      if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
+        dev->dev_errno = ENOENT;
+         Dmsg2(29, "open_guess_name_dev: failed to find suitable file in dir %s (dev=%s)\n", dev->device->mount_point, dev->dev_name);
+        closedir(dp);
+        return -1;
+      }
+      
+      ASSERT(name_max+1 > (int)sizeof(struct dirent) + (int)NAMELEN(entry));
+      
+      if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
+        continue;
+      }
+      
+      pm_strcpy(guessedname, dev->device->mount_point);
+      if (guessedname.c_str()[strlen(guessedname.c_str())-1] != '/') {
+         pm_strcat(guessedname, "/");
+      }
+      pm_strcat(guessedname, entry->d_name);
+      
+      if (stat(guessedname.c_str(), &statp) < 0) {
+        berrno be;
+         Dmsg3(29, "open_guess_name_dev: failed to stat %s (dev=%s), ERR=%s\n",
+              guessedname.c_str(), dev->dev_name, be.strerror());
+        continue;
+      }
+      
+      if (!S_ISREG(statp.st_mode) || (statp.st_size < 500)) {
+         Dmsg2(100, "open_guess_name_dev: %s is not a regular file, or less than 500 bytes (dev=%s)\n", 
+              guessedname.c_str(), dev->dev_name);
+        continue;
+      }
+      
+      /* Ok, we found a good file, remove the part extension if possible. */
+      for (index = strlen(guessedname.c_str())-1; index >= 0; index--) {
+         if ((guessedname.c_str()[index] == '/') || 
+             (guessedname.c_str()[index] < '0') || 
+             (guessedname.c_str()[index] > '9')) {
+           break;
+        }
+         if (guessedname.c_str()[index] == '.') {
+            guessedname.c_str()[index] = '\0';
+           break;
+        }
+      }
+      
+      if ((stat(guessedname.c_str(), &statp) < 0) || (statp.st_size < 500)) {
+        /* The file with extension truncated does not exists or is too small, so use it with its extension. */
+        berrno be;
+         Dmsg3(100, "open_guess_name_dev: failed to stat %s (dev=%s), using the file with its extension, ERR=%s\n", 
+              guessedname.c_str(), dev->dev_name, be.strerror());
+        pm_strcpy(guessedname, dev->device->mount_point);
+         if (guessedname.c_str()[strlen(guessedname.c_str())-1] != '/') {
+            pm_strcat(guessedname, "/");
+        }
+        pm_strcat(guessedname, entry->d_name);
+        continue;
+      }
+      break;
+   }
+   
+   closedir(dp);
+   
+   if (dev->fd >= 0) {
+      close(dev->fd);
+   }
+     
+   if ((dev->fd = open(guessedname.c_str(), O_RDONLY | O_BINARY)) < 0) {
+      berrno be;
+      dev->dev_errno = errno;
+      Dmsg3(29, "open_guess_name_dev: failed to open %s (dev=%s), ERR=%s\n", 
+           guessedname.c_str(), dev->dev_name, be.strerror());
+      if (open_first_part(dev) < 0) {
+        berrno be;
+        dev->dev_errno = errno;
+         Mmsg1(&dev->errmsg, _("Could not open_first_part, ERR=%s\n"), be.strerror());
+        Emsg0(M_FATAL, 0, dev->errmsg);         
+      }
+      return -1;
+   }
+   dev->part_start = 0;
+   dev->part_size = statp.st_size;
+   dev->part = 0;
+   dev->state |= ST_OPENED;
+   dev->use_count = 1;
+   
+   Dmsg2(29, "open_guess_name_dev: %s opened (dev=%s)\n", guessedname.c_str(), dev->dev_name);
+   
+   return 0;
+}
+
+
+/* Update the free space on the device */
+void update_free_space_dev(DEVICE* dev) 
+{
+   POOL_MEM ocmd(PM_FNAME);
+   POOLMEM* results;
+   char* icmd;
+   int timeout;
+   long long int free;
+   
+   icmd = dev->device->free_space_command;
+   
+   if (!icmd) {
+      dev->free_space = 0;
+      dev->free_space_errno = 0;
+      Dmsg2(29, "update_free_space_dev: free_space=%d, free_space_errno=%d (!icmd)\n", dev->free_space, dev->free_space_errno);
+      return;
+   }
+   
+   edit_device_codes_dev(dev, ocmd.c_str(), icmd);
+   
+   Dmsg1(29, "update_free_space_dev: cmd=%s\n", ocmd.c_str());
+
+   results = get_pool_memory(PM_MESSAGE);
+   
+   /* Try at most 3 times to get the free space on the device. This should perhaps be configurable. */
+   timeout = 3;
+   
+   while (1) {
+      char ed1[50];
+      if (run_program_full_output(ocmd.c_str(), dev->max_open_wait/2, results) == 0) {
+         Dmsg1(100, "Free space program run : %s\n", results);
+        free = str_to_int64(results);
+        if (free >= 0) {
+           dev->free_space = free;
+           dev->free_space_errno = 1;
+            Mmsg0(dev->errmsg, "");
+           break;
+        }
+      }
+      dev->free_space = 0;
+      dev->free_space_errno = -EPIPE;
+      Mmsg1(dev->errmsg, "Cannot run free space command (%s)\n", results);
+      
+      if (--timeout > 0) {
+         Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
+            "free_space_errno=%d ERR=%s\n", dev->dev_name, 
+              edit_uint64(dev->free_space, ed1), dev->free_space_errno, 
+              dev->errmsg);
+        bmicrosleep(1, 0);
+        continue;
+      }
+
+      dev->dev_errno = -dev->free_space_errno;
+      Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
+         "free_space_errno=%d ERR=%s\n",
+           dev->dev_name, edit_uint64(dev->free_space, ed1),
+           dev->free_space_errno, dev->errmsg);
+      break;
+   }
+   
+   free_pool_memory(results);
+   Dmsg2(29, "update_free_space_dev: free_space=%lld, free_space_errno=%d\n", dev->free_space, dev->free_space_errno);
+   return;
+}
+
+static int write_part(DEVICE *dev) 
+{
+   Dmsg1(29, "write_part: device is %s\n", dev->dev_name);
+   
+   if (unmount_dev(dev, 1) < 0) {
+      Dmsg0(29, "write_part: unable to unmount the device\n");
+   }
+   
+   POOL_MEM ocmd(PM_FNAME);
+   POOLMEM *results;
+   results = get_pool_memory(PM_MESSAGE);
+   char* icmd;
+   int status;
+   int timeout;
+   
+   icmd = dev->device->write_part_command;
+   
+   edit_device_codes_dev(dev, ocmd.c_str(), icmd);
+      
+   /* Wait at most the time a maximum size part is written in DVD 0.5x speed
+    * FIXME: Minimum speed should be in device configuration 
+    */
+   timeout = dev->max_open_wait + (dev->max_part_size/(1350*1024/2));
+   
+   Dmsg2(29, "write_part: cmd=%s timeout=%d\n", ocmd.c_str(), timeout);
+      
+   status = run_program_full_output(ocmd.c_str(), timeout, results);
+   if (status != 0) {
+      Mmsg1(dev->errmsg, "Error while writing current part to the DVD: %s", results);
+      dev->dev_errno = EIO;
+      free_pool_memory(results);
+      return -1;
+   }
+   else {
+      Dmsg1(29, "write_part: command output=%s\n", results);
+      POOL_MEM archive_name(PM_FNAME);
+      get_filename(dev, dev->VolCatInfo.VolCatName, archive_name);
+      unlink(archive_name.c_str());
+      free_pool_memory(results);
+      return 0;
+   }
+}
+
+/* Open the next part file.
+ *  - Close the fd
+ *  - Increment part number 
+ *  - Reopen the device
+ */
+int open_next_part(DEVICE *dev) {
+   int state;
+      
+   Dmsg3(29, "open_next_part %s %s %d\n", dev->dev_name, dev->VolCatInfo.VolCatName, dev->openmode);
+   /* When appending, do not open a new part if the current is empty */
+   if (dev->can_append() && (dev->part == dev->num_parts) && 
+       (dev->part_size == 0)) {
+      Dmsg0(29, "open_next_part exited immediately (dev->part_size == 0).\n");
+      return dev->fd;
+   }
+   
+   if (dev->fd >= 0) {
+      close(dev->fd);
+   }
+   
+   dev->fd = -1;
+   
+   state = dev->state;
+   dev->state &= ~ST_OPENED;
+   
+   if (dev->is_dvd() && (dev->part == dev->num_parts) && dev->can_append()) {
+      if (write_part(dev) < 0) {
+        return -1;
+      }
+   }
+     
+   dev->part_start += dev->part_size;
+   dev->part++;
+   
+   if ((dev->num_parts < dev->part) && (dev->state & ST_APPEND)) {
+      dev->num_parts = dev->part;
+      
+      /* Check that the next part file does not exists.
+       * If it does, move it away... */
+      POOL_MEM archive_name(PM_FNAME);
+      POOL_MEM archive_bkp_name(PM_FNAME);
+      struct stat buf;
+      
+      get_filename(dev, dev->VolCatInfo.VolCatName, archive_name);
+      
+      /* Check if the next part exists. */
+      if ((stat(archive_name.c_str(), &buf) == 0) || (errno != ENOENT)) {
+         Dmsg1(29, "open_next_part %s is in the way, moving it away...\n", archive_name.c_str());
+        pm_strcpy(archive_bkp_name, archive_name.c_str());
+         pm_strcat(archive_bkp_name, ".bak");
+        unlink(archive_bkp_name.c_str()); 
+        
+        /* First try to rename it */
+        if (rename(archive_name.c_str(), archive_bkp_name.c_str()) < 0) {
+           berrno be;
+            Dmsg3(29, "open_next_part can't rename %s to %s, ERR=%s\n", 
+                 archive_name.c_str(), archive_bkp_name.c_str(), be.strerror());
+           /* Then try to unlink it */
+           if (unlink(archive_name.c_str()) < 0) {
+              berrno be;
+              dev->dev_errno = errno;
+               Mmsg2(&dev->errmsg, _("open_next_part can't unlink existing part %s, ERR=%s\n"), 
+                     archive_name.c_str(), be.strerror());
+              Emsg0(M_FATAL, 0, dev->errmsg);
+              return -1;
+           }
+        }
+      }
+   }
+   
+   if (open_dev(dev, dev->VolCatInfo.VolCatName, dev->openmode) < 0) {
+      return -1;
+   } else {
+      dev->state = state;
+      return dev->fd;
+   }
+}
+
+/* Open the first part file.
+ *  - Close the fd
+ *  - Reopen the device
+ */
+int open_first_part(DEVICE *dev) {
+   int state;
+      
+   Dmsg3(29, "open_first_part %s %s %d\n", dev->dev_name, dev->VolCatInfo.VolCatName, dev->openmode);
+   if (dev->fd >= 0) {
+      close(dev->fd);
+   }
+   
+   dev->fd = -1;
+   state = dev->state;
+   dev->state &= ~ST_OPENED;
+   
+   dev->part_start = 0;
+   dev->part = 0;
+   
+   if (open_dev(dev, dev->VolCatInfo.VolCatName, dev->openmode)) {
+      dev->state = state;
+      return dev->fd;
+   } else {
+      return 0;
+   }
+}
+
+
+/* Protected version of lseek, which opens the right part if necessary */
+off_t lseek_dev(DEVICE *dev, off_t offset, int whence)
+{
+   int pos, openmode;
+   
+   if (dev->num_parts == 0) { /* If there is only one part, simply call lseek. */
+      return lseek(dev->fd, offset, whence);
+   }
+      
+   switch(whence) {
+   case SEEK_SET:
+      Dmsg1(100, "lseek_dev SEEK_SET called %d\n", offset);
+      if ((uint64_t)offset >= dev->part_start) {
+        if ((uint64_t)(offset - dev->part_start) < dev->part_size) {
+           /* We are staying in the current part, just seek */
+           if ((pos = lseek(dev->fd, (off_t)(offset-dev->part_start), SEEK_SET)) < 0) {
+              return pos;   
+           } else {
+              return pos + dev->part_start;
+           }
+        } else {
+           /* Load next part, and start again */
+           if (open_next_part(dev) < 0) {
+               Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
+              return -1;
+           }
+           return lseek_dev(dev, offset, SEEK_SET);
+        }
+      } else {
+        /* pos < dev->part_start :
+         * We need to access a previous part, 
+         * so just load the first one, and seek again
+         * until the right one is loaded */
+        if (open_first_part(dev) < 0) {
+            Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
+           return -1;
+        }
+        return lseek_dev(dev, offset, SEEK_SET);
+      }
+      break;
+   case SEEK_CUR:
+      Dmsg1(100, "lseek_dev SEEK_CUR called %d\n", offset);
+      if ((pos = lseek(dev->fd, (off_t)0, SEEK_CUR)) < 0) {
+        return pos;   
+      }
+      pos += dev->part_start;
+      if (offset == 0) {
+        return pos;
+      }
+      else { /* Not used in Bacula, but should work */
+        return lseek_dev(dev, pos, SEEK_SET);
+      }
+      break;
+   case SEEK_END:
+      Dmsg1(100, "lseek_dev SEEK_END called %d\n", offset);
+      if (offset > 0) { /* Not used by bacula */
+         Dmsg1(100, "lseek_dev SEEK_END called with an invalid offset %d\n", offset);
+        errno = EINVAL;
+        return -1;
+      }
+      
+      if (dev->part == dev->num_parts) { /* The right part is already loaded */
+        if ((pos = lseek(dev->fd, (off_t)0, SEEK_END)) < 0) {
+           return pos;   
+        } else {
+           return pos + dev->part_start;
+        }
+      } else {
+        /* Load the first part, then load the next until we reach the last one.
+         * This is the only way to be sure we compute the right file address. */
+        /* Save previous openmode, and open all but last part read-only (useful for DVDs) */
+        openmode = dev->openmode;
+        dev->openmode = OPEN_READ_ONLY;
+        
+        /* Works because num_parts > 0. */
+        if (open_first_part(dev) < 0) {
+            Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
+           return -1;
+        }
+        while (dev->part < (dev->num_parts-1)) {
+           if (open_next_part(dev) < 0) {
+               Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
+              return -1;
+           }
+        }
+        dev->openmode = openmode;
+        if (open_next_part(dev) < 0) {
+            Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
+           return -1;
+        }
+        return lseek_dev(dev, 0, SEEK_END);
+      }
+      break;
+   default:
+      errno = EINVAL;
+      return -1;
+   }
+}
+
+
+/*
+ * Edit codes into (Un)MountCommand, Write(First)PartCommand
+ *  %% = %
+ *  %a = archive device name
+ *  %m = mount point
+ *  %v = last part name
+ *
+ *  omsg = edited output message
+ *  imsg = input string containing edit codes (%x)
+ *
+ */
+static char *edit_device_codes_dev(DEVICE* dev, char *omsg, const char *imsg)
+{
+   const char *p;
+   const char *str;
+   char add[20];
+   
+   POOL_MEM archive_name(PM_FNAME);
+   get_filename(dev, dev->VolCatInfo.VolCatName, archive_name);
+
+   *omsg = 0;
+   Dmsg1(800, "edit_device_codes: %s\n", imsg);
+   for (p=imsg; *p; p++) {
+      if (*p == '%') {
+        switch (*++p) {
+            case '%':
+               str = "%";
+              break;
+            case 'n':
+               bsnprintf(add, sizeof(add), "%d", dev->part);
+              str = add;
+              break;
+            case 'a':
+              str = dev->dev_name;
+              break;
+            case 'm':
+              str = dev->device->mount_point;
+              break;
+            case 'v':
+              str = archive_name.c_str();
+              break;
+           default:
+               add[0] = '%';
+              add[1] = *p;
+              add[2] = 0;
+              str = add;
+              break;
+        }
+      } else {
+        add[0] = *p;
+        add[1] = 0;
+        str = add;
+      }
+      Dmsg1(900, "add_str %s\n", str);
+      pm_strcat(&omsg, (char *)str);
+      Dmsg1(800, "omsg=%s\n", omsg);
+   }
+   return omsg;
+}
index 7246bc8f765e951dedc5ba3f9a350ed69684e293..aa58181eddc9f4073d9925fae23c6f10b88c6de4 100644 (file)
@@ -21,6 +21,7 @@
 /* #define TRACE_JCR_CHAIN 1 */
 /* #define TRACE_RES 1 */
 /* #define DEBUG_MEMSET 1 */
+/* #define DEBUG_MUTEX 1 */
 
 /* Check if header of tape block is zero before writing */
 #define DEBUG_BLOCK_ZEROING 1
@@ -33,7 +34,7 @@
 /*  #define SEND_DMSG_TO_FILE 1 */
 
 
+/* The following are turned on for performance testing */
 /* #define NO_ATTRIBUTES_TEST 1 */
 /* #define NO_TAPE_WRITE_TEST 1 */
 /* #define FD_NO_SEND TEST 1 */
-/* #define DEBUG_MUTEX 1 */