blob: 548b38a1aaba1d640b284ba972d06fac5e225a02 [file] [log] [blame]
// Copyright 2019 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 <fcntl.h>
#include <getopt.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <cerrno>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <vector>
#include <fbl/unique_fd.h>
#include <lib/cksum.h>
#include <lib/mtd/mtd-interface.h>
#include <lib/nand-redundant-storage/nand-redundant-storage.h>
constexpr const char kOptString[] = "i:n:o:h";
constexpr const option kLongOpts[] = {
{"input", required_argument, nullptr, 'i'},
{"output", required_argument, nullptr, 'o'},
{"num-copies", required_argument, nullptr, 'n'},
{"help", no_argument, nullptr, 'h'},
{nullptr, no_argument, nullptr, 0},
};
constexpr const char kUsageFormatString[] = R""(Usage: %s -o <out-path> -i <in-path> -n <N>
MTD Redundant Storage Tool.
Options:
--help, -h print this message, then exit
--input, -i <in-path> the input file
--num-copies, -n <N> the number of copies to be written to MTD
(required if writing).
--output, -o <out-path> the output file. Overrides -n and -i.
Examples:
Write three copies of foo.zip to /dev/mtd0
$ %s -i foo.zip -o /dev/mtd0 -n 3
Read the contents of /dev/mtd0 into /tmp/foo.zip
$ %s -o /tmp/foo.zip -i /dev/mtd0
Notes:
The user should have read/write permissions for any MTD devices used.
If <out-path> specifies an MTD, then the file that <in-path> points to will
be written to <out-path> with at most <N> minus one backup copies.
If <in-path> specifies an MTD, then the file that <out-path> points to will
be either created or truncated, and then read into from the MTD. If no file
can be found, then <out-path> remains unchanged. <N> is ignored in this
setup.
If both <out-path> and <in-path> are an MTD, this is an error.
)"";
void Usage(const char* prog_name) {
fprintf(stdout, kUsageFormatString, prog_name, prog_name, prog_name);
}
bool IsMtd(const char* path) {
auto mtd = mtd::MtdInterface::Create(path);
if (!mtd) {
// Logs any informative errors. An ioctl error where the path doesn't
// lead to a valid MTD is an uninformative error as this is expected
// behavior for either the input or the output device.
if (errno == EACCES) {
fprintf(stderr, "Unable to open %s: %s\n", path, strerror(errno));
}
return false;
}
return true;
}
int ReadMtd(const char* output, const char* mtd) {
auto mtd_iface = nand_rs::NandRedundantStorage::Create(mtd::MtdInterface::Create(mtd));
if (!mtd_iface) {
fprintf(stderr, "Unable to open MTD interface %s: %s\n", mtd, strerror(errno));
return 1;
}
std::vector<uint8_t> buffer;
if (mtd_iface->ReadToBuffer(&buffer) != ZX_OK) {
return 1;
}
fbl::unique_fd output_fd(open(output, O_CREAT | O_TRUNC | O_RDWR, 0666));
if (!output_fd) {
fprintf(stderr, "Unable to open file %s: %s\n", output, strerror(errno));
return 1;
}
ssize_t written = write(output_fd.get(), buffer.data(), buffer.size());
if (written < 0) {
fprintf(stderr, "Unable to write to file %s: %s\n", output, strerror(errno));
return 1;
}
if (static_cast<uint64_t>(written) != buffer.size()) {
fprintf(stderr, "Unable to complete write to file %s: %s: expected %zd actual %zd\n",
output, strerror(errno), static_cast<uint64_t>(written), buffer.size());
return 1;
}
fprintf(stdout, "SUCCESS: File read from %s into %s\n", mtd, output);
return 0;
}
int WriteMtd(const char* input, const char* mtd, uint32_t num_copies) {
fbl::unique_fd input_fd(open(input, O_RDONLY));
if (!input_fd) {
fprintf(stderr, "Unable to open input file %s: %s\n", input, strerror(errno));
return 1;
}
ssize_t input_file_size = lseek(input_fd.get(), 0L, SEEK_END);
std::vector<uint8_t> file_buffer(input_file_size);
ssize_t ret = pread(input_fd.get(), file_buffer.data(), input_file_size, 0L);
if (ret != input_file_size) {
fprintf(stderr, "Unable to read file to buffer %s: %s\n", input, strerror(errno));
return 1;
}
auto mtd_iface = nand_rs::NandRedundantStorage::Create(mtd::MtdInterface::Create(mtd));
if (!mtd_iface) {
fprintf(stderr, "Unable to open MTD interface %s: %s\n", mtd, strerror(errno));
return 1;
}
uint32_t num_copies_written;
zx_status_t status = mtd_iface->WriteBuffer(file_buffer, num_copies, &num_copies_written);
if (status == ZX_OK) {
fprintf(stdout, "SUCCESS: Wrote %d copies of %s to %s\n", num_copies_written, input, mtd);
}
return status;
}
int main(int argc, char** argv) {
const char* input_file = nullptr;
const char* output_file = nullptr;
uint32_t num_copies = 0;
int opt;
while ((opt = getopt_long(argc, argv, kOptString, kLongOpts, nullptr)) != -1) {
switch (opt) {
case 'i':
input_file = optarg;
continue;
case 'n': {
auto optarg_end = optarg + strlen(optarg);
uint64_t num_copies_long = strtoul(optarg, &optarg_end, 10);
if (errno == EINVAL || num_copies_long == 0) {
fprintf(stderr, "-n value is invalid\n");
return 1;
}
num_copies = static_cast<uint32_t>(num_copies_long);
if (static_cast<uint64_t>(num_copies) != num_copies_long) {
fprintf(stderr, "Overflow on -n argument. Supply 32-bit int.\n");
return 1;
}
continue;
}
case 'o':
output_file = optarg;
continue;
case 'h':
default:
Usage(argv[0]);
return opt == 'h' ? 0 : 1;
}
}
if (output_file == nullptr || input_file == nullptr) {
Usage(argv[0]);
return 1;
}
if (strcmp(input_file, output_file) == 0) {
fprintf(stderr, "ERROR: -i and -o file are the same.\n");
return 1;
}
bool input_is_mtd = IsMtd(input_file);
bool output_is_mtd = IsMtd(output_file);
if (input_is_mtd && output_is_mtd) {
fprintf(stderr, "ERROR: -i and -o are both MTD's.\n");
return 1;
}
if (!input_is_mtd && !output_is_mtd) {
fprintf(stderr, "ERROR: neither -i nor -o can be used as an MTD.\n");
return 1;
}
if (input_is_mtd) {
return ReadMtd(output_file, input_file);
}
if (output_is_mtd && num_copies > 0) {
return WriteMtd(input_file, output_file, num_copies);
}
if (num_copies == 0) {
fprintf(stderr, "ERROR: -n missing.\n");
return 1;
}
Usage(argv[0]);
return 1;
}