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,}, };