From: Kern Sibbald Date: Wed, 18 Jun 2003 13:43:48 +0000 (+0000) Subject: New var.c file + implement multiple simultaneous jobs X-Git-Tag: Release-1.31~67 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=c62398d50d0f5afb873192adfef6a330bb260e3e;p=bacula%2Fbacula New var.c file + implement multiple simultaneous jobs git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@589 91ce42f0-d328-0410-95d8-f526ca767f89 --- diff --git a/bacula/kernstodo b/bacula/kernstodo index 03f966506b..8e1230ed5a 100644 --- a/bacula/kernstodo +++ b/bacula/kernstodo @@ -1,5 +1,5 @@ Kern's ToDo List - 14 June 2003 + 18 June 2003 Documentation to do: (any release a little bit at a time) - Document running a test version. @@ -32,24 +32,16 @@ Testing to do: (painful) - Figure out how to use ssh or stunnel to protect Bacula communications. For 1.31 release: -- Fix first block number after label to be zero instead of 1 (reset after label). -- Grep for Backup OK in regression script. -- Do NOT reuse same JobId if tape written. - Move JobFiles and JobBytes to SD rather than FD -- more correct. - Add client name to cram-md5 challenge so Director can immediately verify if it is the correct client. - Use runbeforejob to unload, then reload a volume previously used, then the next job run gets an error reading the drive. -- Implement non-blocking writes and bsock->terminate in heartbeat thread, - or set it in status.c cancel. -- Add restore to specific date. - lstat() is not going to work on Win32 for testing date. - Implement a Recycle command - Something is not right in last block of fill command. - Implement List Volume Job=xxx or List scheduled volumes or Status Director -- Instrument use_count on DEVICE packets and ensure that the device is - being close()ed at the appropriate time. - Check if Incremental is working correctly when it looks for the previous Job (Phil's problem). - Add next Volume to be used to status output. @@ -983,3 +975,13 @@ Done: (see kernsdone for more) - That restoring a hard link that already exists works correctly. Same for soft link. - Make Pool resource handle Counter resources. +- Fix first block number after label to be zero instead of 1 (reset after label). +- Grep for Backup OK in regression script. +- Do NOT reuse same JobId if tape written. +- Implement non-blocking writes and bsock->terminate in heartbeat + thread, or set it in status.c cancel (used pthread_kill() instead of + non-blocking I/O. +- Add restore to specific date. +- Instrument use_count on DEVICE packets and ensure that the device is + being close()ed at the appropriate time. + diff --git a/bacula/src/dird/catreq.c b/bacula/src/dird/catreq.c index 6c091d90fe..e36bc681a4 100644 --- a/bacula/src/dird/catreq.c +++ b/bacula/src/dird/catreq.c @@ -168,7 +168,7 @@ next_volume: edit_uint64(mr.MaxVolBytes, ed2), edit_uint64(mr.VolCapacityBytes, ed3), mr.VolStatus, mr.Slot, mr.MaxVolJobs, mr.MaxVolFiles); - Dmsg1(200, "Find media: %s", bs->msg); + Dmsg2(100, "Find media for %s: %s", jcr->Job, bs->msg); } else { bnet_fsend(bs, "1901 No Media.\n"); } @@ -222,7 +222,7 @@ next_volume: edit_uint64(mr.MaxVolBytes, ed2), edit_uint64(mr.VolCapacityBytes, ed3), mr.VolStatus, mr.Slot, mr.MaxVolJobs, mr.MaxVolFiles); - Dmsg1(200, "Vol Info: %s", bs->msg); + Dmsg2(100, "Vol Info for %s: %s", jcr->Job, bs->msg); } else { /* Not suitable volume */ bnet_fsend(bs, "1998 Volume \"%s\" %s.\n", diff --git a/bacula/src/dird/getmsg.c b/bacula/src/dird/getmsg.c index 0e5eceae06..b2309f3709 100644 --- a/bacula/src/dird/getmsg.c +++ b/bacula/src/dird/getmsg.c @@ -113,7 +113,7 @@ int bget_dirmsg(BSOCK *bs) /* Handle normal data */ - if (B_ISDIGIT(bs->msg[0])) { /* response? */ + if (n > 0 && B_ISDIGIT(bs->msg[0])) { /* response? */ return n; /* yes, return it */ } diff --git a/bacula/src/dird/msgchan.c b/bacula/src/dird/msgchan.c index b227ffe584..df980e02b2 100644 --- a/bacula/src/dird/msgchan.c +++ b/bacula/src/dird/msgchan.c @@ -183,11 +183,16 @@ int start_storage_daemon_message_thread(JCR *jcr) P(jcr->mutex); jcr->use_count++; /* mark in use by msg thread */ + jcr->sd_msg_thread_done = false; + jcr->SD_msg_chan = 0; V(jcr->mutex); if ((status=pthread_create(&thid, NULL, msg_thread, (void *)jcr)) != 0) { Jmsg1(jcr, M_ABORT, 0, _("Cannot create message thread: %s\n"), strerror(status)); } - jcr->SD_msg_chan = thid; + /* Wait for thread to start */ + while (jcr->SD_msg_chan == 0) { + bmicrosleep(0, 50); + } return 1; } @@ -218,11 +223,11 @@ static void *msg_thread(void *arg) uint64_t JobBytes; int stat; + pthread_detach(pthread_self()); + jcr->SD_msg_chan = pthread_self(); pthread_cleanup_push(msg_thread_cleanup, arg); - jcr->sd_msg_thread_done = false; Dmsg0(200, "msg_thread\n"); sd = jcr->store_bsock; - pthread_detach(pthread_self()); /* Read the Storage daemon's output. */ @@ -235,8 +240,8 @@ static void *msg_thread(void *arg) if (sscanf(sd->msg, Job_end, &Job, &JobStatus, &JobFiles, &JobBytes) == 4) { jcr->SDJobStatus = JobStatus; /* termination status */ - jcr->JobFiles = JobFiles; - jcr->JobBytes = JobBytes; + jcr->SDJobFiles = JobFiles; + jcr->SDJobBytes = JobBytes; break; } if (sscanf(sd->msg, Job_status, &Job, &JobStatus) == 2) { diff --git a/bacula/src/dird/scheduler.c b/bacula/src/dird/scheduler.c index 9ab02e14ef..8ec070d77a 100644 --- a/bacula/src/dird/scheduler.c +++ b/bacula/src/dird/scheduler.c @@ -118,7 +118,7 @@ JCR *wait_for_next_job(char *job_to_run) #ifdef xxxx_debug if (runtime > 0) { bstrftime(dt, sizeof(dt), runjobs[i].runtime); - Dmsg2(000, " %s run %s\n", dt, runjobs[i].job->hdr.name); + Dmsg2(100, " %s run %s\n", dt, runjobs[i].job->hdr.name); } #endif } diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index a5f365c51a..b4fdf052e3 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -894,6 +894,9 @@ int response(JCR *jcr, BSOCK *sd, char *resp, char *cmd) return 1; } } + if (job_canceled(jcr)) { + return 0; /* if canceled avoid useless error messages */ + } if (is_bnet_error(sd)) { Jmsg2(jcr, M_FATAL, 0, _("Comm error with SD. bad response to %s. ERR=%s\n"), cmd, bnet_strerror(sd)); diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index 1f9f0693e2..2add1749c9 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -137,6 +137,9 @@ struct JCR { FILESET *fileset; /* FileSet resource */ CAT *catalog; /* Catalog resource */ MSGS *messages; /* Default message handler */ + uint32_t SDJobFiles; /* Number of files written, this job */ + uint64_t SDJobBytes; /* Number of bytes processed this job */ + uint32_t SDErrors; /* Number of non-fatal errors */ volatile int SDJobStatus; /* Storage Job Status */ volatile int FDJobStatus; /* File daemon Job Status */ B_DB *db; /* database pointer */ @@ -200,8 +203,8 @@ struct JCR { POOLMEM *media_type; /* media type */ POOLMEM *dev_name; /* device name */ VOL_LIST *VolList; /* list to read */ - long NumVolumes; /* number of volumes used */ - long CurVolume; /* current volume number */ + int32_t NumVolumes; /* number of volumes used */ + int32_t CurVolume; /* current volume number */ int spool_attributes; /* set if spooling attributes */ int no_attributes; /* set if no attributes wanted */ int label_status; /* device volume label status */ @@ -210,11 +213,16 @@ struct JCR { DEV_RECORD rec; /* Read/Write record */ long Ticket; /* ticket for this job */ uint32_t VolFirstIndex; /* First file index this Volume */ + uint32_t VolLastIndex; /* Last file index this Volume */ uint32_t FileIndex; /* Current File Index */ uint32_t EndFile; /* End file written */ uint32_t StartFile; /* Start write file */ uint32_t StartBlock; /* Start write block */ uint32_t EndBlock; /* Ending block written */ + bool NewVol; /* set when new Volume mounted */ + bool WroteVol; /* set when Volume written */ + int CurVol; /* Current Volume count */ + uint32_t FileId; /* Last file id inserted */ /* Parmaters for Open Read Session */ diff --git a/bacula/src/lib/bnet.c b/bacula/src/lib/bnet.c index 8ee57ddffd..4e62c4aa9e 100644 --- a/bacula/src/lib/bnet.c +++ b/bacula/src/lib/bnet.c @@ -334,7 +334,7 @@ bnet_send(BSOCK *bsock) bsock->b_errno = errno; } if (rc < 0) { - if (!bsock->suppress_error_msgs) { + if (!bsock->suppress_error_msgs && !bsock->timed_out) { Jmsg4(bsock->jcr, M_ERROR, 0, _("Write error sending to %s:%s:%d: ERR=%s\n"), bsock->who, bsock->host, bsock->port, bnet_strerror(bsock)); } diff --git a/bacula/src/lib/var.c b/bacula/src/lib/var.c new file mode 100644 index 0000000000..ae777fbea8 --- /dev/null +++ b/bacula/src/lib/var.c @@ -0,0 +1,2719 @@ +/* +** OSSP var - Variable Expansion +** Copyright (c) 2001-2002 Ralf S. Engelschall +** Copyright (c) 2001-2002 The OSSP Project (http://www.ossp.org/) +** Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/) +** +** This file is part of OSSP var, a variable expansion +** library which can be found at http://www.ossp.org/pkg/lib/var/. +** +** Permission to use, copy, modify, and distribute this software for +** any purpose with or without fee is hereby granted, provided that +** the above copyright notice and this permission notice appear in all +** copies. +** +** For disclaimer see below. +*/ +/* + * Adapted by Kern Sibbald to Bacula June 2003 + */ +/* + Copyright (C) 2000-2003 Kern Sibbald and John Walker + + 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" +#if defined(HAVE_PCREPOSIX) +# include +#else +# include +#endif +#include "var.h" + +/* support for OSSP ex based exception throwing */ +#ifdef WITH_EX +#include "ex.h" +#define VAR_RC(rv) \ + ( (rv) != VAR_OK && (ex_catching && !ex_shielding) \ + ? (ex_throw(var_id, NULL, (rv)), (rv)) : (rv) ) +#else +#define VAR_RC(rv) (rv) +#endif /* WITH_EX */ + +#ifndef EOS +#define EOS '\0' +#endif + +/* +** +** ==== INTERNAL DATA STRUCTURES ==== +** +*/ + +typedef char char_class_t[256]; /* 256 == 2 ^ sizeof(unsigned char)*8 */ + +/* the external context structure */ +struct var_st { + var_syntax_t syntax; + char_class_t syntax_nameclass; + var_cb_value_t cb_value_fct; + void *cb_value_ctx; + var_cb_operation_t cb_operation_fct; + void *cb_operation_ctx; +}; + +/* the internal expansion context structure */ +struct var_parse_st { + struct var_parse_st *lower; + int force_expand; + int rel_lookup_flag; + int rel_lookup_cnt; + int index_this; +}; +typedef struct var_parse_st var_parse_t; + +/* the default syntax configuration */ +static const var_syntax_t var_syntax_default = { + '\\', /* escape */ + '$', /* delim_init */ + '{', /* delim_open */ + '}', /* delim_close */ + '[', /* index_open */ + ']', /* index_close */ + '#', /* index_mark */ + "a-zA-Z0-9_" /* name_chars */ +}; + +/* +** +** ==== FORMATTING FUNCTIONS ==== +** +*/ + +/* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */ +static int +var_mvxprintf( + int (*output)(void *ctx, const char *buffer, int bufsize), void *ctx, + const char *format, va_list ap) +{ + /* sufficient integer buffer: x log_10(2) + safety */ + char ibuf[((sizeof(int)*8)/3)+10]; + char *cp; + char c; + int d; + int n; + int bytes; + + if (format == NULL || ap == NULL) + return -1; + bytes = 0; + while (*format != '\0') { + if (*format == '%') { + c = *(format+1); + if (c == '%') { + /* expand "%%" */ + cp = &c; + n = sizeof(char); + } + else if (c == 'c') { + /* expand "%c" */ + c = (char)va_arg(ap, int); + cp = &c; + n = sizeof(char); + } + else if (c == 's') { + /* expand "%s" */ + if ((cp = (char *)va_arg(ap, char *)) == NULL) + cp = "(null)"; + n = strlen(cp); + } + else if (c == 'd') { + /* expand "%d" */ + d = (int)va_arg(ap, int); + bsnprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */ + cp = ibuf; + n = strlen(cp); + } + else { + /* any other "%X" */ + cp = (char *)format; + n = 2; + } + format += 2; + } + else { + /* plain text */ + cp = (char *)format; + if ((format = strchr(cp, '%')) == NULL) + format = strchr(cp, '\0'); + n = format - cp; + } + /* perform output operation */ + if (output != NULL) + if ((n = output(ctx, cp, n)) == -1) + break; + bytes += n; + } + return bytes; +} + +/* output callback function context for var_mvsnprintf() */ +typedef struct { + char *bufptr; + int buflen; +} var_mvsnprintf_cb_t; + +/* output callback function for var_mvsnprintf() */ +static int +var_mvsnprintf_cb( + void *_ctx, + const char *buffer, int bufsize) +{ + var_mvsnprintf_cb_t *ctx = (var_mvsnprintf_cb_t *)_ctx; + + if (bufsize > ctx->buflen) + return -1; + memcpy(ctx->bufptr, buffer, bufsize); + ctx->bufptr += bufsize; + ctx->buflen -= bufsize; + return bufsize; +} + +/* minimal vsnprintf(3) variant which supports %{c,s,d} only */ +static int +var_mvsnprintf( + char *buffer, int bufsize, + const char *format, va_list ap) +{ + int n; + var_mvsnprintf_cb_t ctx; + + if (format == NULL || ap == NULL) + return -1; + if (buffer != NULL && bufsize == 0) + return -1; + if (buffer == NULL) + /* just determine output length */ + n = var_mvxprintf(NULL, NULL, format, ap); + else { + /* perform real output */ + ctx.bufptr = buffer; + ctx.buflen = bufsize; + n = var_mvxprintf(var_mvsnprintf_cb, &ctx, format, ap); + if (n != -1 && ctx.buflen == 0) + n = -1; + if (n != -1) + *(ctx.bufptr) = '\0'; + } + return n; +} + +/* +** +** ==== PARSE CONTEXT FUNCTIONS ==== +** +*/ + +static var_parse_t * +var_parse_push( + var_parse_t *lower, var_parse_t *upper) +{ + if (upper == NULL) + return NULL; + memcpy(upper, lower, sizeof(var_parse_t)); + upper->lower = lower; + return upper; +} + +static var_parse_t * +var_parse_pop( + var_parse_t *upper) +{ + if (upper == NULL) + return NULL; + return upper->lower; +} + +/* +** +** ==== TOKEN BUFFER FUNCTIONS ==== +** +*/ + +#define TOKENBUF_INITIAL_BUFSIZE 64 + +typedef struct { + const char *begin; + const char *end; + int buffer_size; +} tokenbuf_t; + +static void +tokenbuf_init( + tokenbuf_t *buf) +{ + buf->begin = NULL; + buf->end = NULL; + buf->buffer_size = 0; + return; +} + +static int +tokenbuf_isundef( + tokenbuf_t *buf) +{ + if (buf->begin == NULL && buf->end == NULL) + return 1; + return 0; +} + +static int +tokenbuf_isempty( + tokenbuf_t *buf) +{ + if (buf->begin == buf->end) + return 1; + return 0; +} + +static void +tokenbuf_set( + tokenbuf_t *buf, const char *begin, const char *end, int buffer_size) +{ + buf->begin = begin; + buf->end = end; + buf->buffer_size = buffer_size; + return; +} + +static void +tokenbuf_move( + tokenbuf_t *src, tokenbuf_t *dst) +{ + dst->begin = src->begin; + dst->end = src->end; + dst->buffer_size = src->buffer_size; + tokenbuf_init(src); + return; +} + +static int +tokenbuf_assign( + tokenbuf_t *buf, const char *data, int len) +{ + char *p; + + if ((p = (char *)malloc(len + 1)) == NULL) + return 0; + memcpy(p, data, len); + buf->begin = p; + buf->end = p + len; + buf->buffer_size = len + 1; + *((char *)(buf->end)) = EOS; + return 1; +} + +static int +tokenbuf_append( + tokenbuf_t *output, const char *data, int len) +{ + char *new_buffer; + int new_size; + char *tmp; + + /* Is the tokenbuffer initialized at all? If not, allocate a + standard-sized buffer to begin with. */ + if (output->begin == NULL) { + if ((output->begin = output->end = (const char *)malloc(TOKENBUF_INITIAL_BUFSIZE)) == NULL) + return 0; + output->buffer_size = TOKENBUF_INITIAL_BUFSIZE; + } + + /* does the token contain text, but no buffer has been allocated yet? */ + if (output->buffer_size == 0) { + /* check whether data borders to output. If, we can append + simly by increasing the end pointer. */ + if (output->end == data) { + output->end += len; + return 1; + } + /* ok, so copy the contents of output into an allocated buffer + so that we can append that way. */ + if ((tmp = (char *)malloc(output->end - output->begin + len + 1)) == NULL) + return 0; + memcpy(tmp, output->begin, output->end - output->begin); + output->buffer_size = output->end - output->begin; + output->begin = tmp; + output->end = tmp + output->buffer_size; + output->buffer_size += len + 1; + } + + /* does the token fit into the current buffer? If not, realloc a + larger buffer that fits. */ + if ((output->buffer_size - (output->end - output->begin)) <= len) { + new_size = output->buffer_size; + do { + new_size *= 2; + } while ((new_size - (output->end - output->begin)) <= len); + if ((new_buffer = (char *)realloc((char *)output->begin, new_size)) == NULL) + return 0; + output->end = new_buffer + (output->end - output->begin); + output->begin = new_buffer; + output->buffer_size = new_size; + } + + /* append the data at the end of the current buffer. */ + if (len > 0) + memcpy((char *)output->end, data, len); + output->end += len; + *((char *)output->end) = EOS; + return 1; +} + +static int +tokenbuf_merge( + tokenbuf_t *output, tokenbuf_t *input) +{ + return tokenbuf_append(output, input->begin, input->end - input->begin); +} + +static void +tokenbuf_free( + tokenbuf_t *buf) +{ + if (buf->begin != NULL && buf->buffer_size > 0) + free((char *)buf->begin); + buf->begin = buf->end = NULL; + buf->buffer_size = 0; + return; +} + +/* +** +** ==== CHARACTER CLASS EXPANSION ==== +** +*/ + +static void +expand_range(char a, char b, char_class_t chrclass) +{ + do { + chrclass[(int)a] = 1; + } while (++a <= b); + return; +} + +static var_rc_t +expand_character_class(const char *desc, char_class_t chrclass) +{ + int i; + + /* clear the class array. */ + for (i = 0; i < 256; ++i) + chrclass[i] = 0; + + /* walk through class description and set appropriate entries in array */ + while (*desc != EOS) { + if (desc[1] == '-' && desc[2] != EOS) { + if (desc[0] > desc[2]) + return VAR_ERR_INCORRECT_CLASS_SPEC; + expand_range(desc[0], desc[2], chrclass); + desc += 3; + } else { + chrclass[(int) *desc] = 1; + desc++; + } + } + return VAR_OK; +} + +/* +** +** ==== ESCAPE SEQUENCE EXPANSION FUNCTIONS ==== +** +*/ + +static int +expand_isoct( + char c) +{ + if (c >= '0' && c <= '7') + return 1; + else + return 0; +} + +static var_rc_t +expand_octal( + const char **src, char **dst, const char *end) +{ + unsigned char c; + + if (end - *src < 3) + return VAR_ERR_INCOMPLETE_OCTAL; + if ( !expand_isoct(**src) + || !expand_isoct((*src)[1]) + || !expand_isoct((*src)[2])) + return VAR_ERR_INVALID_OCTAL; + + c = **src - '0'; + if (c > 3) + return VAR_ERR_OCTAL_TOO_LARGE; + c *= 8; + (*src)++; + + c += **src - '0'; + c *= 8; + (*src)++; + + c += **src - '0'; + + **dst = (char) c; + (*dst)++; + return VAR_OK; +} + +static int +expand_ishex( + char c) +{ + if ((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) + return 1; + else + return 0; +} + +static var_rc_t +expand_simple_hex( + const char **src, char **dst, const char *end) +{ + unsigned char c = 0; + + if (end - *src < 2) + return VAR_ERR_INCOMPLETE_HEX; + if ( !expand_ishex(**src) + || !expand_ishex((*src)[1])) + return VAR_ERR_INVALID_HEX; + + if (**src >= '0' && **src <= '9') + c = **src - '0'; + else if (c >= 'a' && c <= 'f') + c = **src - 'a' + 10; + else if (c >= 'A' && c <= 'F') + c = **src - 'A' + 10; + + c = c << 4; + (*src)++; + + if (**src >= '0' && **src <= '9') + c += **src - '0'; + else if (**src >= 'a' && **src <= 'f') + c += **src - 'a' + 10; + else if (**src >= 'A' && **src <= 'F') + c += **src - 'A' + 10; + + **dst = (char) c; + (*dst)++; + return VAR_OK; +} + +static var_rc_t +expand_grouped_hex( + const char **src, char **dst, const char *end) +{ + var_rc_t rc; + + while (*src < end && **src != '}') { + if ((rc = expand_simple_hex(src, dst, end)) != VAR_OK) + return rc; + (*src)++; + } + if (*src == end) + return VAR_ERR_INCOMPLETE_GROUPED_HEX; + + return VAR_OK; +} + +static var_rc_t +expand_hex( + const char **src, char **dst, const char *end) +{ + if (*src == end) + return VAR_ERR_INCOMPLETE_HEX; + if (**src == '{') { + (*src)++; + return expand_grouped_hex(src, dst, end); + } else + return expand_simple_hex(src, dst, end); +} + +/* +** +** ==== RECURSIVE-DESCEND VARIABLE EXPANSION PARSER ==== +** +*/ + +/* forward declarations */ +static int parse_variable(var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result); +static int parse_numexp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed); +static int parse_name (var_t *var, var_parse_t *ctx, const char *begin, const char *end); + +/* parse pattern text */ +static int +parse_pattern( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end) +{ + const char *p; + + /* parse until '/' */ + for (p = begin; p != end && *p != '/'; p++) { + if (*p == var->syntax.escape) { + if (p + 1 == end) + return VAR_ERR_INCOMPLETE_QUOTED_PAIR; + p++; + } + } + return (p - begin); +} + +/* parse substitution text */ +static int +parse_substext( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end) +{ + const char *p; + + /* parse until delim_init or '/' */ + for (p = begin; p != end && *p != var->syntax.delim_init && *p != '/'; p++) { + if (*p == var->syntax.escape) { + if (p + 1 == end) + return VAR_ERR_INCOMPLETE_QUOTED_PAIR; + p++; + } + } + return (p - begin); +} + +/* parse expression text */ +static int +parse_exptext( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end) +{ + const char *p; + + /* parse until delim_init or delim_close or ':' */ + for (p = begin; p != end + && *p != var->syntax.delim_init + && *p != var->syntax.delim_close + && *p != ':'; p++) { + if (*p == var->syntax.escape) { + if (p + 1 == end) + return VAR_ERR_INCOMPLETE_QUOTED_PAIR; + p++; + } + } + return (p - begin); +} + +/* parse opertion argument text */ +static int +parse_opargtext( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end) +{ + const char *p; + + /* parse until delim_init or ')' */ + for (p = begin; p != end && *p != var->syntax.delim_init && *p != ')'; p++) { + if (*p == var->syntax.escape) { + if (p + 1 == end) + return VAR_ERR_INCOMPLETE_QUOTED_PAIR; + p++; + } + } + return (p - begin); +} + +static int +parse_opargtext_or_variable( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end, + tokenbuf_t *result) +{ + const char *p; + tokenbuf_t tmp; + int rc; + + tokenbuf_init(result); + tokenbuf_init(&tmp); + p = begin; + if (p == end) + return 0; + do { + rc = parse_opargtext(var, ctx, p, end); + if (rc < 0) + goto error_return; + if (rc > 0) { + if (!tokenbuf_append(result, p, rc)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + p += rc; + } + rc = parse_variable(var, ctx, p, end, &tmp); + if (rc < 0) + goto error_return; + if (rc > 0) { + p += rc; + if (!tokenbuf_merge(result, &tmp)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + } + } while (rc > 0); + tokenbuf_free(&tmp); + return (p - begin); + + error_return: + tokenbuf_free(&tmp); + tokenbuf_free(result); + return rc; +} + +/* parse expression or variable */ +static int +parse_exptext_or_variable( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end, + tokenbuf_t *result) +{ + const char *p = begin; + tokenbuf_t tmp; + int rc; + + tokenbuf_init(result); + tokenbuf_init(&tmp); + if (begin == end) + return 0; + do { + /* try to parse expression text */ + rc = parse_exptext(var, ctx, p, end); + if (rc < 0) + goto error_return; + if (rc > 0) { + if (!tokenbuf_append(result, p, rc)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + p += rc; + } + + /* try to parse variable construct */ + rc = parse_variable(var, ctx, p, end, &tmp); + if (rc < 0) + goto error_return; + if (rc > 0) { + p += rc; + if (!tokenbuf_append + (result, tmp.begin, tmp.end - tmp.begin)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + } + } while (rc > 0); + + tokenbuf_free(&tmp); + return (p - begin); + + error_return: + tokenbuf_free(&tmp); + tokenbuf_free(result); + return rc; +} + +/* parse substitution text or variable */ +static int +parse_substext_or_variable( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end, + tokenbuf_t *result) +{ + const char *p = begin; + tokenbuf_t tmp; + int rc; + + tokenbuf_init(result); + tokenbuf_init(&tmp); + if (begin == end) + return 0; + do { + /* try to parse substitution text */ + rc = parse_substext(var, ctx, p, end); + if (rc < 0) + goto error_return; + if (rc > 0) { + if (!tokenbuf_append(result, p, rc)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + p += rc; + } + + /* try to parse substitution text */ + rc = parse_variable(var, ctx, p, end, &tmp); + if (rc < 0) + goto error_return; + if (rc > 0) { + p += rc; + if (!tokenbuf_append + (result, tmp.begin, tmp.end - tmp.begin)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + } + } while (rc > 0); + + tokenbuf_free(&tmp); + return (p - begin); + + error_return: + tokenbuf_free(&tmp); + tokenbuf_free(result); + return rc; +} + +/* parse class description */ +static int +parse_class_description( + var_t *var, var_parse_t *ctx, + tokenbuf_t *src, tokenbuf_t *dst) +{ + unsigned char c, d; + const char *p; + + p = src->begin; + while (p != src->end) { + if ((src->end - p) >= 3 && p[1] == '-') { + if (*p > p[2]) + return VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC; + for (c = *p, d = p[2]; c <= d; ++c) { + if (!tokenbuf_append(dst, (char *)&c, 1)) + return VAR_ERR_OUT_OF_MEMORY; + } + p += 3; + } else { + if (!tokenbuf_append(dst, p, 1)) + return VAR_ERR_OUT_OF_MEMORY; + p++; + } + } + return VAR_OK; +} + +/* parse regex replace part */ +static int +parse_regex_replace( + var_t *var, var_parse_t *ctx, + const char *data, + tokenbuf_t *orig, + regmatch_t *pmatch, + tokenbuf_t *expanded) +{ + const char *p; + int i; + + p = orig->begin; + tokenbuf_init(expanded); + + while (p != orig->end) { + if (*p == '\\') { + if (orig->end - p <= 1) { + tokenbuf_free(expanded); + return VAR_ERR_INCOMPLETE_QUOTED_PAIR; + } + p++; + if (*p == '\\') { + if (!tokenbuf_append(expanded, p, 1)) { + tokenbuf_free(expanded); + return VAR_ERR_OUT_OF_MEMORY; + } + p++; + continue; + } + if (!isdigit((int)*p)) { + tokenbuf_free(expanded); + return VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE; + } + i = (*p - '0'); + p++; + if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) { + tokenbuf_free(expanded); + return VAR_ERR_SUBMATCH_OUT_OF_RANGE; + } + if (!tokenbuf_append(expanded, data + pmatch[i].rm_so, + pmatch[i].rm_eo - pmatch[i].rm_so)) { + tokenbuf_free(expanded); + return VAR_ERR_OUT_OF_MEMORY; + } + } else { + if (!tokenbuf_append(expanded, p, 1)) { + tokenbuf_free(expanded); + return VAR_ERR_OUT_OF_MEMORY; + } + p++; + } + } + + return VAR_OK; +} + +/* operation: transpose */ +static int +op_transpose( + var_t *var, var_parse_t *ctx, + tokenbuf_t *data, + tokenbuf_t *search, + tokenbuf_t *replace) +{ + tokenbuf_t srcclass, dstclass; + const char *p; + int rc; + int i; + + tokenbuf_init(&srcclass); + tokenbuf_init(&dstclass); + if ((rc = parse_class_description(var, ctx, search, &srcclass)) != VAR_OK) + goto error_return; + if ((rc = parse_class_description(var, ctx, replace, &dstclass)) != VAR_OK) + goto error_return; + if (srcclass.begin == srcclass.end) { + rc = VAR_ERR_EMPTY_TRANSPOSE_CLASS; + goto error_return; + } + if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) { + rc = VAR_ERR_TRANSPOSE_CLASSES_MISMATCH; + goto error_return; + } + if (data->buffer_size == 0) { + tokenbuf_t tmp; + if (!tokenbuf_assign(&tmp, data->begin, data->end - data->begin)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + tokenbuf_move(&tmp, data); + } + for (p = data->begin; p != data->end; ++p) { + for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) { + if (*p == srcclass.begin[i]) { + *((char *)p) = dstclass.begin[i]; + break; + } + } + } + tokenbuf_free(&srcclass); + tokenbuf_free(&dstclass); + return VAR_OK; + + error_return: + tokenbuf_free(search); + tokenbuf_free(replace); + tokenbuf_free(&srcclass); + tokenbuf_free(&dstclass); + return rc; +} + +/* operation: search & replace */ +static int +op_search_and_replace( + var_t *var, var_parse_t *ctx, + tokenbuf_t *data, + tokenbuf_t *search, + tokenbuf_t *replace, + tokenbuf_t *flags) +{ + tokenbuf_t tmp; + const char *p; + int case_insensitive = 0; + int multiline = 0; + int global = 0; + int no_regex = 0; + int rc; + + if (search->begin == search->end) + return VAR_ERR_EMPTY_SEARCH_STRING; + + for (p = flags->begin; p != flags->end; p++) { + switch (tolower(*p)) { + case 'm': + multiline = 1; + break; + case 'i': + case_insensitive = 1; + break; + case 'g': + global = 1; + break; + case 't': + no_regex = 1; + break; + default: + return VAR_ERR_UNKNOWN_REPLACE_FLAG; + } + } + + if (no_regex) { + /* plain text pattern based operation */ + tokenbuf_init(&tmp); + for (p = data->begin; p != data->end;) { + if (case_insensitive) + rc = strncasecmp(p, search->begin, search->end - search->begin); + else + rc = strncmp(p, search->begin, search->end - search->begin); + if (rc != 0) { + /* not matched, copy character */ + if (!tokenbuf_append(&tmp, p, 1)) { + tokenbuf_free(&tmp); + return VAR_ERR_OUT_OF_MEMORY; + } + p++; + } else { + /* matched, copy replacement string */ + tokenbuf_merge(&tmp, replace); + p += (search->end - search->begin); + if (!global) { + /* append remaining text */ + if (!tokenbuf_append(&tmp, p, data->end - p)) { + tokenbuf_free(&tmp); + return VAR_ERR_OUT_OF_MEMORY; + } + break; + } + } + } + tokenbuf_free(data); + tokenbuf_move(&tmp, data); + } else { + /* regular expression pattern based operation */ + tokenbuf_t mydata; + tokenbuf_t myreplace; + regex_t preg; + regmatch_t pmatch[10]; + int regexec_flag; + + /* copy pattern and data to own buffer to make sure they are EOS-terminated */ + if (!tokenbuf_assign(&tmp, search->begin, search->end - search->begin)) + return VAR_ERR_OUT_OF_MEMORY; + if (!tokenbuf_assign(&mydata, data->begin, data->end - data->begin)) { + tokenbuf_free(&tmp); + return VAR_ERR_OUT_OF_MEMORY; + } + + /* compile the pattern. */ + rc = regcomp(&preg, tmp.begin, + ( REG_EXTENDED + | (multiline ? REG_NEWLINE : 0) + | (case_insensitive ? REG_ICASE : 0))); + tokenbuf_free(&tmp); + if (rc != 0) { + tokenbuf_free(&mydata); + return VAR_ERR_INVALID_REGEX_IN_REPLACE; + } + + /* match the pattern and create the result string in the tmp buffer */ + tokenbuf_append(&tmp, "", 0); + for (p = mydata.begin; p < mydata.end; ) { + if (p == mydata.begin || p[-1] == '\n') + regexec_flag = 0; + else + regexec_flag = REG_NOTBOL; + rc = regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag); + if (rc != 0) { + /* no (more) matching */ + tokenbuf_append(&tmp, p, mydata.end - p); + break; + } + else if ( multiline + && (p + pmatch[0].rm_so) == mydata.end + && (pmatch[0].rm_eo - pmatch[0].rm_so) == 0) { + /* special case: found empty pattern (usually /^/ or /$/ only) + in multi-line at end of data (after the last newline) */ + tokenbuf_append(&tmp, p, mydata.end - p); + break; + } + else { + /* append prolog string */ + if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so)) { + regfree(&preg); + tokenbuf_free(&tmp); + tokenbuf_free(&mydata); + return VAR_ERR_OUT_OF_MEMORY; + } + /* create replace string */ + rc = parse_regex_replace(var, ctx, p, replace, pmatch, &myreplace); + if (rc != VAR_OK) { + regfree(&preg); + tokenbuf_free(&tmp); + tokenbuf_free(&mydata); + return rc; + } + /* append replace string */ + if (!tokenbuf_append(&tmp, myreplace.begin, myreplace.end - myreplace.begin)) { + regfree(&preg); + tokenbuf_free(&tmp); + tokenbuf_free(&mydata); + tokenbuf_free(&myreplace); + return VAR_ERR_OUT_OF_MEMORY; + } + tokenbuf_free(&myreplace); + /* skip now processed data */ + p += pmatch[0].rm_eo; + /* if pattern matched an empty part (think about + anchor-only regular expressions like /^/ or /$/) we + skip the next character to make sure we do not enter + an infinitive loop in matching */ + if ((pmatch[0].rm_eo - pmatch[0].rm_so) == 0) { + if (p >= mydata.end) + break; + if (!tokenbuf_append(&tmp, p, 1)) { + regfree(&preg); + tokenbuf_free(&tmp); + tokenbuf_free(&mydata); + return VAR_ERR_OUT_OF_MEMORY; + } + p++; + } + /* append prolog string and stop processing if we + do not perform the search & replace globally */ + if (!global) { + if (!tokenbuf_append(&tmp, p, mydata.end - p)) { + regfree(&preg); + tokenbuf_free(&tmp); + tokenbuf_free(&mydata); + return VAR_ERR_OUT_OF_MEMORY; + } + break; + } + } + } + regfree(&preg); + tokenbuf_free(data); + tokenbuf_move(&tmp, data); + tokenbuf_free(&mydata); + } + + return VAR_OK; +} + +/* operation: offset substring */ +static int +op_offset( + var_t *var, var_parse_t *ctx, + tokenbuf_t *data, + int num1, + int num2, + int isrange) +{ + tokenbuf_t res; + const char *p; + + /* determine begin of result string */ + if ((data->end - data->begin) < num1) + return VAR_ERR_OFFSET_OUT_OF_BOUNDS; + p = data->begin + num1; + + /* if num2 is zero, we copy the rest from there. */ + if (num2 == 0) { + if (!tokenbuf_assign(&res, p, data->end - p)) + return VAR_ERR_OUT_OF_MEMORY; + } else { + /* ok, then use num2. */ + if (isrange) { + if ((p + num2) > data->end) + return VAR_ERR_RANGE_OUT_OF_BOUNDS; + if (!tokenbuf_assign(&res, p, num2)) + return VAR_ERR_OUT_OF_MEMORY; + } else { + if (num2 < num1) + return VAR_ERR_OFFSET_LOGIC; + if ((data->begin + num2) > data->end) + return VAR_ERR_RANGE_OUT_OF_BOUNDS; + if (!tokenbuf_assign(&res, p, num2 - num1 + 1)) + return VAR_ERR_OUT_OF_MEMORY; + } + } + tokenbuf_free(data); + tokenbuf_move(&res, data); + return VAR_OK; +} + +/* operation: padding */ +static int +op_padding( + var_t *var, var_parse_t *ctx, + tokenbuf_t *data, + int width, + tokenbuf_t *fill, + char position) +{ + tokenbuf_t result; + int i; + + if (fill->begin == fill->end) + return VAR_ERR_EMPTY_PADDING_FILL_STRING; + tokenbuf_init(&result); + if (position == 'l') { + /* left padding */ + i = width - (data->end - data->begin); + if (i > 0) { + i = i / (fill->end - fill->begin); + while (i > 0) { + if (!tokenbuf_append(data, fill->begin, fill->end - fill->begin)) + return VAR_ERR_OUT_OF_MEMORY; + i--; + } + i = (width - (data->end - data->begin)) % (fill->end - fill->begin); + if (!tokenbuf_append(data, fill->begin, i)) + return VAR_ERR_OUT_OF_MEMORY; + } + } else if (position == 'r') { + /* right padding */ + i = width - (data->end - data->begin); + if (i > 0) { + i = i / (fill->end - fill->begin); + while (i > 0) { + if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) { + tokenbuf_free(&result); + return VAR_ERR_OUT_OF_MEMORY; + } + i--; + } + i = (width - (data->end - data->begin)) % (fill->end - fill->begin); + if (!tokenbuf_append(&result, fill->begin, i)) { + tokenbuf_free(&result); + return VAR_ERR_OUT_OF_MEMORY; + } + if (!tokenbuf_append(&result, data->begin, data->end - data->begin)) { + tokenbuf_free(&result); + return VAR_ERR_OUT_OF_MEMORY; + } + /* move string from temporary buffer to data buffer */ + tokenbuf_free(data); + tokenbuf_move(&result, data); + } + } else if (position == 'c') { + /* centered padding */ + i = (width - (data->end - data->begin)) / 2; + if (i > 0) { + /* create the prefix */ + i = i / (fill->end - fill->begin); + while (i > 0) { + if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) { + tokenbuf_free(&result); + return VAR_ERR_OUT_OF_MEMORY; + } + i--; + } + i = ((width - (data->end - data->begin)) / 2) + % (fill->end - fill->begin); + if (!tokenbuf_append(&result, fill->begin, i)) { + tokenbuf_free(&result); + return VAR_ERR_OUT_OF_MEMORY; + } + /* append the actual data string */ + if (!tokenbuf_append(&result, data->begin, data->end - data->begin)) { + tokenbuf_free(&result); + return VAR_ERR_OUT_OF_MEMORY; + } + /* append the suffix */ + i = width - (result.end - result.begin); + i = i / (fill->end - fill->begin); + while (i > 0) { + if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) { + tokenbuf_free(&result); + return VAR_ERR_OUT_OF_MEMORY; + } + i--; + } + i = width - (result.end - result.begin); + if (!tokenbuf_append(&result, fill->begin, i)) { + tokenbuf_free(&result); + return VAR_ERR_OUT_OF_MEMORY; + } + /* move string from temporary buffer to data buffer */ + tokenbuf_free(data); + tokenbuf_move(&result, data); + } + } + return VAR_OK; +} + +/* parse an integer number ("123") */ +static int +parse_integer( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end, + int *result) +{ + const char *p; + int num; + + p = begin; + num = 0; + while (isdigit(*p) && p != end) { + num *= 10; + num += (*p - '0'); + p++; + } + if (result != NULL) + *result = num; + return (p - begin); +} + +/* parse an operation (":x...") */ +static int +parse_operation( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end, + tokenbuf_t *data) +{ + const char *p; + tokenbuf_t tmptokbuf; + tokenbuf_t search, replace, flags; + tokenbuf_t number1, number2; + int num1, num2; + int isrange; + int rc; + char *ptr; + + /* initialization */ + tokenbuf_init(&tmptokbuf); + tokenbuf_init(&search); + tokenbuf_init(&replace); + tokenbuf_init(&flags); + tokenbuf_init(&number1); + tokenbuf_init(&number2); + p = begin; + if (p == end) + return 0; + + /* dispatch through the first operation character */ + switch (tolower(*p)) { + case 'l': { + /* turn value to lowercase. */ + if (data->begin != NULL) { + /* if the buffer does not live in an allocated buffer, + we have to copy it before modifying the contents. */ + if (data->buffer_size == 0) { + if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + } + /* convert value */ + for (ptr = (char *)data->begin; ptr != data->end; ptr++) + *ptr = (char)tolower((int)(*ptr)); + } + p++; + break; + } + case 'u': { + /* turn value to uppercase. */ + if (data->begin != NULL) { + /* if the buffer does not live in an allocated buffer, + we have to copy it before modifying the contents. */ + if (data->buffer_size == 0) { + if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + } + /* convert value */ + for (ptr = (char *)data->begin; ptr != data->end; ptr++) + *ptr = (char)toupper((int)(*ptr)); + } + p++; + break; + } + case 'o': { + /* cut out substring of value. */ + p++; + rc = parse_integer(var, ctx, p, end, &num1); + if (rc == 0) { + rc = VAR_ERR_MISSING_START_OFFSET; + goto error_return; + } + else if (rc < 0) + goto error_return; + p += rc; + if (*p == ',') { + isrange = 0; + p++; + } else if (*p == '-') { + isrange = 1; + p++; + } else { + rc = VAR_ERR_INVALID_OFFSET_DELIMITER; + goto error_return; + } + rc = parse_integer(var, ctx, p, end, &num2); + p += rc; + if (data->begin != NULL) { + rc = op_offset(var, ctx, data, num1, num2, isrange); + if (rc < 0) + goto error_return; + } + break; + } + case '#': { + /* determine length of the value */ + if (data->begin != NULL) { + char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */ + sprintf(buf, "%d", (int)(data->end - data->begin)); + tokenbuf_free(data); + if (!tokenbuf_assign(data, buf, strlen(buf))) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + } + p++; + break; + } + case '-': { + /* substitute parameter if data is empty */ + p++; + rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf); + if (rc < 0) + goto error_return; + if (rc == 0) { + rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND; + goto error_return; + } + p += rc; + if (tokenbuf_isundef(data)) + tokenbuf_move(&tmptokbuf, data); + else if (tokenbuf_isempty(data)) { + tokenbuf_free(data); + tokenbuf_move(&tmptokbuf, data); + } + break; + } + case '*': { + /* substitute empty string if data is not empty, parameter otherwise. */ + p++; + rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf); + if (rc < 0) + goto error_return; + if (rc == 0) { + rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND; + goto error_return; + } + p += rc; + if (data->begin != NULL) { + if (data->begin == data->end) { + tokenbuf_free(data); + tokenbuf_move(&tmptokbuf, data); + } else { + tokenbuf_free(data); + data->begin = data->end = ""; + data->buffer_size = 0; + } + } + break; + } + case '+': { + /* substitute parameter if data is not empty. */ + p++; + rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf); + if (rc < 0) + goto error_return; + if (rc == 0) { + rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND; + goto error_return; + } + p += rc; + if (data->begin != NULL && data->begin != data->end) { + tokenbuf_free(data); + tokenbuf_move(&tmptokbuf, data); + } + break; + } + case 's': { + /* search and replace. */ + p++; + if (*p != '/') + return VAR_ERR_MALFORMATTED_REPLACE; + p++; + rc = parse_pattern(var, ctx, p, end); + if (rc < 0) + goto error_return; + tokenbuf_set(&search, p, p + rc, 0); + p += rc; + if (*p != '/') { + rc = VAR_ERR_MALFORMATTED_REPLACE; + goto error_return; + } + p++; + rc = parse_substext_or_variable(var, ctx, p, end, &replace); + if (rc < 0) + goto error_return; + p += rc; + if (*p != '/') { + rc = VAR_ERR_MALFORMATTED_REPLACE; + goto error_return; + } + p++; + rc = parse_exptext(var, ctx, p, end); + if (rc < 0) + goto error_return; + tokenbuf_set(&flags, p, p + rc, 0); + p += rc; + if (data->begin != NULL) { + rc = op_search_and_replace(var, ctx, data, &search, &replace, &flags); + if (rc < 0) + goto error_return; + } + break; + } + case 'y': { + /* transpose characters from class A to class B. */ + p++; + if (*p != '/') + return VAR_ERR_MALFORMATTED_TRANSPOSE; + p++; + rc = parse_substext_or_variable(var, ctx, p, end, &search); + if (rc < 0) + goto error_return; + p += rc; + if (*p != '/') { + rc = VAR_ERR_MALFORMATTED_TRANSPOSE; + goto error_return; + } + p++; + rc = parse_substext_or_variable(var, ctx, p, end, &replace); + if (rc < 0) + goto error_return; + p += rc; + if (*p != '/') { + rc = VAR_ERR_MALFORMATTED_TRANSPOSE; + goto error_return; + } else + p++; + if (data->begin) { + rc = op_transpose(var, ctx, data, &search, &replace); + if (rc < 0) + goto error_return; + } + break; + } + case 'p': { + /* padding. */ + p++; + if (*p != '/') + return VAR_ERR_MALFORMATTED_PADDING; + p++; + rc = parse_integer(var, ctx, p, end, &num1); + if (rc == 0) { + rc = VAR_ERR_MISSING_PADDING_WIDTH; + goto error_return; + } + p += rc; + if (*p != '/') { + rc = VAR_ERR_MALFORMATTED_PADDING; + goto error_return; + } + p++; + rc = parse_substext_or_variable(var, ctx, p, end, &replace); + if (rc < 0) + goto error_return; + p += rc; + if (*p != '/') { + rc = VAR_ERR_MALFORMATTED_PADDING; + goto error_return; + } + p++; + if (*p != 'l' && *p != 'c' && *p != 'r') { + rc = VAR_ERR_MALFORMATTED_PADDING; + goto error_return; + } + p++; + if (data->begin) { + rc = op_padding(var, ctx, data, num1, &replace, p[-1]); + if (rc < 0) + goto error_return; + } + break; + } + case '%': { + /* operation callback function */ + const char *op_ptr; + int op_len; + const char *arg_ptr; + int arg_len; + const char *val_ptr; + int val_len; + const char *out_ptr; + int out_len; + int out_size; + tokenbuf_t args; + + p++; + rc = parse_name(var, ctx, p, end); + if (rc < 0) + goto error_return; + op_ptr = p; + op_len = rc; + p += rc; + if (*p == '(') { + p++; + tokenbuf_init(&args); + rc = parse_opargtext_or_variable(var, ctx, p, end, &args); + if (rc < 0) + goto error_return; + p += rc; + arg_ptr = args.begin; + arg_len = args.end - args.begin; + if (*p != ')') { + rc = VAR_ERR_MALFORMED_OPERATION_ARGUMENTS; + goto error_return; + } + p++; + } + else { + arg_ptr = NULL; + arg_len = 0; + } + val_ptr = data->begin; + val_len = data->end - data->begin; + + if (data->begin != NULL && var->cb_operation_fct != NULL) { + /* call operation callback function */ + rc = (*var->cb_operation_fct)(var, var->cb_operation_ctx, + op_ptr, op_len, + arg_ptr, arg_len, + val_ptr, val_len, + &out_ptr, &out_len, &out_size); + if (rc < 0) + goto error_return; + tokenbuf_free(data); + tokenbuf_set(data, out_ptr, out_ptr+out_len, out_size); + } + break; + } + default: + return VAR_ERR_UNKNOWN_COMMAND_CHAR; + } + + /* return successfully */ + tokenbuf_free(&tmptokbuf); + tokenbuf_free(&search); + tokenbuf_free(&replace); + tokenbuf_free(&flags); + tokenbuf_free(&number1); + tokenbuf_free(&number2); + return (p - begin); + + /* return with an error */ + error_return: + tokenbuf_free(data); + tokenbuf_free(&tmptokbuf); + tokenbuf_free(&search); + tokenbuf_free(&replace); + tokenbuf_free(&flags); + tokenbuf_free(&number1); + tokenbuf_free(&number2); + return rc; +} + +/* parse numerical expression operand */ +static int +parse_numexp_operand( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end, + int *result, int *failed) +{ + const char *p; + tokenbuf_t tmp; + int rc; + var_parse_t myctx; + + /* initialization */ + p = begin; + tokenbuf_init(&tmp); + if (p == end) + return VAR_ERR_INCOMPLETE_INDEX_SPEC; + + /* parse opening numerical expression */ + if (*p == '(') { + /* parse inner numerical expression */ + rc = parse_numexp(var, ctx, ++p, end, result, failed); + if (rc < 0) + return rc; + p += rc; + if (p == end) + return VAR_ERR_INCOMPLETE_INDEX_SPEC; + /* parse closing parenthesis */ + if (*p != ')') + return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX; + p++; + } + /* parse contained variable */ + else if (*p == var->syntax.delim_init) { + /* parse variable with forced expansion */ + ctx = var_parse_push(ctx, &myctx); + ctx->force_expand = 1; + rc = parse_variable(var, ctx, p, end, &tmp); + ctx = var_parse_pop(ctx); + + if (rc == VAR_ERR_UNDEFINED_VARIABLE) { + *failed = 1; + /* parse variable without forced expansion */ + ctx = var_parse_push(ctx, &myctx); + ctx->force_expand = 0; + rc = parse_variable(var, ctx, p, end, &tmp); + ctx = var_parse_pop(ctx); + if (rc < 0) + return rc; + p += rc; + *result = 0; + } + else if (rc < 0) + return rc; + else { + p += rc; + /* parse remaining numerical expression */ + rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed); + tokenbuf_free(&tmp); + if (rc < 0) + return rc; + } + } + /* parse relative index mark ("#") */ + else if ( var->syntax.index_mark != EOS + && *p == var->syntax.index_mark) { + p++; + *result = ctx->index_this; + if (ctx->rel_lookup_flag) + ctx->rel_lookup_cnt++; + } + /* parse plain integer number */ + else if (isdigit(*p)) { + rc = parse_integer(var, ctx, p, end, result); + p += rc; + } + /* parse signed positive integer number */ + else if (*p == '+') { + if ((end - p) > 1 && isdigit(p[1])) { + p++; + rc = parse_integer(var, ctx, p, end, result); + p += rc; + } + else + return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC; + } + /* parse signed negative integer number */ + else if (*p == '-') { + if (end - p > 1 && isdigit(p[1])) { + p++; + rc = parse_integer(var, ctx, p, end, result); + *result = -(*result); + p += rc; + } + else + return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC; + } + /* else we failed to parse anything reasonable */ + else + return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC; + + return (p - begin); +} + +/* parse numerical expression ("x+y") */ +static int +parse_numexp( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end, + int *result, int *failed) +{ + const char *p; + char op; + int right; + int rc; + + /* initialization */ + p = begin; + if (p == end) + return VAR_ERR_INCOMPLETE_INDEX_SPEC; + + /* parse left numerical operand */ + rc = parse_numexp_operand(var, ctx, p, end, result, failed); + if (rc < 0) + return rc; + p += rc; + + /* parse numerical operator */ + while (p != end) { + if (*p == '+' || *p == '-') { + op = *p++; + /* recursively parse right operand (light binding) */ + rc = parse_numexp(var, ctx, p, end, &right, failed); + if (rc < 0) + return rc; + p += rc; + if (op == '+') + *result = (*result + right); + else + *result = (*result - right); + } + else if (*p == '*' || *p == '/' || *p == '%') { + op = *p++; + /* recursively parse right operand (string binding) */ + rc = parse_numexp_operand(var, ctx, p, end, &right, failed); + if (rc < 0) + return rc; + p += rc; + if (op == '*') + *result = (*result * right); + else if (op == '/') { + if (right == 0) { + if (*failed) + *result = 0; + else + return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX; + } + else + *result = (*result / right); + } + else if (op == '%') { + if (right == 0) { + if (*failed) + *result = 0; + else + return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX; + } + else + *result = (*result % right); + } + } + else + break; + } + + /* return amount of parsed input */ + return (p - begin); +} + +/* parse variable name ("abc") */ +static int +parse_name( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end) +{ + const char *p; + + /* parse as long as name class characters are found */ + for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++) + ; + return (p - begin); +} + +/* lookup a variable value through the callback function */ +static int +lookup_value( + var_t *var, var_parse_t *ctx, + const char *var_ptr, int var_len, int var_inc, int var_idx, + const char **val_ptr, int *val_len, int *val_size) +{ + char buf[1]; + int rc; + + /* pass through to original callback */ + rc = (*var->cb_value_fct)(var, var->cb_value_ctx, + var_ptr, var_len, var_inc, var_idx, + val_ptr, val_len, val_size); + + /* convert undefined variable into empty variable if relative + lookups are counted. This is the case inside an active loop + construct if no limits are given. There the parse_input() + has to proceed until all variables have undefined values. + This trick here allows it to determine this case. */ + if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) { + ctx->rel_lookup_cnt--; + buf[0] = EOS; + *val_ptr = buf; + *val_len = 0; + *val_size = 0; + return VAR_OK; + } + + return rc; +} + +/* parse complex variable construct ("${name...}") */ +static int +parse_variable_complex( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end, + tokenbuf_t *result) +{ + const char *p; + const char *data; + int len, buffer_size; + int failed = 0; + int rc; + int idx = 0; + int inc; + tokenbuf_t name; + tokenbuf_t tmp; + + /* initializations */ + p = begin; + tokenbuf_init(&name); + tokenbuf_init(&tmp); + tokenbuf_init(result); + + /* parse open delimiter */ + if (p == end || *p != var->syntax.delim_open) + return 0; + p++; + if (p == end) + return VAR_ERR_INCOMPLETE_VARIABLE_SPEC; + + /* parse name of variable to expand. The name may consist of an + arbitrary number of variable name character and contained variable + constructs. */ + do { + /* parse a variable name */ + rc = parse_name(var, ctx, p, end); + if (rc < 0) + goto error_return; + if (rc > 0) { + if (!tokenbuf_append(&name, p, rc)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + p += rc; + } + + /* parse an (embedded) variable */ + rc = parse_variable(var, ctx, p, end, &tmp); + if (rc < 0) + goto error_return; + if (rc > 0) { + if (!tokenbuf_merge(&name, &tmp)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + p += rc; + } + } while (rc > 0); + + /* we must have the complete expanded variable name now, + so make sure we really do. */ + if (name.begin == name.end) { + if (ctx->force_expand) { + rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC; + goto error_return; + } + else { + /* If no force_expand is requested, we have to back-off. + We're not sure whether our approach here is 100% correct, + because it _could_ have side-effects according to Peter + Simons, but as far as we know and tried it, it is + correct. But be warned -- RSE */ + tokenbuf_set(result, begin - 1, p, 0); + goto goahead; + } + } + + /* parse an optional index specification */ + if ( var->syntax.index_open != EOS + && *p == var->syntax.index_open) { + p++; + rc = parse_numexp(var, ctx, p, end, &idx, &failed); + if (rc < 0) + goto error_return; + if (rc == 0) { + rc = VAR_ERR_INCOMPLETE_INDEX_SPEC; + goto error_return; + } + p += rc; + if (p == end) { + rc = VAR_ERR_INCOMPLETE_INDEX_SPEC; + goto error_return; + } + if (*p != var->syntax.index_close) { + rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC; + goto error_return; + } + p++; + } + + /* parse end of variable construct or start of post-operations */ + if (p == end || (*p != var->syntax.delim_close && *p != ':' && *p != '+')) { + rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC; + goto error_return; + } + inc = (*p == '+'); /* increment variable */ + p++; + + /* lookup the variable value now */ + if (failed) { + tokenbuf_set(result, begin - 1, p, 0); + } else { + rc = lookup_value(var, ctx, + name.begin, name.end-name.begin, inc, idx, + &data, &len, &buffer_size); + if (rc == VAR_ERR_UNDEFINED_VARIABLE) { + tokenbuf_init(result); /* delayed handling of undefined variable */ + } else if (rc < 0) { + goto error_return; + } else { + /* the preliminary result is the raw value of the variable. + This may be modified by the operations that may follow. */ + tokenbuf_set(result, data, data + len, buffer_size); + } + } + + /* parse optional post-operations */ + goahead: + if (p[-1] == ':') { + tokenbuf_free(&tmp); + tokenbuf_init(&tmp); + p--; + while (p != end && *p == ':') { + p++; + if (!failed) + rc = parse_operation(var, ctx, p, end, result); + else + rc = parse_operation(var, ctx, p, end, &tmp); + if (rc < 0) + goto error_return; + p += rc; + if (failed) + result->end += rc; + } + if (p == end || *p != var->syntax.delim_close) { + rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC; + goto error_return; + } + p++; + if (failed) + result->end++; + } else if (p[-1] == '+') { + p++; + } + + /* lazy handling of undefined variable */ + if (!failed && tokenbuf_isundef(result)) { + if (ctx->force_expand) { + rc = VAR_ERR_UNDEFINED_VARIABLE; + goto error_return; + } + else + tokenbuf_set(result, begin - 1, p, 0); + } + + /* return successfully */ + tokenbuf_free(&name); + tokenbuf_free(&tmp); + return (p - begin); + + /* return with an error */ + error_return: + tokenbuf_free(&name); + tokenbuf_free(&tmp); + tokenbuf_free(result); + return rc; +} + +/* parse variable construct ("$name" or "${name...}") */ +static int +parse_variable( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end, + tokenbuf_t *result) +{ + const char *p; + const char *data; + int len, buffer_size; + int rc, rc2; + int inc; + + /* initialization */ + p = begin; + tokenbuf_init(result); + + /* parse init delimiter */ + if (p == end || *p != var->syntax.delim_init) + return 0; + p++; + if (p == end) + return VAR_ERR_INCOMPLETE_VARIABLE_SPEC; + + /* parse a simple variable name. + (if this fails, we're try to parse a complex variable construct) */ + rc = parse_name(var, ctx, p, end); + if (rc < 0) + return rc; + if (rc > 0) { + inc = (p[rc] == '+'); + rc2 = lookup_value(var, ctx, p, rc, inc, 0, &data, &len, &buffer_size); + if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) { + tokenbuf_set(result, begin, begin + 1 + rc, 0); + return (1 + rc); + } + if (rc2 < 0) + return rc2; + tokenbuf_set(result, data, data + len, buffer_size); + return (1 + rc); + } + + /* parse a complex variable construct (else case) */ + rc = parse_variable_complex(var, ctx, p, end, result); + if (rc > 0) + rc++; + return rc; +} + +/* parse loop construct limits ("[...]{b,s,e}") */ +static var_rc_t +parse_looplimits( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end, + int *start, int *step, int *stop, int *open_stop) +{ + const char *p; + int rc; + int failed; + + /* initialization */ + p = begin; + + /* we are happy if nothing is to left to parse */ + if (p == end) + return VAR_OK; + + /* parse start delimiter */ + if (*p != var->syntax.delim_open) + return VAR_OK; + p++; + + /* parse loop start value */ + failed = 0; + rc = parse_numexp(var, ctx, p, end, start, &failed); + if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) + *start = 0; /* use default */ + else if (rc < 0) + return (var_rc_t)rc; + else + p += rc; + if (failed) + return VAR_ERR_UNDEFINED_VARIABLE; + + /* parse separator */ + if (*p != ',') + return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS; + p++; + + /* parse loop step value */ + failed = 0; + rc = parse_numexp(var, ctx, p, end, step, &failed); + if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) + *step = 1; /* use default */ + else if (rc < 0) + return (var_rc_t)rc; + else + p += rc; + if (failed) + return VAR_ERR_UNDEFINED_VARIABLE; + + /* parse separator */ + if (*p != ',') { + /* if not found, parse end delimiter */ + if (*p != var->syntax.delim_close) + return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS; + p++; + + /* shift step value to stop value */ + *stop = *step; + *step = 1; + + /* determine whether loop end is open */ + if (rc > 0) + *open_stop = 0; + else + *open_stop = 1; + return (var_rc_t)(p - begin); + } + p++; + + /* parse loop stop value */ + failed = 0; + rc = parse_numexp(var, ctx, p, end, stop, &failed); + if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) { + *stop = 0; /* use default */ + *open_stop = 1; + } + else if (rc < 0) + return (var_rc_t)rc; + else { + *open_stop = 0; + p += rc; + } + if (failed) + return VAR_ERR_UNDEFINED_VARIABLE; + + /* parse end delimiter */ + if (*p != var->syntax.delim_close) + return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS; + p++; + + /* return amount of parsed input */ + return (var_rc_t)(p - begin); +} + +/* parse plain text */ +static int +parse_text( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end) +{ + const char *p; + + /* parse until delim_init (variable construct) + or index_open (loop construct) is found */ + for (p = begin; p != end; p++) { + if (*p == var->syntax.escape) { + p++; /* skip next character */ + if (p == end) + return VAR_ERR_INCOMPLETE_QUOTED_PAIR; + } + else if (*p == var->syntax.delim_init) + break; + else if ( var->syntax.index_open != EOS + && ( *p == var->syntax.index_open + || *p == var->syntax.index_close)) + break; + } + return (p - begin); +} + +/* expand input in general */ +static var_rc_t +parse_input( + var_t *var, var_parse_t *ctx, + const char *begin, const char *end, + tokenbuf_t *output, int recursion_level) +{ + const char *p; + int rc, rc2; + tokenbuf_t result; + int start, step, stop, open_stop; + int i; + int output_backup; + int rel_lookup_cnt; + int loop_limit_length; + var_parse_t myctx; + + /* initialization */ + p = begin; + + do { + /* try to parse a loop construct */ + if ( p != end + && var->syntax.index_open != EOS + && *p == var->syntax.index_open) { + p++; + + /* loop preparation */ + loop_limit_length = -1; + rel_lookup_cnt = ctx->rel_lookup_cnt; + open_stop = 1; + rc = 0; + start = 0; + step = 1; + stop = 0; + output_backup = 0; + + /* iterate over loop construct, either as long as there is + (still) nothing known about the limit, or there is an open + (=unknown) limit stop and there are still defined variables + or there is a stop limit known and it is still not reached */ + re_loop: + for (i = start; + ( ( open_stop + && ( loop_limit_length < 0 + || rel_lookup_cnt > ctx->rel_lookup_cnt)) + || ( !open_stop + && i <= stop) ); + i += step) { + + /* remember current output end for restoring */ + output_backup = (output->end - output->begin); + + /* open temporary context for recursion */ + ctx = var_parse_push(ctx, &myctx); + ctx->force_expand = 1; + ctx->rel_lookup_flag = 1; + ctx->index_this = i; + + /* recursive parse input through ourself */ + rc = parse_input(var, ctx, p, end, + output, recursion_level+1); + + /* retrieve info and close temporary context */ + rel_lookup_cnt = ctx->rel_lookup_cnt; + ctx = var_parse_pop(ctx); + + /* error handling */ + if (rc < 0) + goto error_return; + + /* make sure the loop construct is closed */ + if (p[rc] != var->syntax.index_close) { + rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT; + goto error_return; + } + + /* try to parse loop construct limit specification */ + if (loop_limit_length < 0) { + rc2 = parse_looplimits(var, ctx, p+rc+1, end, + &start, &step, &stop, &open_stop); + if (rc2 < 0) + goto error_return; + else if (rc2 == 0) + loop_limit_length = 0; + else if (rc2 > 0) { + loop_limit_length = rc2; + /* restart loop from scratch */ + output->end = (output->begin + output_backup); + goto re_loop; + } + } + } + + /* if stop value is open, restore to the output end + because the last iteration was just to determine the loop + termination and its result has to be discarded */ + if (open_stop) + output->end = (output->begin + output_backup); + + /* skip parsed loop construct */ + p += rc; + p++; + p += loop_limit_length; + + continue; + } + + /* try to parse plain text */ + rc = parse_text(var, ctx, p, end); + if (rc > 0) { + if (!tokenbuf_append(output, p, rc)) { + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + p += rc; + continue; + } else if (rc < 0) + goto error_return; + + /* try to parse a variable construct */ + tokenbuf_init(&result); + rc = parse_variable(var, ctx, p, end, &result); + if (rc > 0) { + if (!tokenbuf_merge(output, &result)) { + tokenbuf_free(&result); + rc = VAR_ERR_OUT_OF_MEMORY; + goto error_return; + } + tokenbuf_free(&result); + p += rc; + continue; + } + tokenbuf_free(&result); + if (rc < 0) + goto error_return; + + } while (p != end && rc > 0); + + /* We do not know whether this really could happen, but because we + are paranoid, report an error at the outer most parsing level if + there is still any input. Because this would mean that we are no + longer able to parse the remaining input as a loop construct, a + text or a variable construct. This would be very strange, but + could perhaps happen in case of configuration errors!?... */ + if (recursion_level == 0 && p != end) { + rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE; + goto error_return; + } + + /* return amount of parsed text */ + return (var_rc_t)(p - begin); + + /* return with an error where as a special case the output begin is + set to the input begin and the output end to the last input parsing + position. */ + error_return: + tokenbuf_free(output); + tokenbuf_set(output, begin, p, 0); + return (var_rc_t)rc; +} + +/* +** +** ==== APPLICATION PROGRAMMING INTERFACE (API) ==== +** +*/ + +/* create variable expansion context */ +var_rc_t +var_create( + var_t **pvar) +{ + var_t *var; + + if (pvar == NULL) + return VAR_RC(VAR_ERR_INVALID_ARGUMENT); + if ((var = (var_t *)malloc(sizeof(var_t))) == NULL) + return VAR_RC(VAR_ERR_OUT_OF_MEMORY); + memset(var, 0, sizeof(var)); + var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default); + *pvar = var; + return VAR_OK; +} + +/* destroy variable expansion context */ +var_rc_t +var_destroy( + var_t *var) +{ + if (var == NULL) + return VAR_RC(VAR_ERR_INVALID_ARGUMENT); + free(var); + return VAR_OK; +} + +/* configure variable expansion context */ +var_rc_t +var_config( + var_t *var, + var_config_t mode, + ...) +{ + va_list ap; + var_rc_t rc; + + if (var == NULL) + return VAR_RC(VAR_ERR_INVALID_ARGUMENT); + va_start(ap, mode); + switch (mode) { + case VAR_CONFIG_SYNTAX: { + var_syntax_t *s; + s = (var_syntax_t *)va_arg(ap, void *); + if (s == NULL) + return VAR_RC(VAR_ERR_INVALID_ARGUMENT); + var->syntax.escape = s->escape; + var->syntax.delim_init = s->delim_init; + var->syntax.delim_open = s->delim_open; + var->syntax.delim_close = s->delim_close; + var->syntax.index_open = s->index_open; + var->syntax.index_close = s->index_close; + var->syntax.index_mark = s->index_mark; + var->syntax.name_chars = NULL; /* unused internally */ + if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass)) != VAR_OK) + return VAR_RC(rc); + if ( var->syntax_nameclass[(int)var->syntax.delim_init] + || var->syntax_nameclass[(int)var->syntax.delim_open] + || var->syntax_nameclass[(int)var->syntax.delim_close] + || var->syntax_nameclass[(int)var->syntax.escape]) + return VAR_RC(VAR_ERR_INVALID_CONFIGURATION); + break; + } + case VAR_CONFIG_CB_VALUE: { + var_cb_value_t fct; + void *ctx; + fct = (var_cb_value_t)va_arg(ap, void *); + ctx = (void *)va_arg(ap, void *); + var->cb_value_fct = fct; + var->cb_value_ctx = ctx; + break; + } + case VAR_CONFIG_CB_OPERATION: { + var_cb_operation_t fct; + void *ctx; + fct = (var_cb_operation_t)va_arg(ap, void *); + ctx = (void *)va_arg(ap, void *); + var->cb_operation_fct = fct; + var->cb_operation_ctx = ctx; + break; + } + default: + return VAR_RC(VAR_ERR_INVALID_ARGUMENT); + } + va_end(ap); + return VAR_OK; +} + +/* perform unescape operation on a buffer */ +var_rc_t +var_unescape( + var_t *var, + const char *src, int srclen, + char *dst, int dstlen, + int all) +{ + const char *end; + var_rc_t rc; + + if (var == NULL || src == NULL || dst == NULL) + return VAR_RC(VAR_ERR_INVALID_ARGUMENT); + end = src + srclen; + while (src < end) { + if (*src == '\\') { + if (++src == end) + return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER); + switch (*src) { + case '\\': + if (!all) { + *dst++ = '\\'; + } + *dst++ = '\\'; + break; + case 'n': + *dst++ = '\n'; + break; + case 't': + *dst++ = '\t'; + break; + case 'r': + *dst++ = '\r'; + break; + case 'x': + ++src; + if ((rc = expand_hex(&src, &dst, end)) != VAR_OK) + return VAR_RC(rc); + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if ( end - src >= 3 + && isdigit((int)src[1]) + && isdigit((int)src[2])) { + if ((rc = expand_octal(&src, &dst, end)) != 0) + return VAR_RC(rc); + break; + } + default: + if (!all) { + *dst++ = '\\'; + } + *dst++ = *src; + } + ++src; + } else + *dst++ = *src++; + } + *dst = EOS; + return VAR_OK; +} + +/* perform expand operation on a buffer */ +var_rc_t +var_expand( + var_t *var, + const char *src_ptr, int src_len, + char **dst_ptr, int *dst_len, + int force_expand) +{ + var_parse_t ctx; + tokenbuf_t output; + var_rc_t rc; + + /* argument sanity checks */ + if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL) + return VAR_RC(VAR_ERR_INVALID_ARGUMENT); + + /* prepare internal expansion context */ + ctx.lower = NULL; + ctx.force_expand = force_expand; + ctx.rel_lookup_flag = 0; + ctx.rel_lookup_cnt = 0; + ctx.index_this = 0; + + /* start the parsing */ + tokenbuf_init(&output); + rc = parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0); + + /* post-processing */ + if (rc >= 0) { + /* always EOS-terminate output for convinience reasons + but do not count the EOS-terminator in the length */ + if (!tokenbuf_append(&output, "\0", 1)) { + tokenbuf_free(&output); + return VAR_RC(VAR_ERR_OUT_OF_MEMORY); + } + output.end--; + + /* provide result */ + *dst_ptr = (char *)output.begin; + if (dst_len != NULL) + *dst_len = (output.end - output.begin); + rc = VAR_OK; + } + else { + /* provide result */ + if (dst_len != NULL) + *dst_len = (output.end - output.begin); + } + + return VAR_RC(rc); +} + +/* format and expand a string */ +var_rc_t +var_formatv( + var_t *var, + char **dst_ptr, int force_expand, + const char *fmt, va_list ap) +{ + var_rc_t rc; + va_list apbak; + char *cpBuf; + int nBuf; + + /* argument sanity checks */ + if (var == NULL || dst_ptr == NULL || fmt == NULL) + return VAR_RC(VAR_ERR_INVALID_ARGUMENT); + + /* determine formatting buffer length */ + apbak = ap; + nBuf = var_mvsnprintf(NULL, 0, fmt, ap); + ap = apbak; + if (nBuf == -1) + return VAR_RC(VAR_ERR_FORMATTING_FAILURE); + + /* perform formatting */ + if ((cpBuf = (char *)malloc(nBuf+1)) == NULL) + return VAR_RC(VAR_ERR_OUT_OF_MEMORY); + nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap); + if (nBuf == -1) { + free(cpBuf); + return VAR_RC(VAR_ERR_FORMATTING_FAILURE); + } + + /* perform expansion */ + if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) { + free(cpBuf); + return VAR_RC(rc); + } + + /* cleanup */ + free(cpBuf); + + return VAR_OK; +} + +/* format and expand a string */ +var_rc_t +var_format( + var_t *var, + char **dst_ptr, int force_expand, + const char *fmt, ...) +{ + var_rc_t rc; + va_list ap; + + /* argument sanity checks */ + if (var == NULL || dst_ptr == NULL || fmt == NULL) + return VAR_RC(VAR_ERR_INVALID_ARGUMENT); + + va_start(ap, fmt); + rc = var_formatv(var, dst_ptr, force_expand, fmt, ap); + va_end(ap); + + return VAR_RC(rc); +} + +/* var_rc_t to string mapping table */ +static const char *var_errors[] = { + "everything ok", /* VAR_OK = 0 */ + "incomplete named character", /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */ + "incomplete hexadecimal value", /* VAR_ERR_INCOMPLETE_HEX */ + "invalid hexadecimal value", /* VAR_ERR_INVALID_HEX */ + "octal value too large", /* VAR_ERR_OCTAL_TOO_LARGE */ + "invalid octal value", /* VAR_ERR_INVALID_OCTAL */ + "incomplete octal value", /* VAR_ERR_INCOMPLETE_OCTAL */ + "incomplete grouped hexadecimal value", /* VAR_ERR_INCOMPLETE_GROUPED_HEX */ + "incorrect character class specification", /* VAR_ERR_INCORRECT_CLASS_SPEC */ + "invalid expansion configuration", /* VAR_ERR_INVALID_CONFIGURATION */ + "out of memory", /* VAR_ERR_OUT_OF_MEMORY */ + "incomplete variable specification", /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */ + "undefined variable", /* VAR_ERR_UNDEFINED_VARIABLE */ + "input is neither text nor variable", /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */ + "unknown command character in variable", /* VAR_ERR_UNKNOWN_COMMAND_CHAR */ + "malformatted search and replace operation", /* VAR_ERR_MALFORMATTED_REPLACE */ + "unknown flag in search and replace operation", /* VAR_ERR_UNKNOWN_REPLACE_FLAG */ + "invalid regex in search and replace operation", /* VAR_ERR_INVALID_REGEX_IN_REPLACE */ + "missing parameter in command", /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */ + "empty search string in search and replace operation", /* VAR_ERR_EMPTY_SEARCH_STRING */ + "start offset missing in cut operation", /* VAR_ERR_MISSING_START_OFFSET */ + "offsets in cut operation delimited by unknown character", /* VAR_ERR_INVALID_OFFSET_DELIMITER */ + "range out of bounds in cut operation", /* VAR_ERR_RANGE_OUT_OF_BOUNDS */ + "offset out of bounds in cut operation", /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */ + "logic error in cut operation", /* VAR_ERR_OFFSET_LOGIC */ + "malformatted transpose operation", /* VAR_ERR_MALFORMATTED_TRANSPOSE */ + "source and target class mismatch in transpose operation", /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */ + "empty character class in transpose operation", /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */ + "incorrect character class in transpose operation", /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */ + "malformatted padding operation", /* VAR_ERR_MALFORMATTED_PADDING */ + "width parameter missing in padding operation", /* VAR_ERR_MISSING_PADDING_WIDTH */ + "fill string missing in padding operation", /* VAR_ERR_EMPTY_PADDING_FILL_STRING */ + "unknown quoted pair in search and replace operation", /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */ + "sub-matching reference out of range", /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */ + "invalid argument", /* VAR_ERR_INVALID_ARGUMENT */ + "incomplete quoted pair", /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */ + "lookup function does not support variable arrays", /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */ + "index of array variable contains an invalid character", /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */ + "index of array variable is incomplete", /* VAR_ERR_INCOMPLETE_INDEX_SPEC */ + "bracket expression in array variable's index not closed", /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */ + "division by zero error in index specification", /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */ + "unterminated loop construct", /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */ + "invalid character in loop limits", /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */ + "malformed operation argument list", /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */ + "undefined operation", /* VAR_ERR_UNDEFINED_OPERATION */ + "formatting failure" /* VAR_ERR_FORMATTING_FAILURE */ +}; + +/* translate a return code into its corresponding descriptive text */ +char *var_strerror(var_t *var, var_rc_t rc) +{ + char *str; + rc = (var_rc_t)(0 - rc); + if (rc < 0 || rc >= (int)sizeof(var_errors) / (int)sizeof(char *)) { + str = "unknown error"; + } else { + str = (char *)var_errors[rc]; + } + return str; +} diff --git a/bacula/src/lib/var.h b/bacula/src/lib/var.h new file mode 100644 index 0000000000..5bba30f3a8 --- /dev/null +++ b/bacula/src/lib/var.h @@ -0,0 +1,140 @@ +/* +** OSSP var - Variable Expansion +** Copyright (c) 2001-2002 Ralf S. Engelschall +** Copyright (c) 2001-2002 The OSSP Project (http://www.ossp.org/) +** Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/) +** +** This file is part of OSSP var, a variable expansion +** library which can be found at http://www.ossp.org/pkg/lib/var/. +** +** Permission to use, copy, modify, and distribute this software for +** any purpose with or without fee is hereby granted, provided that +** the above copyright notice and this permission notice appear in all +** copies. +** +** For disclaimer see below. +*/ +/* + * Modified for use with Bacula by Kern Sibbald, June 2003 + */ +/* + Copyright (C) 2000-2003 Kern Sibbald and John Walker + + 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. + + */ + + + +#ifndef __VAR_H__ +#define __VAR_H__ + +/* Error codes */ +typedef enum { + VAR_ERR_CALLBACK = -64, + VAR_ERR_FORMATTING_FAILURE = -45, + VAR_ERR_UNDEFINED_OPERATION = -44, + VAR_ERR_MALFORMED_OPERATION_ARGUMENTS = -43, + VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS = -42, + VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT = -41, + VAR_ERR_DIVISION_BY_ZERO_IN_INDEX = -40, + VAR_ERR_UNCLOSED_BRACKET_IN_INDEX = -39, + VAR_ERR_INCOMPLETE_INDEX_SPEC = -37, + VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC = -36, + VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED = -35, + VAR_ERR_INCOMPLETE_QUOTED_PAIR = -34, + VAR_ERR_INVALID_ARGUMENT = -34, + VAR_ERR_SUBMATCH_OUT_OF_RANGE = -33, + VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE = -32, + VAR_ERR_EMPTY_PADDING_FILL_STRING = -31, + VAR_ERR_MISSING_PADDING_WIDTH = -30, + VAR_ERR_MALFORMATTED_PADDING = -29, + VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC = -28, + VAR_ERR_EMPTY_TRANSPOSE_CLASS = -27, + VAR_ERR_TRANSPOSE_CLASSES_MISMATCH = -26, + VAR_ERR_MALFORMATTED_TRANSPOSE = -25, + VAR_ERR_OFFSET_LOGIC = -24, + VAR_ERR_OFFSET_OUT_OF_BOUNDS = -23, + VAR_ERR_RANGE_OUT_OF_BOUNDS = -22, + VAR_ERR_INVALID_OFFSET_DELIMITER = -21, + VAR_ERR_MISSING_START_OFFSET = -20, + VAR_ERR_EMPTY_SEARCH_STRING = -19, + VAR_ERR_MISSING_PARAMETER_IN_COMMAND = -18, + VAR_ERR_INVALID_REGEX_IN_REPLACE = -17, + VAR_ERR_UNKNOWN_REPLACE_FLAG = -16, + VAR_ERR_MALFORMATTED_REPLACE = -15, + VAR_ERR_UNKNOWN_COMMAND_CHAR = -14, + VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE = -13, + VAR_ERR_UNDEFINED_VARIABLE = -12, + VAR_ERR_INCOMPLETE_VARIABLE_SPEC = -11, + VAR_ERR_OUT_OF_MEMORY = -10, + VAR_ERR_INVALID_CONFIGURATION = -9, + VAR_ERR_INCORRECT_CLASS_SPEC = -8, + VAR_ERR_INCOMPLETE_GROUPED_HEX = -7, + VAR_ERR_INCOMPLETE_OCTAL = -6, + VAR_ERR_INVALID_OCTAL = -5, + VAR_ERR_OCTAL_TOO_LARGE = -4, + VAR_ERR_INVALID_HEX = -3, + VAR_ERR_INCOMPLETE_HEX = -2, + VAR_ERR_INCOMPLETE_NAMED_CHARACTER = -1, + VAR_OK = 0 +} var_rc_t; + +struct var_st; +typedef struct var_st var_t; + +typedef enum { + VAR_CONFIG_SYNTAX, + VAR_CONFIG_CB_VALUE, + VAR_CONFIG_CB_OPERATION +} var_config_t; + +typedef struct { + char escape; /* default: '\' */ + char delim_init; /* default: '$' */ + char delim_open; /* default: '{' */ + char delim_close; /* default: '}' */ + char index_open; /* default: '[' */ + char index_close; /* default: ']' */ + char index_mark; /* default: '#' */ + char *name_chars; /* default: "a-zA-Z0-9_" */ +} var_syntax_t; + +typedef var_rc_t (*var_cb_value_t)( + var_t *var, void *ctx, + const char *var_ptr, int var_len, int var_inc, int var_idx, + const char **val_ptr, int *val_len, int *val_size +); + +typedef var_rc_t (*var_cb_operation_t)( + var_t *var, void *ctx, + const char *op_ptr, int op_len, + const char *arg_ptr, int arg_len, + const char *val_ptr, int val_len, + const char **out_ptr, int *out_len, int *out_size +); + + +var_rc_t var_create (var_t **var); +var_rc_t var_destroy (var_t *var); +var_rc_t var_config (var_t *var, var_config_t mode, ...); +var_rc_t var_unescape (var_t *var, const char *src_ptr, int src_len, char *dst_ptr, int dst_len, int all); +var_rc_t var_expand (var_t *var, const char *src_ptr, int src_len, char **dst_ptr, int *dst_len, int force_expand); +var_rc_t var_formatv (var_t *var, char **dst_ptr, int force_expand, const char *fmt, va_list ap); +var_rc_t var_format (var_t *var, char **dst_ptr, int force_expand, const char *fmt, ...); +char *var_strerror (var_t *var, var_rc_t rc); + +#endif /* __VAR_H__ */ diff --git a/bacula/src/stored/acquire.c b/bacula/src/stored/acquire.c index 98ac1eac9f..5ea0884d76 100644 --- a/bacula/src/stored/acquire.c +++ b/bacula/src/stored/acquire.c @@ -108,7 +108,7 @@ int acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) default_path: tape_previously_mounted = 1; Dmsg0(200, "dir_get_volume_info\n"); - if (!dir_get_volume_info(jcr, 0)) { + if (!dir_get_volume_info(jcr, GET_VOL_INFO_FOR_READ)) { Jmsg1(jcr, M_WARNING, 0, "%s", jcr->errmsg); } /* Call autochanger only once unless ask_sysop called */ @@ -186,7 +186,7 @@ DEVICE * acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) * dir_find_next_appendable_volume */ pm_strcpy(&jcr->VolumeName, dev->VolHdr.VolName); - if (!dir_get_volume_info(jcr, 1) && + if (!dir_get_volume_info(jcr, GET_VOL_INFO_FOR_WRITE) && !(dir_find_next_appendable_volume(jcr) && strcmp(dev->VolHdr.VolName, jcr->VolumeName) == 0)) { /* wrong tape mounted */ if (dev->num_writers != 0) { @@ -274,14 +274,6 @@ int release_device(JCR *jcr, DEVICE *dev) } else if (dev->num_writers > 0) { dev->num_writers--; - if (dev->state & ST_TAPE) { - jcr->EndBlock = dev->EndBlock; - jcr->EndFile = dev->EndFile; - Dmsg2(200, "Release device: EndFile=%u EndBlock=%u\n", jcr->EndFile, jcr->EndBlock); - } else { - jcr->EndBlock = (uint32_t)dev->file_addr; - jcr->EndFile = (uint32_t)(dev->file_addr >> 32); - } Dmsg1(100, "There are %d writers in release_device\n", dev->num_writers); if (dev->num_writers == 0) { /* If we have fully acquired the tape */ diff --git a/bacula/src/stored/append.c b/bacula/src/stored/append.c index dd81af9e25..bac65e2861 100644 --- a/bacula/src/stored/append.c +++ b/bacula/src/stored/append.c @@ -66,6 +66,7 @@ int do_append_data(JCR *jcr) Dmsg1(20, "Begin append device=%s\n", dev_name(dev)); block = new_block(dev); + memset(&rec, 0, sizeof(rec)); /* * Acquire output device for writing. Note, after acquiring a @@ -89,7 +90,6 @@ int do_append_data(JCR *jcr) ok = FALSE; } - memset(&rec, 0, sizeof(rec)); /* * Get Data from File daemon, write to device. To clarify what is @@ -109,7 +109,7 @@ int do_append_data(JCR *jcr) * and 3. for the MD5 if any. */ jcr->VolFirstIndex = 0; - time(&jcr->run_time); /* start counting time for rates */ + jcr->run_time = time(NULL); /* start counting time for rates */ for (last_file_index = 0; ok && !job_canceled(jcr); ) { char info[100]; @@ -129,7 +129,6 @@ int do_append_data(JCR *jcr) ok = FALSE; break; } - ds->msg[ds->msglen] = 0; if (sscanf(ds->msg, "%ld %ld %100s", &file_index, &stream, info) != 3) { Jmsg1(jcr, M_FATAL, 0, _("Malformed data header from FD: %s\n"), ds->msg); ok = FALSE; @@ -148,6 +147,7 @@ int do_append_data(JCR *jcr) if (jcr->VolFirstIndex == 0) { jcr->VolFirstIndex = file_index; } + jcr->VolLastIndex = file_index; last_file_index = file_index; } @@ -236,9 +236,9 @@ int do_append_data(JCR *jcr) set_jcr_job_status(jcr, JS_ErrorTerminated); ok = FALSE; } - /* Write out final block of this session */ + /* Flush out final partial block of this session */ if (!write_block_to_device(jcr, dev, block)) { - Pmsg0(000, _("Set ok=FALSE after write_block_to_device.\n")); + Dmsg0(100, _("Set ok=FALSE after write_block_to_device.\n")); set_jcr_job_status(jcr, JS_ErrorTerminated); ok = FALSE; } diff --git a/bacula/src/stored/askdir.c b/bacula/src/stored/askdir.c index 781ee94941..bad70ce0af 100644 --- a/bacula/src/stored/askdir.c +++ b/bacula/src/stored/askdir.c @@ -111,14 +111,15 @@ static int do_request_volume_info(JCR *jcr) * * Volume information returned in jcr */ -int dir_get_volume_info(JCR *jcr, int writing) +int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw writing) { BSOCK *dir = jcr->dir_bsock; bstrncpy(jcr->VolCatInfo.VolCatName, jcr->VolumeName, sizeof(jcr->VolCatInfo.VolCatName)); Dmsg1(200, "dir_get_volume_info=%s\n", jcr->VolCatInfo.VolCatName); bash_spaces(jcr->VolCatInfo.VolCatName); - bnet_fsend(dir, Get_Vol_Info, jcr->Job, jcr->VolCatInfo.VolCatName, writing); + bnet_fsend(dir, Get_Vol_Info, jcr->Job, jcr->VolCatInfo.VolCatName, + writing==GET_VOL_INFO_FOR_WRITE?1:0); return do_request_volume_info(jcr); } @@ -188,12 +189,12 @@ int dir_create_jobmedia_record(JCR *jcr) { BSOCK *dir = jcr->dir_bsock; - if (jcr->VolFirstIndex == 0) { + if (!jcr->WroteVol) { return 1; /* nothing written to tape */ } bnet_fsend(dir, Create_job_media, jcr->Job, - jcr->VolFirstIndex, jcr->JobFiles, + jcr->VolFirstIndex, jcr->VolLastIndex, jcr->StartFile, jcr->EndFile, jcr->StartBlock, jcr->EndBlock); Dmsg1(100, "create_jobmedia(): %s", dir->msg); @@ -271,7 +272,7 @@ int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) if (job_canceled(jcr)) { Mmsg(&dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"), jcr->Job, jcr->dev_name); - Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg); + Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg); return 0; } if (dir_find_next_appendable_volume(jcr)) { /* get suggested volume */ @@ -342,7 +343,7 @@ Please use the \"label\" command to create a new Volume for:\n\ num_wait = 0; /* If no VolumeName, and cannot get one, try again */ if (jcr->VolumeName[0] == 0 && - !dir_find_next_appendable_volume(jcr)) { + !dir_find_next_appendable_volume(jcr) && !job_canceled(jcr)) { Jmsg(jcr, M_MOUNT, 0, _( "Someone woke me up, but I cannot find any appendable\n\ volumes for Job=%s.\n"), jcr->Job); diff --git a/bacula/src/stored/bcopy.c b/bacula/src/stored/bcopy.c index 11b5521532..f0f985eaf7 100644 --- a/bacula/src/stored/bcopy.c +++ b/bacula/src/stored/bcopy.c @@ -37,8 +37,8 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec); /* Global variables */ static DEVICE *in_dev = NULL; static DEVICE *out_dev = NULL; -static JCR *in_jcr; /* input jcr */ -static JCR *out_jcr; /* output jcr */ +static JCR *in_jcr; /* input jcr */ +static JCR *out_jcr; /* output jcr */ static BSR *bsr = NULL; static char *wd = "/tmp"; static int list_records = 0; @@ -78,42 +78,42 @@ int main (int argc, char *argv[]) while ((ch = getopt(argc, argv, "b:c:d:mn:p:rsu:vV:w:?")) != -1) { switch (ch) { case 'b': - bsr = parse_bsr(NULL, optarg); - break; + bsr = parse_bsr(NULL, optarg); + break; case 'c': /* specify config file */ - if (configfile != NULL) { - free(configfile); - } - configfile = bstrdup(optarg); - break; + if (configfile != NULL) { + free(configfile); + } + configfile = bstrdup(optarg); + break; case 'd': /* debug level */ - debug_level = atoi(optarg); - if (debug_level <= 0) - debug_level = 1; - break; + debug_level = atoi(optarg); + if (debug_level <= 0) + debug_level = 1; + break; case 'v': - verbose++; - break; + verbose++; + break; case 'i': /* input Volume name */ - iVolumeName = optarg; - break; + iVolumeName = optarg; + break; case 'o': /* output Volume name */ - oVolumeName = optarg; - break; + oVolumeName = optarg; + break; case 'w': - wd = optarg; - break; + wd = optarg; + break; case '?': - default: - usage(); + default: + usage(); } } @@ -135,7 +135,7 @@ int main (int argc, char *argv[]) /* Setup and acquire input device for reading */ in_jcr = setup_jcr("bcopy", argv[0], bsr, iVolumeName); - in_dev = setup_to_access_device(in_jcr, 1); /* read device */ + in_dev = setup_to_access_device(in_jcr, 1); /* read device */ if (!in_dev) { exit(1); } @@ -144,7 +144,7 @@ int main (int argc, char *argv[]) out_jcr = setup_jcr("bcopy", argv[1], bsr, oVolumeName); out_dev = setup_to_access_device(out_jcr, 0); /* no acquire */ if (!out_dev) { - exit(1); + exit(1); } /* For we must now acquire the device for writing */ out_block = new_block(out_dev); @@ -183,8 +183,8 @@ static void record_cb(JCR *in_jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *re { if (list_records) { Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"), - rec->VolSessionId, rec->VolSessionTime, rec->FileIndex, - rec->Stream, rec->data_len); + rec->VolSessionId, rec->VolSessionTime, rec->FileIndex, + rec->Stream, rec->data_len); } /* * Check for Start or End of Session Record @@ -193,44 +193,44 @@ static void record_cb(JCR *in_jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *re if (rec->FileIndex < 0) { if (verbose > 1) { - dump_label_record(dev, rec, 1); + dump_label_record(dev, rec, 1); } switch (rec->FileIndex) { - case PRE_LABEL: + case PRE_LABEL: Pmsg0(000, "Volume is prelabeled. This volume cannot be copied.\n"); - return; - case VOL_LABEL: + return; + case VOL_LABEL: Pmsg0(000, "Volume label not copied.\n"); - return; - case SOS_LABEL: - jobs++; - break; - case EOS_LABEL: - while (!write_record_to_block(out_block, rec)) { + return; + case SOS_LABEL: + jobs++; + break; + case EOS_LABEL: + while (!write_record_to_block(out_block, rec)) { Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len, - rec->remainder); - if (!write_block_to_device(out_jcr, out_dev, out_block)) { + rec->remainder); + if (!write_block_to_device(out_jcr, out_dev, out_block)) { Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n", - dev_name(out_dev), strerror_dev(out_dev)); + dev_name(out_dev), strerror_dev(out_dev)); Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"), - strerror_dev(out_dev)); - } - } - if (!write_block_to_device(out_jcr, out_dev, out_block)) { + strerror_dev(out_dev)); + } + } + if (!write_block_to_device(out_jcr, out_dev, out_block)) { Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n", - dev_name(out_dev), strerror_dev(out_dev)); + dev_name(out_dev), strerror_dev(out_dev)); Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"), - strerror_dev(out_dev)); - } - break; - case EOM_LABEL: + strerror_dev(out_dev)); + } + break; + case EOM_LABEL: Pmsg0(000, "EOM label not copied.\n"); - return; - case EOT_LABEL: /* end of all tapes */ + return; + case EOT_LABEL: /* end of all tapes */ Pmsg0(000, "EOT label not copied.\n"); - return; - default: - break; + return; + default: + break; } } @@ -238,13 +238,13 @@ static void record_cb(JCR *in_jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *re records++; while (!write_record_to_block(out_block, rec)) { Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len, - rec->remainder); + rec->remainder); if (!write_block_to_device(out_jcr, out_dev, out_block)) { Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n", - dev_name(out_dev), strerror_dev(out_dev)); + dev_name(out_dev), strerror_dev(out_dev)); Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"), - strerror_dev(out_dev)); - break; + strerror_dev(out_dev)); + break; } } return; @@ -252,19 +252,19 @@ static void record_cb(JCR *in_jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *re /* Dummies to replace askdir.c */ -int dir_get_volume_info(JCR *jcr, int writing) { return 1;} -int dir_find_next_appendable_volume(JCR *jcr) { return 1;} -int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; } -int dir_create_jobmedia_record(JCR *jcr) { return 1; } -int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; } -int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;} -int dir_send_job_status(JCR *jcr) {return 1;} +int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw writing) { return 1;} +int dir_find_next_appendable_volume(JCR *jcr) { return 1;} +int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; } +int dir_create_jobmedia_record(JCR *jcr) { return 1; } +int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; } +int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;} +int dir_send_job_status(JCR *jcr) {return 1;} int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev) { fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ", in_jcr->VolumeName, dev_name(dev)); - getchar(); + getchar(); return 1; } diff --git a/bacula/src/stored/bextract.c b/bacula/src/stored/bextract.c index ba9f4443bc..fea67c21f4 100644 --- a/bacula/src/stored/bextract.c +++ b/bacula/src/stored/bextract.c @@ -60,9 +60,9 @@ static int prog_name_msg = 0; static int win32_data_msg = 0; static char *VolumeName = NULL; -static char *wbuf; /* write buffer address */ -static uint32_t wsize; /* write size */ -static uint64_t fileAddr = 0; /* file write address */ +static char *wbuf; /* write buffer address */ +static uint32_t wsize; /* write size */ +static uint64_t fileAddr = 0; /* file write address */ #define CONFIG_FILE "bacula-sd.conf" char *configfile; @@ -93,7 +93,7 @@ int main (int argc, char *argv[]) working_directory = "/tmp"; my_name_is(argc, argv, "bextract"); - init_msg(NULL, NULL); /* setup message handler */ + init_msg(NULL, NULL); /* setup message handler */ memset(ff, 0, sizeof(FF_PKT)); init_include_exclude_files(ff); @@ -102,59 +102,59 @@ int main (int argc, char *argv[]) while ((ch = getopt(argc, argv, "b:c:d:e:i:?")) != -1) { switch (ch) { case 'b': /* bootstrap file */ - bsr = parse_bsr(NULL, optarg); -// dump_bsr(bsr); - break; + bsr = parse_bsr(NULL, optarg); +// dump_bsr(bsr); + break; case 'c': /* specify config file */ - if (configfile != NULL) { - free(configfile); - } - configfile = bstrdup(optarg); - break; + if (configfile != NULL) { + free(configfile); + } + configfile = bstrdup(optarg); + break; case 'd': /* debug level */ - debug_level = atoi(optarg); - if (debug_level <= 0) - debug_level = 1; - break; + debug_level = atoi(optarg); + if (debug_level <= 0) + debug_level = 1; + break; case 'e': /* exclude list */ if ((fd = fopen(optarg, "r")) == NULL) { Pmsg2(0, "Could not open exclude file: %s, ERR=%s\n", - optarg, strerror(errno)); - exit(1); - } - while (fgets(line, sizeof(line), fd) != NULL) { - strip_trailing_junk(line); + optarg, strerror(errno)); + exit(1); + } + while (fgets(line, sizeof(line), fd) != NULL) { + strip_trailing_junk(line); Dmsg1(900, "add_exclude %s\n", line); - add_fname_to_exclude_list(ff, line); - } - fclose(fd); - break; + add_fname_to_exclude_list(ff, line); + } + fclose(fd); + break; case 'i': /* include list */ if ((fd = fopen(optarg, "r")) == NULL) { Pmsg2(0, "Could not open include file: %s, ERR=%s\n", - optarg, strerror(errno)); - exit(1); - } - while (fgets(line, sizeof(line), fd) != NULL) { - strip_trailing_junk(line); + optarg, strerror(errno)); + exit(1); + } + while (fgets(line, sizeof(line), fd) != NULL) { + strip_trailing_junk(line); Dmsg1(900, "add_include %s\n", line); - add_fname_to_include_list(ff, 0, line); - } - fclose(fd); - got_inc = TRUE; - break; + add_fname_to_include_list(ff, 0, line); + } + fclose(fd); + got_inc = TRUE; + break; case 'V': /* Volume name */ - VolumeName = optarg; - break; + VolumeName = optarg; + break; case '?': default: - usage(); + usage(); } /* end switch */ } /* end while */ @@ -172,7 +172,7 @@ int main (int argc, char *argv[]) parse_config(configfile); - if (!got_inc) { /* If no include file, */ + if (!got_inc) { /* If no include file, */ add_fname_to_include_list(ff, 0, "/"); /* include everything */ } @@ -184,11 +184,11 @@ int main (int argc, char *argv[]) } if (prog_name_msg) { Pmsg1(000, "%d Program Name and/or Program Data Stream records ignored.\n", - prog_name_msg); + prog_name_msg); } if (win32_data_msg) { Pmsg1(000, "%d Win32 data or Win32 gzip data stream records. Ignored.\n", - win32_data_msg); + win32_data_msg); } return 0; } @@ -205,7 +205,7 @@ static void do_extract(char *devname) /* Make sure where directory exists and that it is a directory */ if (stat(where, &statp) < 0) { Emsg2(M_ERROR_TERM, 0, "Cannot stat %s. It must exist. ERR=%s\n", - where, strerror(errno)); + where, strerror(errno)); } if (!S_ISDIR(statp.st_mode)) { Emsg1(M_ERROR_TERM, 0, "%s must be a directory.\n", where); @@ -254,11 +254,11 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec) * close the output file. */ if (extract) { - if (!is_bopen(&bfd)) { + if (!is_bopen(&bfd)) { Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n")); - } - extract = FALSE; - set_attributes(jcr, attr, &bfd); + } + extract = FALSE; + set_attributes(jcr, attr, &bfd); } if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) { @@ -267,46 +267,46 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec) if (attr->file_index != rec->FileIndex) { Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"), - rec->FileIndex, attr->file_index); + rec->FileIndex, attr->file_index); } - + if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) { - uint32_t LinkFI; - - decode_stat(attr->attr, &attr->statp, &LinkFI); - - build_attr_output_fnames(jcr, attr); - - extract = FALSE; - stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS); - switch (stat) { - case CF_ERROR: - case CF_SKIP: - break; - case CF_EXTRACT: - extract = TRUE; - print_ls_output(jcr, attr); - num_files++; - fileAddr = 0; - break; - case CF_CREATED: - set_attributes(jcr, attr, &bfd); - print_ls_output(jcr, attr); - num_files++; - fileAddr = 0; - break; - } + uint32_t LinkFI; + + decode_stat(attr->attr, &attr->statp, &LinkFI); + + build_attr_output_fnames(jcr, attr); + + extract = FALSE; + stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS); + switch (stat) { + case CF_ERROR: + case CF_SKIP: + break; + case CF_EXTRACT: + extract = TRUE; + print_ls_output(jcr, attr); + num_files++; + fileAddr = 0; + break; + case CF_CREATED: + set_attributes(jcr, attr, &bfd); + print_ls_output(jcr, attr); + num_files++; + fileAddr = 0; + break; + } } /* Windows Backup data stream */ case STREAM_WIN32_DATA: if (!is_win32_backup()) { - if (!non_support_data) { + if (!non_support_data) { Jmsg(jcr, M_ERROR, 0, _("Win32 backup data not supported on this Client.\n")); - } - extract = FALSE; - non_support_data++; - return; + } + extract = FALSE; + non_support_data++; + return; } goto extract_data; @@ -317,42 +317,42 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec) extract_data: if (extract) { - if (rec->Stream == STREAM_SPARSE_DATA) { - ser_declare; - uint64_t faddr; - wbuf = rec->data + SPARSE_FADDR_SIZE; - wsize = rec->data_len - SPARSE_FADDR_SIZE; - ser_begin(rec->data, SPARSE_FADDR_SIZE); - unser_uint64(faddr); - if (fileAddr != faddr) { - fileAddr = faddr; - if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) { + if (rec->Stream == STREAM_SPARSE_DATA) { + ser_declare; + uint64_t faddr; + wbuf = rec->data + SPARSE_FADDR_SIZE; + wsize = rec->data_len - SPARSE_FADDR_SIZE; + ser_begin(rec->data, SPARSE_FADDR_SIZE); + unser_uint64(faddr); + if (fileAddr != faddr) { + fileAddr = faddr; + if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) { Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"), - attr->ofname, strerror(errno)); - } - } - } else { - wbuf = rec->data; - wsize = rec->data_len; - } - total += wsize; + attr->ofname, strerror(errno)); + } + } + } else { + wbuf = rec->data; + wsize = rec->data_len; + } + total += wsize; Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total); - if ((uint32_t)bwrite(&bfd, wbuf, wsize) != wsize) { + if ((uint32_t)bwrite(&bfd, wbuf, wsize) != wsize) { Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"), - attr->ofname, strerror(errno)); - } - fileAddr += wsize; + attr->ofname, strerror(errno)); + } + fileAddr += wsize; } /* Windows Backup GZIP data stream */ case STREAM_WIN32_GZIP_DATA: if (!is_win32_backup()) { - if (!non_support_attr) { + if (!non_support_attr) { Jmsg(jcr, M_ERROR, 0, _("Win32 GZIP backup data not supported on this Client.\n")); - } - extract = FALSE; - non_support_attr++; - return; + } + extract = FALSE; + non_support_attr++; + return; } /* Fall through desired */ @@ -361,49 +361,49 @@ extract_data: case STREAM_SPARSE_GZIP_DATA: #ifdef HAVE_LIBZ if (extract) { - uLongf compress_len; - int stat; - - if (rec->Stream == STREAM_SPARSE_GZIP_DATA) { - ser_declare; - uint64_t faddr; - wbuf = rec->data + SPARSE_FADDR_SIZE; - wsize = rec->data_len - SPARSE_FADDR_SIZE; - ser_begin(rec->data, SPARSE_FADDR_SIZE); - unser_uint64(faddr); - if (fileAddr != faddr) { - fileAddr = faddr; - if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) { + uLongf compress_len; + int stat; + + if (rec->Stream == STREAM_SPARSE_GZIP_DATA) { + ser_declare; + uint64_t faddr; + wbuf = rec->data + SPARSE_FADDR_SIZE; + wsize = rec->data_len - SPARSE_FADDR_SIZE; + ser_begin(rec->data, SPARSE_FADDR_SIZE); + unser_uint64(faddr); + if (fileAddr != faddr) { + fileAddr = faddr; + if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) { Emsg2(M_ERROR, 0, _("Seek error on %s: %s\n"), - attr->ofname, strerror(errno)); - } - } - } else { - wbuf = rec->data; - wsize = rec->data_len; - } - compress_len = compress_buf_size; - if ((stat=uncompress((Bytef *)compress_buf, &compress_len, - (const Bytef *)wbuf, (uLong)wsize) != Z_OK)) { + attr->ofname, strerror(errno)); + } + } + } else { + wbuf = rec->data; + wsize = rec->data_len; + } + compress_len = compress_buf_size; + if ((stat=uncompress((Bytef *)compress_buf, &compress_len, + (const Bytef *)wbuf, (uLong)wsize) != Z_OK)) { Emsg1(M_ERROR_TERM, 0, _("Uncompression error. ERR=%d\n"), stat); - } + } Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total); - if ((uLongf)bwrite(&bfd, compress_buf, (size_t)compress_len) != compress_len) { + if ((uLongf)bwrite(&bfd, compress_buf, (size_t)compress_len) != compress_len) { Pmsg0(0, "===Write error===\n"); Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"), - attr->ofname, strerror(errno)); - } - total += compress_len; - fileAddr += compress_len; + attr->ofname, strerror(errno)); + } + total += compress_len; + fileAddr += compress_len; Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len, - compress_len); + compress_len); } #else if (extract) { Emsg0(M_ERROR, 0, "GZIP data stream found, but GZIP not configured!\n"); - extract = FALSE; - return; + extract = FALSE; + return; } #endif @@ -415,21 +415,21 @@ extract_data: case STREAM_PROGRAM_DATA: if (!prog_name_msg) { Pmsg0(000, "Got Program Name or Data Stream. Ignored.\n"); - prog_name_msg++; + prog_name_msg++; } break; default: /* If extracting, wierd stream (not 1 or 2), close output file anyway */ if (extract) { - if (!is_bopen(&bfd)) { + if (!is_bopen(&bfd)) { Emsg0(M_ERROR, 0, "Logic error output file should be open but is not.\n"); - } - extract = FALSE; - set_attributes(jcr, attr, &bfd); + } + extract = FALSE; + set_attributes(jcr, attr, &bfd); } Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"), - rec->Stream); + rec->Stream); break; } /* end switch */ @@ -439,19 +439,19 @@ extract_data: /* Dummies to replace askdir.c */ -int dir_get_volume_info(JCR *jcr, int writing) { return 1;} -int dir_find_next_appendable_volume(JCR *jcr) { return 1;} -int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; } -int dir_create_jobmedia_record(JCR *jcr) { return 1; } -int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; } -int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;} -int dir_send_job_status(JCR *jcr) {return 1;} +int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw writing) { return 1;} +int dir_find_next_appendable_volume(JCR *jcr) { return 1;} +int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; } +int dir_create_jobmedia_record(JCR *jcr) { return 1; } +int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; } +int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;} +int dir_send_job_status(JCR *jcr) {return 1;} int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev) { fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ", jcr->VolumeName, dev_name(dev)); - getchar(); + getchar(); return 1; } diff --git a/bacula/src/stored/block.c b/bacula/src/stored/block.c index ed20383c11..4f49ded7e0 100644 --- a/bacula/src/stored/block.c +++ b/bacula/src/stored/block.c @@ -276,9 +276,27 @@ int write_block_to_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) { int stat = 1; lock_device(dev); + + /* + * If a new volume has been mounted since our last write + * Create a JobMedia record for the previous volume written, + * and set new parameters to write this volume + */ + if (jcr->NewVol) { + /* Create a jobmedia record for this job */ + if (!dir_create_jobmedia_record(jcr)) { + Jmsg(jcr, M_ERROR, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"), + jcr->VolCatInfo.VolCatName, jcr->Job); + unlock_device(dev); + return 0; + } + set_new_volume_parameters(jcr, dev); + } + if (!write_block_to_dev(jcr, dev, block)) { stat = fixup_device_block_write_error(jcr, dev, block); } + unlock_device(dev); return stat; } @@ -455,6 +473,9 @@ int write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) #endif return 0; } + + /* Do housekeeping */ + dev->VolCatInfo.VolCatBytes += block->binbuf; dev->VolCatInfo.VolCatBlocks++; dev->file_addr += wlen; @@ -463,6 +484,16 @@ int write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) dev->block_num++; block->BlockNumber++; + /* Update jcr values */ + if (dev->state & ST_TAPE) { + jcr->EndBlock = dev->EndBlock; + jcr->EndFile = dev->EndFile; + } else { + jcr->EndBlock = (uint32_t)dev->file_addr; + jcr->EndFile = (uint32_t)(dev->file_addr >> 32); + } + jcr->WroteVol = true; + Dmsg2(190, "write_block: wrote block %d bytes=%d\n", dev->block_num, wlen); empty_block(block); diff --git a/bacula/src/stored/bls.c b/bacula/src/stored/bls.c index 4a72d54390..f32a9bb30e 100644 --- a/bacula/src/stored/bls.c +++ b/bacula/src/stored/bls.c @@ -91,7 +91,7 @@ int main (int argc, char *argv[]) working_directory = "/tmp"; my_name_is(argc, argv, "bls"); - init_msg(NULL, NULL); /* initialize message handler */ + init_msg(NULL, NULL); /* initialize message handler */ memset(&ff, 0, sizeof(ff)); init_include_exclude_files(&ff); @@ -99,73 +99,73 @@ int main (int argc, char *argv[]) while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLtvV:?")) != -1) { switch (ch) { case 'b': - bsrName = optarg; - break; + bsrName = optarg; + break; case 'c': /* specify config file */ - if (configfile != NULL) { - free(configfile); - } - configfile = bstrdup(optarg); - break; + if (configfile != NULL) { + free(configfile); + } + configfile = bstrdup(optarg); + break; case 'd': /* debug level */ - debug_level = atoi(optarg); - if (debug_level <= 0) - debug_level = 1; - break; + debug_level = atoi(optarg); + if (debug_level <= 0) + debug_level = 1; + break; case 'e': /* exclude list */ if ((fd = fopen(optarg, "r")) == NULL) { Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"), - optarg, strerror(errno)); - exit(1); - } - while (fgets(line, sizeof(line), fd) != NULL) { - strip_trailing_junk(line); + optarg, strerror(errno)); + exit(1); + } + while (fgets(line, sizeof(line), fd) != NULL) { + strip_trailing_junk(line); Dmsg1(100, "add_exclude %s\n", line); - add_fname_to_exclude_list(&ff, line); - } - fclose(fd); - break; + add_fname_to_exclude_list(&ff, line); + } + fclose(fd); + break; case 'i': /* include list */ if ((fd = fopen(optarg, "r")) == NULL) { Pmsg2(0, "Could not open include file: %s, ERR=%s\n", - optarg, strerror(errno)); - exit(1); - } - while (fgets(line, sizeof(line), fd) != NULL) { - strip_trailing_junk(line); + optarg, strerror(errno)); + exit(1); + } + while (fgets(line, sizeof(line), fd) != NULL) { + strip_trailing_junk(line); Dmsg1(100, "add_include %s\n", line); - add_fname_to_include_list(&ff, 0, line); - } - fclose(fd); - break; + add_fname_to_include_list(&ff, 0, line); + } + fclose(fd); + break; case 'j': - list_jobs = TRUE; - break; + list_jobs = TRUE; + break; case 'k': - list_blocks = TRUE; - break; + list_blocks = TRUE; + break; case 'L': - dump_label = TRUE; - break; + dump_label = TRUE; + break; case 'v': - verbose++; - break; + verbose++; + break; case 'V': /* Volume name */ - VolumeName = optarg; - break; + VolumeName = optarg; + break; case '?': default: - usage(); + usage(); } /* end switch */ } /* end while */ @@ -189,12 +189,12 @@ int main (int argc, char *argv[]) for (i=0; i < argc; i++) { if (bsrName) { - bsr = parse_bsr(NULL, bsrName); + bsr = parse_bsr(NULL, bsrName); } jcr = setup_jcr("bls", argv[i], bsr, VolumeName); dev = setup_to_access_device(jcr, 1); /* acquire for read */ if (!dev) { - exit(1); + exit(1); } rec = new_record(); block = new_block(dev); @@ -206,15 +206,15 @@ int main (int argc, char *argv[]) if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */ Pmsg1(0, "\n\ Warning, this Volume is a continuation of Volume %s\n", - dev->VolHdr.PrevVolName); + dev->VolHdr.PrevVolName); } if (list_blocks) { - do_blocks(argv[i]); + do_blocks(argv[i]); } else if (list_jobs) { - do_jobs(argv[i]); + do_jobs(argv[i]); } else { - do_ls(argv[i]); + do_ls(argv[i]); } do_close(jcr); } @@ -246,45 +246,45 @@ static void do_blocks(char *infname) for ( ;; ) { if (!read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) { Dmsg1(100, "!read_block(): ERR=%s\n", strerror_dev(dev)); - if (dev->state & ST_EOT) { - if (!mount_next_read_volume(jcr, dev, block)) { + if (dev->state & ST_EOT) { + if (!mount_next_read_volume(jcr, dev, block)) { Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"), - dev->file, dev_name(dev), jcr->VolumeName); - break; - } - /* Read and discard Volume label */ - DEV_RECORD *record; - record = new_record(); - read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK); - read_record_from_block(block, record); - get_session_record(dev, record, &sessrec); - free_record(record); + dev->file, dev_name(dev), jcr->VolumeName); + break; + } + /* Read and discard Volume label */ + DEV_RECORD *record; + record = new_record(); + read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK); + read_record_from_block(block, record); + get_session_record(dev, record, &sessrec); + free_record(record); Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), jcr->VolumeName); - - } else if (dev->state & ST_EOF) { + + } else if (dev->state & ST_EOF) { Jmsg(jcr, M_INFO, 0, _("Got EOF at file %u on device %s, Volume \"%s\"\n"), - dev->file, dev_name(dev), jcr->VolumeName); + dev->file, dev_name(dev), jcr->VolumeName); Dmsg0(20, "read_record got eof. try again\n"); - continue; - } else if (dev->state & ST_SHORT) { + continue; + } else if (dev->state & ST_SHORT) { Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg); - continue; - } else { - /* I/O error */ - display_tape_error_status(jcr, dev); - break; - } + continue; + } else { + /* I/O error */ + display_tape_error_status(jcr, dev); + break; + } } Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n", - block->BlockNumber, block->block_len, block->BlockVer, - block->VolSessionId, block->VolSessionTime); + block->BlockNumber, block->block_len, block->BlockVer, + block->VolSessionId, block->VolSessionTime); if (verbose == 1) { - read_record_from_block(block, rec); + read_record_from_block(block, rec); Pmsg7(-1, "Block: %u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n", - block->BlockNumber, block->block_len, - FI_to_ascii(rec->FileIndex), rec->VolSessionId, rec->VolSessionTime, - stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len); - rec->remainder = 0; + block->BlockNumber, block->block_len, + FI_to_ascii(rec->FileIndex), rec->VolSessionId, rec->VolSessionTime, + stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len); + rec->remainder = 0; } else if (verbose > 1) { dump_block(block, ""); } else { @@ -343,14 +343,14 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec) if (attr->file_index != rec->FileIndex) { Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"), - rec->FileIndex, attr->file_index); + rec->FileIndex, attr->file_index); } decode_stat(attr->attr, &attr->statp, &LinkFI); if (file_is_included(&ff, attr->fname) && !file_is_excluded(&ff, attr->fname)) { - print_ls_output(jcr, attr); - num_files++; + print_ls_output(jcr, attr); + num_files++; } } return; @@ -364,44 +364,44 @@ static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sess switch (rec->FileIndex) { case PRE_LABEL: rtype = "Fresh Volume Label"; - break; + break; case VOL_LABEL: rtype = "Volume Label"; - unser_volume_label(dev, rec); - break; + unser_volume_label(dev, rec); + break; case SOS_LABEL: rtype = "Begin Session"; - unser_session_label(sessrec, rec); - break; + unser_session_label(sessrec, rec); + break; case EOS_LABEL: rtype = "End Session"; - break; + break; case EOM_LABEL: rtype = "End of Medium"; - break; + break; default: rtype = "Unknown"; - break; + break; } Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n", - rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len); + rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len); } /* Dummies to replace askdir.c */ -int dir_get_volume_info(JCR *jcr, int writing) { return 1;} -int dir_find_next_appendable_volume(JCR *jcr) { return 1;} -int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; } -int dir_create_jobmedia_record(JCR *jcr) { return 1; } -int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; } -int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;} -int dir_send_job_status(JCR *jcr) {return 1;} +int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw writing) { return 1;} +int dir_find_next_appendable_volume(JCR *jcr) { return 1;} +int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; } +int dir_create_jobmedia_record(JCR *jcr) { return 1; } +int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; } +int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;} +int dir_send_job_status(JCR *jcr) {return 1;} int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev) { fprintf(stderr, "Mount Volume \"%s\" on device %s and press return when ready: ", jcr->VolumeName, dev_name(dev)); - getchar(); + getchar(); return 1; } diff --git a/bacula/src/stored/bscan.c b/bacula/src/stored/bscan.c index e01911f95e..ceeebe3de1 100644 --- a/bacula/src/stored/bscan.c +++ b/bacula/src/stored/bscan.c @@ -38,14 +38,14 @@ static void do_scan(void); static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec); static int create_file_attributes_record(B_DB *db, JCR *mjcr, - char *fname, char *lname, int type, - char *ap, DEV_RECORD *rec); + char *fname, char *lname, int type, + char *ap, DEV_RECORD *rec); static int create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl); static int update_media_record(B_DB *db, MEDIA_DBR *mr); static int create_pool_record(B_DB *db, POOL_DBR *pr); static JCR *create_job_record(B_DB *db, JOB_DBR *mr, SESSION_LABEL *label, DEV_RECORD *rec); static int update_job_record(B_DB *db, JOB_DBR *mr, SESSION_LABEL *elabel, - DEV_RECORD *rec); + DEV_RECORD *rec); static int create_client_record(B_DB *db, CLIENT_DBR *cr); static int create_fileset_record(B_DB *db, FILESET_DBR *fsr); static int create_jobmedia_record(B_DB *db, JCR *jcr); @@ -65,7 +65,7 @@ int win32_client = 0; /* Local variables */ static DEVICE *dev = NULL; static B_DB *db; -static JCR *bjcr; /* jcr for bscan */ +static JCR *bjcr; /* jcr for bscan */ static BSR *bsr = NULL; static MEDIA_DBR mr; static POOL_DBR pr; @@ -128,61 +128,61 @@ int main (int argc, char *argv[]) while ((ch = getopt(argc, argv, "b:c:d:mn:p:rsu:vVw:?")) != -1) { switch (ch) { case 'b': - bsr = parse_bsr(NULL, optarg); - break; + bsr = parse_bsr(NULL, optarg); + break; case 'c': /* specify config file */ - if (configfile != NULL) { - free(configfile); - } - configfile = bstrdup(optarg); - break; + if (configfile != NULL) { + free(configfile); + } + configfile = bstrdup(optarg); + break; case 'd': /* debug level */ - debug_level = atoi(optarg); - if (debug_level <= 0) - debug_level = 1; - break; + debug_level = atoi(optarg); + if (debug_level <= 0) + debug_level = 1; + break; case 'm': - update_vol_info = 1; - break; + update_vol_info = 1; + break; case 'n': - db_name = optarg; - break; + db_name = optarg; + break; case 'u': - db_user = optarg; - break; + db_user = optarg; + break; case 'p': - db_password = optarg; - break; + db_password = optarg; + break; case 'r': - list_records = 1; - break; + list_records = 1; + break; case 's': - update_db = 1; - break; + update_db = 1; + break; case 'v': - verbose++; - break; + verbose++; + break; case 'V': /* Volume name */ - VolumeName = optarg; - break; + VolumeName = optarg; + break; case 'w': - wd = optarg; - break; + wd = optarg; + break; case '?': default: - usage(); + usage(); } } @@ -204,7 +204,7 @@ int main (int argc, char *argv[]) if (!me) { UnlockRes(); Emsg1(M_ERROR_TERM, 0, _("No Storage resource defined in %s. Cannot continue.\n"), - configfile); + configfile); } UnlockRes(); /* Check if -w option given, otherwise use resource for working directory */ @@ -212,7 +212,7 @@ int main (int argc, char *argv[]) working_directory = wd; } else if (!me->working_directory) { Emsg1(M_ERROR_TERM, 0, _("No Working Directory defined in %s. Cannot continue.\n"), - configfile); + configfile); } else { working_directory = me->working_directory; } @@ -220,11 +220,11 @@ int main (int argc, char *argv[]) /* Check that working directory is good */ if (stat(working_directory, &stat_buf) != 0) { Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s not found. Cannot continue.\n"), - working_directory); + working_directory); } if (!S_ISDIR(stat_buf.st_mode)) { Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s is not a directory. Cannot continue.\n"), - working_directory); + working_directory); } bjcr = setup_jcr("bscan", argv[0], bsr, VolumeName); @@ -263,15 +263,15 @@ static int bscan_mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) Pmsg1(000, _("Create JobMedia for Job %s\n"), mjcr->Job); } if (dev->state & ST_TAPE) { - mjcr->EndBlock = dev->EndBlock; - mjcr->EndFile = dev->EndFile; + mjcr->EndBlock = dev->EndBlock; + mjcr->EndFile = dev->EndFile; } else { - mjcr->EndBlock = (uint32_t)dev->file_addr; - mjcr->StartBlock = (uint32_t)(dev->file_addr >> 32); + mjcr->EndBlock = (uint32_t)dev->file_addr; + mjcr->StartBlock = (uint32_t)(dev->file_addr >> 32); } if (!create_jobmedia_record(db, mjcr)) { Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"), - dev->VolCatInfo.VolCatName, mjcr->Job); + dev->VolCatInfo.VolCatName, mjcr->Job); } } /* Now let common read routine get up next tape. Note, @@ -284,7 +284,7 @@ static int bscan_mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) return stat; } -static void do_scan() +static void do_scan() { attr = new_attr(); @@ -315,8 +315,8 @@ static void record_cb(JCR *bjcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec) } if (list_records) { Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"), - rec->VolSessionId, rec->VolSessionTime, rec->FileIndex, - rec->Stream, rec->data_len); + rec->VolSessionId, rec->VolSessionTime, rec->FileIndex, + rec->Stream, rec->data_len); } /* * Check for Start or End of Session Record @@ -326,210 +326,210 @@ static void record_cb(JCR *bjcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec) int save_update_db = update_db; if (verbose > 1) { - dump_label_record(dev, rec, 1); + dump_label_record(dev, rec, 1); } switch (rec->FileIndex) { case PRE_LABEL: Pmsg0(000, _("Volume is prelabeled. This tape cannot be scanned.\n")); - return; - break; + return; + break; case VOL_LABEL: - unser_volume_label(dev, rec); - /* Check Pool info */ - strcpy(pr.Name, dev->VolHdr.PoolName); - strcpy(pr.PoolType, dev->VolHdr.PoolType); - if (db_get_pool_record(bjcr, db, &pr)) { - if (verbose) { + unser_volume_label(dev, rec); + /* Check Pool info */ + strcpy(pr.Name, dev->VolHdr.PoolName); + strcpy(pr.PoolType, dev->VolHdr.PoolType); + if (db_get_pool_record(bjcr, db, &pr)) { + if (verbose) { Pmsg1(000, _("Pool record for %s found in DB.\n"), pr.Name); - } - } else { - if (!update_db) { + } + } else { + if (!update_db) { Pmsg1(000, _("VOL_LABEL: Pool record not found for Pool: %s\n"), - pr.Name); - } - create_pool_record(db, &pr); - } - if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) { + pr.Name); + } + create_pool_record(db, &pr); + } + if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) { Pmsg2(000, _("VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n"), - pr.PoolType, dev->VolHdr.PoolType); - return; - } else if (verbose) { + pr.PoolType, dev->VolHdr.PoolType); + return; + } else if (verbose) { Pmsg1(000, _("Pool type \"%s\" is OK.\n"), pr.PoolType); - } - - /* Check Media Info */ - memset(&mr, 0, sizeof(mr)); - strcpy(mr.VolumeName, dev->VolHdr.VolName); - mr.PoolId = pr.PoolId; - if (db_get_media_record(bjcr, db, &mr)) { - if (verbose) { + } + + /* Check Media Info */ + memset(&mr, 0, sizeof(mr)); + strcpy(mr.VolumeName, dev->VolHdr.VolName); + mr.PoolId = pr.PoolId; + if (db_get_media_record(bjcr, db, &mr)) { + if (verbose) { Pmsg1(000, _("Media record for %s found in DB.\n"), mr.VolumeName); - } - /* Clear out some volume statistics that will be updated */ - mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0; - mr.VolBytes = rec->data_len + 20; - } else { - if (!update_db) { + } + /* Clear out some volume statistics that will be updated */ + mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0; + mr.VolBytes = rec->data_len + 20; + } else { + if (!update_db) { Pmsg1(000, _("VOL_LABEL: Media record not found for Volume: %s\n"), - mr.VolumeName); - } - strcpy(mr.MediaType, dev->VolHdr.MediaType); - create_media_record(db, &mr, &dev->VolHdr); - } - if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) { + mr.VolumeName); + } + strcpy(mr.MediaType, dev->VolHdr.MediaType); + create_media_record(db, &mr, &dev->VolHdr); + } + if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) { Pmsg2(000, _("VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n"), - mr.MediaType, dev->VolHdr.MediaType); - return; - } else if (verbose) { + mr.MediaType, dev->VolHdr.MediaType); + return; + } else if (verbose) { Pmsg1(000, _("Media type \"%s\" is OK.\n"), mr.MediaType); - } - /* Reset some JCR variables */ - for (mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) { - mjcr->VolFirstIndex = mjcr->FileIndex = 0; - mjcr->StartBlock = mjcr->EndBlock = 0; - mjcr->StartFile = mjcr->EndFile = 0; - } + } + /* Reset some JCR variables */ + for (mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) { + mjcr->VolFirstIndex = mjcr->FileIndex = 0; + mjcr->StartBlock = mjcr->EndBlock = 0; + mjcr->StartFile = mjcr->EndFile = 0; + } Pmsg1(000, _("VOL_LABEL: OK for Volume: %s\n"), mr.VolumeName); - break; + break; case SOS_LABEL: - mr.VolJobs++; - if (ignored_msgs > 0) { + mr.VolJobs++; + if (ignored_msgs > 0) { Pmsg1(000, _("%d \"errors\" ignored before first Start of Session record.\n"), - ignored_msgs); - ignored_msgs = 0; - } - unser_session_label(&label, rec); - memset(&jr, 0, sizeof(jr)); - jr.JobId = label.JobId; - if (db_get_job_record(bjcr, db, &jr)) { - /* Job record already exists in DB */ + ignored_msgs); + ignored_msgs = 0; + } + unser_session_label(&label, rec); + memset(&jr, 0, sizeof(jr)); + jr.JobId = label.JobId; + if (db_get_job_record(bjcr, db, &jr)) { + /* Job record already exists in DB */ update_db = 0; /* don't change db in create_job_record */ - if (verbose) { + if (verbose) { Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"), jr.JobId); - } - } else { - /* Must create a Job record in DB */ - if (!update_db) { + } + } else { + /* Must create a Job record in DB */ + if (!update_db) { Pmsg1(000, _("SOS_LABEL: Job record not found for JobId: %d\n"), - jr.JobId); - } - } - /* Create Client record if not already there */ - strcpy(cr.Name, label.ClientName); - create_client_record(db, &cr); - jr.ClientId = cr.ClientId; + jr.JobId); + } + } + /* Create Client record if not already there */ + strcpy(cr.Name, label.ClientName); + create_client_record(db, &cr); + jr.ClientId = cr.ClientId; /* process label, if Job record exists don't update db */ - mjcr = create_job_record(db, &jr, &label, rec); - update_db = save_update_db; - - jr.PoolId = pr.PoolId; - /* Set start positions into JCR */ - if (dev->state & ST_TAPE) { - mjcr->StartBlock = dev->block_num; - mjcr->StartFile = dev->file; - } else { - mjcr->StartBlock = (uint32_t)dev->file_addr; - mjcr->StartFile = (uint32_t)(dev->file_addr >> 32); - } - mjcr->start_time = jr.StartTime; - mjcr->JobLevel = jr.Level; - - mjcr->client_name = get_pool_memory(PM_FNAME); - pm_strcpy(&mjcr->client_name, label.ClientName); - mjcr->pool_type = get_pool_memory(PM_FNAME); - pm_strcpy(&mjcr->pool_type, label.PoolType); - mjcr->fileset_name = get_pool_memory(PM_FNAME); - pm_strcpy(&mjcr->fileset_name, label.FileSetName); - mjcr->pool_name = get_pool_memory(PM_FNAME); - pm_strcpy(&mjcr->pool_name, label.PoolName); - - if (rec->VolSessionId != jr.VolSessionId) { + mjcr = create_job_record(db, &jr, &label, rec); + update_db = save_update_db; + + jr.PoolId = pr.PoolId; + /* Set start positions into JCR */ + if (dev->state & ST_TAPE) { + mjcr->StartBlock = dev->block_num; + mjcr->StartFile = dev->file; + } else { + mjcr->StartBlock = (uint32_t)dev->file_addr; + mjcr->StartFile = (uint32_t)(dev->file_addr >> 32); + } + mjcr->start_time = jr.StartTime; + mjcr->JobLevel = jr.Level; + + mjcr->client_name = get_pool_memory(PM_FNAME); + pm_strcpy(&mjcr->client_name, label.ClientName); + mjcr->pool_type = get_pool_memory(PM_FNAME); + pm_strcpy(&mjcr->pool_type, label.PoolType); + mjcr->fileset_name = get_pool_memory(PM_FNAME); + pm_strcpy(&mjcr->fileset_name, label.FileSetName); + mjcr->pool_name = get_pool_memory(PM_FNAME); + pm_strcpy(&mjcr->pool_name, label.PoolName); + + if (rec->VolSessionId != jr.VolSessionId) { Pmsg3(000, _("SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n"), - jr.JobId, - jr.VolSessionId, rec->VolSessionId); - return; - } - if (rec->VolSessionTime != jr.VolSessionTime) { + jr.JobId, + jr.VolSessionId, rec->VolSessionId); + return; + } + if (rec->VolSessionTime != jr.VolSessionTime) { Pmsg3(000, _("SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d Vol=%d\n"), - jr.JobId, - jr.VolSessionTime, rec->VolSessionTime); - return; - } - if (jr.PoolId != pr.PoolId) { + jr.JobId, + jr.VolSessionTime, rec->VolSessionTime); + return; + } + if (jr.PoolId != pr.PoolId) { Pmsg3(000, _("SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n"), - jr.JobId, - jr.PoolId, pr.PoolId); - return; - } - break; + jr.JobId, + jr.PoolId, pr.PoolId); + return; + } + break; case EOS_LABEL: - unser_session_label(&elabel, rec); + unser_session_label(&elabel, rec); - /* Create FileSet record */ - strcpy(fsr.FileSet, label.FileSetName); - strcpy(fsr.MD5, label.FileSetMD5); - create_fileset_record(db, &fsr); - jr.FileSetId = fsr.FileSetId; + /* Create FileSet record */ + strcpy(fsr.FileSet, label.FileSetName); + strcpy(fsr.MD5, label.FileSetMD5); + create_fileset_record(db, &fsr); + jr.FileSetId = fsr.FileSetId; - mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime); - if (!mjcr) { + mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime); + if (!mjcr) { Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"), - rec->VolSessionId, rec->VolSessionTime); - break; - } + rec->VolSessionId, rec->VolSessionTime); + break; + } - /* Do the final update to the Job record */ - update_job_record(db, &jr, &elabel, rec); + /* Do the final update to the Job record */ + update_job_record(db, &jr, &elabel, rec); - mjcr->end_time = jr.EndTime; - mjcr->JobStatus = JS_Terminated; + mjcr->end_time = jr.EndTime; + mjcr->JobStatus = JS_Terminated; - /* Create JobMedia record */ - create_jobmedia_record(db, mjcr); - detach_jcr_from_device(dev, mjcr); - free_jcr(mjcr); + /* Create JobMedia record */ + create_jobmedia_record(db, mjcr); + detach_jcr_from_device(dev, mjcr); + free_jcr(mjcr); - break; + break; case EOM_LABEL: - break; - - case EOT_LABEL: /* end of all tapes */ - /* - * Wiffle through all jobs still open and close - * them. - */ - if (update_db) { - for (mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) { - jr.JobId = mjcr->JobId; - jr.JobStatus = JS_ErrorTerminated; - jr.JobFiles = mjcr->JobFiles; - jr.JobBytes = mjcr->JobBytes; - jr.VolSessionId = mjcr->VolSessionId; - jr.VolSessionTime = mjcr->VolSessionTime; - jr.JobTDate = (utime_t)mjcr->start_time; - jr.ClientId = mjcr->ClientId; - free_jcr(mjcr); - if (!db_update_job_end_record(bjcr, db, &jr)) { + break; + + case EOT_LABEL: /* end of all tapes */ + /* + * Wiffle through all jobs still open and close + * them. + */ + if (update_db) { + for (mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) { + jr.JobId = mjcr->JobId; + jr.JobStatus = JS_ErrorTerminated; + jr.JobFiles = mjcr->JobFiles; + jr.JobBytes = mjcr->JobBytes; + jr.VolSessionId = mjcr->VolSessionId; + jr.VolSessionTime = mjcr->VolSessionTime; + jr.JobTDate = (utime_t)mjcr->start_time; + jr.ClientId = mjcr->ClientId; + free_jcr(mjcr); + if (!db_update_job_end_record(bjcr, db, &jr)) { Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db)); - } - } - } - mr.VolFiles = rec->File; - mr.VolBlocks = rec->Block; - mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */ - mr.VolMounts++; - update_media_record(db, &mr); + } + } + } + mr.VolFiles = rec->File; + mr.VolBlocks = rec->Block; + mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */ + mr.VolMounts++; + update_media_record(db, &mr); Pmsg3(0, _("End of all Volumes. VolFiles=%u VolBlocks=%u VolBytes=%s\n"), mr.VolFiles, - mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1)); - break; + mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1)); + break; default: - break; + break; } /* end switch */ return; } @@ -546,33 +546,33 @@ static void record_cb(JCR *bjcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec) if (attr->file_index != rec->FileIndex) { Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"), - rec->FileIndex, attr->file_index); + rec->FileIndex, attr->file_index); } if (verbose > 1) { - uint32_t LinkFI; - decode_stat(attr->attr, &attr->statp, &LinkFI); - print_ls_output(bjcr, attr); + uint32_t LinkFI; + decode_stat(attr->attr, &attr->statp, &LinkFI); + print_ls_output(bjcr, attr); } mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime); if (!mjcr) { - if (mr.VolJobs > 0) { + if (mr.VolJobs > 0) { Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Attributes record.\n"), - rec->VolSessionId, rec->VolSessionTime); - } else { - ignored_msgs++; - } - return; + rec->VolSessionId, rec->VolSessionTime); + } else { + ignored_msgs++; + } + return; } fr.JobId = mjcr->JobId; fr.FileId = 0; if (db_get_file_attributes_record(bjcr, db, attr->fname, &fr)) { - if (verbose > 1) { + if (verbose > 1) { Pmsg1(000, _("File record already exists for: %s\n"), attr->fname); - } + } } else { - create_file_attributes_record(db, mjcr, attr->fname, attr->lname, - attr->type, attr->attr, rec); + create_file_attributes_record(db, mjcr, attr->fname, attr->lname, + attr->type, attr->attr, rec); } free_jcr(mjcr); break; @@ -583,66 +583,66 @@ static void record_cb(JCR *bjcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec) case STREAM_SPARSE_DATA: mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime); if (!mjcr) { - if (mr.VolJobs > 0) { + if (mr.VolJobs > 0) { Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for File Data record.\n"), - rec->VolSessionId, rec->VolSessionTime); - } else { - ignored_msgs++; - } - return; + rec->VolSessionId, rec->VolSessionTime); + } else { + ignored_msgs++; + } + return; } mjcr->JobBytes += rec->data_len; if (rec->Stream == STREAM_SPARSE_DATA) { - mjcr->JobBytes -= sizeof(uint64_t); + mjcr->JobBytes -= sizeof(uint64_t); } - - free_jcr(mjcr); /* done using JCR */ + + free_jcr(mjcr); /* done using JCR */ break; case STREAM_GZIP_DATA: mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime); if (!mjcr) { - if (mr.VolJobs > 0) { + if (mr.VolJobs > 0) { Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for GZIP Data record.\n"), - rec->VolSessionId, rec->VolSessionTime); - } else { - ignored_msgs++; - } - return; + rec->VolSessionId, rec->VolSessionTime); + } else { + ignored_msgs++; + } + return; } mjcr->JobBytes += rec->data_len; /* No correct, we should expand it */ - free_jcr(mjcr); /* done using JCR */ + free_jcr(mjcr); /* done using JCR */ break; case STREAM_SPARSE_GZIP_DATA: mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime); if (!mjcr) { - if (mr.VolJobs > 0) { + if (mr.VolJobs > 0) { Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Sparse GZIP Data record.\n"), - rec->VolSessionId, rec->VolSessionTime); - } else { - ignored_msgs++; - } - return; + rec->VolSessionId, rec->VolSessionTime); + } else { + ignored_msgs++; + } + return; } mjcr->JobBytes += rec->data_len - sizeof(uint64_t); /* No correct, we should expand it */ - free_jcr(mjcr); /* done using JCR */ + free_jcr(mjcr); /* done using JCR */ break; /* Win32 GZIP stream */ case STREAM_WIN32_GZIP_DATA: mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime); if (!mjcr) { - if (mr.VolJobs > 0) { + if (mr.VolJobs > 0) { Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Win32 GZIP Data record.\n"), - rec->VolSessionId, rec->VolSessionTime); - } else { - ignored_msgs++; - } - return; + rec->VolSessionId, rec->VolSessionTime); + } else { + ignored_msgs++; + } + return; } mjcr->JobBytes += rec->data_len; - free_jcr(mjcr); /* done using JCR */ + free_jcr(mjcr); /* done using JCR */ break; case STREAM_MD5_SIGNATURE: @@ -710,8 +710,8 @@ static void dird_free_jcr(JCR *jcr) * record, and then create the attributes record. */ static int create_file_attributes_record(B_DB *db, JCR *mjcr, - char *fname, char *lname, int type, - char *ap, DEV_RECORD *rec) + char *fname, char *lname, int type, + char *ap, DEV_RECORD *rec) { ar.fname = fname; @@ -866,8 +866,8 @@ static int create_fileset_record(B_DB *db, FILESET_DBR *fsr) } else { if (!db_create_fileset_record(bjcr, db, fsr)) { Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"), - fsr->FileSet, db_strerror(db)); - return 0; + fsr->FileSet, db_strerror(db)); + return 0; } if (verbose) { Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet); @@ -882,7 +882,7 @@ static int create_fileset_record(B_DB *db, FILESET_DBR *fsr) * begins running. */ static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label, - DEV_RECORD *rec) + DEV_RECORD *rec) { JCR *mjcr; struct date_time dt; @@ -927,8 +927,8 @@ static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label, return mjcr; } Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId, - label->JobId); - mjcr->JobId = jr->JobId; /* set new JobId */ + label->JobId); + mjcr->JobId = jr->JobId; /* set new JobId */ return mjcr; } @@ -937,7 +937,7 @@ static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label, * at Job termination time. */ static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel, - DEV_RECORD *rec) + DEV_RECORD *rec) { struct date_time dt; struct tm tm; @@ -946,7 +946,7 @@ static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel, mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime); if (!mjcr) { Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"), - rec->VolSessionId, rec->VolSessionTime); + rec->VolSessionId, rec->VolSessionTime); return 0; } if (elabel->VerNum >= 11) { @@ -992,18 +992,18 @@ static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel, switch (mjcr->JobStatus) { case JS_Terminated: term_msg = _("Backup OK"); - break; + break; case JS_FatalError: case JS_ErrorTerminated: term_msg = _("*** Backup Error ***"); - break; + break; case JS_Canceled: term_msg = _("Backup Canceled"); - break; + break; default: - term_msg = term_code; + term_msg = term_code; sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus); - break; + break; } bstrftime(sdt, sizeof(sdt), mjcr->start_time); bstrftime(edt, sizeof(edt), mjcr->end_time); @@ -1021,20 +1021,20 @@ Volume Session Id: %d\n\ Volume Session Time: %d\n\ Last Volume Bytes: %s\n\ Termination: %s\n\n"), - edt, - mjcr->JobId, - mjcr->Job, - mjcr->fileset_name, - job_level_to_str(mjcr->JobLevel), - mjcr->client_name, - sdt, - edt, - edit_uint64_with_commas(mjcr->JobFiles, ec1), - edit_uint64_with_commas(mjcr->JobBytes, ec2), - mjcr->VolSessionId, - mjcr->VolSessionTime, - edit_uint64_with_commas(mr.VolBytes, ec3), - term_msg); + edt, + mjcr->JobId, + mjcr->Job, + mjcr->fileset_name, + job_level_to_str(mjcr->JobLevel), + mjcr->client_name, + sdt, + edt, + edit_uint64_with_commas(mjcr->JobFiles, ec1), + edit_uint64_with_commas(mjcr->JobBytes, ec2), + mjcr->VolSessionId, + mjcr->VolSessionTime, + edit_uint64_with_commas(mr.VolBytes, ec3), + term_msg); } free_jcr(mjcr); return 1; @@ -1073,7 +1073,7 @@ static int create_jobmedia_record(B_DB *db, JCR *mjcr) } if (verbose) { Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"), - jmr.JobId, jmr.MediaId); + jmr.JobId, jmr.MediaId); } return 1; } @@ -1089,9 +1089,9 @@ static int update_SIG_record(B_DB *db, char *SIGbuf, DEV_RECORD *rec, int type) if (!mjcr) { if (mr.VolJobs > 0) { Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"), - rec->VolSessionId, rec->VolSessionTime); + rec->VolSessionId, rec->VolSessionTime); } else { - ignored_msgs++; + ignored_msgs++; } return 0; } @@ -1122,7 +1122,7 @@ static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId) JCR *jobjcr; /* * Transfer as much as possible to the Job JCR. Most important is - * the JobId and the ClientId. + * the JobId and the ClientId. */ jobjcr = new_jcr(sizeof(JCR), dird_free_jcr); jobjcr->JobType = jr->Type; @@ -1140,19 +1140,19 @@ static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId) } /* Dummies to replace askdir.c */ -int dir_get_volume_info(JCR *jcr, int writing) { return 1;} -int dir_find_next_appendable_volume(JCR *jcr) { return 1;} -int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; } -int dir_create_jobmedia_record(JCR *jcr) { return 1; } -int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; } -int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;} -int dir_send_job_status(JCR *jcr) {return 1;} +int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw writing) { return 1;} +int dir_find_next_appendable_volume(JCR *jcr) { return 1;} +int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; } +int dir_create_jobmedia_record(JCR *jcr) { return 1; } +int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; } +int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;} +int dir_send_job_status(JCR *jcr) {return 1;} int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev) { fprintf(stderr, _("Mount Volume %s on device %s and press return when ready: "), jcr->VolumeName, dev_name(dev)); - getchar(); + getchar(); return 1; } diff --git a/bacula/src/stored/btape.c b/bacula/src/stored/btape.c index ff325d8cb0..ee630be1e8 100644 --- a/bacula/src/stored/btape.c +++ b/bacula/src/stored/btape.c @@ -49,7 +49,7 @@ char VolName[MAX_NAME_LENGTH]; DEVICE *dev = NULL; DEVRES *device = NULL; - + /* Forward referenced subroutines */ static void do_tape_cmds(); static void helpcmd(); @@ -111,7 +111,7 @@ int get_cmd(char *prompt); /********************************************************************* * - * Main Bacula Pool Creation Program + * Main Bacula Pool Creation Program * */ int main(int argc, char *argv[]) @@ -122,7 +122,7 @@ int main(int argc, char *argv[]) /* Sanity checks */ if (TAPE_BSIZE % DEV_BSIZE != 0 || TAPE_BSIZE / DEV_BSIZE == 0) { Emsg2(M_ABORT, 0, "Tape block size (%d) not multiple of system size (%d)\n", - TAPE_BSIZE, DEV_BSIZE); + TAPE_BSIZE, DEV_BSIZE); } if (TAPE_BSIZE != (1 << (ffs(TAPE_BSIZE)-1))) { Emsg1(M_ABORT, 0, "Tape block size (%d) is not a power of 2\n", TAPE_BSIZE); @@ -137,36 +137,36 @@ int main(int argc, char *argv[]) while ((ch = getopt(argc, argv, "b:c:d:sv?")) != -1) { switch (ch) { case 'b': /* bootstrap file */ - bsr = parse_bsr(NULL, optarg); -// dump_bsr(bsr); - break; + bsr = parse_bsr(NULL, optarg); +// dump_bsr(bsr); + break; case 'c': /* specify config file */ - if (configfile != NULL) { - free(configfile); - } - configfile = bstrdup(optarg); - break; + if (configfile != NULL) { + free(configfile); + } + configfile = bstrdup(optarg); + break; case 'd': /* set debug level */ - debug_level = atoi(optarg); - if (debug_level <= 0) { - debug_level = 1; - } - break; + debug_level = atoi(optarg); + if (debug_level <= 0) { + debug_level = 1; + } + break; case 's': - signals = FALSE; - break; + signals = FALSE; + break; case 'v': - verbose++; - break; + verbose++; + break; case '?': - default: - helpcmd(); - exit(0); + default: + helpcmd(); + exit(0); } } @@ -210,9 +210,9 @@ int main(int argc, char *argv[]) Dmsg0(129, "Opening device.\n"); if (open_dev(dev, jcr->VolumeName, READ_WRITE) < 0) { Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), dev->errmsg); - unlock_device(dev); - free_block(block); - goto terminate; + unlock_device(dev); + free_block(block); + goto terminate; } } Dmsg1(129, "open_dev %s OK\n", dev_name(dev)); @@ -258,7 +258,7 @@ static void terminate_btape(int stat) } term_msg(); - close_memory_pool(); /* free memory in pool */ + close_memory_pool(); /* free memory in pool */ sm_dump(False); exit(stat); @@ -280,11 +280,11 @@ static void labelcmd() LockRes(); for (device=NULL; (device=(DEVRES *)GetNextRes(R_DEVICE, (RES *)device)); ) { if (strcmp(device->device_name, dev->dev_name) == 0) { - jcr->device = device; /* Arggg a bit of duplication here */ - device->dev = dev; - dev->device = device; - found = 1; - break; + jcr->device = device; /* Arggg a bit of duplication here */ + device->dev = dev; + dev->device = device; + found = 1; + break; } } UnlockRes(); @@ -297,10 +297,10 @@ static void labelcmd() strcpy(cmd, VolumeName); } else { if (!get_cmd("Enter Volume Name: ")) { - return; + return; } } - + if (!(dev->state & ST_OPENED)) { if (!open_device(dev)) { Pmsg1(0, "Device open failed. ERR=%s\n", strerror_dev(dev)); @@ -310,7 +310,7 @@ static void labelcmd() } /* - * Read the tape label + * Read the tape label */ static void readlabelcmd() { @@ -323,28 +323,28 @@ static void readlabelcmd() switch (stat) { case VOL_NO_LABEL: Pmsg0(0, "Volume has no label.\n"); - break; + break; case VOL_OK: Pmsg0(0, "Volume label read correctly.\n"); - break; + break; case VOL_IO_ERROR: Pmsg1(0, "I/O error on device: ERR=%s", strerror_dev(dev)); - break; + break; case VOL_NAME_ERROR: Pmsg0(0, "Volume name error\n"); - break; + break; case VOL_CREATE_ERROR: Pmsg1(0, "Error creating label. ERR=%s", strerror_dev(dev)); - break; + break; case VOL_VERSION_ERROR: Pmsg0(0, "Volume version error.\n"); - break; + break; case VOL_LABEL_ERROR: Pmsg0(0, "Bad Volume label type.\n"); - break; + break; default: Pmsg0(0, "Unknown error.\n"); - break; + break; } debug_level = 20; @@ -404,7 +404,7 @@ static void weofcmd() } -/* Go to the end of the medium -- raw command +/* Go to the end of the medium -- raw command * The idea was orginally that the end of the Bacula * medium would be flagged differently. This is not * currently the case. So, this is identical to the @@ -534,11 +534,11 @@ static void rectestcmd() rec->data_len = i; sm_check(__FILE__, __LINE__, False); if (write_record_to_block(block, rec)) { - empty_block(block); - blkno++; + empty_block(block); + blkno++; Pmsg2(0, "Block %d i=%d\n", blkno, i); } else { - break; + break; } sm_check(__FILE__, __LINE__, False); } @@ -638,7 +638,7 @@ static int re_read_block_test() for (int i=0; idata[i] != 3) { Pmsg0(0, _("Bad data in record. Test failed!\n")); - goto bail_out; + goto bail_out; } } Pmsg0(0, _("\nBlock re-read correct. Test succeeded!\n")); @@ -675,14 +675,14 @@ static int append_test() " and three records in file 2\n\n")); rewindcmd(); wrcmd(); - weofcmd(); /* end file 0 */ + weofcmd(); /* end file 0 */ wrcmd(); wrcmd(); - weofcmd(); /* end file 1 */ + weofcmd(); /* end file 1 */ wrcmd(); wrcmd(); wrcmd(); - weofcmd(); /* end file 2 */ + weofcmd(); /* end file 2 */ rewindcmd(); Pmsg0(0, _("Now moving to end of medium.\n")); eodcmd(); @@ -720,39 +720,39 @@ static void testcmd() int stat; stat = append_test(); - if (stat == 1) { /* OK get out */ + if (stat == 1) { /* OK get out */ goto all_done; } - if (stat == -1) { /* first test failed */ + if (stat == -1) { /* first test failed */ if (dev_cap(dev, CAP_EOM)) { Pmsg0(-1, "\nAppend test failed. Attempting again.\n" "Setting \"Hardware End of Medium = no\" and retrying append test.\n\n"); - dev->capabilities &= ~CAP_EOM; /* turn off eom */ - stat = append_test(); - if (stat == 1) { + dev->capabilities &= ~CAP_EOM; /* turn off eom */ + stat = append_test(); + if (stat == 1) { Pmsg0(-1, "\n\nIt looks like the test worked this time, please add:\n\n" " Hardware End of Medium = No\n\n" "to your Device resource in the Storage conf file.\n"); - goto all_done; - } - if (stat == -1) { + goto all_done; + } + if (stat == -1) { Pmsg0(-1, "\n\nThat appears not to have corrected the problem.\n"); - goto all_done; - } - /* Wrong count after append */ - if (stat == -2) { + goto all_done; + } + /* Wrong count after append */ + if (stat == -2) { Pmsg0(-1, "\n\nIt looks like the append failed. Attempting again.\n" "Setting \"BSF at EOM = yes\" and retrying append test.\n"); - dev->capabilities |= CAP_BSFATEOM; /* backspace on eom */ - stat = append_test(); - if (stat == 1) { + dev->capabilities |= CAP_BSFATEOM; /* backspace on eom */ + stat = append_test(); + if (stat == 1) { Pmsg0(-1, "\n\nIt looks like the test worked this time, please add:\n\n" " Hardware End of Medium = No\n" " BSR at EOM = yes\n\n" "to your Device resource in the Storage conf file.\n"); - goto all_done; - } - } + goto all_done; + } + } Pmsg0(-1, "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" "Unable to correct the problem. You MUST fix this\n" @@ -909,45 +909,45 @@ static void scancmd() Pmsg1(0, _("Starting scan at file %u\n"), dev->file); for (;;) { if ((stat = read(dev->fd, buf, sizeof(buf))) < 0) { - clrerror_dev(dev, -1); + clrerror_dev(dev, -1); Mmsg2(&dev->errmsg, "read error on %s. ERR=%s.\n", - dev->dev_name, strerror(dev->dev_errno)); + dev->dev_name, strerror(dev->dev_errno)); Pmsg2(0, "Bad status from read %d. ERR=%s\n", stat, strerror_dev(dev)); - if (blocks > 0) + if (blocks > 0) printf("%d block%s of %d bytes in file %d\n", blocks, blocks>1?"s":"", block_size, dev->file); - return; + return; } Dmsg1(200, "read status = %d\n", stat); /* sleep(1); */ if (stat != block_size) { - update_pos_dev(dev); - if (blocks > 0) { + update_pos_dev(dev); + if (blocks > 0) { printf("%d block%s of %d bytes in file %d\n", blocks, blocks>1?"s":"", block_size, dev->file); - blocks = 0; - } - block_size = stat; + blocks = 0; + } + block_size = stat; } - if (stat == 0) { /* EOF */ - update_pos_dev(dev); + if (stat == 0) { /* EOF */ + update_pos_dev(dev); printf("End of File mark.\n"); - /* Two reads of zero means end of tape */ - if (dev->state & ST_EOF) - dev->state |= ST_EOT; - else { - dev->state |= ST_EOF; - dev->file++; - } - if (dev->state & ST_EOT) { + /* Two reads of zero means end of tape */ + if (dev->state & ST_EOF) + dev->state |= ST_EOT; + else { + dev->state |= ST_EOF; + dev->file++; + } + if (dev->state & ST_EOT) { printf("End of tape\n"); - break; - } - } else { /* Got data */ - dev->state &= ~ST_EOF; - blocks++; - tot_blocks++; - bytes += stat; + break; + } + } else { /* Got data */ + dev->state &= ~ST_EOF; + blocks++; + tot_blocks++; + bytes += stat; } } update_pos_dev(dev); @@ -980,58 +980,58 @@ static void scan_blocks() for (;;) { if (!read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) { Dmsg1(100, "!read_block(): ERR=%s\n", strerror_dev(dev)); - if (dev->state & ST_EOT) { - if (blocks > 0) { + if (dev->state & ST_EOT) { + if (blocks > 0) { printf("%d block%s of %d bytes in file %d\n", blocks, blocks>1?"s":"", block_size, dev->file); - blocks = 0; - } - goto bail_out; - } - if (dev->state & ST_EOF) { - if (blocks > 0) { + blocks = 0; + } + goto bail_out; + } + if (dev->state & ST_EOF) { + if (blocks > 0) { printf("%d block%s of %d bytes in file %d\n", blocks, blocks>1?"s":"", block_size, dev->file); - blocks = 0; - } + blocks = 0; + } printf(_("End of File mark.\n")); - continue; - } - if (dev->state & ST_SHORT) { - if (blocks > 0) { + continue; + } + if (dev->state & ST_SHORT) { + if (blocks > 0) { printf("%d block%s of %d bytes in file %d\n", blocks, blocks>1?"s":"", block_size, dev->file); - blocks = 0; - } + blocks = 0; + } printf(_("Short block read.\n")); - continue; - } + continue; + } printf(_("Error reading block. ERR=%s\n"), strerror_dev(dev)); - goto bail_out; + goto bail_out; } if (block->block_len != block_size) { - if (blocks > 0) { + if (blocks > 0) { printf("%d block%s of %d bytes in file %d\n", blocks, blocks>1?"s":"", block_size, dev->file); - blocks = 0; - } - block_size = block->block_len; + blocks = 0; + } + block_size = block->block_len; } blocks++; tot_blocks++; bytes += block->block_len; Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n", - block->BlockNumber, block->block_len, block->BlockVer, - block->VolSessionId, block->VolSessionTime); + block->BlockNumber, block->block_len, block->BlockVer, + block->VolSessionId, block->VolSessionTime); if (verbose == 1) { - DEV_RECORD *rec = new_record(); - read_record_from_block(block, rec); + DEV_RECORD *rec = new_record(); + read_record_from_block(block, rec); Pmsg7(-1, "Block: %u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n", - block->BlockNumber, block->block_len, - FI_to_ascii(rec->FileIndex), rec->VolSessionId, rec->VolSessionTime, - stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len); - rec->remainder = 0; - free_record(rec); + block->BlockNumber, block->block_len, + FI_to_ascii(rec->FileIndex), rec->VolSessionId, rec->VolSessionTime, + stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len); + rec->remainder = 0; + free_record(rec); } else if (verbose > 1) { dump_block(block, ""); } @@ -1066,7 +1066,7 @@ static void statcmd() /* * First we label the tape, then we fill * it with data get a new tape and write a few blocks. - */ + */ static void fillcmd() { DEV_RECORD rec; @@ -1115,8 +1115,8 @@ This may take a long time -- hours! ...\n\n"); /* * Acquire output device for writing. Note, after acquiring a - * device, we MUST release it, which is done at the end of this - * subroutine. + * device, we MUST release it, which is done at the end of this + * subroutine. */ Dmsg0(100, "just before acquire_device\n"); if (!(dev=acquire_device_for_append(jcr, dev, block))) { @@ -1132,7 +1132,7 @@ This may take a long time -- hours! ...\n\n"); if (!write_session_label(jcr, block, SOS_LABEL)) { set_jcr_job_status(jcr, JS_ErrorTerminated); Jmsg1(jcr, M_FATAL, 0, _("Write session label failed. ERR=%s\n"), - strerror_dev(dev)); + strerror_dev(dev)); ok = FALSE; } Pmsg0(-1, "Wrote Start Of Session label.\n"); @@ -1147,7 +1147,7 @@ This may take a long time -- hours! ...\n\n"); * Generate data as if from File daemon, write to device */ jcr->VolFirstIndex = 0; - time(&jcr->run_time); /* start counting time for rates */ + time(&jcr->run_time); /* start counting time for rates */ Pmsg0(-1, "Begin writing records to first tape ...\n"); for (file_index = 0; ok && !job_canceled(jcr); ) { rec.VolSessionId = jcr->VolSessionId; @@ -1161,81 +1161,81 @@ This may take a long time -- hours! ...\n\n"); */ uint64_t *lp = (uint64_t *)rec.data; for (uint32_t i=0; i < (rec.data_len-sizeof(uint64_t))/sizeof(uint64_t); i++) { - *lp++ = ~file_index; + *lp++ = ~file_index; } Dmsg4(250, "before writ_rec FI=%d SessId=%d Strm=%s len=%d\n", - rec.FileIndex, rec.VolSessionId, stream_to_ascii(rec.Stream, rec.FileIndex), - rec.data_len); + rec.FileIndex, rec.VolSessionId, stream_to_ascii(rec.Stream, rec.FileIndex), + rec.data_len); while (!write_record_to_block(block, &rec)) { - /* - * When we get here we have just filled a block - */ + /* + * When we get here we have just filled a block + */ Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len, - rec.remainder); - - /* Write block to tape */ - if (!flush_block(block, 1)) { - return; - } - - /* Every 5000 blocks (approx 322MB) report where we are. - */ - if ((block->BlockNumber % 5000) == 0) { - now = time(NULL); - now -= jcr->run_time; - if (now <= 0) { - now = 1; - } - kbs = (double)dev->VolCatInfo.VolCatBytes / (1000.0 * (double)now); + rec.remainder); + + /* Write block to tape */ + if (!flush_block(block, 1)) { + return; + } + + /* Every 5000 blocks (approx 322MB) report where we are. + */ + if ((block->BlockNumber % 5000) == 0) { + now = time(NULL); + now -= jcr->run_time; + if (now <= 0) { + now = 1; + } + kbs = (double)dev->VolCatInfo.VolCatBytes / (1000.0 * (double)now); Pmsg3(-1, "Wrote block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber, - edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), (float)kbs); - } - /* Every 15000 blocks (approx 1GB) write an EOF. - */ - if ((block->BlockNumber % 15000) == 0) { + edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), (float)kbs); + } + /* Every 15000 blocks (approx 1GB) write an EOF. + */ + if ((block->BlockNumber % 15000) == 0) { Pmsg0(-1, "Flush block, write EOF\n"); - flush_block(block, 0); - weof_dev(dev, 1); - } - - /* Get out after writing 10 blocks to the second tape */ - if (++BlockNumber > 10 && stop != 0) { /* get out */ - break; - } + flush_block(block, 0); + weof_dev(dev, 1); + } + + /* Get out after writing 10 blocks to the second tape */ + if (++BlockNumber > 10 && stop != 0) { /* get out */ + break; + } } if (!ok) { Pmsg0(000, _("Not OK\n")); - break; + break; } jcr->JobBytes += rec.data_len; /* increment bytes this job */ Dmsg4(190, "write_record FI=%s SessId=%d Strm=%s len=%d\n", - FI_to_ascii(rec.FileIndex), rec.VolSessionId, - stream_to_ascii(rec.Stream, rec.FileIndex), rec.data_len); + FI_to_ascii(rec.FileIndex), rec.VolSessionId, + stream_to_ascii(rec.Stream, rec.FileIndex), rec.data_len); /* Get out after writing 10 blocks to the second tape */ - if (BlockNumber > 10 && stop != 0) { /* get out */ + if (BlockNumber > 10 && stop != 0) { /* get out */ Pmsg0(-1, "Done writing ...\n"); - break; + break; } } if (stop > 0) { Dmsg0(100, "Write_end_session_label()\n"); /* Create Job status for end of session label */ if (!job_canceled(jcr) && ok) { - set_jcr_job_status(jcr, JS_Terminated); + set_jcr_job_status(jcr, JS_Terminated); } else if (!ok) { - set_jcr_job_status(jcr, JS_ErrorTerminated); + set_jcr_job_status(jcr, JS_ErrorTerminated); } if (!write_session_label(jcr, block, EOS_LABEL)) { Pmsg1(000, _("Error writting end session label. ERR=%s\n"), strerror_dev(dev)); - ok = FALSE; + ok = FALSE; } /* Write out final block of this session */ if (!write_block_to_device(jcr, dev, block)) { Pmsg0(-1, _("Set ok=FALSE after write_block_to_device.\n")); - ok = FALSE; + ok = FALSE; } Pmsg0(-1, "Wrote End Of Session label.\n"); } @@ -1278,7 +1278,7 @@ static void unfillcmd() if (!simple) { /* Close device so user can use autochanger if desired */ if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) { - offline_dev(dev); + offline_dev(dev); } force_close_dev(dev); get_cmd(_("Mount first tape. Press enter when ready: ")); @@ -1291,11 +1291,11 @@ static void unfillcmd() dev->state &= ~ST_READ; if (!acquire_device_for_read(jcr, dev, block)) { Pmsg1(-1, "%s", dev->errmsg); - return; + return; } } - time(&jcr->run_time); /* start counting time for rates */ + time(&jcr->run_time); /* start counting time for rates */ stop = 0; file_index = 0; if (!simple) { @@ -1309,27 +1309,27 @@ static void unfillcmd() Pmsg0(000, "Rewinding tape ...\n"); if (!rewind_dev(dev)) { Pmsg1(-1, _("Error rewinding: ERR=%s\n"), strerror_dev(dev)); - goto bail_out; + goto bail_out; } if (last_file > 0) { Pmsg1(000, "Forward spacing to last file=%u\n", last_file); - if (!fsf_dev(dev, last_file)) { + if (!fsf_dev(dev, last_file)) { Pmsg1(-1, _("Error in FSF: ERR=%s\n"), strerror_dev(dev)); - goto bail_out; - } + goto bail_out; + } } Pmsg1(-1, _("Forward space to file %u complete. Reading blocks ...\n"), - last_file); + last_file); Pmsg1(-1, _("Now reading to block %u.\n"), last_block_num); for (uint32_t i=0; i <= last_block_num; i++) { - if (!read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) { + if (!read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) { Pmsg1(-1, _("Error reading blocks: ERR=%s\n"), strerror_dev(dev)); Pmsg2(-1, _("Wanted block %u error at block %u\n"), last_block_num, i); - goto bail_out; - } - if (i > 0 && i % 1000 == 0) { + goto bail_out; + } + if (i > 0 && i % 1000 == 0) { Pmsg1(-1, _("At block %u\n"), i); - } + } } if (last_block) { dump_block(last_block, _("Last block written")); @@ -1353,69 +1353,69 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec) { SESSION_LABEL label; - if (stop > 1 && !dumped) { /* on second tape */ + if (stop > 1 && !dumped) { /* on second tape */ dumped = 1; dump_block(block, "First block on second tape"); Pmsg4(-1, "Blk: FileIndex=%d: block=%u size=%d vol=%s\n", - rec->FileIndex, block->BlockNumber, block->block_len, dev->VolHdr.VolName); + rec->FileIndex, block->BlockNumber, block->block_len, dev->VolHdr.VolName); Pmsg6(-1, " Rec: VId=%d VT=%d FI=%s Strm=%s len=%d state=%x\n", - rec->VolSessionId, rec->VolSessionTime, - FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream, rec->FileIndex), - rec->data_len, rec->state); + rec->VolSessionId, rec->VolSessionTime, + FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream, rec->FileIndex), + rec->data_len, rec->state); } if (rec->FileIndex < 0) { if (verbose > 1) { - dump_label_record(dev, rec, 1); + dump_label_record(dev, rec, 1); } switch (rec->FileIndex) { case PRE_LABEL: Pmsg0(-1, "Volume is prelabeled. This tape cannot be scanned.\n"); - return; + return; case VOL_LABEL: - unser_volume_label(dev, rec); + unser_volume_label(dev, rec); Pmsg3(-1, "VOL_LABEL: block=%u size=%d vol=%s\n", block->BlockNumber, - block->block_len, dev->VolHdr.VolName); - stop++; - break; + block->block_len, dev->VolHdr.VolName); + stop++; + break; case SOS_LABEL: - unser_session_label(&label, rec); + unser_session_label(&label, rec); Pmsg1(-1, "SOS_LABEL: JobId=%u\n", label.JobId); - break; + break; case EOS_LABEL: - unser_session_label(&label, rec); + unser_session_label(&label, rec); Pmsg2(-1, "EOS_LABEL: block=%u JobId=%u\n", block->BlockNumber, - label.JobId); - break; + label.JobId); + break; case EOM_LABEL: Pmsg0(-1, "EOM_LABEL:\n"); - break; - case EOT_LABEL: /* end of all tapes */ - char ec1[50]; - - if (LastBlock != block->BlockNumber) { - VolBytes += block->block_len; - } - LastBlock = block->BlockNumber; - now = time(NULL); - now -= jcr->run_time; - if (now <= 0) { - now = 1; - } - kbs = (double)VolBytes / (1000 * now); + break; + case EOT_LABEL: /* end of all tapes */ + char ec1[50]; + + if (LastBlock != block->BlockNumber) { + VolBytes += block->block_len; + } + LastBlock = block->BlockNumber; + now = time(NULL); + now -= jcr->run_time; + if (now <= 0) { + now = 1; + } + kbs = (double)VolBytes / (1000 * now); Pmsg3(000, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber, - edit_uint64_with_commas(VolBytes, ec1), (float)kbs); + edit_uint64_with_commas(VolBytes, ec1), (float)kbs); Pmsg0(000, "End of all tapes.\n"); - break; + break; default: - break; + break; } return; } if (++file_index != rec->FileIndex) { Pmsg3(000, "Incorrect FileIndex in Block %u. Got %d, expected %d.\n", - block->BlockNumber, rec->FileIndex, file_index); + block->BlockNumber, rec->FileIndex, file_index); } /* * Now check that the right data is in the record. @@ -1425,8 +1425,8 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec) for (uint32_t i=0; i < (REC_SIZE-sizeof(uint64_t))/sizeof(uint64_t); i++) { if (*lp++ != val) { Pmsg2(000, "Record %d contains bad data in Block %u.\n", - file_index, block->BlockNumber); - break; + file_index, block->BlockNumber); + break; } } @@ -1438,11 +1438,11 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec) now = time(NULL); now -= jcr->run_time; if (now <= 0) { - now = 1; + now = 1; } kbs = (double)VolBytes / (1000 * now); Pmsg3(000, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber, - edit_uint64_with_commas(VolBytes, ec1), (float)kbs); + edit_uint64_with_commas(VolBytes, ec1), (float)kbs); } LastBlock = block->BlockNumber; if (end_of_tape) { @@ -1475,44 +1475,44 @@ static int flush_block(DEV_BLOCK *block, int dump) this_file = dev->file; this_block_num = dev->block_num; if (!write_block_to_dev(jcr, dev, block)) { - Pmsg0(000, strerror_dev(dev)); + Pmsg0(000, strerror_dev(dev)); Pmsg3(000, "Block not written: FileIndex=%u Block=%u Size=%u\n", - (unsigned)file_index, block->BlockNumber, block->block_len); + (unsigned)file_index, block->BlockNumber, block->block_len); Pmsg2(000, "last_block_num=%u this_block_num=%d\n", last_block_num, - this_block_num); + this_block_num); if (dump) { dump_block(block, "Block not written"); } if (stop == 0) { - eot_block = block->BlockNumber; - eot_block_len = block->block_len; - eot_FileIndex = file_index; + eot_block = block->BlockNumber; + eot_block_len = block->block_len; + eot_FileIndex = file_index; } now = time(NULL); now -= jcr->run_time; if (now <= 0) { - now = 1; + now = 1; } kbs = (double)dev->VolCatInfo.VolCatBytes / (1000 * now); vol_size = dev->VolCatInfo.VolCatBytes; Pmsg2(000, "End of tape. VolumeCapacity=%s. Write rate = %.1f KB/s\n", - edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), kbs); + edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), kbs); if (simple) { - stop = -1; /* stop, but do simplified test */ + stop = -1; /* stop, but do simplified test */ } else { - /* Full test in progress */ - if (!fixup_device_block_write_error(jcr, dev, block)) { + /* Full test in progress */ + if (!fixup_device_block_write_error(jcr, dev, block)) { Pmsg1(000, _("Cannot fixup device error. %s\n"), strerror_dev(dev)); - ok = FALSE; - unlock_device(dev); - return 0; - } - stop = 1; - BlockNumber = 0; /* start counting for second tape */ + ok = FALSE; + unlock_device(dev); + return 0; + } + stop = 1; + BlockNumber = 0; /* start counting for second tape */ } unlock_device(dev); - return 1; /* end of tape reached */ + return 1; /* end of tape reached */ } /* @@ -1534,7 +1534,7 @@ static int flush_block(DEV_BLOCK *block, int dump) /* * First we label the tape, then we fill * it with data get a new tape and write a few blocks. - */ + */ static void qfillcmd() { DEV_BLOCK *block; @@ -1567,11 +1567,11 @@ static void qfillcmd() } if (!write_record_to_block(block, rec)) { Pmsg0(0, _("Error writing record to block.\n")); - goto bail_out; + goto bail_out; } if (!write_block_to_dev(jcr, dev, block)) { Pmsg0(0, _("Error writing block to device.\n")); - goto bail_out; + goto bail_out; } } printf("\n"); @@ -1612,10 +1612,10 @@ static void rawfill_cmd() *p = block_num; stat = write(dev->fd, block->buf, block->buf_len); if (stat == (int)block->buf_len) { - if ((block_num++ % 100) == 0) { + if ((block_num++ % 100) == 0) { printf("+"); - } - continue; + } + continue; } break; } @@ -1656,7 +1656,7 @@ static struct cmdstruct commands[] = { {"wr", wrcmd, "write a single Bacula block"}, {"rr", rrcmd, "read a single record"}, {"qfill", qfillcmd, "quick fill command"}, - }; + }; #define comsize (sizeof(commands)/sizeof(struct cmdstruct)) static void @@ -1669,15 +1669,15 @@ do_tape_cmds() sm_check(__FILE__, __LINE__, False); found = 0; for (i=0; i 0) - cmd[--i] = 0; - continue; + if (i > 0) + cmd[--i] = 0; + continue; } - + cmd[i++] = ch; cmd[i] = 0; } @@ -1739,15 +1739,15 @@ get_cmd(char *prompt) } /* Dummies to replace askdir.c */ -int dir_get_volume_info(JCR *jcr, int writing) { return 1;} -int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; } -int dir_create_jobmedia_record(JCR *jcr) { return 1; } -int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;} -int dir_send_job_status(JCR *jcr) {return 1;} +int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw writing) { return 1;} +int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; } +int dir_create_jobmedia_record(JCR *jcr) { return 1; } +int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;} +int dir_send_job_status(JCR *jcr) {return 1;} -int dir_find_next_appendable_volume(JCR *jcr) +int dir_find_next_appendable_volume(JCR *jcr) { return 1; } @@ -1762,7 +1762,7 @@ int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev) Pmsg1(-1, "%s", dev->errmsg); /* print reason */ fprintf(stderr, "Mount Volume \"%s\" on device %s and press return when ready: ", jcr->VolumeName, dev_name(dev)); - getchar(); + getchar(); return 1; } @@ -1775,7 +1775,7 @@ int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) force_close_dev(dev); fprintf(stderr, "Mount next Volume on device %s and press return when ready: ", dev_name(dev)); - getchar(); + getchar(); set_volume_name("TestVolume2", 2); labelcmd(); VolumeName = NULL; @@ -1801,7 +1801,7 @@ static int my_mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) } kbs = (double)VolBytes / (1000.0 * (double)now); Pmsg3(-1, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber, - edit_uint64_with_commas(VolBytes, ec1), (float)kbs); + edit_uint64_with_commas(VolBytes, ec1), (float)kbs); if (strcmp(jcr->VolumeName, "TestVolume2") == 0) { end_of_tape = 1; @@ -1818,7 +1818,7 @@ static int my_mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) Pmsg2(0, "Cannot open Dev=%s, Vol=%s\n", dev_name(dev), jcr->VolumeName); return 0; } - return 1; /* next volume mounted */ + return 1; /* next volume mounted */ } static void set_volume_name(char *VolName, int volnum) diff --git a/bacula/src/stored/dev.c b/bacula/src/stored/dev.c index de8d15eb4b..1994e23c1c 100644 --- a/bacula/src/stored/dev.c +++ b/bacula/src/stored/dev.c @@ -270,7 +270,7 @@ open_dev(DEVICE *dev, char *VolName, int mode) if (dev->fd >= 0) { dev->dev_errno = 0; dev->state |= ST_OPENED; - dev->use_count++; + dev->use_count = 1; update_pos_dev(dev); /* update position */ } /* Stop any open() timer we started */ @@ -312,7 +312,7 @@ open_dev(DEVICE *dev, char *VolName, int mode) } else { dev->dev_errno = 0; dev->state |= ST_OPENED; - dev->use_count++; + dev->use_count = 1; update_pos_dev(dev); /* update position */ } Dmsg1(29, "open_dev: disk fd=%d opened\n", dev->fd); @@ -1075,7 +1075,7 @@ int flush_dev(DEVICE *dev) static void do_close(DEVICE *dev) { - Dmsg0(29, "really close_dev\n"); + Dmsg1(29, "really close_dev %s\n", dev->dev_name); close(dev->fd); /* Clean up device packet so it can be reused */ dev->fd = -1; @@ -1089,6 +1089,7 @@ static void do_close(DEVICE *dev) stop_thread_timer(dev->tid); dev->tid = 0; } + dev->use_count = 0; } /* @@ -1104,8 +1105,10 @@ close_dev(DEVICE *dev) } if (dev->fd >= 0 && dev->use_count == 1) { do_close(dev); + } else if (dev->use_count > 0) { + dev->use_count--; } - dev->use_count--; + #ifdef FULL_DEBUG ASSERT(dev->use_count >= 0); #endif @@ -1121,9 +1124,9 @@ void force_close_dev(DEVICE *dev) Emsg0(M_FATAL, 0, dev->errmsg); return; } - Dmsg0(29, "really close_dev\n"); + Dmsg1(29, "Force close_dev %s\n", dev->dev_name); do_close(dev); - dev->use_count--; + #ifdef FULL_DEBUG ASSERT(dev->use_count >= 0); #endif diff --git a/bacula/src/stored/device.c b/bacula/src/stored/device.c index 1d4e26411c..c927ee6921 100644 --- a/bacula/src/stored/device.c +++ b/bacula/src/stored/device.c @@ -61,6 +61,13 @@ extern int debug_level; * medium condition or worse, and error condition. * Attempt to "recover" by obtaining a new Volume. * + * Here are a few things to know: + * jcr->VolCatInfo contains the info on the "current" tape for this job. + * dev->VolCatInfo contains the info on the tape in the drive. + * The tape in the drive could have changed several times since + * the last time the job used it (jcr->VolCatInfo). + * jcr->VolumeName is the name of the current/desired tape in the drive. + * * We enter with device locked, and * exit with device locked. * @@ -90,36 +97,21 @@ int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) /* Unlock, but leave BLOCKED */ unlock_device(dev); - /* - * Walk through all attached jcrs creating a jobmedia_record() - */ - Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->VolCatInfo.VolCatName); - for (JCR *mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) { - if (mjcr->JobId == 0) { - continue; /* ignore console */ - } - Dmsg1(100, "create JobMedia for Job %s\n", mjcr->Job); - if (dev->state & ST_TAPE) { - mjcr->EndBlock = dev->EndBlock; - mjcr->EndFile = dev->EndFile; - Dmsg2(200, "Fixup EndFile=%u EndBlock=%u\n", mjcr->EndFile, mjcr->EndBlock); - } else { - mjcr->EndBlock = (uint32_t)dev->file_addr; - mjcr->EndFile = (uint32_t)(dev->file_addr >> 32); - } - if (!dir_create_jobmedia_record(mjcr)) { - Jmsg(mjcr, M_ERROR, 0, _("Could not create JobMedia record for Volume=%s Job=%s\n"), - dev->VolCatInfo.VolCatName, mjcr->Job); - P(dev->mutex); - unblock_device(dev); - return 0; - } - mjcr->VolFirstIndex = 0; /* prevent writing jobmedia second time */ + /* Create a jobmedia record for this job */ + if (!dir_create_jobmedia_record(jcr)) { + Jmsg(jcr, M_ERROR, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"), + jcr->VolCatInfo.VolCatName, jcr->Job); + P(dev->mutex); + unblock_device(dev); + return 0; } strcpy(dev->VolCatInfo.VolCatStatus, "Full"); Dmsg2(200, "Call update_vol_info Stat=%s Vol=%s\n", dev->VolCatInfo.VolCatStatus, dev->VolCatInfo.VolCatName); + dev->VolCatInfo.VolCatFiles = dev->file; /* set number of files */ + /* *****FIXME**** this needs to be done elsewhere */ + dev->VolCatInfo.VolCatJobs++; /* increment number of jobs */ if (!dir_update_volume_info(jcr, &dev->VolCatInfo, 0)) { /* send Volume info to Director */ P(dev->mutex); unblock_device(dev); @@ -165,24 +157,27 @@ int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) } free_block(label_blk); - - /* Update start block/file for overflow block */ - jcr->NumVolumes++; + /* + * Walk through all attached jcrs indicating the volume has changed + */ + Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->VolCatInfo.VolCatName); for (JCR *mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) { - /* Set new start/end positions */ - if (dev->state & ST_TAPE) { - mjcr->StartBlock = dev->block_num; - mjcr->StartFile = dev->file; - } else { - mjcr->StartBlock = (uint32_t)dev->file_addr; - mjcr->StartFile = (uint32_t)(dev->file_addr >> 32); + if (mjcr->JobId == 0) { + continue; /* ignore console */ + } + mjcr->NewVol = true; + if (jcr != mjcr) { + pm_strcpy(&mjcr->VolumeName, jcr->VolumeName); /* get a copy of the new volume */ } - /* Set first FirstIndex for new Volume */ - mjcr->VolFirstIndex = mjcr->JobFiles; - mjcr->run_time += time(NULL) - wait_time; /* correct run time */ } - /* Write overflow block to tape */ + /* Clear NewVol now because dir_get_volume_info() already done */ + jcr->NewVol = false; + set_new_volume_parameters(jcr, dev); + + jcr->run_time += time(NULL) - wait_time; /* correct run time for mount wait */ + + /* Write overflow block to device */ Dmsg0(190, "Write overflow block to dev\n"); if (!write_block_to_dev(jcr, dev, block)) { Pmsg1(0, "write_block_to_device overflow block failed. ERR=%s", @@ -195,6 +190,27 @@ int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) return 1; /* device locked */ } +void set_new_volume_parameters(JCR *jcr, DEVICE *dev) +{ + if (jcr->NewVol && !dir_get_volume_info(jcr, GET_VOL_INFO_FOR_WRITE)) { + Jmsg1(jcr, M_ERROR, 0, "%s", jcr->errmsg); + } + /* Set new start/end positions */ + if (dev->state & ST_TAPE) { + jcr->StartBlock = dev->block_num; + jcr->StartFile = dev->file; + } else { + jcr->StartBlock = (uint32_t)dev->file_addr; + jcr->StartFile = (uint32_t)(dev->file_addr >> 32); + } + /* Reset indicies */ + jcr->VolFirstIndex = 0; + jcr->VolLastIndex = 0; + jcr->NumVolumes++; + jcr->NewVol = false; + jcr->WroteVol = false; +} + /* * Open the device. Expect dev to already be initialized. diff --git a/bacula/src/stored/dircmd.c b/bacula/src/stored/dircmd.c index c6b127862f..68d6b4d038 100644 --- a/bacula/src/stored/dircmd.c +++ b/bacula/src/stored/dircmd.c @@ -402,7 +402,7 @@ static void label_volume_if_ok(JCR *jcr, DEVICE *dev, char *oldname, bnet_fsend(dir, _("3912 Failed to label Volume: ERR=%s\n"), strerror_dev(dev)); break; } - strcpy(jcr->VolumeName, newname); + pm_strcpy(&jcr->VolumeName, newname); bnet_fsend(dir, _("3000 OK label. Volume=%s Device=%s\n"), newname, dev->dev_name); break; diff --git a/bacula/src/stored/mount.c b/bacula/src/stored/mount.c index 2ff25f96f1..4efe9b3641 100644 --- a/bacula/src/stored/mount.c +++ b/bacula/src/stored/mount.c @@ -175,7 +175,7 @@ read_volume: memcpy(&VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo)); /* Check if this is a valid Volume in the pool */ pm_strcpy(&jcr->VolumeName, dev->VolHdr.VolName); - if (!dir_get_volume_info(jcr, 1)) { + if (!dir_get_volume_info(jcr, GET_VOL_INFO_FOR_WRITE)) { Mmsg(&jcr->errmsg, _("Director wanted Volume \"%s\".\n" " Current Volume \"%s\" not acceptable because:\n" " %s"), @@ -363,9 +363,14 @@ int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block) void release_volume(JCR *jcr, DEVICE *dev) { + /********FIXME******* if WroteVol, must write JobMedia record */ /* * First erase all memory of the current volume */ + + if (jcr->WroteVol) { + Jmsg0(jcr, M_ERROR, 0, "Hey!!!!! WriteVol non-zero !!!!!\n"); + } dev->block_num = dev->file = 0; dev->EndBlock = dev->EndFile = 0; memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo)); diff --git a/bacula/src/stored/protos.h b/bacula/src/stored/protos.h index 8ef17a840a..644a37494a 100644 --- a/bacula/src/stored/protos.h +++ b/bacula/src/stored/protos.h @@ -28,131 +28,136 @@ uint32_t new_VolSessionId(); /* From acquire.c */ -DEVICE *acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); -int acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); -int release_device(JCR *jcr, DEVICE *dev); +DEVICE *acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); +int acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); +int release_device(JCR *jcr, DEVICE *dev); /* From askdir.c */ -int dir_get_volume_info(JCR *jcr, int writing); -int dir_find_next_appendable_volume(JCR *jcr); -int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel); -int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev); -int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev); -int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec); -int dir_send_job_status(JCR *jcr); -int dir_create_jobmedia_record(JCR *jcr); +enum get_vol_info_rw { + GET_VOL_INFO_FOR_WRITE, + GET_VOL_INFO_FOR_READ +}; +int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw); +int dir_find_next_appendable_volume(JCR *jcr); +int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel); +int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev); +int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev); +int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec); +int dir_send_job_status(JCR *jcr); +int dir_create_jobmedia_record(JCR *jcr); /* authenticate.c */ -int authenticate_director(JCR *jcr); -int authenticate_filed(JCR *jcr); +int authenticate_director(JCR *jcr); +int authenticate_filed(JCR *jcr); /* From block.c */ -void dump_block(DEV_BLOCK *b, char *msg); +void dump_block(DEV_BLOCK *b, char *msg); DEV_BLOCK *new_block(DEVICE *dev); -void init_block_write(DEV_BLOCK *block); -void empty_block(DEV_BLOCK *block); -void free_block(DEV_BLOCK *block); -int write_block_to_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); -int write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); +void init_block_write(DEV_BLOCK *block); +void empty_block(DEV_BLOCK *block); +void free_block(DEV_BLOCK *block); +int write_block_to_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); +int write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); #define CHECK_BLOCK_NUMBERS true #define NO_BLOCK_NUMBER_CHECK false -int read_block_from_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers); -int read_block_from_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers); +int read_block_from_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers); +int read_block_from_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers); /* From butil.c -- utilities for SD tool programs */ -void print_ls_output(char *fname, char *link, int type, struct stat *statp); +void print_ls_output(char *fname, char *link, int type, struct stat *statp); JCR *setup_jcr(char *name, char *device, BSR *bsr, char *VolumeName); DEVICE *setup_to_access_device(JCR *jcr, int read_access); -void display_tape_error_status(JCR *jcr, DEVICE *dev); +void display_tape_error_status(JCR *jcr, DEVICE *dev); DEVRES *find_device_res(char *device_name, int read_access); /* From dev.c */ -DEVICE *init_dev(DEVICE *dev, DEVRES *device); -int open_dev(DEVICE *dev, char *VolName, int mode); -void close_dev(DEVICE *dev); -void force_close_dev(DEVICE *dev); -int truncate_dev(DEVICE *dev); -void term_dev(DEVICE *dev); -char * strerror_dev(DEVICE *dev); -void clrerror_dev(DEVICE *dev, int func); -int update_pos_dev(DEVICE *dev); -int rewind_dev(DEVICE *dev); -int load_dev(DEVICE *dev); -int offline_dev(DEVICE *dev); -int flush_dev(DEVICE *dev); -int weof_dev(DEVICE *dev, int num); -int write_block(DEVICE *dev); -int write_dev(DEVICE *dev, char *buf, size_t len); -int read_dev(DEVICE *dev, char *buf, size_t len); -int status_dev(DEVICE *dev, uint32_t *status); -int eod_dev(DEVICE *dev); -int fsf_dev(DEVICE *dev, int num); -int fsr_dev(DEVICE *dev, int num); -int bsf_dev(DEVICE *dev, int num); -int bsr_dev(DEVICE *dev, int num); -void attach_jcr_to_device(DEVICE *dev, JCR *jcr); -void detach_jcr_from_device(DEVICE *dev, JCR *jcr); -JCR *next_attached_jcr(DEVICE *dev, JCR *jcr); -int dev_can_write(DEVICE *dev); -int offline_or_rewind_dev(DEVICE *dev); +DEVICE *init_dev(DEVICE *dev, DEVRES *device); +int open_dev(DEVICE *dev, char *VolName, int mode); +void close_dev(DEVICE *dev); +void force_close_dev(DEVICE *dev); +int truncate_dev(DEVICE *dev); +void term_dev(DEVICE *dev); +char * strerror_dev(DEVICE *dev); +void clrerror_dev(DEVICE *dev, int func); +int update_pos_dev(DEVICE *dev); +int rewind_dev(DEVICE *dev); +int load_dev(DEVICE *dev); +int offline_dev(DEVICE *dev); +int flush_dev(DEVICE *dev); +int weof_dev(DEVICE *dev, int num); +int write_block(DEVICE *dev); +int write_dev(DEVICE *dev, char *buf, size_t len); +int read_dev(DEVICE *dev, char *buf, size_t len); +int status_dev(DEVICE *dev, uint32_t *status); +int eod_dev(DEVICE *dev); +int fsf_dev(DEVICE *dev, int num); +int fsr_dev(DEVICE *dev, int num); +int bsf_dev(DEVICE *dev, int num); +int bsr_dev(DEVICE *dev, int num); +void attach_jcr_to_device(DEVICE *dev, JCR *jcr); +void detach_jcr_from_device(DEVICE *dev, JCR *jcr); +JCR *next_attached_jcr(DEVICE *dev, JCR *jcr); +int dev_can_write(DEVICE *dev); +int offline_or_rewind_dev(DEVICE *dev); /* Get info about device */ -char * dev_name(DEVICE *dev); -char * dev_vol_name(DEVICE *dev); +char * dev_name(DEVICE *dev); +char * dev_vol_name(DEVICE *dev); uint32_t dev_block(DEVICE *dev); uint32_t dev_file(DEVICE *dev); -int dev_is_tape(DEVICE *dev); +int dev_is_tape(DEVICE *dev); /* From device.c */ -int open_device(DEVICE *dev); -int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); +int open_device(DEVICE *dev); +int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); void _lock_device(char *file, int line, DEVICE *dev); void _unlock_device(char *file, int line, DEVICE *dev); void _block_device(char *file, int line, DEVICE *dev, int state); void _unblock_device(char *file, int line, DEVICE *dev); void _steal_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state); void _give_back_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold); +void set_new_volume_parameters(JCR *jcr, DEVICE *dev); /* From dircmd.c */ -void *connection_request(void *arg); +void *connection_request(void *arg); /* From fd_cmds.c */ -void run_job(JCR *jcr); +void run_job(JCR *jcr); /* From job.c */ -void stored_free_jcr(JCR *jcr); -void connection_from_filed(void *arg); -void handle_filed_connection(BSOCK *fd, char *job_name); +void stored_free_jcr(JCR *jcr); +void connection_from_filed(void *arg); +void handle_filed_connection(BSOCK *fd, char *job_name); /* From label.c */ -int read_dev_volume_label(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); -void create_session_label(JCR *jcr, DEV_RECORD *rec, int label); -void create_volume_label(DEVICE *dev, char *VolName); -int write_volume_label_to_dev(JCR *jcr, DEVRES *device, char *VolName, char *PoolName); -int write_session_label(JCR *jcr, DEV_BLOCK *block, int label); -int write_volume_label_to_block(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); -void dump_volume_label(DEVICE *dev); -void dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose); -int unser_volume_label(DEVICE *dev, DEV_RECORD *rec); -int unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec); +int read_dev_volume_label(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); +void create_session_label(JCR *jcr, DEV_RECORD *rec, int label); +void create_volume_label(DEVICE *dev, char *VolName); +int write_volume_label_to_dev(JCR *jcr, DEVRES *device, char *VolName, char *PoolName); +int write_session_label(JCR *jcr, DEV_BLOCK *block, int label); +int write_volume_label_to_block(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); +void dump_volume_label(DEVICE *dev); +void dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose); +int unser_volume_label(DEVICE *dev, DEV_RECORD *rec); +int unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec); /* From match_bsr.c */ int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, - SESSION_LABEL *sesrec); + SESSION_LABEL *sesrec); /* From mount.c */ -int mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release); -int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); -void release_volume(JCR *jcr, DEVICE *dev); +int mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release); +int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block); +void release_volume(JCR *jcr, DEVICE *dev); /* From autochanger.c */ -int autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir); -int autochanger_list(JCR *jcr, DEVICE *dev, BSOCK *dir); -void invalidate_slot_in_catalog(JCR *jcr); +int autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir); +int autochanger_list(JCR *jcr, DEVICE *dev, BSOCK *dir); +void invalidate_slot_in_catalog(JCR *jcr); /* From parse_bsr.c */ @@ -167,11 +172,11 @@ extern void create_vol_list(JCR *jcr); /* From record.c */ char *FI_to_ascii(int fi); char *stream_to_ascii(int stream, int fi); -int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec); -int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec); -int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec); +int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec); +int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec); +int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec); DEV_RECORD *new_record(); -void free_record(DEV_RECORD *rec); +void free_record(DEV_RECORD *rec); /* From read_record.c */ int read_records(JCR *jcr, DEVICE *dev, diff --git a/bacula/src/version.h b/bacula/src/version.h index e71542c889..1f79bbf30e 100644 --- a/bacula/src/version.h +++ b/bacula/src/version.h @@ -1,8 +1,8 @@ /* */ #define VERSION "1.31" #define VSTRING "1" -#define BDATE "16 Jun 2003" -#define LSMDATE "16Jun03" +#define BDATE "18 Jun 2003" +#define LSMDATE "18Jun03" /* Debug flags */ #define DEBUG 1