/*
 * tools/lib/lv_setup_for_create.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
 *
 * March-May,November 1997
 * April-July 1998
 * January,August 1999
 * January,August 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
 *
 *    09/11/1997 - switched to lvm_tab_vg_read_with_pv_and_lv
 *    29/04/1998 - changed to new lv_create_kdev_t()
 *    16/05/1998 - avoided lv_create_name()
 *    14/06/1998 - added allocation policy on specified PVs for
 *                 striped LVs
 *    05/07/1998 - fixed endless loop bug
 *    26/01/1999 - made volume group directory prefix a preprocessor option
 *    21/08/1999 - support for snapshot logical volumes
 *    31/01/2000 - use debug_enter()/debug_leave()
 *
 */

#include <liblvm.h>

int lv_setup_for_create ( char *vg_name, vg_t **vg, char *lv_name,
                          int *lv_index, uint size, uint stripes,
                          uint stripesize, uint mirrors, uint allocation,
                          uint permission, char **pv_allowed) {
   int blk_dev = 0;
   int l = 0;
   pe_disk_t lv_pe = { 0, 0};
   int p = 0;
   int pa = 0;
   uint pe = 0;
   uint pe_last = 0;
   uint pe_this = 0;
   int ret = 0;
   uint s = 0;
   char lv_name_this[NAME_LEN] = { 0, };
   vg_t *vg_this;
   pe_t *pe_p = NULL;

   debug_enter ( "lv_setup_for_create -- CALLED  %s  %d  %s\n",
                 vg_name, size, lv_name);

   if ( vg_name == NULL || vg == NULL || lv_name == NULL ||
        strchr ( lv_name, '/') != NULL ||
        lv_index == NULL || size == 0 ||
        ( stripesize != 0 && ( stripesize < LVM_MIN_STRIPE_SIZE ||
          stripesize > LVM_MAX_STRIPE_SIZE)) ||
        stripes > LVM_MAX_STRIPES || mirrors > LVM_MAX_MIRRORS ||
        ( allocation & ~( LV_STRICT | LV_CONTIGUOUS)) != 0 ||
        ( permission & ~( LV_READ | LV_WRITE)) != 0 ||
        vg_check_name ( vg_name) < 0) {
      ret = -LVM_EPARAM;
      goto lv_setup_for_create_end;
   }

   vg_this = *vg;
   if ( stripesize > vg_this->pe_size) {
      ret = -LVM_ELV_SETUP_FOR_CREATE_STRIPESIZE;
      goto lv_setup_for_create_end;
   }

   if ( vg_this->lv_cur == vg_this->lv_max) {
      ret = -LVM_ELV_SETUP_FOR_CREATE_LV_MAX;
      goto lv_setup_for_create_end;
   }

   /* search for next free LV */
   for ( l = 0; vg_this->lv[l] != NULL && l < vg_this->lv_max; l++);
   if ( l == vg_this->lv_max) {
      ret = -LVM_ELV_SETUP_FOR_CREATE_LV_MAX;
      goto lv_setup_for_create_end;
   }
   *lv_index = l;
   debug ( "lv_setup_for_create -- l: %d\n", l);
   /* and create it */
   if ( ( vg_this->lv[l] = malloc ( sizeof ( lv_t))) == NULL) {
      fprintf ( stderr, "malloc error in %s [line %d]\n",
                        __FILE__, __LINE__);
      ret = -LVM_ELV_SETUP_FOR_CREATE_MALLOC;
      goto lv_setup_for_create_end;
   }
   debug ( "lv_setup_for_create -- vg_this->lv[%d]: %X\n",
            l, ( uint) vg_this->lv[l]);
   if ( strlen ( lv_name) == 0) {
      sprintf ( lv_name, LVM_DIR_PREFIX "%s/lvol%d", vg_name, l + 1);
   } else {
      strncpy ( lv_name_this, lv_name,
                sizeof ( lv_name_this) -
                strlen ( vg_name) -
                strlen ( LVM_DIR_PREFIX) - 2);
      sprintf ( lv_name, LVM_DIR_PREFIX "%s/%s", vg_name, lv_name_this);
   }
   lv_pe.lv_num = l + 1;
   lv_pe.le_num = 0;
   debug ( "lv_setup_for_create -- creating %s  %d\n", lv_name, lv_pe.lv_num);
   strcpy ( vg_this->lv[l]->vg_name, vg_name);
   strcpy ( vg_this->lv[l]->lv_name, lv_name);
   vg_this->lv[l]->lv_access = permission;
   vg_this->lv[l]->lv_status = LV_ACTIVE;
   if ( ( blk_dev = lvm_tab_get_free_blk_dev ( NULL)) < 0) {
      ret = -LVM_ELV_SETUP_FOR_CREATE_LVM_TAB_GET_FREE_BLK_DEV;
      goto lv_setup_for_create_end;
   }
   vg_this->lv[l]->lv_dev = blk_dev;
   vg_this->lv[l]->lv_number = l;
   vg_this->lv[l]->lv_open = 0;
   vg_this->lv[l]->lv_mirror_copies = 0;
   vg_this->lv[l]->lv_recovery = 0;
   vg_this->lv[l]->lv_schedule = 0;
   vg_this->lv[l]->lv_size = size / vg_this->pe_size * vg_this->pe_size;
   debug ( "lv_setup_for_create -- lv_size: %lu\n", vg_this->lv[l]->lv_size);
   vg_this->lv[l]->lv_current_le = size / vg_this->pe_size;
   if ( vg_this->lv[l]->lv_current_le >
        vg_this->pe_total - vg_this->pe_allocated) {
      ret = -LVM_ESIZE;
      goto lv_setup_for_create_end;
   }
   vg_this->lv[l]->lv_allocated_le = vg_this->lv[l]->lv_current_le;
   vg_this->lv[l]->lv_stripes = stripes;
   vg_this->lv[l]->lv_stripesize = stripesize;
   vg_this->lv[l]->lv_badblock = 0;
   vg_this->lv[l]->lv_allocation = allocation;
   vg_this->lv[l]->lv_io_timeout = 0;
   vg_this->lv[l]->lv_read_ahead = LVM_MAX_READ_AHEAD;
   vg_this->lv[l]->lv_snapshot_org = NULL;
   vg_this->lv[l]->lv_snapshot_prev = NULL;
   vg_this->lv[l]->lv_snapshot_next = NULL;
   vg_this->lv[l]->lv_block_exception = NULL;
   vg_this->lv[l]->lv_remap_ptr = 0;
   vg_this->lv[l]->lv_remap_end = 0;
   vg_this->lv[l]->lv_chunk_size = 0;
   vg_this->lv[l]->lv_snapshot_minor = 0;

   /* search for the free PEs */
   if ( ( vg_this->lv[l]->lv_current_pe =
          malloc ( ( vg_this->lv[l]->lv_allocated_le + 1) *
          sizeof ( pe_t))) == NULL) {
      fprintf ( stderr, "malloc error in %s [line %d]\n",
                        __FILE__, __LINE__);
      ret = -LVM_ELV_SETUP_FOR_CREATE_MALLOC;
      goto lv_setup_for_create_end;
   }

   /* walk through physical volumes searching for physical extents */
   stripes = vg_this->lv[l]->lv_stripes;
   pe = vg_this->lv[l]->lv_allocated_le;

   p = 0;
   /* linear mapping */
   if ( stripes < 2) {
      if ( pe > vg_this->pe_total - vg_this->pe_allocated) {
         ret = -LVM_ELV_SETUP_FOR_CREATE_PE;
         goto lv_setup_for_create_end;
      }
      while ( p < vg_this->pv_cur && pe > 0) {
         if ( pv_allowed != NULL) {
            for ( pa = 0; pv_allowed[pa] != NULL; pa++) {
               if ( strcmp ( vg_this->pv[p]->pv_name, pv_allowed[pa]) == 0)
                  break;
            }
            if ( pv_allowed[pa] == NULL) {
               p++;
               continue;
            }
         }

         if ( ! ( vg_this->pv[p]->pv_allocatable & PV_ALLOCATABLE)) {
            debug ( "lv_setup_for_create -- %s NOT allocatable\n",
                      vg_this->pv[p]->pv_name);
            p++;
            continue;
         }
         pe_last = pe;
         pe_p = &vg_this->lv[l]->lv_current_pe[vg_this->\
                lv[l]->lv_allocated_le-pe];
         if ( ( ret = pv_reserve_pe ( vg_this->pv[p], &lv_pe, &pe, pe_p,
                                      vg_this->lv[l]->lv_allocation,
                                      TRUE)) < 0) {
            ret = -LVM_ESIZE;
            goto lv_setup_for_create_end;
         }
         if ( pe < pe_last) vg_this->pv[p]->lv_cur++;
         debug ( "lv_setup_for_create -- pv_reserve_pe returned: %d   "
                  "pe_last: %d  pe: %d\n", ret, pe_last, pe);
         p++;
      }
      debug ( "lv_setup_for_create -- pe: %d\n", pe);
      if ( pe != 0) {
         ret = -LVM_ESIZE;
         goto lv_setup_for_create_end;
      }
   /* striped mapping */
   } else {
      if ( stripes > vg_this->pv_cur) {
         ret = -LVM_ELV_SETUP_FOR_CREATE_STRIPES;
         goto lv_setup_for_create_end;
      }
      if ( pe % stripes > 0) {
         ret = -LVM_ELV_SETUP_FOR_CREATE_PE;
         goto lv_setup_for_create_end;
      }
      pe_this = pe_last = pe / stripes;
      s = stripes;
      while ( p < vg_this->pv_cur && s > 0) {
         if ( pv_allowed != NULL) {
            for ( pa = 0; pv_allowed[pa] != NULL; pa++) {
               if ( strcmp ( vg_this->pv[p]->pv_name, pv_allowed[pa]) == 0)
                  break;
            }
            if ( pv_allowed[pa] == NULL) {
               p++;
               continue;
            }
         }

         if ( ! ( vg_this->pv[p]->pv_allocatable & PV_ALLOCATABLE)) {
            debug ( "lv_setup_for_create -- %s NOT allocatable\n",
                      vg_this->pv[p]->pv_name);
            p++;
            continue;
         }
         if ( pe_this <= vg_this->pv[p]->pe_total -
                         vg_this->pv[p]->pe_allocated) {
            pe_this = pe_last;
            pe_p = &vg_this->lv[l]->lv_current_pe[vg_this->lv[l]->
                   lv_allocated_le-pe];
            debug ( "lv_setup_for_create -- pe_this: %d\n", pe_this);
            if ( ( ret = pv_reserve_pe ( vg_this->pv[p],
                                         &lv_pe, &pe_this,
                                         pe_p, allocation, TRUE)) < 0) {
               ret = -LVM_ESIZE;
               goto lv_setup_for_create_end;
            }
            debug ( "lv_setup_for_create -- pe_this: %d\n", pe_this);
            if ( pe_this == 0) {
               vg_this->pv[p]->lv_cur++;
               pe -= pe_last;
               s--;
            } else {
               ret = -LVM_ESIZE;
               goto lv_setup_for_create_end;
            }
         }
         p++;
      }
      if ( pe != 0) {
         ret = -LVM_ESIZE;
         goto lv_setup_for_create_end;
      }
   }

   vg_this->lv_cur++;
   vg_this->pe_allocated += vg_this->lv[l]->lv_allocated_le;

lv_setup_for_create_end:

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