CFLAGS="$(CFLAGS) -D_XOPEN_SOURCE_EXTENDED=1"
DISTVER=`uname -r`
TAPEDRIVE="/dev/rmt/0hnb"
+ PTHREAD_LIB="-lpthread"
;;
irix)
DISTVER=`uname -r`
CFLAGS="$(CFLAGS) -D_XOPEN_SOURCE_EXTENDED=1"
DISTVER=`uname -r`
TAPEDRIVE="/dev/rmt/0hnb"
+ PTHREAD_LIB="-lpthread"
;;
irix)
DISTVER=`uname -r`
- 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), ...
- 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.
-
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
/* 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" {
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;
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);
}
}
}
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();
backup(curline);
delchr(1, curline, sizeof(curline));
if (cp == 0) {
- t_char(' ');
+ t_char(' ');
t_char(0x8);
}
break;
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:
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 */
#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
*/
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;
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.
}
#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)) {
}
}
}
-#endif
/* Terminate any signature and send it to Storage daemon and the Director */
if (chksum.updated) {
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;
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 */
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;
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;
+}
* 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
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 \
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 \
# 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
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);
#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
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.
}
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);
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)
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;
}
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"),
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;
}
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;
/* 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 {
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");
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;
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 */
#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;
}
--- /dev/null
+/*
+ *
+ * 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;
+}
/* #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
/* #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 */