Sophie

Sophie

distrib > Mandriva > current > i586 > media > contrib-release-src > by-pkgid > 6a41704604c0e0eaefc3923641944fd5 > files > 3

amanda-2.5.1-9mdv2009.1.src.rpm


http://sourceforge.net/tracker/index.php?func=detail&aid=1445174&group_id=120&atid=300120

 This is a good patch, because it adds a tapeio plugin. But
 although well-written, it seems to me there are some
 fundamental problems with this design:

 -- Data for the entire disc is copied from the holding disk
 into a temporary file, then burned from there; this will
 incur a substantial performance penalty.
 -- It will not work with non-rewritable DVDs; the label
 cannot be overwritten.
 -- It requires kernel support for 4.7GB files and 4.7GB DVD
 sessions
 -- It does not properly maintain state (FSF) between
 different Amanda tools.

 I'll see if I can adapt this idea into something more
 comprehensive that addresses these problems.



diff -Naur amanda-2.5.0/tape-src/Makefile.am amanda-2.5.0.oden/tape-src/Makefile.am
--- amanda-2.5.0/tape-src/Makefile.am	2006-02-14 12:51:24.000000000 +0100
+++ amanda-2.5.0.oden/tape-src/Makefile.am	2006-03-27 16:26:58.000000000 +0200
@@ -12,6 +12,7 @@
 			output-null.c \
 			output-rait.c \
 			output-tape.c \
+			output-dvdrw.c \
 			tapeio.c
 
 libamtape_la_LDFLAGS =  -release $(VERSION)
@@ -42,6 +43,7 @@
 			output-null.h \
 			output-rait.h \
 			output-tape.h \
+			output-dvdrw.h \
 			tapeio.h
 
 install-exec-hook:
