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