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

/*
 * 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

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;
   uint size = 0;
   int size_rest = 0;
   long read_ahead = 0;
   uint allocation = 0;
   uint mirrors = 0;
   uint permission = LV_READ | LV_WRITE;
   uint stripes = 1;
   uint stripesize = 0;
   ulong pe_free = 0;
   char buf[2*BLOCK_SIZE];
   char buffer[NAME_LEN];
#ifdef DEBUG
   char *options = "A:c:C:dh?i:I:l:L:n:p:r:svZ:";
#else
   char *options = "A:c:C:h?i:I:l:L:n:p:r:svZ:";
#endif
   struct option long_options[] = {
      { "active",      required_argument, NULL, 'a'},
      { "autobackup",  required_argument, NULL, 'A'},
      { "chunksize",   required_argument, NULL, 'c'},
      { "contiguous",  required_argument, NULL, 'C'},
#ifdef DEBUG
      { "debug",       no_argument,       NULL, 'd'},
#endif
      { "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'},
      { "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;

   cmd = basename ( argv[0]);

   SUSER_CHECK;
   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;
            }
            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 'c':
            if ( opt_c > 0) {
               fprintf ( stderr, "%s -- c option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }

            if ( ( chunk_size = lvm_check_number ( optarg, TRUE)) > 0) {
               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\n",
                                    cmd, dummy, dummy1);
                  free ( dummy);
                  free ( dummy1);
                  return LVM_EINVALID_CMD_LINE;
               }
               opt_c++;
            } else {
               fprintf ( stderr, "%s -- ERROR option c argument \"%s\"\n\n",
                                 cmd, optarg);
               return LVM_EINVALID_CMD_LINE;
            }
            break;

         case 'C':
            if ( opt_C > 0) {
               fprintf ( stderr, "%s -- C option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_C++;
            if ( strcmp ( optarg, "y") == 0) { allocation |= LV_CONTIGUOUS;
                                               break;}
            if ( strcmp ( optarg, "n") == 0) break;
            fprintf ( stderr, "%s -- C option argument \"%s\" invalid\n\n",
                      cmd, optarg);
            return LVM_EINVALID_CMD_LINE;
            break;

#ifdef DEBUG
         case 'd':
            if ( opt_d > 0) {
               fprintf ( stderr, "%s -- d option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_d++;
            break;
#endif

         case 'h':
         case '?':
            printf ( "\n%s (IOP %d)\n\n%s -- Logical Volume Create\n\n"
                     "Synopsis:\n"
                     "---------\n\n"
                     "%s\n"
                     "\t[-A/--autobackup y/n]\n"
                     "\t[-C/--contiguous y/n]\n"
#ifdef DEBUG
                     "\t[-d/--debug]\n"
#endif
                     "\t[-h/-?/--help]\n"
                     "\t[-i/--stripes Stripes [-I/--stripesize StripeSize]]\n"
                     "\t{-l/--extents LogicalExtentsNumber |\n"
                     "\t -L/--size LogicalVolumeSize[kKmMgGtT]}\n"
                     "\t[-n/--name LogicalVolumeName]\n"
                     "\t[-p/--permission r/rw]\n"
                     "\t[-r/--readahead ReadAheadSectors]\n"
                     "\t[-v/--verbose]\n"
                     "\t[-Z/--zero y/n]\n"
                     "\tVolumeGroupName [PhysicalVolumePath...]\n\n"
                     "%s\n"
                     "\t{-l/--extents LogicalExtentsNumber |\n"
                     "\t -L/--size LogicalVolumeSize[kKmMgGtT]}\n"
                     "\t[-c/--chunksize ChunkSize]\n"
                     "\t-s/--snapshot -n/--name SnapshotLogicalVolumeName\n"
                     "\tLogicalVolume[Path] [PhysicalVolumePath...]\n\n",
                     lvm_version, LVM_LIB_IOP_VERSION,  cmd, cmd, cmd);
            return 0;
            break;

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

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

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

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

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

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

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

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

         case 'p':
            if ( opt_p > 0) {
               fprintf ( stderr, "%s -- p option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_p++;
            if ( strcmp ( optarg, "rw") == 0) {
               permission = ( LV_READ | LV_WRITE);
               break;
            }
            if ( strcmp ( optarg, "r") == 0) { permission = LV_READ; break;}
            fprintf ( stderr, "%s -- p option argument \"%s\" invalid\n\n",
                      cmd, optarg);
            return LVM_EINVALID_CMD_LINE;
            break;

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

         case 's':
            if ( opt_s > 0) {
               fprintf ( stderr, "%s -- s option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            permission = LV_READ;
            opt_s++;
            opt_Z = 0;
            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 'Z':
            if ( opt_Z > 1) {
               fprintf ( stderr, "%s -- Z option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            if ( strcmp ( optarg, "y") == 0) opt_Z = 1;
            else if ( strcmp ( optarg, "n") == 0) opt_Z = 0;
            else {
               fprintf ( stderr, "%s -- invalid option argument \"%s\"\n\n",
                                 cmd, optarg);
               return LVM_EINVALID_CMD_LINE;
            }
            break;

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

   CMD_MINUS_CHK;
   CMD_CHECK_OPT_A_SET;

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

   if ( optind == argc) {
      /* snapshot logical volume */
      if ( opt_s > 0) {
         fprintf ( stderr, "%s -- please enter a logical volume name for "
                           "snapshooting\n\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\n", cmd);
      }
      if ( vg_name == NULL) return 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\n", cmd);
         return LVM_EINVALID_CMD_LINE;
      }
      strncpy ( lv_snapshot_name, lv_name, sizeof ( lv_name) - 1);
      lv_path[sizeof ( lv_name) - 1] = 0;
      dummy = argv[optind];
      LVM_CHECK_DEFAULT_VG_NAME ( dummy, buffer, NAME_LEN);
      strncpy ( lv_name, dummy, sizeof ( lv_name) - 1);
      dummy = NULL;
      lv_name[sizeof ( lv_name) - 1] = 0;
      vg_name = vg_name_of_lv ( lv_name);
      sprintf ( lv_path, LVM_DIR_PREFIX "%s/%s%c",
                vg_name, lv_snapshot_name, 0);

      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\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\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\n",
                cmd, vg_name);
      return 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);
      return 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;
      }
      sprintf ( lv_path, LVM_DIR_PREFIX "%s/%s%c", vg_name, lv_name, 0);

      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\n",
                           cmd, lv_name);
         return 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\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\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\n",
                           cmd, stripes, cmd, 1, LVM_MAX_STRIPES);
         return 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);
         return 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 || opt_l > 0) {
      if ( opt_L > 0) size *= 2;
      else            size *= vg_core->pe_size;
      if ( size % vg_core->pe_size > 0) {
         size += vg_core->pe_size - size % vg_core->pe_size;
         printf ( "%s -- rounding up size to physical extent boundary \"%s\"\n",
                  cmd, ( dummy = lvm_show_size ( size / 2, SHORT)));
         free ( dummy); dummy = NULL;
      }

      size_rest = size % ( stripes * vg_core->pe_size);
      if ( size_rest != 0) {
         printf ( "%s -- rounding %d KB to stripe boundary size ",
                  cmd, size / 2);
         size = size - size_rest + stripes * vg_core->pe_size;
         printf ( "%d KB / %u PE\n", size / 2, size / vg_core->pe_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\n",
                              cmd, lv_name);
            return 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\n",
                              cmd, lv_name, cmd);
            return 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 must "
                     "be disabled if it gets full\n", cmd);
         
         }
      }
   } else if ( opt_s == 0) {
      fprintf ( stderr, "%s -- please give an l or L option\n\n", cmd);
      return LVM_EINVALID_CMD_LINE;
   }

   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\n",
                        cmd, lvm_error ( ret), vg_name);
      return LVM_ELVCREATE_VG_READ;
   }

   if ( opt_v > 0) printf ( "%s -- checking logical volume maximum size\n",
                            cmd);
   if ( size > LVM_LV_SIZE_MAX( vg)) {
      fprintf ( stderr, "%s -- size %d is larger than maximum VGDA "
                        "kernel size "
                        "of %llu\n\n",
                        cmd, size / 2, LVM_LV_SIZE_MAX( vg) / 2);
      return LVM_ELVCREATE_LV_SIZE;
   }

   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\n",
                        cmd, vg_name);
      return LVM_ELVCREATE_PE_FREE;
   } else if ( size / vg->pe_size > pe_free) {
      fprintf ( stderr, "%s -- only %lu free physical extent%s in "
                        "volume group \"%s\"\n\n",
                        cmd, pe_free, pe_free > 1 ? "s" : "", 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 -- too many stripes for \"%s\" with %u "
                           "physical volume%s\n\n",
                           cmd, vg_name, vg->pv_cur, vg->pv_cur > 1 ? "s" : "");
         return LVM_ELVCREATE_STRIPE_COUNT;
      }
   }

   if ( opt_s > 0 && ( ( chunk_size * 1024) / SECTOR_SIZE) > vg->pe_size) {
      dummy = lvm_show_size ( ( vg->pe_size * 1024) / SECTOR_SIZE, SHORT);
      fprintf ( stderr, "%s -- ERROR: snapshot chunk size must be less "
                        "than or equal to PE size %s!\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\n",
                              cmd, pv_allowed[pa], vg_name);
            return 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\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, 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\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\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\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 "
                           "existent\n\n",
                           cmd, lv_name_ptr);
      } else {
         fprintf ( stderr, "%s -- ERROR \"%s\" creating logical volume "
                           "node \"%s\"\n\n",
                           cmd, lvm_error ( ret), lv_name_ptr);
      }
      return LVM_ELVCREATE_LV_CREATE_NODE;
   }

   if ( opt_Z > 0) {
      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\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\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;
}
