]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/stored/record.c
- Tweak install chapter of French manual to add new paragraph
[bacula/bacula] / bacula / src / stored / record.c
index 382cb54792c1157caf314f7e64f416bfaaf30705..d3bb44eab6c35b45128c9ffb28bea864a7e15d52 100644 (file)
@@ -3,12 +3,13 @@
  *   record.c -- tape record handling functions
  *
  *             Kern Sibbald, April MMI
+ *               added BB02 format October MMII
  *
  *   Version $Id$
  *
  */
 /*
-   Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
+   Copyright (C) 2000-2004 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
@@ -35,9 +36,12 @@ extern int debug_level;
 
 /*
  * Convert a FileIndex into a printable
- * ASCII string.  Not reentrant.
+ *   ASCII string.  Not reentrant.
+ * If the FileIndex is negative, it flags the
+ *   record as a Label, otherwise it is simply
+ *   the FileIndex of the current file.
  */
-char *FI_to_ascii(int fi)
+const char *FI_to_ascii(int fi)
 {
    static char buf[20];
    if (fi >= 0) {
@@ -55,6 +59,9 @@ char *FI_to_ascii(int fi)
       return "SOS_LABEL";
    case EOS_LABEL:
       return "EOS_LABEL";
+   case EOT_LABEL:
+      return "EOT_LABEL";
+      break;
    default:
      sprintf(buf, "unknown: %d", fi);
      return buf;
@@ -65,19 +72,70 @@ char *FI_to_ascii(int fi)
 /* 
  * Convert a Stream ID into a printable
  * ASCII string.  Not reentrant.
+
+ * A negative stream number represents
+ *   stream data that is continued from a
+ *   record in the previous block.
+ * If the FileIndex is negative, we are
+ *   dealing with a Label, hence the
+ *   stream is the JobId.
  */
-char *stream_to_ascii(int stream)
+const char *stream_to_ascii(int stream, int fi)
 {
     static char buf[20];
+    if (fi < 0) {
+       sprintf(buf, "%d", stream);
+       return buf;     
+    }
     switch (stream) {
     case STREAM_UNIX_ATTRIBUTES:
        return "UATTR";
     case STREAM_FILE_DATA:
        return "DATA";
+    case STREAM_WIN32_DATA:
+       return "WIN32-DATA";
+    case STREAM_WIN32_GZIP_DATA:
+       return "WIN32-GZIP";
     case STREAM_MD5_SIGNATURE:
        return "MD5";
+    case STREAM_SHA1_SIGNATURE:
+       return "SHA1";
     case STREAM_GZIP_DATA:
        return "GZIP";
+    case STREAM_UNIX_ATTRIBUTES_EX:
+       return "UNIX-ATTR-EX";
+    case STREAM_SPARSE_DATA:
+       return "SPARSE-DATA";
+    case STREAM_SPARSE_GZIP_DATA:
+       return "SPARSE-GZIP";
+    case STREAM_PROGRAM_NAMES:
+       return "PROG-NAMES";
+    case STREAM_PROGRAM_DATA:
+       return "PROG-DATA";
+    case -STREAM_UNIX_ATTRIBUTES:
+       return "contUATTR";
+    case -STREAM_FILE_DATA:
+       return "contDATA";
+    case -STREAM_WIN32_DATA:
+       return "contWIN32-DATA";
+    case -STREAM_WIN32_GZIP_DATA:
+       return "contWIN32-GZIP";
+    case -STREAM_MD5_SIGNATURE:
+       return "contMD5";
+    case -STREAM_SHA1_SIGNATURE:
+       return "contSHA1";
+    case -STREAM_GZIP_DATA:
+       return "contGZIP";
+    case -STREAM_UNIX_ATTRIBUTES_EX:
+       return "contUNIX-ATTR-EX";
+    case -STREAM_SPARSE_DATA:
+       return "contSPARSE-DATA";
+    case -STREAM_SPARSE_GZIP_DATA:
+       return "contSPARSE-GZIP";
+    case -STREAM_PROGRAM_NAMES:
+       return "contPROG-NAMES";
+    case -STREAM_PROGRAM_DATA:
+       return "contPROG-DATA";
     default:
        sprintf(buf, "%d", stream);
        return buf;     
@@ -91,12 +149,21 @@ DEV_RECORD *new_record(void)
 {
    DEV_RECORD *rec;
 
-   rec = (DEV_RECORD *) get_memory(sizeof(DEV_RECORD));
+   rec = (DEV_RECORD *)get_memory(sizeof(DEV_RECORD));
    memset(rec, 0, sizeof(DEV_RECORD));
-   rec->data = (char *) get_pool_memory(PM_MESSAGE);
+   rec->data = get_pool_memory(PM_MESSAGE);
    return rec;
 }
 
+void empty_record(DEV_RECORD *rec)
+{
+   rec->File = rec->Block = 0;
+   rec->VolSessionId = rec->VolSessionTime = 0;
+   rec->FileIndex = rec->Stream = 0;
+   rec->data_len = rec->remainder = 0;
+   rec->state &= ~(REC_PARTIAL_RECORD|REC_BLOCK_EMPTY|REC_NO_MATCH|REC_CONTINUATION);
+}
+
 /*
  * Free the record entity 
  *
@@ -104,71 +171,20 @@ DEV_RECORD *new_record(void)
 void free_record(DEV_RECORD *rec) 
 {
    Dmsg0(150, "Enter free_record.\n");
-   free_pool_memory(rec->data);
+   if (rec->data) {
+      free_pool_memory(rec->data);
+   }
    Dmsg0(150, "Data buf is freed.\n");
    free_pool_memory((POOLMEM *)rec);
    Dmsg0(150, "Leave free_record.\n");
 } 
 
-/*
- * Read a record from a block
- *   if necessary, read the block from the device without locking
- *   if necessary, handle getting a new Volume
- *
- *  Returns: 0 on failure
- *          1 on success
- */
-int read_record(DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *record)
-{
-   Dmsg2(90, "read_record() dev=%x state=%x\n", dev, dev->state);
-
-   while (!read_record_from_block(block, record)) {
-      Dmsg2(90, "!read_record_from_block data_len=%d rem=%d\n", record->data_len,
-               record->remainder);
-      if (!read_block_from_dev(dev, block)) {
-        /**** ****FIXME**** handle getting a new Volume */
-         Dmsg0(200, "===== Got read block I/O error ======\n");
-        return 0;
-      }
-   }
-   Dmsg4(90, "read_record FI=%s SessId=%d Strm=%s len=%d\n",
-             FI_to_ascii(record->FileIndex), record->VolSessionId, 
-             stream_to_ascii(record->Stream), record->data_len);
-   return 1;
-}
-
-
-/*
- * Write a record to block 
- *   if necessary, write the block to the device with locking
- *   if necessary, handle getting a new Volume
- *
- *  Returns: 0 on failure
- *          1 on success
- */
-int write_record_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *record)
-{
-   Dmsg2(190, "write_device() dev=%x state=%x\n", dev, dev->state);
-
-   while (!write_record_to_block(block, record)) {
-      Dmsg2(190, "!write_record data_len=%d rem=%d\n", record->data_len,
-                record->remainder);
-      if (!write_block_to_device(jcr, dev, block)) {
-         Dmsg0(90, "Got write_block_to_dev error.\n");
-        return 0;
-      }
-   }
-   Dmsg4(190, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
-      FI_to_ascii(record->FileIndex), record->VolSessionId, 
-      stream_to_ascii(record->Stream), record->data_len);
-   return 1;
-}
 
 /*
  * Write a Record to the block
  *
- *  Returns: 0 on failure (none or partially written)
- *          1 on success (all bytes written)
+ *  Returns: false on failure (none or partially written)
+ *          true  on success (all bytes written)
  *
  *  and remainder returned in packet.
  *
@@ -178,42 +194,56 @@ int write_record_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec
  *  non-zero), and 2. The remaining bytes to write may not
  *  all fit into the block.
  */
-int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
+bool write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
 {
    ser_declare;
    uint32_t remlen;
 
-   sm_check(__FILE__, __LINE__, False);
    remlen = block->buf_len - block->binbuf;
 
    ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
-   ASSERT(remlen >= 0);
+   ASSERT(block->buf_len >= block->binbuf);
 
    Dmsg6(190, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n\
 rem=%d remainder=%d\n",
       FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
-      stream_to_ascii(rec->Stream), rec->data_len,
+      stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len,
       remlen, rec->remainder);
 
+   /*
+    * If rec->remainder is non-zero, we have been called a
+    *  second (or subsequent) time to finish writing a record
+    *  that did not previously fit into the block.
+    */
    if (rec->remainder == 0) {
       /* Require enough room to write a full header */
-      if (remlen >= RECHDR_LENGTH) {
-        ser_begin(block->bufp, RECHDR_LENGTH);
-        ser_uint32(rec->VolSessionId);
-        ser_uint32(rec->VolSessionTime);
+      if (remlen >= WRITE_RECHDR_LENGTH) {
+        ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
+        if (BLOCK_VER == 1) {
+           ser_uint32(rec->VolSessionId);
+           ser_uint32(rec->VolSessionTime);
+        } else {
+           block->VolSessionId = rec->VolSessionId;
+           block->VolSessionTime = rec->VolSessionTime;
+        }
         ser_int32(rec->FileIndex);
         ser_int32(rec->Stream);
         ser_uint32(rec->data_len);
-        ASSERT(ser_length(block->bufp) == RECHDR_LENGTH);
 
-        block->bufp += RECHDR_LENGTH;
-        block->binbuf += RECHDR_LENGTH;
-        remlen -= RECHDR_LENGTH;
+        block->bufp += WRITE_RECHDR_LENGTH;
+        block->binbuf += WRITE_RECHDR_LENGTH;
+        remlen -= WRITE_RECHDR_LENGTH;
         rec->remainder = rec->data_len;
+        if (rec->FileIndex > 0) {
+           /* If data record, update what we have in this block */
+           if (block->FirstIndex == 0) {
+              block->FirstIndex = rec->FileIndex;
+           }
+           block->LastIndex = rec->FileIndex;
+        }
       } else {
-        rec->remainder = rec->data_len + RECHDR_LENGTH;
-        sm_check(__FILE__, __LINE__, False);
-        return 0;
+        rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH;
+        return false;
       }
    } else {
       /* 
@@ -221,15 +251,24 @@ rem=%d remainder=%d\n",
        * time. Presumably we have a new buffer (possibly 
        * containing a volume label), so the new header 
        * should be able to fit in the block -- otherwise we have
-       * an error.  Note, we may have to continue splitting the
-       * data record though.
+       * an error.  Note, we have to continue splitting the
+       * data record if it is longer than the block.
        * 
-       *  First, write the header, then write as much as 
+       * First, write the header, then write as much as 
        * possible of the data record.
+       *
+       * Every time we write a header and it is a continuation
+       * of a previous partially written record, we store the
+       * Stream as -Stream in the record header.
        */
-      ser_begin(block->bufp, RECHDR_LENGTH);
-      ser_uint32(rec->VolSessionId);
-      ser_uint32(rec->VolSessionTime);
+      ser_begin(block->bufp, WRITE_RECHDR_LENGTH);
+      if (BLOCK_VER == 1) {
+        ser_uint32(rec->VolSessionId);
+        ser_uint32(rec->VolSessionTime);
+      } else {
+        block->VolSessionId = rec->VolSessionId;
+        block->VolSessionTime = rec->VolSessionTime;
+      }
       ser_int32(rec->FileIndex);
       if (rec->remainder > rec->data_len) {
         ser_int32(rec->Stream);      /* normal full header */
@@ -239,21 +278,27 @@ rem=%d remainder=%d\n",
         ser_int32(-rec->Stream);     /* mark this as a continuation record */
         ser_uint32(rec->remainder);  /* bytes to do */
       }
-      ASSERT(ser_length(block->bufp) == RECHDR_LENGTH);
 
       /* Require enough room to write a full header */
-      ASSERT(remlen >= RECHDR_LENGTH);
-
-      block->bufp += RECHDR_LENGTH;
-      block->binbuf += RECHDR_LENGTH;
-      remlen -= RECHDR_LENGTH;
+      ASSERT(remlen >= WRITE_RECHDR_LENGTH);
+
+      block->bufp += WRITE_RECHDR_LENGTH;
+      block->binbuf += WRITE_RECHDR_LENGTH;
+      remlen -= WRITE_RECHDR_LENGTH;
+      if (rec->FileIndex > 0) {
+        /* If data record, update what we have in this block */
+        if (block->FirstIndex == 0) {
+           block->FirstIndex = rec->FileIndex;
+        }
+        block->LastIndex = rec->FileIndex;
+      }
    }
    if (remlen == 0) {
-      sm_check(__FILE__, __LINE__, False);
-      return 0;                      /* partial transfer */
+      return false;                  /* partial transfer */
    }
 
-   /* Now deal with data record.
+   /*
+    * Now deal with data record.
     * Part of it may have already been transferred, and we 
     * may not have enough room to transfer the whole this time.
     */
@@ -267,12 +312,13 @@ rem=%d remainder=%d\n",
       } else {
         memcpy(block->bufp, rec->data+rec->data_len-rec->remainder, 
                remlen);
+#ifdef xxxxxSMCHECK
         if (!sm_check_rtn(__FILE__, __LINE__, False)) {
            /* We damaged a buffer */
             Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n\
 rem=%d remainder=%d\n",
               FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
-              stream_to_ascii(rec->Stream), rec->data_len,
+              stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len,
               remlen, rec->remainder);
             Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
               block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
@@ -282,85 +328,175 @@ rem=%d remainder=%d\n",
 
                Emsg0(M_ABORT, 0, "Damaged buffer\n");
         }
+#endif
 
         block->bufp += remlen;
         block->binbuf += remlen;
         rec->remainder -= remlen;
-        return 0;                    /* did partial transfer */
+        return false;                /* did partial transfer */
       }
    }
    rec->remainder = 0;               /* did whole transfer */
-   sm_check(__FILE__, __LINE__, False);
-   return 1;
+   return true;
+}
+
+
+/*
+ * Test if we can write whole record to the block
+ *
+ *  Returns: false on failure 
+ *          true  on success (all bytes can be written)
+ */
+bool can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
+{
+   uint32_t remlen;
+
+   remlen = block->buf_len - block->binbuf;
+   if (rec->remainder == 0) {
+      if (remlen >= WRITE_RECHDR_LENGTH) {
+        remlen -= WRITE_RECHDR_LENGTH;
+        rec->remainder = rec->data_len;
+      } else {
+        return false;
+      }
+   } else {
+      return false;
+   }
+   if (rec->remainder > 0 && remlen < rec->remainder) {
+      return false;
+   }
+   return true;
 }
 
 
 /*
  * Read a Record from the block
- *  Returns: 0 on failure
- *          1 on success
+ *  Returns: false if nothing read or if the continuation record does not match.
+ *                In both of these cases, a block read must be done.
+ *          true  if at least the record header was read, this 
+ *                routine may have to be called again with a new
+ *                block if the entire record was not read.
  */
-int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
+bool read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
 {
    ser_declare;
-   uint32_t data_bytes;
-   int32_t Stream;
    uint32_t remlen;
+   uint32_t VolSessionId;
+   uint32_t VolSessionTime;
+   int32_t  FileIndex;
+   int32_t  Stream;
+   uint32_t data_bytes;
+   uint32_t rhl;
 
    remlen = block->binbuf;
+   rec->Block = block->BlockNumber;
+   rec->File = ((DEVICE *)block->dev)->file;
+
+   /* Clear state flags */      
+   rec->state = 0;
+   if (((DEVICE *)block->dev)->state & ST_TAPE) {
+      rec->state |= REC_ISTAPE;
+   }
+
 
    /* 
     * Get the header. There is always a full header,
     * otherwise we find it in the next block.
     */
-   if (remlen >= RECHDR_LENGTH) {
-      Dmsg3(90, "read_record_block: remlen=%d data_len=%d rem=%d\n", 
-           remlen, rec->data_len, rec->remainder);
-/*    memcpy(rec->ser_buf, block->bufp, RECHDR_LENGTH); */
-
-      unser_begin(block->bufp, RECHDR_LENGTH);
-      unser_uint32(rec->VolSessionId);
-      unser_uint32(rec->VolSessionTime);
-      unser_int32(rec->FileIndex);
+   Dmsg3(100, "Block=%d Ver=%d size=%u\n", block->BlockNumber, block->BlockVer,
+        block->block_len);
+   if (block->BlockVer == 1) {
+      rhl = RECHDR1_LENGTH;
+   } else {
+      rhl = RECHDR2_LENGTH;
+   }
+   if (remlen >= rhl) {
+      Dmsg4(90, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n", 
+           remlen, rec->data_len, rec->remainder, block->BlockVer);
+
+      unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
+      if (block->BlockVer == 1) {
+        unser_uint32(VolSessionId);
+        unser_uint32(VolSessionTime);
+      } else {
+        VolSessionId = block->VolSessionId;
+        VolSessionTime = block->VolSessionTime;
+      }
+      unser_int32(FileIndex);
       unser_int32(Stream);
       unser_uint32(data_bytes);
 
-      ASSERT(unser_length(block->bufp) == RECHDR_LENGTH);
-      block->bufp += RECHDR_LENGTH;
-      block->binbuf -= RECHDR_LENGTH;
-      remlen -= RECHDR_LENGTH;
+      block->bufp += rhl;
+      block->binbuf -= rhl;
+      remlen -= rhl;
 
+      /* If we are looking for more (remainder!=0), we reject anything
+       *  where the VolSessionId and VolSessionTime don't agree
+       */
+      if (rec->remainder && (rec->VolSessionId != VolSessionId || 
+                            rec->VolSessionTime != VolSessionTime)) {
+        rec->state |= REC_NO_MATCH;
+         Dmsg0(500, "remainder and VolSession doesn't match\n");
+        return false;             /* This is from some other Session */
+      }
+
+      /* if Stream is negative, it means that this is a continuation
+       * of a previous partially written record.
+       */
       if (Stream < 0) {              /* continuation record? */
          Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n", 
            rec->remainder);
-        /* ****FIXME**** add code to verify that this
-         *  is the correct continuation of the previous
-         *  record.
-         */
+        rec->state |= REC_CONTINUATION;
          if (!rec->remainder) {       /* if we didn't read previously */
-           rec->data_len = 0;        /* simply return data */
+           rec->data_len = 0;        /* return data as if no continuation */
+        } else if (rec->Stream != -Stream) {
+           rec->state |= REC_NO_MATCH;
+           return false;             /* This is from some other Session */
         }
         rec->Stream = -Stream;       /* set correct Stream */
       } else {                       /* Regular record */
         rec->Stream = Stream;
         rec->data_len = 0;           /* transfer to beginning of data */
       }
-      Dmsg6(90, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%d\n\
-remlen=%d data_len=%d\n",
+      rec->VolSessionId = VolSessionId;
+      rec->VolSessionTime = VolSessionTime;
+      rec->FileIndex = FileIndex;
+      if (FileIndex > 0) {
+        if (block->FirstIndex == 0) {
+           block->FirstIndex = FileIndex;
+        }
+        block->LastIndex = FileIndex;
+      }
+
+      Dmsg6(100, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
+                 "remlen=%d data_len=%d\n",
         FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
-        stream_to_ascii(rec->Stream), data_bytes, remlen, rec->data_len);
+        stream_to_ascii(rec->Stream, rec->FileIndex), data_bytes, remlen, 
+        rec->data_len);
    } else {
+      /*    
+       * No more records in this block because the number   
+       * of remaining bytes are less than a record header 
+       * length, so return empty handed, but indicate that
+       * he must read again. By returning, we allow the
+       * higher level routine to fetch the next block and
+       * then reread.
+       */
       Dmsg0(90, "read_record_block: nothing\n");
+#ifdef xxx
       if (!rec->remainder) {
         rec->remainder = 1;          /* set to expect continuation */
         rec->data_len = 0;           /* no data transferred */
       }
-      return 0;
+#endif
+      rec->state |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
+      empty_block(block);                     /* mark block empty */
+      return false;
    }
 
    ASSERT(data_bytes < MAX_BLOCK_LENGTH);      /* temp sanity check */
 
-   rec->data = (char *) check_pool_memory_size(rec->data, rec->data_len+data_bytes);
+   rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
    
    /*
     * At this point, we have read the header, now we
@@ -384,11 +520,12 @@ remlen=%d data_len=%d\n",
       rec->data_len += remlen;
       rec->remainder = 1;            /* partial record transferred */
       Dmsg1(90, "read_record_block: partial xfered=%d\n", rec->data_len);
-      return 0;
+      rec->state |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
+      return 1;
    }
    rec->remainder = 0;
    Dmsg4(90, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
       FI_to_ascii(rec->FileIndex), rec->VolSessionId, 
-      stream_to_ascii(rec->Stream), rec->data_len);
-   return 1;                         /* transferred full record */
+      stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len);
+   return true;                      /* transferred full record */
 }