List:Commits« Previous MessageNext Message »
From:rsomla Date:November 14 2007 6:49pm
Subject:bk commit into 6.0 tree (rafal:1.2660) WL#4063
View as plain text  
Below is the list of changes that have just been committed into a local
6.0 repository of rafal. When rafal does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-11-14 19:49:28+01:00, rafal@quant.(none) +6 -0
  WL#4063 (Online Backup: Finalize backup stream format)
    
  This patch introduces the backup stream library for reading/writing backup 
  image using version 1 of the format. The format is documented in WL#4063 and
  in source comments.

  sql/backup/CMakeLists.txt@stripped, 2007-11-14 19:48:06+01:00, rafal@quant.(none) +1 -0
    Added definition of backupstream library.

  sql/backup/Makefile.am@stripped, 2007-11-14 19:48:06+01:00, rafal@quant.(none) +10 -2
    Added definition of backupstream library.

  sql/backup/stream_v1.c@stripped, 2007-11-14 19:48:06+01:00, rafal@quant.(none) +1976 -0
    Implementation of backup stream API - high level functions.

  sql/backup/stream_v1.c@stripped, 2007-11-14 19:48:06+01:00, rafal@quant.(none) +0 -0

  sql/backup/stream_v1.h@stripped, 2007-11-14 19:48:06+01:00, rafal@quant.(none) +396 -0
    Backup stream API (types and functions).

  sql/backup/stream_v1.h@stripped, 2007-11-14 19:48:06+01:00, rafal@quant.(none) +0 -0

  sql/backup/stream_v1_services.h@stripped, 2007-11-14 19:48:07+01:00, rafal@quant.(none) +273 -0
    Catalogue and other services API used by backup stream library.

  sql/backup/stream_v1_services.h@stripped, 2007-11-14 19:48:07+01:00, rafal@quant.(none) +0 -0

  sql/backup/stream_v1_transport.c@stripped, 2007-11-14 19:48:07+01:00, rafal@quant.(none) +1231 -0
    Implementation of backup stream API - transport layer.

  sql/backup/stream_v1_transport.c@stripped, 2007-11-14 19:48:07+01:00, rafal@quant.(none) +0 -0

