/*
 * tools/lib/pv_move.c
 *
 * Copyright (C) 1997 - 2002  Heinz Mauelshagen, Sistina Software
 *
 * May  1998
 * July 1998
 * January,February,September,October 1999
 * January,February 2000
 * March 2001
 * 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
 *
 *    29/04/1998 - changed to new _pe_lock() calling convention
 *    14/05/1998 - implemented handling of striped logical volumes
 *    16/05/1998 - implemented handling of contiguous (optional striped)
 *                 logical volumes
 *    05/07/1998 - added move of list of les in pv_move_pes()
 *               - allow source PV == destination PV in pv_move_pe()
 *    06/07/1998 - added move of lists of physical extents from one source
 *                 to one destination physical volume
 *    20/01/1999 - used LVM_PE_DISK_OFFSET macro in pv_move_pe()
 *    06/02/1999 - fixed lv_current_le corection bug
 *    16/02/1999 - changed to new _pe_lock()
 *    26/08/1999 - support for loop devices (Johannes Deisenhofer
 *                                           jo@hexmac.com)
 *    29/10/1999 - fixed possible free() bug
 *    01/10/1999 - fixed offset by one mistake in case of striped logical
 *                 volumes while checking if logical volume already has extents
 *                 on a destination physical volume
 *    03/02/2000 - use debug_enter()/debug_leave()
 *    15/02/2000 - use lvm_error()
 *    05/04/2001 - changed parameter list of pv_move_pes() and pv_move()
 *                 to reflect new buffer_size parameter (HM)
 *               - changed pv_move_pe() to copy buffer_size'd chunks rather
 *                 than whole PEs in order to reduce memory pressure on systems
 *                 with little RAM (HM)
 *    13/02/2002 - support to optionally ignore read errors
 *
 */


#include <liblvm.h>
#ifdef __KERNEL__
#   undef __KERNEL__
#   define __HM_KERNEL__
#endif
#include <signal.h>
#ifdef __HM_KERNEL__
#   undef __HM_KERNEL__
#   define __KERNEL__
#endif

extern int opt_ignore;
int vg_active = TRUE;

typedef struct pe_struct {
   ushort p;
   long pe;
   ushort lv_num;
   ushort le_num;
} pe_struct_t;

/* internal functions */
static ulong _read ( int, char*, ulong, char*);
loff_t       _llseek ( int, loff_t, int);
void         pv_move_interrupt ( int);
int         _pe_lock ( char*, kdev_t, ulong, ushort, ushort, kdev_t);
int         _pe_unlock ( char*);
int         _lv_le_remap ( vg_t*, le_remap_req_t*);
int         _pv_change ( char*, pv_t*);
static int  _belong_to_snapshot ( vg_t *vg, pe_struct_t *, int); 

static int pv_move_int = FALSE;
static lv_t *lv_this = NULL;

int pe_compare(const void *one, const void *two)
{
   struct pe_struct *pe_one = (struct pe_struct *)one;
   struct pe_struct *pe_two = (struct pe_struct *)two;

   return pe_one->lv_num == pe_two->lv_num ? (pe_one->le_num - pe_two->le_num) :
                                             (pe_one->lv_num - pe_two->lv_num);
}

