List:Commits« Previous MessageNext Message »
From:Ingo Struewing Date:October 16 2008 7:50pm
Subject:bzr commit into mysql-6.0-backup branch (ingo.struewing:2698) WL#4534
View as plain text  
#At file:///home2/mydev/bzrroot/mysql-6.0-wl4534/

 2698 Ingo Struewing	2008-10-16
      WL#4534 - Backup client program
      
      Second prototype for early design review.
      
      It has a C-based wrapper file (backup_stream.c), which does all
      stream handling and collects all data in a catalog structure.
      
      This implementation continues the type-inheritance model of the
      stream library. This makes simpler code. Less circumstances to
      integrate stream library objects into application objects.
      
      And there is no longer a need to make stream library functions
      accessible from C++.
added:
  client/backup_stream.c
  client/backup_stream.h
  client/mysqlbackup.cc
modified:
  .bzrignore
  client/Makefile.am
  mysql-test/t/backup_myisam2.test

per-file messages:
  .bzrignore
    WL#4534 - Backup client program
    Added mysqlbackup related files.
  client/Makefile.am
    WL#4534 - Backup client program
    Added mysqlbackup client build requirements.
  client/backup_stream.c
    WL#4534 - Backup client program
    Backup stream reader for the second prototype implementation.
  client/backup_stream.h
    WL#4534 - Backup client program
    Backup stream reader declarations.
  client/mysqlbackup.cc
    WL#4534 - Backup client program
    Second prototype implementation.
  mysql-test/t/backup_myisam2.test
    WL#4534 - Backup client program
    Temporarily added copy of a backup file to /tmp/wl4534-test-1.bak.
=== modified file '.bzrignore'
--- a/.bzrignore	2008-09-12 09:22:46 +0000
+++ b/.bzrignore	2008-10-16 19:49:57 +0000
@@ -1893,3 +1893,6 @@ libmysqld/ddl_blocker.cc
 libmysqld/mdl.cc
 client/transaction.h
 libmysqld/transaction.cc
+client/mysqlbackup
+client/stream_v1.h
+client/stream_v1_services.h

=== modified file 'client/Makefile.am'
--- a/client/Makefile.am	2008-09-04 18:30:34 +0000
+++ b/client/Makefile.am	2008-10-16 19:49:57 +0000
@@ -42,6 +42,7 @@ CLEANFILES =			$(BUILT_SOURCES)
 
 bin_PROGRAMS =			mysql \
 				mysqladmin \
+				mysqlbackup \
 				mysqlbinlog \
 				mysqlcheck \
 				mysqldump \
@@ -57,6 +58,12 @@ mysql_LDADD =			@readline_link@ @TERMCAP
 				$(LDADD) $(CXXLDFLAGS)
 mysqladmin_SOURCES =		mysqladmin.cc
 
+mysqlbackup_SOURCES =		mysqlbackup.cc \
+				backup_stream.c \
+				$(top_srcdir)/sql/backup/stream_v1.c \
+				$(top_srcdir)/sql/backup/stream_v1_transport.c
+mysqlbackup_LDADD =		$(LDADD) $(CXXLDFLAGS)
+
 mysqlbinlog_SOURCES =		mysqlbinlog.cc \
 				$(top_srcdir)/mysys/mf_tempdir.c \
 				$(top_srcdir)/mysys/my_new.cc \
@@ -110,6 +117,7 @@ sql_src=log_event.h mysql_priv.h rpl_con
 	rpl_record_old.h rpl_record_old.cc \
 	transaction.h
 strings_src=decimal.c dtoa.c
+backup_src=stream_v1.h stream_v1_services.h
 
 link_sources:
 	for f in $(sql_src) ; do \
@@ -120,6 +128,10 @@ link_sources:
 	  rm -f $(srcdir)/$$f; \
 	  @LN_CP_F@ $(top_srcdir)/strings/$$f $$f; \
 	done; \
+	for f in $(backup_src) ; do \
+	  rm -f $(srcdir)/$$f; \
+	  @LN_CP_F@ $(top_srcdir)/sql/backup/$$f $$f; \
+	done; \
 	rm -f $(srcdir)/my_user.c; \
 	@LN_CP_F@ $(top_srcdir)/sql-common/my_user.c my_user.c;
 	echo timestamp > link_sources;

