/*
 * tools/lib/lvm_tab_get_free_blk_dev.c
 *
 * Copyright (C) 1999-2003  Heinz Mauelshagen, Sistina Software
 *
 * February-March,September 1999
 * January 2000
 * Sepetember 2003
 *
 *
 * 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
 *
 *    29/10/1999 - fixed possible free() bug
 *    31/01/2000 - use debug_enter()/debug_leave()
 *    02/09/2003 - avoid to free array of block devices; up to the caller
 *
 */



#include <liblvm.h>

#define	GET_LV_DEV \
   { \
      if ( lv_dev_this != NULL) { \
         int i, l, foundfree = 0; \
         for ( i = 0; i < lv_dev_count && foundfree == 0; i++) { \
            foundfree = 1; \
            for ( l = 0; l < lv_dev_count; l++) { \
               if ( lv_dev == lv_dev_this[l]) { \
                  lv_dev++; \
                  foundfree = 0; \
               } \
            } \
         } \
      } \
   }

int lvm_tab_get_free_blk_dev ( kdev_t **lv_blk_dev_free) {
   int lv_dev_count = 0;
   int lv_blk_dev_free_count = 0;
   int i = 0;
   int ret = 0;
   kdev_t lv_dev = MKDEV(LVM_BLK_MAJOR, 0);
   char **vg_name_ptr = NULL;
   vg_t *vg = NULL;
   kdev_t *lv_dev_this = NULL;
   kdev_t *lv_dev_this_sav = NULL;
   kdev_t *lv_blk_dev_free_this = NULL;
   kdev_t *lv_blk_dev_free_this_sav = NULL;

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

   if ( ( vg_name_ptr = lvm_tab_vg_check_exist_all_vg ()) != NULL) {
      while ( *vg_name_ptr != NULL) {
         if ( ( ret = lvm_tab_vg_check_exist ( *vg_name_ptr, &vg)) < 0) {
            free ( vg_name_ptr);
            ret = -LVM_ELVM_TAB_GET_FREE_BLK_DEV_LVM_TAB_VG_CHECK_EXIST;
            goto lvm_tab_get_free_blk_dev_end;
         } else {
            int l;
            for ( l = 0; l < vg->lv_max; l++) {
               if ( vg->lv[l] == NULL) continue;
               lv_dev_count++;

               lv_dev_this_sav = lv_dev_this;
               if ( ( lv_dev_this = realloc ( lv_dev_this,
                                              lv_dev_count *
                                              sizeof ( kdev_t))) == NULL) {
                  if ( lv_dev_this_sav != NULL) free ( lv_dev_this_sav);
                  ret = -LVM_ELVM_TAB_GET_FREE_BLK_DEV_REALLOC;
                  goto lvm_tab_get_free_blk_dev_end;
               } else lv_dev_this_sav = NULL;
               lv_dev_this[lv_dev_count-1] = vg->lv[l]->lv_dev;
            }
            vg_free ( vg, FALSE);
         }
         vg_name_ptr++;
      }

      if ( lv_blk_dev_free == NULL) {
         GET_LV_DEV;
         if ( lv_dev < MKDEV(LVM_BLK_MAJOR, MAX_LV)) ret = lv_dev;
         else ret = -LVM_ELVM_TAB_GET_FREE_BLK_DEV_NO_DEV;
      } else {
         lv_blk_dev_free_count = 0;
         while ( lv_dev < MKDEV(LVM_BLK_MAJOR, MAX_LV)) {
            GET_LV_DEV;
            if ( lv_dev < MKDEV(LVM_BLK_MAJOR, MAX_LV)) {
               lv_blk_dev_free_this_sav = lv_blk_dev_free_this;
               lv_blk_dev_free_count++;
               if ( ( lv_blk_dev_free_this =
                      realloc ( lv_blk_dev_free_this,
                                lv_blk_dev_free_count *
                                sizeof ( kdev_t))) == NULL) {
                  if ( lv_blk_dev_free_this_sav != NULL)
                     free ( lv_blk_dev_free_this_sav);
                  lv_blk_dev_free_this = NULL;
                  ret = -LVM_ELVM_TAB_GET_FREE_BLK_DEV_REALLOC;
                  goto lvm_tab_get_free_blk_dev_end;
               }
               lv_blk_dev_free_this[lv_blk_dev_free_count-1] = lv_dev;
               lv_dev++;
            }
            ret = lv_blk_dev_free_count;
         }
      }
   } else { /* vg_name_ptr == NULL */
      if ( lv_blk_dev_free != NULL) {
         if ( ( lv_blk_dev_free_this =
                realloc ( NULL, MAX_LV * sizeof ( kdev_t))) == NULL) {
            ret = -LVM_ELVM_TAB_GET_FREE_BLK_DEV_REALLOC;
            goto lvm_tab_get_free_blk_dev_end;
         }
         for ( i = 0; i < MAX_LV; i++) lv_blk_dev_free_this[i] = lv_dev + i;
         ret = MAX_LV;
      } else ret = lv_dev;
   }


lvm_tab_get_free_blk_dev_end:
   if ( lv_dev_this != NULL) free ( lv_dev_this);
   if ( lv_blk_dev_free_this != NULL && lv_blk_dev_free != NULL)
      *lv_blk_dev_free = lv_blk_dev_free_this;

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