1 /* Copyright (C) 2018, 2020 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 /* Code for doing backups of Aria tables */
17 
18 #include "maria_def.h"
19 #include "ma_blockrec.h"                        /* PAGE_SUFFIX_SIZE */
20 #include "ma_checkpoint.h"
21 #include <aria_backup.h>
22 
23 /**
24   @brief Get capabilities for an Aria table
25 
26   @param kfile   key file (.MAI)
27   @param cap     Capabilities are stored here
28 
29   @return 0      ok
30   @return X      errno
31 */
32 
33 int aria_get_capabilities(File kfile, ARIA_TABLE_CAPABILITIES *cap)__attribute__((visibility("default"))) ;
aria_get_capabilities(File kfile,ARIA_TABLE_CAPABILITIES * cap)34 int aria_get_capabilities(File kfile, ARIA_TABLE_CAPABILITIES *cap)
35 {
36   MARIA_SHARE share;
37   int error= 0;
38   uint head_length= sizeof(share.state.header), base_pos;
39   uint aligned_bit_blocks;
40   size_t info_length;
41   uchar *disc_cache;
42   DBUG_ENTER("aria_get_capabilities");
43 
44   bzero(cap, sizeof(*cap));
45   if (my_pread(kfile,share.state.header.file_version, head_length, 0,
46                MYF(MY_NABP)))
47     DBUG_RETURN(HA_ERR_NOT_A_TABLE);
48 
49   if (memcmp(share.state.header.file_version, maria_file_magic, 4))
50     DBUG_RETURN(HA_ERR_NOT_A_TABLE);
51 
52   share.options= mi_uint2korr(share.state.header.options);
53 
54   info_length= mi_uint2korr(share.state.header.header_length);
55   base_pos=    mi_uint2korr(share.state.header.base_pos);
56 
57   /*
58     Allocate space for header information and for data that is too
59     big to keep on stack
60   */
61   if (!(disc_cache= my_malloc(PSI_NOT_INSTRUMENTED, info_length, MYF(MY_WME))))
62     DBUG_RETURN(ENOMEM);
63 
64   if (my_pread(kfile, disc_cache, info_length, 0L, MYF(MY_NABP)))
65   {
66     error= my_errno;
67     goto err;
68   }
69   _ma_base_info_read(disc_cache + base_pos, &share.base);
70   cap->transactional= share.base.born_transactional;
71   cap->checksum= MY_TEST(share.options & HA_OPTION_PAGE_CHECKSUM);
72   cap->online_backup_safe= cap->transactional && cap->checksum;
73   cap->header_size= share.base.keystart;
74   cap->keypage_header= ((share.base.born_transactional ?
75                          LSN_STORE_SIZE + TRANSID_SIZE :
76                          0) + KEYPAGE_KEYID_SIZE + KEYPAGE_FLAG_SIZE +
77                         KEYPAGE_USED_SIZE);
78   cap->block_size= share.base.block_size;
79   cap->data_file_type= share.state.header.data_file_type;
80   cap->s3_block_size=  share.base.s3_block_size;
81   cap->compression=    share.base.compression_algorithm;
82   cap->encrypted=      MY_TEST(share.base.extra_options &
83                                MA_EXTRA_OPTIONS_ENCRYPTED);
84 
85   if (share.state.header.data_file_type == BLOCK_RECORD)
86   {
87     /* Calulate how man pages the row bitmap covers. From _ma_bitmap_init() */
88     aligned_bit_blocks= (cap->block_size - PAGE_SUFFIX_SIZE) / 6;
89     /*
90       In each 6 bytes, we have 6*8/3 = 16 pages covered
91       The +1 is to add the bitmap page, as this doesn't have to be covered
92     */
93     cap->bitmap_pages_covered= aligned_bit_blocks * 16 + 1;
94   }
95 
96   /* Do a check that that we got things right */
97   if (share.state.header.data_file_type != BLOCK_RECORD &&
98       cap->online_backup_safe)
99     error= HA_ERR_NOT_A_TABLE;
100 
101 err:
102   my_free(disc_cache);
103   DBUG_RETURN(error);
104 } /* aria_get_capabilities */
105 
106 /****************************************************************************
107 **  store MARIA_BASE_INFO
108 ****************************************************************************/
109 
_ma_base_info_read(uchar * ptr,MARIA_BASE_INFO * base)110 uchar *_ma_base_info_read(uchar *ptr, MARIA_BASE_INFO *base)
111 {
112   bmove(base->uuid, ptr, MY_UUID_SIZE);                 ptr+= MY_UUID_SIZE;
113   base->keystart= mi_sizekorr(ptr);			ptr+= 8;
114   base->max_data_file_length= mi_sizekorr(ptr); 	ptr+= 8;
115   base->max_key_file_length= mi_sizekorr(ptr);		ptr+= 8;
116   base->records=  (ha_rows) mi_sizekorr(ptr);		ptr+= 8;
117   base->reloc= (ha_rows) mi_sizekorr(ptr);		ptr+= 8;
118   base->mean_row_length= mi_uint4korr(ptr);		ptr+= 4;
119   base->reclength= mi_uint4korr(ptr);			ptr+= 4;
120   base->pack_reclength= mi_uint4korr(ptr);		ptr+= 4;
121   base->min_pack_length= mi_uint4korr(ptr);		ptr+= 4;
122   base->max_pack_length= mi_uint4korr(ptr);		ptr+= 4;
123   base->min_block_length= mi_uint4korr(ptr);		ptr+= 4;
124   base->fields= mi_uint2korr(ptr);			ptr+= 2;
125   base->fixed_not_null_fields= mi_uint2korr(ptr);       ptr+= 2;
126   base->fixed_not_null_fields_length= mi_uint2korr(ptr);ptr+= 2;
127   base->max_field_lengths= mi_uint2korr(ptr);	        ptr+= 2;
128   base->pack_fields= mi_uint2korr(ptr);			ptr+= 2;
129   base->extra_options= mi_uint2korr(ptr);		ptr+= 2;
130   base->null_bytes= mi_uint2korr(ptr);			ptr+= 2;
131   base->original_null_bytes= mi_uint2korr(ptr);		ptr+= 2;
132   base->field_offsets= mi_uint2korr(ptr);		ptr+= 2;
133   base->language= mi_uint2korr(ptr);		        ptr+= 2;
134   base->block_size= mi_uint2korr(ptr);			ptr+= 2;
135 
136   base->rec_reflength= *ptr++;
137   base->key_reflength= *ptr++;
138   base->keys=	       *ptr++;
139   base->auto_key=      *ptr++;
140   base->born_transactional= *ptr++;
141   base->compression_algorithm= *ptr++;
142   base->pack_bytes= mi_uint2korr(ptr);			ptr+= 2;
143   base->blobs= mi_uint2korr(ptr);			ptr+= 2;
144   base->max_key_block_length= mi_uint2korr(ptr);	ptr+= 2;
145   base->max_key_length= mi_uint2korr(ptr);		ptr+= 2;
146   base->extra_alloc_bytes= mi_uint2korr(ptr);		ptr+= 2;
147   base->extra_alloc_procent= *ptr++;
148   base->s3_block_size= mi_uint3korr(ptr);               ptr+= 3;
149   ptr+= 13;
150   return ptr;
151 }
152 
153 
154 /**
155    @brief Copy an index block with re-read if checksum doesn't match
156 
157    @param dfile       data file (.MAD)
158    @param cap         aria capabilities from aria_get_capabilities
159    @param block       block number to read (0, 1, 2, 3...)
160    @param buffer      read data to this buffer
161    @param bytes_read  number of bytes actually read (in case of end of file)
162 
163    @return 0 ok
164    @return HA_ERR_END_OF_FILE  ; End of file
165    @return # error number
166 */
167 
168 #define MAX_RETRY 10
169 
aria_read_index(File kfile,ARIA_TABLE_CAPABILITIES * cap,ulonglong block,uchar * buffer)170 int aria_read_index(File kfile, ARIA_TABLE_CAPABILITIES *cap, ulonglong block,
171                     uchar *buffer)
172 {
173   MARIA_SHARE share;
174   int retry= 0;
175   DBUG_ENTER("aria_read_index");
176 
177   share.keypage_header= cap->keypage_header;
178   share.block_size= cap->block_size;
179   do
180   {
181     int error;
182     size_t length;
183     if ((length= my_pread(kfile, buffer, cap->block_size,
184                           block * cap->block_size, MYF(0))) != cap->block_size)
185     {
186       if (length == 0)
187         DBUG_RETURN(HA_ERR_END_OF_FILE);
188       if (length == (size_t) -1)
189         DBUG_RETURN(my_errno ? my_errno : -1);
190       /* Assume we got a half read; Do a re-read */
191     }
192     /* If not transactional or key file header, there are no checksums */
193     if (!cap->online_backup_safe ||
194         block < cap->header_size/ cap->block_size)
195       DBUG_RETURN(length == cap->block_size ? 0 : HA_ERR_CRASHED);
196 
197     if (length == cap->block_size)
198     {
199       length= _ma_get_page_used(&share, buffer);
200       if (length > cap->block_size - CRC_SIZE)
201         DBUG_RETURN(HA_ERR_CRASHED);
202       error= maria_page_crc_check(buffer, block, &share,
203                                   MARIA_NO_CRC_NORMAL_PAGE,
204                                   (int) length);
205       if (error != HA_ERR_WRONG_CRC)
206         DBUG_RETURN(error);
207     }
208     my_sleep(100000);                              /* Sleep 0.1 seconds */
209   } while (retry < MAX_RETRY);
210   DBUG_RETURN(HA_ERR_WRONG_CRC);
211 }
212 
213 
214 /**
215    @brief Copy a data block with re-read if checksum doesn't match
216 
217    @param dfile       data file (.MAD)
218    @param cap         aria capabilities from aria_get_capabilities
219    @param block       block number to read (0, 1, 2, 3...)
220    @param buffer      read data to this buffer
221    @param bytes_read  number of bytes actually read (in case of end of file)
222 
223    @return 0 ok
224    @return HA_ERR_END_OF_FILE  ; End of file
225    @return # error number
226 */
227 
aria_read_data(File dfile,ARIA_TABLE_CAPABILITIES * cap,ulonglong block,uchar * buffer,size_t * bytes_read)228 int aria_read_data(File dfile, ARIA_TABLE_CAPABILITIES *cap, ulonglong block,
229                    uchar *buffer, size_t *bytes_read)
230 {
231   MARIA_SHARE share;
232   int retry= 0;
233   DBUG_ENTER("aria_read_data");
234 
235   share.keypage_header= cap->keypage_header;
236   share.block_size= cap->block_size;
237 
238   if (!cap->online_backup_safe)
239   {
240     *bytes_read= my_pread(dfile, buffer, cap->block_size,
241                           block * cap->block_size, MY_WME);
242     if (*bytes_read == 0)
243       DBUG_RETURN(HA_ERR_END_OF_FILE);
244     DBUG_RETURN(*bytes_read > 0 ? 0 : (my_errno ? my_errno : -1));
245   }
246 
247   *bytes_read= cap->block_size;
248   do
249   {
250     int error;
251     size_t length;
252     if ((length= my_pread(dfile, buffer, cap->block_size,
253                           block * cap->block_size, MYF(0))) != cap->block_size)
254     {
255       if (length == 0)
256         DBUG_RETURN(HA_ERR_END_OF_FILE);
257       if (length == (size_t) -1)
258         DBUG_RETURN(my_errno ? my_errno : -1);
259     }
260 
261     /* If not transactional or key file header, there are no checksums */
262     if (!cap->online_backup_safe)
263       DBUG_RETURN(length == cap->block_size ? 0 : HA_ERR_CRASHED);
264 
265     if (length == cap->block_size)
266     {
267       error= maria_page_crc_check(buffer, block, &share,
268                                   ((block % cap->bitmap_pages_covered) == 0 ?
269                                    MARIA_NO_CRC_BITMAP_PAGE :
270                                    MARIA_NO_CRC_NORMAL_PAGE),
271                                   share.block_size - CRC_SIZE);
272       if (error != HA_ERR_WRONG_CRC)
273         DBUG_RETURN(error);
274     }
275     my_sleep(100000);                              /* Sleep 0.1 seconds */
276   } while (retry < MAX_RETRY);
277     DBUG_RETURN(HA_ERR_WRONG_CRC);
278 }
279