=== added file 'client/backup_stream.c'
--- a/client/backup_stream.c	1970-01-01 00:00:00 +0000
+++ b/client/backup_stream.c	2008-10-16 19:49:57 +0000
@@ -0,0 +1,1449 @@
+/* Copyright (C) 2008 Sun Microsystems Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/*
+  MySQL Backup Image Stream reading
+*/
+
+#include "backup_stream.h"
+
+/* fprintf, stderr */
+#include <stdio.h>
+/* strerror, memcmp */
+#include <string.h>
+
+
+/**
+  Allocate given amount of memory and return pointer to it.
+
+  @param[in]    size            amount of memory to allocate
+
+  @return       pointer to allocated memory
+*/
+bstream_byte* bstream_alloc(unsigned long int size)
+{
+  return my_malloc(size, MYF(MY_WME));
+}
+
+
+/**
+  Free previously allocated memory.
+
+  @param[in]    ptr             pointer to allocated memory
+*/
+void bstream_free(bstream_byte *ptr)
+{
+  my_free(ptr, MYF(MY_ALLOW_ZERO_PTR));
+}
+
+
+/*
+  ====================================================
+  Helper functions for stream access.
+  These are callback functions for the stream library.
+  ====================================================
+*/
+
+/*
+  Helper struct for stream access functions.
+
+  The stream access functions do not read the backup image file
+  directly. They call back functions provided in st_backup_stream by the
+  application.
+
+  The struct st_stream ties together the st_backup_stream
+  with the data required by the I/O functions provided by this
+  application. Note that 'st_backup_stream' must be the first part of
+  the helper struct.
+
+  'stream_pos' is used to describe which section/chunk of a backup
+  image we have read last.
+*/
+struct st_stream
+{
+  struct st_backup_stream       bupstrm;        /* Must be first in st_stream */
+  File                          fd;             /* File descriptor */
+  my_off_t                      pos;            /* Byte position in file */
+  const char                    *path;          /* File name */
+  const char                    *stream_pos;    /* Verbal stream position */
+};
+
+
+/**
+  Read from the stream/image.
+
+  @param[in,out]    strm        stream handle, updating position
+  @param[in,out]    data        data container, updating contents and ptrs
+  @param            envelope    not used
+
+  @return       status
+    @retval     BSTREAM_OK      ok
+    @retval     BSTREAM_EOS     end of stream
+    @retval     otherwise       error
+
+  @note The return value is specified as 'int' in stream_v1.h
+  though only values from enum_bstream_ret_codes are expected.
+*/
+
+static int
+str_read(struct st_stream *strm, struct st_blob *data,
+         struct st_blob envelope __attribute__((unused)))
+{
+  size_t        lgt;
+  int           rc= BSTREAM_OK;
+  DBUG_ENTER("str_read");
+
+  /*
+    Compute wanted length.
+  */
+  DBUG_ASSERT(data->end >= data->begin);
+  lgt= data->end - data->begin;
+  DBUG_PRINT("bupstrm", ("want bytes: %lu", (ulong) lgt));
+
+  /*
+    Read.
+  */
+  lgt= my_read(strm->fd, data->begin, lgt, MYF(0));
+  if (lgt == MY_FILE_ERROR)
+  {
+    fprintf(stderr, "ERROR: cannot read image '%s': %s\n",
+            strm->path, strerror(my_errno));
+    rc= BSTREAM_ERROR;
+    goto end;
+  }
+  DBUG_PRINT("bupstrm", ("read bytes: %lu", (ulong) lgt));
+
+  /*
+    Check for end of stream.
+  */
+  if (lgt == 0)
+  {
+    rc= BSTREAM_EOS;
+    goto end;
+  }
+
+  /*
+    Update stream handle and data container.
+  */
+  strm->pos+= lgt;
+  data->begin+= lgt;
+
+ end:
+  DBUG_RETURN(rc);
+}
+
+
+/**
+  Skip part of the stream/image.
+
+  @param[in,out]    strm        stream handle, updating position
+  @param[in,out]    len         number of bytes to skip, skipped
+
+  @return       status
+    @retval     BSTREAM_OK      ok
+    @retval     otherwise       error
+
+  @note The return value is specified as 'int' in stream_v1.h
+  though only values from enum_bstream_ret_codes are expected.
+*/
+
+static int
+str_forward(struct st_stream *strm, size_t *len)
+{
+  my_off_t      off_new;
+  int           rc= BSTREAM_OK;
+  DBUG_ENTER("str_forward");
+
+#if !defined(DBUG_OFF)
+  {
+    /*
+      Check if internal counter matches real file position.
+    */
+    my_off_t off_cur= my_seek(strm->fd, (my_off_t) 0, SEEK_CUR, MYF(0));
+    if (off_new == MY_FILEPOS_ERROR)
+    {
+      fprintf(stderr, "ERROR: cannot seek in image '%s': %s\n",
+              strm->path, strerror(my_errno));
+      rc= BSTREAM_ERROR;
+      goto end;
+    }
+    DBUG_ASSERT(off_cur == strm->pos);
+  }
+#endif
+
+  /*
+    Advance file pointer by *len bytes.
+  */
+  off_new= my_seek(strm->fd, (my_off_t) *len, SEEK_CUR, MYF(0));
+  if (off_new == MY_FILEPOS_ERROR)
+  {
+    fprintf(stderr, "ERROR: cannot seek in image '%s': %s\n",
+            strm->path, strerror(my_errno));
+    rc= BSTREAM_ERROR;
+    goto end;
+  }
+
+  /*
+    Compute actual skip length from old and new file positions.
+  */
+  *len= off_new - strm->pos;
+
+  /*
+    Remember current file position.
+  */
+  strm->pos= off_new;
+
+ end:
+  DBUG_RETURN(rc);
+}
+
+
+/**
+  Open the stream/image for reading.
+
+  A backup stream has a prefix, consisting of 8 bytes magic number
+  and 2 bytes version number.
+
+  TODO A compressed image has a gzip magic number.
+  TODO An encrypted image has yet another magic number.
+
+  @param[in,out]    strm        stream handle, updating path, fd, bupstrm
+  @param[in]        path        image path name
+
+  @return       status
+    @retval     BSTREAM_OK      ok
+    @retval     otherwise       error
+
+  @note The return value is specified as 'int' in stream_v1.h
+  though only values from enum_bstream_ret_codes are expected.
+*/
+
+static int
+str_open_rd(struct st_stream *strm, const char *path)
+{
+  size_t                lgt;
+  int                   rc= BSTREAM_OK;
+  int                   errpos= 0;
+  uint16                version;
+  const unsigned char   backup_magic_bytes[8]=
+    {
+      0xE0, // ###.....
+      0xF8, // #####...
+      0x7F, // .#######
+      0x7E, // .######.
+      0x7E, // .######.
+      0x5F, // .#.#####
+      0x0F, // ....####
+      0x03  // ......##
+    };
+  uchar  prefix[sizeof(backup_magic_bytes) + sizeof(version)];
+  DBUG_ENTER("str_open_rd");
+
+  /*
+    Set image path name.
+  */
+  strm->path= path;
+  DBUG_PRINT("bupstrm", ("opening image file: '%s'", strm->path));
+
+  /*
+    Open the image file.
+  */
+  strm->fd= my_open(strm->path, O_RDONLY, MYF(0));
+  if (strm->fd < 0)
+  {
+    fprintf(stderr, "ERROR: cannot open backup image '%s': %s\n",
+            strm->path, strerror(my_errno));
+    goto err;
+  }
+  errpos= 10;
+  DBUG_PRINT("bupstrm", ("opened  image file: '%s'  fd: %d",
+                         strm->path, strm->fd));
+
+  /*
+    Read prefix with magic number and image version.
+  */
+  lgt= my_read(strm->fd, prefix, sizeof(prefix), MYF(0));
+  if (lgt != sizeof(prefix))
+  {
+    if (lgt == MY_FILE_ERROR)
+      fprintf(stderr, "ERROR: cannot read image '%s': %s\n",
+              strm->path, strerror(my_errno));
+    else
+      fprintf(stderr, "ERROR: image file has only %lu bytes of at least %lu\n",
+              (ulong) lgt, (ulong) sizeof(prefix));
+    goto err;
+  }
+  DBUG_PRINT("bupstrm", ("read magic number and version"));
+
+  /*
+    Check magic number and image version.
+  */
+  if (memcmp(prefix, backup_magic_bytes, sizeof(backup_magic_bytes)))
+  {
+    fprintf(stderr, "ERROR: not a backup image file: '%s'. "
+            "Magic number mismatch.\n",
+            strm->path);
+    goto err;
+  }
+  version= uint2korr(prefix+8);
+  if (version != 1)
+  {
+    fprintf(stderr, "ERROR: backup image version %u is not supported\n",
+            version);
+    goto err;
+  }
+
+  /*
+    Set callback functions in struct st_backup_stream.
+  */
+  strm->bupstrm.stream.write=   (as_write_m) NULL;
+  strm->bupstrm.stream.read=    (as_read_m) str_read;
+  strm->bupstrm.stream.forward= (as_forward_m) str_forward;
+
+  /*
+    Open the stream.
+  */
+  DBUG_PRINT("bupstrm", ("opening stream: '%s'  fd: %d", strm->path, strm->fd));
+  rc= bstream_open_rd(&strm->bupstrm, sizeof(prefix));
+  DBUG_PRINT("bupstrm", ("opened  stream: '%s'  fd: %d  rc: %d",
+                         strm->path, strm->fd, rc));
+
+  goto end;
+
+err:
+  switch (errpos) {
+  case 10:
+    (void) my_close(strm->fd, MYF(0));
+  }
+  rc= BSTREAM_ERROR;
+
+ end:
+  DBUG_RETURN(rc);
+}
+
+
+/**
+  Close the stream/image.
+
+  @param[in]    strm            stream handle
+
+  @return       status
+    @retval     BSTREAM_OK      ok
+    @retval     otherwise       error
+
+  @note The return value is specified as 'int' in stream_v1.h
+  though only values from enum_bstream_ret_codes are expected.
+*/
+
+static int
+str_close(struct st_stream *strm)
+{
+  int rc;
+  DBUG_ENTER("str_close");
+
+  /* Close the stream. */
+  rc= bstream_close(&strm->bupstrm);
+
+  /* Close the image file. */
+  if (my_close(strm->fd, MYF(0)))
+  {
+    fprintf(stderr, "ERROR: cannot close image '%s': %s\n",
+            strm->path, strerror(my_errno));
+    rc= BSTREAM_ERROR;
+  }
+
+  DBUG_RETURN(rc);
+}
+
+
+/*
+  ====================================================
+  Helper functions for backup image item handling.
+  These are callback functions for the stream library.
+  ====================================================
+*/
+
+/*
+  Helper struct for iterators.
+*/
+struct st_backup_iterator
+{
+  DYNAMIC_ARRAY                 *it_array;      /* Array to iterate */
+  int                           it_index;       /* Iterator value */
+  enum enum_bstream_item_type   it_type;        /* Iterator type */
+};
+
+
+/**
+  Clear catalogue and prepare it for populating with items.
+
+  @param[in]    hdr             catalog reference
+
+  @return       status
+    @retval     BSTREAM_OK      ok
+    @retval     otherwise       error
+
+  @note The return value is specified as 'int' in stream_v1.h
+  though only values from enum_bstream_ret_codes are expected.
+
+  @note This is empty because backup_catalog_allocate() initializes
+  the catalog properly.
+*/
+
+int
+bcat_reset(struct st_bstream_image_header *hdr __attribute__((unused)))
+{
+  DBUG_ENTER("bcat_reset");
+  DBUG_RETURN(BSTREAM_OK);
+}
+
+
+/**
+  Close catalogue after all items have been added to it.
+
+  This allows for finalizing operations. It is not meant for
+  deletion of the catalog. There is no "open" action. The
+  approximate counterpart to bcat_close() is bcat_reset().
+
+  @param[in]    hdr             catalog reference
+
+  @return       status
+    @retval     BSTREAM_OK      ok
+    @retval     otherwise       error
+
+  @note The return value is specified as 'int' in stream_v1.h
+  though only values from enum_bstream_ret_codes are expected.
+
+  @note This is empty because there is no finalization required.
+*/
+
+int
+bcat_close(struct st_bstream_image_header *hdr __attribute__((unused)))
+{
+  DBUG_ENTER("bcat_close");
+  DBUG_RETURN(BSTREAM_OK);
+}
+
+
+/**
+  Add item to the catalog.
+
+  For items that belong to a database, the base.db element points
+  to the databases' catalog item. The stream library evaluates
+  that pointer using an iterator provided by bcat_iterator_get().
+
+  The item name is allocated by the stream library and must be freed
+  by the application later.
+
+  @param[in,out]    hdr         catalog ref, updating catalog
+  @param[in]        item        item->pos should be set to indicate position
+                                of the item in the catalogue
+
+  @return       status
+    @retval     BSTREAM_OK      ok
+    @retval     otherwise       error
+
+  @note The return value is specified as 'int' in stream_v1.h
+  though only values from enum_bstream_ret_codes are expected.
+
+  @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 *hdr,
+              struct st_bstream_item_info *item)
+{
+  struct st_backup_catalog      *bup_catalog= (struct st_backup_catalog*) hdr;
+  enum enum_bstream_ret_codes   rc= BSTREAM_OK;
+  DBUG_ENTER("bcat_add_item");
+  DBUG_PRINT("bupstrm", ("adding item: 0x%lx  pos: %lu  type: %d  name: '%s",
+                         (long) item, item->pos, item->type,
+                         item->name.begin));
+
+  switch (item->type) {
+  case BSTREAM_IT_CHARSET:
+  {
+    struct st_backup_charset bup_charset;
+
+    /* Copy the item struct into the local struct. */
+    bup_charset.cs_item= *item;
+    /* Check consistency of array position. */
+    DBUG_ASSERT(item->pos == bup_catalog->cat_charsets.elements);
+    /* Insert into the catalog. */
+    if (insert_dynamic(&bup_catalog->cat_charsets, (uchar*) &bup_charset))
+    {
+      rc= BSTREAM_ERROR;
+      goto end;
+    }
+    DBUG_PRINT("bupstrm", ("added charset: '%s'  item: 0x%lx",
+                           item->name.begin,
+                           (long) dynamic_array_ptr(&bup_catalog->cat_charsets,
+                                                    item->pos)));
+    break;
+  }
+
+  case BSTREAM_IT_USER:
+  {
+    DBUG_PRINT("bupstrm", ("adding user: '%s'", item->name.begin));
+    break;
+  }
+
+  case BSTREAM_IT_PRIVILEGE:
+  {
+    DBUG_PRINT("bupstrm", ("adding privilege: '%s'", item->name.begin));
+    break;
+  }
+
+  case BSTREAM_IT_DB:
+  {
+    struct st_backup_database bup_database;
+
+    /* Copy the item struct into the local struct. */
+    bup_database.db_item= *((struct st_bstream_db_info*) item);
+    /* Initialize other elements. */
+    if (my_init_dynamic_array(&bup_database.db_tables,
+                              sizeof(struct st_backup_table), 16, 16))
+    {
+      rc= BSTREAM_ERROR;
+      goto end;
+    }
+    /* Check consistency of array position. */
+    DBUG_ASSERT(item->pos == bup_catalog->cat_databases.elements);
+    /* Insert into the catalog. */
+    if (insert_dynamic(&bup_catalog->cat_databases, (uchar*) &bup_database))
+    {
+      rc= BSTREAM_ERROR;
+      goto end;
+    }
+    DBUG_PRINT("bupstrm", ("added database: '%s'  item: 0x%lx",
+                           item->name.begin,
+                           (long) dynamic_array_ptr(&bup_catalog->cat_databases,
+                                                    item->pos)));
+    break;
+  }
+
+  case BSTREAM_IT_VIEW:
+  {
+    DBUG_PRINT("bupstrm", ("adding view: '%s'", item->name.begin));
+    break;
+  }
+
+  case BSTREAM_IT_TABLE:
+  {
+    struct st_backup_table      bup_table;
+    struct st_backup_database   *bup_database;
+
+    /* Copy the item struct into the local struct. */
+    bup_table.tbl_item= *((struct st_bstream_table_info*) item);
+    bup_database= (struct st_backup_database*) bup_table.tbl_item.base.db;
+    DBUG_PRINT("bupstrm",
+               ("referred db item: 0x%lx  pos: %lu  type: %d  name: '%s'",
+                (long) bup_database, bup_database->db_item.base.pos,
+                bup_database->db_item.base.type,
+                bup_database->db_item.base.name.begin));
+    /* Check consistency of array position. */
+    DBUG_ASSERT(item->pos == bup_database->db_tables.elements);
+    /* Insert into its database's array. */
+    if (insert_dynamic(&bup_database->db_tables, (uchar*) &bup_table))
+    {
+      rc= BSTREAM_ERROR;
+      goto end;
+    }
+    IF_DBUG({
+        struct st_blob *db_name= &bup_database->db_item.base.name;
+        DBUG_PRINT("bupstrm",
+                   ("added table: '%s'.'%s'  item: 0x%lx",
+                    db_name->begin, item->name.begin,
+                    (long) dynamic_array_ptr(&bup_database->db_tables,
+                                             item->pos)));
+        });
+    break;
+  }
+
+  default:
+  {
+    DBUG_PRINT("bupstrm", ("adding unknown item type: '%s'", item->name.begin));
+    rc= BSTREAM_ERROR;
+  }
+  }
+
+ end:
+  DBUG_RETURN(rc);
+}
+
+
+/**
+  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.
+
+  @param[in]    hdr             catalog reference
+  @param[in]    it_type         iterator type
+
+  @return       pointer to the iterator
+    @retval     NULL            error
+*/
+
+void*
+bcat_iterator_get(struct st_bstream_image_header *hdr, unsigned int it_type)
+{
+  struct st_backup_catalog      *bup_catalog= (struct st_backup_catalog*) hdr;
+  struct st_backup_iterator     *iter;
+  DBUG_ENTER("bcat_iterator_get");
+  DBUG_PRINT("bupstrm", ("getting iterator for type: %u", it_type));
+
+  if (!(iter= my_malloc(sizeof(struct st_backup_iterator), MYF(MY_WME))))
+    goto end;
+  iter->it_index= -1;
+  iter->it_type= it_type;
+
+  switch (it_type) {
+  case BSTREAM_IT_CHARSET:
+    iter->it_array= &bup_catalog->cat_charsets;
+    DBUG_PRINT("bupstrm", ("charset"));
+    break;
+
+  case BSTREAM_IT_USER:
+    DBUG_PRINT("bupstrm", ("user"));
+    break;
+
+  case BSTREAM_IT_DB:
+    iter->it_array= &bup_catalog->cat_databases;
+    DBUG_PRINT("bupstrm", ("database"));
+    break;
+
+  case BSTREAM_IT_GLOBAL:
+    DBUG_PRINT("bupstrm", ("global"));
+    break;
+
+  case BSTREAM_IT_PERDB:
+    DBUG_PRINT("bupstrm", ("perdb"));
+    break;
+
+  default:
+    my_free(iter, MYF(0));
+    iter= NULL;
+    DBUG_PRINT("bupstrm", ("unknown"));
+  }
+
+ end:
+  DBUG_PRINT("bupstrm", ("iterator: 0x%lx", (long) iter));
+  DBUG_RETURN((void*) iter);
+}
+
+
+/**
+  Return next item pointed by iterator.
+
+  @param[in]    hdr             catalog reference
+  @param[in]    iter_arg        iterator reference
+
+  @return       pointer to catalog item
+    @retval     NULL            error
+*/
+
+struct st_bstream_item_info*
+bcat_iterator_next(struct st_bstream_image_header *hdr __attribute__((unused)),
+                   void *iter_arg)
+{
+  struct st_backup_iterator     *iter= (struct st_backup_iterator*) iter_arg;
+  struct st_bstream_item_info   *item= NULL;
+  DBUG_ENTER("bcat_iterator_next");
+  DBUG_PRINT("bupstrm", ("iter: 0x%lx", (long) iter));
+  DBUG_ASSERT(iter);
+  DBUG_ASSERT(iter->it_array);
+
+  /* Check for end of array. */
+  if (++iter->it_index >= (int) iter->it_array->elements)
+    goto end;
+  item= (struct st_bstream_item_info*) dynamic_array_ptr(iter->it_array,
+                                                         iter->it_index);
+
+  DBUG_PRINT("bupstrm", ("next iterator for type: %u", iter->it_type));
+  DBUG_PRINT("bupstrm", ("next item: 0x%lx  pos: %lu  type: %d  name: '%s'",
+                         (long) item, item->pos, item->type,
+                         item->name.begin));
+
+  switch (iter->it_type) {
+  case BSTREAM_IT_CHARSET:
+    DBUG_PRINT("bupstrm", ("charset"));
+    break;
+
+  case BSTREAM_IT_USER:
+    DBUG_PRINT("bupstrm", ("user"));
+    break;
+
+  case BSTREAM_IT_DB:
+    DBUG_PRINT("bupstrm", ("database"));
+    break;
+
+  case BSTREAM_IT_GLOBAL:
+    DBUG_PRINT("bupstrm", ("global"));
+    break;
+
+  case BSTREAM_IT_PERDB:
+    DBUG_PRINT("bupstrm", ("perdb"));
+    break;
+
+  default:
+    item= NULL;
+    DBUG_PRINT("bupstrm", ("unknown"));
+    DBUG_ASSERT(0);
+  }
+
+ end:
+  DBUG_PRINT("bupstrm", ("item: 0x%lx", (long) item));
+  DBUG_RETURN(item);
+}
+
+
+/**
+  Free iterator resources.
+
+  @param[in]    hdr             catalog reference
+  @param[in]    iter_arg        iterator reference
+
+  @note
+  The iterator can not be used after call to this function.
+*/
+
+void
+bcat_iterator_free(struct st_bstream_image_header *hdr __attribute__((unused)),
+                   void *iter_arg)
+{
+  DBUG_ENTER("bcat_iterator_free");
+  my_free(iter_arg, MYF(0));
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Create iterator for items belonging to a given database.
+
+  @param[in]    hdr             catalog reference
+  @param[in]    db              database item reference
+
+  @return       pointer to the iterator
+    @retval     NULL            error
+
+  @note Not used when reading a backup stream.
+*/
+
+void*
+bcat_db_iterator_get(struct st_bstream_image_header *hdr
+                     __attribute__((unused)),
+                     struct st_bstream_db_info *db
+                     __attribute__((unused)))
+{
+  DBUG_ENTER("bcat_db_iterator_get");
+  DBUG_ASSERT(0);
+  DBUG_RETURN(NULL);
+}
+
+
+/**
+  Return next item from database items iterator
+
+  @param[in]    hdr             catalog reference
+  @param[in]    db              database item reference
+  @param[in]    iter_arg        iterator reference
+
+  @return       pointer to catalog item
+    @retval     NULL            error
+
+  @note Not used when reading a backup stream.
+*/
+
+struct st_bstream_dbitem_info*
+bcat_db_iterator_next(struct st_bstream_image_header *hdr
+                      __attribute__((unused)),
+                      struct st_bstream_db_info *db
+                      __attribute__((unused)),
+                      void *iter_arg
+                      __attribute__((unused)))
+{
+  DBUG_ENTER("bcat_db_iterator_next");
+  DBUG_ASSERT(0);
+  DBUG_RETURN(NULL);
+}
+
+
+/**
+  Free database items iterator resources
+
+  @param[in]    hdr             catalog reference
+  @param[in]    db              database item reference
+  @param[in]    iter_arg        iterator reference
+
+  @note Not used when reading a backup stream.
+*/
+
+void
+bcat_db_iterator_free(struct st_bstream_image_header *hdr
+                      __attribute__((unused)),
+                      struct st_bstream_db_info *db
+                      __attribute__((unused)),
+                      void *iter_arg
+                      __attribute__((unused)))
+{
+  DBUG_ENTER("bcat_db_iterator_free");
+  DBUG_ASSERT(0);
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Produce CREATE statement for a given item.
+
+  Backup stream library calls this function when saving item's
+  meta-data. If function successfully produces the statement, it becomes
+  part of meta-data.
+
+  @param[in]    hdr             catalog reference
+  @param[in]    item            item reference
+  @param[out]   query           query string
+
+  @return       status
+    @retval     BSTREAM_OK      ok
+    @retval     otherwise       error
+
+  @note The return value is specified as 'int' in stream_v1.h
+  though only values from enum_bstream_ret_codes are expected.
+
+  @note Not used when reading a backup stream.
+*/
+
+int
+bcat_get_item_create_query(struct st_bstream_image_header *hdr
+                           __attribute__((unused)),
+                           struct st_bstream_item_info *item
+                           __attribute__((unused)),
+                           bstream_blob *query
+                           __attribute__((unused)))
+{
+  DBUG_ENTER("bcat_get_item_create_query");
+  DBUG_ASSERT(0);
+  DBUG_RETURN(BSTREAM_OK);
+}
+
+
+/**
+  Return meta-data (other than CREATE statement) for a given item.
+
+  Backup stream library calls this function when saving item's
+  meta-data. If function returns successfully, the bytes returned become
+  part of meta-data.
+
+  @param[in]    hdr             catalog reference
+  @param[in]    item            item reference
+  @param[out]   data            data blob
+
+  @return       status
+    @retval     BSTREAM_OK      ok
+    @retval     otherwise       error
+
+  @note The return value is specified as 'int' in stream_v1.h
+  though only values from enum_bstream_ret_codes are expected.
+
+  @note Not used when reading a backup stream.
+*/
+
+int
+bcat_get_item_create_data(struct st_bstream_image_header *hdr
+                          __attribute__((unused)),
+                          struct st_bstream_item_info *item
+                          __attribute__((unused)),
+                          struct st_blob *data
+                          __attribute__((unused)))
+{
+  DBUG_ENTER("bcat_get_item_create_data");
+  DBUG_ASSERT(0);
+  DBUG_RETURN(BSTREAM_ERROR);
+}
+
+
+/**
+  Create database object from its meta-data.
+
+  When the meta-data section of backup image is read, items can be created
+  as their meta-data is read (so that there is no need to store these
+  meta-data). This functions stores them in the catalog instead of
+  creating database objects. So the application can make different use
+  of the data.
+
+  @param[in]    hdr             catalog reference
+  @param[in]    item            item reference
+  @param[in]    query           query string
+  @param[in]    data            data blob
+
+  @note The item has set the 'type' element only. No item name nor
+  a catalog position is provided. Let alone a reference to a database.
+
+  @note Either query or data or both can be empty, depending
+  on what was stored in the image.
+
+  @note The blob provided by query and/or data is not guaranteed to
+  exist after the call. It must be copied to become part of the catalog.
+
+  @return       status
+    @retval     BSTREAM_OK      ok
+    @retval     otherwise       error
+
+  @note The return value is specified as 'int' in stream_v1.h
+  though only values from enum_bstream_ret_codes are expected.
+*/
+
+int
+bcat_create_item(struct st_bstream_image_header *hdr,
+                 struct st_bstream_item_info *item,
+                 struct st_blob query,
+                 struct st_blob data)
+{
+  struct st_backup_catalog      *bup_catalog= (struct st_backup_catalog*) hdr;
+  struct st_backup_metadata     mdata;
+  enum enum_bstream_ret_codes   rc= BSTREAM_OK;
+  DBUG_ENTER("bcat_create_item");
+  DBUG_PRINT("bupstrm", ("item: 0x%lx  pos: %lu  type: %d  name: '%s'",
+                         (long) item, item->pos, item->type,
+                         item->name.begin ? (const char*) item->name.begin :
+                         "[NULL]"));
+  DBUG_PRINT("bupstrm", ("query: '%s'", query.begin));
+  DBUG_PRINT("bupstrm", ("data length: %lu", (ulong) (data.end - data.begin)));
+
+  /*
+    Create new st_blob structs. The strings from the
+    stream libraray have a short life time.
+  */
+  mdata.md_query.begin= (query.begin ?
+                         my_memdup(query.begin,
+                                   (size_t) (query.end - query.begin),
+                                   MYF(MY_WME)) : NULL);
+  mdata.md_query.end= (mdata.md_query.begin ? mdata.md_query.begin +
+                       (query.end - query.begin) : NULL);
+
+  mdata.md_data.begin= (data.begin ?
+                         my_memdup(data.begin,
+                                   (size_t) (data.end - data.begin),
+                                   MYF(MY_WME)) : NULL);
+  mdata.md_data.end= (mdata.md_data.begin ? mdata.md_data.begin +
+                      (data.end - data.begin) : NULL);
+
+  DBUG_PRINT("bupstrm", ("query.begin: 0x%lx  data.begin: 0x%lx",
+                         (long) mdata.md_query.begin,
+                         (long) mdata.md_data.begin));
+
+  if ((query.begin && !mdata.md_query.begin) ||
+      (data.begin && !mdata.md_data.begin))
+  {
+    /* memory allocation failed. */
+    rc= BSTREAM_ERROR;
+    goto end;
+  }
+
+  switch (item->type) {
+  case BSTREAM_IT_DB:
+  {
+    DBUG_PRINT("bupstrm", ("database"));
+    if (insert_dynamic(&bup_catalog->cat_metadata_databases,
+                       (uchar*) &mdata))
+    {
+      rc= BSTREAM_ERROR;
+      goto end;
+    }
+    break;
+  }
+  case BSTREAM_IT_TABLE:
+  {
+    DBUG_PRINT("bupstrm", ("table"));
+    if (insert_dynamic(&bup_catalog->cat_metadata_tables,
+                       (uchar*) &mdata))
+    {
+      rc= BSTREAM_ERROR;
+      goto end;
+    }
+    break;
+  }
+  case BSTREAM_IT_PRIVILEGE:
+  {
+    DBUG_PRINT("bupstrm", ("privilege"));
+    break;
+  }
+  default:
+  {
+    DBUG_PRINT("bupstrm", ("unknown"));
+    rc= BSTREAM_ERROR;
+  }
+  }
+
+ end:
+  DBUG_RETURN(rc);
+}
+
+
+/*
+  ========================================
+  Functions for reading of a backup image.
+  ========================================
+*/
+
+/**
+  Allocate a backup catalog.
+
+  @return       catalog reference
+    @retval     NULL            error
+*/
+
+struct st_backup_catalog*
+backup_catalog_allocate(void)
+{
+  struct st_backup_catalog      *bup_catalog;
+  int                           errpos= 0;
+  DBUG_ENTER("backup_catalog_allocate");
+
+  bup_catalog= my_malloc(sizeof(struct st_backup_catalog),
+                         MYF(MY_WME | MY_ZEROFILL));
+  if (!bup_catalog)
+    goto err;
+  errpos= 10;
+
+  if (my_init_dynamic_array(&bup_catalog->cat_charsets,
+                            sizeof(struct st_backup_charset), 16, 16))
+    goto err;
+  errpos= 20;
+
+  if (my_init_dynamic_array(&bup_catalog->cat_databases,
+                            sizeof(struct st_backup_database), 16, 16))
+    goto err;
+  errpos= 30;
+
+  if (my_init_dynamic_array(&bup_catalog->cat_metadata_databases,
+                            sizeof(struct st_backup_charset), 16, 16))
+    goto err;
+  errpos= 40;
+
+  if (my_init_dynamic_array(&bup_catalog->cat_metadata_tables,
+                            sizeof(struct st_backup_charset), 16, 16))
+    goto err;
+  errpos= 50;
+
+  goto end;
+
+ err:
+  switch(errpos) {
+  case 50:
+    delete_dynamic(&bup_catalog->cat_metadata_tables);
+  case 40:
+    delete_dynamic(&bup_catalog->cat_metadata_databases);
+  case 30:
+    delete_dynamic(&bup_catalog->cat_databases);
+  case 20:
+    delete_dynamic(&bup_catalog->cat_charsets);
+  case 10:
+    my_free(bup_catalog, MYF(0));
+    bup_catalog= NULL;
+  }
+
+ end:
+  DBUG_RETURN(bup_catalog);
+}
+
+
+/**
+  Free a backup catalog.
+
+  @param[in]    bup_catalog             catalog reference
+*/
+
+void
+backup_catalog_free(struct st_backup_catalog *bup_catalog)
+{
+  struct st_bstream_image_header        *hdr= &bup_catalog->cat_header;
+  struct st_backup_metadata             *mdata;
+  uint                                  idx;
+  DBUG_ENTER("backup_catalog_free");
+
+  /* Table meta data. */
+  for (idx= 0; idx < bup_catalog->cat_metadata_tables.elements; idx++)
+  {
+    mdata= (struct st_backup_metadata*)
+      dynamic_array_ptr(&bup_catalog->cat_metadata_tables, idx);
+    my_free(mdata->md_data.begin, MYF(MY_ALLOW_ZERO_PTR));
+    my_free(mdata->md_query.begin, MYF(MY_ALLOW_ZERO_PTR));
+  }
+  delete_dynamic(&bup_catalog->cat_metadata_tables);
+
+  /* Database meta data. */
+  for (idx= 0; idx < bup_catalog->cat_metadata_databases.elements; idx++)
+  {
+    mdata= (struct st_backup_metadata*)
+      dynamic_array_ptr(&bup_catalog->cat_metadata_databases, idx);
+    my_free(mdata->md_data.begin, MYF(MY_ALLOW_ZERO_PTR));
+    my_free(mdata->md_query.begin, MYF(MY_ALLOW_ZERO_PTR));
+  }
+  delete_dynamic(&bup_catalog->cat_metadata_databases);
+
+  /* Databases and contained objects. */
+  for (idx= 0; idx < bup_catalog->cat_databases.elements; idx++)
+  {
+    struct st_backup_database   *dbase;
+
+    dbase= (struct st_backup_database*)
+      dynamic_array_ptr(&bup_catalog->cat_databases, idx);
+
+    /* Tables. */
+    for (idx= 0; idx < dbase->db_tables.elements; idx++)
+    {
+      struct st_backup_table    *tbl;
+
+      tbl= (struct st_backup_table*)
+        dynamic_array_ptr(&dbase->db_tables, idx);
+      my_free(tbl->tbl_item.base.base.name.begin, MYF(MY_ALLOW_ZERO_PTR));
+    }
+    delete_dynamic(&dbase->db_tables);
+
+    my_free(dbase->db_item.base.name.begin, MYF(MY_ALLOW_ZERO_PTR));
+  }
+  delete_dynamic(&bup_catalog->cat_databases);
+
+  /* Character sets. */
+  for (idx= 0; idx < bup_catalog->cat_charsets.elements; idx++)
+  {
+    struct st_backup_charset    *chrset;
+
+    chrset= (struct st_backup_charset*)
+      dynamic_array_ptr(&bup_catalog->cat_charsets, idx);
+
+    my_free(chrset->cs_item.name.begin, MYF(MY_ALLOW_ZERO_PTR));
+  }
+  delete_dynamic(&bup_catalog->cat_charsets);
+
+  /* Header, snapshots. */
+  for (idx= 0; idx < hdr->snap_count; ++idx)
+    bstream_free(hdr->snapshot[idx].engine.name.begin);
+
+  /* Header, server version. */
+  if (hdr->server_version.extra.begin)
+    bstream_free(hdr->server_version.extra.begin);
+
+  /* Catalog. */
+  my_free(bup_catalog, MYF(0));
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Open a backup image.
+
+  @param[in]    filename                file name
+  @param[in]    bup_catalog             catalog reference
+
+  @return       image handle reference
+    @retval     NULL                    error
+*/
+
+void*
+backup_image_open(const char *filename, struct st_backup_catalog *bup_catalog)
+{
+  struct st_stream      *strm;
+  int                   rc;
+  int                   errpos= 0;
+  DBUG_ENTER("backup_image_open");
+
+  strm= my_malloc(sizeof(struct st_stream),
+                  MYF(MY_WME | MY_ZEROFILL));
+  if (!strm)
+    goto err;
+  errpos= 10;
+
+  /*
+    Open stream.
+  */
+  rc= str_open_rd(strm, filename);
+  DBUG_PRINT("bupstrm", ("Opened backup image file (rc=%d)", rc));
+  if (rc != BSTREAM_OK)
+    goto err;
+  strm->stream_pos= "prefix";
+  errpos= 20;
+
+  /*
+    Read backup image stream header.
+  */
+  rc= bstream_rd_header(&strm->bupstrm, &bup_catalog->cat_header);
+  DBUG_PRINT("bupstrm", ("Read archive header (rc=%d)", rc));
+  if ((rc != BSTREAM_OK) && (rc != BSTREAM_EOC))
+    goto err;
+  strm->stream_pos= "header";
+
+  goto end;
+
+ err:
+  switch (errpos) {
+  case 20:
+    (void) str_close(strm);
+  case 10:
+    my_free(strm, MYF(MY_ALLOW_ZERO_PTR));
+    strm= NULL;
+  }
+
+ end:
+  DBUG_RETURN(strm);
+}
+
+
+/**
+  Close a backup image.
+
+  @param[in]    image_handle            image handle reference
+*/
+
+void
+backup_image_close(void* image_handle)
+{
+  struct st_stream *strm= (struct st_stream*) image_handle;
+  DBUG_ENTER("backup_image_close");
+  (void) str_close(strm);
+  my_free(strm, MYF(0));
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Read backup image catalog.
+
+  @param[in]    image_handle            image handle reference
+  @param[in]    bup_catalog             catalog reference
+
+  @return       status
+    @retval     BSTREAM_OK              ok
+    @retval     != BSTREAM_OK           error
+*/
+
+enum enum_bstream_ret_codes
+backup_read_catalog(void* image_handle, struct st_backup_catalog *bup_catalog)
+{
+  struct st_stream *strm= (struct st_stream*) image_handle;
+  enum enum_bstream_ret_codes rc;
+  DBUG_ENTER("backup_read_catalog");
+
+  /*
+    Read catalog requires search of next chunk start.
+  */
+  rc= bstream_next_chunk(&strm->bupstrm);
+  DBUG_PRINT("bupstrm", ("Find next chunk (rc=%d)", rc));
+  if (rc != BSTREAM_OK)
+  {
+    if (rc == BSTREAM_EOS)
+    {
+      fprintf(stderr, "ERROR: end of stream after %s.\n", strm->stream_pos);
+      goto end;
+    }
+    fprintf(stderr, "ERROR: cannot find catalog after %s.\n", strm->stream_pos);
+    goto end;
+  }
+
+  /*
+    Read catalog.
+  */
+  rc= bstream_rd_catalogue(&strm->bupstrm, &bup_catalog->cat_header);
+  DBUG_PRINT("bupstrm", ("Read archive catalogue (rc=%d)", rc));
+  if ((rc != BSTREAM_OK) && (rc != BSTREAM_EOC))
+  {
+    if (rc == BSTREAM_EOS)
+    {
+      fprintf(stderr, "ERROR: end of stream within catalogue.\n");
+      goto end;
+    }
+    fprintf(stderr, "ERROR: cannot read catalogue.\n");
+    goto end;
+  }
+  strm->stream_pos= "catalog";
+  rc= BSTREAM_OK;
+
+ end:
+  DBUG_RETURN(rc);
+}
+
+
+/**
+  Read backup image meta data.
+
+  @param[in]    image_handle            image handle reference
+  @param[in]    bup_catalog             catalog reference
+
+  @return       status
+    @retval     BSTREAM_OK              ok
+    @retval     != BSTREAM_OK           error
+*/
+
+enum enum_bstream_ret_codes
+backup_read_metadata(void* image_handle, struct st_backup_catalog *bup_catalog)
+{
+  struct st_stream *strm= (struct st_stream*) image_handle;
+  enum enum_bstream_ret_codes rc;
+  DBUG_ENTER("backup_read_metadata");
+
+  /*
+    Read meta data requires search of next chunk start.
+  */
+  rc= bstream_next_chunk(&strm->bupstrm);
+  DBUG_PRINT("bupstrm", ("Find next chunk (rc=%d)", rc));
+  if (rc != BSTREAM_OK)
+  {
+    if (rc == BSTREAM_EOS)
+    {
+      fprintf(stderr, "ERROR: end of stream after %s.\n", strm->stream_pos);
+      goto end;
+    }
+    fprintf(stderr, "ERROR: cannot find meta data after %s.\n",
+            strm->stream_pos);
+    goto end;
+  }
+
+  /*
+    Read meta data.
+  */
+  rc= bstream_rd_meta_data(&strm->bupstrm, &bup_catalog->cat_header);
+  DBUG_PRINT("bupstrm", ("Read meta data (rc=%d)", rc));
+  if ((rc != BSTREAM_OK) && (rc != BSTREAM_EOC))
+  {
+    if (rc == BSTREAM_EOS)
+    {
+      fprintf(stderr, "ERROR: end of stream within meta data.\n");
+      goto end;
+    }
+    fprintf(stderr, "ERROR: cannot read meta data.\n");
+    goto end;
+  }
+  strm->stream_pos= "meta data";
+  rc= BSTREAM_OK;
+
+ end:
+  DBUG_RETURN(rc);
+}
+
+
+/**
+  Read backup image table data.
+
+  @param[in]    image_handle            image handle reference
+  @param[in]    bup_catalog             catalog reference
+
+  @return       status
+    @retval     BSTREAM_OK              ok
+    @retval     != BSTREAM_OK           error
+*/
+
+enum enum_bstream_ret_codes
+backup_read_snapshot(void* image_handle,
+                     struct st_backup_catalog *bup_catalog
+                      __attribute__((unused)),
+                     struct st_bstream_data_chunk *snapshot)
+{
+  struct st_stream *strm= (struct st_stream*) image_handle;
+  enum enum_bstream_ret_codes rc;
+  DBUG_ENTER("backup_read_snapshot");
+
+  /*
+    First read data chunk requires search of next chunk start.
+  */
+  if (!strcmp(strm->stream_pos, "meta data"))
+  {
+    rc= bstream_next_chunk(&strm->bupstrm);
+    DBUG_PRINT("bupstrm", ("Find next chunk (rc=%d)", rc));
+    if (rc != BSTREAM_OK)
+    {
+      if (rc == BSTREAM_EOS)
+      {
+        fprintf(stderr, "ERROR: end of stream after %s.\n", strm->stream_pos);
+        goto end;
+      }
+      fprintf(stderr, "ERROR: cannot find next chunk after %s.\n",
+              strm->stream_pos);
+      goto end;
+    }
+  }
+
+  /*
+    Read data chunk.
+  */
+  snapshot->data.begin= NULL;
+  snapshot->data.end= NULL;
+  rc= bstream_rd_data_chunk(&strm->bupstrm, snapshot);
+  DBUG_PRINT("bupstrm", ("Read data chunk (rc=%d)", rc));
+  if ((rc != BSTREAM_OK) && (rc != BSTREAM_EOC))
+  {
+    fprintf(stderr, "ERROR: cannot read snapshot.\n");
+    goto end;
+  }
+  strm->stream_pos= "table data";
+
+ end:
+  DBUG_RETURN(rc);
+}
+
+
+/**
+  Read backup image summary.
+
+  @param[in]    image_handle            image handle reference
+  @param[in]    bup_catalog             catalog reference
+
+  @return       status
+    @retval     BSTREAM_OK              ok
+    @retval     != BSTREAM_OK           error
+*/
+
+enum enum_bstream_ret_codes
+backup_read_summary(void* image_handle, struct st_backup_catalog *bup_catalog)
+{
+  struct st_stream *strm= (struct st_stream*) image_handle;
+  enum enum_bstream_ret_codes rc;
+  DBUG_ENTER("backup_read_summary");
+
+  rc= bstream_rd_summary(&strm->bupstrm, &bup_catalog->cat_header);
+  DBUG_PRINT("bupstrm", ("Read summary (rc=%d)", rc));
+  if (rc != BSTREAM_EOS)
+  {
+    if (rc == BSTREAM_ERROR)
+      fprintf(stderr, "ERROR: cannot read summary.\n");
+    else
+      fprintf(stderr, "ERROR: stream not at end after reading summary.\n");
+    rc= BSTREAM_ERROR;
+    goto end;
+  }
+  strm->stream_pos= "summary";
+  rc= BSTREAM_OK;
+
+ end:
+  DBUG_RETURN(rc);
+}
+
+

=== added file 'client/backup_stream.h'
--- a/client/backup_stream.h	1970-01-01 00:00:00 +0000
+++ b/client/backup_stream.h	2008-10-16 19:49:57 +0000
@@ -0,0 +1,121 @@
+/* Copyright (C) 2008 Sun Microsystems Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/*
+  MySQL Backup Image Stream reading
+*/
+
+/*
+  Include from mysys functions.
+*/
+#include "my_global.h"
+#include "my_sys.h"
+
+/*
+  Include from the low-level stream access functions.
+*/
+#include "stream_v1.h"
+#include "stream_v1_services.h"
+
+/*
+  =====================
+  Catalog declarations.
+  =====================
+*/
+
+/*
+  Meta data.
+*/
+struct st_backup_metadata
+{
+  struct st_blob                        md_query;
+  struct st_blob                        md_data;
+};
+
+/*
+  Catalog.
+*/
+struct st_backup_catalog
+{
+  struct st_bstream_image_header        cat_header;
+  DYNAMIC_ARRAY                         cat_charsets;
+  DYNAMIC_ARRAY                         cat_databases;
+
+  DYNAMIC_ARRAY                         cat_metadata_databases;
+  DYNAMIC_ARRAY                         cat_metadata_tables;
+};
+
+/*
+  Character set.
+*/
+struct st_backup_charset
+{
+  struct st_bstream_item_info           cs_item;
+};
+
+/*
+  Table.
+*/
+struct st_backup_table
+{
+  struct st_bstream_table_info          tbl_item;
+};
+
+/*
+  Database.
+*/
+struct st_backup_database
+{
+  struct st_bstream_db_info             db_item;
+  DYNAMIC_ARRAY                         db_tables;
+};
+
+/*
+  =========================
+  Stream reading functions.
+  =========================
+*/
+
+/* The below shall also be usable by C++. */
+C_MODE_START
+
+struct st_backup_catalog*
+backup_catalog_allocate(void);
+
+void
+backup_catalog_free(struct st_backup_catalog *bup_catalog);
+
+void*
+backup_image_open(const char *filename, struct st_backup_catalog *bup_catalog);
+
+void
+backup_image_close(void* image_handle);
+
+enum enum_bstream_ret_codes
+backup_read_catalog(void* image_handle, struct st_backup_catalog *bup_catalog);
+
+enum enum_bstream_ret_codes
+backup_read_metadata(void* image_handle, struct st_backup_catalog *bup_catalog);
+
+enum enum_bstream_ret_codes
+backup_read_snapshot(void* image_handle, struct st_backup_catalog *bup_catalog,
+                     struct st_bstream_data_chunk *snapshot);
+
+enum enum_bstream_ret_codes
+backup_read_summary(void* image_handle, struct st_backup_catalog *bup_catalog);
+
+/* The above shall also be usable by C++. */
+C_MODE_END
+

=== added file 'client/mysqlbackup.cc'
--- a/client/mysqlbackup.cc	1970-01-01 00:00:00 +0000
+++ b/client/mysqlbackup.cc	2008-10-16 19:49:57 +0000
@@ -0,0 +1,574 @@
+/* Copyright (C) 2001-2004 MySQL AB
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+/*
+  MySQL Backup Utility
+*/
+
+/* Include client related stuff, which includes a lot of common stuff. */
+#include "client_priv.h"
+/* Include special stuff. */
+#include <my_time.h>
+
+/* As long as we don't connect to the server, we are not a MYSQL_CLIENT. */
+#undef MYSQL_CLIENT
+
+/*
+  Include from the low-level stream access functions.
+*/
+#include "backup_stream.h"
+
+/*
+  Command line options.
+*/
+#ifdef MYSQL_CLIENT
+static const char       *opt_database;
+static const char       *opt_host;
+static char             *opt_pass;
+static int              opt_port;
+static uint             opt_protocol;
+static const char       *opt_sock;
+static const char       *opt_user;
+#endif
+static uint             opt_verbose;
+static ulong            opt_open_files_limit;
+
+#ifndef DBUG_OFF
+static const char *default_dbug_option = "d:t:o,/tmp/mysqlbackup.trace";
+#endif
+static const char *load_default_groups[]= { "mysqlbackup",
+#ifdef MYSQL_CLIENT
+                                            "client",
+#endif
+                                            NULL };
+
+static struct my_option my_long_options[] =
+{
+  {"help", '?', "Display this help and exit.",
+   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+#ifdef __NETWARE__
+  {"autoclose", OPT_AUTO_CLOSE, "Auto close the screen on exit for Netware.",
+   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+#ifndef DBUG_OFF
+  {"debug", '#', "Output debug log.", (uchar**) &default_dbug_option,
+   (uchar**) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+
+#ifdef MYSQL_CLIENT
+  /* Options required by a MySQL client (connecting to a server). */
+  {"database", 'd', "Unused.",
+   (uchar**) &opt_database, (uchar**) &opt_database,
+   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"host", 'h', "Unused.",
+   (uchar**) &opt_host, (uchar**) &opt_host,
+   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"password", 'p', "Unused.",
+   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+  {"port", 'P', "Unused.",
+   (uchar**) &opt_port, (uchar**) &opt_port, 0, GET_INT, REQUIRED_ARG,
+   0, 0, 0, 0, 0, 0},
+  {"protocol", OPT_MYSQL_PROTOCOL, "Unused.",
+   0, 0, 0, GET_STR,  REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"socket", 'S', "Unused.",
+   (uchar**) &opt_sock, (uchar**) &opt_sock,
+   0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+  {"user", 'u', "Unused.",
+   (uchar**) &opt_user, (uchar**) &opt_user,
+   0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#endif /*MYSQL_CLIENT*/
+
+  {"verbose", 'v', "Print verbose information.",
+   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+  {"version", 'V', "Print version and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0,
+   0, 0, 0, 0, 0},
+  {"open_files_limit", OPT_OPEN_FILES_LIMIT,
+   "Used to reserve file descriptors for usage by this program.",
+   (uchar**) &opt_open_files_limit, (uchar**) &opt_open_files_limit,
+   0, GET_ULONG, REQUIRED_ARG, MY_NFILE, 8, OS_FILE_LIMIT, 0, 1, 0},
+  {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+#include <help_start.h>
+
+static void print_version()
+{
+  printf("%s Ver 1.0 for %s at %s\n", my_progname, SYSTEM_TYPE, MACHINE_TYPE);
+  NETWARE_SET_SCREEN_MODE(1);
+}
+
+
+static void usage()
+{
+  print_version();
+  puts("By Sun Microsystems, for your professional use\n\
+This software comes with NO WARRANTY:  This is free software,\n\
+and you are welcome to modify and redistribute it under the GPL license\n");
+
+  printf("Displays information from a backup image.\n\n");
+  printf("Usage: %s [options] backup-image-file\n", my_progname);
+  my_print_help(my_long_options);
+  my_print_variables(my_long_options);
+}
+
+#include <help_end.h>
+
+
+/**
+  Treat some options specially.
+
+  Called for each option that appears on the command line.
+*/
+
+extern "C" my_bool
+get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
+               char *argument)
+{
+#ifdef MYSQL_CLIENT
+  bool tty_password=0;
+#endif
+
+  switch (optid) {
+#ifdef __NETWARE__
+  case OPT_AUTO_CLOSE:
+    setscreenmode(SCR_AUTOCLOSE_ON_EXIT);
+    break;
+#endif
+#ifndef DBUG_OFF
+  case '#':
+    DBUG_PUSH(argument ? argument : default_dbug_option);
+    break;
+#endif
+
+#ifdef MYSQL_CLIENT
+  case 'p':
+    if (argument)
+    {
+      my_free(opt_pass,MYF(MY_ALLOW_ZERO_PTR));
+      char *start=argument;
+      opt_pass= my_strdup(argument,MYF(MY_FAE));
+      while (*argument) *argument++= 'x';		/* Destroy argument */
+      if (*start)
+        start[1]=0;				/* Cut length of argument */
+    }
+    else
+      tty_password=1;
+    break;
+  case OPT_MYSQL_PROTOCOL:
+    opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib,
+                                    opt->name);
+    break;
+#endif /*MYSQL_CLIENT*/
+
+  case 'v':
+    if (argument == disabled_my_option)
+      opt_verbose= 0;
+    else
+      opt_verbose++;
+    break;
+  case 'V':
+    print_version();
+    exit(0);
+  case '?':
+    usage();
+    exit(0);
+  }
+
+#ifdef MYSQL_CLIENT
+  if (tty_password)
+    opt_pass= get_tty_password(NullS);
+#endif /*MYSQL_CLIENT*/
+
+  return 0;
+}
+
+
+/**
+  Auxiliary function used by error() and warning().
+
+  Prints the given text (normally "WARNING: " or "ERROR: ") to stderr,
+  followed by the given vprintf-style string, followed by a newline.
+
+  @param[in]    format          printf-style format string
+  @param[in]    args            list of arguments for the format string
+  @param[in]    msg             text to print before the string
+*/
+
+static void error_or_warning(const char *format, const char *msg, va_list args)
+{
+  fprintf(stderr, "%s: ", msg);
+  vfprintf(stderr, format, args);
+  fprintf(stderr, "\n");
+}
+
+
+/**
+  Print a message to stderr, prefixed with the text "ERROR: " and
+  suffixed with a newline.
+
+  @param[in]    format          printf-style format string
+  @param[in]    ...             printf-style varargs
+*/
+
+void error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+void error(const char *format, ...)
+{
+  va_list args;
+  va_start(args, format);
+  error_or_warning(format, "ERROR", args);
+  va_end(args);
+}
+
+
+/**
+  Print a message to stderr, prefixed with the text "WARNING: " and
+  suffixed with a newline.
+
+  @param[in]    format          printf-style format string
+  @param[in]    ...             printf-style varargs
+*/
+void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
+void warning(const char *format, ...)
+{
+  va_list args;
+  va_start(args, format);
+  error_or_warning(format, "WARNING", args);
+  va_end(args);
+}
+
+
+/**
+  Print a formatted time string.
+*/
+
+int print_time(bstream_time_t *time)
+{
+  return printf("%02d-%02d-%04d %02d:%02d:%02d",
+    time->mday, time->mon, 1900 + time->year,
+    time->hour, time->min, time->sec);
+}
+
+
+/*
+  =============
+  Main program.
+  =============
+*/
+
+/**
+  Main
+
+  @parameter[in]        argc            argument count
+  @parameter[in]        argv            argument vector
+
+  @return               status
+    @retval             0               ok
+    @retval             != 0            error
+*/
+
+int main(int argc, char** argv)
+{
+  char                                  **defaults_argv;
+  void                                  *image_handle;
+  struct st_backup_catalog              *bup_catalog= NULL;
+  struct st_bstream_image_header        *hdr;
+  enum enum_bstream_ret_codes           brc;
+  int                                   rc;
+  uint                                  idx;
+  int                                   errpos= 0;
+
+  /* Preparations */
+  MY_INIT(argv[0]);
+  DBUG_ENTER("main");
+  DBUG_PROCESS(argv[0]);
+  my_init_time(); // for time functions
+  load_defaults("my", load_default_groups, &argc, &argv);
+  defaults_argv= argv;
+  errpos= 10;
+  rc= handle_options(&argc, &argv, my_long_options, get_one_option);
+  if (rc)
+    goto end;
+  errpos= 20;
+  if (argc != 1)
+  {
+    rc= 1;
+    goto use_err;
+  }
+  my_set_max_open_files(opt_open_files_limit);
+
+  /*
+    Allocate catalog.
+  */
+  bup_catalog= backup_catalog_allocate();
+  if (!bup_catalog)
+  {
+    rc= 2;
+    goto end;
+  }
+  errpos= 30;
+
+  /*
+    Open backup image stream.
+  */
+  image_handle= backup_image_open(argv[0], bup_catalog);
+  if (!image_handle)
+  {
+    rc= 2;
+    goto end;
+  }
+  errpos= 40;
+
+  /*
+    Print backup image stream header.
+  */
+  hdr= &bup_catalog->cat_header;
+  printf("\n");
+  printf("Image  version is %u\n", hdr->version);
+  printf("Server version is %d.%d.%d (%.*s)\n", hdr->server_version.major,
+         hdr->server_version.minor, hdr->server_version.release,
+         (int) (hdr->server_version.extra.end -
+                hdr->server_version.extra.begin),
+         (char*) hdr->server_version.extra.begin);
+  printf("Image options are %u\n", hdr->flags);
+  printf("Creation time  is ");
+  print_time(&hdr->start_time);
+  printf("\n");
+  printf("Validity time  is ");
+  print_time(&hdr->vp_time);
+  printf("\n");
+  printf("Finish   time  is ");
+  print_time(&hdr->end_time);
+  printf("\n");
+  printf("Binlog pos     is %s:%lu\n",
+         hdr->binlog_pos.file, hdr->binlog_pos.pos);
+  printf("Binlog group   is %s:%lu\n",
+         hdr->binlog_group.file, hdr->binlog_group.pos);
+  printf("Snapshot count is %u\n", hdr->snap_count);
+
+  printf("\n");
+
+  printf("Snapshots:\n");
+  printf("\n");
+  for (idx= 0; idx < hdr->snap_count; idx++)
+  {
+    struct st_bstream_snapshot_info *snapshot= hdr->snapshot + idx;
+    printf("Snapshot version %u\n", snapshot->version);
+    printf("Snapshot type    %d\n", snapshot->type);
+    printf("Snapshot options %u\n", snapshot->options);
+    printf("Snapshot tables  %lu\n", snapshot->table_count);
+    printf("Snapshot engine  '%.*s'  version %u.%u\n",
+           (int) (snapshot->engine.name.end - snapshot->engine.name.begin),
+           snapshot->engine.name.begin,
+           snapshot->engine.major, snapshot->engine.minor);
+    printf("\n");
+  }
+
+  /*
+    Read catalog.
+  */
+  brc= backup_read_catalog(image_handle, bup_catalog);
+  if (brc != BSTREAM_OK)
+  {
+    rc= 2;
+    goto end;
+  }
+
+  /*
+    Print catalog.
+  */
+  printf("Catalog:\n");
+  /* Charsets. */
+  {
+    struct st_backup_charset    *chrset;
+    struct st_blob              *name;
+
+    printf("  Character sets:\n");
+    for (idx= 0; idx < bup_catalog->cat_charsets.elements; idx++)
+    {
+      chrset= (struct st_backup_charset*)
+        dynamic_array_ptr(&bup_catalog->cat_charsets, idx);
+      name= &chrset->cs_item.name;
+      printf("    '%.*s'\n", (int) (name->end - name->begin), name->begin);
+      DBUG_PRINT("mysqlbackup",
+                 ("cs item: 0x%lx  pos: %lu  type: %d  name: '%s'",
+                  (long) chrset, chrset->cs_item.pos, chrset->cs_item.type,
+                  chrset->cs_item.name.begin));
+    }
+  }
+  /* Databases. */
+  {
+    struct st_backup_database   *dbase;
+    struct st_blob              *name;
+
+    printf("  Databases:\n");
+    for (idx= 0; idx < bup_catalog->cat_databases.elements; idx++)
+    {
+      dbase= (struct st_backup_database*)
+        dynamic_array_ptr(&bup_catalog->cat_databases, idx);
+      name= &dbase->db_item.base.name;
+      printf("    '%.*s'\n", (int) (name->end - name->begin), name->begin);
+      DBUG_PRINT("mysqlbackup",
+                 ("db item: 0x%lx  pos: %lu  type: %d  name: '%s'",
+                  (long) dbase, dbase->db_item.base.pos,
+                  dbase->db_item.base.type,
+                  dbase->db_item.base.name.begin));
+
+      /* Tables. */
+      {
+        struct st_backup_table  *tbl;
+        uint                    jdx;
+
+        printf("      Tables:\n");
+        for (jdx= 0; jdx < dbase->db_tables.elements; jdx++)
+        {
+          tbl= (struct st_backup_table*)
+            dynamic_array_ptr(&dbase->db_tables, jdx);
+          name= &tbl->tbl_item.base.base.name;
+          printf("        '%.*s'\n", (int) (name->end - name->begin),
+                 name->begin);
+          DBUG_PRINT("mysqlbackup",
+                     ("tb item: 0x%lx  pos: %lu  type: %d  name: '%s'",
+                      (long) tbl, tbl->tbl_item.base.base.pos,
+                      tbl->tbl_item.base.base.type,
+                      tbl->tbl_item.base.base.name.begin));
+        }
+      }
+    }
+  }
+  printf("\n");
+
+  /*
+    Read meta data.
+  */
+  brc= backup_read_metadata(image_handle, bup_catalog);
+  if (brc != BSTREAM_OK)
+  {
+    rc= 2;
+    goto end;
+  }
+
+  /*
+    Print meta data.
+  */
+  printf("Meta data:\n");
+  {
+    struct st_backup_metadata   *mdata;
+
+    printf("  Databases:\n");
+    for (idx= 0; idx < bup_catalog->cat_metadata_databases.elements; idx++)
+    {
+      mdata= (struct st_backup_metadata*)
+        dynamic_array_ptr(&bup_catalog->cat_metadata_databases, idx);
+      printf("    query: '%.*s'\n",
+             (int) (mdata->md_query.end - mdata->md_query.begin),
+             mdata->md_query.begin);
+      printf("    data length: %lu\n",
+             (ulong) (mdata->md_data.end - mdata->md_data.begin));
+      DBUG_PRINT("mysqlbackup",
+                 ("db metadata: 0x%lx  query.begin: 0x%lx  data.begin: 0x%lx",
+                  (long) mdata, (long) mdata->md_query.begin,
+                  (long) mdata->md_data.begin));
+    }
+
+    printf("  Tables:\n");
+    for (idx= 0; idx < bup_catalog->cat_metadata_tables.elements; idx++)
+    {
+      mdata= (struct st_backup_metadata*)
+        dynamic_array_ptr(&bup_catalog->cat_metadata_tables, idx);
+      printf("    query: '%.*s'\n",
+             (int) (mdata->md_query.end - mdata->md_query.begin),
+             mdata->md_query.begin);
+      printf("    data length: %lu\n",
+             (ulong) (mdata->md_data.end - mdata->md_data.begin));
+      DBUG_PRINT("mysqlbackup",
+                 ("tb metadata: 0x%lx  query.begin: 0x%lx  data.begin: 0x%lx",
+                  (long) mdata, (long) mdata->md_query.begin,
+                  (long) mdata->md_data.begin));
+    }
+  }
+  printf("\n");
+
+  /*
+    Read table data.
+  */
+  printf("Snapshot data:\n");
+  do
+  {
+    struct st_bstream_data_chunk snapshot;
+
+    brc= backup_read_snapshot(image_handle, bup_catalog, &snapshot);
+    if (brc != BSTREAM_OK)
+      break;
+    printf("  Snapshot %d has %lu bytes for table %lu\n",
+           snapshot.snap_num, snapshot.data.end - snapshot.data.begin,
+           snapshot.table_num);
+  } while (1);
+  printf("\n");
+
+  /*
+    Read summary.
+  */
+  brc= backup_read_summary(image_handle, bup_catalog);
+  if (brc != BSTREAM_OK)
+  {
+    rc= 2;
+    goto end;
+  }
+
+  printf("Summary:\n");
+  printf("Creation time  is ");
+  print_time(&hdr->start_time);
+  printf("\n");
+  printf("Validity time  is ");
+  print_time(&hdr->vp_time);
+  printf("\n");
+  printf("Finish   time  is ");
+  print_time(&hdr->end_time);
+  printf("\n");
+  printf("Binlog pos     is %s:%lu\n",
+         hdr->binlog_pos.file, hdr->binlog_pos.pos);
+  printf("Binlog group   is %s:%lu\n",
+         hdr->binlog_group.file, hdr->binlog_group.pos);
+
+  goto end;
+
+ use_err:
+  usage();
+  rc= 1;
+
+ end:
+  /* Cleanup. */
+  switch (errpos) {
+  default:
+  case 40:
+    backup_image_close(image_handle);
+  case 30:
+    backup_catalog_free(bup_catalog);
+  case 20:
+#ifdef MYSQL_CLIENT
+    my_free((char*) opt_database, MYF(MY_ALLOW_ZERO_PTR));
+    my_free((char*) opt_host, MYF(MY_ALLOW_ZERO_PTR));
+    my_free((char*) opt_pass, MYF(MY_ALLOW_ZERO_PTR));
+    my_free((char*) opt_user, MYF(MY_ALLOW_ZERO_PTR));
+#endif
+  case 10:
+    free_defaults(defaults_argv);
+  }
+  my_free_open_file_info();
+  /* We cannot free DBUG, it is used in global destructors after exit(). */
+  my_end(MY_DONT_FREE_DBUG);
+  DBUG_RETURN(rc);
+}
+

=== modified file 'mysql-test/t/backup_myisam2.test'
--- a/mysql-test/t/backup_myisam2.test	2008-07-14 15:33:03 +0000
+++ b/mysql-test/t/backup_myisam2.test	2008-10-16 19:49:57 +0000
@@ -78,6 +78,7 @@ SET DEBUG_SYNC= 'now SIGNAL bup_finish';
 --echo
 --echo connection default: cleanup
 connection default;
+--copy_file $MYSQLTEST_VARDIR/master-data/test.ba /tmp/wl4534-test-1.bak
 --remove_file $MYSQLTEST_VARDIR/master-data/test.ba
 SET DEBUG_SYNC= 'RESET';
 

Thread
bzr commit into mysql-6.0-backup branch (ingo.struewing:2698) WL#4534Ingo Struewing16 Oct