/*
 * tools/lib/vg_cfgrestore.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
 *
 * November 1997
 * May,August 1998
 * January,July 2000
 * January 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
 *
 *    16/05/1998 - added errror return code for ordinary file existing
 *                 instead of directory
 *    30/08/1998 - seperated disk and core structures by using
 *                 lv_copy_from_disk(), pv_copy_from_disk
 *                 and vg_copy_from_disk()
 *    08/02/2000 - use debug_enter()/debug_leave()
 *    06/07/2000 - freed malloced memory in case of failure
 *    08/01/2001 - set up core pointers for snapshots
 *
 */

#include <liblvm.h>


#define	VGCFG_READ( handle, what, prefered_size, file_name) ( { \
      int size; \
      if ( read ( handle, &size, sizeof ( size)) != sizeof ( size)) { \
         fprintf ( stderr, "%s -- ERROR %d reading structure size form " \
                           "volume group backup file %s in %s [line %d]\n",\
                           cmd, errno, file_name, __FILE__, __LINE__); \
         close ( handle); \
         ret = -LVM_EVG_CFGRESTORE_READ; \
         goto vg_cfgrestore_end; \
      }; \
      if ( size != prefered_size) { \
         fprintf ( stderr, "%s -- ERROR: different structure size stored in " \
                           "\"%s\" than expected in file %s [line %d]\n", \
                           cmd, file_name, __FILE__, __LINE__); \
         close ( handle); \
         ret = -LVM_EVG_CFGRESTORE_READ; \
         goto vg_cfgrestore_end; \
      } \
      if ( read ( handle, what, size) != size) { \
         fprintf ( stderr, "%s -- ERROR %d reading volume group backup " \
                           "file %s in %s [line %d]\n",\
                           cmd, errno, file_name, __FILE__, __LINE__); \
         close ( handle); \
         ret = -LVM_EVG_CFGRESTORE_READ; \
         goto vg_cfgrestore_end; \
      }; \
} )


