// 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 #include #include #include 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; }