1ced56a00Sopenharmony_ci// SPDX-License-Identifier: MIT 2ced56a00Sopenharmony_ci/* 3ced56a00Sopenharmony_ci * The 'fsverity dump_metadata' command 4ced56a00Sopenharmony_ci * 5ced56a00Sopenharmony_ci * Copyright 2021 Google LLC 6ced56a00Sopenharmony_ci * 7ced56a00Sopenharmony_ci * Use of this source code is governed by an MIT-style 8ced56a00Sopenharmony_ci * license that can be found in the LICENSE file or at 9ced56a00Sopenharmony_ci * https://opensource.org/licenses/MIT. 10ced56a00Sopenharmony_ci */ 11ced56a00Sopenharmony_ci 12ced56a00Sopenharmony_ci#include "fsverity.h" 13ced56a00Sopenharmony_ci 14ced56a00Sopenharmony_ci#include <fcntl.h> 15ced56a00Sopenharmony_ci#include <getopt.h> 16ced56a00Sopenharmony_ci#include <sys/ioctl.h> 17ced56a00Sopenharmony_ci#include <unistd.h> 18ced56a00Sopenharmony_ci 19ced56a00Sopenharmony_cistatic const struct option longopts[] = { 20ced56a00Sopenharmony_ci {"offset", required_argument, NULL, OPT_OFFSET}, 21ced56a00Sopenharmony_ci {"length", required_argument, NULL, OPT_LENGTH}, 22ced56a00Sopenharmony_ci {NULL, 0, NULL, 0} 23ced56a00Sopenharmony_ci}; 24ced56a00Sopenharmony_ci 25ced56a00Sopenharmony_cistatic const struct { 26ced56a00Sopenharmony_ci const char *name; 27ced56a00Sopenharmony_ci int val; 28ced56a00Sopenharmony_ci} metadata_types[] = { 29ced56a00Sopenharmony_ci {"merkle_tree", FS_VERITY_METADATA_TYPE_MERKLE_TREE}, 30ced56a00Sopenharmony_ci {"descriptor", FS_VERITY_METADATA_TYPE_DESCRIPTOR}, 31ced56a00Sopenharmony_ci {"signature", FS_VERITY_METADATA_TYPE_SIGNATURE}, 32ced56a00Sopenharmony_ci}; 33ced56a00Sopenharmony_ci 34ced56a00Sopenharmony_cistatic bool parse_metadata_type(const char *name, __u64 *val_ret) 35ced56a00Sopenharmony_ci{ 36ced56a00Sopenharmony_ci size_t i; 37ced56a00Sopenharmony_ci 38ced56a00Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(metadata_types); i++) { 39ced56a00Sopenharmony_ci if (strcmp(name, metadata_types[i].name) == 0) { 40ced56a00Sopenharmony_ci *val_ret = metadata_types[i].val; 41ced56a00Sopenharmony_ci return true; 42ced56a00Sopenharmony_ci } 43ced56a00Sopenharmony_ci } 44ced56a00Sopenharmony_ci error_msg("unknown metadata type: %s", name); 45ced56a00Sopenharmony_ci fputs(" Expected", stderr); 46ced56a00Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(metadata_types); i++) { 47ced56a00Sopenharmony_ci if (i != 0 && ARRAY_SIZE(metadata_types) > 2) 48ced56a00Sopenharmony_ci putc(',', stderr); 49ced56a00Sopenharmony_ci putc(' ', stderr); 50ced56a00Sopenharmony_ci if (i != 0 && i == ARRAY_SIZE(metadata_types) - 1) 51ced56a00Sopenharmony_ci fputs("or ", stderr); 52ced56a00Sopenharmony_ci fprintf(stderr, "\"%s\"", metadata_types[i].name); 53ced56a00Sopenharmony_ci } 54ced56a00Sopenharmony_ci fprintf(stderr, "\n"); 55ced56a00Sopenharmony_ci return false; 56ced56a00Sopenharmony_ci} 57ced56a00Sopenharmony_ci 58ced56a00Sopenharmony_ci/* Dump the fs-verity metadata of the given file. */ 59ced56a00Sopenharmony_ciint fsverity_cmd_dump_metadata(const struct fsverity_command *cmd, 60ced56a00Sopenharmony_ci int argc, char *argv[]) 61ced56a00Sopenharmony_ci{ 62ced56a00Sopenharmony_ci bool offset_specified = false; 63ced56a00Sopenharmony_ci bool length_specified = false; 64ced56a00Sopenharmony_ci struct filedes file = { .fd = -1 }; 65ced56a00Sopenharmony_ci struct filedes stdout_filedes = { .fd = STDOUT_FILENO, 66ced56a00Sopenharmony_ci .name = "stdout" }; 67ced56a00Sopenharmony_ci struct fsverity_read_metadata_arg arg = { .length = 32768 }; 68ced56a00Sopenharmony_ci void *buf = NULL; 69ced56a00Sopenharmony_ci char *tmp; 70ced56a00Sopenharmony_ci int c; 71ced56a00Sopenharmony_ci int status; 72ced56a00Sopenharmony_ci int bytes_read; 73ced56a00Sopenharmony_ci 74ced56a00Sopenharmony_ci while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) { 75ced56a00Sopenharmony_ci switch (c) { 76ced56a00Sopenharmony_ci case OPT_OFFSET: 77ced56a00Sopenharmony_ci if (offset_specified) { 78ced56a00Sopenharmony_ci error_msg("--offset can only be specified once"); 79ced56a00Sopenharmony_ci goto out_usage; 80ced56a00Sopenharmony_ci } 81ced56a00Sopenharmony_ci errno = 0; 82ced56a00Sopenharmony_ci arg.offset = strtoull(optarg, &tmp, 10); 83ced56a00Sopenharmony_ci if (errno || *tmp) { 84ced56a00Sopenharmony_ci error_msg("invalid value for --offset"); 85ced56a00Sopenharmony_ci goto out_usage; 86ced56a00Sopenharmony_ci } 87ced56a00Sopenharmony_ci offset_specified = true; 88ced56a00Sopenharmony_ci break; 89ced56a00Sopenharmony_ci case OPT_LENGTH: 90ced56a00Sopenharmony_ci if (length_specified) { 91ced56a00Sopenharmony_ci error_msg("--length can only be specified once"); 92ced56a00Sopenharmony_ci goto out_usage; 93ced56a00Sopenharmony_ci } 94ced56a00Sopenharmony_ci errno = 0; 95ced56a00Sopenharmony_ci arg.length = strtoull(optarg, &tmp, 10); 96ced56a00Sopenharmony_ci if (errno || *tmp || arg.length > SIZE_MAX) { 97ced56a00Sopenharmony_ci error_msg("invalid value for --length"); 98ced56a00Sopenharmony_ci goto out_usage; 99ced56a00Sopenharmony_ci } 100ced56a00Sopenharmony_ci length_specified = true; 101ced56a00Sopenharmony_ci break; 102ced56a00Sopenharmony_ci default: 103ced56a00Sopenharmony_ci goto out_usage; 104ced56a00Sopenharmony_ci } 105ced56a00Sopenharmony_ci } 106ced56a00Sopenharmony_ci 107ced56a00Sopenharmony_ci argv += optind; 108ced56a00Sopenharmony_ci argc -= optind; 109ced56a00Sopenharmony_ci 110ced56a00Sopenharmony_ci if (argc != 2) 111ced56a00Sopenharmony_ci goto out_usage; 112ced56a00Sopenharmony_ci 113ced56a00Sopenharmony_ci if (!parse_metadata_type(argv[0], &arg.metadata_type)) 114ced56a00Sopenharmony_ci goto out_usage; 115ced56a00Sopenharmony_ci 116ced56a00Sopenharmony_ci if (length_specified && !offset_specified) { 117ced56a00Sopenharmony_ci error_msg("--length specified without --offset"); 118ced56a00Sopenharmony_ci goto out_usage; 119ced56a00Sopenharmony_ci } 120ced56a00Sopenharmony_ci if (offset_specified && !length_specified) { 121ced56a00Sopenharmony_ci error_msg("--offset specified without --length"); 122ced56a00Sopenharmony_ci goto out_usage; 123ced56a00Sopenharmony_ci } 124ced56a00Sopenharmony_ci 125ced56a00Sopenharmony_ci buf = xzalloc(arg.length); 126ced56a00Sopenharmony_ci arg.buf_ptr = (uintptr_t)buf; 127ced56a00Sopenharmony_ci 128ced56a00Sopenharmony_ci if (!open_file(&file, argv[1], O_RDONLY, 0)) 129ced56a00Sopenharmony_ci goto out_err; 130ced56a00Sopenharmony_ci 131ced56a00Sopenharmony_ci /* 132ced56a00Sopenharmony_ci * If --offset and --length were specified, then do only the single read 133ced56a00Sopenharmony_ci * requested. Otherwise read until EOF. 134ced56a00Sopenharmony_ci */ 135ced56a00Sopenharmony_ci do { 136ced56a00Sopenharmony_ci bytes_read = ioctl(file.fd, FS_IOC_READ_VERITY_METADATA, &arg); 137ced56a00Sopenharmony_ci if (bytes_read < 0) { 138ced56a00Sopenharmony_ci error_msg_errno("FS_IOC_READ_VERITY_METADATA failed on '%s'", 139ced56a00Sopenharmony_ci file.name); 140ced56a00Sopenharmony_ci goto out_err; 141ced56a00Sopenharmony_ci } 142ced56a00Sopenharmony_ci if (bytes_read == 0) 143ced56a00Sopenharmony_ci break; 144ced56a00Sopenharmony_ci if (!full_write(&stdout_filedes, buf, bytes_read)) 145ced56a00Sopenharmony_ci goto out_err; 146ced56a00Sopenharmony_ci arg.offset += bytes_read; 147ced56a00Sopenharmony_ci } while (!length_specified); 148ced56a00Sopenharmony_ci 149ced56a00Sopenharmony_ci status = 0; 150ced56a00Sopenharmony_ciout: 151ced56a00Sopenharmony_ci free(buf); 152ced56a00Sopenharmony_ci filedes_close(&file); 153ced56a00Sopenharmony_ci return status; 154ced56a00Sopenharmony_ci 155ced56a00Sopenharmony_ciout_err: 156ced56a00Sopenharmony_ci status = 1; 157ced56a00Sopenharmony_ci goto out; 158ced56a00Sopenharmony_ci 159ced56a00Sopenharmony_ciout_usage: 160ced56a00Sopenharmony_ci usage(cmd, stderr); 161ced56a00Sopenharmony_ci status = 2; 162ced56a00Sopenharmony_ci goto out; 163ced56a00Sopenharmony_ci} 164