/*
 * tools/vgchange.c
 *
 * Copyright (C) 1997 - 2002  Heinz Mauelshagen, Sistina Software
 *
 * March-May,October 1997
 * May,June,September 1998
 * February,July,October,December 1999
 * February,July 2000
 * June 2001
 * February 2002
 *
 * LVM is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * LVM 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with LVM; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA. 
 *
 */

/*
 * Changelog
 *
 *    11/10/1997 - added display of missing physical volumes
 *    12/10/1997 - added trying deactivation of inconsistent volume groups
 *    10/11/1997 - used lvm_tab
 *    09/05/1998 - implemented -x option
 *    16/05/1998 - added lvmtab checking
 *    27/06/1998 - changed lvm_tab_* calling convention
 *    20/09/1998 - changed message in deativation volume group
 *                 with open logical volumes
 *    03/08/1999 - avoided snapshot logical volumes during activation
 *    06/10/1999 - implemented support for long options
 *    25/12/1999 - check for vg_check_online_all_pv() error in case
 *                 pv_create_name_from_kdev_t() wasn't able to get a name,
 *                 because /proc isn't mounted
 *    15/02/2000 - use lvm_error()
 *    05/07/2000 - added reading copy on write exception table of snapshot
 *                 logical volumes using new function lv_read_COW_table()
 *    23/01/2001 - added call to lvm_init (JT)
 *    05/03/2001 - fixed call to lv_setup_COW_table_for_create() in order to
 *                 make use of the actual COW chunk size on disk rather than
 *                 the DEFAULT one
 *               - added check of the first array element after call to
 *                 lv_read_COW_table in order to print information about
 *                 the fact that the snapshot was dropped before VG deactivation
 *               - replaced vg->lv[l] by lv_ptr
 *    09/04/2001 - Tidy usage message and command-line options.
 *    22/06/2001 - added Andreas Dilger's PE on 4k boundary alignment enhancements
 *    12/02/2002 - drop snapshots where we find an error while setting up
 *                 or loading the COW table
 *
 */

#include <lvm_user.h>

char *cmd = NULL;

#ifdef DEBUG
int opt_d = 0;
#endif

#define	CREATE	1
#define	REMOVE	2

void usage ( int ret)
{
    FILE *stream = stderr;

    if (ret == 0) {
	stream = stdout;
	printf ( "%s (IOP %d)\n\n", lvm_version, LVM_LIB_IOP_VERSION);
	printf ( "%s -- change volume group settings\n", cmd);
    }

    fprintf ( stream, "\n%s "
	      "[-A|--autobackup {y|n}] "
              DEBUG_HELP_2
	      "[-h|--help]\n\t"
	      "{-a|--available {y|n} [VolumeGroupName...] |\n\t "
	      " -x|--allocation {y|n} [VolumeGroupName...]\n\t"
	      " -l|--logicalvolume MaxLogicalVolumes}\n\t"
	      "[-v|--verbose] "
	      "[--version]\n",
	      cmd);
    exit ( ret);
}