/* perform the move of all physical extents / those of a logical volume */
int pv_move_pes ( vg_t *vg, char *buffer, size_t buffer_size, char **pv_allowed,
                  int src_pv_index, int lv_index,
                  long *les, long *source_pes, long *dest_pes,
                  int opt_v, int opt_t) {
   int add_it = FALSE;
   int dest_pe_count = 0;
   int dst_pv_index = 0;
   int source_pe_count = 0;
   int ext = 0;
   int pa = 0;
   int ret = 0;
   long le = 0;
   long pe = 0;
   long pe_for_dest = 0;
   long pe_to_move = 0;
   long pe_start = 0;
   struct pe_struct *pe_src = NULL, *pe_src_sav;
   struct pe_struct *pe_dest = NULL, *pe_dest_sav;
   struct pe_struct pe_tmp;

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

   if ( ( ret = vg_check_consistency ( vg)) < 0)
      goto pv_move_pes_end;
   if ( buffer == NULL || pv_allowed == NULL ||
        src_pv_index < 0 || src_pv_index >= vg->pv_cur ||
        lv_index < -1 || lv_index >= ( int) vg->lv_max ||
        opt_v < 0 || opt_t < 0 ||
        buffer_size < SECTOR_SIZE || buffer_size % SECTOR_SIZE != 0) {
      ret = -LVM_EPARAM;
      goto pv_move_pes_end;
   }

   /* avoid LE list for striped LV */
   if ( lv_index > -1 && lv_index < ( int) vg->lv_max &&
        les != NULL && vg->lv[lv_index]->lv_stripes > 1) {
      ret = -LVM_EPARAM;
      goto pv_move_pes_end;
   }

   if ( source_pes != NULL) {
      if ( les != NULL) {
         ret = -LVM_EPARAM;
         goto pv_move_pes_end;
      }
      source_pe_count = 0;
      for ( ext = 0; source_pes[ext] != -1; ext++) {
         if ( source_pes[ext] < 0 ||
              source_pes[ext] >= vg->pv[src_pv_index]->pe_total) {
            ret = -LVM_EPARAM;
            goto pv_move_pes_end;
         } else source_pe_count++;
      }
   }

   if ( dest_pes != NULL) {
      dst_pv_index = pv_get_index_by_name ( vg, pv_allowed[0]);
      dest_pe_count = 0;
      for ( ext = 0; dest_pes[ext] != -1; ext++) {
         if ( dest_pes[ext] < 0 ||
              dest_pes[ext] >= vg->pv[dst_pv_index]->pe_total) {
            ret = -LVM_EPARAM;
            goto pv_move_pes_end;
         } else dest_pe_count++;
      }
   }

   if ( source_pes != NULL && dest_pes != NULL &&
        source_pe_count != dest_pe_count) {
      ret = -LVM_EPARAM;
      goto pv_move_pes_end;
   }
   /* end of parameter check */

   vg_active = vg_check_active ( vg->vg_name);
   pv_move_int = FALSE;


   /* get all source PEs */
   if ( opt_v > 0) printf ( "%s -- checking for enough free physical "
                            "extents in \"%s\"\n", cmd, vg->vg_name);
   pe_to_move = 0;
   for ( pe = 0; pe < vg->pv[src_pv_index]->pe_total; pe++) {
      add_it = FALSE;

      /* have to move a given logical volume */
      if ( lv_index != -1) {
         if ( vg->pv[src_pv_index]->pe[pe].lv_num == lv_index + 1) {
            /* have to move command line given list of logical extents */
            if ( les != NULL) {
               for ( le = 0; les[le] != -1; le++)
                  if ( les[le] == vg->pv[src_pv_index]->pe[pe].le_num) {
                     add_it = TRUE;
                     break;
                  }
            } else add_it = TRUE;
         }
      /* have to move given physical extents */
      } else if ( source_pes != NULL) {
         for ( ext = 0; source_pes[ext] != -1; ext++) {
            if ( pe == source_pes[ext]) {
               if ( ( vg->lv[vg->pv[src_pv_index]->pe[pe].lv_num-1]\
                      ->lv_allocation & LV_CONTIGUOUS) ||
                    vg->lv[vg->pv[src_pv_index]->pe[pe].lv_num-1]\
                    ->lv_stripes > 1) {
                  ret = -LVM_EPV_MOVE_PES_ALLOC_STRIPES;
                  goto pv_move_pes_end;
               }
               add_it = TRUE;
               break;
            }
         }
      /* have to move all logical extents */
      } else if ( vg->pv[src_pv_index]->pe[pe].lv_num != 0) add_it = TRUE;

      if ( add_it == TRUE) {
         pe_src_sav = pe_src;
         if ( ( pe_src = realloc ( pe_src, ( pe_to_move + 1) *
                                   sizeof ( struct pe_struct))) == NULL) {
            fprintf ( stderr, "%s -- realloc error in %s [line %d]\n",
                              cmd, __FILE__, __LINE__);
            if ( pe_src_sav != NULL) free ( pe_src_sav);
            ret = -LVM_EPV_MOVE_PES_REALLOC;
            goto pv_move_pes_end;
         } else pe_src_sav = NULL;

         pe_src[pe_to_move].p  = src_pv_index;
         pe_src[pe_to_move].pe = pe;
         pe_src[pe_to_move].le_num = vg->pv[src_pv_index]->pe[pe].le_num;
         pe_src[pe_to_move].lv_num = vg->pv[src_pv_index]->pe[pe].lv_num;
         pe_to_move++;
      }
   }

   if ( pe_to_move == 0) {
      ret = -LVM_EPV_MOVE_PES_NO_PES;
      goto pv_move_pes_end;
   }

   /* sort source PEs in ascending LV, LE order */
   qsort ( pe_src, pe_to_move, sizeof ( pe_tmp), pe_compare);

   /* Check if a source extent belongs to an original
      or snapshot logical volume */
   if ( _belong_to_snapshot ( vg, pe_src, pe_to_move)) {
      ret = -LVM_EPV_MOVE_PES_SNAPSHOT;
      goto pv_move_pes_end;
   }

   /* get all destination PEs */
   pe_for_dest = 0;
   for ( pa = 0; pv_allowed[pa] != NULL && pe_for_dest < pe_to_move; pa++) {
      dst_pv_index = pv_get_index_by_name ( vg, pv_allowed[pa]);

      /* physical volume is full */
      if ( vg->pv[dst_pv_index]->pe_allocated ==
           vg->pv[dst_pv_index]->pe_total) continue;

      /* physical volume is not allocatable */
      if ( ! ( vg->pv[dst_pv_index]->pv_allocatable & PV_ALLOCATABLE)) continue;

      /* given logical volume */
      if ( lv_index > -1) {
         /* striped LV on this PV ? */
         if ( lv_check_on_pv ( vg->pv[dst_pv_index], lv_index + 1) == TRUE &&
              vg->lv[lv_index]->lv_stripes > 1) continue;
   
         pe_start = 0;
         /* Enough contiguous free available in case of contiguous LV? */
         if ( ( vg->lv[lv_index]->lv_allocation & LV_CONTIGUOUS) &&
              pv_check_free_contiguous ( vg->pv[dst_pv_index],
                                         pe_to_move, &pe_start) ==
                                         FALSE) continue;
      }

      for ( pe = 0; pe < pe_to_move; pe++) {
         if ( lv_check_on_pv ( vg->pv[dst_pv_index],
                               pe_src[pe].lv_num) == TRUE &&
              vg->lv[pe_src[pe].lv_num-1]->lv_stripes > 1) break;
         if ( ( vg->lv[pe_src[pe].lv_num-1]->lv_allocation & LV_CONTIGUOUS) &&
              pv_check_free_contiguous ( vg->pv[dst_pv_index],
                                         lv_count_pe ( vg->pv[src_pv_index],
                                         pe_src[pe].lv_num), &pe_start) ==
                                         FALSE) break;
      }
      if ( pe < pe_to_move) continue;

      for ( pe = pe_start; pe < vg->pv[dst_pv_index]->pe_total; pe++) {
         if ( vg->pv[dst_pv_index]->pe[pe].lv_num != 0) continue;
         if ( dest_pes != NULL) {
            for ( ext = 0; dest_pes[ext] != -1; ext++) {
               if ( pe == dest_pes[ext]) break;
            }
            if ( dest_pes[ext] == -1) continue;
         }

         pe_dest_sav = pe_dest;
         if ( ( pe_dest = realloc ( pe_dest, ( pe_for_dest + 1) *
                                    sizeof ( struct pe_struct))) == NULL) {
            fprintf ( stderr, "%s -- realloc error in %s [line %d]\n",
                              cmd, __FILE__, __LINE__);
            if ( pe_dest_sav != NULL) free ( pe_dest_sav);
            ret = -LVM_EPV_MOVE_PES_REALLOC;
            goto pv_move_pes_end;
         } else pe_dest_sav = NULL;
         pe_dest[pe_for_dest].p = dst_pv_index;
         pe_dest[pe_for_dest].pe = pe;
         pe_for_dest++;
         if ( pe_for_dest == pe_to_move) break;
      }

      /* striped logical volume has to have all
         source PEs on one destination PV*/
      if ( lv_index > -1 && pe_for_dest != pe_to_move) {
         free ( pe_src); pe_src = NULL;
         free ( pe_dest); pe_dest = NULL;
         pe_for_dest = 0;
      }
   }
   if ( pe_for_dest != pe_to_move) {
      ret = -LVM_EPV_MOVE_PES_NO_SPACE;
      goto pv_move_pes_end;
   }
   /* END space check/get */

   lvm_dont_interrupt ( 0);
   pv_move_interrupt ( 0);

   /* Move PE by PE */
   for ( pe = 0; pv_move_int == FALSE && pe < pe_to_move; pe++) {
      lv_index = pe_src[pe].lv_num - 1;
      lv_this = vg->lv[lv_index];
      if ( ( ret = pv_move_pe ( vg, buffer, buffer_size,
                                pe_src[pe].p,  pe_dest[pe].p,
                                pe_src[pe].pe, pe_dest[pe].pe,
                                opt_v, opt_t, pe + 1, pe_to_move)) < 0)
         goto pv_move_pes_end;
   }

pv_move_pes_end:
   if ( pe_src  != NULL) free ( pe_src);
   if ( pe_dest != NULL) free ( pe_dest);

   if ( ret >= 0) ret = pe;

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


/* perform the move of a physical extent */
int pv_move_pe ( vg_t *vg, char *buffer, size_t buffer_size,
                 long src_pv_index, long dst_pv_index,
                 long pe_source,    long pe_dest,
                 int opt_v, int opt_t,
                 int act_pe, int off_pe) {
   int in = -1;
   int out = -1;
   int l = 0;
   int le = 0;
   int ret = 0;
   loff_t offset = 0;
   loff_t result = 0;
   char *lv_name_this = NULL;
   size_t size = 0;
   le_remap_req_t le_remap_req;

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

   if ( src_pv_index < 0 || src_pv_index >= vg->pv_cur ||
        dst_pv_index < 0 || dst_pv_index >= vg->pv_cur ||
        pe_source < 0 || pe_source >= vg->pv[src_pv_index]->pe_total ||
        pe_dest   < 0 || pe_dest   >= vg->pv[dst_pv_index]->pe_total ||
        opt_t < 0 || act_pe < 0 || off_pe < 0 ||
        vg->pv[dst_pv_index]->pe[pe_dest].lv_num != 0) {
      ret = -LVM_EPARAM;
      goto pv_move_pe_end;
   }

   if ( ( in = open ( vg->pv[src_pv_index]->pv_name, O_RDONLY)) == -1) {
      fprintf ( stderr, "%s -- couldn't open input physical volume %s\n",
                cmd, vg->pv[src_pv_index]->pv_name);
      ret = -LVM_EPV_MOVE_PE_OPEN_IN;
      goto pv_move_pe_end;
   }
   
   /* is this LV not allready on destination PV?
      --> have to increment LV current count */
   if ( lv_check_on_pv ( vg->pv[dst_pv_index],
                         vg->pv[src_pv_index]->pe[pe_source].lv_num) == FALSE)
      vg->pv[dst_pv_index]->lv_cur++;

   /* remap LV numbers and LE numbers in arrays */
   vg->pv[dst_pv_index]->pe[pe_dest].lv_num =
      vg->pv[src_pv_index]->pe[pe_source].lv_num;
   vg->pv[dst_pv_index]->pe[pe_dest].le_num =
      vg->pv[src_pv_index]->pe[pe_source].le_num;

   vg->pv[src_pv_index]->pe[pe_source].lv_num = \
   vg->pv[src_pv_index]->pe[pe_source].le_num = 0;
   vg->pv[src_pv_index]->pe_allocated--;
   vg->pv[dst_pv_index]->pe_allocated++;

   /* last LE remapped away from source PV?
      --> have to decrement LV count */
   if ( lv_check_on_pv ( vg->pv[src_pv_index],
                         vg->pv[dst_pv_index]->pe[pe_dest].lv_num) == FALSE)
       vg->pv[src_pv_index]->lv_cur--;

   if ( ( lv_name_this = lv_get_name ( vg, vg->pv[dst_pv_index]->\
                                           pe[pe_dest].lv_num-1)) == NULL) {
      ret = -LVM_EPV_MOVE_PE_LV_GET_NAME;
      goto pv_move_pe_end;
   }

   /* Fill the remapping request structure */
   memset ( &le_remap_req, 0, sizeof ( le_remap_req));
   strncpy ( le_remap_req.lv_name, lv_name_this,
             sizeof ( le_remap_req.lv_name) - 1);
   le_remap_req.old_dev = vg->pv[src_pv_index]->pv_dev;
   le_remap_req.new_dev = vg->pv[dst_pv_index]->pv_dev;
   le_remap_req.old_pe  = get_pe_offset(pe_source, vg->pv[src_pv_index]);
   le_remap_req.new_pe  = get_pe_offset(pe_dest, vg->pv[dst_pv_index]);

   /* remap lv_current_pe device and PE in arrays */
   for ( l = 0; l < vg->lv_max; l++) {
      if ( vg->lv[l] == NULL) continue;
      for ( le = 0; le < vg->lv[l]->lv_allocated_le; le++) {
         if ( vg->lv[l]->lv_current_pe[le].dev == le_remap_req.old_dev &&
              vg->lv[l]->lv_current_pe[le].pe  == le_remap_req.old_pe) {
            vg->lv[l]->lv_current_pe[le].dev = le_remap_req.new_dev;
            vg->lv[l]->lv_current_pe[le].pe  = le_remap_req.new_pe;
            goto pv_move_pe_continue;
         }
      }
   }
pv_move_pe_continue:

   if ( opt_v > 1) {
      printf ( "lv: %s[%d]  old_dev: %02d:%02d  new_dev: %02d:%02d  "
               "old_pe_sector: %u  new_pe_sector: %u\n",
               le_remap_req.lv_name,
               vg->pv[dst_pv_index]->pe[pe_dest].lv_num,
               MAJOR ( le_remap_req.old_dev),
               MINOR ( le_remap_req.old_dev),
               MAJOR ( le_remap_req.new_dev),
               MINOR ( le_remap_req.new_dev),
               le_remap_req.old_pe,
               le_remap_req.new_pe);
   }

   if ( opt_v > 1) printf ( "%s -- opening output physical volume \"%s\"\n",
                            cmd, vg->pv[dst_pv_index]->pv_name);
   if ( ( out = open ( vg->pv[dst_pv_index]->pv_name,
                       O_WRONLY)) == -1) {
      fprintf ( stderr, "%s -- couldn't open output "
                        "physical volume \"%s\"\n",
                cmd, vg->pv[dst_pv_index]->pv_name);
      ret = -LVM_EPV_MOVE_PE_OPEN;
      goto pv_move_pe_end;
   }

   if ( opt_v > 1) printf ( "%s -- llseeking input physical volume \"%s\"\n",
                            cmd, vg->pv[src_pv_index]->pv_name);
   offset = ( loff_t) le_remap_req.old_pe * SECTOR_SIZE;
   if ( ( result = _llseek ( in, offset, SEEK_SET)) == -1) {
      fprintf ( stderr, "%s -- couldn't llseek to sector %u on input "
                        "physical volume \"%s\"\n",
                cmd,
                le_remap_req.old_pe,
                vg->pv[src_pv_index]->pv_name);
      ret = -LVM_EPV_MOVE_PE_LLSEEK_IN;
      goto pv_move_pe_end;
   }

   if ( opt_v > 1) printf ( "%s -- llseeking output physical volume \"%s\"\n",
                            cmd, vg->pv[dst_pv_index]->pv_name);
   offset = ( loff_t) le_remap_req.new_pe * SECTOR_SIZE;
   if ( ( result = llseek ( out, offset, SEEK_SET)) == -1) {
      fprintf ( stderr, "%s -- couldn't llseek to sector %u on output "
                        "physical volume \"%s\"\n",
                cmd,
                le_remap_req.new_pe,
                vg->pv[dst_pv_index]->pv_name);
      ret = -LVM_EPV_MOVE_PE_LLSEEK_OUT;
      goto pv_move_pe_end;
   }

   if ( opt_v > 0)
       printf ( "%s -- %s [PE %lu [%s [LE %d]]] -> %s [PE %lu] [%d/%d]\n",
                cmd,
                vg->pv[src_pv_index]->pv_name,
                pe_source,
                basename ( lv_get_name ( vg,
                                         vg->pv[dst_pv_index]->pe[pe_dest].
                                         lv_num-1)),
                vg->pv[dst_pv_index]->pe[pe_dest].le_num,
                vg->pv[dst_pv_index]->pv_name,
                pe_dest,
                act_pe,
                off_pe);

   /* lock extent in kernel */
   if ( opt_v > 1) printf ( "%s -- locking physical extent %lu "
                            "of \"%s\" in kernel\n",
                            cmd, pe_source,
                            vg->pv[src_pv_index]->pv_name);
   if ( opt_t == 0) {
      int lv_num = vg->pv[dst_pv_index]->pe[pe_dest].lv_num;

      if ( ( ret = _pe_lock ( vg->vg_name, vg->pv[src_pv_index]->pv_dev,
                             le_remap_req.old_pe, vg->vg_number,
                             lv_num, vg->lv[lv_num-1]->lv_dev)) < 0) {
         ret = -LVM_EPV_MOVE_PE_LOCK;
         goto pv_move_pe_end;
      }
   }


   /* we are going to read and write a whole physical extent
      in buffer_size chunks maximum io requests */
   size = vg->pe_size * SECTOR_SIZE;

   if ( opt_v > 1) printf ( "%s -- about to read input physical volume \"%s\" "
                            "and to write output physical volume \"%s\"\n",
                            cmd, vg->pv[src_pv_index]->pv_name,
                            vg->pv[dst_pv_index]->pv_name);
   /* we do it buffer_size chunk per chunk in order
      to minimize memory pressure unless we find an error and opt_ignore is set
      where we drop to SECTOR_SIZEed reads */
   while ( size > 0) {
      ulong red, to_read, to_write;

      red = to_read = to_write = size > buffer_size ? buffer_size : size;
      if ( opt_t == 0) {
         if ( ( red = _read ( in, buffer, to_read,
                              vg->pv[src_pv_index]->pv_name)) != to_read) {
            fprintf ( stderr, "%s -- ERROR reading input "
                              "physical volume \"%s\" (still %d bytes "
                              "to read)\n",
                      cmd, vg->pv[src_pv_index]->pv_name, size);
            _pe_unlock ( vg->vg_name);
            ret = -LVM_EPV_MOVE_PE_READ_IN;
            goto pv_move_pe_end;
         }
         to_write = red;
      }
   
      if ( opt_t == 0) {
         if ( write ( out, buffer, to_write) != to_write) {
            fprintf ( stderr, "%s -- ERROR writing output "
                              "physical volume \"%s\" (still %d bytes to "
                              "write)\n",
                              cmd, vg->pv[dst_pv_index]->pv_name, size);
            _pe_unlock ( vg->vg_name);
            ret = -LVM_EPV_MOVE_PE_WRITE_OUT;
            goto pv_move_pe_end;
         }
      }
      size -= red;
   }

   if ( fsync ( out) < 0  && (errno != EINVAL && errno != EROFS)) {
	   fprintf ( stderr, "%s -- ERROR syncing output to "
		     "physical volume \"%s\"\n",
		     cmd, vg->pv[dst_pv_index]->pv_name);
	   _pe_unlock ( vg->vg_name);
	   ret = -LVM_EPV_MOVE_PE_WRITE_OUT;
	   goto pv_move_pe_end;
   }

   if ( opt_v > 1) printf ( "%s -- remapping physical extent "
                            "in VGDA of kernel\n", cmd);
   if ( opt_t == 0) {
      if ( ( ret = _lv_le_remap ( vg, &le_remap_req)) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" remapping\n",
                           cmd, lvm_error ( ret));
         _pe_unlock ( vg->vg_name);
         ret = -LVM_EPV_MOVE_LV_LE_REMAP;
         goto pv_move_pe_end;
      }
   }

   if ( opt_v > 1) printf ( "%s -- unlocking physical extent\n",
                            cmd);
   if ( opt_t == 0) {
      if ( ( ret = _pe_unlock ( vg->vg_name)) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" unlocking PE %lu on \"%s\"\n",
                           cmd, lvm_error ( ret),
                           pe_source,
                           vg->pv[src_pv_index]->pv_name);
         ret = -LVM_EPV_MOVE_PE_UNLOCK;
         goto pv_move_pe_end;
      }
   }

   if ( opt_v > 1) printf ( "%s -- changing source \"%s\" in VGDA "
                            "of kernel\n",
                            cmd, vg->pv[src_pv_index]->pv_name);
   if ( opt_t == 0) {
      if ( ( ret = _pv_change ( vg->vg_name, vg->pv[src_pv_index])) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" changing source \"%s\"\n",
                           cmd, lvm_error ( ret),
                           vg->pv[src_pv_index]->pv_name);
         ret = -LVM_EPV_MOVE_PV_CHANGE_SRC;
         goto pv_move_pe_end;
      }
   }

   if ( opt_v > 1) printf ( "%s -- changing destination \"%s\" in VGDA "
                            "of kernel\n",
                            cmd, vg->pv[dst_pv_index]->pv_name);
   if ( opt_t == 0) {
      if ( ( ret = _pv_change ( vg->vg_name, vg->pv[dst_pv_index])) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" changing destinatiom \"%s\"\n",
                           cmd, lvm_error ( ret),
                           vg->pv[dst_pv_index]->pv_name);
         ret = -LVM_EPV_MOVE_PV_CHANGE_DEST;
         goto pv_move_pe_end;
      }
   }

   if ( opt_v > 1) printf ( "%s -- writing physical extent part "
                            "of VGDA on source \"%s\"\n",
                            cmd, vg->pv[src_pv_index]->pv_name);
   if ( opt_t == 0) {
      if ( ( ret = pv_write_with_pe ( vg->pv[src_pv_index]->pv_name,
                                      vg->pv[src_pv_index])) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" writing source "
                           "physical volume data on \"%s\"\n", 
                           cmd, lvm_error ( ret),
                           vg->pv[src_pv_index]->pv_name);
         ret = -LVM_EPV_MOVE_PV_PV_WRITE_WITH_PE_SRC;
         goto pv_move_pe_end;
      }
   }

   if ( opt_v > 1) printf ( "%s -- writing physical extent part "
                            "of VGDA on destination \"%s\"\n",
                            cmd, vg->pv[dst_pv_index]->pv_name);
   if ( opt_t == 0) {
      if ( ( ret = pv_write_with_pe ( vg->pv[dst_pv_index]->pv_name,
                                      vg->pv[dst_pv_index])) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" writing destination "
                           "physical volume data on \"%s\"\n", 
                           cmd, lvm_error ( ret),
                           vg->pv[dst_pv_index]->pv_name);
         ret = -LVM_EPV_MOVE_PV_PV_WRITE_WITH_PE_DEST;
         goto pv_move_pe_end;
      }
   }

