/*
 * tools/lvcreate.c
 *
 * Copyright (C) 1997 - 2002  Heinz Mauelshagen, Sistina Software
 *
 * March,October,November 1997
 * January,May-July,September,December 1998
 * January,February,July,October 1999
 * February,July,September,November 2000
 * January,March,June,July 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
 *
 *    08/10/1997 - added initialization of logical volume (1st KB zero)
 *    08/11/1997 - added message if default stripe size used
 *    09/11/1997 - added lvmtab handling
 *    18/01/1998 - added option z to give possibility to disallow zeroing 
 *                 of newly created logical volumes
 *    29/04/1998 - changed to new lv_create_node()
 *    16/05/1998 - added lvmtab checking
 *    26/05/1998 - added message in case of low space
 *    12/06/1998 - enhanced checking numbers in option arguments
 *    14/06/1998 - supported physical volumes on command line for
 *                 striped logical volumes
 *    27/06/1998 - changed lvm_tab_* calling convention
 *    05/07/1998 - fixed display bug with option verbose and
 *                 physical volumes on command line
 *    09/07/1998 - fixed VG name bug
 *                 (VG name starting with "/dev/" was an error);
 *                 Thanks to Pascal van Dam <pascal@ramoth.xs4all.nl>
 *    02/09/1998 - corrected some messages
 *    20/12/1998 - added check for volume group existence
 *    26/01/1999 - made volume group directory prefix a preprocessor option
 *    28/01/1999 - fixed logical volume path too long in command line
 *    06/02/1999 - fixed lvm_check_number() usage
 *    16/02/1999 - changed to new lv_create_node() calling convention
 *    26/07/1999 - support snapshot logical volume creation
 *    06/10/1999 - implemented support for long options
 *    16/11/1999 - checked for option -l or -L given on command line
 *    15/02/2000 - use lvm_error()
 *    16/02/2000 - use new function lv_setup_snapshot_exception_table()
 *    18/02/2000 - check existance of original logical volume with
 *                 lvm_tab_lv_check_exist()
 *    07/11/2000 - used LVM_VG_NAME environment variable
 *    22/01/2001 - changed types of vars for lvm_check_number() to signed
 *                 in order to have correct check results on them
 *               - fixed chunk size default unit bug
 *    23/01/2001 - added call to lvm_init (JT)
 *    29/01/2001 - changed type of size var for correct use
 *                 with lvm_check_number() I forgot last time
 *    13/03/2001 - changed snapshot disable warning message to better
 *                 discribe the drop behaviour
 *    20/03/2001 - fixed bug leading to wrong stripe size (-I) if stripe
 *                 size was given before stripes (-i) in command line
 *    05/03/2001 - fixed segfault in case no snapshot name was entered (JW)
 *    06/26/2001 - fixed misleading error message in case the original logical
 *                 volume name whas given wrong or incomplete
 *               - fixed wrong snapshot chunk size message
 *    07/11/2001 - Add usage message, clean up printf [AED]
 *    25/07/2001 - lv_path->lv_name in err mesg (AGK)
 *    07/02/2002 - fixes for > 1TB support
 *    14/02/2002 - use lvm_check_number_ll() for (long long) numbers
 *
 */

/*
 * TODO
 *
 * allow striped volume creation on multiple stripe count volumes
 * (lv_setup_for_create() and lv_setup_for_extend concerned())
 *
 */


#include <lvm_user.h>

char *cmd = NULL;

#ifdef DEBUG
int opt_d = 0;
#endif

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

   if ( ret == 0) {
      printf ( "%s (IOP %d)\n\n", lvm_version, LVM_LIB_IOP_VERSION);
      printf ( "%s -- initialize a logical volume for use by LVM\n", cmd);
      stream = stdout;
   }

   fprintf ( stream, "\n%s "
             "[-A|--autobackup {y|n}] "
             "[-C|--contiguous {y|n}] "
	     DEBUG_HELP_2 "\n"
             "[-h|--help] "
             "[-i|--stripes Stripes [-I|--stripesize StripeSize]]\n\t"
             "{-l|--extents LogicalExtentsNumber |\n\t"
             " -L|--size LogicalVolumeSize[kKmMgGtT]} "
             "[-n|--name LogicalVolumeName]\n\t"
             "[-p|--permission {r|rw}] "
             "[-r|--readahead ReadAheadSectors]\n\t"
             "[-v|--verbose] "
             "[-Z|--zero {y|n}] "
             "[--version]\n\t"
             "VolumeGroupName [PhysicalVolumePath...]\n\n"
             "%s "
             "-s|--snapshot "
             "[-c|--chunksize ChunkSize]\n\t"
             "{-l|--extents LogicalExtentsNumber |\n\t"
             " -L|--size LogicalVolumeSize[kKmMgGtT]}\n\t"
             "-n|--name SnapshotLogicalVolumeName\n\t"
             "LogicalVolume[Path] [PhysicalVolumePath...]\n",
             cmd, cmd);
   exit ( ret);
}

