1 /*
2  * Copyright 2018 Jonathan Dieter <jdieter@gmail.com>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *  1. Redistributions of source code must retain the above copyright notice,
8  *     this list of conditions and the following disclaimer.
9  *
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <stdbool.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <argp.h>
36 #include <zck.h>
37 
38 #include "util_common.h"
39 
40 static char doc[] = "zck_read_header - Read header from a zchunk file";
41 
42 static char args_doc[] = "<file>";
43 
44 static struct argp_option options[] = {
45     {"verbose",     'v', 0,        0,
46      "Increase verbosity (can be specified more than once for debugging)"},
47     {"show-chunks", 'c', 0,        0, "Show chunk information"},
48     {"quiet",       'q', 0,        0, "Only show errors"},
49     {"version",     'V', 0,        0, "Show program version"},
50     {"verify",      'f', 0,        0, "Verify full zchunk file"},
51     { 0 }
52 };
53 
54 struct arguments {
55   char *args[1];
56   bool verify;
57   bool quiet;
58   bool show_chunks;
59   zck_log_type log_level;
60   bool exit;
61 };
62 
parse_opt(int key,char * arg,struct argp_state * state)63 static error_t parse_opt (int key, char *arg, struct argp_state *state) {
64     struct arguments *arguments = state->input;
65 
66     if(arguments->exit)
67         return 0;
68 
69     switch (key) {
70         case 'v':
71             arguments->log_level--;
72             if(arguments->log_level < ZCK_LOG_DDEBUG)
73                 arguments->log_level = ZCK_LOG_DDEBUG;
74             break;
75         case 'c':
76             arguments->show_chunks = true;
77             break;
78         case 'q':
79             arguments->quiet = true;
80             break;
81         case 'V':
82             version();
83             arguments->exit = true;
84             break;
85         case 'f':
86             arguments->verify = true;
87             break;
88 
89         case ARGP_KEY_ARG:
90             if (state->arg_num >= 1) {
91                 argp_usage (state);
92                 return EINVAL;
93             }
94             arguments->args[state->arg_num] = arg;
95 
96             break;
97 
98         case ARGP_KEY_END:
99             if (state->arg_num < 1) {
100                 argp_usage (state);
101                 return EINVAL;
102             }
103             break;
104 
105         default:
106             return ARGP_ERR_UNKNOWN;
107     }
108     return 0;
109 }
110 
111 static struct argp argp = {options, parse_opt, args_doc, doc};
112 
main(int argc,char * argv[])113 int main (int argc, char *argv[]) {
114     struct arguments arguments = {0};
115 
116     /* Defaults */
117     arguments.log_level = ZCK_LOG_ERROR;
118 
119     int retval = argp_parse (&argp, argc, argv, 0, 0, &arguments);
120     if(retval || arguments.exit)
121         exit(retval);
122 
123     zck_set_log_level(arguments.log_level);
124 
125     int src_fd = open(arguments.args[0], O_RDONLY);
126     if(src_fd < 0) {
127         printf("Unable to open %s\n", arguments.args[0]);
128         perror("");
129         exit(1);
130     }
131 
132     zckCtx *zck = zck_create();
133     if(zck == NULL) {
134         dprintf(STDERR_FILENO, "%s", zck_get_error(NULL));
135         zck_clear_error(NULL);
136         exit(1);
137     }
138     if(!zck_init_read(zck, src_fd)) {
139         dprintf(STDERR_FILENO, "Error reading zchunk header: %s",
140                 zck_get_error(zck));
141         zck_free(&zck);
142         exit(1);
143     }
144 
145     int valid_cks = 1;
146     if(arguments.verify) {
147         valid_cks = zck_validate_checksums(zck);
148         if(!valid_cks)
149             exit(1);
150     }
151     close(src_fd);
152 
153     if(!arguments.quiet) {
154         printf("Overall checksum type: %s\n",
155                zck_hash_name_from_type(zck_get_full_hash_type(zck)));
156         printf("Header size: %lu\n", zck_get_header_length(zck));
157         char *digest = zck_get_header_digest(zck);
158         printf("Header checksum: %s\n", digest);
159         free(digest);
160         printf("Data size: %lu\n", zck_get_data_length(zck));
161         digest = zck_get_data_digest(zck);
162         printf("Data checksum: %s\n", digest);
163         free(digest);
164         printf("Chunk count: %lu\n", (long unsigned)zck_get_chunk_count(zck));
165         printf("Chunk checksum type: %s\n", zck_hash_name_from_type(zck_get_chunk_hash_type(zck)));
166     }
167     if(!arguments.quiet && arguments.show_chunks)
168         printf("\n");
169     if(arguments.show_chunks) {
170         printf("       Chunk Checksum %*c        Start    Comp size         Size\n",
171                (((int)zck_get_chunk_digest_size(zck)*2)-9), ' ');
172         for(zckChunk *chk=zck_get_first_chunk(zck); chk;
173             chk=zck_get_next_chunk(chk)) {
174             char *digest = zck_get_chunk_digest(chk);
175             if(digest == NULL) {
176                 dprintf(STDERR_FILENO, "%s", zck_get_error(zck));
177                 exit(1);
178             }
179             printf("%12lu %s %12lu %12lu %12lu",
180                    (long unsigned)zck_get_chunk_number(chk),
181                    digest,
182                    (long unsigned)zck_get_chunk_start(chk),
183                    (long unsigned)zck_get_chunk_comp_size(chk),
184                    (long unsigned)zck_get_chunk_size(chk));
185             if(arguments.verify) {
186                 if(zck_get_chunk_valid(chk) == 1)
187                     printf("  +");
188                 else
189                     printf("  !");
190             }
191             printf("\n");
192             free(digest);
193         }
194     }
195     if(arguments.verify) {
196         if(valid_cks == 1 && arguments.log_level <= ZCK_LOG_WARNING)
197             printf("All checksums are valid\n");
198         else if(valid_cks == -1)
199             printf("Some checksums failed\n");
200     }
201     zck_free(&zck);
202     return 1-valid_cks;
203 }
204