diff -Naur amanda-2.5.0/tape-src/output-dvdrw.c amanda-2.5.0.oden/tape-src/output-dvdrw.c
--- amanda-2.5.0/tape-src/output-dvdrw.c	1970-01-01 01:00:00.000000000 +0100
+++ amanda-2.5.0.oden/tape-src/output-dvdrw.c	2006-03-27 16:27:23.000000000 +0200
@@ -0,0 +1,1463 @@
+
+/*
+ * $Id: output-dvdrw.c,v  $
+ *
+ */
+
+#include "amanda.h"
+
+#include "token.h"
+#include "tapeio.h"
+#include "output-dvdrw.h"
+#include "fileheader.h"
+
+#include <sys/mount.h>
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+#define	MAX_TOKENS		10
+
+#define	DATA_INDICATOR		"."
+
+/* #define DEBUG */
+#ifdef DEBUG
+#define DEBUGP(x1,x2,x3,x4,x5,x6,x7) fprintf(stderr,x1,x2,x3,x4,x5,x6,x7)
+#else
+#define DEBUGP(x1,x2,x3,x4,x5,x6,x7)
+#endif
+
+static
+struct volume_info {
+    char *basename;			/* filename from read open */
+    char *wrbasename;		/* filename from write open */
+    char *drvname;          /* device of dvd */
+    dev_t dev ;             /* dev number of dvd */
+    struct dvdrw_info *fi;  /* dvdrw info array */
+    int fi_limit;			/* length of dvdrw info array */
+    int flags;				/* open flags */
+    int mask;				/* open mask */
+    int dvdrw_count;			/* number of dvdrws */
+    int dvdrw_current;			/* current dvdrw position */
+    int fd;				/* data dvdrw descriptor */
+    int is_online;			/* true if "tape" is "online" */
+    int at_bof;				/* true if at begining of dvdrw */
+    int at_eof;				/* true if at end of dvdrw */
+    int at_eom;				/* true if at end of medium */
+    int isRplus;  	        /* true if is R+ DVD  */
+    int isNew;              /* true if R+ DVD is new (means writing) */
+    int last_operation_write;		/* true if last op was a write */
+    int must_burn  ;		/* true if any outstanding writes must
+                                 * burn*/
+    long amount_written;		/* KBytes written since open/rewind */
+} *volume_info = NULL;
+
+struct dvdrw_info {
+    char *name;				/* dvdrw name (tapefd_getinfo_...) */
+    int flags;				/* open flags */
+};
+
+static int waitforpid ( pid_t pid )
+{
+    pid_t rpid;
+	int   rval;
+
+    while (1)
+    {
+        rpid = waitpid (pid,&rval,0);
+        if (rpid == (pid_t)-1 || (rpid != pid && (errno=ECHILD)))
+        {
+            if (errno==EINTR)
+                continue;
+            else
+                return 1;
+        }
+        if (WIFSTOPPED(rval))
+            continue;
+        errno=0;
+        if (WIFEXITED(rval))
+            errno=WEXITSTATUS(rval);
+        else
+            errno=ECHILD;
+        break;
+    }
+    if ( errno )
+        return 1;
+    else
+        return 0;
+}
+
+static int do_mount(int fd, int mnt)
+{
+    pid_t pid;
+    char *prog, *progpath;
+
+    if ( mnt )
+    {
+        /* if mount is still checked */
+        if ( volume_info[fd].isNew )
+            return 0;
+
+        progpath = "/bin/mount";
+        prog     = "mount";
+    }
+    else
+    {
+        progpath = "/bin/umount";
+        prog     = "umount";
+    }
+
+    switch(pid = fork()) 
+    {
+        case -1:
+            return -1;
+            break;
+        case 0:		/* child process */
+            /* remove last slash */
+            volume_info[fd].basename[strlen(volume_info[fd].basename)-1] = '\0';
+            fprintf(stderr, "dvdrw: %s %s\n", prog, volume_info[fd].basename);
+            execl (progpath, prog, volume_info[fd].basename, NULL);
+            fprintf(stderr, "dvdrw: ERROR: cannot start %s: %s\n",progpath,strerror(errno));
+            exit(1);
+            break;
+        default:	/* parent process */
+            waitforpid(pid);
+            if ( errno )
+            {
+                if (  mnt && volume_info[fd].isRplus )
+                {
+                    volume_info[fd].isNew = 1;
+                    DEBUGP( "dvd: new R+ DVD\n",0,0,0,0,0,0);
+                    errno = 0;
+                    break;
+                }
+
+                fprintf(stderr, "dvdrw: ERROR: %s error: %s\n",progpath,strerror(errno));
+                return -1;
+            }
+            break;
+    }
+    return 0;
+}
+
+static int open_count = 0;
+static int
+dvdrw_burn( fd )
+int fd;
+{
+    struct stat sbuf;
+    pid_t pid;
+    DIR *tapedir;
+    struct dirent *entry;
+    char *fname;
+
+    if ( ! stat(volume_info[fd].basename, &sbuf) &&
+         sbuf.st_dev == volume_info[fd].dev )
+    {
+        do_mount(fd,0);
+    }
+
+    /* now do really burn */
+    fprintf(stderr, "dvdrw: now burn with command growisofs\n");
+
+    /*
+     * Fork and set up the return or run the program.
+     */
+    switch(pid = fork()) 
+    {
+        case -1:
+            return 1;
+            break;
+        case 0:		/* child process */
+            setenv("PATH","/usr/bin:/usr/sbin:/usr/local/bin",1);
+
+            execlp("growisofs","growisofs","-use-the-force-luke=notray","-use-the-force-luke=tty",
+                   "-Z",volume_info[fd].drvname,
+                   "-J","-R","-pad","-quiet",volume_info[fd].wrbasename,NULL);
+            fprintf(stderr, "dvdrw: ERROR: cannot start growisofs: %s\n",strerror(errno));
+            exit(1);
+            break;
+        default:	/* parent process */
+            fprintf(stderr, "dvdrw: waiting until burned!\n");
+            waitforpid(pid);
+            if ( errno )
+            {
+                fprintf(stderr, "dvdrw: ERROR: burn error: %s\n",strerror(errno));
+                return 1;
+            }
+            break;
+    }
+
+    volume_info[fd].must_burn = 0;
+    /* remove all files */
+    if ((tapedir = opendir(volume_info[fd].wrbasename))) 
+    {
+        while ((entry = readdir(tapedir)) != NULL) 
+        {
+            if (is_dot_or_dotdot(entry->d_name)) 
+            {
+                continue;
+            }
+            fname = vstralloc(volume_info[fd].wrbasename,entry->d_name,NULL);
+            unlink(fname);
+            amfree(fname);
+        }
+        closedir(tapedir);
+    }
+    return 0;
+}
+
+/*
+ * "Open" the tape by scanning the "data" directory.  "Tape dvdrws"
+ * have five leading digits indicating the position (counting from zero)
+ * followed by a '.' and optional other information (e.g. host/disk/level
+ * image name).
+ *
+ * We allow for the following situations:
+ *
+ *   + If we see the same "dvdrw" (position number) more than once, the
+ *     last one seen wins.  This should not normally happen.
+ *
+ *   + We allow gaps in the positions.  This should not normally happen.
+ *
+ * Anything in the directory that does not match a "tape dvdrw" name
+ * pattern is ignored.
+ *
+ * If the data directory does not exist, the "tape" is considered offline.
+ * It is allowed to "appear" later.
+ */
+
+static int
+check_online(fd)
+int fd;
+{
+    char *token[MAX_TOKENS];
+    DIR *tapedir;
+    struct dirent *entry;
+    struct dvdrw_info *fi;
+    struct stat sbuf;
+    char *line;
+    int f;
+    int pos;
+    int rc = 0;
+
+    /* ist 'data' noch gemounted ? sonst offline  */
+    if ( stat(volume_info[fd].basename, &sbuf) || sbuf.st_dev != volume_info[fd].dev )
+    {
+        volume_info[fd].is_online = 0;
+    }
+
+    /*
+     * If we are already online, there is nothing else to do.
+     */
+    if (volume_info[fd].is_online) 
+    {
+        goto common_exit;
+    }
+
+    /* hier 'data' auf dvd mounten, falls gelesen wird */ 
+
+    if ( ( volume_info[fd].flags & 3)  == O_RDONLY )
+    {
+        if ( volume_info[fd].must_burn )
+        {
+            if (dvdrw_burn( fd ))
+            {
+                rc = 1;
+                goto common_exit;
+            }
+        }
+        if ( do_mount(fd,1))
+        {
+            rc = 1;
+            goto common_exit;
+        }
+    }
+    else
+    {
+        if ( ! stat(volume_info[fd].basename, &sbuf) &&
+             sbuf.st_dev == volume_info[fd].dev )
+        {
+            do_mount(fd,0);
+        }
+        if ( volume_info[fd].isRplus )
+        {
+            volume_info[fd].isNew = 1;
+        }
+    }
+
+    DEBUGP( "dvd: check dir %d online=%d isNew=%d %s %s\n",
+            fd,volume_info[fd].is_online,volume_info[fd].isNew,
+            volume_info[fd].wrbasename,volume_info[fd].basename,0);
+
+    if ((tapedir = opendir( volume_info[fd].isNew ? 
+                            volume_info[fd].wrbasename:
+                            volume_info[fd].basename)) == NULL) {
+        /*
+         * We have already opened the info dvdrw which is in the same
+         * directory as the data directory, so ENOENT has to mean the data
+         * directory is not there, which we treat as being "offline".
+         * We're already offline at this point (see the above test)
+         * and this is not an error, so just return success (no error).
+         */
+
+        rc = (errno != ENOENT);
+        goto common_exit;
+    }
+    while ((entry = readdir(tapedir)) != NULL) {
+        if (is_dot_or_dotdot(entry->d_name)) {
+            continue;
+        }
+        if (isdigit((int)entry->d_name[0])
+            && isdigit((int)entry->d_name[1])
+            && isdigit((int)entry->d_name[2])
+            && isdigit((int)entry->d_name[3])
+            && isdigit((int)entry->d_name[4])
+            && entry->d_name[5] == '.') {
+
+            /*
+             * This is a "tape dvdrw".
+             */
+            pos = atoi(entry->d_name);
+            amtable_alloc((void **)&volume_info[fd].fi,
+                          &volume_info[fd].fi_limit,
+                          sizeof(*volume_info[fd].fi),
+                          pos + 1,
+                          10,
+                          NULL);
+            fi = &volume_info[fd].fi[pos];
+            if (fi->name != NULL) {
+                /*
+                 * Two dvdrws with the same position???
+                 */
+                amfree(fi->name);
+            }
+            fi->name = stralloc(&entry->d_name[6]);
+            if (pos + 1 > volume_info[fd].dvdrw_count) {
+                volume_info[fd].dvdrw_count = pos + 1;
+            }
+            DEBUGP( "dvd: add file %s pos=%d count=%d\n",entry->d_name,pos,
+                    volume_info[fd].dvdrw_count,0,0,0);
+        }
+    }
+    closedir(tapedir);
+
+    /*
+     * Parse the info dvdrw.  We know we are at beginning of dvdrw because
+     * the only thing that can happen to it prior to here is it being
+     * opened.
+     */
+    for (; (line = areads(fd)) != NULL; free(line)) {
+        f = split(line, token, sizeof(token) / sizeof(token[0]), " ");
+        if (f == 2 && strcmp(token[1], "position") == 0) {
+            volume_info[fd].dvdrw_current = atoi(token[2]);
+        }
+    }
+
+    /*
+     * Set EOM and make sure we are not pre-BOI.
+     */
+    if (volume_info[fd].dvdrw_current >= volume_info[fd].dvdrw_count) {
+        volume_info[fd].at_eom = 1;
+    }
+    if (volume_info[fd].dvdrw_current < 0) {
+        volume_info[fd].dvdrw_current = 0;
+    }
+
+    volume_info[fd].is_online = 1;
+
+ common_exit:
+
+    DEBUGP( "dvd: check %d online=%d current=%d count=%d ateom=%d rc=%d\n",
+            fd,volume_info[fd].is_online,volume_info[fd].dvdrw_current,
+            volume_info[fd].dvdrw_count,volume_info[fd].at_eom,rc);
+    if ( errno )
+        fprintf(stderr,"ERROR: %s: %s\n", volume_info[fd].basename, strerror(errno));
+    return rc;
+}
+
+/*
+ * Open the tape dvdrw if not already.  If we are beyond the dvdrw count
+ * (end of tape) or the dvdrw is missing and we are only reading, set
+ * up to read /dev/null which will look like EOF.  If we are writing,
+ * create the dvdrw.
+ */
+
+static int
+dvdrw_open(fd)
+int fd;
+{
+    struct dvdrw_info *fi;
+    char *datadvdrwname = NULL;
+    char *f = NULL;
+    int pos;
+    char *host;
+    char *disk;
+    int level;
+    char number[NUM_STR_SIZE];
+    int flags;
+    int rfd;
+    int n;
+    char *line = NULL;
+    int start_record;
+    int end_record;
+    int record_size;
+
+    /* check if read or write */
+    if (volume_info[fd].fd >= 0 && ((volume_info[fd].flags & 3) != volume_info[fd].fi[volume_info[fd].dvdrw_current].flags )) 
+    {
+        /* first write */
+        aclose(volume_info[fd].fd);
+    }
+
+    if (volume_info[fd].fd < 0) 
+    {
+        flags = volume_info[fd].flags;
+        pos = volume_info[fd].dvdrw_current;
+        amtable_alloc((void **)&volume_info[fd].fi,
+                      &volume_info[fd].fi_limit,
+                      sizeof(*volume_info[fd].fi),
+                      pos + 1,
+                      10,
+                      NULL);
+        fi = &volume_info[fd].fi[pos];
+
+        /*
+         * See if we are creating a new dvdrw.
+         */
+        if (pos >= volume_info[fd].dvdrw_count) 
+        {
+            volume_info[fd].dvdrw_count = pos + 1;
+        }
+
+        /*
+         * Generate the dvdrw name to open.
+         */
+        if (fi->name == NULL) 
+        {
+            fi->flags = (volume_info[fd].flags & 3);
+            if ( fi->flags != O_RDONLY) 
+            {
+
+                /*
+                 * This is a new dvdrw, so make sure we create/truncate
+                 * it.	Generate the name based on the host/disk/level
+                 * information from the caller, if available, else
+                 * a constant.
+                 */
+                flags |= (O_CREAT | O_TRUNC);
+                host = tapefd_getinfo_host(fd);
+                disk = tapefd_getinfo_disk(fd);
+                level = tapefd_getinfo_level(fd);
+                snprintf(number, sizeof(number), "%d", level);
+                if (host != NULL) {
+                    f = stralloc(host);
+                }
+                if (disk != NULL) {
+                    disk = sanitise_filename(disk);
+                    if (f == NULL) {
+                        f = stralloc(disk);
+                    } else {
+                        f = newvstralloc(f, f, ".", disk, NULL);
+                    }
+                    amfree(disk);
+                }
+                if (level >= 0) {
+                    if (f == NULL) {
+                        f = stralloc(number);
+                    } else {
+                        f = newvstralloc(f, f, ".", number, NULL);
+                    }
+                }
+                if (f == NULL) {
+                    f = stralloc("unknown");
+                }
+                amfree(fi->name);
+                fi->name = stralloc(f);
+                amfree(f);
+            } else {
+
+                /*
+                 * This is a missing dvdrw, so set up to read nothing.
+                 */
+                datadvdrwname = stralloc("/dev/null");
+            }
+        }
+        if (datadvdrwname == NULL) 
+        {
+            snprintf(number, sizeof(number), "%05d", pos);
+            datadvdrwname = vstralloc((fi->flags != O_RDONLY) || 
+                                      volume_info[fd].isNew ? 
+                                      volume_info[fd].wrbasename: 
+                                      volume_info[fd].basename,
+                                      number,
+                                      DATA_INDICATOR,
+                                      volume_info[fd].fi[pos].name,
+                                      NULL);
+        }
+
+        /*
+         * Do the data dvdrw open.
+         */
+        volume_info[fd].fd = open(datadvdrwname, flags, volume_info[fd].mask);
+        fprintf(stderr, "dvdrw: opens %s [info=%d] flags=%08x pos=%d fd=%d\n",
+                datadvdrwname,fd,flags,pos,volume_info[fd].fd);
+        amfree(datadvdrwname);
+    }
+
+    return volume_info[fd].fd;
+}
+
+/*
+ * Close the current data dvdrw, if open.  
+ */
+
+static void
+dvdrw_close(fd)
+int fd;
+{
+    struct dvdrw_info *fi;
+    int pos;
+    int r;
+    FILE *f;
+
+    if ( volume_info[fd].fd >= 0 )
+        DEBUGP( "dvd: data close %d fd=%d\n",fd,volume_info[fd].fd,0,0,0,0);
+    aclose(volume_info[fd].fd);
+
+}
+
+/*
+ * Release any dvdrws beyond a given position current position and reset
+ * dvdrw_count to dvdrw_current to indicate EOM.
+ */
+
+static void
+dvdrw_release(fd)
+int fd;
+{
+    int position;
+    char *dvdrwname;
+    int pos;
+    char number[NUM_STR_SIZE];
+
+    /*
+     * If the current dvdrw is open, release everything beyond it.
+     * If it is not open, release everything from current.
+     */
+    if (volume_info[fd].fd >= 0) {
+        position = volume_info[fd].dvdrw_current + 1;
+    } else {
+        position = volume_info[fd].dvdrw_current;
+    }
+    DEBUGP( "dvd: release %d from %d to %d\n",fd,position,volume_info[fd].dvdrw_count-1,0,0,0);
+    for (pos = position; pos < volume_info[fd].dvdrw_count; pos++) {
+        amtable_alloc((void **)&volume_info[fd].fi,
+                      &volume_info[fd].fi_limit,
+                      sizeof(*volume_info[fd].fi),
+                      pos + 1,
+                      10,
+                      NULL);
+        if (volume_info[fd].fi[pos].name != NULL) {
+            snprintf(number, sizeof(number), "%05d", pos);
+            dvdrwname = vstralloc( volume_info[fd].isRplus ?
+                                   volume_info[fd].wrbasename:
+                                   volume_info[fd].basename,
+                                  number,
+                                  DATA_INDICATOR,
+                                  volume_info[fd].fi[pos].name,
+                                  NULL);
+            DEBUGP( "dvd: release %d remove %s\n",fd,dvdrwname,0,0,0,0);
+            unlink(dvdrwname);
+            amfree(dvdrwname);
+            amfree(volume_info[fd].fi[pos].name);
+        }
+    }
+    volume_info[fd].dvdrw_count = position;
+}
+
+static int isRplus;
+
+static char *devname( char *name )
+{
+    char *retval ;
+    isRplus = 0;
+    retval = strchr(name,':');
+    if ( retval )
+    {
+         retval++ ;
+         if ( !strncmp(retval+strlen(retval)-3,"ONE",3))
+         {
+             /* only R+ : ONE write */
+             isRplus = 1;
+         }
+    }
+    return retval;
+}
+
+static int
+is_space_or_tab (char c) {
+	return (c == ' ' || c == '\t');
+}
+
+static char *
+skip_spaces(char *s) {
+	while (is_space_or_tab(*s))
+		s++;
+	return s;
+}
+
+static char *
+skip_nonspaces(char *s) {
+	while (*s && !is_space_or_tab(*s))
+		s++;
+	return s;
+}
+
+static int check_fstab(char *dirname,char *devname) 
+{
+	char buf[4096], *s, *name, slash;
+    int slen, retval;
+    FILE *fp = fopen("/etc/fstab","r");
+
+    if ( ! fp )
+        return -1;
+
+    slen = strlen(dirname) - 1;
+    slash = dirname[slen];
+    dirname[slen] = '\0';
+    DEBUGP( "check_fstab: dir %s dev %s\n",dirname,devname,0,0,0,0);
+
+	retval = -1;
+    while (fgets (buf, sizeof(buf), fp) != NULL )
+    {
+        if ( buf[0] == '#' || buf[0] == '\0' )
+            continue;
+		s = skip_spaces(buf);
+        name = s;
+        s = skip_nonspaces(s);
+        *s++ = '\0';
+        if ( strcmp(name, devname))
+            continue;
+        s = skip_spaces(s);
+        name = s;
+        s = skip_nonspaces(s);
+        *s = '\0';
+        if ( !strcmp(name, dirname))
+        {
+            retval = 0;
+            break;
+        }
+    }
+    dirname[slen] = slash;
+    fclose(fp);
+    return retval;
+}
+
+/*
+ * The normal interface routines ...
+ */
+
+int
+dvdrw_tape_open(dvdrwname, flags, mask)
+char *dvdrwname;
+int flags;
+int mask;
+{
+    int fd = -1;
+    int save_errno;
+    struct stat sbuf;
+    char *info_dvdrw = NULL;
+    char *dev_dvdrw = NULL;
+
+    DEBUGP( "\ndvd: open %s flags=%d mask=0%o ",dvdrwname, flags, mask,0,0,0);
+    dev_dvdrw = devname(dvdrwname);
+    if ( dev_dvdrw )
+        *(dev_dvdrw-1) = '\0';
+    
+    DEBUGP( "cachedir = %s, device = %s\n",dvdrwname,dev_dvdrw?dev_dvdrw:"-",0,0,0,0);
+
+    /*
+     * Use only O_RDONLY and O_RDWR.
+     */
+    if ((flags & 3) != O_RDONLY) {
+        flags &= ~3;
+        flags |= O_RDWR;
+    }
+
+    /*
+     * If the caller did not set O_CREAT (and thus, pass a mask
+     * parameter), we may still end up creating data dvdrws and need a
+     * "reasonable" value.  Pick a "tight" value on the "better safe
+     * than sorry" theory.
+     */
+    if ((flags & O_CREAT) == 0) {
+        mask = 0600;
+    }
+
+    /*
+     * Open/create the info dvdrw for this "tape".
+     */
+    info_dvdrw = stralloc2(dvdrwname, "/info");
+    if ((fd = open(info_dvdrw, O_RDWR|O_CREAT, 0600)) < 0) {
+        goto common_exit;
+    }
+
+    /*
+     * Create the internal info structure for this "tape".
+     */
+    amtable_alloc((void **)&volume_info,
+                  &open_count,
+                  sizeof(*volume_info),
+                  fd + 1,
+                  10,
+                  NULL);
+    volume_info[fd].flags = flags;
+    volume_info[fd].mask = mask;
+    volume_info[fd].dvdrw_count = 0;
+    volume_info[fd].dvdrw_current = 0;
+    volume_info[fd].fd = -1;
+    volume_info[fd].is_online = 0;		/* true when .../data found */
+    volume_info[fd].at_bof = 1;			/* by definition */
+    volume_info[fd].at_eof = 0;			/* do not know yet */
+    volume_info[fd].at_eom = 0;			/* may get reset below */
+    volume_info[fd].last_operation_write = 0;
+    volume_info[fd].must_burn = 0;
+    volume_info[fd].amount_written = 0;
+    volume_info[fd].isRplus = isRplus;
+    volume_info[fd].isNew   = 0;
+
+    /*
+     * Save the base directory name and see if we are "online".
+     */
+    volume_info[fd].basename   = stralloc2(dvdrwname, "/data/");
+    volume_info[fd].wrbasename = stralloc2(dvdrwname, "/burn/");
+
+    if ( stat(volume_info[fd].wrbasename, &sbuf))
+    {
+        mkdir(volume_info[fd].wrbasename,0750);
+    }
+    if ( stat(volume_info[fd].wrbasename, &sbuf) || !S_ISDIR(sbuf.st_mode ))
+    {
+        if ( dev_dvdrw )
+            *--dev_dvdrw = ':';
+        dev_dvdrw = NULL ;
+    }
+    volume_info[fd].drvname = NULL;
+    if (dev_dvdrw)
+    {
+        /* readlink device */
+        char buffer [512], *p;
+        memset(buffer, 0, 512);
+        if (( p = strrchr(dev_dvdrw,'/')))
+        {
+            strncpy(buffer,dev_dvdrw,p-dev_dvdrw+1);
+            p = buffer + (p-dev_dvdrw+1);
+        }
+        else
+        {
+            p = buffer;
+        }
+        if (readlink(dev_dvdrw, p, 512) != -1)
+        {
+            volume_info[fd].drvname = stralloc(buffer);
+        }
+        else if ( errno == EINVAL )
+        {
+            volume_info[fd].drvname = stralloc(dev_dvdrw);
+        }
+        if ( volume_info[fd].drvname )
+        {
+            if(stat(volume_info[fd].drvname, &sbuf) || !S_ISBLK(sbuf.st_mode ))
+            {
+                fprintf(stderr, "device = %s is not a block device\n", volume_info[fd].drvname);
+                volume_info[fd].drvname = NULL;
+            }
+            else
+            {
+                volume_info[fd].dev = sbuf.st_rdev;    
+                DEBUGP( "devno of dvd=0x%04x\n",volume_info[fd].dev,0,0,0,0,0);
+                if ( ! stat(volume_info[fd].basename, &sbuf) &&
+                     sbuf.st_dev == volume_info[fd].dev )
+                {
+                    do_mount(fd,0);
+                }
+            }
+        }
+    }
+
+    /* check also fstab */
+
+    if (!volume_info[fd].drvname || 
+        check_fstab(volume_info[fd].basename,dev_dvdrw) ||
+        check_online(fd))
+    {
+        save_errno = errno;
+        aclose(fd);
+        fd = -1;
+        amfree(volume_info[fd].drvname);
+        amfree(volume_info[fd].wrbasename);
+        amfree(volume_info[fd].basename);
+        errno = save_errno;
+        goto common_exit;
+    }
+
+ common_exit:
+
+    if ( dev_dvdrw )
+        *--dev_dvdrw = ':';
+
+    amfree(info_dvdrw);
+
+    /*
+     * Return the info dvdrw descriptor as the unique descriptor for
+     * this open.
+     */
+    return fd;
+}
+
+ssize_t
+dvdrw_tapefd_read(fd, buffer, count)
+int fd;
+void *buffer;
+size_t count;
+{
+    int result;
+    int dvdrw_fd;
+    int pos;
+    int record_size;
+    int read_size;
+
+    DEBUGP("dvd: read %d buf=%p len=%d\n",fd, buffer, count,0,0,0);
+    /*
+     * Make sure we are online.
+     */
+    if ((result = check_online(fd)) != 0) {
+        return result;
+    }
+    if (! volume_info[fd].is_online) {
+        errno = EIO;
+        return -1;
+    }
+
+    /*
+     * Do not allow any more reads after we find EOF.
+     */
+    if (volume_info[fd].at_eof) {
+        errno = EIO;
+        return -1;
+    }
+
+    /*
+     * If we are at EOM, set EOF and return a zero length result.
+     */
+    if (volume_info[fd].at_eom) {
+        volume_info[fd].at_eof = 1;
+        return 0;
+    }
+
+    /*
+     * Open the dvdrw, if needed.
+     */
+    if ((dvdrw_fd = dvdrw_open(fd)) < 0) {
+        return dvdrw_fd;
+    }
+
+    read_size = count;
+
+    /*
+     * Read the data.  If we ask for less than the record size, skip to
+     * the next record boundary.
+     */
+    result = read(dvdrw_fd, buffer, read_size);
+    if (result > 0) {
+        volume_info[fd].at_bof = 0;
+    } else if (result == 0) {
+        volume_info[fd].at_eof = 1;
+    }
+    return result;
+}
+
+ssize_t
+dvdrw_tapefd_write(fd, buffer, count)
+int fd;
+const void *buffer;
+size_t count;
+{
+    int dvdrw_fd;
+    int write_count = count;
+    long length;
+    long kbytes_left;
+    int result;
+    int pos;
+
+    DEBUGP( "dvd: write %d buf=%p len=%d\n",fd, buffer, count,0,0,0);
+    /*
+     * Make sure we are online.
+     */
+    if ((result = check_online(fd)) != 0) {
+        return result;
+    }
+    if (! volume_info[fd].is_online) {
+        errno = EIO;
+        return -1;
+    }
+
+    /*
+     * Check for write access first.
+     */
+    if ((volume_info[fd].flags & 3) == O_RDONLY) {
+        errno = EBADF;
+        return -1;
+    }
+
+    /*
+     * Special case: allow negative buffer size.
+     */
+    if (write_count <= 0) {
+        return 0;				/* special case */
+    }
+
+    /*
+     * If we are at EOM, it takes precedence over EOF.
+     */
+    if (volume_info[fd].at_eom) {
+        volume_info[fd].at_eof = 0;
+    }
+
+#if 0 /*JJ*/
+    /*
+     * Writes are only allowed at BOF and EOM.
+     */
+    if (! (volume_info[fd].at_bof || volume_info[fd].at_eom)) {
+        errno = EIO;
+        return -1;
+    }
+#endif /*JJ*/
+
+    /*
+     * Writes are only allowed if we are not at EOF.
+     */
+    if (volume_info[fd].at_eof) {
+        errno = EIO;
+        return -1;
+    }
+
+    /*
+     * Open the dvdrw, if needed.
+     */
+    if((dvdrw_fd = volume_info[fd].fd) < 0) {
+        dvdrw_release(fd);
+        if ((dvdrw_fd = dvdrw_open(fd)) < 0) {
+            return dvdrw_fd;
+        }
+    }
+
+    /*
+     * Truncate the write if requested and return a simulated ENOSPC.
+     */
+    if ((length = tapefd_getinfo_length(fd)) > 0) {
+        kbytes_left = length - volume_info[fd].amount_written;
+        if (write_count / 1024 > kbytes_left) {
+            write_count = kbytes_left * 1024;
+        }
+    }
+    volume_info[fd].amount_written += (write_count + 1023) / 1024;
+    if (write_count <= 0) {
+        volume_info[fd].at_bof = 0;
+        volume_info[fd].at_eom = 1;
+        errno = ENOSPC;
+        return -1;
+    }
+
+    /*
+     * Do the write and truncate the dvdrw, if needed.  Checking for
+     * last_operation_write is an optimization so we only truncate
+     * once.
+     */
+    if (! volume_info[fd].last_operation_write) {
+        (void)ftruncate(dvdrw_fd, lseek(dvdrw_fd, 0, SEEK_CUR));
+        volume_info[fd].at_bof = 0;
+        volume_info[fd].at_eom = 1;
+    }
+    result = write(dvdrw_fd, buffer, write_count);
+    if (result >= 0) {
+        volume_info[fd].last_operation_write = 1;
+        volume_info[fd].must_burn = 1;
+    }
+
+    return result;
+}
+
+int
+dvdrw_tapefd_close(fd)
+int fd;
+{
+    int pos;
+    int save_errno;
+    char *line;
+    int len,retval;
+    struct stat sbuf;
+    char number[NUM_STR_SIZE];
+    int result,mustburn;
+
+    DEBUGP( "dvd: try to close %d must_burn %d current=%d count=%d file=%s\n",fd,
+            volume_info[fd].must_burn,
+            volume_info[fd].dvdrw_current,
+            volume_info[fd].dvdrw_count,
+            volume_info[fd].fi ? volume_info[fd].fi[volume_info[fd].dvdrw_current].name:"-",0);
+
+    mustburn = 0;
+    if ( volume_info[fd].must_burn && volume_info[fd].fi &&
+         volume_info[fd].fi[volume_info[fd].dvdrw_current].name && 
+         !strcmp(volume_info[fd].fi[volume_info[fd].dvdrw_current].name,"TAPEEND"))
+    {
+    DEBUGP( "dvd: check burn %d must_burn %d isRplus %d current=%d count=%d file=%s\n",fd,
+            volume_info[fd].must_burn,
+            volume_info[fd].isRplus,
+            volume_info[fd].dvdrw_current,
+            volume_info[fd].dvdrw_count,
+            volume_info[fd].fi[volume_info[fd].dvdrw_current].name);
+
+        /* R+ nur wenn mehr als 2 Dateien (wg Label) */
+        if ( !volume_info[fd].isRplus ||
+             volume_info[fd].dvdrw_count > 2 )
+            mustburn = 1;
+    }
+
+    /*
+     * If our last operation was a write, write a tapemark.
+     */
+    if (volume_info[fd].last_operation_write) {
+        if ((result = dvdrw_tapefd_weof(fd, 1)) != 0) {
+            return result;
+        }
+    }
+
+
+    /*
+     * If we are not at BOF, fsf to the next dvdrw unless we
+     * are already at end of tape.
+     */
+    if (! volume_info[fd].at_bof && ! volume_info[fd].at_eom) {
+        if ((result = dvdrw_tapefd_fsf(fd, 1)) != 0) {
+            return result;
+        }
+    }
+
+    /*
+     * Close the dvdrw if it is still open.
+     */
+    dvdrw_close(fd);
+
+    DEBUGP( "dvd: real close %d mustburn=%d\n",fd,mustburn,0,0,0,0);
+
+    /*
+     * Release the info structure areas.
+     */
+    for (pos = 0; pos < volume_info[fd].fi_limit; pos++) {
+        amfree(volume_info[fd].fi[pos].name);
+    }
+    amtable_free((void **)&volume_info[fd].fi, &volume_info[fd].fi_limit);
+    volume_info[fd].dvdrw_count = 0;
+
+    /*
+     * Update the status dvdrw if we were online.
+     */
+    if (volume_info[fd].is_online) {
+        if (lseek(fd, 0, SEEK_SET) != 0) {
+            save_errno = errno;
+            aclose(fd);
+            errno = save_errno;
+            retval = -1;
+            goto myret;
+        }
+        if (ftruncate(fd, 0) != 0) {
+            save_errno = errno;
+            aclose(fd);
+            errno = save_errno;
+            retval = -1;
+            goto myret;
+        }
+        if ( mustburn )
+            volume_info[fd].dvdrw_current = 0;
+
+        snprintf(number, sizeof(number),
+                    "%d", volume_info[fd].dvdrw_current);
+        line = vstralloc("position ", number, "\n", NULL);
+        len = strlen(line);
+        result = write(fd, line, len);
+        amfree(line);
+        if (result != len) {
+            if (result >= 0) {
+                errno = ENOSPC;
+            }
+            save_errno = errno;
+            aclose(fd);
+            errno = save_errno;
+            retval = -1;
+            goto myret;
+        }
+    }
+
+    areads_relbuf(fd);
+    retval = close(fd);
+
+ myret:
+    if ( ! stat(volume_info[fd].basename, &sbuf) &&
+         sbuf.st_dev == volume_info[fd].dev )
+    {
+        do_mount(fd,0);
+    }
+    if ( mustburn )
+    {
+        result = dvdrw_burn( fd );
+        if ( !retval )
+            retval = result;
+    }
+    amfree(volume_info[fd].drvname);
+    amfree(volume_info[fd].wrbasename);
+    amfree(volume_info[fd].basename);
+
+    return retval;
+}
+
+void
+dvdrw_tapefd_resetofs(fd)
+int fd;
+{
+    DEBUGP( "dvd: resetofs %d\n",fd,0,0,0,0,0);
+}
+
+int
+dvdrw_tapefd_status(fd, stat)
+int fd;
+struct am_mt_status *stat;
+{
+    int result;
+
+    DEBUGP( "dvd: status %d\n",fd,0,0,0,0,0);
+    /*
+     * See if we are online.
+     */
+    if ((result = check_online(fd)) != 0) {
+        return result;
+    }
+    memset((void *)stat, 0, sizeof(*stat));
+    stat->online_valid = 1;
+    stat->online = volume_info[fd].is_online;
+    DEBUGP( "dvd: status valid=1 online=%d\n",fd,stat->online,0,0,0,0);
+    return 0;
+}
+
+int
+dvdrw_tape_stat(dvdrwname, buf)
+char *dvdrwname;
+struct stat *buf;
+{
+    char *dev = devname(dvdrwname);
+    if ( dev )
+        return stat(dev, buf);
+    else
+        return -1;
+}
+
+int
+dvdrw_tape_access(dvdrwname, mode)
+char *dvdrwname;
+int mode;
+{
+    char *dev = devname(dvdrwname);
+    if ( dev )
+        return access(dev, mode);
+    else
+        return -1;
+}
+
+int
+dvdrw_tapefd_rewind(fd)
+int fd;
+{
+    int result = 0;
+
+    DEBUGP( "dvd: rewind %d current=%d count=%d\n",fd,volume_info[fd].dvdrw_current,
+            volume_info[fd].dvdrw_count,0,0,0);
+    /*
+     * Make sure we are online.
+     */
+    if ((result = check_online(fd)) != 0) {
+        return result;
+    }
+    if (! volume_info[fd].is_online) {
+#if 1
+        result = 0;
+#else
+        errno = EIO;
+        return -1;
+#endif
+    }
+
+
+    /*
+     * If our last operation was a write, write a tapemark.
+     */
+    if (volume_info[fd].last_operation_write) {
+        if ((result = dvdrw_tapefd_weof(fd, 1)) != 0) {
+            return result;
+        }
+    }
+
+    /*
+     * Close the dvdrw if it is still open.
+     */
+    dvdrw_close(fd);
+
+    /*
+     * Adjust the position and reset the flags.
+     */
+    volume_info[fd].dvdrw_current = 0;
+
+    volume_info[fd].at_bof = 1;
+    volume_info[fd].at_eof = 0;
+    volume_info[fd].at_eom
+        = (volume_info[fd].dvdrw_current >= volume_info[fd].dvdrw_count);
+    volume_info[fd].last_operation_write = 0;
+    volume_info[fd].amount_written = 0;
+    if ( volume_info[fd].must_burn )
+    {
+        result = dvdrw_burn( fd );
+    }
+
+    return result;
+}
+
+int
+dvdrw_tapefd_unload(fd)
+int fd;
+{
+    int result;
+
+    DEBUGP( "dvd: unload %d\n",fd,0,0,0,0,0);
+    /*
+     * Make sure we are online.
+     */
+    if ((result = check_online(fd)) != 0) {
+        return result;
+    }
+    if (! volume_info[fd].is_online) {
+        errno = EIO;
+        return -1;
+    }
+
+    dvdrw_tapefd_rewind(fd);
+    return 0;
+}
+
+int
+dvdrw_tapefd_fsf(fd, count)
+int fd, count;
+{
+    int result = 0;
+
+    DEBUGP( "dvd: fsf %d count=%d\n",fd,count,0,0,0,0);
+   /*
+     * Make sure we are online.
+     */
+    if ((result = check_online(fd)) != 0) {
+        return result;
+    }
+    if (! volume_info[fd].is_online) {
+        errno = EIO;
+        return -1;
+    }
+
+    /*
+     * If our last operation was a write and we are going to move
+     * backward, write a tapemark.
+     */
+    if (volume_info[fd].last_operation_write && count < 0) {
+        if ((result = dvdrw_tapefd_weof(fd, 1)) != 0) {
+            errno = EIO;
+            return -1;
+        }
+    }
+
+    /*
+     * Close the dvdrw if it is still open.
+     */
+    dvdrw_close(fd);
+
+    /*
+     * If we are at EOM and moving backward, adjust the count to go
+     * one more dvdrw.
+     */
+    if (volume_info[fd].at_eom && count < 0) {
+        count--;
+    }
+
+    /*
+     * Adjust the position and return an error if we go beyond either
+     * end of the tape.
+     */
+    volume_info[fd].dvdrw_current += count;
+    if (volume_info[fd].dvdrw_current > volume_info[fd].dvdrw_count) {
+        volume_info[fd].dvdrw_current = volume_info[fd].dvdrw_count;
+        errno = EIO;
+        result = -1;
+    } else if (volume_info[fd].dvdrw_current < 0) {
+        volume_info[fd].dvdrw_current = 0;
+        errno = EIO;
+        result = -1;
+    }
+
+    /*
+     * Set BOF to true so we can write.  Set to EOF to false if the
+     * fsf succeeded or if it failed but we were moving backward (and
+     * thus we are at beginning of tape), otherwise set it to true so
+     * a subsequent read will fail.  Set EOM to whatever is right.
+     * Reset amount_written if we ended up back at BOM.
+     */
+    volume_info[fd].at_bof = 1;
+    if (result == 0 || count < 0) {
+        volume_info[fd].at_eof = 0;
+    } else {
+        volume_info[fd].at_eof = 1;
+    }
+    volume_info[fd].at_eom
+        = (volume_info[fd].dvdrw_current >= volume_info[fd].dvdrw_count);
+    volume_info[fd].last_operation_write = 0;
+    if (volume_info[fd].dvdrw_current == 0) {
+        volume_info[fd].amount_written = 0;
+    }
+
+    return result;
+}
+
+int
+dvdrw_tapefd_weof(fd, count)
+int fd, count;
+{
+    int dvdrw_fd;
+    int result = 0;
+    char *save_host;
+    char *save_disk;
+    int save_level;
+    int save_errno;
+
+    DEBUGP( "dvd: weof %d count=%d\n",fd,count,0,0,0,0);
+    /*
+     * Make sure we are online.
+     */
+    if ((result = check_online(fd)) != 0) {
+        return result;
+    }
+    if (! volume_info[fd].is_online) {
+        errno = EIO;
+        return -1;
+    }
+
+    /*
+     * Check for write access first.
+     */
+    if ((volume_info[fd].flags & 3) == O_RDONLY) {
+        errno = EACCES;
+        return -1;
+    }
+
+    /*
+     * Special case: allow a zero count.
+     */
+    if (count == 0) {
+        return 0;				/* special case */
+    }
+
+    /*
+     * Disallow negative count.
+     */
+    if (count < 0) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    /*
+     * Close out the current dvdrw if open.
+     */
+    if ((dvdrw_fd = volume_info[fd].fd) >= 0) {
+        (void)ftruncate(dvdrw_fd, lseek(dvdrw_fd, 0, SEEK_CUR));
+        dvdrw_close(fd);
+        volume_info[fd].dvdrw_current++;
+        volume_info[fd].at_bof = 1;
+        volume_info[fd].at_eof = 0;
+        volume_info[fd].at_eom = 1;
+        volume_info[fd].last_operation_write = 0;
+        count--;
+    }
+
+    /*
+     * Release any data dvdrws from current through the end.
+     */
+    dvdrw_release(fd);
+
+    /*
+     * Save any labelling information in case we clobber it.
+     */
+    if ((save_host = tapefd_getinfo_host(fd)) != NULL) {
+        save_host = stralloc(save_host);
+    }
+    if ((save_disk = tapefd_getinfo_disk(fd)) != NULL) {
+        save_disk = stralloc(save_disk);
+    }
+    save_level = tapefd_getinfo_level(fd);
+
+    /*
+     * Add more tapemarks.
+     */
+    while (--count >= 0) {
+        if (dvdrw_open(fd) < 0) {
+            break;
+        }
+        dvdrw_close(fd);
+        volume_info[fd].dvdrw_current++;
+        volume_info[fd].dvdrw_count = volume_info[fd].dvdrw_current;
+        volume_info[fd].at_bof = 1;
+        volume_info[fd].at_eof = 0;
+        volume_info[fd].at_eom = 1;
+        volume_info[fd].last_operation_write = 0;
+
+        /*
+         * Only the first "dvdrw" terminated by an EOF gets the naming
+         * information from the caller.
+         */
+        tapefd_setinfo_host(fd, NULL);
+        tapefd_setinfo_disk(fd, NULL);
+        tapefd_setinfo_level(fd, -1);
+    }
+
+    /*
+     * Restore the labelling information.
+     */
+    save_errno = errno;
+    tapefd_setinfo_host(fd, save_host);
+    amfree(save_host);
+    tapefd_setinfo_disk(fd, save_disk);
+    amfree(save_disk);
+    tapefd_setinfo_level(fd, save_level);
+    errno = save_errno;
+
+    return result;
+}
+
+int
+dvdrw_tapefd_can_fork(fd)
+int fd;
+{
+    DEBUGP( "dvd: can_fork %d\n",fd,0,0,0,0,0);
+    return 0; /* 1 ?? */
+}
diff -Naur amanda-2.5.0/tape-src/output-dvdrw.h amanda-2.5.0.oden/tape-src/output-dvdrw.h
--- amanda-2.5.0/tape-src/output-dvdrw.h	1970-01-01 01:00:00.000000000 +0100
+++ amanda-2.5.0.oden/tape-src/output-dvdrw.h	2006-03-27 16:26:58.000000000 +0200
@@ -0,0 +1,26 @@
+
+/*
+ * $Id: output-dvdrw.h,v $
+ *
+ */
+
+#ifndef OUTPUT_DVDRW_H
+#define OUTPUT_DVDRW_H
+
+#include "amanda.h"
+
+extern int dvdrw_tape_access P((char *, int));
+extern int dvdrw_tape_open ();
+extern int dvdrw_tape_stat P((char *, struct stat *));
+extern int dvdrw_tapefd_close P((int));
+extern int dvdrw_tapefd_fsf P((int, int));
+extern ssize_t dvdrw_tapefd_read P((int, void *, size_t));
+extern int dvdrw_tapefd_rewind P((int));
+extern void dvdrw_tapefd_resetofs P((int));
+extern int dvdrw_tapefd_unload P((int));
+extern int dvdrw_tapefd_status P((int, struct am_mt_status *));
+extern int dvdrw_tapefd_weof P((int, int));
+extern ssize_t dvdrw_tapefd_write P((int, const void *, size_t));
+extern int dvdrw_tapefd_can_fork P((int));
+
+#endif /* OUTPUT_FILE_H */
diff -Naur amanda-2.5.0/tape-src/tapeio.c amanda-2.5.0.oden/tape-src/tapeio.c
--- amanda-2.5.0/tape-src/tapeio.c	2006-01-14 23:11:24.000000000 +0100
+++ amanda-2.5.0.oden/tape-src/tapeio.c	2006-03-27 16:26:58.000000000 +0200
@@ -46,6 +46,7 @@
 #include "output-null.h"
 #include "output-rait.h"
 #include "output-file.h"
+#include "output-dvdrw.h"
 
 static struct virtualtape {
     char *prefix;
@@ -86,6 +87,11 @@
 	file_tapefd_read, file_tapefd_rewind, file_tapefd_resetofs,
 	file_tapefd_unload, file_tapefd_status, file_tapefd_weof,
         file_tapefd_write, file_tapefd_can_fork },
+  {"dvdrw", dvdrw_tape_access, dvdrw_tape_open, dvdrw_tape_stat,
+	dvdrw_tapefd_close, dvdrw_tapefd_fsf,
+	dvdrw_tapefd_read, dvdrw_tapefd_rewind, dvdrw_tapefd_resetofs,
+	dvdrw_tapefd_unload, dvdrw_tapefd_status, dvdrw_tapefd_weof,
+        dvdrw_tapefd_write, dvdrw_tapefd_can_fork },
   {NULL,},
 };