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