pv_move_pe_end:
   if ( in != -1) close ( in);
   if ( out != -1) close ( out);

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


void pv_move_interrupt ( int sig) {
   static int first = 0;

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

   signal ( SIGINT, pv_move_interrupt);
   if ( first != 0) {
      if ( lv_this->lv_stripes > 1) {
         printf ( "%s -- interrupt of a striped logical volume move "
                  "not possible\n",
                  cmd);
         goto pv_move_interrupt_end;
      } else {
         printf ( "%s -- interrupting move... Please wait.\n",
                  cmd);
         pv_move_int = TRUE;
         goto pv_move_interrupt_end;
      }
   } else first++;

pv_move_interrupt_end:

   debug_leave ( "pv_move_interrupt -- LEAVING\n");
   return;
}

/* llseek with optional error ignore */
loff_t _llseek ( int fh, loff_t offset, int whence) {
   loff_t pos = llseek ( fh, offset, whence);

   if ( opt_ignore &&
        pos == -1)
      pos = 0;

   return pos;
}

/* read with fallback to SECTOR_SIZEed io in case of an error */
static ulong _read ( int in, char *buffer, ulong to_read, char *pv_name) {
   ulong ret;
   ulong total_read = 0;
   loff_t pos = _llseek ( in, 0, SEEK_CUR); /* get actual position */

   /* if we can't even get our actual position */
   if ( pos <= 0) {
      if ( opt_ignore == 0)
         return 0;
      else
         return to_read;
   }

   memset ( buffer, 0, to_read);

   ret = read ( in, buffer, to_read);
   /* If we could read it fine, return it :-) */
   if ( ret == to_read)
      return ret;

   /* in case we should ignore and couldn't read the amount
      we wanted, we start to read single sectors */
   if ( opt_ignore > 0) {
      memset ( buffer, 0, to_read);

      while ( total_read < to_read) {
         /* If we can't position (for eg. on a test device),
            return buffer of zeroed bytes */
         if ( _llseek ( in, pos, SEEK_SET) <= 0)
            return to_read;

         ret = read ( in, buffer, SECTOR_SIZE);
         if ( ret != SECTOR_SIZE) {
            fprintf ( stderr, "%s -- ERROR \"%s\" reading sector %lld from "
                              "\"%s\"\n",
                              cmd, strerror ( errno),
                              pos / SECTOR_SIZE, pv_name);
         }
         pos += SECTOR_SIZE;
         total_read += SECTOR_SIZE;
      }
      ret = total_read;
   }

   return ret;
} /* _read() */

