/*
 * tools/lib/vg_setup_for_extend.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
 *
 * June 1998
 * January 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
 *
 *    08/02/2000 - use debug_enter()/debug_leave()
 *
 */
  
#include <liblvm.h>


int vg_setup_for_extend ( char **extend_pv, int num_extend_pv,
                          pv_t **all_pv, vg_t *vg, char **error_pv_name) {
   int ep = 0;
   int new_pv = 0;
   int np = 0;
   int np_sav = 0;
   int p = 0;
   int p1 = 0;
   int ret = 0;
   uint size = 0;
   ulong rest = 0;

   debug_enter ( "vg_setup_for_extend -- CALLED\n");

   if ( extend_pv == NULL || vg == NULL ||
        num_extend_pv <= 0 || num_extend_pv > vg->pv_max ||
        all_pv == NULL || error_pv_name == NULL) {
      ret = -LVM_EPARAM;
      goto vg_setup_for_extend_end;
   }

   new_pv = np = 0;
   while ( vg->pv[np] != NULL && np < vg->pv_max) np++;
   np_sav = np;

   for ( ep = 0; ep < num_extend_pv; ep++) {
      if ( pv_check_name ( extend_pv[ep]) < 0) {
         ret = -LVM_EVG_SETUP_FOR_EXTEND_PV_CHECK_NAME;
         goto vg_setup_for_extend_end;
      }

      for ( p = 0; all_pv[p] != NULL; p++) {
         if ( strcmp ( extend_pv[ep], all_pv[p]->pv_name) == 0) {
            debug ( "%s -- checking for maximum physical "
                    "volume count of %s\n", cmd, vg->vg_name);
            if ( np >= vg->pv_max) {
               ret = -LVM_EVG_SETUP_FOR_EXTEND_MAX_PV;
               goto vg_setup_for_extend_end;
            }
            debug ( "%s -- getting size of physical "
                    "volume %s\n", cmd, all_pv[p]->pv_name);
            if ( ( size = pv_get_size ( all_pv[p]->pv_name, NULL)) <= 0) {
               *error_pv_name = all_pv[p]->pv_name;
               ret = -LVM_EVG_SETUP_FOR_EXTEND_PV_GET_SIZE;
               goto vg_setup_for_extend_end;
            }
 
            debug ( "%s -- checking physical volume %s "
                    "against volume group %s\n",
                    cmd, extend_pv[ep], vg->vg_name);
            for ( p1 = 0; vg->pv[p1] != NULL; p1++) {
               if ( strcmp ( extend_pv[ep], vg->pv[p1]->pv_name) == 0) {
                  *error_pv_name = extend_pv[ep];
                  ret = -LVM_EVG_SETUP_FOR_EXTEND_PV_ALREADY;
                  goto vg_setup_for_extend_end;
               }
            }
            debug ( "%s -- checking for new physical "
                    "volume %s\n", cmd, all_pv[p]->pv_name);
            if ( pv_check_new ( all_pv[p]) == FALSE) {
               *error_pv_name = all_pv[p]->pv_name;
               ret = -LVM_EVG_SETUP_FOR_EXTEND_PV_CHECK_NEW;
               goto vg_setup_for_extend_end;
            }

            if ( pv_get_size ( all_pv[p]->pv_name, NULL) /
                 vg->pv[0]->pe_size < LVM_PE_SIZE_PV_SIZE_REL) {
               *error_pv_name = all_pv[p]->pv_name;
               ret = -LVM_EVG_SETUP_FOR_EXTEND_PV_SIZE_REL;
               goto vg_setup_for_extend_end;
            }

            /* setup PV and correct VG */
            all_pv[p]->vg_on_disk.base = vg->pv[0]->vg_on_disk.base;
            all_pv[p]->vg_on_disk.size = vg->pv[0]->vg_on_disk.size;
            all_pv[p]->pv_uuidlist_on_disk.base =
               vg->pv[0]->pv_uuidlist_on_disk.base;
            all_pv[p]->pv_uuidlist_on_disk.size =
               vg->pv[0]->pv_uuidlist_on_disk.size;
            all_pv[p]->lv_on_disk.base = vg->pv[0]->lv_on_disk.base;
            all_pv[p]->lv_on_disk.size = vg->pv[0]->lv_on_disk.size;
            all_pv[p]->pe_on_disk.base = vg->pv[0]->pe_on_disk.base;
            rest = all_pv[p]->pv_size - all_pv[p]->pe_on_disk.base /
                                        SECTOR_SIZE;
            all_pv[p]->pe_total = rest / vg->pe_size;
            rest -= ( all_pv[p]->pe_total * vg->pe_size);
            while ( rest * SECTOR_SIZE / sizeof ( pe_disk_t) <
                    all_pv[p]->pe_total) {
               rest += vg->pe_size;
               all_pv[p]->pe_total--;
            }
            all_pv[p]->pe_on_disk.size =
               ( all_pv[p]->pv_size - all_pv[p]->pe_total * vg->pe_size -
                 all_pv[p]->pe_on_disk.base / SECTOR_SIZE) * SECTOR_SIZE;
            if ( LVM_DISK_SIZE ( all_pv[p]) % BLOCK_SIZE > 0)
               all_pv[p]->pe_on_disk.size -= SECTOR_SIZE;
            strcpy ( all_pv[p]->vg_name, vg->vg_name);
            all_pv[p]->pv_dev = pv_create_kdev_t ( all_pv[p]->pv_name);
            all_pv[p]->pv_status = 0; /* bitfield */
            all_pv[p]->pv_allocatable = PV_ALLOCATABLE;
            all_pv[p]->pv_size = size;
            all_pv[p]->lv_cur = 0;
            all_pv[p]->pe_size = vg->pe_size;
            all_pv[p]->pe_total = ( all_pv[p]->pv_size -
                                ( all_pv[p]->pe_on_disk.base +
                                  all_pv[p]->pe_on_disk.size) / SECTOR_SIZE ) /
                                vg->pe_size;
            all_pv[p]->pe_allocated = 0;
            all_pv[p]->pe_stale = 0;
            if ( ( all_pv[p]->pe = malloc ( all_pv[p]->pe_total *
                                        sizeof ( pe_disk_t))) == NULL) {
               fprintf ( stderr, "%s -- malloc error in %s at line %d\n\n",
                                 cmd, __FILE__, __LINE__);
               ret = -LVM_EVG_SETUP_FOR_EXTEND_MALLOC;
               goto vg_setup_for_extend_end;
            }
            memset ( all_pv[p]->pe,
                     0,
                     all_pv[p]->pe_total * sizeof ( pe_disk_t));
            vg->pv[np] = all_pv[p];
            vg->pv_act++;
            vg->pv_cur++;
            vg->pe_total += vg->pv[np]->pe_total;

            np++;
            new_pv++;
         }
      }
   }

   if ( new_pv > 0) {
      for ( p = 0; vg->pv[p] != NULL; p++) vg->pv[p]->pv_number = p + 1;
      ret = np_sav;
   } else ret = -LVM_EVG_SETUP_FOR_EXTEND_NO_PV;

vg_setup_for_extend_end:

   debug_leave ( "vg_setup_for_extend -- LEAVING with %d\n", ret);
   return ret;
}
