/*
 * tools/lib/vg_setup_for_split.c
 *
 * Copyright (C) 1997 - 2001  Heinz Mauelshagen, Sistina Software
 *
 * June,December 1998
 * January 2000
 * April,November 2001
 *
 *
 * 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
 *
 *    27/06/1998 - changed lvm_tab_* calling convention
 *    20/12/1998 - fixed index bug of lv_number
 *    08/02/2000 - use debug_enter()/debug_leave()
 *    09/04/2001 - cleaned up debug output (Andreas Dilger)
 *    23/11/2001 - support persistant PV numbers for snapshots
 *
 */

#include <liblvm.h>


int vg_setup_for_split ( vg_t *vg_exist, char *vg_name_new,
                         vg_t **vg_new, char **pv_for_new,
                         char ***lv_for_new, char **error_pv_name) {
   int l = 0;
   int nl = 0;
   int lv_for_new_count = 0;
   int np = 0;
   int p = 0;
   int pfn = 0;
   int pv_for_new_count = 0;
   int pv_for_old_count = 0;
   int pe = 0;
   int ret = 0;
   int *lv_i_for_new = NULL;
   int *pv_i_for_old = NULL;
   int *pv_i_for_new = NULL;
   char **lv_for_new_this = NULL;
   static vg_t vg_new_this;

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

   if ( vg_exist == NULL || vg_check_consistency_with_pv_and_lv ( vg_exist) ||
        vg_new == NULL || pv_for_new == NULL ||
        lv_for_new == NULL || error_pv_name == NULL) {
      ret = -LVM_EPARAM;
      goto vg_setup_for_split_end;
   }

   if ( vg_exist->pv_cur == 1) {
      ret = -LVM_EVG_SETUP_FOR_SPLIT_PV_COUNT;
      *error_pv_name = vg_exist->pv[0]->pv_name;
      goto vg_setup_for_split_end;
   }

   for ( pfn = 0; pv_for_new[pfn] != NULL; pfn++) {
      if ( pv_check_in_vg ( vg_exist, pv_for_new[pfn]) == FALSE) {
         *error_pv_name = pv_for_new[p];
         ret = -LVM_EVG_SETUP_FOR_SPLIT_PV;
         goto vg_setup_for_split_end;
      }
   }

   if ( ( pv_i_for_old = malloc ( vg_exist->pv_cur * sizeof ( int))) == NULL) {
      fprintf ( stderr, "malloc error in file %s [line %d]\n\n",
                        __FILE__, __LINE__);
      ret = -LVM_EVG_SETUP_FOR_SPLIT_MALLOC;
      goto vg_setup_for_split_end;
   }

   if ( ( pv_i_for_new = malloc ( vg_exist->pv_cur * sizeof ( int))) == NULL) {
      fprintf ( stderr, "malloc error in file %s [line %d]\n\n",
                        __FILE__, __LINE__);
      ret = -LVM_EVG_SETUP_FOR_SPLIT_MALLOC;
      goto vg_setup_for_split_end;
   }

   /* Build list of PV indexes going to new VG */
   for ( pfn = 0; pv_for_new[pfn] != NULL; pfn++) {
      pv_i_for_new[pfn] = pv_get_index_by_name ( vg_exist, pv_for_new[pfn]);
   }
   pv_i_for_new[pfn] = -1;
   pv_for_new_count = pfn;

   /* Build list of PV indexes staying in existing VG */
   np = 0;
   for ( p = 0; vg_exist->pv[p] != NULL; p++) {
      for ( pfn = 0; pv_i_for_new[pfn] != -1; pfn++) {
         if ( p == pv_i_for_new[pfn]) break;
      }
      if ( pv_i_for_new[pfn] == -1) pv_i_for_old[np++] = p;
   }
   pv_i_for_old[np] = -1;
   pv_for_old_count = np;

   /* Scan all given PVs to check if LVs don't have
      any LEs on different PVs than the given ones */
   for ( pfn = 0; pv_i_for_new[pfn] != -1; pfn++) {
      for ( pe = 0; pe < vg_exist->pv[pv_i_for_new[pfn]]->pe_total; pe++) {
         if ( vg_exist->pv[pv_i_for_new[pfn]]->pe[pe].lv_num > 0) {
            for ( p = 0; pv_i_for_old[p] != -1; p++) {
               if ( lv_check_on_pv (
                       vg_exist->pv[pv_i_for_old[p]],
                       vg_exist->pv[pv_i_for_new[pfn]]->pe[pe].lv_num) == TRUE)
               {
                  ret = -LVM_EVG_SETUP_FOR_SPLIT_LV_ON_PV;
                  goto vg_setup_for_split_end;
               }
            }
         }
      }
   }

   /* Now we are o.k. to setup structures for new VG */
   /* Just adopt lv_max, pv_max and pe_size for this point in time... */
   vg_new_this.lv_max = vg_exist->lv_max;
   vg_new_this.pv_max = vg_exist->pv_max;
   vg_new_this.pe_size = vg_exist->pe_size;

   for ( pfn = 0; pfn < vg_new_this.pv_max; pfn++) vg_new_this.pv[pfn] = NULL;
   for ( l = 0; l < vg_new_this.lv_max; l++) vg_new_this.lv[l] = NULL;

   strcpy ( vg_new_this.vg_name, vg_name_new);
   vg_new_this.vg_number = lvm_tab_get_free_vg_number ();
   if ( vg_new_this.vg_number < 0 || vg_new_this.vg_number > MAX_VG - 1) {
      ret = -LVM_EVG_SETUP_FOR_SPLIT_VG_NUMBER;
      goto vg_setup_for_split_end;
   }
   debug ( "vg_setup_for_split -- vg_new_this.vg_number: %d\n",
           vg_new_this.vg_number);
   vg_new_this.vg_access = VG_READ | VG_WRITE;
   vg_new_this.vg_status = VG_EXTENDABLE; /* bitfield */

   /* Figure out, how many LVs go to new (split of) VG */
   if ( ( lv_i_for_new = malloc ( vg_exist->lv_max * sizeof ( int))) == NULL) {
      fprintf ( stderr, "malloc error in file %s [line %d]\n\n",
                        __FILE__, __LINE__);
      ret = -LVM_EVG_SETUP_FOR_SPLIT_MALLOC;
      goto vg_setup_for_split_end;
   }

   for ( l = 0; l < vg_exist->lv_max; l++) lv_i_for_new[l] = -1;

   for ( pfn = 0; pv_i_for_new[pfn] != -1; pfn++) {
      for ( pe = 0; pe < vg_exist->pv[pv_i_for_new[pfn]]->pe_total; pe++) {
         if ( vg_exist->pv[pv_i_for_new[pfn]]->pe[pe].lv_num > 0) {
            lv_i_for_new[vg_exist->pv[pv_i_for_new[pfn]]->pe[pe].lv_num-1] =
               vg_exist->pv[pv_i_for_new[pfn]]->pe[pe].lv_num - 1;
         }
      }
   }

   lv_for_new_count = 0;
   for ( l = 0; l < vg_exist->lv_max; l++) {
      if ( lv_i_for_new[l] != -1) {
         lv_for_new_count++;
      }
   }

   if ( ( lv_for_new_this = malloc ( ( lv_for_new_count + 1) *
                                     sizeof ( char*))) == NULL) {
      fprintf ( stderr, "malloc error in file %s [line %d]\n\n",
                        __FILE__, __LINE__);
      ret = -LVM_EVG_SETUP_FOR_SPLIT_MALLOC;
      goto vg_setup_for_split_end;
   }
   for ( l = 0; l <= lv_for_new_count; l++) lv_for_new_this[l] = NULL;
   debug ( "vg_setup_for_split -- after lv_num\n");
   nl = vg_new_this.lv_cur = 0;
   for ( l = 0; l < vg_exist->lv_max; l++) {
      if ( lv_i_for_new[l] != -1) {
         vg_exist->lv_cur--;
         vg_new_this.lv_cur++;
         vg_new_this.lv[lv_i_for_new[l]] = vg_exist->lv[lv_i_for_new[l]];
         vg_new_this.lv[lv_i_for_new[l]]->lv_number = lv_i_for_new[l];
         vg_exist->lv[lv_i_for_new[l]] = NULL;
         strcpy ( vg_new_this.lv[lv_i_for_new[l]]->vg_name, vg_name_new);
         if ( ( lv_for_new_this[nl] =
                malloc ( strlen ( vg_new_this.lv[lv_i_for_new[l]]\
                                  ->lv_name) + 1)) == NULL) {
            for ( l = 0; l < lv_for_new_count; l++)
               if ( lv_for_new_this[l] != NULL) free ( lv_for_new_this[l]);
            free ( lv_for_new_this);
            fprintf ( stderr, "malloc error in file %s [line %d]\n\n",
                              __FILE__, __LINE__);
            ret = -LVM_EVG_SETUP_FOR_SPLIT_MALLOC;
            goto vg_setup_for_split_end;
         }
         strcpy ( lv_for_new_this[nl],
                  vg_new_this.lv[lv_i_for_new[l]]->lv_name);
         strcpy ( vg_new_this.lv[lv_i_for_new[l]]->lv_name,
                  lv_change_vgname ( vg_name_new,
                                     vg_new_this.lv[lv_i_for_new[l]]->lv_name));
         nl++;
      }
   }

   debug ( "vg_setup_for_split -- vg_new_this.lv_cur: %d\n",
           vg_new_this.lv_cur);
   debug ( "vg_setup_for_split -- after initializing pv[*] and lv[*]\n");

   vg_new_this.lv_open = 0;
   vg_new_this.pv_cur = 0;
   vg_new_this.pv_act = 0;
   vg_new_this.vgda = 0;


   /* Calculate pe_total for new (split of) VG */
   vg_new_this.pe_allocated = 0;
   vg_new_this.pe_total = 0;
   for ( pfn = 0; pv_i_for_new[pfn] != -1; pfn++) {
      vg_new_this.pe_allocated += vg_exist->pv[pv_i_for_new[pfn]]->pe_allocated;
      vg_new_this.pe_total     += vg_exist->pv[pv_i_for_new[pfn]]->pe_total;
      vg_exist->pe_allocated   -= vg_exist->pv[pv_i_for_new[pfn]]->pe_allocated;
      vg_exist->pe_total       -= vg_exist->pv[pv_i_for_new[pfn]]->pe_total;
      vg_new_this.pv_cur++;
      vg_new_this.pv_act++;
      vg_exist->pv_cur--;
      vg_exist->pv_act--;
      vg_new_this.pv[pfn] = vg_exist->pv[pv_i_for_new[pfn]];
      vg_exist->pv[pv_i_for_new[pfn]] = NULL;
      strcpy ( vg_new_this.pv[pfn]->vg_name, vg_name_new);
   }


   debug ( "vg_setup_for_split -- after correcting allocated PE\n");
   vg_new_this.pvg_total = 0;
   vg_new_this.proc = NULL;


   /* Make PV pointer array contiguous and correct PV numbers in existing VG */
   np = 0;
   for ( p = 0; p < vg_exist->pv_max; p++) {
      if ( vg_exist->pv[p] != NULL) {
         vg_exist->pv[np] = vg_exist->pv[p];
         np++;
      }
   }
   for ( ; np < vg_exist->pv_max; np++) vg_exist->pv[np] = NULL;

   if ( vg_check_consistency_with_pv_and_lv ( vg_exist) < 0) {
      ret = -LVM_EVG_SETUP_FOR_MERGE_VG_CHECK_CONSISTENCY_WITH_PV_AND_LV;
      goto vg_setup_for_split_end;
   }

   if ( vg_check_consistency_with_pv_and_lv ( &vg_new_this) < 0) {
      ret = -LVM_EVG_SETUP_FOR_MERGE_VG_CHECK_CONSISTENCY_WITH_PV_AND_LV;
      goto vg_setup_for_split_end;
   }

   *vg_new = &vg_new_this;
   *lv_for_new = lv_for_new_this;

vg_setup_for_split_end:
   if ( lv_i_for_new != NULL) free ( lv_i_for_new);
   if ( pv_i_for_old != NULL) free ( pv_i_for_old);
   if ( pv_i_for_new != NULL) free ( pv_i_for_new);

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