int main ( int argc, char **argv) {
   int active = 1;
   int c = 0;
   int chunk_size = LVM_SNAPSHOT_DEF_CHUNK;
   int i = 0;
   int l = 0;
   int lv_handle = -1;
   int pa = 0;
   int opt_A = 1;
   int opt_A_set = 0;
   int opt_c = 0;
   int opt_C = 0;
   int opt_i = 0;
   int opt_I = 0;
   int opt_l = 0;
   int opt_L = 0;
   int opt_n = 0;
   int opt_p = 0;
   int opt_r = 0;
   int opt_s = 0;
   int opt_v = 0;
   int opt_Z = 1;
   int ret = 0;
   int size_rest = 0;
   long long size = 0;
   long read_ahead = 0;
   uint allocation = 0;
   uint mirrors = 0;
   uint permission = LV_READ | LV_WRITE;
   int stripes = 1;
   int stripesize = LVM_DEFAULT_STRIPE_SIZE;
   ulong pe_free = 0;
   char buffer[NAME_LEN];
   char *options = "A:c:C:h?i:I:l:L:n:p:r:svZ:" DEBUG_SHORT_OPTION;
   struct option long_options[] = {
      { "active",      required_argument, NULL, 'a'},
      { "autobackup",  required_argument, NULL, 'A'},
      { "chunksize",   required_argument, NULL, 'c'},
      { "contiguous",  required_argument, NULL, 'C'},
      DEBUG_LONG_OPTION
      { "help",        no_argument,       NULL, 'h'},
      { "stripes",     required_argument, NULL, 'i'},
      { "stripesize",  required_argument, NULL, 'I'},
      { "extents",     required_argument, NULL, 'l'},
      { "size",        required_argument, NULL, 'L'},
      { "name",        required_argument, NULL, 'n'},
      { "permissions", required_argument, NULL, 'p'},
      { "readahead",   required_argument, NULL, 'r'},
      { "snapshot",    no_argument,       NULL, 's'},
      { "verbose",     no_argument,       NULL, 'v'},
      { "version",     no_argument,       NULL,  22},
      { "zero",        required_argument, NULL, 'Z'},
      { NULL,          0,                 NULL, 0},
   };
   char *dummy = NULL;
   char *dummy1 = NULL;
   char *lv_name_ptr = NULL;
   char *vg_name = NULL;
   char **pv_allowed = NULL;
   char lv_name[NAME_LEN] = { 0, };
   char lv_snapshot_name[NAME_LEN] = { 0, };
   char lv_path[NAME_LEN] = { 0, };
   pv_t *pv = NULL;
   vg_t *vg = NULL;
   vg_t *vg_core = NULL;
   lv_t *lv = NULL;
   struct stat stat_buf;

   /* 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++;
            opt_A++;
            if ( optarg[0] == 'n') opt_A = 0;
            else if (optarg[0] != 'y') {
               fprintf ( stderr, "%s -- ERROR option A argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         case 'c':
            if ( ( chunk_size = lvm_check_number ( optarg, TRUE)) > 0) {
               /* if no unit has been given --> assume kilobyte */
               if ( isdigit (optarg[strlen ( optarg)-1])) chunk_size /= 1024;
               if ( chunk_size < LVM_SNAPSHOT_MIN_CHUNK ||
                    chunk_size > LVM_SNAPSHOT_MAX_CHUNK ||
                    ( chunk_size & ( chunk_size - 1)) != 0)  {
                  dummy  = lvm_show_size ( LVM_SNAPSHOT_MIN_CHUNK, SHORT);
                  dummy1 = lvm_show_size ( LVM_SNAPSHOT_MAX_CHUNK, SHORT);
                  fprintf ( stderr, "%s -- ERROR: chunk size must be >= %s "
                                    "and <= %s and power of 2\n",
                                    cmd, dummy, dummy1);
                  free ( dummy);
                  free ( dummy1);
                  usage ( LVM_EINVALID_CMD_LINE);
               }
               opt_c++;
            } else {
               fprintf ( stderr, "%s -- ERROR option c argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         case 'C':
            opt_C++;
            if ( optarg[0] == 'y')
               allocation |= LV_CONTIGUOUS;
            else if ( optarg[0] != 'n') {
               fprintf ( stderr, "%s -- ERROR option C argument \"%s\"\n",
                         cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         DEBUG_HANDLE_CASE_D;

         case 'h':
            usage ( 0);

         case 'i':
            if ( ( stripes = lvm_check_number ( optarg, FALSE)) > 0) {
               opt_i++;
            } else {
               fprintf ( stderr, "%s -- ERROR option i argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         case 'I':
            if ( ( stripesize = lvm_check_number ( optarg, FALSE)) > 0) {
               opt_I++;
            } else {
               fprintf ( stderr, "%s -- ERROR option I argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         case 'l':
            if ( opt_L > 0) {
               fprintf ( stderr, "%s -- ERROR option L already given\n", cmd);
               usage ( LVM_EINVALID_CMD_LINE);
            }

            if ( ( size = lvm_check_number_ll ( optarg, FALSE)) > 0) {
               opt_l++;
            } else {
               fprintf ( stderr, "%s -- ERROR option l argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         case 'L':
            if ( opt_l > 0) {
               fprintf ( stderr, "%s -- ERROR option l already given\n", cmd);
               usage ( LVM_EINVALID_CMD_LINE);
            }

            if ( ( size = lvm_check_number_ll ( optarg, TRUE)) > 0) {
               opt_L++;
            } else {
               fprintf ( stderr, "%s -- ERROR option L argument \"%s\"\n\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         case 'n':
            if ( strchr ( optarg, '/') != NULL) {
               fprintf ( stderr, "%s -- ERROR option n argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            strncpy ( lv_name, optarg, sizeof ( lv_name) - 1);
            lv_name[sizeof ( lv_name) - 1] = 0;
            break;

         case 'p':
            opt_p++;
            if ( strcmp ( optarg, "r") == 0)
               permission = LV_READ;
            else if ( strcmp ( optarg, "rw")) {
               fprintf ( stderr, "%s -- ERROR option p argument \"%s\"\n",
                         cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         case 'r':
            if ( ( read_ahead = lvm_check_number ( optarg, FALSE)) < 0) {
               fprintf ( stderr, "%s -- ERROR option r argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            opt_r++;
            break;

         case 's':
            permission = LV_READ;
            opt_s++;
            opt_Z = 0;
            break;

         case 'v':
            opt_v++;
            break;

         case 'Z':
            opt_Z++;
            if ( optarg[0] == 'n') opt_Z = 0;
            else if ( optarg[0] != 'y') {
               fprintf ( stderr, "%s -- ERROR option Z argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            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",
                      cmd, c);
         case '?':
            usage ( LVM_EINVALID_CMD_LINE);
      }
   }

   /* Not until all commands are converted
   CMD_MINUS_CHK;
    */
   if ( optind < argc && *argv[optind] == '-') {
      fprintf ( stderr, "%s -- invalid command line\n", cmd);
      usage ( LVM_EINVALID_CMD_LINE);
   }
   CMD_CHECK_OPT_A_SET;

   if ( opt_L + opt_l == 0) {
      fprintf ( stderr, "%s -- please enter either option -l or -L\n\n",
                        cmd);
      usage ( LVM_EINVALID_CMD_LINE);
   }

   if ( optind == argc) {
      /* snapshot logical volume */
      if ( opt_s > 0) {
         fprintf ( stderr,
                   "%s -- please enter logical volume name for snapshooting\n",
                   cmd);
      } else {
         LVM_GET_DEFAULT_VG_NAME( vg_name, buffer, NAME_LEN);
         if ( vg_name == NULL)
            fprintf ( stderr, "%s -- please enter a volume group name\n", cmd);
      }
      if ( vg_name == NULL)
         usage ( LVM_ELVCREATE_VG_NAME);
   }

   /* snapshot logical volume */
   if ( opt_s > 0) {
      /* these options are not allowed for snapshot */
      if ( opt_C + opt_i + opt_I + opt_n + opt_p + opt_r + opt_Z > 0) {
         fprintf ( stderr, "%s -- invalid command line\n", cmd);
         usage ( LVM_EINVALID_CMD_LINE);
      }
      memset ( lv_snapshot_name, 0, sizeof ( lv_snapshot_name));
      strncpy ( lv_snapshot_name, lv_name, sizeof ( lv_name) - 1);
      dummy = argv[optind];
      LVM_CHECK_DEFAULT_VG_NAME ( dummy, buffer, NAME_LEN);
      if (dummy == NULL) {
         fprintf ( stderr, "%s -- original logical volume name invalid!\n\n",
                           cmd);
         usage ( LVM_EINVALID_CMD_LINE);
      }
      strncpy ( lv_name, dummy, sizeof ( lv_name) - 1);
      dummy = NULL;
      lv_name[sizeof ( lv_name) - 1] = 0;
      if ( lv_check_name ( lv_name) < 0) {
         fprintf ( stderr, "%s -- invalid original logical volume name '%s'\n",
                   cmd, lv_name);
         usage ( LVM_ELVCREATE_LV_NAME);
      }
      vg_name = vg_name_of_lv ( lv_name);
      memset ( lv_path, 0, sizeof ( lv_path));
      snprintf ( lv_path, sizeof ( lv_path) - 1,
                          LVM_DIR_PREFIX "%s/%s", vg_name, lv_snapshot_name);

      if ( opt_v > 0)
         printf ( "%s -- checking snapshot logical volume name \"%s\"\n",
                  cmd, lv_path);
      if ( lv_check_name ( lv_path) < 0) {
         fprintf ( stderr,
                   "%s -- no valid snapshot logical volume name \"%s\"\n",
                   cmd, lv_path);
         return LVM_ELVCREATE_LV_NAME;
      } else if ( lstat ( lv_path, &stat_buf) != -1) {
         lvm_show_filetype ( stat_buf.st_mode, lv_path);
         return LVM_ELVCREATE_LV_LSTAT;
      } else if ( lvm_tab_lv_check_exist ( lv_path) == TRUE) {
         fprintf ( stderr,
                   "%s -- snapshot logical volume \"%s\" already exists\n",
                   cmd, lv_path);
         return LVM_ELVCREATE_LV_CHECK_EXIST;
      }
   } else {
      if ( vg_name == NULL) vg_name = argv[optind];
   }
   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 -- no valid volume group name \"%s\"\n",
                cmd, vg_name);
      usage ( LVM_ELVCREATE_VG_NAME);
   }

   /* does VG exist? */
   if ( opt_v > 0) printf ( "%s -- checking volume group existence\n", cmd);
   if ( lvm_tab_vg_check_exist ( vg_name, NULL) != TRUE) {
      fprintf ( stderr, "%s -- can't create logical volume: volume group "
                        "\"%s\" doesn't exist\n\n", cmd, vg_name);
      usage ( LVM_ELVCREATE_VG_CHECK_EXIST);
   }

   if ( opt_v > 0) printf ( "%s -- checking volume group activity\n", cmd);
   if ( vg_check_active ( vg_name) != TRUE) {
      fprintf ( stderr, "%s -- can't create logical volume:"
                        " \"%s\" isn't active\n\n", cmd, vg_name);
      return LVM_ELVCREATE_VG_CHECK_ACTIVE;
   }

   if ( opt_s == 0 && strlen ( lv_name) > 0) {
      if ( strlen ( LVM_DIR_PREFIX) + strlen ( vg_name) +
           strlen ( lv_name) + 1 > NAME_LEN - 1) {
         fprintf ( stderr, "%s -- the logical volume path is longer than "
                           "the maximum of %d!\n\n",
                           cmd, sizeof ( lv_path));
         return LVM_ELVCREATE_LV_CHECK_NAME;
      }
      memset ( lv_path, 0, sizeof ( lv_path));
      snprintf ( lv_path, sizeof ( lv_path) - 1,
                          LVM_DIR_PREFIX "%s/%s", vg_name, lv_name);

      if ( opt_v > 0) printf ( "%s -- checking logical volume path \"%s\"\n",
                               cmd, lv_path);
      if ( lv_check_name ( lv_path) < 0) {
         fprintf ( stderr, "%s -- invalid logical volume name \"%s\"\n",
                           cmd, lv_name);
         usage ( LVM_ELVCREATE_LV_CHECK_NAME);
      } else if ( lstat ( lv_path, &stat_buf) != -1) {
         lvm_show_filetype ( stat_buf.st_mode, lv_path);
         return LVM_ELVCREATE_LV_LSTAT;
      } else if ( lvm_tab_lv_check_exist ( lv_path) == TRUE) {
         fprintf ( stderr, "%s -- logical volume \"%s\" already exists\n",
                           cmd, lv_path);
         return LVM_ELVCREATE_LV_CHECK_EXIST;
      }
   }

   /* physical volumes in command line */
   if ( optind < argc) {
      if ( opt_v > 0) printf ( "%s -- checking physical volume names\n", cmd);
      for ( i = optind; i < argc; i++) {
         if ( pv_check_name ( argv[i]) < 0) {
            fprintf ( stderr,
                      "%s -- \"%s\" is an invalid physical volume name\n\n",
                      cmd, argv[i]);
            return LVM_ELVCREATE_PV_CHECK_NAME;
         }
      }
      pv_allowed = &argv[optind];
   }

   if ( opt_i) {
      if ( opt_I == 0)
         printf ( "%s -- INFO: using default stripe size %lu KB\n",
                  cmd,  LVM_DEFAULT_STRIPE_SIZE);
      if ( pv_allowed != NULL && argc - optind != stripes) {
         fprintf ( stderr,
                   "%s -- invalid number of physical volumes on command line\n",
                   cmd);
         return LVM_ELVCREATE_PV_NUMBER;
      }
      if ( stripes == 1) {
         printf ( "%s -- redundant option i with value 1 is default\n", cmd);
         if ( opt_I > 0)
            printf ( "%s -- option I with one stripe ignored\n", cmd);
      }
   }

   if ( opt_s == 0) {
      if ( opt_v > 0) printf ( "%s -- checking stripe count\n", cmd);
      if ( stripes < 1 || stripes > LVM_MAX_STRIPES) {
         fprintf ( stderr, "%s -- invalid number of stripes: %d\n"
                           "%s -- must be between %d and %d\n",
                           cmd, stripes, cmd, 1, LVM_MAX_STRIPES);
         usage ( LVM_ELVCREATE_STRIPES);
      }

      if ( opt_v > 0) printf ( "%s -- checking stripe size\n", cmd);
      if ( stripesize > 0 && lv_check_stripesize ( stripesize) < 0) {
         fprintf ( stderr, "%s -- invalid stripe size %d\n\n",
                   cmd, stripesize);
         usage ( LVM_ELVCREATE_STRIPE_SIZE);
      }
      stripesize *= 2;
   }

   if ( stripes == 1) stripesize = 0;

   LVM_CHECK_IOP;
   LVM_LOCK ( 0);

   if ( opt_v > 0)
      printf ( "%s -- getting volume group status from VGDA in kernel\n", cmd);
   if ( ( ret = vg_status_with_pv_and_lv ( vg_name, &vg_core)) < 0) {
      fprintf ( stderr, "%s -- ERROR \"%s\" getting status of volume group "
                        "\"%s\" from kernel\n\n",
                        cmd, lvm_error ( ret), vg_name);
      return LVM_ELVCREATE_VG_STATUS;
   }

   if ( opt_s == 0) {
      if ( opt_v > 0) printf ( "%s -- checking stripe size against "
                               "volume group physical extent size\n", cmd);
      if ( stripesize > vg_core->pe_size) {
         fprintf ( stderr, "%s -- setting stripe size %d KB to "
                           "physical extent size %u KB\n",
                           cmd, stripesize / 2, vg_core->pe_size / 2);
         stripesize = vg_core->pe_size;
      }
   }

   if ( opt_L > 0) size *= 2LL;
   else            size *= vg_core->pe_size;

   size_rest = size % vg_core->pe_size;
   if ( size_rest > 0) {
      printf ( "%s -- rounding size up to physical extent boundary\n", cmd);
      size += vg_core->pe_size - size_rest;
   }

   /* So that -L2t can be given */
   if ( size > LVM_LV_SIZE_2TB( vg_core)) size -= vg_core->pe_size;

   if ( stripes > 1) {
      size_rest = size % ( stripes * vg_core->pe_size);
      if ( size_rest != 0LL) {
         printf ( "%s -- rounding to stripe boundary size\n", cmd);
         size = size - size_rest + stripes * vg_core->pe_size;
      }
      if ( size > LVM_LV_SIZE_2TB( vg_core))
         size -= stripes * vg_core->pe_size;
   }

   if ( opt_v > 0)
      printf ( "%s -- checking logical volume maximum size\n", cmd);
   if ( size > LVM_LV_SIZE_2TB( vg_core)) {
      fprintf ( stderr, "%s -- size %Ld KB is larger than maximum VGDA "
                        "kernel size of %Ld KB\n",
                        cmd, size / 2, LVM_LV_SIZE_2TB( vg_core) / 2);
      usage ( LVM_ELVCREATE_LV_SIZE);
   }

   if ( opt_s > 0) {
      if ( opt_v > 0)
         printf ( "%s -- getting logical volume status from kernel\n", cmd);
      if ( lvm_tab_lv_check_exist ( lv_name) != TRUE) {
         fprintf ( stderr,
                   "%s -- original logical volume \"%s\" doesn't exist\n",
                   cmd, lv_name);
         usage ( LVM_ELVCREATE_LV_CHECK_EXIST);
      }
      if ( ( ret = lv_status_byname ( vg_name, lv_name, &lv)) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" getting status of logical "
                           "volume \"%s\"\n\n",
                           cmd, lvm_error ( ret), lv_name);
         return LVM_ELVCREATE_LV_STATUS_BYNAME;
      }
      if ( lv->lv_access & LV_SNAPSHOT) {
         fprintf ( stderr, "%s -- ERROR: can't create snapshot of "
                           "snapshot \"%s\"\n"
                           "%s -- please use original logical volume name\n",
                           cmd, lv_name, cmd);
         usage ( LVM_EINVALID_CMD_LINE);
      }
      if ( size > lv->lv_size * 1.1) {
         fprintf ( stderr, "%s -- ERROR: size of snapshot is too large\n",
                           cmd);
         return LVM_EINVALID_CMD_LINE;
      } else if ( size < lv->lv_size) {
         printf ( "%s -- WARNING: the snapshot will be automatically "
                  "disabled once it gets full\n", cmd);
      }
   }

   if ( opt_v > 0)
      printf ( "%s -- reading volume group data of \"%s\"\n", cmd, vg_name);
   if ( ( ret = lvm_tab_vg_read_with_pv_and_lv ( vg_name, &vg)) < 0) {
      fprintf ( stderr,
                "%s -- ERROR \"%s\" couldn't get volume group data of \"%s\"\n",
                cmd, lvm_error ( ret), vg_name);
      return LVM_ELVCREATE_VG_READ;
   }

   if ( opt_v > 0) printf ( "%s -- checking volume group free space\n", cmd);
   pe_free = vg->pe_total - vg->pe_allocated;
   if ( pe_free == 0) {
      fprintf ( stderr,
                "%s -- no free physical extents in volume group \"%s\"\n",
                cmd, vg_name);
      return LVM_ELVCREATE_PE_FREE;
   } else if ( size / vg->pe_size > pe_free) {
      fprintf ( stderr,
                "%s -- only %lu free physical %s in volume group \"%s\"\n",
                cmd, pe_free, pe_free > 1 ? "extents" : "extent", vg_name);
      return LVM_ELVCREATE_PE_FREE;
   }

   if ( opt_s == 0) {
      if ( opt_v > 0)
         printf ( "%s -- checking stripe count against physical volume count\n",
                  cmd);
      if ( stripes > vg->pv_cur) {
         fprintf ( stderr,
                   "%s -- %u stripes more than %u physical volumes in \"%s\"\n",
                   cmd, stripes, vg->pv_cur, vg_name);
         return LVM_ELVCREATE_STRIPE_COUNT;
      }
   }

   if ( opt_s > 0 && ( ( chunk_size * 1024) / SECTOR_SIZE) > vg->pe_size) {
      dummy = lvm_show_size ( ( vg->pe_size * SECTOR_SIZE) / 1024, SHORT);
      fprintf ( stderr, "%s -- ERROR: snapshot chunk size must be less "
                        "than or equal to PE size %s / 2!\n\n",
                        cmd, dummy);
      free ( dummy); dummy = NULL;
      return LVM_ELVCREATE_VG_READ;
   }

   if ( pv_allowed != NULL) {
      for ( pa = 0; pv_allowed[pa] != NULL; pa++) {
         if ( opt_v > 0) printf ( "%s -- checking physical volume \"%s\" "
                                  "against volume group\n",
                                  cmd, pv_allowed[pa]);
         if ( pv_check_in_vg ( vg, pv_allowed[pa]) == FALSE) {
            fprintf ( stderr,
                      "%s -- \"%s\" doesn't belong to volume group \"%s\"\n",
                      cmd, pv_allowed[pa], vg_name);
            usage ( LVM_ELVCREATE_PV_CHECK_IN_VG);
         }
         if ( pv_read ( pv_allowed[pa], &pv, NULL) < 0) {
            fprintf ( stderr, "%s -- couldn't read physical volume \"%s\"\n\n",
                              cmd, pv_allowed[pa]);
            return LVM_ELVCREATE_PV_READ;
         }
         if ( pv->pe_total - pv->pe_allocated == 0)
            printf ( "%s -- no space on physical volume \"%s\"\n",
                     cmd, pv_allowed[pa]);
      }
   }

   if ( opt_v > 0)
      printf ( "%s -- checking for maximum logical volume count\n", cmd);
   if ( vg->lv_cur == vg->lv_max) {
      fprintf ( stderr,
                "%s -- maximum number of logical volumes reached in \"%s\"\n",
                cmd, vg_name);
      return LVM_ELVCREATE_LV_COUNT;
   }

   if ( opt_s > 0) lv_name_ptr = lv_snapshot_name;
   else            lv_name_ptr = lv_name;

   /* setup logical volume */
   if ( opt_v > 0) printf ( "%s -- setting up logical volume\n", cmd);
   if ( ( ret = lv_setup_for_create ( vg_name, &vg, lv_name_ptr, &l,
                                      ( unsigned int) size,
                                      stripes, stripesize, mirrors,
                                      allocation, permission,
                                      pv_allowed)) < 0) {
      if ( ret == -LVM_ESIZE) {
         if ( pv_allowed != NULL)
            fprintf ( stderr, "%s -- not enough allocatable/free space "
                              "on the given physical volume(s)\n", cmd);
         else
            fprintf ( stderr, "%s -- not enough allocatable/free physical "
                              "volume space in \"%s\"\n", cmd, vg_name);
         fprintf ( stderr, "%s -- please check, if physical volumes are "
                           "allocatable\n\n", cmd);
         return LVM_ELVCREATE_VG_SIZE;
      } else if ( ret == -LVM_ELV_SETUP_FOR_CREATE_LVM_TAB_GET_FREE_BLK_DEV) {
         fprintf ( stderr,
                   "%s -- no free LVM block device specials available\n", cmd);
         return LVM_ELVCREATE_NO_DEV;
      } else {
         fprintf ( stderr, "%s -- ERROR \"%s\" creating \"%s\" in \"%s\"\n",
                   cmd, lvm_error ( ret), lv_name, vg_name);
         return LVM_ELVCREATE_LV_SETUP;
      }
   }

   /* read ahead sector change; must be after lv_setup_for_create! */
   if ( opt_v > 0) printf ( "%s -- setting read ahead sectors\n", cmd);
   if ( opt_r > 0) {
      if ( read_ahead < LVM_MIN_READ_AHEAD ||
           read_ahead > LVM_MAX_READ_AHEAD) {
         fprintf ( stderr, "%s -- read ahead sector argument is invalid\n\n",
                           cmd);
         return LVM_ELVCREATE_READ_AHEAD;
      }
      vg->lv[l]->lv_read_ahead = read_ahead;
   }

   /* setup snapshot logical volume copy on write exception structs */
   if ( opt_s > 0) {
      if ( chunk_size == LVM_SNAPSHOT_DEF_CHUNK) {
          printf ( "%s -- INFO: using default snapshot chunk size of %s "
                   "for \"%s\"\n",
                   cmd,
                   ( dummy = lvm_show_size ( chunk_size, SHORT)),
                   lv_name_ptr);
          free ( dummy); dummy = NULL;
      }
      if ( ( ret = lv_setup_COW_table_for_create ( vg, lv_name,
                                                   l, chunk_size)) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" setting up snapshot "
                           "copy on write exception table for \"%s\"\n\n",
                           cmd, lvm_error ( ret), lv_name_ptr);
         return LVM_ELVCREATE_LV_SETUP_COW_TABLE_FOR_CREATE;
      }

      if ( ( ret = lv_init_COW_table ( vg, vg->lv[l])) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" initializing snapshot "
                           "copy on write exception table for \"%s\" "
                           "on disk\n\n",
                           cmd, lvm_error ( ret), lv_name_ptr);
         return LVM_ELVCREATE_LV_INIT_COW_TABLE;
      }
      if ( active > 0) vg->lv[l]->lv_status |= LV_ACTIVE;
      else             vg->lv[l]->lv_status &= ~LV_ACTIVE;
   }

   lvm_dont_interrupt ( 0);

   /* create it in kernel */
   if ( opt_v > 0)
      printf ( "%s -- creating logical volume VGDA in kernel\n", cmd);
   if ( ( ret = lv_create ( vg, vg->lv[l], vg->lv[l]->lv_name)) < 0) {
      fprintf ( stderr,
                "%s -- ERROR \"%s\" creating VGDA for \"%s\" in kernel\n",
                        cmd, lvm_error ( ret), lv_name_ptr);
      return LVM_ELVCREATE_LV_CREATE;
   }

   /* store it on disks */
   if ( opt_v > 0)
      printf ( "%s -- storing logical volume VGDA on disk(s)\n", cmd);
   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);
      if ( ( ret = lv_remove ( vg, vg->lv[l], lv_name_ptr)) < 0)
         fprintf ( stderr,
                   "%s -- ERROR \"%s\" removing VGDA for \"%s\" from kernel\n",
                   cmd, lvm_error ( ret), lv_name_ptr);
      return LVM_ELVCREATE_VG_WRITE;
   }

   if ( opt_v > 0) printf ( "%s -- creating device special file\n", cmd);
   if ( ( ret = lv_create_node ( vg->lv[l])) < 0) {
      if ( ret == -LVM_ELV_CREATE_NODE_MKNOD) {
         fprintf ( stderr,
                   "%s -- logical volume node \"%s\" already exists\n",
                   cmd, lv_name_ptr);
      } else {
         fprintf ( stderr,
                   "%s -- ERROR \"%s\" creating logical volume node \"%s\"\n",
                   cmd, lvm_error ( ret), lv_name_ptr);
      }
      return LVM_ELVCREATE_LV_CREATE_NODE;
   }

   if ( opt_Z > 0) {
      char buf[2*BLOCK_SIZE];

      if ( opt_v > 0) printf ( "%s -- opening logical volume \"%s\" for init\n",
                               cmd, lv_name_ptr);
      if ( ( lv_handle = open ( lv_name_ptr, O_WRONLY)) == -1) {
         fprintf ( stderr, "%s -- ERROR \"%s\" opening logical volume \"%s\"\n",
                   cmd, lvm_error ( errno), lv_name_ptr);
         return LVM_ELVCREATE_LV_OPEN;
      }

      memset ( buf, 0, sizeof ( buf));
      if ( opt_v > 0) printf ( "%s -- initializing logical volume \"%s\"\n",
                               cmd, lv_name_ptr);
      if ( write ( lv_handle, buf, sizeof ( buf)) != sizeof ( buf)) {
         close ( lv_handle);
         fprintf ( stderr,
                   "%s -- ERROR \"%s\" initializing logical volume \"%s\"\n",
                   cmd, lvm_error ( errno), lv_name_ptr);
         return LVM_ELVCREATE_LV_WRITE;
      }
      fsync ( lv_handle);
      close ( lv_handle);
   } else {
      if ( opt_s == 0)
         printf ( "%s -- WARNING: you are not initializing logical "
                  "volume \"%s\"\n", cmd, lv_name_ptr);
   }

   if ( opt_v > 0) printf ( "%s -- changing lvmtab\n", cmd);
   if ( vg_cfgbackup ( vg_name, LVMTAB_DIR, opt_v, vg) == 0 &&
        opt_A > 0) {
      printf ( "%s -- doing automatic backup of \"%s\"\n", cmd, vg_name);
      vg_cfgbackup ( vg_name, VG_BACKUP_DIR, opt_v, vg);
   } else {
      printf ( "%s -- WARNING: you don't have an automatic backup of \"%s\"\n",
               cmd, vg_name);
   }

   lvm_interrupt ();
   LVM_UNLOCK ( 0);

   printf ( "%s -- logical volume \"%s\" successfully created\n\n",
             cmd, lv_name_ptr);

   return 0;
}
