| // Copyright 2017 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 <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "garnet/lib/far/archive_reader.h" |
| #include "garnet/lib/far/archive_writer.h" |
| #include "garnet/lib/far/manifest.h" |
| #include "src/lib/fxl/command_line.h" |
| #include "src/lib/files/unique_fd.h" |
| |
| namespace archive { |
| |
| // Commands |
| constexpr fxl::StringView kCat = "cat"; |
| constexpr fxl::StringView kCreate = "create"; |
| constexpr fxl::StringView kList = "list"; |
| constexpr fxl::StringView kExtract = "extract"; |
| constexpr fxl::StringView kExtractFile = "extract-file"; |
| |
| constexpr fxl::StringView kKnownCommands = |
| "create, list, cat, extract, or extract-file"; |
| |
| // Options |
| constexpr fxl::StringView kArchive = "archive"; |
| constexpr fxl::StringView kManifest = "manifest"; |
| constexpr fxl::StringView kFile = "file"; |
| constexpr fxl::StringView kOutput = "output"; |
| |
| constexpr fxl::StringView kCatUsage = "cat --archive=<archive> --file=<path> "; |
| constexpr fxl::StringView kCreateUsage = |
| "create --archive=<archive> --manifest=<manifest>"; |
| constexpr fxl::StringView kListUsage = "list --archive=<archive>"; |
| constexpr fxl::StringView kExtractUsage = |
| "extract --archive=<archive> --output=<path>"; |
| constexpr fxl::StringView kExtractFileUsage = |
| "extract-file --archive=<archive> --file=<path> --output=<path>"; |
| |
| bool GetOptionValue(const fxl::CommandLine& command_line, |
| fxl::StringView option, fxl::StringView usage, |
| std::string* value) { |
| if (!command_line.GetOptionValue(option, value)) { |
| fprintf(stderr, |
| "error: Missing --%s argument.\n" |
| "Usage: far %s\n", |
| option.data(), usage.data()); |
| return false; |
| } |
| return true; |
| } |
| |
| int Create(const fxl::CommandLine& command_line) { |
| std::string archive_path; |
| if (!GetOptionValue(command_line, kArchive, kCreateUsage, &archive_path)) |
| return -1; |
| |
| std::vector<fxl::StringView> manifest_paths = |
| command_line.GetOptionValues(kManifest); |
| if (manifest_paths.empty()) |
| return -1; |
| |
| archive::ArchiveWriter writer; |
| for (const auto& manifest_path : manifest_paths) { |
| if (!archive::ReadManifest(manifest_path, &writer)) |
| return -1; |
| } |
| fxl::UniqueFD fd(open(archive_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, |
| S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); |
| if (!fd.is_valid()) |
| return -1; |
| return writer.Write(fd.get()) ? 0 : -1; |
| } |
| |
| int List(const fxl::CommandLine& command_line) { |
| std::string archive_path; |
| if (!GetOptionValue(command_line, kArchive, kListUsage, &archive_path)) |
| return -1; |
| |
| fxl::UniqueFD fd(open(archive_path.c_str(), O_RDONLY)); |
| if (!fd.is_valid()) |
| return -1; |
| archive::ArchiveReader reader(std::move(fd)); |
| if (!reader.Read()) |
| return -1; |
| reader.ListPaths([](fxl::StringView string) { |
| printf("%.*s\n", static_cast<int>(string.size()), string.data()); |
| }); |
| return 0; |
| } |
| |
| int Extract(const fxl::CommandLine& command_line) { |
| std::string archive_path; |
| if (!GetOptionValue(command_line, kArchive, kExtractUsage, &archive_path)) |
| return -1; |
| |
| std::string output_dir; |
| if (!GetOptionValue(command_line, kOutput, kExtractUsage, &output_dir)) |
| return -1; |
| |
| fxl::UniqueFD fd(open(archive_path.c_str(), O_RDONLY)); |
| if (!fd.is_valid()) |
| return -1; |
| archive::ArchiveReader reader(std::move(fd)); |
| if (!reader.Read()) |
| return -1; |
| if (!reader.Extract(output_dir)) |
| return -1; |
| return 0; |
| } |
| |
| int ExtractFile(const fxl::CommandLine& command_line) { |
| std::string archive_path; |
| if (!GetOptionValue(command_line, kArchive, kExtractFileUsage, &archive_path)) |
| return -1; |
| |
| std::string file_path; |
| if (!GetOptionValue(command_line, kFile, kExtractFileUsage, &file_path)) |
| return -1; |
| |
| std::string output_path; |
| if (!GetOptionValue(command_line, kOutput, kExtractFileUsage, &output_path)) |
| return -1; |
| |
| fxl::UniqueFD fd(open(archive_path.c_str(), O_RDONLY)); |
| if (!fd.is_valid()) |
| return -1; |
| archive::ArchiveReader reader(std::move(fd)); |
| if (!reader.Read()) |
| return -1; |
| if (!reader.ExtractFile(file_path, output_path.c_str())) |
| return -1; |
| return 0; |
| } |
| |
| int Cat(const fxl::CommandLine& command_line) { |
| std::string archive_path; |
| if (!GetOptionValue(command_line, kArchive, kCatUsage, &archive_path)) |
| return -1; |
| |
| std::string file_path; |
| if (!GetOptionValue(command_line, kFile, kCatUsage, &file_path)) |
| return -1; |
| |
| fxl::UniqueFD fd(open(archive_path.c_str(), O_RDONLY)); |
| if (!fd.is_valid()) |
| return -1; |
| archive::ArchiveReader reader(std::move(fd)); |
| if (!reader.Read()) |
| return -1; |
| if (!reader.CopyFile(file_path, STDOUT_FILENO)) |
| return -1; |
| return 0; |
| } |
| |
| int RunCommand(std::string command, const fxl::CommandLine& command_line) { |
| if (command == kCreate) { |
| return archive::Create(command_line); |
| } else if (command == kList) { |
| return archive::List(command_line); |
| } else if (command == kExtract) { |
| return archive::Extract(command_line); |
| } else if (command == kExtractFile) { |
| return archive::ExtractFile(command_line); |
| } else if (command == kCat) { |
| return archive::Cat(command_line); |
| } else { |
| fprintf(stderr, |
| "error: Unknown command: %s\n" |
| "Known commands: %s.\n", |
| command.c_str(), kKnownCommands.data()); |
| return -1; |
| } |
| } |
| |
| } // namespace archive |
| |
| int main(int argc, char** argv) { |
| if (argc < 2) { |
| fprintf(stderr, |
| "error: Missing command.\n" |
| "Usage: far <command> ...\n" |
| " where <command> is %s.\n", |
| archive::kKnownCommands.data()); |
| return -1; |
| } |
| |
| return archive::RunCommand(argv[1], |
| fxl::CommandLineFromArgcArgv(argc - 1, argv + 1)); |
| } |