blob: a9b96c606a3942a43280305236ac930ca0ffa74d [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/storage/extractor/bin/parse.h"
#include <fcntl.h>
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <zircon/errors.h>
#include <iostream>
#include "lib/zx/status.h"
namespace extractor {
namespace {
// Prints usage message for the utility.
void PrintUsage() {
std::cerr
<< "usage:\n"
<< "disk-extract extract [ <option>* ] --type [disk-type] --disk [disk-path]"
<< " --image [image-path]\n"
<< " Extracts disk image from disk-path and writes the image to [image-path]\n"
<< " where disk-path contains disk-type image.\n"
<< " --type : \"minfs\" is the only allowed type.\n"
<< " --disk: Path of the device file that needs to be extracted.\n"
<< " --image: Path of the image file where extracted image will be written to.\n"
<< " --dump-pii : dumps pii in addition to disk metadata.\n"
<< "disk-extract deflate [--verbose] --input-file <input-image> "
<< "--output-file <output-file>\n"
<< " Deflates an extracted disk image into its original form\n"
<< " --input-file: The path of the extracted image file.\n"
<< " --output-file: The path where deflated file will be created.\n"
<< " --verbose: Prints additional info about extracted imagewhile deflating the file.\n"
<< "--help : Show this help message\n";
}
zx::status<> ParseInputFile(const char* path, ExtractOptions& options) {
options.input_path.assign(path);
if (options.input_path.length() == 0) {
std::cerr << "Missing disk path argument" << std::endl;
PrintUsage();
return zx::error(ZX_ERR_INVALID_ARGS);
}
options.input_fd.reset(open(options.input_path.c_str(), O_RDONLY));
if (!options.input_fd) {
std::cerr << "Failed to open input path " << options.input_path << std::endl;
return zx::error(ZX_ERR_IO);
}
return zx::ok();
}
zx::status<> ParseOutputFile(const char* path, ExtractOptions& options) {
options.output_path.assign(path);
if (options.output_path.length() == 0) {
std::cerr << "Missing image path argument" << options.output_path << std::endl;
PrintUsage();
return zx::error(ZX_ERR_INVALID_ARGS);
}
struct stat stats;
if (stat(options.output_path.c_str(), &stats) == 0) {
std::cerr << "Image file already exists" << options.output_path << std::endl;
return zx::error(ZX_ERR_ALREADY_EXISTS);
}
options.output_fd.reset(open(options.output_path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
if (!options.output_fd) {
std::cerr << "Failed to open/create image file" << options.output_path << std::endl;
return zx::error(ZX_ERR_IO);
}
return zx::ok();
}
} // namespace
zx::status<ExtractOptions> ParseExtractArguments(int argc, char* const argv[]) {
ExtractOptions options;
options.sub_command = SubCommand::kExtract;
static const struct option opts[] = {
{"disk", required_argument, nullptr, 'd'}, {"image", required_argument, nullptr, 'i'},
{"type", required_argument, nullptr, 't'}, {"dump-pii", no_argument, nullptr, 'p'},
{"help", no_argument, nullptr, 'h'}, {nullptr, 0, nullptr, 0},
};
for (int opt; (opt = getopt_long(argc, argv, "d:i:t:p:h", opts, nullptr)) != -1;) {
switch (opt) {
case 'd':
if (auto status = ParseInputFile(optarg, options); status.is_error()) {
return zx::error(status.error_value());
}
break;
case 'i':
if (auto status = ParseOutputFile(optarg, options); status.is_error()) {
return zx::error(status.error_value());
}
break;
case 't':
if (strncmp(optarg, "minfs", strlen(optarg)) == 0) {
options.type = DiskType::kMinfs;
} else if (strncmp(optarg, "blobfs", strlen(optarg)) == 0) {
options.type = DiskType::kBlobfs;
} else if (strncmp(optarg, "fvm", strlen(optarg)) == 0) {
options.type = DiskType::kFvm;
} else {
std::cerr << "Type supplied " << optarg
<< " and needs to be either minfs, blobfs, or fvm\n";
return zx::error(ZX_ERR_INVALID_ARGS);
}
break;
case 'p':
std::cerr << "Dumping Pii\n";
options.dump_pii = true;
break;
case 'h':
__FALLTHROUGH;
default:
PrintUsage();
return zx::error(ZX_ERR_INVALID_ARGS);
}
}
if (options.type == std::nullopt || !options.output_fd || !options.input_fd) {
PrintUsage();
return zx::error(ZX_ERR_INVALID_ARGS);
}
return zx::ok(std::move(options));
}
zx::status<ExtractOptions> ParseDeflateArguments(int argc, char* const argv[]) {
ExtractOptions options;
options.sub_command = SubCommand::kDeflate;
static const struct option opts[] = {
{"input_file", required_argument, nullptr, 'i'},
{"output_file", required_argument, nullptr, 'o'},
{"verbose", no_argument, nullptr, 'v'},
{"help", no_argument, nullptr, 'h'},
{nullptr, 0, nullptr, 0},
};
for (int opt; (opt = getopt_long(argc, argv, "i:o:v:h", opts, nullptr)) != -1;) {
switch (opt) {
case 'i':
if (auto status = ParseInputFile(optarg, options); status.is_error()) {
return zx::error(status.error_value());
}
break;
case 'o':
if (auto status = ParseOutputFile(optarg, options); status.is_error()) {
return zx::error(status.error_value());
}
break;
case 'v':
options.verbose = true;
break;
case 'h':
__FALLTHROUGH;
default:
PrintUsage();
return zx::error(ZX_ERR_INVALID_ARGS);
}
}
if (!options.output_fd || !options.input_fd) {
PrintUsage();
return zx::error(ZX_ERR_INVALID_ARGS);
}
return zx::ok(std::move(options));
}
zx::status<ExtractOptions> ParseCommandLineArguments(int argc, char* const argv[]) {
if (argc <= 1) {
PrintUsage();
return zx::error(ZX_ERR_INVALID_ARGS);
}
if (strcmp(argv[1], "extract") == 0) {
return ParseExtractArguments(argc - 1, &argv[1]);
}
if (strcmp(argv[1], "deflate") == 0) {
return ParseDeflateArguments(argc - 1, &argv[1]);
}
std::cerr << "subcommand missing." << std::endl;
PrintUsage();
return zx::error(ZX_ERR_INVALID_ARGS);
}
} // namespace extractor