File: cmd_dump_metadata.c

package info (click to toggle)
fsverity-utils 1.6-1.2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 316 kB
  • sloc: ansic: 2,683; sh: 324; makefile: 191
file content (163 lines) | stat: -rw-r--r-- 3,820 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// 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;
}