/*
 * tools/lib/vg_setup_for_extend.c
 *
 * Copyright (C) 1997 - 2002  Heinz Mauelshagen, Sistina Software
 *
 * June 1998
 * February 2000
 * June,November 2001
 * February 2002
 *
 *
 * 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()
 *    22/06/2001 - added Andreas Dilger's PE on 4k boundary
 *                 alignment enhancements
 *    25/06/2001 - Round down pv_size when calculating the number of
 *                 PEs it can hold so that it leaves room for the VGDA.
 *    22/11/2001 - find next unused PV number while adding a PV
 *    06/02/2002 - cope with changed pv_get_size() return cast
 *    17/02/2002 - use pv_get_size_ll() for library compatibility
 *
 */
  
#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;
   long long pv_size;

   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++) {
         pv_t *pp = all_pv[p];
         if ( strcmp ( extend_pv[ep], pp->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, pp->pv_name);
            if ( pp->pv_size != 0) {
               size = pp->pv_size;
            } else {
               pv_size = pv_get_size_ll ( pp->pv_name, NULL);
               if ( pv_size < 0) {
                  *error_pv_name = pp->pv_name;
                  ret = -LVM_EVG_SETUP_FOR_EXTEND_PV_GET_SIZE;
                  goto vg_setup_for_extend_end;
               }
               size = pv_size;
            }
 
            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, pp->pv_name);
            if ( pv_check_new ( pp) == FALSE) {
               *error_pv_name = pp->pv_name;
               ret = -LVM_EVG_SETUP_FOR_EXTEND_PV_CHECK_NEW;
               goto vg_setup_for_extend_end;
            }

            if ( size / vg->pv[0]->pe_size < LVM_PE_SIZE_PV_SIZE_REL) {
               *error_pv_name = pp->pv_name;
               ret = -LVM_EVG_SETUP_FOR_EXTEND_PV_SIZE_REL;
               goto vg_setup_for_extend_end;
            }

            /* setup PV and correct VG */
            strcpy ( pp->vg_name, vg->vg_name);
            pp->pv_number = _find_unused_pv_number ( vg);
            pp->pv_dev = pv_create_kdev_t ( pp->pv_name);
            pp->pv_status = 0; /* bitfield */
            pp->pv_allocatable = PV_ALLOCATABLE;
            pp->pv_size = size;
            pp->lv_cur = 0;
            pp->pe_size = vg->pe_size;

            pp->vg_on_disk.base = vg->pv[0]->vg_on_disk.base;
            pp->vg_on_disk.size = vg->pv[0]->vg_on_disk.size;
            pp->pv_uuidlist_on_disk.base = vg->pv[0]->pv_uuidlist_on_disk.base;
            pp->pv_uuidlist_on_disk.size = vg->pv[0]->pv_uuidlist_on_disk.size;
            pp->lv_on_disk.base = vg->pv[0]->lv_on_disk.base;
            pp->lv_on_disk.size = vg->pv[0]->lv_on_disk.size;

	    if(!setup_pe_table(pp)) {
		    ret = -LVM_EVG_SETUP_FOR_CREATE_PV_SIZE_MIN;
		    goto vg_setup_for_extend_end;
	    }

            if ( ( pp->pe = malloc ( pp->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 ( pp->pe, 0, pp->pe_total * sizeof ( pe_disk_t));
            pp->pe_allocated = 0;
            pp->pe_stale = 0;

            vg->pv[np] = pp;
            vg->pv_act++;
            vg->pv_cur++;
            vg->pe_total += pp->pe_total;

            np++;
            new_pv++;
         }
      }
   }

   if ( new_pv > 0) {
      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;
}