diff -Nrup a/sql/backup/CMakeLists.txt b/sql/backup/CMakeLists.txt
--- a/sql/backup/CMakeLists.txt	2007-11-06 19:32:04 +01:00
+++ b/sql/backup/CMakeLists.txt	2007-11-14 19:48:06 +01:00
@@ -29,4 +29,5 @@ SET(BACKUP_SOURCES stream.cc logger.cc s
 
 IF(NOT SOURCE_SUBLIBS)
   ADD_LIBRARY(backup ${BACKUP_SOURCES})
+  ADD_LIBRARY(backupstream stream_v1.c stream_v1_transport.c)
 ENDIF(NOT SOURCE_SUBLIBS)
diff -Nrup a/sql/backup/Makefile.am b/sql/backup/Makefile.am
--- a/sql/backup/Makefile.am	2007-11-06 19:32:05 +01:00
+++ b/sql/backup/Makefile.am	2007-11-14 19:48:06 +01:00
@@ -15,7 +15,9 @@
 
 ## Process this file with automake to create Makefile.in
 
-noinst_LTLIBRARIES = libbackup.la
+# TODO: check how to correctly specify libraries (pkglib_LIBRARIES?)
+
+noinst_LTLIBRARIES = libbackup.la libbackupstream.la
 
 INCLUDES = \
 	-I$(top_builddir)/include \
@@ -35,6 +37,10 @@ libbackup_la_SOURCES = \
       be_snapshot.cc \
       buffer_iterator.cc
 
+libbackupstream_la_SOURCES= \
+  stream_v1_transport.c \
+  stream_v1.c
+
 noinst_HEADERS = \
 	api_types.h \
 	backup_engine.h \
@@ -50,7 +56,9 @@ noinst_HEADERS = \
     meta_backup.h \
       be_default.h \
       be_snapshot.h \
-      buffer_iterator.h
+      buffer_iterator.h \
+	stream_v1.h \
+	stream_v1_services.h
 
 DEFS = \
 	-DMYSQL_SERVER \
diff -Nrup a/sql/backup/stream_v1.c b/sql/backup/stream_v1.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/stream_v1.c	2007-11-14 19:48:06 +01:00
@@ -0,0 +1,1976 @@
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "stream_v1.h"
+#include "stream_v1_services.h"
+
+/**
+  @file
+
+  @brief
+  Implementation of the high-level functions for writing and reading backup
+  image using version 1 of backup stream format.
+
+  @todo handle errors when creating iterators in functions like bstream_wr_catalogue()
+  @todo use data chunk sequence numbers to detect discontinuities in backup stream.
+*/
+
+/**
+ @page streamlib Backup Stream Library
+
+ This library defines version 1 of backup stream format. It provides functions
+ for writing and reading streams using this format. The functions are declared
+ in the stream_v1.h header.
+*/
+
+/* local types */
+
+#define ASSERT(X) assert(X)
+
+typedef unsigned char bool;
+#define TRUE    1
+#define FALSE   0
+
+typedef bstream_byte byte;
+typedef bstream_blob blob;
+
+
+/*
+  Macros used to test results of functions writing/reading backup stream.
+
+  To use these macros, function should define ret variable of type int and
+  also wr_error/rd_error label to which these macros would jump in case of
+  error.
+*/
+
+#define CHECK_WR_RES(X) \
+  do{\
+   if ((ret= X) != BSTREAM_OK) goto wr_error;\
+  } while(0)
+
+#define CHECK_RD_OK(X) \
+ do{\
+   if ((ret= X) != BSTREAM_OK)\
+    { ret=BSTREAM_ERROR; goto rd_error; }\
+ } while(0)
+
+#define CHECK_RD_RES(X) \
+ do{\
+   if ((ret= X) == BSTREAM_ERROR) goto rd_error;\
+ } while(0)
+
+
+/* functions for writing basic types */
+
+int bstream_wr_byte(backup_stream*, unsigned short int);
+int bstream_wr_int2(backup_stream*, unsigned int);
+int bstream_wr_int4(backup_stream*, unsigned long int);
+int bstream_wr_num(backup_stream*, unsigned long int);
+int bstream_wr_string(backup_stream*, bstream_blob);
+int bstream_wr_time(backup_stream*, bstream_time_t*);
+
+/* low level i/o operations on backup stream (defined in stream_v1_carrier.c) */
+
+int bstream_write(backup_stream*, bstream_blob*);
+int bstream_write_part(backup_stream*, bstream_blob*, bstream_blob);
+int bstream_write_blob(backup_stream*, bstream_blob);
+int bstream_end_chunk(backup_stream*);
+int bstream_flush(backup_stream*);
+
+int bstream_read(backup_stream*, bstream_blob*);
+int bstream_read_part(backup_stream*, bstream_blob*, bstream_blob);
+int bstream_read_blob(backup_stream*, bstream_blob);
+int bstream_skip(backup_stream*, unsigned long int);
+
+
+/*************************************************************************
+ *
+ *   IMAGE PREAMBLE AND SUMMARY
+ *
+ *************************************************************************/
+
+/**
+  @page stream_format Backup Stream Format (v1)
+
+  Backup image consists of 3 main parts: preamble, table data and summary.
+  @verbatim
+
+  [backup image]= [ preamble | table data | 0x00 ! summary(*) ]
+  @endverbatim
+
+  The 0x00 byte separates table data chunks from the summary chunk. This works
+  because no table data chunk can start with 0x00.
+
+  Optionally, summary can be included in the preamble, instead of being stored
+  at the end of the image. This is indicated by flags in the header.
+  @verbatim
+
+  [preamble]= [ header | summary (*) | catalogue | meta data ]
+  @endverbatim
+*/
+
+int bstream_wr_header(backup_stream*, struct st_bstream_image_header*);
+int bstream_wr_catalogue(backup_stream*, struct st_bstream_image_header*);
+int bstream_wr_meta_data(backup_stream *, struct st_bstream_image_header*);
+
+/** Write backup image preamble */
+int bstream_wr_preamble(backup_stream *s, struct st_bstream_image_header *hdr)
+{
+  int ret= BSTREAM_OK;
+
+  CHECK_WR_RES(bstream_wr_header(s,hdr));
+  CHECK_WR_RES(bstream_end_chunk(s));
+
+  CHECK_WR_RES(bstream_wr_catalogue(s,hdr));
+  CHECK_WR_RES(bstream_end_chunk(s));
+
+  CHECK_WR_RES(bstream_wr_meta_data(s,hdr));
+  CHECK_WR_RES(bstream_end_chunk(s));
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  Read backup image preamble creating all items stored in it
+
+  @retval BSTREAM_ERROR  Error while reading preamble
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOS    Preamble has been read and there are no more chunks in
+                         the stream.
+*/
+int bstream_rd_preamble(backup_stream *s, struct st_bstream_image_header *hdr)
+{
+  int ret= BSTREAM_OK;
+
+  CHECK_RD_RES(bstream_rd_header(s,hdr));
+  if (ret == BSTREAM_EOS)
+    return BSTREAM_ERROR;
+  CHECK_RD_OK(bstream_next_chunk(s));
+
+  if (hdr->flags & BSTREAM_FLAG_INLINE_SUMMARY)
+  {
+    CHECK_RD_RES(bstream_rd_summary(s,hdr));
+    if (ret == BSTREAM_EOS)
+      return BSTREAM_ERROR;
+    CHECK_RD_OK(bstream_next_chunk(s));
+  }
+
+  CHECK_RD_RES(bstream_rd_catalogue(s,hdr));
+  if (ret == BSTREAM_EOS)
+    return BSTREAM_ERROR;
+  CHECK_RD_OK(bstream_next_chunk(s));
+
+  CHECK_RD_RES(bstream_rd_meta_data(s,hdr));
+  CHECK_RD_RES(bstream_next_chunk(s));
+
+  rd_error:
+
+  return ret;
+}
+
+/**
+  @page stream_format
+
+  @section summary Summary section
+
+  @verbatim
+
+  [summary]= [ vp time ! end time ! binlog pos ! binlog group pos ]
+  @endverbatim
+
+  Summary starts with 0x00 byte to distinguish it from table data chunks which
+  never start with that value.
+  @verbatim
+
+  [binlog pos]= [ pos:4 ! binlog file name ]
+
+  [binlog group pos] uses the same format as [binlog pos].
+  @endverbatim
+*/
+
+/** Save binlog position. */
+int bstream_wr_binlog_pos(backup_stream *s, struct st_bstream_binlog_pos pos)
+{
+  blob name= {pos.file, NULL};
+  int ret= BSTREAM_OK;
+
+  name.end= name.begin + (pos.file ? strlen(pos.file) : 0);
+  CHECK_WR_RES(bstream_wr_int4(s,pos.pos));
+  CHECK_WR_RES(bstream_wr_string(s,name));
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  Read binlog position.
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+*/
+int bstream_rd_binlog_pos(backup_stream *s, struct st_bstream_binlog_pos *pos)
+{
+  blob name= {NULL, NULL};
+  int ret= BSTREAM_OK;
+
+  CHECK_RD_OK(bstream_rd_int4(s,&pos->pos));
+  CHECK_RD_RES(bstream_rd_string(s,&name));
+
+  if (ret != BSTREAM_ERROR)
+    pos->file= name.begin;
+
+  rd_error:
+
+  return ret;
+}
+
+/**
+  Write backup image summary.
+
+  This function assumes that all members of the header (such as @c vp_time) are
+  already filled. It also stores the 0x00 byte separating summary from the
+  preceding table data chunks.
+*/
+int bstream_wr_summary(backup_stream *s, struct st_bstream_image_header *hdr)
+{
+  int ret= BSTREAM_OK;
+
+  CHECK_WR_RES(bstream_wr_byte(s,0x00));
+  CHECK_WR_RES(bstream_wr_time(s,&hdr->vp_time));
+  CHECK_WR_RES(bstream_wr_time(s,&hdr->end_time));
+  CHECK_WR_RES(bstream_wr_binlog_pos(s,hdr->binlog_pos));
+  CHECK_WR_RES(bstream_wr_binlog_pos(s,hdr->binlog_group));
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  Read backup image summary
+
+  The information stored in the summary section is read and stored in the image
+  header structure.
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+*/
+int bstream_rd_summary(backup_stream *s, struct st_bstream_image_header *hdr)
+{
+  int ret= BSTREAM_OK;
+
+  CHECK_RD_OK(bstream_rd_time(s,&hdr->vp_time));
+  CHECK_RD_OK(bstream_rd_time(s,&hdr->end_time));
+  CHECK_RD_OK(bstream_rd_binlog_pos(s,&hdr->binlog_pos));
+  CHECK_RD_RES(bstream_rd_binlog_pos(s,&hdr->binlog_group));
+
+  rd_error:
+
+  return ret;
+}
+
+
+/*************************************************************************
+ *
+ *   IMAGE HEADER
+ *
+ *************************************************************************/
+
+int bstream_wr_snapshot_info(backup_stream*, struct st_bstream_snapshot_info*);
+int bstream_rd_image_info(backup_stream*, struct st_bstream_snapshot_info*);
+
+/**
+  @page stream_format
+
+  @section header Header
+
+  @verbatim
+
+  [header]= [ flags:2 ! creation time ! #of snapshots:1 ! server version !
+              extra data | snapshot descriptions ]
+  @endverbatim
+
+  [snapshot descriptions] contains descriptions of the table data snapshots
+  present in the image. Each description is stored in a separate chunk
+  (number of snapshots is given in the header).
+  @verbatim
+
+  [snapshot descriptions]= [ snapshot description | ... | snapshot description ]
+  @endverbatim
+*/
+
+/** Write header of backup image */
+int bstream_wr_header(backup_stream *s, struct st_bstream_image_header *hdr)
+{
+  int ret= BSTREAM_OK;
+  unsigned int i;
+
+  CHECK_WR_RES(bstream_wr_int2(s, hdr->flags));
+  CHECK_WR_RES(bstream_wr_time(s, &hdr->start_time));
+  CHECK_WR_RES(bstream_wr_byte(s, hdr->snap_count));
+
+  CHECK_WR_RES(bstream_wr_byte(s, hdr->server_version.major));
+  CHECK_WR_RES(bstream_wr_byte(s, hdr->server_version.minor));
+  CHECK_WR_RES(bstream_wr_byte(s, hdr->server_version.release));
+  CHECK_WR_RES(bstream_wr_string(s, hdr->server_version.extra));
+
+  for (i=0; i < hdr->snap_count; ++i)
+  {
+    CHECK_WR_RES(bstream_end_chunk(s));
+    CHECK_WR_RES(bstream_wr_snapshot_info(s, &hdr->snapshot[i]));
+  }
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  Read backup image header and fill @c st_bstream_image_header structure
+  with the data read.
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+*/
+int bstream_rd_header(backup_stream *s, struct st_bstream_image_header *hdr)
+{
+  int ret= BSTREAM_OK;
+  unsigned int i;
+
+  CHECK_RD_OK(bstream_rd_int2(s, &hdr->flags));
+  CHECK_RD_OK(bstream_rd_time(s, &hdr->start_time));
+  CHECK_RD_OK(bstream_rd_byte(s, &hdr->snap_count));
+
+  CHECK_RD_OK(bstream_rd_byte(s, &hdr->server_version.major));
+  CHECK_RD_OK(bstream_rd_byte(s, &hdr->server_version.minor));
+  CHECK_RD_OK(bstream_rd_byte(s, &hdr->server_version.release));
+  CHECK_RD_RES(bstream_rd_string(s, &hdr->server_version.extra));
+
+  for (i=0; i < hdr->snap_count; ++i)
+  {
+    if (ret != BSTREAM_EOC)
+      return BSTREAM_ERROR;
+
+    CHECK_RD_OK(bstream_next_chunk(s));
+    CHECK_RD_RES(bstream_rd_image_info(s, &hdr->snapshot[i]));
+  }
+
+  rd_error:
+
+  return ret;
+}
+
+/**
+  @page stream_format
+
+  @subsection snapshot Snapshot description entry
+
+  @verbatim
+
+  [snapshot description] = [ image type:1 ! format version: 2 ! global options:2 !
+                             #of tables ! backup engine info ! extra data ]
+  @endverbatim
+
+  [image type] is encoded as follows:
+
+  - 0 = snapshot created by native backup driver (BI_NATIVE),
+  - 1 = snapshot created by built-in blocking driver (BI_DEFAULT),
+  - 2 = snapshot created using created by built-in driver using consistent
+      read transaction (BI_CS).
+
+  Format of [backup engine info] depends on snapshot type. It is empty for the
+  default and CS snapshots. For native snapshots it has format
+  @verbatim
+
+  [backup engine info (native)] = [ storage engine name ! storage engine version ]
+
+  [server version] = [ major:1 ! minor:1 ! release:1 ! extra string ]
+
+  [engine version] = [ major:1 ! minor:1 ]
+  @endverbatim
+*/
+
+/** Save description of table data snapshot */
+int bstream_wr_snapshot_info(backup_stream *s, struct st_bstream_snapshot_info *info)
+{
+  int ret= BSTREAM_OK;
+
+  switch (info->type) {
+  case BI_NATIVE:  ret= bstream_wr_byte(s,0); break;
+  case BI_DEFAULT: ret= bstream_wr_byte(s,1); break;
+  case BI_CS:      ret= bstream_wr_byte(s,2); break;
+  default:         ret= bstream_wr_byte(s,3); break;
+  }
+
+  CHECK_WR_RES(bstream_wr_int2(s,info->version));
+
+  if (ret != BSTREAM_OK)
+    goto wr_error;
+
+  CHECK_WR_RES(bstream_wr_int2(s,info->options));
+  CHECK_WR_RES(bstream_wr_num(s,info->table_count));
+
+  if (info->type == BI_NATIVE )
+  {
+    CHECK_WR_RES(bstream_wr_string(s,info->engine.name));
+    CHECK_WR_RES(bstream_wr_byte(s,info->engine.major));
+    CHECK_WR_RES(bstream_wr_byte(s,info->engine.minor));
+  }
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  Read description of table data snapshot and save it in
+  @c st_bstream_snapshot_info structure.
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+
+  @note
+  This function allocates memory to store snapshot info. The caller is
+  responsible for freeing this memory.
+*/
+int bstream_rd_image_info(backup_stream *s, struct st_bstream_snapshot_info *info)
+{
+  unsigned short int type;
+  int ret= BSTREAM_OK;
+
+  bzero(info,sizeof(struct st_bstream_snapshot_info));
+
+  CHECK_RD_OK(bstream_rd_byte(s,&type));
+
+  switch (type) {
+  case 0: type= BI_NATIVE; break;
+  case 1: type= BI_DEFAULT; break;
+  case 2: type= BI_CS; break;
+  default: return BSTREAM_ERROR;
+  }
+
+  info->type= type;
+
+  CHECK_RD_OK(bstream_rd_int2(s,&info->version));
+  CHECK_RD_OK(bstream_rd_int2(s,&info->options));
+  CHECK_RD_RES(bstream_rd_num(s,&info->table_count));
+
+  if (type == BI_NATIVE )
+  {
+    if (ret != BSTREAM_OK)
+      return BSTREAM_ERROR;
+
+    CHECK_RD_OK(bstream_rd_string(s,&info->engine.name));
+    CHECK_RD_OK(bstream_rd_byte(s,&info->engine.major));
+    CHECK_RD_RES(bstream_rd_byte(s,&info->engine.minor));
+  }
+
+  rd_error:
+
+  return ret;
+}
+
+/*************************************************************************
+ *
+ *   CATALOGUE
+ *
+ *************************************************************************/
+
+/**
+  @page stream_format
+
+  @section catalogue Image catalogue
+
+  The catalogue describes what items are stored in the image. Note that it
+  doesn't contain any meta-data, only item names and other information needed to
+  identify and select them.
+  @verbatim
+
+  [catalogue]= [ charsets ! 0x00 ! users ! 0x00 ! databases |
+                 db catalogue | ... | db catalogue ]
+  @endverbatim
+
+  Catalogue starts with list of charsets where each charset is identified by its
+  name. In other places of the image, charsets can be identified by their
+  positions in this list. Number of charsets is limited to 256 so that one byte
+  is enough to identify a charset.
+  @verbatim
+
+  [charsets]= [ charset name ! ... ! charset name ]
+  @endverbatim
+
+  Two first entries in [charsets] have special meaning and should be always
+  present.
+
+  First charset is the charset used to encode all strings stored in
+  the preamble. This should be a universal charset like UTF8, capable
+  of representing any string.
+
+  Second charset in the list is the default charset of the server on which
+  image was created. It can be the same as the first charset.
+
+  The following charsets are any charsets used by the items stored in the image
+  and thus needed to restore these items.
+  @verbatim
+
+  [users]= [ user name ! ... ! user name ]
+  @endverbatim
+
+  User list contains users for which any privileges are stored in the image.
+
+  After [users] a list of all databases follows. If the list is empty, it
+  consists of a single null string. Otherwise it has format:
+  @verbatim
+
+  [databases]= [ db info ! ... ! db info ]
+
+  [db info]= [ db name ! db flags:1 ! optional extra data ]
+  [db flags]= [ has extra data:.1 ! unused:.7 ]
+  [optional extra data]= [data len:2 ! the data:(data len) ]
+  @endverbatim
+
+  [optional extra data] is present only if indicated in the flags.
+
+  If there are no databases in the image, the database list is empty and there
+  are no database catalogues.
+  @verbatim
+
+  [catalogue (no databases)] = [ charsets ! 0x00 ! users ! 0x00 ]
+  @endverbatim
+*/
+
+#define BSTREAM_FLAG_HAS_EXTRA_DATA   0x80
+
+int bstream_wr_db_catalogue(backup_stream*, struct st_bstream_image_header*,
+                            struct st_bstream_db_info*);
+int bstream_rd_db_catalogue(backup_stream*, struct st_bstream_image_header*,
+                            struct st_bstream_db_info*);
+
+/**
+  Save catalogue of backup image.
+
+  The contents of the image is read from the @c cat object using iterators
+  and @c bcat_*() functions defined by the program using this library.
+
+  @see @c bcat_iterator_get(), @c bcat_iterator_next(), @c bcat_iterator_free()
+*/
+int bstream_wr_catalogue(backup_stream *s, struct st_bstream_image_header *cat)
+{
+  void *it;
+  blob *name;
+  struct st_bstream_db_info *db_info;
+  int ret;
+
+  /* charset list */
+
+  it= bcat_iterator_get(cat,BSTREAM_IT_CHARSET);
+
+  while ((name= (blob*) bcat_iterator_next(cat,it)))
+  {
+    CHECK_WR_RES(bstream_wr_string(s,*name));
+  }
+
+  CHECK_WR_RES(bstream_wr_byte(s,0x00));
+
+  bcat_iterator_free(cat,it);
+
+  /* list of users */
+
+  it= bcat_iterator_get(cat,BSTREAM_IT_USER);
+
+  while ((name= (blob*) bcat_iterator_next(cat,it)))
+  {
+    CHECK_WR_RES(bstream_wr_string(s,*name));
+  }
+
+  CHECK_WR_RES(bstream_wr_byte(s,0x00));
+
+  bcat_iterator_free(cat,it);
+
+  /* list of databases */
+
+  it= bcat_iterator_get(cat,BSTREAM_IT_DB);
+
+  while ((db_info= (struct st_bstream_db_info*) bcat_iterator_next(cat,it)))
+  {
+    CHECK_WR_RES(bstream_wr_string(s,db_info->base.name));
+    CHECK_WR_RES(bstream_wr_byte(s,0x00)); // flags
+  }
+
+  bcat_iterator_free(cat,it);
+
+  /* db catalogues */
+
+  it= bcat_iterator_get(cat,BSTREAM_IT_DB);
+
+  while ((db_info= (struct st_bstream_db_info*) bcat_iterator_next(cat,it)))
+  {
+    CHECK_WR_RES(bstream_end_chunk(s));
+    CHECK_WR_RES(bstream_wr_db_catalogue(s,cat,db_info));
+  }
+
+  bcat_iterator_free(cat,it);
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  Read backup image catalogue.
+
+  The @c cat object is populated with items read from the stream using
+  @c bcat_add_item() function defined by the program using this library.
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+
+  @see @c bcat_add_item()
+*/
+int bstream_rd_catalogue(backup_stream *s, struct st_bstream_image_header *cat)
+{
+  int ret= BSTREAM_OK;
+  unsigned short int flags;
+  unsigned int len;
+  void *iter;
+  struct st_bstream_item_info item;
+  struct st_bstream_db_info *db_info;
+
+  bcat_reset(cat);
+
+  /* charset list */
+
+  item.type= BSTREAM_IT_CHARSET;
+  item.pos= 0;
+
+  do{
+
+    CHECK_RD_OK(bstream_rd_string(s,&item.name));
+
+    /* empty string signals end of the list */
+    if (item.name.begin == NULL)
+      break;
+
+    bcat_add_item(cat,&item);
+    item.pos++;
+
+  } while (ret == BSTREAM_OK);
+
+  /* list of users */
+
+  item.type= BSTREAM_IT_USER;
+
+  do{
+
+    CHECK_RD_RES(bstream_rd_string(s,&item.name));
+
+    /* empty string signals end of the list */
+    if (item.name.begin == NULL)
+      break;
+
+    bcat_add_item(cat,&item);
+    item.pos++;
+
+  } while (ret == BSTREAM_OK);
+
+  /*
+    If ret != BSTREAM_OK here, we hit end of chunk or stream. This means
+    that there are no databases in the image and we are done reading the
+    catalogue.
+   */
+  if (ret != BSTREAM_OK)
+    return ret;
+
+  /* list of databases */
+
+  item.type= BSTREAM_IT_DB;
+  item.pos= 0;
+
+  do {
+
+    CHECK_RD_OK(bstream_rd_string(s,&item.name));
+
+    bcat_add_item(cat,&item);
+    item.pos++;
+
+    CHECK_RD_RES(bstream_rd_byte(s,&flags));
+
+    if (flags & BSTREAM_FLAG_HAS_EXTRA_DATA)
+    {
+      if (ret != BSTREAM_OK)
+        return BSTREAM_ERROR;
+
+      CHECK_RD_OK(bstream_rd_int2(s,&len));
+      CHECK_RD_RES(bstream_skip(s,len));
+    }
+
+  } while (ret == BSTREAM_OK);
+
+
+  /* db catalogues */
+
+  iter= bcat_iterator_get(cat,BSTREAM_IT_DB);
+
+  while ((db_info= (struct st_bstream_db_info*) bcat_iterator_next(cat,iter)))
+  {
+    if (ret != BSTREAM_EOC)
+      return BSTREAM_ERROR;
+
+    CHECK_RD_OK(bstream_next_chunk(s));
+    CHECK_RD_RES(bstream_rd_db_catalogue(s,cat,db_info));
+  }
+
+  bcat_iterator_free(cat,iter);
+  bcat_close(cat);
+
+  rd_error:
+
+  return ret;
+}
+
+/**
+  @page stream_format
+
+  Encoding of item types used in a backup image.
+
+  - 1 = character set,
+  - 2 = user,
+  - 3 = privilege,
+  - 4 = database,
+  - 5 = table,
+  - 6 = view.
+
+  Value 0 doesn't encode a valid item type and is used for other purposes.
+ */
+
+/**
+  Save item type.
+
+  @retval BSTREAM_OK   type was saved successfully
+  @retval BSTREAM_ERROR error writing or attempt to save unknown type.
+*/
+int bstream_wr_item_type(backup_stream *s, enum enum_bstream_item_type type)
+{
+  switch (type) {
+  case BSTREAM_IT_CHARSET:   return bstream_wr_int2(s,1);
+  case BSTREAM_IT_USER:      return bstream_wr_int2(s,2);
+  case BSTREAM_IT_PRIVILEGE: return bstream_wr_int2(s,3);
+  case BSTREAM_IT_DB:        return bstream_wr_int2(s,4);
+  case BSTREAM_IT_TABLE:     return bstream_wr_int2(s,5);
+  case BSTREAM_IT_VIEW:      return bstream_wr_int2(s,6);
+  case BSTREAM_IT_LAST:      return bstream_wr_int2(s,0);
+  default: return BSTREAM_ERROR;
+  }
+}
+
+/**
+  Read item type.
+
+  @retval BSTREAM_ERROR  Error while reading or non-recognized type found.
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+*/
+int bstream_rd_item_type(backup_stream *s, enum enum_bstream_item_type *type)
+{
+  int ret;
+  unsigned int x;
+
+  ret= bstream_rd_int2(s,&x);
+
+  if (ret == BSTREAM_ERROR)
+    return BSTREAM_ERROR;
+
+  switch (x) {
+  case 0: *type= BSTREAM_IT_LAST; break;
+  case 1: *type= BSTREAM_IT_CHARSET; break;
+  case 2: *type= BSTREAM_IT_USER; break;
+  case 3: *type= BSTREAM_IT_PRIVILEGE; break;
+  case 4: *type= BSTREAM_IT_DB; break;
+  case 5: *type= BSTREAM_IT_TABLE; break;
+  case 6: *type= BSTREAM_IT_VIEW; break;
+  default: return BSTREAM_ERROR;
+  }
+
+  return ret;
+}
+
+/*
+  @page stream_format
+
+  @subsection db_catalogue Database catalogue
+
+  Database catalogue lists all tables and other per-db items belonging to that
+  database.
+  @verbatim
+
+  [db catalogue]= [ db-item info ! ... ! db-item info ]
+  @endverbatim
+
+  Each entry in the catalogue describes a single item, which can be a table or
+  of other kind.
+  @verbatim
+
+  [db-item info]= [ type:2 ! name ! optional item data ]
+
+  [optional item data] is used only for tables:
+
+  [optional item data (table)]= [ flags:1 ! snapshot no:1 ! optional extra data ]
+  @endverbatim
+
+  [snapshot no] tells which snapshot contains tables data.
+
+  Presence of extra data is indicated by a flag.
+  @verbatim
+
+  [flags]= [ has_extra_data:.1 ! unused:.7 ]
+
+  [optional extra data]= [ data_len:1 ! extra data:(data_len) ]
+  @endverbatim
+
+  If database is empty, it stores single 0x00 byte.
+  @verbatim
+
+  [db catalogue (empty)] = [ 0x00 ]
+  @endverbatim
+*/
+
+
+/** Save catalogue of items belonging to given database. */
+int bstream_wr_db_catalogue(backup_stream *s, struct st_bstream_image_header *cat,
+                            struct st_bstream_db_info *db_info)
+{
+  void *iter;
+  struct st_bstream_dbitem_info *item;
+  int ret;
+  bool catalogue_empty= TRUE;
+
+  iter= bcat_db_iterator_get(cat, db_info);
+
+  while ((item= bcat_db_iterator_next(cat, db_info, iter)))
+  {
+    catalogue_empty= FALSE;
+
+    CHECK_WR_RES(bstream_wr_item_type(s,item->base.type));
+    CHECK_WR_RES(bstream_wr_string(s, item->base.name));
+
+    if (item->base.type == BSTREAM_IT_TABLE)
+    {
+      CHECK_WR_RES(bstream_wr_byte(s,0x00)); /* flags: we don't use extra data */
+      CHECK_WR_RES(bstream_wr_byte(s,((struct st_bstream_table_info*)item)->snap_no));
+    }
+  }
+
+  bcat_db_iterator_free(cat, db_info, iter);
+
+  if (catalogue_empty)
+    CHECK_WR_RES(bstream_wr_byte(s,0x00));
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  Read catalogue of given database.
+
+  Object @c cat is populated with the items read using @c bcat_add_item()
+  function defined by the program using this library.
+
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+
+  @see @c bcat_add_item()
+*/
+int bstream_rd_db_catalogue(backup_stream *s, struct st_bstream_image_header *cat,
+                            struct st_bstream_db_info *db_info)
+{
+  unsigned short int flags;
+  struct st_bstream_table_info ti;
+  int ret;
+
+  bzero(&ti,sizeof(ti));
+  ti.base.db= db_info;
+
+  /* we read the first byte to see if the catalogue is empty */
+
+  CHECK_RD_RES(bstream_rd_item_type(s,&ti.base.base.type));
+
+  if (ti.base.base.type == BSTREAM_IT_LAST)
+    return ret;
+
+  /* we have some entries in the catalogue, we read them in the following loop */
+
+  do {
+
+    CHECK_RD_RES(bstream_rd_string(s,&ti.base.base.name));
+
+    if (ti.base.base.type == BSTREAM_IT_TABLE)
+    {
+      if (ret != BSTREAM_OK)
+        return BSTREAM_ERROR;
+
+      CHECK_RD_OK(bstream_rd_byte(s,&flags)); /* flags are ignored currently */
+      CHECK_RD_RES(bstream_rd_byte(s,&ti.snap_no));
+    }
+
+    if (bcat_add_item(cat, &ti.base.base) != BSTREAM_OK)
+      return BSTREAM_ERROR;
+
+    /* read type of next item, if we haven't hit end of chunk/stream */
+
+    if (ret == BSTREAM_OK)
+      CHECK_RD_OK(bstream_rd_item_type(s,&ti.base.base.type));
+
+  } while (ret == BSTREAM_OK);
+
+  rd_error:
+
+  return ret;
+}
+
+/*************************************************************************
+ *
+ *   META DATA
+ *
+ *************************************************************************/
+
+/**
+  @page stream_format
+
+  @section meta_data Meta data section
+
+  Meta data section contains meta-data for items which need to be created
+  when restoring data. It is divided into three main parts, storing meta data
+  for global items, tables and other items (per-db and per-table).
+  @verbatim
+
+  [meta data]= [ global items | tables | other items ]
+  @endverbatim
+
+  [Global items] include all databases. [Tables] section contains all tables
+  which are grouped on per-database basis (this is for easier skipping of tables
+  upon selective restore).
+  @verbatim
+
+  [tables] = [ tables from db1 | ... | tables from dbN ]
+  @endverbatim
+
+  [Other items] has two parts for all per-database items (except tables) and
+  all per-table items.
+  @verbatim
+
+  [other items]= [ per-db items ! 0x00 ! per-table items ]
+  @endverbatim
+
+  The per-database items other than tables can not be grouped by database
+  because of possible inter-database dependenciens. This is why they are stored
+  in a separate section.
+
+  If there are no databases in the image, [meta data] consists of [global items]
+  only.
+  @verbatim
+
+  [meta data (no databases)]= [ global items ]
+  @endverbatim
+
+  Meta data item lists can be empty or consist of several item entries. Empty
+  item list consist of a single byte 0x00 which can not start any valid
+  [item entry].
+  @verbatim
+
+  [item list] = [ item entry ! ... ! item entry ]
+  [item list (empty)]= [ 0x00 ]
+  @endverbatim
+*/
+
+/** different formats in which item positions are stored */
+enum enum_bstream_meta_item_kind {
+  GLOBAL_ITEM,   /**< only item position is stored */
+  TABLE_ITEM,    /**< only table position is stored (database is implicit) */
+  PER_DB_ITEM,   /**< item position followed by it's database position */
+  /**
+    Item position followed by it's table's database position followed by the
+    table's position inside that database
+  */
+  PER_TABLE_ITEM
+};
+
+int bstream_wr_meta_item(backup_stream*, enum enum_bstream_meta_item_kind,
+                         unsigned short int, struct st_bstream_item_info*);
+
+int bstream_rd_meta_item(backup_stream*, struct st_bstream_image_header*,
+                         struct st_bstream_db_info*,
+                         enum enum_bstream_meta_item_kind, unsigned short int*,
+                         struct st_bstream_item_info**);
+
+int bstream_wr_item_def(backup_stream*, struct st_bstream_image_header*,
+                        enum enum_bstream_meta_item_kind,
+                        struct st_bstream_item_info*);
+
+int read_and_create_items(backup_stream *s, struct st_bstream_image_header *cat,
+                          struct st_bstream_db_info *db,
+                          enum enum_bstream_meta_item_kind kind);
+
+/** Write meta-data section of a backup image */
+int bstream_wr_meta_data(backup_stream *s, struct st_bstream_image_header *cat)
+{
+  void *iter, *titer;
+  struct st_bstream_item_info *item;
+  struct st_bstream_db_info   *db_info;
+  int ret= BSTREAM_OK;
+  bool item_written= FALSE;
+  bool has_db= FALSE;
+
+  /* global items (this includes databases) */
+
+  iter= bcat_iterator_get(cat,BSTREAM_IT_GLOBAL);
+
+  while ((item= bcat_iterator_next(cat,iter)))
+  {
+    item_written= TRUE;
+    CHECK_WR_RES(bstream_wr_item_def(s,cat,GLOBAL_ITEM,item));
+  }
+
+  /* mark empty list if no items were written */
+  if (!item_written)
+    bstream_wr_byte(s,0x00);
+
+  bcat_iterator_free(cat,iter);
+
+  /* tables */
+
+  iter= bcat_iterator_get(cat,BSTREAM_IT_DB);
+
+  while ((db_info= (struct st_bstream_db_info*)bcat_iterator_next(cat,iter)))
+  {
+    has_db= TRUE;
+    CHECK_WR_RES(bstream_end_chunk(s));
+
+    titer= bcat_db_iterator_get(cat,db_info);
+
+    item_written= FALSE;
+    while ((item= (struct st_bstream_item_info*)
+                  bcat_db_iterator_next(cat,db_info,titer)))
+    {
+      if (item->type != BSTREAM_IT_TABLE)
+        continue;
+
+      CHECK_WR_RES(bstream_wr_item_def(s,cat,TABLE_ITEM,item));
+      item_written= TRUE;
+    }
+
+    // mark empty list
+    if (!item_written)
+      bstream_wr_byte(s,0x00);
+
+    bcat_db_iterator_free(cat,db_info,titer);
+  }
+
+  bcat_iterator_free(cat,iter);
+
+  /* if we found no databases in the catalogue, we are done */
+  if (!has_db)
+    return BSTREAM_OK;
+
+  /* other per-db items */
+
+  CHECK_WR_RES(bstream_end_chunk(s));
+
+  iter= bcat_iterator_get(cat,BSTREAM_IT_PERDB);
+
+  while ((item= bcat_iterator_next(cat,iter)))
+  {
+    if (item->type == BSTREAM_IT_TABLE)
+      continue;
+
+    CHECK_WR_RES(bstream_wr_item_def(s,cat,PER_DB_ITEM,item));
+  }
+
+  bcat_iterator_free(cat,iter);
+
+  /* per-table items */
+
+  CHECK_WR_RES(bstream_wr_byte(s,0x00));
+
+  iter= bcat_iterator_get(cat,BSTREAM_IT_PERTABLE);
+
+  while ((item= bcat_iterator_next(cat,iter)))
+  {
+    if (item->type == BSTREAM_IT_TABLE)
+      continue;
+
+    CHECK_WR_RES(bstream_wr_item_def(s,cat,PER_TABLE_ITEM,item));
+  }
+
+  bcat_iterator_free(cat,iter);
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  Read backup image meta-data section.
+
+  All items read are created using @c bstream_create_item() function.
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+*/
+int bstream_rd_meta_data(backup_stream *s, struct st_bstream_image_header *cat)
+{
+  void *iter;
+  struct st_bstream_db_info *db_info;
+  int ret=BSTREAM_OK;
+  bool has_db= FALSE;
+
+  /* global items */
+
+  CHECK_RD_RES(read_and_create_items(s,cat,NULL,GLOBAL_ITEM));
+
+  /* tables */
+
+  iter= bcat_iterator_get(cat,BSTREAM_IT_DB);
+
+  while ((db_info= (struct st_bstream_db_info*)bcat_iterator_next(cat,iter)))
+  {
+    has_db= TRUE;
+
+    if (ret != BSTREAM_EOC)
+      return BSTREAM_ERROR;
+
+    CHECK_RD_OK(bstream_next_chunk(s));
+    CHECK_RD_RES(read_and_create_items(s,cat,db_info,TABLE_ITEM));
+  }
+
+  bcat_iterator_free(cat,iter);
+
+  /* if image has no databases, there is nothing more to read */
+
+  if (!has_db)
+    return ret;
+
+  /* other per-db item */
+
+  if (ret != BSTREAM_EOC)
+    return BSTREAM_ERROR;
+
+  CHECK_RD_OK(bstream_next_chunk(s));
+  CHECK_RD_RES(read_and_create_items(s,cat,NULL,PER_DB_ITEM));
+
+  /*
+    If we hit end of chunk/stream, there is nothing more to read
+    (no per-table items)
+  */
+
+  if (ret != BSTREAM_OK)
+    return ret;
+
+  /* per-table items */
+
+  CHECK_RD_RES(read_and_create_items(s,cat,NULL,PER_TABLE_ITEM));
+
+  rd_error:
+
+  return ret;
+}
+
+
+/**
+  @page stream_format
+
+  @subsection item_entry Single item entry
+
+  If list starts with byte different than 0x00, then it is a sequence of
+  meta data item entries, each having the following format:
+  @verbatim
+
+  [item entry]= [ type:2 ! flags:1 ! position in the catalogue !
+                  optional extra data ! optional CREATE statement ]
+  @endverbatim
+
+  Item meta-data contains a CREATE statement or other data in unspecified format
+  or both. [flags] inform about which meta-data elements are present in the
+  entry.
+  @verbatim
+
+  [flags]= [ has_extra_data:.1 ! has_create_stmt:.1 ! unused:.6 ]
+  @endverbatim
+
+  The position in the catalogue is represented by 1 to 3 numbers, depending on
+  in which part of catalogue the entry lies.
+  @verbatim
+
+  [item position (global)]= [db no]
+  [item position (table)]= [pos in db item list]
+  [item position (other per-db item)]= [ pos in db item list ! db no ]
+  [item position (per-table item)] = [ pos in table's item list ! db no ! table pos ]
+  @endverbatim
+
+  Note that for tables, the database to which it belongs is implicit, as tables
+  are grouped by database.
+  @verbatim
+
+  [optional extra data]= [ data_len:2 ! extra data:(data_len) ]
+  @endverbatim
+*/
+
+#define BSTREAM_FLAG_HAS_CREATE_STMT 0x40
+
+/**
+  Write entry describing single item but without CREATE statement or
+  other meta data.
+*/
+int bstream_wr_meta_item(backup_stream *s,
+                    enum enum_bstream_meta_item_kind kind,
+                    unsigned short int flags,
+                    struct st_bstream_item_info *item)
+{
+  int ret= BSTREAM_OK;
+
+  /* save type and flags */
+
+  CHECK_WR_RES(bstream_wr_item_type(s,item->type));
+  CHECK_WR_RES(bstream_wr_byte(s,flags));
+
+  /* save item's position in the catalogue */
+
+  CHECK_WR_RES(bstream_wr_num(s,item->pos));
+
+  if (kind == PER_TABLE_ITEM || kind == PER_DB_ITEM)
+    CHECK_WR_RES(bstream_wr_num(s,((struct st_bstream_dbitem_info*)item)->db->base.pos));
+
+  if (kind == PER_TABLE_ITEM)
+    CHECK_WR_RES(bstream_wr_num(s,
+                   ((struct st_bstream_titem_info*)item)->table->base.base.pos));
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  Read initial part of item entry and locate that item in the catalogue.
+
+  Pointer to an appropriate structure describing the located item is stored in
+  @c (*item). This description is not persistent - next call to this function
+  can overwrite it with description of another item.
+
+  @param cat  the catalogue object where items are located
+  @param db   default database for per-db items for which database coordinates
+              are not stored in the entry (i.e., for tables)
+  @param kind  format in which item coordinates are stored
+  @param flags the flags saved in the entry are stored in that location
+  @param item  pointer to a structure describing item found is stored here.
+
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+
+  If function returns BSTREAM_OK and @c (*item) is set to NULL, it means that we
+  are looking at an empty item list.
+*/
+int bstream_rd_meta_item(backup_stream *s,
+                         struct st_bstream_image_header *cat,
+                         struct st_bstream_db_info *db,
+                         enum enum_bstream_meta_item_kind kind,
+                         unsigned short int *flags,
+                         struct st_bstream_item_info **item)
+{
+  enum enum_bstream_item_type type;
+  unsigned long int pos, pos1;
+  struct st_bstream_table_info *table= NULL;
+
+  static union
+  {
+    struct st_bstream_dbitem_info per_db;
+    struct st_bstream_titem_info  per_table;
+  } privilege;
+
+  int ret= BSTREAM_OK;
+
+  ASSERT( db != NULL || kind != TABLE_ITEM );
+
+  CHECK_RD_RES(bstream_rd_item_type(s,&type));
+
+  /* type == BSTREAM_IT_LAST means that we hit a no-item marker (0x00) */
+  if (type == BSTREAM_IT_LAST)
+  {
+    *item= NULL;
+    return ret;
+  }
+
+  if (ret != BSTREAM_OK)
+    return BSTREAM_ERROR;
+
+  ASSERT(item);
+
+  CHECK_RD_OK(bstream_rd_byte(s,flags));
+
+  /* read item's position */
+
+  CHECK_RD_RES(bstream_rd_num(s,&pos));
+
+  /* read db pos if present */
+
+  if (kind == PER_TABLE_ITEM || (kind == PER_DB_ITEM && db == NULL))
+  {
+    if (ret != BSTREAM_OK)
+      return BSTREAM_ERROR;
+
+    CHECK_RD_RES(bstream_rd_num(s,&pos1));
+
+    db= (struct st_bstream_db_info*)bcat_get_item(cat,pos1);
+
+    ASSERT(db != NULL);
+    ASSERT(db->base.type == BSTREAM_IT_DB);
+  }
+
+  /* read table pos if present */
+
+  if (kind == PER_TABLE_ITEM)
+  {
+    if (ret != BSTREAM_OK)
+      return BSTREAM_ERROR;
+
+    ASSERT(db != NULL);
+
+    CHECK_RD_RES(bstream_rd_num(s,&pos1));
+
+    table= (struct st_bstream_table_info*)bcat_get_db_item(cat,db,pos1);
+
+    ASSERT(table != NULL);
+    ASSERT(table->base.base.type == BSTREAM_IT_TABLE);
+  }
+
+  /*
+    special case: privileges are not stored in the catalogue and thus we
+    store privilege description in a static structure and return pointer
+    to it. Warning: not thread safe!
+  */
+  if (type == BSTREAM_IT_PRIVILEGE)
+  {
+    if (table)
+    {
+      *item= &privilege.per_table.base;
+      privilege.per_table.table= table;
+    }
+    else
+    {
+      *item= &privilege.per_db.base;
+      privilege.per_db.db= db;
+    }
+
+    (*item)->type= BSTREAM_IT_PRIVILEGE;
+    (*item)->pos= pos;
+    return ret;
+  }
+
+  /* locate item in the catalogue */
+
+  if (table)
+    *item= (struct st_bstream_item_info*)bcat_get_table_item(cat,table,pos);
+  else if (db)
+    *item= (struct st_bstream_item_info*)bcat_get_db_item(cat,db,pos);
+  else
+    *item= bcat_get_item(cat,pos);
+
+  /* signal error if we couldn't locate the item */
+  if (*item == NULL)
+   return BSTREAM_ERROR;
+
+  (*item)->type= type;
+
+  rd_error:
+
+  return ret;
+}
+
+
+/**
+  Write entry with given item's meta-data.
+
+  @param kind  determines format in which item's coordinates are saved.
+*/
+int bstream_wr_item_def(backup_stream *s,
+                   struct st_bstream_image_header *cat,
+                   enum enum_bstream_meta_item_kind kind,
+                   struct st_bstream_item_info *item)
+{
+  unsigned short int flags= 0x00;
+  blob query;
+  blob data;
+  int ret=BSTREAM_OK;
+
+  if (bcat_get_item_create_query(cat,item,&query) == BSTREAM_OK)
+    flags |= BSTREAM_FLAG_HAS_CREATE_STMT;
+
+  if (bcat_get_item_create_data(cat,item,&data) == BSTREAM_OK)
+    flags |= BSTREAM_FLAG_HAS_EXTRA_DATA;
+
+  ret= bstream_wr_meta_item(s,kind,flags,item);
+
+  /* save create query and/or create data */
+
+  if (flags & BSTREAM_FLAG_HAS_EXTRA_DATA)
+    CHECK_WR_RES(bstream_wr_string(s,data));
+
+  if (flags & BSTREAM_FLAG_HAS_CREATE_STMT)
+    CHECK_WR_RES(bstream_wr_string(s,query));
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  Read list of meta-data entries and create the corresponding items.
+
+  The entries are read until the end of chunk or 0x00 marker is hit.
+  After reading meta-data for each item (CREATE statement and/or extra meta-data)
+  the item is created using @c bstream_create_item() function which should be
+  implemented by the program using this library.
+
+  @retval BSTREAM_ERROR error while reading
+  @retval BSTREAM_EOC   either list was empty or all items were read and created
+                        successfully
+  @retval BSTREAM_EOS   all items read and created successfully and end of
+                        stream has been reached
+*/
+int read_and_create_items(backup_stream *s, struct st_bstream_image_header *cat,
+                          struct st_bstream_db_info *db,
+                          enum enum_bstream_meta_item_kind kind)
+{
+  unsigned short int flags;
+  unsigned int ret;
+  struct st_bstream_item_info *item;
+  blob query,data;
+
+  do {
+
+    CHECK_RD_RES(bstream_rd_meta_item(s,cat,db,kind,&flags,&item));
+
+    /* if 0x00 marker was read, item == NULL */
+    if (item == NULL)
+      return ret;
+
+    query.begin= query.end= NULL;
+    data.begin= data.end= NULL;
+
+    if (flags & BSTREAM_FLAG_HAS_EXTRA_DATA)
+    {
+      if (ret != BSTREAM_OK)
+        return BSTREAM_ERROR;
+      CHECK_RD_RES(bstream_rd_string(s,&data));
+    }
+
+    if (flags & BSTREAM_FLAG_HAS_CREATE_STMT)
+    {
+      if (ret != BSTREAM_OK)
+        return BSTREAM_ERROR;
+      CHECK_RD_RES(bstream_rd_string(s,&query));
+    }
+
+    bcat_create_item(cat,item,query,data);
+
+    bstream_free(query.begin);
+    bstream_free(data.begin);
+
+  } while (ret == BSTREAM_OK);
+
+  rd_error:
+
+  return ret;
+}
+
+
+/*************************************************************************
+ *
+ *   TABLE DATA
+ *
+ *************************************************************************/
+
+/**
+  @page stream_format
+
+  @section data Table data section
+
+  Format of table data section of backup image.
+  @verbatim
+
+  [table data]= [ table data chunk | ... | table data chunk ]
+
+  [table data chunk]= [ snapshot no:1 ! seq no:2 ! flags:1 ! table no ! data ]
+  @endverbatim
+
+  Data chunks of each snapshot are numbered by consecutive numbers. This can be
+  used to detect discontinuities in a backup stream. Currently only one flag
+  is used, indicating last data chunk for a given table.
+  @verbatim
+
+  [flags]= [ unused:.7 ! last data block:.1 ]
+  @endverbatim
+*/
+
+/**
+  Write chunk with data from backup driver.
+*/
+int bstream_wr_data_chunk(backup_stream *s,
+                     struct st_bstream_data_chunk *chunk)
+{
+  int ret= BSTREAM_OK;
+
+  ASSERT(chunk);
+
+  CHECK_WR_RES(bstream_wr_byte(s,chunk->snap_no + 1));
+  CHECK_WR_RES(bstream_wr_int2(s,0)); // sequence number - FIXME
+  CHECK_WR_RES(bstream_wr_byte(s,chunk->flags));
+  CHECK_WR_RES(bstream_wr_num(s,chunk->table_no));
+  CHECK_WR_RES(bstream_write_blob(s,chunk->data));
+  CHECK_WR_RES(bstream_end_chunk(s));
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  The amount by which input buffer is increased if whole data chunk can't fit
+  into it.
+*/
+#define DATA_BUF_STEP   (1024*1024)
+
+
+/**
+  Read chunk of data for restore driver.
+
+  Blob @c chunk->data describes memory area where data from the chunk is saved.
+  If this blob is non-empty when function is called, the data will be stored
+  in the given area if it fits. If chunk data doesn't fit, or the blob was
+  empty, the data is read into an internal buffer and the blob is updated to
+  indicate where to find it. In the latter case the data will be overwritten upon
+  next call to this function.
+
+  @retval BSTREAM_OK   data chunk has been read, stream moved to next chunk
+  @retval BSTREAM_EOS  data chunk has been read, no more chunks in the stream.
+  @retval BSTREAM_EOC  not a data chunk (0x00 read). Note: stream doesn't have to be
+                  at end of chunk!
+
+  Return value @c BSTREAM_EOC indicates that all table data chunks have been read.
+  The rest of the backup stream can contain image summary block.
+*/
+int bstream_rd_data_chunk(backup_stream *s,
+                     struct st_bstream_data_chunk *chunk)
+{
+  blob *buf= &s->data_buf;
+  byte *new_buf;
+  blob *envelope;
+  blob to_read;
+  unsigned long int howmuch;
+  unsigned int seq_no;
+  int ret= BSTREAM_OK;
+
+  ASSERT(chunk);
+
+  CHECK_RD_RES(bstream_rd_byte(s,&chunk->snap_no));
+
+  /*
+    Saved snapshot numbers start from 1 - if we read 0 it means that this is not
+    a table data chunk
+  */
+  if (chunk->snap_no == 0)
+    return BSTREAM_EOC;
+  else if (ret != BSTREAM_OK)
+    return BSTREAM_ERROR;
+
+  (chunk->snap_no)--;
+
+  CHECK_RD_OK(bstream_rd_int2(s,&seq_no));  /* FIxME: handle sequence numbers */
+  CHECK_RD_OK(bstream_rd_byte(s,&chunk->flags));
+  CHECK_RD_OK(bstream_rd_num(s,&chunk->table_no));
+
+  /*
+    read rest of the chunk data into provided buffer or the internal buffer
+    @c s->data_buf if there is not enough space
+  */
+
+  envelope= &chunk->data; /* envelope indicates which buffer we are using:
+                             provided or internal (initially provided) */
+  to_read= *envelope; /* how much space is available for reading bytes */
+
+  /*
+    In the following loop we call bstream_read_part() until we hit end of the chunk.
+    If there is no more space to fit the data, buffer is enlarged (reallocated)
+    and the bytes which were read before are copied into the new buffer.
+  */
+
+  while (ret == BSTREAM_OK)
+  {
+    /*
+      Read bytes until current buffer is full or end of chunk is reached.
+     */
+    while (ret == BSTREAM_OK && to_read.end > to_read.begin)
+      ret= bstream_read_part(s,&to_read,*envelope);
+
+    if (ret == BSTREAM_OK)
+    {
+      /* we have filled-up the buffer - we need to enlarge it */
+
+      howmuch= to_read.begin - envelope->begin; /* how much data have been read
+                                                   so far */
+      /*
+        If there is not enough space in the internal buffer, enlarge it.
+      */
+      if ( buf->begin + howmuch >= buf->end )
+      {
+        new_buf= bstream_alloc(howmuch + DATA_BUF_STEP);
+
+        if (!new_buf)
+          return BSTREAM_ERROR;
+
+        /* copy data from old buffer to the new one */
+        memmove(new_buf, buf->begin, buf->end - buf->begin);
+        bstream_free(buf->begin);
+
+        buf->begin= new_buf;
+        buf->end= buf->begin + howmuch + DATA_BUF_STEP;
+      }
+
+      /* if we were using the provided buffer, switch to internal one */
+      if (envelope == &chunk->data)
+      {
+        memmove(buf->begin, chunk->data.begin, howmuch);
+        envelope= buf;
+        chunk->data= *buf;
+      }
+
+      /* update to_read blob to indicate free space left */
+      to_read.begin= buf->begin + howmuch;
+      to_read.end= buf->end;
+    }
+  }
+
+  if (ret == BSTREAM_ERROR)
+    return BSTREAM_ERROR;
+
+  /* We have read all data from the chunk - record where it ends */
+
+  chunk->data.end= to_read.begin;
+
+  /* move to next chunk */
+
+  CHECK_RD_RES(bstream_next_chunk(s));
+
+  rd_error:
+
+  return ret;
+}
+
+/*********************************************************************
+ *
+ *   WRITING/READING BASIC TYPES
+ *
+ *********************************************************************/
+
+/** Write single byte to backup stream */
+int bstream_wr_byte(backup_stream *s, unsigned short int x)
+{
+  byte buf= x & 0xFF;
+  blob b= { &buf, &buf + 1 };
+  return bstream_write_part(s,&b,b);
+}
+
+/**
+  Read one byte from backup stream.
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+*/
+int bstream_rd_byte(backup_stream *s, unsigned short int *x)
+{
+  byte buf;
+  blob b= { &buf, &buf+1 };
+  int ret;
+
+  ret= bstream_read_part(s,&b,b);
+
+  if (b.begin == b.end)
+  {
+    *x= buf;
+    return ret;
+  }
+  else return BSTREAM_ERROR;
+}
+
+/**
+  Write 2 byte unsigned number. Least significant byte
+  comes first.
+*/
+int bstream_wr_int2(backup_stream *s, unsigned int x)
+{
+  byte buf[2]= { x & 0xFF, (x >> 8) & 0xFF };
+  blob b= {buf, buf+2};
+
+  return bstream_write_blob(s,b);
+}
+
+/**
+  Read 2 byte unsigned number.
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+*/
+int bstream_rd_int2(backup_stream *s, unsigned int *x)
+{
+  byte buf[2];
+  blob b= {buf, buf+2};
+  int ret;
+
+  ret= bstream_read_blob(s,b);
+  if (ret == BSTREAM_ERROR)
+    return BSTREAM_ERROR;
+
+  *x = buf[0] + (buf[1] << 8);
+
+  return ret;
+}
+
+/**
+  Write 4 byte unsigned number. Least significant bytes come first.
+*/
+int bstream_wr_int4(backup_stream *s, unsigned long int x)
+{
+  byte buf[4];
+  blob b= {buf, buf+4};
+
+  buf[0]= x & 0xFF; x >>= 8;
+  buf[1]= x & 0xFF; x >>= 8;
+  buf[2]= x & 0xFF; x >>= 8;
+  buf[3]= x & 0xFF; x >>= 8;
+
+  return bstream_write_blob(s,b);
+}
+
+/**
+  Read 4 byte unsigned number.
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+*/
+int bstream_rd_int4(backup_stream *s, unsigned long int *x)
+{
+  byte buf[4];
+  blob b= {buf, buf+4};
+  int ret;
+
+  ret= bstream_read_blob(s,b);
+  if (ret == BSTREAM_ERROR)
+    return BSTREAM_ERROR;
+
+  *x = buf[0];
+  *x += (buf[1] << 8);
+  *x += (buf[1] << 2*8);
+  *x += (buf[1] << 3*8);
+
+  return ret;
+}
+
+/**
+  Write number using variable length format.
+
+  Number is stored as a sequence of bytes, each byte storing 7 bits. The most
+  significant bit in a byte tells if there are more bytes to follow
+  (if it is set) or if current byte is the last one (if it is not set).
+  The bits are saved starting with least significant ones.
+*/
+int bstream_wr_num(backup_stream *s, unsigned long int x)
+{
+  int ret= BSTREAM_OK;
+
+  do {
+    CHECK_WR_RES(bstream_wr_byte(s, (x & 0x7F) | ( x>0x7F ? 0x80: 0)));
+    x >>= 7;
+  } while (x);
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  Read number saved using variable length format.
+
+  @retval BSTREAM_ERROR  Error while reading or number doesn't fit in unsigned
+                         long variable
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+*/
+int bstream_rd_num(backup_stream *s, unsigned long int *x)
+{
+  unsigned short int b;
+  unsigned int i=0;
+  int ret= BSTREAM_OK;
+
+  *x= 0;
+
+  do {
+
+    ret= bstream_rd_byte(s,&b);
+
+    if (ret == BSTREAM_ERROR)
+      return BSTREAM_ERROR;
+
+    *x += (b & 0x7F) << (7*(i++));
+
+  } while ((b & 0x80) && i < sizeof(unsigned long int));
+
+  return (b & 0x80) ? BSTREAM_ERROR : ret;
+}
+
+/*
+  String format.
+
+  [string]= [ size ! string bytes:(size) ]
+
+  All strings are stored using the same universal character set, which is listed
+  in image's catalogue as the first entry.
+*/
+
+/**
+  Write a string.
+*/
+int bstream_wr_string(backup_stream *s, bstream_blob str)
+{
+  int ret= BSTREAM_OK;
+
+  CHECK_WR_RES(bstream_wr_num(s, str.end - str.begin));
+  CHECK_WR_RES(bstream_write_blob(s, str));
+
+  wr_error:
+
+  return ret;
+}
+
+/**
+  Read a string.
+
+  New memory is allocated (with @c bstream_alloc() function) to accommodate the
+  string. Blob @c str is updated to point at the memory where string is stored.
+  The byte after last byte of the string is set to 0.
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+
+  @note
+  Caller of this function is responsible for freeing memory allocated to store
+  the string.
+*/
+int bstream_rd_string(backup_stream *s, bstream_blob *str)
+{
+  int ret= BSTREAM_OK;
+  unsigned long int len;
+
+  ret= bstream_rd_num(s, &len);
+
+  if (len == 0)
+  {
+    str->begin= str->end= NULL;
+    return ret;
+  }
+
+  if (ret != BSTREAM_OK)
+    return BSTREAM_ERROR;
+
+  str->begin= bstream_alloc(len+1);
+  if (!str->begin)
+    return BSTREAM_ERROR;
+  str->end= str->begin + len;
+  *str->end= '\0';
+
+  return bstream_read_blob(s, *str);
+}
+
+/*
+ Time format:
+
+ [time]= [ year and month:2 ! mday:1 ! hour:1 ! min:1 ! sec:1 ]
+
+ [year and month]= [ year:.12 ! month:.4 ]
+
+*/
+
+/** Write time entry */
+int bstream_wr_time(backup_stream *s, bstream_time_t *time)
+{
+  byte buf[6];
+  blob b= {buf, buf+6};
+
+  buf[0]= (time->year>>4) & 0xFF;
+  buf[1]= ((time->year<<4) & 0xF0) | (time->mon &0x0F);
+  buf[2]= time->mday;
+  buf[3]= time->hour;
+  buf[4]= time->min;
+  buf[5]= time->sec;
+
+  return bstream_write_blob(s,b);
+}
+
+/**
+  Read time entry
+
+  @retval BSTREAM_ERROR  Error while reading
+  @retval BSTREAM_OK     Read successful
+  @retval BSTREAM_EOC    Read successful and end of chunk has been reached
+  @retval BSTREAM_EOS    Read successful and end of stream has been reached
+*/
+int bstream_rd_time(backup_stream *s, bstream_time_t *time)
+{
+  byte buf[6];
+  blob b= {buf, buf+6};
+  int ret= BSTREAM_OK;
+
+  ret= bstream_read_blob(s,b);
+
+  if (ret != BSTREAM_OK)
+    return ret;
+
+  time->year= (buf[0]<<4) + (buf[1]>>4);
+  time->mon= buf[1] & 0x0F;
+  time->mday= buf[2];
+  time->hour= buf[3];
+  time->min= buf[4];
+  time->sec= buf[5];
+
+  return BSTREAM_OK;
+}
diff -Nrup a/sql/backup/stream_v1.h b/sql/backup/stream_v1.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/stream_v1.h	2007-11-14 19:48:06 +01:00
@@ -0,0 +1,396 @@
+#ifndef BACKUP_STREAM_V1_
+#define BACKUP_STREAM_V1_
+
+/** 
+  @file
+
+  @brief
+  Backup stream library API for version 1 of stream format.
+
+  This file declares functions and data types used to read and write backup
+  stream using version 1 of backup stream format.
+*/ 
+
+/*********************************************************************
+ * 
+ *   BASIC TYPES
+ * 
+ *********************************************************************/
+  
+typedef unsigned char bstream_byte;
+ 
+/**
+  Describes continuous area of memory.
+  
+  The @c begin member points at the first byte in the area, @c at one byte
+  after the last byte of the area. Thus, blob @c b contains exactly 
+  <code>b.end - b.begin</code> bytes and is empty if 
+  <code>b.begin == b.end</code>. A null blob is a blob @c b with 
+  <code>b.begin == NULL</code>.
+*/ 
+struct st_blob
+{
+  bstream_byte *begin; /**< first byte of the blob */
+  bstream_byte *end;   /**< one byte after the last byte of the blob */
+};
+
+typedef struct st_blob bstream_blob;
+
+/**
+  Stores time point with one second accuracy.
+  
+  This structure is similar to the POSIX <code>struct tm</code>. We define it
+  explicitly to make this header self-contained.
+*/ 
+struct st_bstream_time
+{
+  unsigned short int  sec;   /**< seconds [0,61] */
+  unsigned short int  min;   /**< minutes [0,59] */
+  unsigned short int  hour;  /**< hour [0,23] */
+  unsigned short int  mday;  /**< day of month [1,31] */
+  unsigned short int  mon;   /**< month of year [0,11] */
+  unsigned int        year;  /**< years since 1900 */
+};
+
+typedef struct st_bstream_time bstream_time_t;
+
+/**
+  Describes position of an event in MySQL server's binary log.
+  
+  The event is identified by the name of the file in which it is stored and
+  the position within that file.
+*/ 
+struct st_bstream_binlog_pos
+{
+  char              *file;  /**< binlog file storing the event */
+  unsigned long int pos;    /**< position (offset) within the file */ 
+};
+
+/* struct st_backup_stream is defined below */
+typedef struct st_backup_stream backup_stream;
+
+/** Codes returned by backup stream functions */
+enum enum_bstream_ret_codes { 
+  BSTREAM_OK=0,  /**< Success */ 
+  BSTREAM_EOC,   /**< End of chunk detected */
+  BSTREAM_EOS,   /**< End of stream detected */
+  BSTREAM_ERROR  /**< Error */
+};
+
+/*********************************************************************
+ * 
+ *   TYPES FOR IMAGE HEADER
+ * 
+ *********************************************************************/
+
+/**
+  Describes version of MySQL server.
+  
+  For example, if server has version "5.2.32-online-backup" then:
+  major   = 5
+  minor   = 2
+  release = 32
+  extra   = "5.2.32-online-backup"
+*/ 
+struct st_server_version
+{
+  unsigned short int  major;
+  unsigned short int  minor;
+  unsigned short int  release;
+  bstream_blob        extra;
+};
+
+/**
+  Describes version of storage engine.
+*/ 
+struct st_bstream_engine_info
+{
+  bstream_blob        name;   /**< name of the storage engine */
+  unsigned short int  major;  /**< version number (major) */
+  unsigned short int  minor;  /**< version number (minor) */
+};
+
+/** Types of table data snapshots. */
+enum enum_bstream_snapshot_type { 
+  BI_NATIVE,  /**< created by native backup driver of a storage engine */ 
+  BI_DEFAULT, /**< created by built-in blocking backup driver */
+  BI_CS       /**< created by built-in driver using consistent read transaction */
+};
+
+/** Describes table data snapshot. */
+struct st_bstream_snapshot_info
+{
+  unsigned int            version;      /**< snapshot format version */
+  enum enum_bstream_snapshot_type type; /**< type of the snapshot */
+  unsigned int            options;      /**< snapshot options (not used currently) */
+  unsigned long int       table_count;  /**< number of tables in the snapshot */
+  /**
+    In case of native snapshot, information about storage engine 
+    which created it 
+  */
+  struct st_bstream_engine_info  engine; 
+};
+
+/** 
+  Extension of st_bstream_snapshot_info describing snapshot created by a native
+  backup driver.
+*/ 
+struct st_native_snapshot_info
+{
+  struct st_bstream_snapshot_info  base;   /**< standard snapshot data */   
+};
+
+/** Information about backup image. */ 
+struct st_bstream_image_header
+{
+  unsigned int      version;    /**< image's format version number */
+  /** version of the server which created the image */ 
+  struct st_server_version  server_version; 
+  unsigned int      flags;      /**< image options */
+  bstream_time_t    start_time; /**< time when backup operation started */
+  bstream_time_t    end_time;   /**< time when backup operation completed */
+  bstream_time_t    vp_time;    /**< time of the image's validity point */ 
+  
+  /*
+    If server which created backup image had binary log enabled, the image 
+    contains information about the position inside the log corresponding to
+    the validity point time.
+   */ 
+   
+  /** position of the last binlog entry at the VP time */ 
+  struct st_bstream_binlog_pos  binlog_pos;
+  /** start of the last binlog event group at the VP time */ 
+  struct st_bstream_binlog_pos  binlog_group;
+  
+  /** number of table data snapshots in the image */
+  unsigned short int        snap_count;
+  /** descriptions of table data snapshots */
+  struct st_bstream_snapshot_info snapshot[256];
+};
+
+/* Flags */
+
+/**
+  Position of the summary block.
+  
+  If this flag is set, the block containing summary info (available at the end
+  of backup process) is stored in the image's preamble. Otherwise, this block
+  is appended at the end of the image.
+ */ 
+#define BSTREAM_FLAG_INLINE_SUMMARY (1U<<0)
+
+/**
+  Byte order of the server which created backup image.
+  
+  If set, informs that backup image was created on big-endian server. This might
+  be useful to detect problems, if backup engines are not endian-agnostic.
+*/ 
+#define BSTREAM_FLAG_BIG_ENDIAN     (1U<<1)
+
+/**
+  Informs if image stores binlog position.
+  
+  If this flag is not set, the @c binlog_pos and @c binlog_group entries in
+  the image should be ignored.
+ */ 
+#define BSTREAM_FLAG_BINLOG         (1U<<2)
+
+
+
+/*********************************************************************
+ * 
+ *   TYPES FOR DESCRIBING BACKUP ITEMS
+ * 
+ *********************************************************************/
+
+/** 
+  Types of items stored in a backup image.
+  
+  @note Not all of these types are supported currently.
+*/
+enum enum_bstream_item_type {
+   BSTREAM_IT_CHARSET,
+   BSTREAM_IT_USER,
+   BSTREAM_IT_PRIVILEGE,
+   BSTREAM_IT_DB,
+   BSTREAM_IT_TABLE,
+   BSTREAM_IT_VIEW,
+   BSTREAM_IT_LAST
+};
+
+/**
+  Common data about backup image item.
+*/ 
+struct st_bstream_item_info
+{
+  enum enum_bstream_item_type type;  /**< type of the item */
+  bstream_blob name;     /**< name of the item */
+  unsigned long int pos; /**< position of the item in image's catalogue */
+};
+
+/**
+  Describes database item.
+  
+  Currently no data specific to database items is used.
+*/ 
+struct st_bstream_db_info
+{
+  struct st_bstream_item_info  base;
+};
+
+
+/**
+  Describes item which sits inside a database.
+*/ 
+struct st_bstream_dbitem_info
+{
+  struct st_bstream_item_info  base; /**< data common to all items */
+  struct st_bstream_db_info    *db;  /**< database to which this item belongs */
+};
+
+/**
+  Describes a table item.
+  
+  Table is a per-database item. Additionally we store information about the
+  snapshot which contains its data.
+*/ 
+struct st_bstream_table_info
+{
+  struct st_bstream_dbitem_info  base;  /**< data common to all per-db items */
+  unsigned short int  snap_no;  /**< snapshot where table's data is stored */
+};
+
+/**
+  Describes item which sits inside a table.
+ */ 
+struct st_bstream_titem_info
+{
+  struct st_bstream_item_info  base;   /**< data common to all items */
+  struct st_bstream_table_info *table; /**< table to which this item belongs */
+};
+
+/*
+  The following constants denote additional backup item categories. Items of 
+  different types can fall into one of these categories. Thus the categories 
+  are something different than item types and therefore are not listed  
+  inside enum_bstream_item_type but defined separately.  
+*/ 
+
+#define BSTREAM_IT_GLOBAL    BSTREAM_IT_LAST
+#define BSTREAM_IT_PERDB     (BSTREAM_IT_LAST+1)
+#define BSTREAM_IT_PERTABLE  (BSTREAM_IT_LAST+2)
+
+
+/*************************************************************************
+ * 
+ *   STRUCTURE FOR WRITING/READING TABLE DATA
+ * 
+ *************************************************************************/
+
+/**
+  Describe chunk of data from backup driver or for restore driver.
+*/ 
+struct st_bstream_data_chunk
+{
+  unsigned long int  table_no;  /**< table to which this data belongs */
+  bstream_blob       data;      /**< the data */
+  unsigned short int flags;     /**< flags to be saved together with the chunk */
+  unsigned short int snap_no;   /**< which snapshot this chunk belongs to */
+};
+
+/** Indicates that given chunk is the last chunk of data for a given table */
+#define BSTREAM_FLAG_LAST_CHUNK  1
+
+
+/*************************************************************************
+ * 
+ *   FUNCTIONS FOR WRITING BACKUP IMAGE
+ * 
+ *************************************************************************/
+
+int bstream_wr_preamble(backup_stream*, struct st_bstream_image_header*);
+int bstream_wr_data_chunk(backup_stream*, 
+                          struct st_bstream_data_chunk*);
+int bstream_wr_summary(backup_stream *s, struct st_bstream_image_header *hdr);
+
+/*********************************************************************
+ * 
+ *   FUNCTIONS FOR READING BACKUP IMAGE
+ * 
+ *********************************************************************/
+
+int bstream_rd_preamble(backup_stream*, struct st_bstream_image_header*);
+int bstream_rd_data_chunk(backup_stream*, 
+                          struct st_bstream_data_chunk*);
+int bstream_rd_summary(backup_stream *s, struct st_bstream_image_header *hdr);
+
+/* parts of preamble */
+
+int bstream_rd_header(backup_stream*, struct st_bstream_image_header*);
+int bstream_rd_catalogue(backup_stream*, struct st_bstream_image_header*);
+int bstream_rd_meta_data(backup_stream *, struct st_bstream_image_header*);
+
+/* basic types */
+
+int bstream_rd_time(backup_stream*, bstream_time_t*); 
+int bstream_rd_string(backup_stream*, bstream_blob*);
+int bstream_rd_num(backup_stream*, unsigned long int*);
+int bstream_rd_int4(backup_stream*, unsigned long int*);
+int bstream_rd_int2(backup_stream*, unsigned int*);
+int bstream_rd_byte(backup_stream*, unsigned short int*); 
+
+int bstream_next_chunk(backup_stream*);
+
+
+/*********************************************************************
+ * 
+ *   DEFINITION OF BACKUP STREAM STRUCTURE
+ * 
+ *********************************************************************/
+
+/**
+  Structure defining base I/O operations.
+  
+  Abstract stream is a way of defining the final destination/source of
+  the bytes being written to/read from a backup stream. This is done by
+  storing pointers to functions performing basic I/O operations inside
+  this structure.
+ */
+struct st_abstract_stream
+{
+  int (*write)(void*, bstream_blob*, bstream_blob);
+  int (*read)(void*, bstream_blob*, bstream_blob);
+  int (*forward)(void*, unsigned long int*);
+};
+
+/** 
+  Structure describing backup stream`s input or output buffer. 
+  @see stream_v1_carrier.c  
+*/
+struct st_bstream_buffer
+{
+  bstream_byte  *begin;
+  bstream_byte  *pos;
+  bstream_byte  *header;
+  bstream_byte  *end;
+};
+
+/** 
+  Structure describing state of a backup stream. 
+*/
+struct st_backup_stream
+{
+  struct st_abstract_stream stream;
+  size_t  block_size;
+  enum { CLOSED, READING, WRITING, EOS, ERROR } state;
+  struct st_bstream_buffer buf;  
+  int  reading_last_fragment;
+  bstream_blob mem;
+  bstream_blob data_buf;
+};
+
+int bstream_open_wr(backup_stream*, unsigned long int);
+int bstream_open_rd(backup_stream*, unsigned long int);
+int bstream_close(backup_stream*);
+
+#endif /*BACKUP_V1_*/
diff -Nrup a/sql/backup/stream_v1_services.h b/sql/backup/stream_v1_services.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/stream_v1_services.h	2007-11-14 19:48:07 +01:00
@@ -0,0 +1,273 @@
+#ifndef STREAM_V1_SERVICES_H_
+#define STREAM_V1_SERVICES_H_
+
+#include <stream_v1.h>
+
+/** 
+  @file
+
+  @brief
+  Service API for backup stream library (format version 1)
+
+  This header declares functions which should be implemented by an user of
+  the backup stream library. The library will call these functions to browse
+  the catalogue of a backup image and perform other tasks.
+*/ 
+
+/*********************************************************************
+ * 
+ *   CATALOGUE SERVICES
+ * 
+ *********************************************************************/
+
+/* 
+  Functions for manipulating backup image's catalogue.
+
+  Backup stream library doesn't make any assumptions about how the catalogue
+  of a backup image is stored internally. Instead, program using backup stream
+  library must define the following functions which are used by the library
+  to populate backup catalogue with items or to enumerate and access items 
+  stored there.
+*/
+
+/**
+  Clear catalogue and prepare it for populating with items.
+*/ 
+int bcat_reset(struct st_bstream_image_header *catalogue);
+
+/**
+  Close catalogue after all items have been added to it.
+*/ 
+int bcat_close(struct st_bstream_image_header *catalogue);
+
+/** 
+  Add item to the catalogue.
+
+  The @c item->pos field should be set to indicate position of the item
+  in the catalogue.
+  
+  @note  
+  Global, per-table and per-database items can have independent address
+  spaces. Thus item belonging to a database is identified by its position inside
+  that database's item list. Similar for items belonging to tables.
+*/
+int bcat_add_item(struct st_bstream_image_header *catalogue, 
+                  struct st_bstream_item_info *item);
+
+/**
+  Get global item stored in the catalogue at given position.
+*/ 
+struct st_bstream_item_info* 
+bcat_get_item(struct st_bstream_image_header *catalogue, 
+              unsigned long int pos);
+
+/**
+  Get per-database item stored in the catalogue at given position.
+*/ 
+struct st_bstream_dbitem_info* 
+bcat_get_db_item(struct st_bstream_image_header *catalogue, 
+                 struct st_bstream_db_info *db, 
+                 unsigned long int pos);
+/**
+  Get per-table item stored in the catalogue at given position.
+*/ 
+struct st_bstream_titem_info* 
+bcat_get_table_item(struct st_bstream_image_header *catalogue, 
+                    struct st_bstream_table_info *table, 
+                    unsigned long int pos);
+
+/*
+ Iterators are used to iterate over items inside backup catalogue.
+ 
+ The internal implementation of an iterator is hidden behind this API which
+ should be implemented by the program using backup stream library. An iterator
+ is represented by a void* pointer pointing at a memory area containing its 
+ internal state.
+*/ 
+
+/**
+  Create global iterator of a given type.
+  
+  Possible iterator types.
+  
+  - BSTREAM_IT_CHARSET: all charsets
+  - BSTREAM_IT_USER:    all users
+  - BSTREAM_IT_DB:      all databases
+  
+  The following types of iterators iterate only over items for which
+  some meta-data should be saved in the image.
+  
+  - BSTREAM_IT_GLOBAL: all global items in create-dependency order
+  - BSTREAM_IT_PERDB: all per-db items except tables which are enumerated by 
+                      a table iterator (see below)
+  - BSTREAM_IT_PERTABLE: all per-table items in create-dependency orders.
+  
+  @return Pointer to the iterator or NULL in case of error.
+*/ 
+void* bcat_iterator_get(struct st_bstream_image_header *catalogue, 
+                        unsigned int type);
+
+/**
+  Return next item pointed by iterator. 
+  
+  @return NULL if there are no more items in the set.
+*/ 
+struct st_bstream_item_info* 
+bcat_iterator_next(struct st_bstream_image_header *catalogue, void *iter);
+
+/**
+  Free iterator resources.
+  
+  @note
+  The iterator can not be used after call to this function.
+*/ 
+void  bcat_iterator_free(struct st_bstream_image_header *catalogue, void *iter);
+
+/**
+  Create iterator for items belonging to a given database.
+*/ 
+void* bcat_db_iterator_get(struct st_bstream_image_header *catalogue, 
+                           struct st_bstream_db_info *db);
+
+/** Return next item from database items iterator */
+struct st_bstream_dbitem_info* 
+bcat_db_iterator_next(struct st_bstream_image_header *catalogue, 
+                      struct st_bstream_db_info *db, 
+                      void *iter);
+
+/** Free database items iterator resources */
+void  bcat_db_iterator_free(struct st_bstream_image_header *catalogue, 
+                            struct st_bstream_db_info *db, 
+                            void *iter);
+
+
+/*********************************************************************
+ * 
+ *   SAVING ITEM META DATA AND RESTORING ITEM FROM IT
+ * 
+ *********************************************************************/
+
+/**
+  Produce CREATE statement for a given item.
+  
+  Backup stream library calls that function when saving item's
+  meta-data. If function successfully produces the statement, it becomes
+  part of meta-data.
+  
+  @retval BSTREAM_OK    blob @c stmt contains the CREATE query
+  @retval BSTREAM_ERROR no CREATE statement for that item  
+*/ 
+int bcat_get_item_create_query(struct st_bstream_image_header *catalogue, 
+                               struct st_bstream_item_info *item, 
+                               bstream_blob *stmt);
+
+/**
+  Return meta-data (other than CREATE statement) for a given item.
+  
+  Backup stream library calls that function when saving item's
+  meta-data. If function returns successfully, the bytes returned become
+  part of meta-data.
+
+  @retval BSTREAM_OK    blob @c data contains the meta-data
+  @retval BSTREAM_ERROR no extra meta-data for that item  
+*/ 
+int bcat_get_item_create_data(struct st_bstream_image_header *catalogue, 
+                              struct st_bstream_item_info *item, 
+                              bstream_blob *data);
+
+/**
+  Create item from its meta-data.
+  
+  When the meta-data section of backup image is read, items are created
+  as their meta-data is read (so that there is no need to store these 
+  meta-data). Backup stream library calls this function to create an item.
+  
+  @note
+  Either @c create_stmt or @c other_meta_data or both can be empty, depending
+  on what was stored in the image.
+  
+  @retval BSTREAM_OK     item created
+  @retval BSTREAM_ERROR  error while creating item
+*/ 
+int bcat_create_item(struct st_bstream_image_header *catalogue, 
+                     struct st_bstream_item_info *item, 
+                     bstream_blob create_stmt, 
+                     bstream_blob other_meta_data);
+
+/*********************************************************************
+ * 
+ *   ABSTRACT STREAM INTERFACE
+ * 
+ *********************************************************************/
+
+/*
+  The following typedefs define signatures of functions implementing basic
+  I/O operations on the output stream. Application using backup stream stores
+  pointers to appropriate functions inside backup_stream::stream structure.
+*/ 
+
+/**
+  Function writing bytes to the underlying media.
+  
+  For specification, see @c bstream_write_part() function.
+*/ 
+typedef int (*as_write_m)(void*, bstream_blob*, bstream_blob);
+
+/**
+  Function reading bytes from the underlying media.
+  
+  For specification, see @c bstream_read_part() function.
+*/ 
+typedef int (*as_read_m)(void *, bstream_blob*, bstream_blob);
+
+/**
+  Function skipping bytes in the underlying stream.
+  
+  This function should move the read/write "head" of the underlying stream by
+  the amount stored in variable pointed by @c offset. The variable is updated
+  to show how much the head was actually moved.
+  
+  @retval BSTREAM_OK     operation successful
+  @retval BSTREAM_ERROR  error has happened, @c *offset informs how many bytes 
+                         have been skipped.
+*/ 
+typedef int (*as_forward_m)(void *, unsigned long int *offest);
+
+
+/*********************************************************************
+ * 
+ *   MEMORY ALLOCATOR
+ * 
+ *********************************************************************/
+
+/*
+ Programs which doesn't need a specialized memory allocator can define
+ BSTREAM_USE_MALLOC macro before including this file and then the standard
+ allocating functions will be used.
+
+ Note: the BSTREAM_USE_MALLOC macro should be used in at most one compilation
+ module - otherwise several copies of bstream_{malloc,free} will be defined. 
+*/
+ 
+#ifdef BSTREAM_USE_MALLOC
+
+#include <stdlib.h>
+
+bstream_byte* bstream_alloc(unsigned long int size)
+{ return (bstream_byte*)malloc(size); }
+
+void bstream_free(bstream_byte *ptr)
+{ free((bstream_byte*)ptr); }
+
+#else
+
+/** Allocate given amount of memory and return pointer to it */
+bstream_byte* bstream_alloc(unsigned long int size);
+
+/** Free previously allocated memory */
+void bstream_free(bstream_byte *ptr);
+
+#endif
+
+
+#endif /*STREAM_V1_SERVICES_H_*/
diff -Nrup a/sql/backup/stream_v1_transport.c b/sql/backup/stream_v1_transport.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/backup/stream_v1_transport.c	2007-11-14 19:48:07 +01:00
@@ -0,0 +1,1231 @@
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "stream_v1.h"
+#include "stream_v1_services.h"
+
+/**
+  @file
+
+  @brief
+  Implementation of the low-level I/O functions from backup stream library.
+
+  These functions form the transport layer of the 1st version of backup stream
+  format. They split stream into a sequence of data chunks.
+*/
+
+/**
+  @brief Default size of a stream block.
+
+  When opening stream for writing a different size can be specified.
+*/
+#define DEFAULT_BLOCK_SIZE  (32*1024)
+
+/**
+  @brief Input buffer size.
+
+  When reading stream, we need sometimes to look ahead into it (for example
+  to see a fragment header byte). This constant determines how much data will
+  be loaded into internal input buffer in such cases.
+
+  @note
+
+  - The underlying stream will most probably have its own input buffer and
+  thus increasing this constant will introduce more double buffering.
+
+  - The size of input buffer can be different from the stream block size.
+*/
+#define INPUT_BUF_SIZE      512
+
+/**
+  @brief
+  Minimal amount of data considered for writing without internal buffering.
+
+  The write code tries to avoid double buffering and write data directly to
+  the underlying stream if possible. However, for small amounts of data it is
+  perhaps better to save them in an internal output buffer and write the whole
+  buffer later. This constant defines the threshold below which data will be
+  buffered internally.
+*/
+#define MIN_WRITE_SIZE      1024
+
+/* Local type definitions. */
+
+#define TRUE    1
+#define FALSE   0
+#define ASSERT(X) assert(X)
+
+typedef unsigned char bool;
+typedef bstream_byte byte;
+typedef bstream_blob blob;
+
+
+/*
+ Carrier format
+ --------------
+
+ [backup stream] = [ block ! ... ! block ]
+
+ [block] = [ fragment ! ... ! fragment ]
+
+ where [fragment] is one of
+
+ [EOC marker] = [ 0x80 ]
+ [EOS marker] = [ 0xC0 ]
+ [fragment extending to the end of block, last in a chunk] = [ 0x00 ! payload ]
+ [fragment extending to the end of block, more follow] = [ 0x40 ! payload ]
+ [limited size fragment] = [ header:1 ! payload ]
+
+ The header of [limited size fragment] consists of two bit fragment type info
+ followed by 6 bit, non-zero value encoding length of the fragment:
+
+ [fragment header] = [ type:.2 ! value:.6 ]
+
+ There are four types of fragments determined by the first 2 bits of the header:
+
+ 00 - small fragment which is the last fragment of a chunk,
+ 01 - small fragment with more fragments following it,
+ 10 - big fragment (more fragments follow),
+ 11 - huge fragment (more fragments follow).
+
+ Encoding of the size of the fragment depends on its type:
+
+ - for small fragments: size= value
+ - for big fragments:   size= value << 6
+ - for huge fragments:  size= value << 12
+
+ For small fragments, second bit of the header determines if it is the last
+ fragment of a chunk or there are more fragments to come. Chunk can't end with
+ a big or huge fragment and thus for these fragments we always expect more
+ fragments to come. [EOC marker] ends a chunk, even if the last fragment said
+ that more fragments will follow.
+
+ The biggest fragment size is 64*2^12 ~= 250 Kb. The format of fragment header
+ puts constraints on possible fragment sizes. If a chunk of data has size not
+ possible to encode by a single fragment header, it is split into several
+ fragments of correct sizes.
+*/
+
+
+/*************************************************************************
+ *
+ *   ENCODING OF CHUNK FRAGMENTS
+ *
+ *************************************************************************/
+
+#define FR_EOC    0x80
+#define FR_EOS    0xC0
+#define FR_MORE   0x00
+#define FR_LAST   0x40
+
+#define FR_TYPE_MASK  0xC0
+#define FR_LEN_MASK   (~FR_TYPE_MASK)
+
+/** biggest size of small fragment */
+#define FR_SMALL_MAX  ((size_t)FR_LEN_MASK)
+#define FR_BIG        0x80        /**< type bits for big fragment */
+#define FR_HUGE       0xC0        /**< type bits for huge fragment */
+#define FR_BIG_SHIFT  6           /**< value shift for big fragment */
+#define FR_HUGE_SHIFT 12          /**< value shift for huge fragment */
+/** header for the biggest possible chunk */
+#define FR_HUGE_MAX_HDR (FR_HUGE|FR_LEN_MASK)
+/** size of the biggest possible chunk */
+#define FR_HUGE_MAX_LEN ((size_t)FR_LEN_MASK << FR_HUGE_SHIFT)
+
+/**
+  Determine biggest prefix of a given blob of data which can be stored as
+  a single fragment.
+
+  @return FR_EOC if there is no data in the blob. Otherwise header of the
+  prefix fragment and @c *data is updated to describe the part of the blob
+  which is not included in the prefix.
+*/
+static
+byte biggest_fragment_prefix(blob *data)
+{
+  size_t len= data->end - data->begin;
+  byte *prefix_end= data->begin;
+  byte hdr;
+
+  ASSERT(data->end >= data->begin);
+
+  if (len == 0)
+    return FR_EOC;
+
+  if (len > FR_HUGE_MAX_LEN)
+  {
+    data->begin += FR_HUGE_MAX_LEN;
+    return FR_HUGE_MAX_HDR;
+  }
+
+  hdr= len & FR_LEN_MASK;
+  prefix_end= data->begin + hdr;
+
+  len >>= 6;
+  if (len)
+  {
+    hdr = FR_BIG|(len & FR_LEN_MASK);
+    prefix_end= data->begin + ((len & FR_LEN_MASK) << FR_BIG_SHIFT);
+  }
+
+  len >>= 6;
+  if (len)
+  {
+    hdr = FR_HUGE|(len & FR_LEN_MASK);
+    prefix_end= data->begin + ((len & FR_LEN_MASK) << FR_HUGE_SHIFT);
+  }
+
+  data->begin = prefix_end;
+  return hdr;
+}
+
+/**
+ Read fragment header.
+
+ Fragment header is in the byte pointed by @c *header. After reading the
+ header, @c *header is set to point at the first byte after the
+ end of the fragment unless the fragment extends to the end of the current
+ block, in which case @c *header is unchanged.
+
+ @retval BSTREAM_EOC   header contains EOC marker
+ @retval BSTREAM_EOS   header contains EOS marker
+ @retval FR_MORE  normal fragment which is not the last fragment of a chunk
+ @retval FR_LAST  this is last fragment of a chunk
+*/
+static
+int read_fragment_header(byte **header)
+{
+  byte hdr= *((*header)++);
+  size_t len= hdr & FR_LEN_MASK; /* 6 bit value stored in the header */
+  bool last;
+
+  if (hdr == FR_EOS)
+    return BSTREAM_EOS;
+
+  if (hdr == FR_EOC)
+    return BSTREAM_EOC;
+
+  last= hdr & FR_LAST ? FR_LAST : FR_MORE; /* is it last fragment of a chunk? */
+
+  /*
+    If len == 0 then we have a fragment which extends to the end of a block,
+    thus we restore *header to signal that fact to the caller.
+   */
+  if (len == 0)
+  {
+    (*header)--;
+    return last;
+  }
+
+  /*
+    If the highest bit is set, we are looking at a big or huge fragment. Such
+    fragment is never the last fragment of a chunk.
+   */
+  if (hdr & 0x80)
+  {
+    last= FR_MORE;
+
+    if ((hdr & FR_TYPE_MASK) == FR_BIG)
+      len <<= FR_BIG_SHIFT;
+
+    if ((hdr & FR_TYPE_MASK) == FR_HUGE)
+      len <<= FR_HUGE_SHIFT;
+  }
+
+  /* move *header pointer to the first byte after the fragment */
+  *header += len;
+
+  return last;
+}
+
+
+/*************************************************************************
+ *
+ *   OPERATIONS ON ABSTRACT STREAM
+ *
+ *************************************************************************/
+
+/*
+ These macros use function pointers stored in a backup_stream structure
+ (parameter S) to write/read bytes to/from underlying stream.
+*/
+
+#define as_write(S,Data,Env) \
+  ((S)->write ? (S)->write((S),(Data),(Env)) : BSTREAM_ERROR)
+
+#define as_read(S,Buf,Env) \
+  ((S)->read ?(S)->read((S),(Buf),(Env)) : BSTREAM_ERROR)
+
+#define as_forward(S,Off) \
+  ((S)->forward ? (S)->forward((S),(Off)) : (*(Off)=0, BSTREAM_ERROR))
+
+/** Write all bytes in a given blob */
+int as_write_all(struct st_abstract_stream *s, bstream_blob b, bstream_blob env)
+{
+  int ret= BSTREAM_OK;
+
+  while (ret == BSTREAM_OK && b.begin < b.end)
+   ret= as_write(s,&b,env);
+
+  return ret;
+};
+
+/** Fill blob with bytes from stream */
+int as_read_all(struct st_abstract_stream *s, bstream_blob b, bstream_blob env)
+{
+  int ret= BSTREAM_OK;
+
+  while (ret == BSTREAM_OK && b.begin < b.end)
+   ret= as_read(s,&b,env);
+
+  return ret;
+};
+
+
+/*************************************************************************
+ OUTPUT BUFFER
+
+ It stores data to be written to the next block in the output stream. Data
+ in block is divided into fragments. If buffer is non-empty, the header of
+ last fragment stored in it is pointed by header. pos marks the end of data
+ in the buffer, end indicates how much bytes is left until the end of output
+ block
+
+    other fragments   current fragment   free space
+ [===================|XX:=============:..............]
+ ^                    ^               ^              ^
+ begin                header          pos            end
+
+ Note: pos == header means that the current fragment is empty and when appending
+ data to it, one byte should be left for the fragment header.
+
+ Invariant:
+
+    (end - begin) is the number of bytes left to the end of block
+
+ *************************************************************************/
+
+/**
+  Prepare stream`s output buffer for writing a new block
+*/
+static
+void reset_output_buffer(backup_stream *s)
+{
+  s->buf.pos= s->buf.header= s->buf.begin= s->mem.begin;
+  s->buf.end= s->buf.begin + s->block_size;
+}
+
+/**
+  Write stream output buffer's contents to the output stream.
+
+  After this call the buffer becomes empty (pos == header == begin). The end
+  pointer indicates end of output block as always (the invariant).
+
+  @returns error code if there was an error while writing to the output stream.
+*/
+static
+int write_buffer(backup_stream *s)
+{
+  int ret= BSTREAM_OK;
+
+  if (s->buf.pos > s->buf.begin)
+    ret= as_write_all(&s->stream,*(bstream_blob*)&s->buf,s->mem);
+
+  /*
+    Now buffer should be empty. If whole block has been written, reset buffer
+    so that it is prepared for next block. Otherwise leave as much space as
+    remains to the end of the block.
+   */
+  if (s->buf.pos == s->buf.end)
+    reset_output_buffer(s);
+  else
+    s->buf.begin= s->buf.header= s->buf.pos; /* now invariant should hold */
+
+  return ret;
+}
+
+/**
+  Append given blob of data to the current fragment in stream`s output buffer.
+
+  @note if the buffer or the current fragment are empty then one byte space
+  is left for fragment`s header.
+*/
+static
+int append_to_buffer(backup_stream *s, blob *b)
+{
+  if (b->begin >= b->end) // no data to append
+    return BSTREAM_OK;
+
+  if(s->buf.pos == s->buf.header) // current fragment empty
+    s->buf.pos++;
+
+  while (s->buf.pos < s->buf.end && b->begin < b->end)
+   *(s->buf.pos++)= *(b->begin++);
+
+  return BSTREAM_OK;
+}
+
+/**
+  Prepare the current fragment in stream`s output buffer to be written
+  to the output stream.
+
+  This means correctly setting the header of the fragment. If it is not possible
+  to describe fragment`s length in a single byte header, the data is split into
+  more fragments. When splitting is done, the last of the resulting fragments
+  becomes the current fragment in the buffer (with header correctly set)
+  while all other data from the buffer is send to the output stream.
+
+  @return Error code if there was an error while writing output stream.
+*/
+static
+int close_current_fragment(backup_stream *s)
+{
+  struct st_bstream_buffer *buf= &s->buf;
+  int ret= BSTREAM_OK;
+
+  /* blob describing data in the current fragment */
+  blob current_fragment= {buf->header+1, buf->pos};
+
+  /* nothing to do if current fragment is empty */
+  if (buf->pos == buf->header)
+    return BSTREAM_OK;
+
+  /*
+    The following loop will split the current_fragment data into as many
+    sub-fragments as necessary, chopping off a biggest possible prefix in each
+    iteration.
+
+    However, if the data extends to the end of the block, we don't have to
+    do any splitting since we can create fragment which spans to the end of the
+    block.
+   */
+  while (current_fragment.begin < current_fragment.end &&
+         current_fragment.end < buf->end)
+  {
+    /*
+      Determine longest prefix of the current fragments which can be described
+      by a fragemnt header.
+     */
+    *(buf->header)= biggest_fragment_prefix(&current_fragment);
+
+    /*
+      If there are any bytes not included in the prefix, we write prefix
+      to the output stream and deal with the remainder in the next iteration
+      of the loop.
+     */
+    if (current_fragment.begin < current_fragment.end)
+    {
+      buf->pos= current_fragment.begin;
+
+      ret= write_buffer(s); /* this empties the buffer */
+      if (ret != BSTREAM_OK)
+        return ret;
+
+      buf->pos= current_fragment.end;
+
+      /*
+        Now buffer contains only the remaining bytes. We shift whole bufer
+        one byte to the left to make space for the header of next fragment.
+       */
+      buf->begin= --(buf->header);
+      buf->end--; /* now invariant should hold */
+    }
+  }
+
+  /*
+    If there any data left here it means that it extends to the end of the
+    block. We set the fragment header accordingly.
+   */
+  if (current_fragment.begin < current_fragment.end)
+  {
+    ASSERT(current_fragment.end == buf->end);
+    *(buf->header)= FR_MORE;
+  }
+
+  return BSTREAM_OK;
+}
+
+/*************************************************************************
+ INPUT BUFFER
+
+ Stores bytes read from input stream. These bytes are stored in the region
+ starting from begin and ending at pos (pos points one byte after the end of
+ data). The bytes starting from begin belong to the current fragment. The next
+ fragment starts at byte pointed by header (this byte contains fragment`s
+ header). When all data from the current fragment have been read, begin==header.
+ To start reading next fragment, it must be entered so that header is moved to
+ point at the header of the fragment following it (this is done in the
+ load_next_fragment() function).
+
+ The header of next fragment can be inside the input buffer:
+
+                               rest of input block
+              next fragment   (still in the stream)
+ [==========|XX:============].......................|
+  ^          ^              ^                       ^
+  begin      header         pos                     end
+
+ or still in the stream:
+
+ [=============]...........|........................|
+  ^            ^            ^                       ^
+  begin        pos          header                  end
+
+ In each case header points at the position where the header should be if
+ the data were present in the buffer.
+
+ Invariant:
+
+    (end - pos) is the number of bytes left in the input block
+    header <= end and header points at the first byte of next fragment
+
+ *************************************************************************/
+
+/**
+  Prepare stream`s input buffer for reading next block from input stream.
+*/
+static
+void reset_input_buffer(backup_stream *s)
+{
+  s->buf.header -= (s->buf.pos - s->mem.begin);
+  s->buf.pos= s->buf.begin= s->mem.begin;
+  s->buf.end= s->buf.begin + s->block_size;
+}
+
+/**
+  Read few more bytes into the stream`s input buffer if it is empty.
+
+  Normally INPUT_BUF_SIZE bytes is read, unless current input block doesn't
+  contain that many bytes in which case the rest of the input block is read.
+
+  @retval BSTREAM_OK   input buffer was filled with data
+  @retval BSTREAM_EOS  end of stream was hit, all remaining bytes from the stream
+                  are in the buffer
+  @retval BSTREAM_ERROR  error when reading from the stream
+*/
+static
+int load_buffer(backup_stream *s)
+{
+  int ret= BSTREAM_OK;
+  byte  *saved_begin;
+  size_t howmuch= s->buf.end - s->buf.pos;
+
+  /* do nothing if there already is some data in the buffer */
+  if (s->buf.pos > s->buf.begin)
+    return BSTREAM_OK;
+
+  /*
+    Call reset_input_buffer() to move buffer head to the beginning of
+    available memory
+   */
+  reset_input_buffer(s);
+
+  /*
+    If howmuch > 0 the current input block has not been completely read yet.
+    In that case we restore input buffer's invariant by setting end pointer
+    accordingly.
+   */
+  if (howmuch > 0)
+    s->buf.end= s->buf.pos + howmuch;
+  else
+    howmuch= s->buf.end - s->buf.pos;
+
+  /* don't read more than INPUT_BUF_SIZE */
+  if (howmuch > INPUT_BUF_SIZE)
+    howmuch= INPUT_BUF_SIZE;
+
+  /* read into the buffer howmuch bytes from the input stream */
+  s->buf.pos += howmuch;
+  saved_begin= s->buf.begin;
+
+  ret= as_read(&s->stream,(bstream_blob*)&s->buf,s->mem);
+
+  s->buf.pos= s->buf.begin;
+  s->buf.begin= saved_begin;
+
+  return ret;
+}
+
+/**
+  Setup input buffer to read next fragment.
+
+  This moves buf.header so that it points at the header of the fragment
+  following the fragment which is entered now. buf.begin will point at the
+  first byte of the entered fragment.
+
+  @pre All bytes from previous fragment have been consumed (buf.begin ==
+  buf.header) and header of next fragment is loaded into the buffer.
+
+  @retval BSTREAM_OK   next fragment has been entered
+  @retval BSTREAM_EOC  the entered fragment is the last fragment of a chunk
+  @retval BSTREAM_EOS  there are no more fragments in the stream
+*/
+static
+int load_next_fragment(backup_stream *s)
+{
+  byte *saved_header= s->buf.header;
+
+  ASSERT(s->buf.pos > s->buf.header);
+  ASSERT(s->buf.begin == s->buf.header);
+
+  s->reading_last_fragment= 0;
+
+  int ret= read_fragment_header(&s->buf.header);
+
+  /*
+    If buf.header was not moved, it means that the fragment extends to
+    the end of the input block and thus we set buf.header= buf.end.
+   */
+  if (s->buf.header == saved_header)
+    s->buf.header= s->buf.end;
+
+  /* move buf.begin to point at the first byte of the fragment */
+  s->buf.begin++;
+
+  /*
+    It can happen that fragment header was the last byte in
+    the block. In that case we reload input buffer and start over.
+
+    TODO: remove recursion
+   */
+  if (s->buf.begin == s->buf.end)
+  {
+    ret= load_buffer(s);
+    return ret == BSTREAM_OK ? load_next_fragment(s) : ret;
+  }
+
+  switch (ret) {
+
+  case BSTREAM_EOS: s->state= EOS; return BSTREAM_EOS;
+
+  case BSTREAM_EOC: s->reading_last_fragment= 1; return BSTREAM_EOC;
+
+  case FR_LAST: s->reading_last_fragment= 1;
+
+  default: return BSTREAM_OK;
+  }
+}
+
+
+/*********************************************************************
+ *
+ *   INITIALIZATION
+ *
+ *********************************************************************/
+
+/**
+  Open backup stream for writing.
+
+  @pre The abstract stream methods in @c s should be setup and ready for
+  writing.
+
+  @note Output buffer is allocated.
+*/
+int bstream_open_wr(backup_stream *s, unsigned long int block_size)
+{
+  s->state= ERROR;
+  s->block_size= block_size > 0 ? block_size : DEFAULT_BLOCK_SIZE;
+
+  s->mem.begin= bstream_alloc(s->block_size);
+
+  if (!s->mem.begin)
+    return BSTREAM_ERROR;
+
+  s->mem.end= s->mem.begin + s->block_size;
+
+  reset_output_buffer(s);
+
+  s->data_buf.begin= s->data_buf.end= NULL;
+  s->state= WRITING;
+
+  return BSTREAM_OK;
+}
+
+/**
+  Open backup stream for reading.
+
+  @pre The abstract stream methods in @c s should be setup and ready for
+  reading.
+
+  @note Input buffer is allocated.
+*/
+int bstream_open_rd(backup_stream *s, unsigned long int block_size)
+{
+  s->state= ERROR;
+  s->block_size= block_size;
+
+  s->mem.begin= bstream_alloc(INPUT_BUF_SIZE);
+
+  if (!s->mem.begin)
+    return BSTREAM_ERROR;
+
+  s->mem.end= s->mem.begin + INPUT_BUF_SIZE;
+
+  /* initialize input buffer */
+  reset_input_buffer(s);
+  s->buf.header= s->buf.begin;
+
+  s->data_buf.begin= s->data_buf.end= NULL;
+  s->state= READING;
+
+  return BSTREAM_OK;
+}
+
+/**
+  Close backup stream.
+
+  @note This function can be used both for streams opened for reading or
+  for writing.
+*/
+int bstream_close(backup_stream *s)
+{
+  int ret= BSTREAM_OK;
+
+  if (s->state == CLOSED)
+    return BSTREAM_OK;
+
+  if (s->state == WRITING)
+  {
+    bstream_end_chunk(s);
+    bstream_flush(s);
+    /* write EOS marker */
+    reset_output_buffer(s);
+    *(s->buf.pos++)= FR_EOS;
+    ret= write_buffer(s);
+  }
+
+  if (s->mem.begin)
+  {
+    bstream_free(s->mem.begin);
+    s->mem.begin= NULL;
+  }
+
+  if (s->data_buf.begin)
+  {
+    bstream_free(s->data_buf.begin);
+    s->data_buf.begin= NULL;
+  }
+
+  if (s->state == ERROR)
+    return BSTREAM_ERROR;
+
+  s->state= CLOSED;
+
+  return ret;
+}
+
+
+/*********************************************************************
+ *
+ *   WRITING
+ *
+ *********************************************************************/
+
+/**
+  Write part of buffer to backup stream.
+
+  Write the part of @c buf described by @c data blob. Not all bytes form
+  @c data need to be written at once - the blob is modified to describe these
+  bytes which have not been written.
+
+  @verbatim
+
+  buf  [---------------=================--------]
+
+  data blob before writing:
+
+                      [=================]
+
+  data blob after writing:
+
+                       **********[======]
+                       ^          ^
+                       |          bytes yet to be written
+                       bytes which have been written
+  @endverbatim
+
+  @retval BSTREAM_OK at least one byte was written (or copied to the output buffer)
+  @retval BSTREAM_ERROR  error was detected
+*/
+int bstream_write_part(backup_stream *s, bstream_blob *data, bstream_blob buf)
+{
+  int ret;
+  blob fragment;
+  byte *saved_end;
+
+  if (s->state != WRITING)
+    return BSTREAM_ERROR;
+
+  if (data->begin >= data->end)
+    return BSTREAM_OK;
+
+  if (s->buf.pos == s->buf.end)
+  {
+    ret= bstream_flush(s);
+    if (ret != BSTREAM_OK)
+      return ret;
+  }
+
+  ASSERT(s->buf.pos < s->buf.end);
+
+  /* if current fragment is empty, make space for the header */
+  if (s->buf.pos == s->buf.header)
+    s->buf.pos++;
+
+  /*
+    Setup fragment to describe all the data available in the current fragment
+    of the stream`s output buffer together with the data to be written
+
+       output buffer
+               current fragment
+   [ ===== | 0x00 ===============]
+            ^
+            header                       data
+                                 [====================]
+
+                  [-----------------------------------]
+                              fragment
+   */
+  fragment.begin= s->buf.header+1;
+  fragment.end=   s->buf.pos + (data->end - data->begin);
+
+  /*
+    If there is enough data to fill a complete block, we will write it and
+    mark that the last fragment extends to its end.
+
+    We first write the contents of output buffer and then as much of data
+    as needed to fill the block.
+   */
+  if (fragment.end > s->buf.end)
+  {
+    /*
+      Possible optimization: try to write block in one go by copying part
+      of data to output buffer or prepending output buffer to data if there is
+      space for that. Do that only if it doesn't mean a lot of data copying.
+     */
+
+    *s->buf.header= FR_MORE;
+
+    ret= write_buffer(s);
+    if (ret != BSTREAM_OK)
+      return ret;
+
+    /* write bytes from data blob to fill the block */
+    saved_end= data->end;
+    data->end= data->begin + (s->buf.end - s->buf.pos);
+
+    ret= as_write_all(&s->stream,*data,buf);
+    if (ret != BSTREAM_OK)
+      return ret;
+
+    data->begin= data->end;
+    data->end= saved_end;
+
+    reset_output_buffer(s); /* prepare for next block */
+    return BSTREAM_OK;
+  }
+
+  /*
+   To avoid copying bytes to the internal output buffer we try to cut a prefix
+   of the data to be written which forms a valid fragment and write this
+   fragment to output stream.
+  */
+  *(s->buf.header)= biggest_fragment_prefix(&fragment);
+
+  /*
+    We use this method only if it will actually write enough of the bytes
+    to be written - if it is only few bytes we save them into the output
+    buffer anyway.
+   */
+  if (fragment.end > s->buf.pos + MIN_WRITE_SIZE)
+  {
+    /* write contents of the output buffer */
+    ret= write_buffer(s);
+    if (ret != BSTREAM_OK)
+      return ret;
+
+    /* write remainder of the fragment from data blob */
+    saved_end= data->end;
+    data->end= data->begin + (fragment.end - s->buf.pos);
+
+    ret= as_write_all(&s->stream,*data,*data);
+
+    data->begin= data->end;
+    data->end= saved_end;
+
+    /* move buffer beginning to keep the invariant */
+
+    s->buf.begin = fragment.end;
+    s->buf.pos= s->buf.header= s->buf.begin;
+
+    return ret;
+  }
+
+  /*
+    If nothing else worked, we just append the data to the output buffer
+    and return.
+   */
+  append_to_buffer(s,data);
+  return BSTREAM_OK;
+}
+
+/**
+  Write bytes to backup stream.
+
+  @param s      backup stream to write to
+  @param b      blob with bytes to be written
+
+  The blob is modified to indicate which data was not written.
+
+  @retval BSTREAM_OK at least one byte was written (or copied to the output buffer)
+  @retval BSTREAM_ERROR  error was detected
+*/
+int bstream_write(backup_stream *s, bstream_blob *b)
+{
+  blob buf= *b;
+
+  return bstream_write_part(s,b,buf);
+}
+
+/**
+  Write complete blob to backup stream.
+
+  This function iterates until all bytes of the blob are written or
+  error is detected.
+
+  @retval BSTREAM_OK all bytes from the blob have been written (or copied to the
+                output buffer)
+  @retval BSTREAM_ERROR  error was detected
+*/
+int bstream_write_blob(backup_stream *s, bstream_blob buf)
+{
+  blob envelope= buf;
+  int ret= BSTREAM_OK;
+
+  while (ret == BSTREAM_OK && buf.begin < buf.end)
+    ret= bstream_write_part(s,&buf,envelope);
+
+  return ret;
+}
+
+/**
+  Create new chunk in the stream.
+
+  The new chunk will contain all data which has been send to the stream
+  after the last chunk was closed. If no data was sent nothing will happen
+  (it is not possible to have an empty chunk).
+
+  @retval BSTREAM_OK chunk has been created
+  @retval BSTREAM_ERROR  error was detected
+*/
+int bstream_end_chunk(backup_stream *s)
+{
+  int ret= BSTREAM_OK;
+
+  if (s->state != WRITING)
+    return BSTREAM_ERROR;
+
+
+  ret= close_current_fragment(s);
+
+  /*
+    If buffer is empty, store EOC marker in it otherwise mark the last
+    fragment of the chunk.
+   */
+  if (s->buf.pos == s->buf.begin)
+  {
+    *(s->buf.header++)= FR_EOC;
+    s->buf.pos= s->buf.header;
+  }
+  else
+    *s->buf.header |= FR_LAST;
+
+  /*
+    Start new fragment. Note that if the current fragment is empty, these
+    operations will have no effect.
+  */
+  s->buf.header= s->buf.pos;
+
+  if (s->buf.pos == s->buf.end-1)
+  {
+    *(s->buf.pos++)= FR_MORE;
+    ret= bstream_flush(s);
+  }
+
+  return ret;
+}
+
+/**
+ Flush backup stream`s output buffer to the output stream.
+*/
+int bstream_flush(backup_stream *s)
+{
+  struct st_bstream_buffer *buf= &s->buf;
+  int ret;
+
+  if (s->state != WRITING)
+    return BSTREAM_ERROR;
+
+  /* if buffer is empty, do nothing */
+  if (buf->pos == buf->begin)
+    return BSTREAM_OK;
+
+  /*
+    If current fragment is empty, just write the other fragments stored
+    in the buffer
+   */
+  if (buf->pos == buf->header)
+  {
+    ret= write_buffer(s);
+    return ret;
+  }
+
+  /* Otherwise close the current fragment to set its header */
+  ret= close_current_fragment(s);
+  if (ret != BSTREAM_OK)
+    return ret;
+
+  /*
+    If there is only one byte left to the end of block, we fill this byte with
+    a header of a fragment which will effectively be empty.
+   */
+  if (buf->pos == buf->end-1)
+    *(buf->pos++)= FR_MORE;
+
+  if (buf->pos > buf->begin)
+    ret= write_buffer(s);
+
+  /* start new fragment */
+
+  buf->header= buf->pos;
+
+  return ret;
+}
+
+
+/*********************************************************************
+ *
+ *   READING
+ *
+ *********************************************************************/
+
+/**
+  Read data from the stream to the indicated part of a buffer.
+
+  Data are stored in the part of @c buf described by @c data. At most as many
+  bytes will be read as will fit into the @c data blob, however less bytes can
+  be read. After the call, @c data is modified to describe the area which was
+  not filled with data.
+
+  @verbatim
+
+  buf  [---------------=================--------]
+
+  data blob before reading:
+
+                      [=================]
+
+  data blob after reading:
+
+                       **********[======]
+                       ^          ^
+                       |          space remaining free
+                       bytes which have been read
+  @endverbatim
+
+  @retval BSTREAM_EOC  current data chunk ended while reading data - the following
+                  calls to @c bstream_read() will not read any data until we move to
+                  the next chunk with @c bstream_next_chunk()
+  @retval BSTREAM_EOS  end of stream was detected while reading data
+  @retval BSTREAM_ERROR error was detected while reading data
+  @retval BSTREAM_OK   successful read - at least one byte was read
+*/
+int bstream_read_part(backup_stream *s, bstream_blob *data, bstream_blob buf)
+{
+  int ret= BSTREAM_OK;
+  size_t howmuch;
+  blob saved;
+
+  if (s->state != READING)
+    return s->state == EOS ? BSTREAM_EOS : BSTREAM_ERROR;
+
+  /* fill input buffer if it is empty */
+  if (s->buf.pos == s->buf.begin)
+  {
+    ret= load_buffer(s);
+    if (ret != BSTREAM_OK)
+      return ret;
+  }
+
+  ASSERT(s->buf.pos > s->buf.begin);
+
+  /*
+    If we finished reading a fragment, we should load next one
+    or signal EOC if it was the last fragment of a chunk.
+   */
+  if (s->buf.header == s->buf.begin)
+  {
+    if (s->reading_last_fragment)
+      return BSTREAM_EOC;
+
+    ret= load_next_fragment(s);
+    if (ret != BSTREAM_OK)
+      return ret;
+  }
+
+  /*
+    Determine length of the fragment remainder stored in the input
+    buffer.
+  */
+  if (s->buf.header <= s->buf.pos)
+    howmuch= s->buf.header - s->buf.begin;
+  else
+    howmuch= s->buf.pos - s->buf.begin;
+
+  /*
+    If there is some data in the buffer we copy it to the data blob,
+    otherwise we fill data from the stream.
+   */
+  if (howmuch > 0)
+  {
+    if (howmuch > (size_t)(data->end - data->begin))
+      howmuch= data->end - data->begin;
+
+    memmove(data->begin, s->buf.begin, howmuch);
+    data->begin  += howmuch;
+    s->buf.begin += howmuch;
+
+  }
+  else
+  {
+    /* read directly into data area */
+    ASSERT(s->buf.header > s->buf.pos);
+
+    howmuch= data->end - data->begin;
+    if (howmuch > (size_t)(s->buf.header - s->buf.pos))
+      howmuch= s->buf.header - s->buf.pos;
+
+    saved= *data;
+    data->end= data->begin + howmuch;
+
+    as_read(&s->stream,data,buf);
+
+    s->buf.begin += data->begin - saved.begin;
+    s->buf.pos= s->buf.begin;
+
+    data->begin= data->end;
+    data->end= saved.end;
+  }
+
+  return s->buf.begin == s->buf.header && s->reading_last_fragment ?
+         BSTREAM_EOC: BSTREAM_OK;
+}
+
+
+/**
+  Read bytes from backup stream.
+
+  @param s      backup stream to read from
+  @param b      blob where the data should be placed
+
+  The blob is modified to describe the area which was not filled with the data.
+
+  @retval BSTREAM_EOC  current data chunk ended while reading data - the
+            following calls to @c bstream_read() will not read any data until
+            we move to the next chunk with @c bstream_next_chunk()
+  @retval BSTREAM_EOS   end of stream was detected while reading data
+  @retval BSTREAM_ERROR error was detected while reading data
+  @retval BSTREAM_OK    successful read - at least one byte was read
+*/
+int bstream_read(backup_stream *s, bstream_blob *b)
+{
+  return bstream_read_part(s,b,*b);
+}
+
+
+/**
+  Read complete blob to backup stream.
+
+  This function iterates until whole blob is filled with bytes from the
+  stream.
+*/
+int bstream_read_blob(backup_stream *s, bstream_blob buf)
+{
+  blob envelope= buf;
+  int ret= BSTREAM_OK;
+
+  while (ret == BSTREAM_OK && buf.begin < buf.end)
+    ret= bstream_read_part(s,&buf,envelope);
+
+  return buf.begin == buf.end ? ret : BSTREAM_ERROR;
+}
+
+/**
+ Position backup stream at the first byte of next chunk.
+
+ If there are no more chunks in the stream, stream position is
+ moved to its end (so that following reads will not read any bytes
+ and report EOS).
+
+ @retval BSTREAM_OK  stream is positioned at the beginning of next chunk
+ @retval BSTREAM_EOS there are no more chunks in the stream
+ @retval BSTREAM_ERROR an error was detected - stream position is undefined
+*/
+int bstream_next_chunk(backup_stream *s)
+{
+  int ret;
+  unsigned long int howmuch;
+
+  if (s->state != READING)
+    return s->state == EOS ? BSTREAM_EOS : BSTREAM_ERROR;
+
+  /* if we are not at the beginning of next fragment, move there */
+
+  if (s->buf.begin < s->buf.header)
+  {
+    /*
+      The header of next fragment can be in the buffer, or still in
+      the stream. In former case we just move beginning of the buffer to the
+      header, otherwise we move the stream forward and empty the buffer so that
+      header will be loaded into it When the buffer is filled below.
+     */
+    if (s->buf.header < s->buf.pos)
+      s->buf.begin= s->buf.header;
+    else
+    {
+      howmuch= s->buf.header - s->buf.pos;
+      as_forward(&s->stream, &howmuch);
+      s->buf.begin= s->buf.pos= s->buf.header;
+    }
+  }
+
+  /*
+    If buffer is empty, we load few bytes into it to have access to
+    the fragment header.
+   */
+  if (s->buf.pos == s->buf.begin)
+    load_buffer(s);
+
+  ASSERT(s->buf.begin == s->buf.header);
+  ASSERT(s->buf.pos > s->buf.header);
+
+  ret= load_next_fragment(s);
+
+  /* if we hit EOC marker here, we treat it as empty chunk */
+  if (ret == BSTREAM_EOC)
+    ret= BSTREAM_OK;
+
+  return ret;
+}
+
+/**
+  Skip given amount of bytes in the stream.
+
+  @todo do something also when the abstract stream doesn't define forward
+  function (e.g., read and discard bytes).
+*/
+int bstream_skip(backup_stream *s, unsigned long int howmuch)
+{
+  return as_forward(&s->stream, &howmuch);
+}
Thread
bk commit into 6.0 tree (rafal:1.2660) WL#4063rsomla14 Nov