/*
 * tools/lib/pv_move.c
 *
 * Copyright (C) 1997 - 2001  Heinz Mauelshagen, Sistina Software
 *
 * May  1998
 * July 1998
 * January,February,September,October 1999
 * January,February 2000
 * March 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
 *
 *    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)
 *
 */

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

void pv_move_interrupt ( int);
static int pv_move_int = FALSE;
static lv_t *lv_this = NULL;

/* 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 i = 0;
   int l = 0;
   int pa = 0;
   int ret = 0;
   long le = 0;
   long pe = 0;
   long pe_for_dest = 0;
   long pe_last = 0;
   long pe_to_move = 0;
   long pe_start = 0;
   struct pe_struct {
      ushort p;
      long pe;
      ushort lv_num;
      ushort le_num;
   } *pe_src = NULL, *pe_dest = NULL, *pe_src_sav, *pe_dest_sav, pe_tmp;

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

   if ( vg == NULL || vg_check_consistency ( vg) < 0 ||
        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 */

   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\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 LE order */
   for ( l = 0; l < vg->pv[src_pv_index]->lv_cur; l++) {
      for ( pe = pe_last ; pe < pe_to_move - 1; pe++) {
         if ( pe_src[pe].lv_num > pe_src[pe+1].lv_num) {
            memcpy ( &pe_tmp, &pe_src[pe], sizeof ( pe_tmp));
            memcpy ( &pe_src[pe], &pe_src[pe+1], sizeof ( pe_tmp));
            memcpy ( &pe_src[pe+1], &pe_tmp, sizeof ( pe_tmp));
         }
      }
   }

   pe_last = 0;
   for ( i = 0; i < pe_to_move; i++) {
      for ( pe = pe_last ; pe < pe_to_move - 1; pe++) {
         if ( pe_src[pe].lv_num == pe_src[pe+1].lv_num) {
            if ( pe_src[pe].le_num > pe_src[pe+1].le_num) {
               memcpy ( &pe_tmp, &pe_src[pe], sizeof ( pe_tmp));
               memcpy ( &pe_src[pe], &pe_src[pe+1], sizeof ( pe_tmp));
               memcpy ( &pe_src[pe+1], &pe_tmp, sizeof ( pe_tmp));
            }
         } else {
            pe_last = pe;
            break;
         }
      }
   }
   /* END sort source PEs in ascending LE order */

   /* 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\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) {
         fprintf ( stderr, "%s -- ERROR \"%s\" pv_move_pe\n\n",
                           cmd, lvm_error ( ret));
         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_v < 0 || 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\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 */
   strcpy ( le_remap_req.lv_name, lv_name_this);
   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  = LVM_PE_DISK_OFFSET( pe_source, vg->pv[src_pv_index]);
   le_remap_req.new_pe  = LVM_PE_DISK_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\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 %lu:%lu on input "
                        "physical volume \"%s\"\n\n",
                cmd,
                ( long) ( offset >> 32),
                ( long) ( offset & 0xFFFFFFFF),
                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 %lu:%lu on output "
                        "physical volume \"%s\"\n\n",
                cmd,
                ( ulong) ( offset >> 32),
                ( ulong) ( offset & 0xFFFFFFFF),
                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) {
      if ( ( ret = pe_lock ( vg->vg_name,
                             vg->pv[src_pv_index]->pv_dev,
                             le_remap_req.old_pe,
                             vg->vg_number,
                             vg->pv[dst_pv_index]->\
                             pe[pe_dest].lv_num,
                             vg->lv[vg->pv[dst_pv_index]->\
                             pe[pe_dest].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 */
   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 */
   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)) != to_read) {
            fprintf ( stderr, "%s -- ERROR reading input "
                              "physical volume \"%s\" (still %d bytes to read)\n\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\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;
         }
         if ( fsync ( out) < 0  && (errno != EINVAL && errno != EROFS)) {
            fprintf ( stderr, "%s -- ERROR syncing output to "
                              "physical volume \"%s\"\n\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;
         }
      }
      size -= red;
   }

   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\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\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\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\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\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\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;
}
