1 /* Copyright (C) 2019 MariaDB Corporation Ab
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 as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software Foundation,
14 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
15
16 /*
17 Interface function used by S3 storage engine and aria_copy_for_s3
18 */
19
20 #include "maria_def.h"
21 #include "s3_func.h"
22 #include <aria_backup.h>
23 #include <mysqld_error.h>
24 #include <sql_const.h>
25 #include <mysys_err.h>
26 #include <mysql_com.h>
27 #include <zlib.h>
28
29 /* number of '.' to print during a copy in verbose mode */
30 #define DISPLAY_WITH 79
31
32 static void convert_index_to_s3_format(uchar *header, ulong block_size,
33 int compression);
34 static void convert_index_to_disk_format(uchar *header);
35 static void convert_frm_to_s3_format(uchar *header);
36 static void convert_frm_to_disk_format(uchar *header);
37 static int s3_read_file_from_disk(const char *filename, uchar **to,
38 size_t *to_size, my_bool print_error);
39
40 /* Used by ha_s3.cc and tools to define different protocol options */
41
42 static const char *protocol_types[]= {"Auto", "Original", "Amazon", NullS};
43 TYPELIB s3_protocol_typelib= {array_elements(protocol_types)-1,"",
44 protocol_types, NULL};
45
46 /******************************************************************************
47 Allocations handler for libmarias3
48 To be removed when we do the init allocation in mysqld.cc
49 ******************************************************************************/
50
s3_wrap_malloc(size_t size)51 static void *s3_wrap_malloc(size_t size)
52 {
53 return my_malloc(PSI_NOT_INSTRUMENTED, size, MYF(MY_WME));
54 }
55
s3_wrap_calloc(size_t nmemb,size_t size)56 static void *s3_wrap_calloc(size_t nmemb, size_t size)
57 {
58 return my_malloc(PSI_NOT_INSTRUMENTED, nmemb * size,
59 MYF(MY_WME | MY_ZEROFILL));
60 }
61
s3_wrap_realloc(void * ptr,size_t size)62 static void *s3_wrap_realloc(void *ptr, size_t size)
63 {
64 return my_realloc(PSI_NOT_INSTRUMENTED, ptr, size,
65 MYF(MY_WME | MY_ALLOW_ZERO_PTR));
66 }
67
s3_wrap_strdup(const char * str)68 static char *s3_wrap_strdup(const char *str)
69 {
70 return my_strdup(PSI_NOT_INSTRUMENTED, str, MYF(MY_WME));
71 }
72
s3_wrap_free(void * ptr)73 static void s3_wrap_free(void *ptr)
74 {
75 if (ptr) /* Avoid tracing of null */
76 my_free(ptr);
77 }
78
s3_init_library()79 void s3_init_library()
80 {
81 ms3_library_init_malloc(s3_wrap_malloc, s3_wrap_free, s3_wrap_realloc,
82 s3_wrap_strdup, s3_wrap_calloc);
83 }
84
s3_deinit_library()85 void s3_deinit_library()
86 {
87 ms3_library_deinit();
88 }
89
90 /******************************************************************************
91 Functions on S3_INFO and S3_BLOCK
92 ******************************************************************************/
93
94 /*
95 Free memory allocated by s3_get_object
96 */
97
s3_free(S3_BLOCK * data)98 void s3_free(S3_BLOCK *data)
99 {
100 my_free(data->alloc_ptr);
101 data->alloc_ptr= 0;
102 }
103
104
105 /*
106 Copy a S3_INFO structure
107 */
108
s3_info_copy(S3_INFO * old)109 S3_INFO *s3_info_copy(S3_INFO *old)
110 {
111 S3_INFO *to, tmp;
112
113 /* Copy lengths */
114 memcpy(&tmp, old, sizeof(tmp));
115 /* Allocate new buffers */
116 if (!my_multi_malloc(PSI_NOT_INSTRUMENTED, MY_WME, &to, sizeof(S3_INFO),
117 &tmp.access_key.str, old->access_key.length+1,
118 &tmp.secret_key.str, old->secret_key.length+1,
119 &tmp.region.str, old->region.length+1,
120 &tmp.bucket.str, old->bucket.length+1,
121 &tmp.database.str, old->database.length+1,
122 &tmp.table.str, old->table.length+1,
123 &tmp.base_table.str, old->base_table.length+1,
124 NullS))
125 return 0;
126 /* Copy lengths and new pointers to to */
127 memcpy(to, &tmp, sizeof(tmp));
128 /* Copy data */
129 strmov((char*) to->access_key.str, old->access_key.str);
130 strmov((char*) to->secret_key.str, old->secret_key.str);
131 strmov((char*) to->region.str, old->region.str);
132 strmov((char*) to->bucket.str, old->bucket.str);
133 /* Database may not be null terminated */
134 strmake((char*) to->database.str, old->database.str, old->database.length);
135 strmov((char*) to->table.str, old->table.str);
136 strmov((char*) to->base_table.str, old->base_table.str);
137 return to;
138 }
139
140 /**
141 Open a connection to s3
142 */
143
s3_open_connection(S3_INFO * s3)144 ms3_st *s3_open_connection(S3_INFO *s3)
145 {
146 ms3_st *s3_client;
147 if (!(s3_client= ms3_init(s3->access_key.str,
148 s3->secret_key.str,
149 s3->region.str,
150 s3->host_name.str)))
151 {
152 my_printf_error(HA_ERR_NO_SUCH_TABLE,
153 "Can't open connection to S3, error: %d %s", MYF(0),
154 errno, ms3_error(errno));
155 my_errno= HA_ERR_NO_SUCH_TABLE;
156 }
157 if (s3->protocol_version)
158 ms3_set_option(s3_client, MS3_OPT_FORCE_PROTOCOL_VERSION,
159 &s3->protocol_version);
160 if (s3->port)
161 ms3_set_option(s3_client, MS3_OPT_PORT_NUMBER, &s3->port);
162
163 if (s3->use_http)
164 ms3_set_option(s3_client, MS3_OPT_USE_HTTP, NULL);
165
166 return s3_client;
167 }
168
169 /**
170 close a connection to s3
171 */
172
s3_deinit(ms3_st * s3_client)173 void s3_deinit(ms3_st *s3_client)
174 {
175 DBUG_PUSH(""); /* Avoid tracing free calls */
176 ms3_deinit(s3_client);
177 DBUG_POP();
178 }
179
180
181 /******************************************************************************
182 High level functions to copy tables to and from S3
183 ******************************************************************************/
184
185 /**
186 Create suffix for object name
187 @param to_end end of suffix (from previous call or 000000 at start)
188
189 The suffix is a 6 length '0' prefixed number. If the number
190 gets longer than 6, then it's extended to 7 and more digits.
191 */
192
fix_suffix(char * to_end,ulong nr)193 static void fix_suffix(char *to_end, ulong nr)
194 {
195 char buff[11];
196 uint length= (uint) (int10_to_str(nr, buff, 10) - buff);
197 set_if_smaller(length, 6);
198 strmov(to_end - length, buff);
199 }
200
201 /**
202 Copy file to 'aws_path' in blocks of block_size
203
204 @return 0 ok
205 @return 1 error. Error message is printed to stderr
206
207 Notes:
208 file is always closed before return
209 */
210
copy_from_file(ms3_st * s3_client,const char * aws_bucket,char * aws_path,File file,my_off_t start,my_off_t file_end,uchar * block,size_t block_size,my_bool compression,my_bool display)211 static my_bool copy_from_file(ms3_st *s3_client, const char *aws_bucket,
212 char *aws_path,
213 File file, my_off_t start, my_off_t file_end,
214 uchar *block, size_t block_size,
215 my_bool compression, my_bool display)
216 {
217 my_off_t pos;
218 char *path_end= strend(aws_path);
219 ulong bnr;
220 my_bool print_done= 0;
221 size_t length;
222
223 for (pos= start, bnr=1 ; pos < file_end ; pos+= length, bnr++)
224 {
225 if ((length= my_pread(file, block, block_size, pos, MYF(MY_WME))) ==
226 MY_FILE_ERROR)
227 goto err;
228 if (length == 0)
229 {
230 my_error(EE_EOFERR, MYF(0), my_filename(file), my_errno);
231 goto err;
232 }
233
234 fix_suffix(path_end, bnr);
235 if (s3_put_object(s3_client, aws_bucket, aws_path, block, length,
236 compression))
237 goto err;
238
239 /* Write up to DISPLAY_WITH number of '.' during copy */
240 if (display &&
241 ((pos + block_size) * DISPLAY_WITH / file_end) >
242 (pos * DISPLAY_WITH/file_end))
243 {
244 fputc('.', stdout); fflush(stdout);
245 print_done= 1;
246 }
247 }
248 if (print_done)
249 {
250 fputc('\n', stdout); fflush(stdout);
251 }
252 my_close(file, MYF(MY_WME));
253 return 0;
254
255 err:
256 my_close(file, MYF(MY_WME));
257 if (print_done)
258 {
259 fputc('\n', stdout); fflush(stdout);
260 }
261 return 1;
262 }
263
264
265 /**
266 Copy an Aria table to S3
267 @param s3_client connection to S3
268 @param aws_bucket Aws bucket
269 @param path Path for Aria table (can be temp table)
270 @param database database name
271 @param table_name table name
272 @param block_size Block size in s3. If 0 then use block size
273 and compression as specified in the .MAI file as
274 specified as part of open.
275 @param compression Compression algorithm (0 = none, 1 = zip)
276 If block size is 0 then use .MAI file.
277 @return 0 ok
278 @return 1 error
279
280 The table will be copied in S3 into the following locations:
281
282 frm file (for discovery):
283 aws_bucket/database/table/frm
284
285 First index block (contains description if the Aria file):
286 aws_bucket/database/table/aria
287
288 Rest of the index file:
289 aws_bucket/database/table/index/block_number
290
291 Data file:
292 aws_bucket/database/table/data/block_number
293
294 block_number is 6 digits decimal number, prefixed with 0
295 (Can be larger than 6 numbers, the prefix is just for nice output)
296
297 frm and base blocks are small (just the needed data).
298 index and blocks are of size 's3_block_size'
299
300 If compression is used, then original block size is s3_block_size
301 but the stored block will be the size of the compressed block.
302 */
303
aria_copy_to_s3(ms3_st * s3_client,const char * aws_bucket,const char * path,const char * database,const char * table_name,ulong block_size,my_bool compression,my_bool force,my_bool display,my_bool copy_frm)304 int aria_copy_to_s3(ms3_st *s3_client, const char *aws_bucket,
305 const char *path,
306 const char *database, const char *table_name,
307 ulong block_size, my_bool compression,
308 my_bool force, my_bool display, my_bool copy_frm)
309 {
310 ARIA_TABLE_CAPABILITIES cap;
311 char aws_path[FN_REFLEN+100];
312 char filename[FN_REFLEN];
313 char *aws_path_end, *end;
314 uchar *alloc_block= 0, *block;
315 ms3_status_st status;
316 File file= -1;
317 my_off_t file_size;
318 size_t frm_length;
319 int error;
320 my_bool frm_created= 0;
321 DBUG_ENTER("aria_copy_to_s3");
322 DBUG_PRINT("enter",("from: %s database: %s table: %s",
323 path, database, table_name));
324
325 aws_path_end= strxmov(aws_path, database, "/", table_name, NullS);
326 strmov(aws_path_end, "/aria");
327
328 if (!ms3_status(s3_client, aws_bucket, aws_path, &status))
329 {
330 if (!force)
331 {
332 my_printf_error(EE_CANTCREATEFILE, "File %s exists in s3", MYF(0),
333 aws_path);
334 DBUG_RETURN(EE_CANTCREATEFILE);
335 }
336 if ((error= aria_delete_from_s3(s3_client, aws_bucket, database,
337 table_name, display)))
338 DBUG_RETURN(error);
339 }
340
341 if (copy_frm)
342 {
343 /*
344 Copy frm file if it exists
345 We do this first to ensure that .frm always exists. This is needed to
346 ensure that discovery of the table will work.
347 */
348 fn_format(filename, path, "", ".frm", MY_REPLACE_EXT);
349 if (!s3_read_file_from_disk(filename, &alloc_block, &frm_length,0))
350 {
351 if (display)
352 printf("Copying frm file %s\n", filename);
353
354 end= strmov(aws_path_end,"/frm");
355 convert_frm_to_s3_format(alloc_block);
356
357 /* Note that frm is not compressed! */
358 if (s3_put_object(s3_client, aws_bucket, aws_path, alloc_block, frm_length,
359 0))
360 goto err;
361
362 frm_created= 1;
363 my_free(alloc_block);
364 alloc_block= 0;
365 }
366 }
367
368 if (display)
369 printf("Copying aria table: %s.%s to s3\n", database, table_name);
370
371 /* Index file name */
372 fn_format(filename, path, "", ".MAI", MY_REPLACE_EXT);
373 if ((file= my_open(filename,
374 O_RDONLY | O_SHARE | O_NOFOLLOW | O_CLOEXEC,
375 MYF(MY_WME))) < 0)
376 DBUG_RETURN(1);
377 if ((error= aria_get_capabilities(file, &cap)))
378 {
379 fprintf(stderr, "Got error %d when reading Aria header from %s\n",
380 error, path);
381 goto err;
382 }
383 if (cap.transactional || cap.data_file_type != BLOCK_RECORD ||
384 cap.encrypted)
385 {
386 fprintf(stderr,
387 "Aria table %s doesn't match criteria to be copied to S3.\n"
388 "It should be non-transactional and should have row_format page\n",
389 path);
390 goto err;
391 }
392 /*
393 If block size is not specified, use the values specified as part of
394 create
395 */
396 if (block_size == 0)
397 {
398 block_size= cap.s3_block_size;
399 compression= cap.compression;
400 }
401
402 /* Align S3_BLOCK size with table block size */
403 block_size= (block_size/cap.block_size)*cap.block_size;
404
405 /* Allocate block for data + flag for compress header */
406 if (!(alloc_block= (uchar*) my_malloc(PSI_NOT_INSTRUMENTED,
407 block_size+ALIGN_SIZE(1),
408 MYF(MY_WME))))
409 goto err;
410 /* Read/write data here, but with prefix space for compression flag */
411 block= alloc_block+ ALIGN_SIZE(1);
412
413 if (my_pread(file, block, cap.header_size, 0, MYF(MY_WME | MY_FNABP)))
414 goto err;
415
416 strmov(aws_path_end, "/aria");
417
418 if (display)
419 printf("Creating aria table information %s\n", aws_path);
420
421 convert_index_to_s3_format(block, block_size, compression);
422
423 /*
424 The first page is not compressed as we need it to know if the rest is
425 compressed
426 */
427 if (s3_put_object(s3_client, aws_bucket, aws_path, block, cap.header_size,
428 0 /* no compression */ ))
429 goto err;
430
431 file_size= my_seek(file, 0L, MY_SEEK_END, MYF(0));
432
433 end= strmov(aws_path_end,"/index");
434
435 if (display)
436 printf("Copying index information %s\n", aws_path);
437
438 /* The 000000 will be update with block number by fix_suffix() */
439 end= strmov(end, "/000000");
440
441 error= copy_from_file(s3_client, aws_bucket, aws_path, file, cap.header_size,
442 file_size, block, block_size, compression, display);
443 file= -1;
444 if (error)
445 goto err;
446
447 /* Copy data file */
448 fn_format(filename, path, "", ".MAD", MY_REPLACE_EXT);
449 if ((file= my_open(filename,
450 O_RDONLY | O_SHARE | O_NOFOLLOW | O_CLOEXEC,
451 MYF(MY_WME))) < 0)
452 DBUG_RETURN(1);
453
454 file_size= my_seek(file, 0L, MY_SEEK_END, MYF(0));
455
456 end= strmov(aws_path_end, "/data");
457
458 if (display)
459 printf("Copying data information %s\n", aws_path);
460
461 /* The 000000 will be update with block number by fix_suffix() */
462 end= strmov(end, "/000000");
463
464 error= copy_from_file(s3_client, aws_bucket, aws_path, file, 0, file_size,
465 block, block_size, compression, display);
466 file= -1;
467 if (error)
468 goto err;
469
470 my_free(alloc_block);
471 DBUG_RETURN(0);
472
473 err:
474 if (frm_created)
475 {
476 end= strmov(aws_path_end,"/frm");
477 (void) s3_delete_object(s3_client, aws_bucket, aws_path, MYF(ME_NOTE));
478 }
479 if (file >= 0)
480 my_close(file, MYF(0));
481 my_free(alloc_block);
482 DBUG_RETURN(1);
483 }
484
485
486 /**
487 Copy file to 'aws_path' in blocks of block_size
488
489 @return 0 ok
490 @return 1 error. Error message is printed to stderr
491
492 Notes:
493 file is always closed before return
494 */
495
copy_to_file(ms3_st * s3_client,const char * aws_bucket,char * aws_path,File file,my_off_t start,my_off_t file_end,my_bool compression,my_bool display)496 static my_bool copy_to_file(ms3_st *s3_client, const char *aws_bucket,
497 char *aws_path, File file, my_off_t start,
498 my_off_t file_end, my_bool compression,
499 my_bool display)
500 {
501 my_off_t pos;
502 char *path_end= strend(aws_path);
503 size_t error;
504 ulong bnr;
505 my_bool print_done= 0;
506 S3_BLOCK block;
507 DBUG_ENTER("copy_to_file");
508 DBUG_PRINT("enter", ("path: %s start: %llu end: %llu",
509 aws_path, (ulonglong) start, (ulonglong) file_end));
510
511 for (pos= start, bnr=1 ; pos < file_end ; pos+= block.length, bnr++)
512 {
513 fix_suffix(path_end, bnr);
514 if (s3_get_object(s3_client, aws_bucket, aws_path, &block, compression, 1))
515 goto err;
516
517 error= my_write(file, block.str, block.length, MYF(MY_WME | MY_FNABP));
518 s3_free(&block);
519 if (error == MY_FILE_ERROR)
520 goto err;
521
522 /* Write up to DISPLAY_WITH number of '.' during copy */
523 if (display &&
524 ((pos + block.length) * DISPLAY_WITH /file_end) >
525 (pos * DISPLAY_WITH/file_end))
526 {
527 fputc('.', stdout); fflush(stdout);
528 print_done= 1;
529 }
530 }
531 if (print_done)
532 {
533 fputc('\n', stdout); fflush(stdout);
534 }
535 my_close(file, MYF(MY_WME));
536 DBUG_RETURN(0);
537
538 err:
539 my_close(file, MYF(MY_WME));
540 if (print_done)
541 {
542 fputc('\n', stdout); fflush(stdout);
543 }
544 DBUG_RETURN(1);
545 }
546
547
548 /**
549 Copy a table from S3 to current directory
550 */
551
aria_copy_from_s3(ms3_st * s3_client,const char * aws_bucket,const char * path,const char * database,my_bool compression,my_bool force,my_bool display)552 int aria_copy_from_s3(ms3_st *s3_client, const char *aws_bucket,
553 const char *path, const char *database,
554 my_bool compression, my_bool force, my_bool display)
555
556 {
557 MARIA_STATE_INFO state;
558 MY_STAT stat_info;
559 char table_name[FN_REFLEN], aws_path[FN_REFLEN+100];
560 char filename[FN_REFLEN];
561 char *aws_path_end, *end;
562 File file= -1;
563 S3_BLOCK block;
564 my_off_t index_file_size, data_file_size;
565 uint offset;
566 int error;
567 DBUG_ENTER("aria_copy_from_s3");
568
569 /* Check if index file exists */
570 fn_format(filename, path, "", ".MAI", MY_REPLACE_EXT);
571 if (!force && my_stat(filename, &stat_info, MYF(0)))
572 {
573 my_printf_error(EE_CANTCREATEFILE, "Table %s already exists on disk",
574 MYF(0), filename);
575 DBUG_RETURN(EE_CANTCREATEFILE);
576 }
577
578 fn_format(table_name, path, "", "", MY_REPLACE_DIR | MY_REPLACE_EXT);
579 block.str= 0;
580
581 aws_path_end= strxmov(aws_path, database, "/", table_name, NullS);
582 strmov(aws_path_end, "/aria");
583
584 if (s3_get_object(s3_client, aws_bucket, aws_path, &block, 0, 0))
585 {
586 my_printf_error(EE_FILENOTFOUND, "File %s/%s doesn't exist in s3", MYF(0),
587 database,filename);
588 goto err;
589 }
590 if (block.length < MARIA_STATE_INFO_SIZE)
591 {
592 fprintf(stderr, "Wrong block length for first block: %lu\n",
593 (ulong) block.length);
594 goto err_with_free;
595 }
596
597 if (display)
598 printf("Copying aria table: %s.%s from s3\n", database, table_name);
599
600 /* For offset positions, check _ma_state_info_readlength() */
601 offset= sizeof(state.header) + 4+ LSN_STORE_SIZE*3 + 8*5;
602 index_file_size= mi_sizekorr(block.str + offset);
603 data_file_size= mi_sizekorr(block.str + offset+8);
604
605 if ((file= my_create(filename, 0,
606 O_WRONLY | O_TRUNC | O_NOFOLLOW, MYF(MY_WME))) < 0)
607 goto err_with_free;
608
609 convert_index_to_disk_format(block.str);
610
611 if (my_write(file, block.str, block.length, MYF(MY_WME | MY_FNABP)))
612 goto err_with_free;
613
614 if (display)
615 printf("Copying index information %s\n", aws_path);
616
617 end= strmov(aws_path_end,"/index/000000");
618
619 error= copy_to_file(s3_client, aws_bucket, aws_path, file, block.length,
620 index_file_size, compression, display);
621 file= -1;
622 if (error)
623 goto err_with_free;
624
625 /* Copy data file */
626 fn_format(filename, path, "", ".MAD", MY_REPLACE_EXT);
627 if ((file= my_create(filename, 0,
628 O_WRONLY | O_TRUNC | O_NOFOLLOW, MYF(MY_WME))) < 0)
629 DBUG_RETURN(1);
630
631 end= strmov(aws_path_end, "/data");
632
633 if (display)
634 printf("Copying data information %s\n", aws_path);
635
636 /* The 000000 will be update with block number by fix_suffix() */
637 strmov(end, "/000000");
638
639 error= copy_to_file(s3_client, aws_bucket, aws_path, file, 0, data_file_size,
640 compression, display);
641 file= -1;
642 s3_free(&block);
643 block.str= 0;
644 if (error)
645 goto err;
646
647 /* Copy frm file if it exists */
648 strmov(aws_path_end, "/frm");
649 if (!s3_get_object(s3_client, aws_bucket, aws_path, &block, 0, 0))
650 {
651 fn_format(filename, path, "", ".frm", MY_REPLACE_EXT);
652 if ((file= my_create(filename, 0,
653 O_WRONLY | O_SHARE | O_NOFOLLOW | O_CLOEXEC,
654 MYF(0))) >= 0)
655 {
656 if (display)
657 printf("Copying frm file %s\n", filename);
658
659 convert_frm_to_disk_format(block.str);
660
661 if (my_write(file, block.str, block.length, MYF(MY_WME | MY_FNABP)))
662 goto err_with_free;
663 }
664 s3_free(&block);
665 my_close(file, MYF(MY_WME));
666 file= -1;
667 }
668
669 DBUG_RETURN(0);
670
671 err_with_free:
672 s3_free(&block);
673 err:
674 if (file >= 0)
675 my_close(file, MYF(0));
676 DBUG_RETURN(1);
677 }
678
679
680 /**
681 Drop all files related to a table from S3
682 */
683
aria_delete_from_s3(ms3_st * s3_client,const char * aws_bucket,const char * database,const char * table,my_bool display)684 int aria_delete_from_s3(ms3_st *s3_client, const char *aws_bucket,
685 const char *database, const char *table,
686 my_bool display)
687 {
688 ms3_status_st status;
689 char aws_path[FN_REFLEN+100];
690 char *aws_path_end;
691 int error;
692 DBUG_ENTER("aria_delete_from_s3");
693
694 aws_path_end= strxmov(aws_path, database, "/", table, NullS);
695 strmov(aws_path_end, "/aria");
696
697 /* Check if either /aria or /frm exists */
698
699 if (ms3_status(s3_client, aws_bucket, aws_path, &status))
700 {
701 strmov(aws_path_end, "/frm");
702 if (ms3_status(s3_client, aws_bucket, aws_path, &status))
703 {
704 my_printf_error(HA_ERR_NO_SUCH_TABLE,
705 "Table %s.%s doesn't exist in s3", MYF(0),
706 database, table);
707 my_errno= HA_ERR_NO_SUCH_TABLE;
708 DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
709 }
710 }
711
712 if (display)
713 printf("Delete of aria table: %s.%s\n", database, table);
714
715 strmov(aws_path_end,"/index");
716
717 if (display)
718 printf("Delete of index information %s\n", aws_path);
719
720 error= s3_delete_directory(s3_client, aws_bucket, aws_path);
721
722 strmov(aws_path_end,"/data");
723 if (display)
724 printf("Delete of data information %s\n", aws_path);
725
726 error|= s3_delete_directory(s3_client, aws_bucket, aws_path);
727
728 if (display)
729 printf("Delete of base information and frm\n");
730
731 strmov(aws_path_end,"/aria");
732 if (s3_delete_object(s3_client, aws_bucket, aws_path, MYF(MY_WME)))
733 error= 1;
734
735 /*
736 Delete .frm last as this is used by discovery to check if a s3 table
737 exists
738 */
739 strmov(aws_path_end,"/frm");
740 /* Ignore error if .frm file doesn't exist */
741 s3_delete_object(s3_client, aws_bucket, aws_path, MYF(ME_NOTE));
742
743 DBUG_RETURN(error);
744 }
745
746
747 /**
748 Rename a table in s3
749 */
750
aria_rename_s3(ms3_st * s3_client,const char * aws_bucket,const char * from_database,const char * from_table,const char * to_database,const char * to_table,my_bool rename_frm)751 int aria_rename_s3(ms3_st *s3_client, const char *aws_bucket,
752 const char *from_database, const char *from_table,
753 const char *to_database, const char *to_table,
754 my_bool rename_frm)
755 {
756 ms3_status_st status;
757 char to_aws_path[FN_REFLEN+100], from_aws_path[FN_REFLEN+100];
758 char *to_aws_path_end, *from_aws_path_end;
759 int error;
760 DBUG_ENTER("aria_rename_s3");
761
762 from_aws_path_end= strxmov(from_aws_path, from_database, "/", from_table,
763 NullS);
764 to_aws_path_end= strxmov(to_aws_path, to_database, "/", to_table, NullS);
765 strmov(from_aws_path_end, "/aria");
766
767 if (ms3_status(s3_client, aws_bucket, from_aws_path, &status))
768 {
769 my_printf_error(HA_ERR_NO_SUCH_TABLE,
770 "Table %s.%s doesn't exist in s3", MYF(0), from_database,
771 from_table);
772 my_errno= HA_ERR_NO_SUCH_TABLE;
773 DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
774 }
775
776 strmov(from_aws_path_end,"/index");
777 strmov(to_aws_path_end,"/index");
778
779 error= s3_rename_directory(s3_client, aws_bucket, from_aws_path, to_aws_path,
780 MYF(MY_WME));
781
782 strmov(from_aws_path_end,"/data");
783 strmov(to_aws_path_end,"/data");
784
785 error|= s3_rename_directory(s3_client, aws_bucket, from_aws_path,
786 to_aws_path, MYF(MY_WME));
787
788 if (rename_frm) {
789 strmov(from_aws_path_end, "/frm");
790 strmov(to_aws_path_end, "/frm");
791
792 s3_rename_object(s3_client, aws_bucket, from_aws_path, to_aws_path,
793 MYF(MY_WME));
794 }
795
796 strmov(from_aws_path_end,"/aria");
797 strmov(to_aws_path_end,"/aria");
798 if (s3_rename_object(s3_client, aws_bucket, from_aws_path, to_aws_path,
799 MYF(MY_WME)))
800 error= 1;
801 DBUG_RETURN(error);
802 }
803
804 /**
805 Copy all partition files related to a table from S3 (.frm and .par)
806
807 @param s3_client s3 client connection
808 @param aws_bucket bucket to use
809 @param path The path to the partitioned table files (no extension)
810 @param old_path In some cases the partioned files are not yet renamed.
811 This points to the temporary files that will later
812 be renamed to the partioned table
813 @param database Database for the partitioned table
814 @param database table name for the partitioned table
815 */
816
partition_copy_to_s3(ms3_st * s3_client,const char * aws_bucket,const char * path,const char * old_path,const char * database,const char * table_name)817 int partition_copy_to_s3(ms3_st *s3_client, const char *aws_bucket,
818 const char *path, const char *old_path,
819 const char *database, const char *table_name)
820 {
821 char aws_path[FN_REFLEN+100];
822 char filename[FN_REFLEN];
823 char *aws_path_end;
824 uchar *alloc_block= 0;
825 ms3_status_st status;
826 size_t frm_length;
827 int error;
828 DBUG_ENTER("partition_copy_to_s3");
829 DBUG_PRINT("enter",("from: %s database: %s table: %s",
830 path, database, table_name));
831
832 if (!old_path)
833 old_path= path;
834
835 aws_path_end= strxmov(aws_path, database, "/", table_name, "/", NullS);
836 strmov(aws_path_end, "frm");
837 fn_format(filename, old_path, "", ".frm", MY_REPLACE_EXT);
838
839 /* Just to be safe, delete any conflicting object */
840 if (!ms3_status(s3_client, aws_bucket, aws_path, &status))
841 {
842 if ((error= s3_delete_object(s3_client, aws_bucket, aws_path,
843 MYF(ME_FATAL))))
844 DBUG_RETURN(error);
845 }
846 if ((error= s3_read_file_from_disk(filename, &alloc_block, &frm_length, 0)))
847 {
848 /*
849 In case of ADD PARTITION PARTITON the .frm file is already renamed.
850 Copy the renamed file if it exists.
851 */
852 fn_format(filename, path, "", ".frm", MY_REPLACE_EXT);
853 if ((error= s3_read_file_from_disk(filename, &alloc_block, &frm_length,
854 1)))
855 goto err;
856 }
857 if ((error= s3_put_object(s3_client, aws_bucket, aws_path, alloc_block,
858 frm_length, 0)))
859 goto err;
860
861 /*
862 Note that because ha_partiton::rename_table() is called before
863 this function, the .par table already has it's final name!
864 */
865 fn_format(filename, path, "", ".par", MY_REPLACE_EXT);
866 strmov(aws_path_end, "par");
867 if (!ms3_status(s3_client, aws_bucket, aws_path, &status))
868 {
869 if ((error= s3_delete_object(s3_client, aws_bucket, aws_path,
870 MYF(ME_FATAL))))
871 goto err;
872 }
873
874 my_free(alloc_block);
875 alloc_block= 0;
876 if ((error=s3_read_file_from_disk(filename, &alloc_block, &frm_length, 1)))
877 goto err;
878 if ((error= s3_put_object(s3_client, aws_bucket, aws_path, alloc_block,
879 frm_length, 0)))
880 {
881 /* Delete the .frm file created above */
882 strmov(aws_path_end, "frm");
883 (void) s3_delete_object(s3_client, aws_bucket, aws_path,
884 MYF(ME_FATAL));
885 goto err;
886 }
887 error= 0;
888
889 err:
890 my_free(alloc_block);
891 DBUG_RETURN(error);
892 }
893
894
895 /**
896 Drop all partition files related to a table from S3
897 */
898
partition_delete_from_s3(ms3_st * s3_client,const char * aws_bucket,const char * database,const char * table,myf error_flags)899 int partition_delete_from_s3(ms3_st *s3_client, const char *aws_bucket,
900 const char *database, const char *table,
901 myf error_flags)
902 {
903 char aws_path[FN_REFLEN+100];
904 char *aws_path_end;
905 int error=0, res;
906 DBUG_ENTER("partition_delete_from_s3");
907
908 aws_path_end= strxmov(aws_path, database, "/", table, NullS);
909 strmov(aws_path_end, "/par");
910
911 if ((res= s3_delete_object(s3_client, aws_bucket, aws_path, error_flags)))
912 error= res;
913 /*
914 Delete .frm last as this is used by discovery to check if a s3 table
915 exists
916 */
917 strmov(aws_path_end, "/frm");
918 if ((res= s3_delete_object(s3_client, aws_bucket, aws_path, error_flags)))
919 error= res;
920
921 DBUG_RETURN(error);
922 }
923
924 /******************************************************************************
925 Low level functions interfacing with libmarias3
926 ******************************************************************************/
927
928 /**
929 Create an object for index or data information
930
931 Note that if compression is used, the data may be overwritten and
932 there must be COMPRESS_HEADER length of free space before the data!
933
934 */
935
s3_put_object(ms3_st * s3_client,const char * aws_bucket,const char * name,uchar * data,size_t length,my_bool compression)936 int s3_put_object(ms3_st *s3_client, const char *aws_bucket,
937 const char *name, uchar *data, size_t length,
938 my_bool compression)
939 {
940 uint8_t error;
941 const char *errmsg;
942 DBUG_ENTER("s3_put_object");
943 DBUG_PRINT("enter", ("name: %s", name));
944
945 if (compression)
946 {
947 size_t comp_len;
948
949 data[-COMPRESS_HEADER]= 0; // No compression
950 if (!my_compress(data, &length, &comp_len))
951 data[-COMPRESS_HEADER]= 1; // Compressed package
952 data-= COMPRESS_HEADER;
953 length+= COMPRESS_HEADER;
954 int3store(data+1, comp_len); // Original length or 0
955 }
956
957 if (likely(!(error= ms3_put(s3_client, aws_bucket, name, data, length))))
958 DBUG_RETURN(0);
959
960 if (!(errmsg= ms3_server_error(s3_client)))
961 errmsg= ms3_error(error);
962
963 my_printf_error(EE_WRITE, "Got error from put_object(%s): %d %s", MYF(0),
964 name, error, errmsg);
965 DBUG_RETURN(EE_WRITE);
966 }
967
968
969 /**
970 Read an object for index or data information
971
972 @param print_error 0 Don't print error
973 @param print_error 1 Print error that object doesn't exists
974 @param print_error 2 Print error that table doesn't exists
975 */
976
s3_get_object(ms3_st * s3_client,const char * aws_bucket,const char * name,S3_BLOCK * block,my_bool compression,int print_error)977 int s3_get_object(ms3_st *s3_client, const char *aws_bucket,
978 const char *name, S3_BLOCK *block,
979 my_bool compression, int print_error)
980 {
981 uint8_t error;
982 int result= 0;
983 uchar *data;
984 DBUG_ENTER("s3_get_object");
985 DBUG_PRINT("enter", ("name: %s compression: %d", name, compression));
986
987 block->str= block->alloc_ptr= 0;
988 if (likely(!(error= ms3_get(s3_client, aws_bucket, name,
989 (uint8_t**) &block->alloc_ptr,
990 &block->length))))
991 {
992 block->str= block->alloc_ptr;
993 if (compression)
994 {
995 ulong length;
996
997 /* If not compressed */
998 if (!block->str[0])
999 {
1000 block->length-= COMPRESS_HEADER;
1001 block->str+= COMPRESS_HEADER;
1002
1003 /* Simple check to ensure that it's a correct block */
1004 if (block->length % 1024)
1005 {
1006 s3_free(block);
1007 my_printf_error(HA_ERR_NOT_A_TABLE,
1008 "Block '%s' is not compressed", MYF(0), name);
1009 DBUG_RETURN(HA_ERR_NOT_A_TABLE);
1010 }
1011 DBUG_RETURN(0);
1012 }
1013
1014 if (((uchar*)block->str)[0] > 1)
1015 {
1016 s3_free(block);
1017 my_printf_error(HA_ERR_NOT_A_TABLE,
1018 "Block '%s' is not compressed", MYF(0), name);
1019 DBUG_RETURN(HA_ERR_NOT_A_TABLE);
1020 }
1021
1022 length= uint3korr(block->str+1);
1023
1024 if (!(data= (uchar*) my_malloc(PSI_NOT_INSTRUMENTED,
1025 length, MYF(MY_WME | MY_THREAD_SPECIFIC))))
1026 {
1027 s3_free(block);
1028 DBUG_RETURN(EE_OUTOFMEMORY);
1029 }
1030 if (uncompress(data, &length, block->str + COMPRESS_HEADER,
1031 block->length - COMPRESS_HEADER))
1032 {
1033 my_printf_error(ER_NET_UNCOMPRESS_ERROR,
1034 "Got error uncompressing s3 packet", MYF(0));
1035 s3_free(block);
1036 my_free(data);
1037 DBUG_RETURN(ER_NET_UNCOMPRESS_ERROR);
1038 }
1039 s3_free(block);
1040 block->str= block->alloc_ptr= data;
1041 block->length= length;
1042 }
1043 DBUG_RETURN(0);
1044 }
1045
1046 if (error == 9)
1047 {
1048 result= my_errno= (print_error == 1 ? EE_FILENOTFOUND :
1049 HA_ERR_NO_SUCH_TABLE);
1050 if (print_error)
1051 my_printf_error(my_errno, "Expected object '%s' didn't exist",
1052 MYF(0), name);
1053 }
1054 else
1055 {
1056 result= my_errno= EE_READ;
1057 if (print_error)
1058 {
1059 const char *errmsg;
1060 if (!(errmsg= ms3_server_error(s3_client)))
1061 errmsg= ms3_error(error);
1062
1063 my_printf_error(EE_READ, "Got error from get_object(%s): %d %s", MYF(0),
1064 name, error, errmsg);
1065 }
1066 }
1067 s3_free(block);
1068 DBUG_RETURN(result);
1069 }
1070
1071
s3_delete_object(ms3_st * s3_client,const char * aws_bucket,const char * name,myf error_flags)1072 int s3_delete_object(ms3_st *s3_client, const char *aws_bucket,
1073 const char *name, myf error_flags)
1074 {
1075 uint8_t error;
1076 int result= 0;
1077 DBUG_ENTER("s3_delete_object");
1078 DBUG_PRINT("enter", ("name: %s", name));
1079
1080 if (likely(!(error= ms3_delete(s3_client, aws_bucket, name))))
1081 DBUG_RETURN(0);
1082
1083 if (error_flags)
1084 {
1085 error_flags&= ~MY_WME;
1086 if (error == 9)
1087 my_printf_error(result= EE_FILENOTFOUND,
1088 "Expected object '%s' didn't exist",
1089 error_flags, name);
1090 else
1091 {
1092 const char *errmsg;
1093 if (!(errmsg= ms3_server_error(s3_client)))
1094 errmsg= ms3_error(error);
1095
1096 my_printf_error(result= EE_READ,
1097 "Got error from delete_object(%s): %d %s",
1098 error_flags, name, error, errmsg);
1099 }
1100 }
1101 DBUG_RETURN(result);
1102 }
1103
1104
1105 /*
1106 Drop all files in a 'directory' in s3
1107 */
1108
s3_delete_directory(ms3_st * s3_client,const char * aws_bucket,const char * path)1109 int s3_delete_directory(ms3_st *s3_client, const char *aws_bucket,
1110 const char *path)
1111 {
1112 ms3_list_st *list, *org_list= 0;
1113 my_bool error;
1114 DBUG_ENTER("delete_directory");
1115 DBUG_PRINT("enter", ("path: %s", path));
1116
1117 if ((error= ms3_list(s3_client, aws_bucket, path, &org_list)))
1118 {
1119 const char *errmsg;
1120 if (!(errmsg= ms3_server_error(s3_client)))
1121 errmsg= ms3_error(error);
1122
1123 my_printf_error(EE_FILENOTFOUND,
1124 "Can't get list of files from %s. Error: %d %s", MYF(0),
1125 path, error, errmsg);
1126 DBUG_RETURN(EE_FILENOTFOUND);
1127 }
1128
1129 for (list= org_list ; list ; list= list->next)
1130 if (s3_delete_object(s3_client, aws_bucket, list->key, MYF(MY_WME)))
1131 error= 1;
1132 if (org_list)
1133 ms3_list_free(org_list);
1134 DBUG_RETURN(error);
1135 }
1136
1137
s3_rename_object(ms3_st * s3_client,const char * aws_bucket,const char * from_name,const char * to_name,myf error_flags)1138 my_bool s3_rename_object(ms3_st *s3_client, const char *aws_bucket,
1139 const char *from_name, const char *to_name,
1140 myf error_flags)
1141 {
1142 uint8_t error;
1143 DBUG_ENTER("s3_rename_object");
1144 DBUG_PRINT("enter", ("from: %s to: %s", from_name, to_name));
1145
1146 if (likely(!(error= ms3_move(s3_client,
1147 aws_bucket, from_name,
1148 aws_bucket, to_name))))
1149 DBUG_RETURN(FALSE);
1150
1151 if (error_flags)
1152 {
1153 error_flags&= ~MY_WME;
1154 if (error == 9)
1155 {
1156 my_printf_error(EE_FILENOTFOUND, "Expected object '%s' didn't exist",
1157 error_flags, from_name);
1158 }
1159 else
1160 {
1161 const char *errmsg;
1162 if (!(errmsg= ms3_server_error(s3_client)))
1163 errmsg= ms3_error(error);
1164
1165 my_printf_error(EE_READ, "Got error from move_object(%s -> %s): %d %",
1166 error_flags,
1167 from_name, to_name, error, errmsg);
1168 }
1169 }
1170 DBUG_RETURN(TRUE);
1171 }
1172
1173
s3_rename_directory(ms3_st * s3_client,const char * aws_bucket,const char * from_name,const char * to_name,myf error_flags)1174 int s3_rename_directory(ms3_st *s3_client, const char *aws_bucket,
1175 const char *from_name, const char *to_name,
1176 myf error_flags)
1177 {
1178 ms3_list_st *list, *org_list= 0;
1179 my_bool error= 0;
1180 char name[AWS_PATH_LENGTH], *end;
1181 DBUG_ENTER("s3_delete_directory");
1182
1183 if ((error= ms3_list(s3_client, aws_bucket, from_name, &org_list)))
1184 {
1185 const char *errmsg;
1186 if (!(errmsg= ms3_server_error(s3_client)))
1187 errmsg= ms3_error(error);
1188
1189 my_printf_error(EE_FILENOTFOUND,
1190 "Can't get list of files from %s. Error: %d %s",
1191 MYF(error_flags & ~MY_WME),
1192 from_name, error, errmsg);
1193 DBUG_RETURN(EE_FILENOTFOUND);
1194 }
1195
1196 end= strmov(name, to_name);
1197 for (list= org_list ; list ; list= list->next)
1198 {
1199 const char *sep= strrchr(list->key, '/');
1200 if (sep) /* Safety */
1201 {
1202 strmake(end, sep, (sizeof(name) - (end-name) - 1));
1203 if (s3_rename_object(s3_client, aws_bucket, list->key, name,
1204 error_flags))
1205 error= 1;
1206 }
1207 }
1208 if (org_list)
1209 ms3_list_free(org_list);
1210 DBUG_RETURN(error);
1211 }
1212
1213
1214 /******************************************************************************
1215 Converting index and frm files to from S3 storage engine
1216 ******************************************************************************/
1217
1218 /**
1219 Change index information to be of type s3
1220
1221 @param header Copy of header in index file
1222 @param block_size S3 block size
1223 @param compression Compression algorithm to use
1224
1225 The position are from _ma_base_info_write()
1226 */
1227
convert_index_to_s3_format(uchar * header,ulong block_size,int compression)1228 static void convert_index_to_s3_format(uchar *header, ulong block_size,
1229 int compression)
1230 {
1231 MARIA_STATE_INFO state;
1232 uchar *base_pos;
1233 uint base_offset;
1234
1235 memcpy(state.header.file_version, header, sizeof(state.header));
1236 base_offset= mi_uint2korr(state.header.base_pos);
1237 base_pos= header + base_offset;
1238
1239 base_pos[107]= (uchar) compression;
1240 mi_int3store(base_pos+119, block_size);
1241 }
1242
1243
1244 /**
1245 Change index information to be a normal disk based table
1246 */
1247
convert_index_to_disk_format(uchar * header)1248 static void convert_index_to_disk_format(uchar *header)
1249 {
1250 MARIA_STATE_INFO state;
1251 uchar *base_pos;
1252 uint base_offset;
1253
1254 memcpy(state.header.file_version, header, sizeof(state.header));
1255 base_offset= mi_uint2korr(state.header.base_pos);
1256 base_pos= header + base_offset;
1257
1258 base_pos[107]= 0;
1259 mi_int3store(base_pos+119, 0);
1260 }
1261
1262 /**
1263 Change storage engine in the .frm file from Aria to s3
1264
1265 For information about engine types, see legacy_db_type
1266 */
1267
convert_frm_to_s3_format(uchar * header)1268 static void convert_frm_to_s3_format(uchar *header)
1269 {
1270 DBUG_ASSERT(header[3] == 42 || header[3] == 41); /* Aria or S3 */
1271 header[3]= 41; /* S3 */
1272 }
1273
1274 /**
1275 Change storage engine in the .frm file from S3 to Aria
1276
1277 For information about engine types, see legacy_db_type
1278 */
1279
convert_frm_to_disk_format(uchar * header)1280 static void convert_frm_to_disk_format(uchar *header)
1281 {
1282 DBUG_ASSERT(header[3] == 41); /* S3 */
1283 header[3]= 42; /* Aria */
1284 }
1285
1286
1287 /******************************************************************************
1288 Helper functions
1289 ******************************************************************************/
1290
1291 /**
1292 Set database and table name from path
1293
1294 s3->database and s3->table_name will be pointed into path
1295 Note that s3->database will not be null terminated!
1296 */
1297
set_database_and_table_from_path(S3_INFO * s3,const char * path)1298 my_bool set_database_and_table_from_path(S3_INFO *s3, const char *path)
1299 {
1300 size_t org_length= dirname_length(path);
1301 size_t length= 0;
1302
1303 if (!org_length)
1304 return 1;
1305
1306 s3->table.str= path+org_length;
1307 s3->table.length= strlen(s3->table.str);
1308 for (length= --org_length; length > 0 ; length --)
1309 {
1310 if (path[length-1] == FN_LIBCHAR || path[length-1] == '/')
1311 break;
1312 #ifdef FN_DEVCHAR
1313 if (path[length-1] == FN_DECVHAR)
1314 break;
1315 #endif
1316 }
1317 if (length &&
1318 (path[length] != FN_CURLIB || org_length - length != 1))
1319 {
1320 s3->database.str= path + length;
1321 s3->database.length= org_length - length;
1322 return 0;
1323 }
1324 return 1; /* Can't find database */
1325 }
1326
1327
1328 /**
1329 Read frm from the disk
1330 */
1331
s3_read_file_from_disk(const char * filename,uchar ** to,size_t * to_size,my_bool print_error)1332 static int s3_read_file_from_disk(const char *filename, uchar **to,
1333 size_t *to_size, my_bool print_error)
1334 {
1335 File file;
1336 uchar *alloc_block;
1337 size_t file_size;
1338 int error;
1339
1340 *to= 0;
1341 if ((file= my_open(filename,
1342 O_RDONLY | O_SHARE | O_NOFOLLOW | O_CLOEXEC,
1343 MYF(print_error ? MY_WME: 0))) < 0)
1344 return(my_errno);
1345
1346 file_size= (size_t) my_seek(file, 0L, MY_SEEK_END, MYF(0));
1347 if (!(alloc_block= my_malloc(PSI_NOT_INSTRUMENTED, file_size, MYF(MY_WME))))
1348 goto err;
1349
1350 if (my_pread(file, alloc_block, file_size, 0, MYF(MY_WME | MY_FNABP)))
1351 goto err;
1352
1353 *to= alloc_block;
1354 *to_size= file_size;
1355 my_close(file, MYF(0));
1356 return 0;
1357
1358 err:
1359 error= my_errno;
1360 my_free(alloc_block);
1361 my_close(file, MYF(0));
1362 return error;
1363 }
1364
1365
1366 /**
1367 Get .frm or par from S3
1368
1369 @return 0 ok
1370 @return 1 error
1371 */
1372
s3_get_def(ms3_st * s3_client,S3_INFO * s3_info,S3_BLOCK * block,const char * ext)1373 my_bool s3_get_def(ms3_st *s3_client, S3_INFO *s3_info, S3_BLOCK *block,
1374 const char *ext)
1375 {
1376 char aws_path[AWS_PATH_LENGTH];
1377
1378 strxnmov(aws_path, sizeof(aws_path)-1, s3_info->database.str, "/",
1379 s3_info->table.str, "/", ext, NullS);
1380
1381 return s3_get_object(s3_client, s3_info->bucket.str, aws_path, block,
1382 0, 0);
1383 }
1384
1385 /**
1386 Check if .frm exits in S3
1387
1388 @return 0 frm exists
1389 @return 1 error
1390 */
1391
s3_frm_exists(ms3_st * s3_client,S3_INFO * s3_info)1392 my_bool s3_frm_exists(ms3_st *s3_client, S3_INFO *s3_info)
1393 {
1394 char aws_path[AWS_PATH_LENGTH];
1395 ms3_status_st status;
1396
1397 strxnmov(aws_path, sizeof(aws_path)-1, s3_info->database.str, "/",
1398 s3_info->table.str, "/frm", NullS);
1399
1400 return ms3_status(s3_client, s3_info->bucket.str, aws_path, &status);
1401 }
1402
1403
1404 /**
1405 Get version from frm file
1406
1407 @param out Store the table_version_here. It's of size MY_UUID_SIZE
1408 @param frm_image Frm image
1409 @param frm_length size of image
1410
1411 @return 0 Was able to read table version
1412 @return 1 Wrong information in frm file
1413 */
1414
1415 #define FRM_HEADER_SIZE 64
1416 #define EXTRA2_TABLEDEF_VERSION 0
1417
is_binary_frm_header(const uchar * head)1418 static inline my_bool is_binary_frm_header(const uchar *head)
1419 {
1420 return head[0] == 254
1421 && head[1] == 1
1422 && head[2] >= FRM_VER
1423 && head[2] <= FRM_VER_CURRENT;
1424 }
1425
get_tabledef_version_from_frm(char * out,const uchar * frm_image,size_t frm_length)1426 static my_bool get_tabledef_version_from_frm(char *out, const uchar *frm_image,
1427 size_t frm_length)
1428 {
1429 uint segment_len;
1430 const uchar *extra, *extra_end;
1431 if (!is_binary_frm_header(frm_image) || frm_length <= FRM_HEADER_SIZE)
1432 return 1;
1433
1434 /* Length of the MariaDB extra2 segment in the form file. */
1435 segment_len= uint2korr(frm_image + 4);
1436 if (frm_length < FRM_HEADER_SIZE + segment_len)
1437 return 1;
1438
1439 extra= frm_image + FRM_HEADER_SIZE;
1440 if (*extra == '/') // old frm had '/' there
1441 return 1;
1442
1443 extra_end= extra + segment_len;
1444 while (extra + 4 < extra_end)
1445 {
1446 uchar type= *extra++;
1447 size_t length= *extra++;
1448 if (!length)
1449 {
1450 length= uint2korr(extra);
1451 extra+= 2;
1452 if (length < 256)
1453 return 1; /* Something is wrong */
1454 }
1455 if (extra + length > extra_end)
1456 return 1;
1457 if (type == EXTRA2_TABLEDEF_VERSION)
1458 {
1459 if (length != MY_UUID_SIZE)
1460 return 1;
1461 memcpy(out, extra, length);
1462 return 0; /* Found it */
1463 }
1464 extra+= length;
1465 }
1466 return 1;
1467 }
1468
1469
1470 /**
1471 Check if version in frm file matches what the server expects
1472
1473 @return 0 table definitions matches
1474 @return 1 table definitions doesn't match
1475 @return 2 Can't find the frm version
1476 @return 3 Can't read the frm version
1477 */
1478
s3_check_frm_version(ms3_st * s3_client,S3_INFO * s3_info)1479 int s3_check_frm_version(ms3_st *s3_client, S3_INFO *s3_info)
1480 {
1481 my_bool res= 0;
1482 char aws_path[AWS_PATH_LENGTH];
1483 char uuid[MY_UUID_SIZE];
1484 S3_BLOCK block;
1485 DBUG_ENTER("s3_check_frm_version");
1486
1487 strxnmov(aws_path, sizeof(aws_path)-1, s3_info->database.str, "/",
1488 s3_info->base_table.str, "/frm", NullS);
1489
1490 if (s3_get_object(s3_client, s3_info->bucket.str, aws_path, &block, 0, 0))
1491 {
1492 DBUG_PRINT("exit", ("No object found"));
1493 DBUG_RETURN(2); /* Ignore check, use old frm */
1494 }
1495
1496 if (get_tabledef_version_from_frm(uuid, (uchar*) block.str, block.length) ||
1497 s3_info->tabledef_version.length != MY_UUID_SIZE)
1498 {
1499 s3_free(&block);
1500 DBUG_PRINT("error", ("Wrong definition"));
1501 DBUG_RETURN(3); /* Wrong definition */
1502 }
1503 /* res is set to 1 if versions numbers doesn't match */
1504 res= bcmp(s3_info->tabledef_version.str, uuid, MY_UUID_SIZE) != 0;
1505 s3_free(&block);
1506 if (res)
1507 DBUG_PRINT("error", ("Wrong table version"));
1508 else
1509 DBUG_PRINT("exit", ("Version strings matches"));
1510 DBUG_RETURN(res);
1511 }
1512
1513
1514 /******************************************************************************
1515 Reading blocks from index or data from S3
1516 ******************************************************************************/
1517
1518 /*
1519 Read the index header (first page) from the index file
1520
1521 In case of error, my_error() is called
1522 */
1523
read_index_header(ms3_st * client,S3_INFO * s3,S3_BLOCK * block)1524 my_bool read_index_header(ms3_st *client, S3_INFO *s3, S3_BLOCK *block)
1525 {
1526 char aws_path[AWS_PATH_LENGTH];
1527 DBUG_ENTER("read_index_header");
1528 strxnmov(aws_path, sizeof(aws_path)-1, s3->database.str, "/", s3->table.str,
1529 "/aria", NullS);
1530 DBUG_RETURN(s3_get_object(client, s3->bucket.str, aws_path, block, 0, 2));
1531 }
1532
1533
1534 #ifdef FOR_FUTURE_IF_NEEDED_FOR_DEBUGGING_WITHOUT_S3
1535 /**
1536 Read a big block from disk
1537 */
1538
s3_block_read(struct st_pagecache * pagecache,PAGECACHE_IO_HOOK_ARGS * args,struct st_pagecache_file * file,LEX_STRING * data)1539 my_bool s3_block_read(struct st_pagecache *pagecache,
1540 PAGECACHE_IO_HOOK_ARGS *args,
1541 struct st_pagecache_file *file,
1542 LEX_STRING *data)
1543 {
1544 MARIA_SHARE *share= (MARIA_SHARE*) file->callback_data;
1545 my_bool datafile= file != &share->kfile;
1546
1547 DBUG_ASSERT(file->big_block_size > 0);
1548 DBUG_ASSERT(((((my_off_t) args->pageno - file->head_blocks) <<
1549 pagecache->shift) %
1550 file->big_block_size) == 0);
1551
1552 if (!(data->str= (char *) my_malloc(file->big_block_size, MYF(MY_WME))))
1553 return TRUE;
1554
1555 data->length= mysql_file_pread(file->file,
1556 (unsigned char *)data->str,
1557 file->big_block_size,
1558 ((my_off_t) args->pageno << pagecache->shift),
1559 MYF(MY_WME));
1560 if (data->length == 0 || data->length == MY_FILE_ERROR)
1561 {
1562 if (data->length == 0)
1563 {
1564 LEX_STRING *file_name= (datafile ?
1565 &share->data_file_name :
1566 &share->index_file_name);
1567 my_error(EE_EOFERR, MYF(0), file_name->str, my_errno);
1568 }
1569 my_free(data->str);
1570 data->length= 0;
1571 data->str= 0;
1572 return TRUE;
1573 }
1574 return FALSE;
1575 }
1576 #endif
1577
1578
1579 /**
1580 Read a block from S3 to page cache
1581 */
1582
s3_block_read(struct st_pagecache * pagecache,PAGECACHE_IO_HOOK_ARGS * args,struct st_pagecache_file * file,S3_BLOCK * block)1583 my_bool s3_block_read(struct st_pagecache *pagecache,
1584 PAGECACHE_IO_HOOK_ARGS *args,
1585 struct st_pagecache_file *file,
1586 S3_BLOCK *block)
1587 {
1588 char aws_path[AWS_PATH_LENGTH];
1589 MARIA_SHARE *share= (MARIA_SHARE*) file->callback_data;
1590 my_bool datafile= file->file != share->kfile.file;
1591 MARIA_HA *info= (MARIA_HA*) my_thread_var->keycache_file;
1592 ms3_st *client= info->s3;
1593 const char *path_suffix= datafile ? "/data/" : "/index/";
1594 char *end;
1595 S3_INFO *s3= share->s3_path;
1596 ulong block_number;
1597 DBUG_ENTER("s3_block_read");
1598
1599 DBUG_ASSERT(file->big_block_size > 0);
1600 DBUG_ASSERT(((((my_off_t) args->pageno - file->head_blocks) <<
1601 pagecache->shift) %
1602 file->big_block_size) == 0);
1603
1604 block_number= (((args->pageno - file->head_blocks) << pagecache->shift) /
1605 file->big_block_size) + 1;
1606
1607 end= strxnmov(aws_path, sizeof(aws_path)-12, s3->database.str, "/",
1608 s3->table.str, path_suffix, "000000", NullS);
1609 fix_suffix(end, block_number);
1610
1611 DBUG_RETURN(s3_get_object(client, s3->bucket.str, aws_path, block,
1612 share->base.compression_algorithm, 1));
1613 }
1614
1615 /*
1616 Start file numbers from 1000 to more easily find bugs when the file number
1617 could be mistaken for a real file
1618 */
1619 static volatile int32 unique_file_number= 1000;
1620
s3_unique_file_number()1621 int32 s3_unique_file_number()
1622 {
1623 return my_atomic_add32_explicit(&unique_file_number, 1,
1624 MY_MEMORY_ORDER_RELAXED);
1625 }
1626