1 /* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 #include "azlib.h"
24 #include <string.h>
25 #include <assert.h>
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <m_ctype.h>
29 #include <m_string.h>
30 #include <my_getopt.h>
31 #include <mysql_version.h>
32 #include <welcome_copyright_notice.h> // ORACLE_WELCOME_COPYRIGHT_NOTICE
33 
34 #define BUFFER_LEN 1024
35 #define ARCHIVE_ROW_HEADER_SIZE 4
36 
37 #define SHOW_VERSION "0.1"
38 
39 static void get_options(int *argc,char * * *argv);
40 static void print_version(void);
41 static void usage(void);
42 static const char *opt_tmpdir;
43 static const char *new_auto_increment;
44 unsigned long long new_auto_increment_value;
45 static const char *load_default_groups[]= { "archive_reader", 0 };
46 static char **default_argv;
47 int opt_check, opt_force, opt_quiet, opt_backup= 0, opt_extract_frm;
48 int opt_autoincrement;
49 
main(int argc,char * argv[])50 int main(int argc, char *argv[])
51 {
52   unsigned int ret;
53   azio_stream reader_handle;
54 
55   MY_INIT(argv[0]);
56   get_options(&argc, &argv);
57 
58   if (argc < 1)
59   {
60     printf("No file specified. \n");
61     return 0;
62   }
63 
64   if (!(ret= azopen(&reader_handle, argv[0], O_RDONLY|O_BINARY)))
65   {
66     printf("Could not open Archive file\n");
67     return 0;
68   }
69 
70   if (opt_autoincrement)
71   {
72     azio_stream writer_handle;
73 
74     if (new_auto_increment_value)
75     {
76       if (reader_handle.auto_increment >= new_auto_increment_value)
77       {
78         printf("Value is lower then current value\n");
79         goto end;
80       }
81     }
82     else
83     {
84       new_auto_increment_value= reader_handle.auto_increment + 1;
85     }
86 
87     if (!(ret= azopen(&writer_handle, argv[0], O_CREAT|O_RDWR|O_BINARY)))
88     {
89       printf("Could not open file for update: %s\n", argv[0]);
90       goto end;
91     }
92 
93     writer_handle.auto_increment= new_auto_increment_value;
94 
95     azclose(&writer_handle);
96     azflush(&reader_handle, Z_SYNC_FLUSH);
97   }
98 
99   printf("Version %u\n", reader_handle.version);
100   if (reader_handle.version > 2)
101   {
102     printf("\tMinor version %u\n", reader_handle.minor_version);
103     printf("\tStart position %llu\n", (unsigned long long)reader_handle.start);
104     printf("\tBlock size %u\n", reader_handle.block_size);
105     printf("\tRows %llu\n", reader_handle.rows);
106     printf("\tAutoincrement %llu\n", reader_handle.auto_increment);
107     printf("\tCheck Point %llu\n", reader_handle.check_point);
108     printf("\tForced Flushes %llu\n", reader_handle.forced_flushes);
109     printf("\tLongest Row %u\n", reader_handle.longest_row);
110     printf("\tShortest Row %u\n", reader_handle.shortest_row);
111     printf("\tState %s\n", ( reader_handle.dirty ? "dirty" : "clean"));
112     printf("\tFRM stored at %u\n", reader_handle.frm_start_pos);
113     printf("\tComment stored at %u\n", reader_handle.comment_start_pos);
114     printf("\tData starts at %u\n", (unsigned int)reader_handle.start);
115     if (reader_handle.frm_start_pos)
116       printf("\tFRM length %u\n", reader_handle.frm_length);
117     if (reader_handle.comment_start_pos)
118     {
119       char *comment =
120         (char *) malloc(sizeof(char) * reader_handle.comment_length);
121       azread_comment(&reader_handle, comment);
122       printf("\tComment length %u\n\t\t%.*s\n", reader_handle.comment_length,
123              reader_handle.comment_length, comment);
124       free(comment);
125     }
126   }
127   else
128   {
129     goto end;
130   }
131 
132   printf("\n");
133 
134   if (opt_check)
135   {
136     uchar size_buffer[ARCHIVE_ROW_HEADER_SIZE];
137     int error;
138     unsigned int x;
139     unsigned int read;
140     unsigned int row_len;
141     unsigned long long row_count= 0;
142     char buffer;
143 
144     while ((read= azread(&reader_handle, (uchar *)size_buffer,
145                         ARCHIVE_ROW_HEADER_SIZE, &error)))
146     {
147       if (error == Z_STREAM_ERROR ||  (read && read < ARCHIVE_ROW_HEADER_SIZE))
148       {
149         printf("Table is damaged\n");
150         goto end;
151       }
152 
153       /* If we read nothing we are at the end of the file */
154       if (read == 0 || read != ARCHIVE_ROW_HEADER_SIZE)
155         break;
156 
157       row_len=  uint4korr(size_buffer);
158       row_count++;
159 
160       if (row_len > reader_handle.longest_row)
161       {
162         printf("Table is damaged, row %llu is invalid\n",
163                row_count);
164         goto end;
165       }
166 
167 
168       for (read= x= 0; x < row_len ; x++)
169       {
170         read+= (unsigned int)azread(&reader_handle, &buffer, sizeof(char), &error);
171         if (!read)
172           break;
173       }
174 
175 
176       if (row_len != read)
177       {
178         printf("Row length did not match row (at %llu). %u != %u \n",
179                row_count, row_len, read);
180         goto end;
181       }
182     }
183 
184     if (0)
185     {
186       printf("Table is damaged\n");
187       goto end;
188     }
189     else
190     {
191       printf("Found %llu rows\n", row_count);
192     }
193   }
194 
195   if (opt_backup)
196   {
197     uchar size_buffer[ARCHIVE_ROW_HEADER_SIZE];
198     int error;
199     unsigned int read;
200     unsigned int row_len;
201     unsigned long long row_count= 0;
202     char *buffer;
203 
204     azio_stream writer_handle;
205 
206     buffer= (char *)malloc(reader_handle.longest_row);
207     if (buffer == NULL)
208     {
209       printf("Could not allocate memory for row %llu\n", row_count);
210       goto end;
211     }
212 
213 
214     if (!(ret= azopen(&writer_handle, argv[1], O_CREAT|O_RDWR|O_BINARY)))
215     {
216       printf("Could not open file for backup: %s\n", argv[1]);
217       goto end;
218     }
219 
220     writer_handle.auto_increment= reader_handle.auto_increment;
221     if (reader_handle.frm_length)
222     {
223       char *ptr;
224       ptr= (char *)my_malloc(sizeof(char) * reader_handle.frm_length, MYF(0));
225       azread_frm(&reader_handle, ptr);
226       azwrite_frm(&writer_handle, ptr, reader_handle.frm_length);
227       my_free(ptr);
228     }
229 
230     if (reader_handle.comment_length)
231     {
232       char *ptr;
233       ptr= (char *)my_malloc(sizeof(char) * reader_handle.comment_length, MYF(0));
234       azread_comment(&reader_handle, ptr);
235       azwrite_comment(&writer_handle, ptr, reader_handle.comment_length);
236       my_free(ptr);
237     }
238 
239     while ((read= azread(&reader_handle, (uchar *)size_buffer,
240                         ARCHIVE_ROW_HEADER_SIZE, &error)))
241     {
242       if (error == Z_STREAM_ERROR ||  (read && read < ARCHIVE_ROW_HEADER_SIZE))
243       {
244         printf("Table is damaged\n");
245         goto end;
246       }
247 
248       /* If we read nothing we are at the end of the file */
249       if (read == 0 || read != ARCHIVE_ROW_HEADER_SIZE)
250         break;
251 
252       row_len=  uint4korr(size_buffer);
253 
254       row_count++;
255 
256       memcpy(buffer, size_buffer, ARCHIVE_ROW_HEADER_SIZE);
257 
258       read= (unsigned int)azread(&reader_handle, buffer + ARCHIVE_ROW_HEADER_SIZE,
259                                  row_len, &error);
260 
261       DBUG_ASSERT(read == row_len);
262 
263       azwrite(&writer_handle, buffer, row_len + ARCHIVE_ROW_HEADER_SIZE);
264 
265 
266       if (row_len != read)
267       {
268         printf("Row length did not match row (at %llu). %u != %u \n",
269                row_count, row_len, read);
270         goto end;
271       }
272 
273       if (reader_handle.rows == writer_handle.rows)
274         break;
275     }
276 
277     free(buffer);
278 
279     azclose(&writer_handle);
280   }
281 
282   if (opt_extract_frm)
283   {
284     File frm_file;
285     char *ptr;
286     frm_file= my_open(argv[1], O_CREAT|O_RDWR|O_BINARY, MYF(0));
287     ptr= (char *)my_malloc(sizeof(char) * reader_handle.frm_length, MYF(0));
288     azread_frm(&reader_handle, ptr);
289     my_write(frm_file, (uchar*) ptr, reader_handle.frm_length, MYF(0));
290     my_close(frm_file, MYF(0));
291     my_free(ptr);
292   }
293 
294 end:
295   printf("\n");
296   azclose(&reader_handle);
297 
298   return 0;
299 }
300 
301 static my_bool
get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)302 get_one_option(int optid,
303 	       const struct my_option *opt MY_ATTRIBUTE((unused)),
304 	       char *argument)
305 {
306   switch (optid) {
307   case 'b':
308     opt_backup= 1;
309     break;
310   case 'c':
311     opt_check= 1;
312     break;
313   case 'e':
314     opt_extract_frm= 1;
315     break;
316   case 'f':
317     opt_force= 1;
318     printf("Not implemented yet\n");
319     break;
320   case 'q':
321     opt_quiet= 1;
322     printf("Not implemented yet\n");
323     break;
324   case 'V':
325     print_version();
326     exit(0);
327   case 't':
328     printf("Not implemented yet\n");
329     break;
330   case 'A':
331     opt_autoincrement= 1;
332     if (argument)
333       new_auto_increment_value= strtoull(argument, NULL, 0);
334     else
335       new_auto_increment_value= 0;
336     break;
337   case '?':
338     usage();
339     exit(0);
340   case '#':
341     if (argument == disabled_my_option)
342     {
343       DBUG_POP();
344     }
345     else
346     {
347       DBUG_PUSH(argument ? argument : "d:t:o,/tmp/archive_reader.trace");
348     }
349     break;
350   }
351   return 0;
352 }
353 
354 static struct my_option my_long_options[] =
355 {
356   {"backup", 'b',
357    "Make a backup of an archive table.",
358    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
359   {"check", 'c', "Check table for errors.",
360    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
361 #ifndef DBUG_OFF
362   {"debug", '#',
363    "Output debug log. Often this is 'd:t:o,filename'.",
364    0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
365 #endif
366   {"extract-frm", 'e',
367    "Extract the frm file.",
368    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
369   {"force", 'f',
370    "Restart with -r if there are any errors in the table.",
371    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
372   {"help", '?',
373    "Display this help and exit.",
374    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
375   {"quick", 'q', "Faster repair by not modifying the data file.",
376    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
377   {"repair", 'r', "Repair a damaged Archive version 3 or above file.",
378    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
379   {"set-auto-increment", 'A',
380    "Force auto_increment to start at this or higher value. If no value is given, then sets the next auto_increment value to the highest used value for the auto key + 1.",
381    &new_auto_increment, &new_auto_increment,
382    0, GET_ULL, OPT_ARG, 0, 0, 0, 0, 0, 0},
383   {"silent", 's',
384    "Only print errors. One can use two -s to make archive_reader very silent.",
385    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
386   {"tmpdir", 't',
387    "Path for temporary files.",
388    &opt_tmpdir,
389    0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
390   {"version", 'V',
391    "Print version and exit.",
392    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
393   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
394 };
395 
usage(void)396 static void usage(void)
397 {
398   print_version();
399   puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2007"));
400 
401   puts("Read and modify Archive files directly\n");
402   printf("Usage: %s [OPTIONS] file_to_be_looked_at [file_for_backup]\n", my_progname);
403   print_defaults("my", load_default_groups);
404   my_print_help(my_long_options);
405 }
406 
print_version(void)407 static void print_version(void)
408 {
409   printf("%s  Ver %s Distrib %s, for %s (%s)\n", my_progname, SHOW_VERSION,
410          MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
411 }
412 
get_options(int * argc,char *** argv)413 static void get_options(int *argc, char ***argv)
414 {
415   if (load_defaults("my", load_default_groups, argc, argv))
416     exit(1);
417   default_argv= *argv;
418 
419   handle_options(argc, argv, my_long_options, get_one_option);
420 
421   if (*argc == 0)
422   {
423     usage();
424     exit(-1);
425   }
426 
427   return;
428 } /* get options */
429