blob: 94cc8ec8fc00c5e49b1704463f4bcf14956c1008 [file] [log] [blame]
// SPDX-License-Identifier: MIT
/*
* The 'fsverity dump_metadata' command
*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
#include "fsverity.h"
#include <fcntl.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <unistd.h>
static const struct option longopts[] = {
{"offset", required_argument, NULL, OPT_OFFSET},
{"length", required_argument, NULL, OPT_LENGTH},
{NULL, 0, NULL, 0}
};
static const struct {
const char *name;
int val;
} metadata_types[] = {
{"merkle_tree", FS_VERITY_METADATA_TYPE_MERKLE_TREE},
{"descriptor", FS_VERITY_METADATA_TYPE_DESCRIPTOR},
{"signature", FS_VERITY_METADATA_TYPE_SIGNATURE},
};
static bool parse_metadata_type(const char *name, __u64 *val_ret)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(metadata_types); i++) {
if (strcmp(name, metadata_types[i].name) == 0) {
*val_ret = metadata_types[i].val;
return true;
}
}
error_msg("unknown metadata type: %s", name);
fputs(" Expected", stderr);
for (i = 0; i < ARRAY_SIZE(metadata_types); i++) {
if (i != 0 && ARRAY_SIZE(metadata_types) > 2)
putc(',', stderr);
putc(' ', stderr);
if (i != 0 && i == ARRAY_SIZE(metadata_types) - 1)
fputs("or ", stderr);
fprintf(stderr, "\"%s\"", metadata_types[i].name);
}
fprintf(stderr, "\n");
return false;
}
/* Dump the fs-verity metadata of the given file. */
int fsverity_cmd_dump_metadata(const struct fsverity_command *cmd,
int argc, char *argv[])
{
bool offset_specified = false;
bool length_specified = false;
struct filedes file = { .fd = -1 };
struct filedes stdout_filedes = { .fd = STDOUT_FILENO,
.name = "stdout" };
struct fsverity_read_metadata_arg arg = { .length = 32768 };
void *buf = NULL;
char *tmp;
int c;
int status;
int bytes_read;
while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
switch (c) {
case OPT_OFFSET:
if (offset_specified) {
error_msg("--offset can only be specified once");
goto out_usage;
}
errno = 0;
arg.offset = strtoull(optarg, &tmp, 10);
if (errno || *tmp) {
error_msg("invalid value for --offset");
goto out_usage;
}
offset_specified = true;
break;
case OPT_LENGTH:
if (length_specified) {
error_msg("--length can only be specified once");
goto out_usage;
}
errno = 0;
arg.length = strtoull(optarg, &tmp, 10);
if (errno || *tmp || arg.length > SIZE_MAX) {
error_msg("invalid value for --length");
goto out_usage;
}
length_specified = true;
break;
default:
goto out_usage;
}
}
argv += optind;
argc -= optind;
if (argc != 2)
goto out_usage;
if (!parse_metadata_type(argv[0], &arg.metadata_type))
goto out_usage;
if (length_specified && !offset_specified) {
error_msg("--length specified without --offset");
goto out_usage;
}
if (offset_specified && !length_specified) {
error_msg("--offset specified without --length");
goto out_usage;
}
buf = xzalloc(arg.length);
arg.buf_ptr = (uintptr_t)buf;
if (!open_file(&file, argv[1], O_RDONLY, 0))
goto out_err;
/*
* If --offset and --length were specified, then do only the single read
* requested. Otherwise read until EOF.
*/
do {
bytes_read = ioctl(file.fd, FS_IOC_READ_VERITY_METADATA, &arg);
if (bytes_read < 0) {
error_msg_errno("FS_IOC_READ_VERITY_METADATA failed on '%s'",
file.name);
goto out_err;
}
if (bytes_read == 0)
break;
if (!full_write(&stdout_filedes, buf, bytes_read))
goto out_err;
arg.offset += bytes_read;
} while (!length_specified);
status = 0;
out:
free(buf);
filedes_close(&file);
return status;
out_err:
status = 1;
goto out;
out_usage:
usage(cmd, stderr);
status = 2;
goto out;
}