/*
 * tools/lib/lv_read_COW_table.c
 *
 * Copyright (C) 2000 - 2002  Heinz Mauelshagen, Sistina Software
 *
 * July 2000
 * 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
 *
 *    17/02/2002 - remove fsync/pv_flush() because we don't write here
 *
 */

#include <liblvm.h>


int lv_read_COW_table ( vg_t *vg, lv_t *lv) {
   int COW_table_chunks_per_PE = 0;
   int e = 0;
   int end = 0;
   int index = 0;
   int le = 0;
   int pv_handle = -1;
   int ret = 0;
   ulong size = 0;
   kdev_t last_dev = 0;
   loff_t offset = 0;
   lv_COW_table_disk_t *lv_COW_table_disk = NULL;
   char *pv_name = 0;

   debug_enter ( "lv_read_COW_table -- CALLED with: \"%s\" \"%s\"\n",
                 vg->vg_name, lv->lv_name);

   if ( vg == NULL || lv == NULL)
      ret = -LVM_EPARAM;
   else if ( ( ret = vg_check_name ( vg->vg_name)) == 0 &&
             ( ret = lv_check_name ( lv->lv_name)) == 0) {
      end = LVM_GET_COW_TABLE_ENTRIES_PER_PE(vg, lv);

      if(!end) {
	      ret = -LVM_ELV_READ_COW_TABLE_MALLOC;
	      goto out;
      }

      COW_table_chunks_per_PE = LVM_GET_COW_TABLE_CHUNKS_PER_PE(vg, lv);
      size = end * sizeof ( lv_COW_table_disk_t);
      if ( ( lv_COW_table_disk = malloc ( size)) == NULL) {
         fprintf ( stderr, "%s -- ERROR: malloc at line %d\n\n", cmd, __LINE__);
         ret = -LVM_ELV_READ_COW_TABLE_MALLOC;
         goto out;
      }
      memset ( lv_COW_table_disk, 0, size);

      index = 0;
      for ( le = 0; le < lv->lv_allocated_le; le++) {
         if ( lv->lv_current_pe[le].dev != last_dev) {
            last_dev = lv->lv_current_pe[le].dev;
            if ( pv_handle != -1 && close ( pv_handle) < 0) {
               ret = -LVM_ELV_READ_COW_TABLE_CLOSE;
               goto out;
            }                 

            pv_name = pv_create_name_from_kdev_t ( last_dev);

            if ( ( pv_handle = open ( pv_name, O_RDONLY)) == -1) {
               ret = -LVM_ELV_READ_COW_TABLE_OPEN;
               goto out;
            }                 
         }

         offset = ( loff_t) lv->lv_current_pe[le].pe * SECTOR_SIZE;
         if ( llseek ( pv_handle, offset, SEEK_SET) == -1) {
            ret = -LVM_ELV_READ_COW_TABLE_LLSEEK;
            goto out;
         }                 

         if ( read ( pv_handle, lv_COW_table_disk, size) != size) {
            ret = -LVM_ELV_READ_COW_TABLE_READ;
            goto out;
         }                 

         for ( e = 0; e < end; e++) {
            lv->lv_block_exception[index].rsector_org =
               LVM_TO_CORE64 ( lv_COW_table_disk[e].pv_org_rsector);
            if ( lv->lv_block_exception[index].rsector_org == 0) break;
            lv->lv_block_exception[index].rdev_org =
               pv_get_kdev_t_by_number ( vg, LVM_TO_CORE64 ( lv_COW_table_disk[e].pv_org_number));
            lv->lv_block_exception[index].rsector_new =
               LVM_TO_CORE64 ( lv_COW_table_disk[e].pv_snap_rsector);
            lv->lv_block_exception[index].rdev_new =
               pv_get_kdev_t_by_number ( vg, LVM_TO_CORE64 ( lv_COW_table_disk[e].pv_snap_number));
            index++;
         }
         if ( e < end) break;
      }

      lv->lv_remap_end = end * lv->lv_allocated_le;
      lv->lv_remap_ptr = index;
   }

 out:
   if ( pv_handle != -1) {
      if ( close ( pv_handle) < 0) ret = -LVM_ELV_INIT_COW_TABLE_CLOSE;
   }

   if ( ret < 0) {
      free ( lv->lv_block_exception);
      lv->lv_block_exception = NULL;
   }
   if ( lv_COW_table_disk != NULL) free ( lv_COW_table_disk);

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