/*
 * tools/lib/vg_setup_for_create.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
 *
 * March 1997
 * February 1998
 * January,September 2000
 *
 *
 * This LVM library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This LVM library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this LVM library; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA
 *
 */

/*
 * Changelog
 *
 *    12/02/1998 - changed creation of VG number in vg_setup_for_create()
 *    27/06/1998 - changed lvm_tab_* calling convention
 *    08/02/2000 - use debug_enter()/debug_leave()
 *    24/04/2000 - used lvm_create_uuid() to initialize volume group UUID
 *    15/09/2000 - alligned beginning of pv_uuidlist_in_disk structures
 *                 on sector boundary (Christoph Hellwig <hch@ns.lst.de>)
 *
 */

#include <liblvm.h>

int vg_setup_for_create ( char *vg_name, vg_t *vg, pv_t **pv,
                          int pe_size, ulong max_pv, ulong max_lv) {
   int l = 0;
   int p = 0;
   int ret = 0;
   uint pe_total = 0;
   ulong rest = 0;
   ulong size = 0;

#ifdef DEBUG
   debug_enter ( "vg_setup_for_create -- CALLED\n");
#endif

   if ( vg_name == NULL || vg == NULL || pv == NULL ||
        pe_size < LVM_MIN_PE_SIZE || pe_size > LVM_MAX_PE_SIZE ||
        pe_size % LVM_MIN_PE_SIZE != 0 ||
        max_pv > ABS_MAX_PV || max_lv > ABS_MAX_LV) {
      ret = -LVM_EPARAM;
      goto vg_setup_for_create_end;
   }

   strcpy ( vg->vg_name, vg_name);
   vg->vg_number = lvm_tab_get_free_vg_number ();
   if ( vg->vg_number < 0 || vg->vg_number > MAX_VG) {
      ret = -LVM_EVG_SETUP_FOR_CREATE_VG_NUMBER;
      goto vg_setup_for_create_end;
   }
   vg->vg_access = VG_READ | VG_WRITE;
   vg->vg_status = VG_EXTENDABLE; /* bitfield */
   vg->lv_max = max_lv;
   vg->lv_cur = 0;
   vg->lv_open = 0;
   vg->pv_max = max_pv;
   vg->pe_size = pe_size;
   vg->vgda = 0;
   vg->pe_allocated = 0;
   vg->pvg_total = 0;
   vg->proc = NULL;
   memset ( vg->vg_uuid, 0, sizeof ( vg->vg_uuid));
   memcpy ( vg->vg_uuid, lvm_create_uuid ( UUID_LEN), sizeof ( vg->vg_uuid)-1);

   /* Walk through physical volumes */
   pe_total = 0;
   for ( p = 0; pv[p] != NULL; p++) {
      if ( pv_get_size ( pv[p]->pv_name, NULL) /
           pe_size < LVM_PE_SIZE_PV_SIZE_REL) {
         ret = -LVM_EVG_SETUP_FOR_CREATE_PV_SIZE;
         goto vg_setup_for_create_end;
      }
      vg->pv[p] = pv[p];
      vg->pv[p]->pv_on_disk.base = LVM_PV_DISK_BASE;
      vg->pv[p]->pv_on_disk.size = LVM_PV_DISK_SIZE;
      vg->pv[p]->vg_on_disk.base = LVM_VG_DISK_BASE;
      vg->pv[p]->vg_on_disk.size = LVM_VG_DISK_SIZE;
      vg->pv[p]->pv_uuidlist_on_disk.base = LVM_PV_UUIDLIST_DISK_BASE;
      vg->pv[p]->pv_uuidlist_on_disk.size = ( max_pv + 1) * NAME_LEN;

      size = vg->pv[p]->pv_uuidlist_on_disk.base +
             vg->pv[p]->pv_uuidlist_on_disk.size;
      vg->pv[p]->lv_on_disk.base = (size / SECTOR_SIZE) * SECTOR_SIZE;
      if (size % SECTOR_SIZE)
         vg->pv[p]->lv_on_disk.base += SECTOR_SIZE; 
      vg->pv[p]->lv_on_disk.size = ( max_lv + 1) * sizeof ( lv_disk_t);
      LVM_PE_ON_DISK_BASE ( vg->pv[p]);
      strcpy ( vg->pv[p]->vg_name, vg->vg_name);
      vg->pv[p]->pv_number = p + 1;
      vg->pv[p]->pv_status = 0; /* bitfield */
      vg->pv[p]->pv_allocatable = PV_ALLOCATABLE; /* bitfield */
      vg->pv[p]->pe_size = vg->pe_size;

      rest = vg->pv[p]->pv_size - vg->pv[p]->pe_on_disk.base / SECTOR_SIZE;
      vg->pv[p]->pe_total = rest / vg->pe_size;
      rest -= ( vg->pv[p]->pe_total * vg->pe_size);
      while ( rest * SECTOR_SIZE / sizeof ( pe_disk_t) < vg->pv[p]->pe_total) {
         rest += vg->pe_size;
         vg->pv[p]->pe_total--;
      }
      vg->pv[p]->pe_on_disk.size = ( vg->pv[p]->pv_size -
                                     vg->pv[p]->pe_total * vg->pe_size -
                                     vg->pv[p]->pe_on_disk.base / SECTOR_SIZE)
                                   * SECTOR_SIZE;
      if ( LVM_DISK_SIZE ( vg->pv[p]) % BLOCK_SIZE > 0)
         vg->pv[p]->pe_on_disk.size -= SECTOR_SIZE;
      if ( ( vg->pv[p]->pe = malloc ( vg->pv[p]->pe_total *
                                      sizeof ( pe_disk_t))) == NULL) {
         fprintf ( stderr, "malloc error in %s [line %d]\n",
                           __FILE__, __LINE__);
         ret = -LVM_EVG_SETUP_FOR_CREATE_MALLOC;
         goto vg_setup_for_create_end;
      }
      memset ( vg->pv[p]->pe, 0, vg->pv[p]->pe_total * sizeof ( pe_disk_t));
      vg->pv[p]->pe_allocated = 0;
      vg->pv[p]->pe_stale = 0;
      pe_total += vg->pv[p]->pe_total;
   }
   vg->pv_cur = vg->pv_act = p;
   vg->pe_total = pe_total;

   for ( ; p < vg->pv_max; p++) vg->pv[p] = NULL;      /* Rest should be NULL */
   for ( l = 0; l < vg->lv_max; l++) vg->lv[l] = NULL; /* No LVs in NEW VG */

vg_setup_for_create_end:
#ifdef DEBUG
   debug_leave ( "vg_setup_for_create -- LEAVING with ret: %d\n", ret);
#endif
   return ret;
}
