/* Copy store backend

   Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.

   Written by Miles Bader <miles@gnu.ai.mit.edu>

   This task is part of the GNU Hurd.

   The GNU Hurd is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2, or (at
   your option) any later version.

   The GNU Hurd 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, USA. */

#include <stdio.h>
#include <string.h>
#include <malloc.h>

#include "store.h"

static error_t
copy_read (struct store *store,
	   off_t addr, size_t index, size_t amount, void **buf, size_t *len)
{
  if (*len < amount)
    /* Have to allocate memory for the return value.  */
    {
      error_t err =
	vm_allocate (mach_task_self (), (vm_address_t *)buf, amount, 1);
      if (err)
	return err;
    }

  bcopy (store->hook + (addr * store->block_size), *buf, amount);
  *len = amount;
  return 0;
}

static error_t
copy_write (struct store *store,
	    off_t addr, size_t index, void *buf, size_t len, size_t *amount)
{
  bcopy (buf, store->hook + (addr * store->block_size), len);
  *amount = len;
  return 0;
}

error_t
copy_allocate_encoding (const struct store *store, struct store_enc *enc)
{
  /* ... */
  return EOPNOTSUPP;
}

error_t
copy_encode (const struct store *store, struct store_enc *enc)
{
  /* ... */
  return EOPNOTSUPP;
}

static error_t
copy_decode (struct store_enc *enc, const struct store_class *const *classes,
	     struct store **store)
{
  /* ... */
  return EOPNOTSUPP;
}

static error_t
copy_open (const char *name, int flags,
	   const struct store_class *const *classes,
	   struct store **store)
{
  return store_copy_open (name, flags, classes, store);
}

static error_t
copy_set_flags (struct store *store, int flags)
{
  if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
    /* Trying to set flags we don't support.  */
    return EINVAL;

  /* ... */

  store->flags |= flags;	/* When inactive, anything goes.  */

  return 0;
}

static error_t
copy_clear_flags (struct store *store, int flags)
{
  error_t err = 0;
  if ((flags & ~(STORE_INACTIVE | STORE_ENFORCED)) != 0)
    err = EINVAL;
  /* ... */
  if (! err)
    store->flags &= ~flags;
  return err;
}

/* Called just before deallocating STORE.  */
void
copy_cleanup (struct store *store)
{
  if (store->size > 0)
    vm_deallocate (mach_task_self (), (vm_address_t)store->hook, store->size);
}

/* Copy any format-dependent fields in FROM to TO; if there's some reason
   why the copy can't be made, an error should be returned.  This call is
   made after all format-indendependent fields have been cloned.  */
error_t
copy_clone (const struct store *from, struct store *to)
{
  error_t err =
    vm_allocate (mach_task_self (), (vm_address_t *)&to->hook, to->size, 1);
  if (! err)
    bcopy (from->hook, to->hook, from->size);
  return err;
}

struct store_class
store_copy_class =
{
  STORAGE_COPY, "copy", copy_read, copy_write,
  copy_allocate_encoding, copy_encode, copy_decode,
  copy_set_flags, copy_clear_flags, copy_cleanup, copy_clone, 0, copy_open
};

/* Return a new store in STORE which contains a snapshot of the contents of
   the store FROM; FROM is consumed.  */
error_t
store_copy_create (struct store *from, int flags, struct store **store)
{
  error_t err;
  struct store_run run;

  run.start = 0;
  run.length = from->size;

  flags |= STORE_ENFORCED;	/* Only uses local resources.  */

  err =
    _store_create (&store_copy_class,
		   MACH_PORT_NULL, flags, from->block_size, &run, 1, 0,
		   store);
  if (! err)
    {
      size_t buf_len = 0;

      /* Copy the input store.  */
      err = store_read (from, 0, from->size, &(*store)->hook, &buf_len);

      if (! err)
	/* Set the store name.  */
	{
	  if (from->name)
	    {
	      size_t len =
		strlen (from->class->name) + 1 + strlen (from->name) + 1;
	      (*store)->name = malloc (len);
	      if ((*store)->name)
		snprintf ((*store)->name, len,
			  "%s:%s", from->class->name, from->name);
	    }
	  else
	    (*store)->name = strdup (from->class->name);

	  if (! (*store)->name)
	    err = ENOMEM;
	}

      if (err)
	store_free (*store);
    }

  return err;
}

/* Return a new store in STORE which contains the memory buffer BUF, of
   length BUF_LEN, and uses the block size BLOCK_SIZE.  BUF must be
   vm_allocated, and will be consumed, and BUF_LEN must be a multiple of
   BLOCK_SIZE.  */
error_t
store_buffer_create (void *buf, size_t buf_len, int flags,
		     struct store **store)
{
  error_t err;
  struct store_run run;

  run.start = 0;
  run.length = buf_len;

  flags |= STORE_ENFORCED;	/* Only uses local resources.  */

  err =
    _store_create (&store_copy_class,
		   MACH_PORT_NULL, flags, 1, &run, 1, 0, store);
  if (! err)
    (*store)->hook = buf;

  return err;
}

/* Open the copy store NAME -- which consists of another store-class name, a
   ':', and a name for that store class to open -- and return the
   corresponding store in STORE.  CLASSES is used to select classes specified
   by the type name; if it is 0, STORE_STD_CLASSES is used.  */
error_t
store_copy_open (const char *name, int flags,
		 const struct store_class *const *classes,
		 struct store **store)
{
  struct store *from;
  error_t err =
    store_typed_open (name, flags | STORE_HARD_READONLY, classes, &from);

  if (! err)
    {
      err = store_copy_create (from, flags, store);
      if (err)
	store_free (from);
    }

  return err;
}