int vg_cfgrestore ( char *vg_name, char *vg_backup_path,
                    int opt_v, vg_t *vg) {
   int l = 0;
   int p = 0;
   int ret = 0;
   int vg_backup = -1;
   char directory[NAME_LEN] = { 0, };
   struct stat stat_buf;

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

   if ( vg_name == NULL || vg_backup_path == NULL ||
        strchr ( vg_backup_path, '/') == NULL ||
        opt_v < 0 || vg == NULL ||
        vg_check_name ( vg_name) < 0) {
      ret = -LVM_EPARAM;
      goto vg_cfgrestore_end;
   }

   strncpy ( directory, vg_backup_path, sizeof ( directory) - 1);
   directory[ sizeof ( directory) - 1] = 0;
   *(strrchr ( directory, '/')) = 0;

   if ( stat ( directory, &stat_buf) == 0) {
      if ( ! S_ISDIR ( stat_buf.st_mode)) {
         fprintf ( stderr, "%s -- ERROR: file \"%s\" exists; "
                           "must be directory\n",
                           cmd, directory);
         ret = -LVM_EVG_CFGRESTORE_FILE_EXISTS;
         goto vg_cfgrestore_end;
      }
   }

   if ( opt_v > 0) printf ( "%s -- checking existence of \"%s\"\n",
                            cmd, vg_backup_path);
   if ( ( vg_backup = open ( vg_backup_path, O_RDONLY)) == -1) {
      fprintf ( stderr, "%s -- \"%s\" doesn't exist\n", cmd, vg_backup_path);
      ret = -LVM_EVG_CFGRESTORE_OPEN;
      goto vg_cfgrestore_end;
   }

   if ( opt_v > 0) printf ( "%s -- reading volume group data for \"%s\" "
                            "from \"%s\"\n",
                            cmd, vg_name, vg_backup_path);

   VGCFG_READ ( vg_backup, vg, sizeof ( vg_t), vg_backup_path);
   memset(vg->pv, 0, sizeof(vg->pv));
   memset(vg->lv, 0, sizeof(vg->lv));

   if ( vg_check_consistency ( vg) < 0) {
      ret = -LVM_EVG_CFGRESTORE_VG_CHECK_CONSISTENCY;
      goto vg_cfgrestore_end;
   }

   if ( opt_v > 0) printf ( "%s -- reading physical volume data for \"%s\" "
                            "from \"%s\"\n",
                            cmd, vg_name, vg_backup_path);
   for ( p = 0; p < vg->pv_cur; p++) {
      if ( ( vg->pv[p] = malloc ( sizeof ( pv_t))) == NULL) {
         fprintf ( stderr, "%s -- malloc error in file \"%s\" [line %d]\n",
                           cmd, __FILE__, __LINE__);
         ret = -LVM_EVG_CFGRESTORE_MALLOC;
         goto vg_cfgrestore_fail_end;
      }
      VGCFG_READ ( vg_backup, vg->pv[p], sizeof ( pv_t), vg_backup_path);
      if ( pv_check_consistency ( vg->pv[p]) < 0) {
         ret = -LVM_EVG_CFGRESTORE_PV_CHECK_CONSISTENCY;
         goto vg_cfgrestore_fail_end;
      }
      if ( ( vg->pv[p]->pe =
                malloc ( vg->pv[p]->pe_total * sizeof ( pe_disk_t))) == NULL) {
         fprintf ( stderr, "%s -- malloc error in file \"%s\" [line %d]\n",
                           cmd, __FILE__, __LINE__);
         ret = -LVM_EVG_CFGRESTORE_MALLOC;
         goto vg_cfgrestore_fail_end;
      }
      VGCFG_READ ( vg_backup, vg->pv[p]->pe,
                              vg->pv[p]->pe_total * sizeof ( pe_disk_t),
                              vg_backup_path);
   }

   if ( opt_v > 0) printf ( "%s -- reading logical volume data for \"%s\" "
                            "from \"%s\"\n",
                            cmd, vg_name, vg_backup_path);
   for ( l = 0; l < vg->lv_max; l++) {
      if ( ( vg->lv[l] = malloc ( sizeof ( lv_t))) == NULL) {
         fprintf ( stderr, "%s -- malloc error in file \"%s\" [line %d]\n",
                           cmd, __FILE__, __LINE__);
         ret = -LVM_EVG_CFGRESTORE_MALLOC;
         goto vg_cfgrestore_fail_end;
      }
      VGCFG_READ ( vg_backup, vg->lv[l], sizeof ( lv_t), vg_backup_path);
      if ( vg->lv[l]->lv_name[0] == 0) {
         free ( vg->lv[l]);
         vg->lv[l] = NULL;
         continue;
      }

      if ( lv_check_consistency ( vg->lv[l]) < 0) {
         ret = -LVM_EVG_CFGRESTORE_LV_CHECK_CONSISTENCY;
         goto vg_cfgrestore_fail_end;
      } else {
         if ( ( vg->lv[l]->lv_current_pe =
                   malloc ( vg->lv[l]->lv_allocated_le * \
                            sizeof ( pe_t))) == NULL) {
            fprintf ( stderr, "%s -- malloc error in file \"%s\" [line: %d]\n",
                              cmd, __FILE__, __LINE__);
            ret = -LVM_EVG_CFGRESTORE_MALLOC;
            goto vg_cfgrestore_fail_end;
         }
         VGCFG_READ ( vg_backup, vg->lv[l]->lv_current_pe,
                                 vg->lv[l]->lv_allocated_le * sizeof ( pe_t),
                                 vg_backup_path);
      }
   }

   /* insert the pointers of snapshots into the LV structures */
   vg_setup_pointers_for_snapshots ( vg);

   if ( opt_v > 0) printf ( "%s -- checking volume group consistency "
                            "of \"%s\"\n",
                            cmd, vg_name);
   if ( vg_check_consistency_with_pv_and_lv ( vg) < 0)
      ret = -LVM_EVG_CFGRESTORE_VG_CHECK_CONSISTENCY_WITH_PV_AND_LV;

vg_cfgrestore_end:
   if ( vg_backup != -1) close ( vg_backup);

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

vg_cfgrestore_fail_end:
   for ( p = 0; p < vg->pv_max; p++) {
      if ( vg->pv[p] != NULL) {
         free ( vg->pv[p]);
         vg->pv[p] = NULL;
      }
   }
   for ( l = 0; l < vg->lv_max; l++) {
      if ( vg->lv[l] != NULL) {
         free ( vg->lv[l]);
         vg->lv[l] = NULL;
      }
   }
   goto vg_cfgrestore_end;
}
