Esempio n. 1
0
/**
 * create_partitions
 *
 * Generate a list of PARTITION objects, which store the coordinates of blocks
 * in the matrix. These blocks can then be considered in the future - eg
 * for the purpose of choosing a list of EM starts from the matrix. The
 * array of partitions objects is referenced by the sp_matrix.
 *
 */
static void create_partitions (
  SP_MATRIX *sp_mat  ///< The sp_matrix object
  ///< Currently no parameters. May allow user manipulation later.
) {
  /* For every central s_point (ie s_point with central w and n) in the
   * matrix, calculate the boundaries of the current partition and then
   * generate a partition that fits within those bounds... */
  int *central_widths = get_central_ws(sp_mat);
  int *central_nsites = get_central_ns(sp_mat);

  int w_idx;
  int n_partitions = 0;
  PARTITION **part_array = NULL;
  for (w_idx = 0; w_idx < get_num_central_widths(sp_mat); w_idx++) {
    int n_idx;
    for (n_idx = 0; n_idx < get_num_central_nsites(sp_mat); n_idx++) {
      int curr_w = central_widths[w_idx];
      int curr_n = central_nsites[n_idx];

      /* Calculate the boundaries within which the current partition must
       * be located. The boundaries are half way between the current
       * value and the adjacent value, unless there is no adjacent value
       * (in which case the boundary IS the current value):
       */
      double min_w_bounds, max_w_bounds, min_n_bounds, max_n_bounds;
      if (curr_w == get_min_width(sp_mat)) {
        min_w_bounds = curr_w;
      } else {
        int prev_w = central_widths[w_idx - 1];
        min_w_bounds = (curr_w + prev_w)/(double)2;
      }
      if (curr_w == get_max_width(sp_mat)) {
        max_w_bounds = curr_w;
      } else {
        int next_w = central_widths[w_idx + 1];
        max_w_bounds = (curr_w + next_w)/(double)2;
      }

      if (curr_n == get_min_nsites(sp_mat)) {
        min_n_bounds = curr_n;
      } else {
        int prev_n = central_nsites[n_idx - 1];
        min_n_bounds = (curr_n + prev_n)/(double)2;
      }
      if (curr_n == get_max_nsites(sp_mat)) {
        max_n_bounds = curr_n;
      } else {
        int next_n = central_nsites[n_idx + 1];
        max_n_bounds = (curr_n + next_n)/(double)2;
      }

      /* Calculate the minimum and maximum w and n values which define the
         current partition. The minimum value is the smallest integer that is
         >= the minimum boundary. The maximum value is the largest integer that
         is < the maximum boundary, unless the maximum boundary is the max
         value in the sp_matrix (in which case the maximum value is the
         maximum boundary itself). This ensures that every s_point in the
         matrix will be assigned to a partition.
       */
      int part_min_w, part_max_w, part_min_n, part_max_n;
      part_min_w = (int)ceil(min_w_bounds);
      part_min_n = (int)ceil(min_n_bounds);

      if (curr_w == get_max_width(sp_mat)) {
        part_max_w = curr_w;
      } else {
        // Largest integer LESS than max bounds:
        if (max_w_bounds == ceil(max_w_bounds)) {
          part_max_w = (int)max_w_bounds - 1;
        } else {
          part_max_w = (int)floor(max_w_bounds);
        }
      }

      if (curr_n == get_max_nsites(sp_mat)) {
        part_max_n = curr_n;
      } else {
        // Largest integer LESS than max bounds:
        if (max_n_bounds == ceil(max_n_bounds)) {
          part_max_n = (int)max_n_bounds - 1;
        } else {
          part_max_n = (int)floor(max_n_bounds);
        }
      }

      // Generate the current partition and add it to the growing array:
      PARTITION *curr_part = new_partition(part_min_w, part_max_w, curr_w,
                                           part_min_n, part_max_n, curr_n);
      (n_partitions)++;
      Resize(part_array, n_partitions, PARTITION *);
      part_array[(n_partitions) - 1] = curr_part;
    } // n_idx
  } // w_idx

  assert(sp_mat->partitions == NULL);
  sp_mat->partitions = part_array;
  sp_mat->n_parts = n_partitions;
} // create_partitions
int main(int argc, char *argv[])
  try {
    State state;
    auto parsers = new_subparser({"device"});
    argp argp = {options, init_parsers, nullptr, doc, parsers.get(), nullptr,
      nullptr};
    argp_parse(&argp, argc, argv, 0, nullptr, &state);

    Params params;

    try {
      params.load(state.device);
    } catch(const std::exception& e) {
      std::cerr << "Error: Header corrupt." << std::endl;
      return 1;
    }
    std::uint64_t blocks = state.device.size()/params.block_size;

    if (blocks <= 1) {
      std::cerr << "Error: No room for any partitions." << std::endl;
      return 1;
    }

    std::vector<bool> allocated_blocks(blocks);
    allocated_blocks[0] = true;

    std::string passphrase;
    Pinentry pinentry;
    pinentry.SETDESC("Enter passphrases for all partitions on this volume. "
        "Enter an empty passphrase after last passphrase.");
    pinentry.SETPROMPT("Passphrase:");
    while ((passphrase = pinentry.GETPIN()) != "") {
      Superblock superblock(params, passphrase, blocks);
      try {
        superblock.load(state.device);
        for (auto block : superblock.blocks)
          allocated_blocks[block] = true;
      } catch(...) {
        pinentry.SETERROR("No partition found for that passphrase.");
      }
    }

    std::size_t free_blocks = 0;
    for (bool allocated : allocated_blocks)
      if (!allocated)
        free_blocks++;

    std::cout << free_blocks << " blocks free." << std::endl;

    if (free_blocks == 0) {
      std::cout << "Error: not enough free space." << std::endl;
      return 1;
    }

    if (state.blocks == 0)
      state.blocks = free_blocks;
    if (state.partition_size == 0) {
      decltype(state.partition_size) last;
      state.partition_size = state.blocks;
      do {
        last = state.partition_size;
        state.partition_size = state.blocks-Superblock::size_in_blocks(params,
          state.partition_size);
      } while (last != state.partition_size);
    }

    std::uint64_t blocks_required = state.partition_size+
      Superblock::size_in_blocks(params, state.partition_size);
    state.blocks = std::min(state.blocks, blocks_required);
    
    if (state.blocks > free_blocks) {
      std::cerr << "Error: not enough free space." << std::endl;
      return 1;
    }

    pinentry.SETDESC("Enter passphrase for the new partition.");
    Superblock new_partition(params, pinentry.GETPIN(), blocks);
    if (allocated_blocks[new_partition.blocks.front()]) {
      std::cerr << "Error: superblock location already in use." << std::endl;
      return 1;
    }
    allocated_blocks[new_partition.blocks.front()] = true;
    state.blocks--;

    std::vector<std::uint64_t> pool;
    pool.reserve(free_blocks-1);
    for (std::size_t i = 0; i < blocks; i++)
      if (!allocated_blocks[i])
        pool.push_back(i);
    std::shuffle(pool.begin(), pool.end(), std::random_device());
    for (; state.blocks > 0; state.blocks--, state.partition_size--) {
      new_partition.blocks.push_back(pool.back());
      pool.pop_back();
    }
    for (; state.partition_size > 0; state.partition_size--)
      new_partition.blocks.push_back(0);
    new_partition.store(state.device);
    return 0;
  } catch(const std::exception& e) {
    std::cerr << "Error: " << e.what() << std::endl;
    return 1;
  }