int main ( int argc, char **argv) {
   int backup = FALSE;
   int c = 0;
   int l = 0;
   int l_dest = 0;
   int max_lv = 0;
   int p = 0;
   int ret = 0;
   int set_extendable = FALSE;
   int v = 0;
   char *dir = NULL;
   char *vg_name = NULL;
   char *options = "A:a:h?l:vx:" DEBUG_SHORT_OPTION;
   struct option long_options[] = {
      { "autobackup",    required_argument, NULL, 'A'},
      { "available",     required_argument, NULL, 'a'},
      DEBUG_LONG_OPTION
      { "help",          no_argument,       NULL, 'h'},
      { "logicalvolume", required_argument, NULL, 'l'},
      { "verbose",       no_argument,       NULL, 'v'},
      { "allocation",    required_argument, NULL, 'x'},
      { "version",       no_argument,       NULL, 22 },
      { NULL, 0, NULL, 0}
   };
   char **vg_name_ptr = NULL;
   int opt_A = 1;
   int opt_A_set = 0;
   int opt_a = 0;
   int opt_a_action = 0;
   int opt_l = 0;
   int opt_v = 0;
   int opt_x = 0;
   int opt_x_action = 0;
   uint32_t pe_on_disk_base_sav;
   pv_t **pv_offl = NULL;
   pv_t **pv_incons = NULL;
   lv_t *lv_ptr = NULL;
   vg_t *vg = NULL;
   vg_t *vg_s = NULL;

   /* lvm_init(argc, argv); */
   cmd = basename ( argv[0]);

   LVMTAB_CHECK;

   while ( ( c = getopt_long ( argc, argv, options,
                               long_options, NULL)) != EOF) {
      switch ( c) {
         case 'A':
            opt_A_set++;
            if ( opt_A > 1) {
               fprintf ( stderr, "%s -- A option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_A++;
            if ( strcmp ( optarg, "y") == 0);
            else if ( strcmp ( optarg, "n") == 0) opt_A = 0;
            else {
               fprintf ( stderr, "%s -- invalid option argument \"%s\"\n\n",
                                 cmd, optarg);
               return LVM_EINVALID_CMD_LINE;
            }
            break;

         case 'a':
            if ( opt_a > 0) {
               fprintf ( stderr, "%s -- a option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_a++;
            if ( strcmp ( optarg, "y") == 0) {
               opt_a_action = CREATE;
               break;
            } else if ( strcmp ( optarg, "n") == 0) {
               opt_a_action = REMOVE;
               break;
            }
            fprintf ( stderr, "%s -- a option argument \"%s\" invalid\n\n",
                      cmd, optarg);
            return LVM_EINVALID_CMD_LINE;
            break;

         DEBUG_HANDLE_CASE_D;

         case 'h':
         case '?':
	     usage(0);
	     break;

         case 'l':
            if ( opt_l > 0) {
               fprintf ( stderr, "%s -- l option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            if ( ( max_lv = lvm_check_number ( optarg, FALSE)) > 0) {
               if ( max_lv < 1 || max_lv > ABS_MAX_LV) {
                  fprintf ( stderr, "%s -- MaxLogicalVolume has to be "
                                    "between 1 and %d\n\n",
                                    cmd, ABS_MAX_LV);
                  return LVM_EINVALID_CMD_LINE;
               }
               opt_l++;
            } else {
               fprintf ( stderr, "%s -- ERROR option l argument \"%s\"\n\n",
                                 cmd, optarg);
               return LVM_EINVALID_CMD_LINE;
            }
            break;

         case 'x':
            if ( opt_x > 0) {
               fprintf ( stderr, "%s -- x option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_x++;
            if ( strcmp ( optarg, "y") == 0) {
               opt_x_action = VG_EXTENDABLE;
               break;
            } else if ( strcmp ( optarg, "n") == 0) {
               opt_x_action = ~VG_EXTENDABLE;
               break;
            }
            fprintf ( stderr, "%s -- a option argument \"%s\" invalid\n\n",
                      cmd, optarg);
            return LVM_EINVALID_CMD_LINE;
            break;

         case 'v':
            if ( opt_v > 0) {
               fprintf ( stderr, "%s -- v option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_v++;
            break;

         case 22:
            printf ("%s: %s (IOP %d)\n", cmd, lvm_version, LVM_LIB_IOP_VERSION);
            return 0;

         default:
            fprintf ( stderr, "%s -- invalid command line option \"%c\"\n\n",
                      cmd, c);
            usage ( LVM_EINVALID_CMD_LINE );
      }
   }
  
   CMD_MINUS_CHK;
   CMD_CHECK_OPT_A_SET;


   if ( opt_a + opt_l + opt_x == 0) {
      fprintf ( stderr, "%s -- you have to give the -a, -l or the -x "
                        "option\n\n",
                        cmd);
      usage (LVM_EINVALID_CMD_LINE);
   }
   
   if ( opt_a + opt_l + opt_x > 1 ) {
      fprintf ( stderr, "%s -- you have to give only one option out of -a,"
                        " -l or -x\n\n",
                        cmd);
      return LVM_EINVALID_CMD_LINE;
   }
   
   if ( opt_a == 1 && opt_A > 1) {
      fprintf ( stderr, "%s -- -A option not necessary with "
                        "-a option\n\n", cmd);
      return LVM_EINVALID_CMD_LINE;
   }
   
   LVM_CHECK_IOP;
   LVM_LOCK ( 0);

   if ( optind < argc) {
      if ( opt_v > 0) printf ( "%s -- using volume group(s) on command line\n",
                               cmd);
      vg_name_ptr = argv + optind;
      argc -= optind;
   } else {
      if ( opt_v > 0) printf ( "%s -- finding all volume group(s)\n", cmd);
      vg_name_ptr = lvm_tab_vg_check_exist_all_vg ();
      argc = 0;
      if ( vg_name_ptr != NULL)
         for ( v = 0; vg_name_ptr[v] != NULL; v++) argc++;
   }
   argv = vg_name_ptr;
   optind = 0;

   if ( optind == argc) {
      printf ( "%s -- no volume groups found\n\n", cmd);
      return 0;
   }

   /* work on all given/found volume groups */
   for ( ; optind < argc; optind++) {
      vg_name = argv[optind];

      if ( opt_v > 0) printf ( "%s -- checking volume group name \"%s\"\n",
                               cmd, vg_name);
      if ( vg_check_name ( vg_name) < 0) {
         fprintf ( stderr, "%s -- invalid volume group name \"%s\"\n",
                   cmd, vg_name);
         continue;
      }
   
      if ( opt_a_action != REMOVE) {
         if ( opt_v > 0) printf ( "%s -- checking existence of volume "
                                  "group \"%s\"\n",
                                  cmd, vg_name);
         if ( ( ret = lvm_tab_vg_check_exist ( vg_name, NULL)) != TRUE) {
            if ( ret == -LVM_EPV_READ_PV_EXPORTED)
               fprintf ( stderr, "%s -- can't access exported volume "
                                 "group \"%s\"\n",
                                 cmd, vg_name);
            else if ( ret == -LVM_EVG_CHECK_EXIST_PV_COUNT)
               fprintf ( stderr, "%s -- ERROR: not all physical volumes of "
                                 "volume group \"%s\" online\n", cmd, vg_name);
            else if ( ret == -LVM_EPV_READ_ALL_PV_OF_VG_NP)
               fprintf ( stderr, "%s -- volume group \"%s\" doesn't exist\n",
                         cmd, vg_name);
            else if ( ret == -LVM_EVG_READ_LVM_STRUCT_VERSION)
               fprintf ( stderr, "%s -- volume group \"%s\" has physical "
                                 "volumes with invalid version\n",
                         cmd, vg_name);
            else
               fprintf ( stderr, "%s -- volume group \"%s\" does not exist\n",
                                 cmd, vg_name);
            continue;
         }
   
         if ( opt_v > 0) printf ( "%s -- reading volume group data for \"%s\" "
                                  "from lvmtab\n", cmd, vg_name);
         if ( vg != NULL) {
            vg_free ( vg, FALSE);
            vg = NULL;
         }
   
         if ( ( ret = lvm_tab_vg_read_with_pv_and_lv ( vg_name, &vg)) < 0) {
            if ( ret == -LVM_EVG_CFGRESTORE_OPEN) {
               fprintf ( stderr, "%s -- ERROR: volume group \"%s\" "
                                 "doesn't exist\n",
                                 cmd, vg_name);
            } else {
               fprintf ( stderr, "%s -- ERROR \"%s\" reading lvmtab data of "
                                 "volume group \"%s\"\n",
                                 cmd, lvm_error ( ret), vg_name);
            }
            continue;
         }
      
         if ( opt_v > 0) printf ( "%s -- checking volume group consistency "
                                  " of \"%s\"\n", cmd, vg_name);
         if ( ( ret = vg_check_consistency_with_pv_and_lv ( vg)) < 0) {
            fprintf ( stderr, "%s -- ERROR \"%s\" volume group \"%s\" "
                              " is inconsistent\n",
                              cmd, lvm_error ( ret), vg_name);
            continue;
         }
      }

      lvm_dont_interrupt ( 0);
   
      if ( opt_a > 0) {
         if ( opt_a_action == CREATE) {
            if ( opt_v > 0) printf ( "%s -- checking if all physical volumes "
                                     "of volume group \"%s\" are available\n",
                                     cmd, vg_name);
            if ( ( ret = vg_check_online_all_pv ( vg, &pv_offl, &pv_incons)) < 0
                 &&  ret != -LVM_EPV_READ_MD_DEVICE) {
               if ( ret == -LVM_EPV_READ_PV_CREATE_NAME_FROM_KDEV_T) {
                     fprintf ( stderr, "%s -- ERROR: can't get name(s) of "
                                       "physical volumes\n"
                                       "%s -- Please check, if /proc "
                                       "is mounted\n",
                                       cmd, cmd);
               } else {
                  if ( pv_offl != NULL) for ( p = 0; pv_offl[p] != NULL; p++) {
                     fprintf ( stderr, "%s -- ERROR: physical volume \"%s\" of "
                                       "volume group \"%s\" is offline\n",
                                       cmd, pv_offl[p]->pv_name, vg_name);
                  }
                  if ( pv_incons != NULL) for ( p = 0; pv_incons[p] != NULL; p++) {
                     fprintf ( stderr, "%s -- ERROR: physical volume \"%s\" of "
                                       "volume group \"%s\" is inconsistent\n",
                                       cmd, pv_incons[p]->pv_name, vg_name);
                  }
                  printf ( "%s -- run vgscan\n", cmd);
               }
               if ( pv_offl   != NULL) free ( pv_offl);
               if ( pv_incons != NULL) free ( pv_incons);
               continue;
            }
      
            /* check for snapshot logical volumes and get the COW table data */
            for ( l = 0; l < vg->lv_max; l++) {
               lv_ptr = vg->lv[l];
               if ( lv_ptr == NULL) continue;

               lv_ptr->lv_block_exception = NULL;
               lv_ptr->lv_remap_ptr = 0;
               lv_ptr->lv_remap_end = 0;
               lv_ptr->lv_snapshot_prev = NULL;
               lv_ptr->lv_snapshot_next = NULL;

               if ( lv_ptr->lv_access & LV_SNAPSHOT_ORG)
                   lv_ptr->lv_snapshot_org = lv_ptr;

               if ( lv_ptr->lv_access & LV_SNAPSHOT) {
                  if ( ( ret = lv_setup_COW_table_for_create ( vg, vg->lv[lv_get_index_by_minor(vg, lv_ptr->lv_snapshot_minor)]->lv_name, l, lv_ptr->lv_chunk_size / 2)) < 0) {
                     fprintf ( stderr, "%s -- ERROR \"%s\" setting up snapshot "
                                       "copy on write exception table for "
                                       "\"%s\"\n%s -- WARNING: dropping "
                                       "\"%s\"\n",
                                       cmd, lvm_error ( ret), lv_ptr->lv_name,
                                       cmd, lv_ptr->lv_name);
                     free ( lv_ptr->lv_current_pe);
                     free ( lv_ptr);
                     vg->lv[l] = NULL;
                     continue;
                  }
                  if ( ( ret = lv_read_COW_table ( vg, lv_ptr)) < 0) {
                     fprintf ( stderr, "%s -- ERROR %d reading copy on "
                                       "write exception table of \"%s\"\n"
                                       "%s -- WARNING: dropping \"%s\"\n",
                                       cmd, ret, lv_ptr->lv_name,
                                       cmd, lv_ptr->lv_name);
                     free ( lv_ptr->lv_current_pe);
                     free ( lv_ptr);
                     vg->lv[l] = NULL;
                     continue;
                  }
                  if ( lv_ptr->lv_block_exception[0].rsector_org ==
                       LVM_SNAPSHOT_DROPPED_SECTOR) {
                     lv_ptr->lv_status &= ~LV_ACTIVE;
                     /* Needs to be 0 to avoid that the driver tries
                        to hash link crap */
                     lv_ptr->lv_remap_ptr = 0;
                     printf ( "%s -- INFO: logical volume \"%s\" is a "
                              "dropped snapshot.\n"
                              "%s -- You can't use it any longer,"
                              " remove it please\n",
                              cmd, lv_ptr->lv_name,cmd);
                  }
               } else {
                  lv_ptr->lv_snapshot_minor = 0;
                  lv_ptr->lv_chunk_size = 0;
               }
            }

            if ( opt_v > 0) printf ( "%s -- creating VGDA for \"%s\" "
                                     "in kernel\n",
                                     cmd, vg_name);
            if ( ( ret = vg_create ( vg_name, vg)) == -LVM_EVG_CREATE_REMOVE_OPEN) {
                  vg_create_dir_and_group ( vg);
                  ret = vg_create ( vg_name, vg);
            }

            if ( ret == 0)
               printf ( "%s -- volume group \"%s\" successfully activated\n",
                        cmd, vg_name);
            else {
               debug ( "%s -- vg_create returned: %d\n", cmd, ret);
               if ( ret == -LVM_EVG_CREATE_REMOVE_OPEN) {
                  fprintf ( stderr, "%s -- can't open logical volume manager "
                                    "to activate volume group \"%s\"\n",
                                    cmd, vg_name);
               } else if ( ret == -EPERM) {
                  fprintf ( stderr, "%s -- volume group \"%s\" "
                                    "already active\n",
                                    cmd, vg_name);
               } else if ( ret == -ENOMEM) {
                  fprintf ( stderr, "%s -- memory error activating "
                                    "volume group \"%s\"\n",
                                    cmd, vg_name);
               } else {
                  fprintf ( stderr, "%s -- ERROR \"%s\" activating "
                                    "volume group \"%s\"\n",
                                    cmd, lvm_error ( ret), vg_name);
               }
               continue;
            }
         } else { /* VG_REMOVE */
            if ( opt_v > 0) printf ( "%s -- removing VGDA for volume "
                                     "group \"%s\" from kernel\n",
                                     cmd, vg_name);
            if ( ( ret = vg_remove ( vg_name)) == 0)
               printf ( "%s -- volume group \"%s\" successfully deactivated\n",
                        cmd, vg_name);
            else {
               debug ( "%s -- vg_remove returned: %d\n", cmd, ret);
               if ( ret == -LVM_EVG_CREATE_REMOVE_OPEN) {
                  fprintf ( stderr, "%s -- couldn't  open volume group \"%s\" "
                                    "group special file\n"
                                    "%s -- try vgmknodes\n",
                                    cmd, vg_name, cmd);
               } else if ( ret == -EPERM) {
                  if ( opt_v > 0)
                     printf ( "%s -- getting volume group status from "
                              "VGDA in kernel\n", cmd);
                  if ( vg_status ( vg_name, &vg_s) < 0) {
                     fprintf ( stderr, "%s -- can't get status of volume group "
                                       "\"%s\" from kernel\n",
                                       cmd, vg_name);
                  } else {
                     fprintf ( stderr, "%s -- can't deactivate volume group "
                                       "\"%s\" with %d open logical volume%s\n",
                                       cmd, vg_name, vg_s->lv_open,
                                       vg_s->lv_open > 1 ? "s" : "");
                  }
               } else if ( ret == -ENXIO) {
                   fprintf ( stderr, "%s -- volume group \"%s\" isn't active\n",
                                     cmd, vg_name);
               } else {
                   fprintf ( stderr, "%s -- ERROR \"%s\" DEactivating volume "
                                     "group \"%s\"\n",
                                     cmd, lvm_error ( ret), vg_name);
               }
               continue;
            }
         }
      }

      backup = FALSE;
      if ( opt_x > 0) {
         if ( opt_x_action == VG_EXTENDABLE) {
            if ( vg->vg_status & VG_EXTENDABLE) {
               printf ( "%s -- volume group \"%s\" is extendable\n",
                        cmd, vg->vg_name);
            } else {
               vg->vg_status |= VG_EXTENDABLE;
               set_extendable = TRUE;
               backup = TRUE;
            }
         } else {
            if ( ! ( vg->vg_status & VG_EXTENDABLE)) {
               printf ( "%s -- volume group \"%s\" is not extendable\n",
                        cmd, vg_name);
            } else {
               vg->vg_status &= ~VG_EXTENDABLE;
               set_extendable = FALSE;
               backup = TRUE;
            }
         }
         if ( backup == TRUE) {
            /* store vg on disk(s) */
            if ( opt_v > 0) printf ( "%s -- storing volume group data of "
                                     "%s on disk(s)\n", cmd, vg_name);
            if ( ( ret = vg_write_with_pv_and_lv ( vg)) < 0) {
               fprintf ( stderr, "%s -- ERROR \"%s\"storing volume group data "
                                 "of \"%s\" on disk(s)\n\n",
                                 cmd, lvm_error ( ret), vg_name);
               return LVM_EVGCHANGE_VG_WRITE;
            }
      
            if ( vg_check_active ( vg_name) == TRUE) {
               if ( opt_v > 0) printf ( "%s -- changing VGDA in kernel\n", cmd);
               if ( set_extendable == TRUE) {
                  ret = vg_set_extendable ( vg_name);
               } else {
                  ret = vg_clear_extendable ( vg_name);
               }
               if ( ret < 0) {
                  fprintf ( stderr, "%s -- ERROR \"%s\" ",
                                    cmd, lvm_error ( ret));
                  if ( set_extendable == TRUE) {
                      fprintf ( stderr, "en");
                  } else {
                      fprintf ( stderr, "dis");
                 }
                 fprintf ( stderr, "abling extension of volume group \"%s\"\n\n",
                                   vg_name);
                 return LVM_EVGCHANGE_VG_EXTEND;
               }
            }

            if ( opt_v > 0) printf ( "%s -- changing lvmtab\n", cmd);
            dir = LVMTAB_DIR;
            if ( ( ret = vg_cfgbackup ( vg_name, dir, opt_v, vg)) == 0 &&
                 opt_A > 0) {
               printf ( "%s -- doing automatic backup of volume group \"%s\"\n",
                        cmd, vg_name);
               dir = VG_BACKUP_DIR;
               ret = vg_cfgbackup ( vg_name, dir, opt_v, vg);
            }
            if ( ret == 0) {
               printf ( "%s -- volume group \"%s\" successfully changed\n\n",
                        cmd, vg_name);
            } else {
               fprintf ( stderr, "%s -- ERROR \"%s\" storing volume group "
                                 "backup of \"%s\" in %s\n\n",
                                 cmd, lvm_error ( ret), vg_name, dir);
               return LVM_EVGCHANGE_VG_CFG_BACKUP;
            }
         }
      }

      /* change MaxLogicalVolume */
      if ( opt_l > 0) {
         pv_t *pp;

         if ( opt_v > 0) printf ( "%s -- checking for volume group \"%s\" "
                                  "activity\n", cmd, vg_name);
         if ( vg_check_active ( vg_name) == TRUE) {
            fprintf ( stderr, "%s -- MaxLogicalVolume can't be changed "
                              "in active volume group \"%s\"\n",
                              cmd, vg_name);
            continue;
         }

         if ( ! ( vg->vg_status & VG_EXTENDABLE)) {
            fprintf ( stderr, "%s -- volume group \"%s\"must be extendable "
                              "to change MaxLogicalVolume\n",
                              cmd, vg_name);
            continue;
         }

         if ( opt_v > 0) printf ( "%s -- checking if MaxLogicalVolume fits\n",
                                  cmd);
         if ( max_lv <= vg->lv_cur) {
            fprintf ( stderr, "%s -- MaxLogicalVolume is %s "
                              "the current logical volume number of \"%s\"\n",
                              cmd,
                              max_lv < vg->lv_cur ? "smaller than" : "equal to",
                              vg_name);
            continue;
         }

         if ( opt_v > 0) printf ( "%s -- rearanging VGDA structures of "
                                  "\"%s\"\n", cmd, vg_name);
         l_dest = 0;
         for ( l = 0; l < vg->lv_max; l++) {
            if ( vg->lv[l] == NULL) {
                l_dest = l;
            } else if ( vg->lv[l_dest] == NULL) {
               vg->lv[l_dest] = vg->lv[l];
               vg->lv[l] = NULL;
               vg->lv[l_dest]->lv_number = l_dest + 1;
               l_dest = l;
            }
         }
         for ( ; l < max_lv; l++) vg->lv[l] = NULL;

         vg->lv_max = max_lv;
         for ( p = 0, pp = *vg->pv; p < vg->pv_max; p++, pp++) {
            if ( pp != NULL) {
		    pp->lv_on_disk.size =
			    round_up((max_lv + 1) * sizeof ( lv_disk_t),
				     LVM_VGDA_ALIGN);
               pe_on_disk_base_sav = pp->pe_on_disk.base;
               pp->pe_on_disk.base = pp->lv_on_disk.base + pp->lv_on_disk.size;
               pp->pe_on_disk.size -= pp->pe_on_disk.base - pe_on_disk_base_sav;
               if ( LVM_VGDA_SIZE ( pp) / SECTOR_SIZE >
                    (pp->pv_size & ~LVM_PE_ALIGN) - pp->pe_total * pp->pe_size){
                  fprintf ( stderr, "%s -- extended VGDA would overlap "
                                    "first physical extent\n\n",
                                    cmd);
                  return LVM_EVGCHANGE_PE_OVERLAP;
               }
            }
         }

         if ( ( ret = vg_write_with_pv_and_lv ( vg)) < 0) {
            fprintf ( stderr, "%s -- ERROR \"%s\" storing volume group data "
                              "of \"%s\" on disks\n\n", 
                              cmd, lvm_error ( ret), vg_name);
            return LVM_EVGCHANGE_VG_WRITE;
         }

         if ( ( ret = vg_cfgbackup ( vg_name, LVMTAB_DIR, opt_v, vg)) == 0) {
            if ( opt_A > 0) {
               printf ( "%s -- doing automatic backup of volume group \"%s\"\n",
                        cmd, vg_name);
               if ( ( ret = vg_cfgbackup ( vg_name, VG_BACKUP_DIR,
                                           opt_v, vg)) < 0) {
                  fprintf ( stderr, "%s -- ERROR \"%s\" writing VG backup of "
                                    "\"%s\"\n\n",
                                    cmd, lvm_error ( ret), vg_name);
                  return LVM_EVGCHANGE_VG_CFGBACKUP;
               }
            } else {
               printf ( "%s -- WARNING: you don't have an automatic "
                        "backup of \"%s\"\n",
                        cmd, vg_name);
            }
         } else {
            fprintf ( stderr, "%s -- ERROR \"%s\" writing \"%s\"\n\n",
                              cmd, lvm_error ( ret), LVMTAB);
            return LVM_EVGCHANGE_VG_CFGBACKUP_LVMTAB;
         }

         if ( opt_v > 0) printf ( "%s -- removing directory and nodes of "
                                  "volume group \"%s\"\n",
                                  cmd, vg_name);
         if ( ( ret = vg_remove_dir_and_group_and_nodes ( vg_name)) < 0) {
            fprintf ( stderr, "%s -- ERROR \"%s\" removing volume group "
                              "nodes of \"%s\"\n\n",
                              cmd, lvm_error ( ret), vg_name);
            return LVM_EVGCHANGE_VG_REMOVE_DIR_AND_GROUP_NODES;
         }
      
         if ( ( ret = vg_create_dir_and_group_and_nodes ( vg, opt_v)) < 0) {
            fprintf ( stderr, "%s -- ERROR \"%s\" creating volume group "
                              "nodes of \"%s\"\n\n",
                              cmd, lvm_error ( ret), vg_name);
            return LVM_EVGCHANGE_VG_CREATE_DIR_AND_GROUP_NODES;
         }
      } /* if ( opt_l > 0) */
      lvm_interrupt ();
   }

   putchar ( '\n');

   if ( vg != NULL) vg_free ( vg, FALSE);
   LVM_UNLOCK ( 0);

   return 0;
}