/* avoid pe_lock() in inactive VG */
int _pe_lock ( char *vg_name, kdev_t pv_dev, ulong old_pe, ushort vg_number,
               ushort lv_number, kdev_t lv_dev) {
   if ( vg_active == TRUE)
      return pe_lock ( vg_name, pv_dev, old_pe, vg_number, lv_number, lv_dev);
   else
      return 0;
}

/* avoid pe_unlock() in inactive VG */
int _pe_unlock ( char *vg_name) {
   if ( vg_active == TRUE)
      return pe_unlock ( vg_name);
   else
      return 0;
}

/* avoid lv_le_remap() in inactive VG */
int _lv_le_remap ( vg_t *vg, le_remap_req_t *le_remap_req) {
   if ( vg_active == TRUE)
      return lv_le_remap ( vg, le_remap_req);
   else
      return 0;
}

/* avoid pv_change() in inactive VG */
int _pv_change ( char *vg_name, pv_t *pv) {
   if ( vg_active == TRUE)
      return pv_change ( vg_name, pv);
   else
      return 0;
}


lv_t *_get_lv_for_offset ( vg_t *vg, pv_t *pv, ulong offset) {
   int l, le;
   lv_t *lv;

   for ( l = 0; l < vg->lv_max; l++) {
      lv = vg->lv[l];
      if ( lv == NULL) continue;
      for ( le = 0; le < lv->lv_allocated_le; le++) {
         if ( lv->lv_current_pe[le].dev != pv->pv_dev)
            continue;
         if ( lv->lv_current_pe[le].pe == offset)
            return lv;
      }
   }

   return NULL;
}

static int _belong_to_snapshot ( vg_t *vg, pe_struct_t *pe_src, int pes) {
   int pe;

   for ( pe = 0; pe < pes; pe++) {
      pv_t *pv = vg->pv[pe_src[pe].p];
      lv_t *lv;

      lv = _get_lv_for_offset ( vg, pv, get_pe_offset ( pe_src[pe].pe, pv));
      if ( lv == NULL) continue;

      if ((lv->lv_access & LV_SNAPSHOT_ORG) ||
          (lv->lv_access & LV_SNAPSHOT))
         return 1;
   }

   return 0;
}
