1 /* Copyright (c) 2000, 2018, 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 /*
24   Functions to handle space-packed-records and blobs
25 
26   A row may be stored in one or more linked blocks.
27   The block size is between MI_MIN_BLOCK_LENGTH and MI_MAX_BLOCK_LENGTH.
28   Each block is aligned on MI_DYN_ALIGN_SIZE.
29   The reson for the max block size is to not have too many different types
30   of blocks.  For the differnet block types, look at _mi_get_block_info()
31 */
32 
33 #include "myisamdef.h"
34 
35 /* Enough for comparing if number is zero */
36 static char zero_string[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
37 
38 static int write_dynamic_record(MI_INFO *info,const uchar *record,
39 				ulong reclength);
40 static int _mi_find_writepos(MI_INFO *info,ulong reclength,my_off_t *filepos,
41 			     ulong *length);
42 static int update_dynamic_record(MI_INFO *info,my_off_t filepos,uchar *record,
43 				 ulong reclength);
44 static int delete_dynamic_record(MI_INFO *info,my_off_t filepos,
45 				 uint second_read);
46 static int _mi_cmp_buffer(File file, const uchar *buff, my_off_t filepos,
47 			  uint length);
48 
49 	/* Interface function from MI_INFO */
50 
51 /*
52   Create mmaped area for MyISAM handler
53 
54   SYNOPSIS
55     mi_dynmap_file()
56     info		MyISAM handler
57 
58   RETURN
59     0  ok
60     1  error.
61 */
62 
mi_dynmap_file(MI_INFO * info,my_off_t size)63 my_bool mi_dynmap_file(MI_INFO *info, my_off_t size)
64 {
65   DBUG_ENTER("mi_dynmap_file");
66   if (size == 0 || size > (my_off_t) (~((size_t) 0)))
67   {
68     if (size)
69       DBUG_PRINT("warning", ("File is too large for mmap"));
70     else
71       DBUG_PRINT("warning", ("Do not mmap zero-length"));
72     DBUG_RETURN(1);
73   }
74   /*
75       MAP_NORESERVE is unimplemented in FreeBSD
76   */
77   info->s->file_map= (uchar*)
78                   my_mmap(0, (size_t) size,
79                           info->s->mode==O_RDONLY ? PROT_READ :
80                           PROT_READ | PROT_WRITE,
81                           MAP_SHARED,
82                           info->dfile, 0L);
83   if (info->s->file_map == (uchar*) MAP_FAILED)
84   {
85     info->s->file_map= NULL;
86     DBUG_RETURN(1);
87   }
88 #if defined(HAVE_MADVISE)
89   madvise((char*) info->s->file_map, size, MADV_RANDOM);
90 #endif
91   info->s->mmaped_length= size;
92   info->s->file_read= mi_mmap_pread;
93   info->s->file_write= mi_mmap_pwrite;
94   DBUG_RETURN(0);
95 }
96 
97 
98 /*
99   Destroy mmaped area for MyISAM handler
100 
101   SYNOPSIS
102     mi_munmap_file()
103     info                  MyISAM handler
104 
105   RETURN
106     0  ok
107    !0  error.
108 */
109 
mi_munmap_file(MI_INFO * info)110 int mi_munmap_file(MI_INFO *info)
111 {
112   int ret;
113   DBUG_ENTER("mi_unmap_file");
114   if ((ret= my_munmap((void*) info->s->file_map,
115                       (size_t)info->s->mmaped_length)))
116     DBUG_RETURN(ret);
117   info->s->file_read= mi_nommap_pread;
118   info->s->file_write= mi_nommap_pwrite;
119   info->s->file_map= 0;
120   info->s->mmaped_length= 0;
121   DBUG_RETURN(0);
122 }
123 
124 
125 /*
126   Resize mmaped area for MyISAM handler
127 
128   SYNOPSIS
129     mi_remap_file()
130     info		MyISAM handler
131 
132   RETURN
133 */
134 
mi_remap_file(MI_INFO * info,my_off_t size)135 void mi_remap_file(MI_INFO *info, my_off_t size)
136 {
137   if (info->s->file_map)
138   {
139     mi_munmap_file(info);
140     mi_dynmap_file(info, size);
141   }
142 }
143 
144 
145 /*
146   Read bytes from MySAM handler, using mmap or pread
147 
148   SYNOPSIS
149     mi_mmap_pread()
150     info		MyISAM handler
151     Buffer              Input buffer
152     Count               Count of bytes for read
153     offset              Start position
154     MyFlags
155 
156   RETURN
157     0  ok
158 */
159 
mi_mmap_pread(MI_INFO * info,uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)160 size_t mi_mmap_pread(MI_INFO *info, uchar *Buffer,
161                     size_t Count, my_off_t offset, myf MyFlags)
162 {
163   DBUG_PRINT("info", ("mi_read with mmap %d\n", info->dfile));
164   if (info->s->concurrent_insert)
165     mysql_rwlock_rdlock(&info->s->mmap_lock);
166 
167   /*
168     The following test may fail in the following cases:
169     - We failed to remap a memory area (fragmented memory?)
170     - This thread has done some writes, but not yet extended the
171     memory mapped area.
172   */
173 
174   if (info->s->mmaped_length >= offset + Count)
175   {
176     memcpy(Buffer, info->s->file_map + offset, Count);
177     if (info->s->concurrent_insert)
178       mysql_rwlock_unlock(&info->s->mmap_lock);
179     return 0;
180   }
181   else
182   {
183     if (info->s->concurrent_insert)
184       mysql_rwlock_unlock(&info->s->mmap_lock);
185     return mysql_file_pread(info->dfile, Buffer, Count, offset, MyFlags);
186   }
187 }
188 
189 
190         /* wrapper for mysql_file_pread in case if mmap isn't used */
191 
mi_nommap_pread(MI_INFO * info,uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)192 size_t mi_nommap_pread(MI_INFO *info, uchar *Buffer,
193                        size_t Count, my_off_t offset, myf MyFlags)
194 {
195   return mysql_file_pread(info->dfile, Buffer, Count, offset, MyFlags);
196 }
197 
198 
199 /*
200   Write bytes to MySAM handler, using mmap or pwrite
201 
202   SYNOPSIS
203     mi_mmap_pwrite()
204     info		MyISAM handler
205     Buffer              Output buffer
206     Count               Count of bytes for write
207     offset              Start position
208     MyFlags
209 
210   RETURN
211     0  ok
212     !=0  error.  In this case return error from pwrite
213 */
214 
mi_mmap_pwrite(MI_INFO * info,const uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)215 size_t mi_mmap_pwrite(MI_INFO *info, const uchar *Buffer,
216                       size_t Count, my_off_t offset, myf MyFlags)
217 {
218   DBUG_PRINT("info", ("mi_write with mmap %d\n", info->dfile));
219   if (info->s->concurrent_insert)
220     mysql_rwlock_rdlock(&info->s->mmap_lock);
221 
222   /*
223     The following test may fail in the following cases:
224     - We failed to remap a memory area (fragmented memory?)
225     - This thread has done some writes, but not yet extended the
226     memory mapped area.
227   */
228 
229   if (info->s->mmaped_length >= offset + Count)
230   {
231     memcpy(info->s->file_map + offset, Buffer, Count);
232     if (info->s->concurrent_insert)
233       mysql_rwlock_unlock(&info->s->mmap_lock);
234     return 0;
235   }
236   else
237   {
238     info->s->nonmmaped_inserts++;
239     if (info->s->concurrent_insert)
240       mysql_rwlock_unlock(&info->s->mmap_lock);
241     return mysql_file_pwrite(info->dfile, Buffer, Count, offset, MyFlags);
242   }
243 
244 }
245 
246 
247         /* wrapper for mysql_file_pwrite in case if mmap isn't used */
248 
mi_nommap_pwrite(MI_INFO * info,const uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)249 size_t mi_nommap_pwrite(MI_INFO *info, const uchar *Buffer,
250                       size_t Count, my_off_t offset, myf MyFlags)
251 {
252   return mysql_file_pwrite(info->dfile, Buffer, Count, offset, MyFlags);
253 }
254 
255 
_mi_write_dynamic_record(MI_INFO * info,const uchar * record)256 int _mi_write_dynamic_record(MI_INFO *info, const uchar *record)
257 {
258   ulong reclength=_mi_rec_pack(info,info->rec_buff,record);
259   return (write_dynamic_record(info,info->rec_buff,reclength));
260 }
261 
_mi_update_dynamic_record(MI_INFO * info,my_off_t pos,const uchar * record)262 int _mi_update_dynamic_record(MI_INFO *info, my_off_t pos, const uchar *record)
263 {
264   uint length=_mi_rec_pack(info,info->rec_buff,record);
265   return (update_dynamic_record(info,pos,info->rec_buff,length));
266 }
267 
_mi_write_blob_record(MI_INFO * info,const uchar * record)268 int _mi_write_blob_record(MI_INFO *info, const uchar *record)
269 {
270   uchar *rec_buff;
271   int error;
272   ulong reclength,reclength2,extra;
273 
274   extra= (ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
275 	  MI_DYN_DELETE_BLOCK_HEADER+1);
276   reclength= (info->s->base.pack_reclength +
277 	      _my_calc_total_blob_length(info,record)+ extra);
278   if (!(rec_buff=(uchar*) my_malloc(mi_key_memory_record_buffer,
279                                     reclength, MYF(0))))
280   {
281     set_my_errno(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
282     return(-1);
283   }
284   reclength2= _mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
285 			   record);
286   DBUG_PRINT("info",("reclength: %lu  reclength2: %lu",
287 		     reclength, reclength2));
288   DBUG_ASSERT(reclength2 <= reclength);
289   error=write_dynamic_record(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
290 			     reclength2);
291   my_free(rec_buff);
292   return(error);
293 }
294 
295 
_mi_update_blob_record(MI_INFO * info,my_off_t pos,const uchar * record)296 int _mi_update_blob_record(MI_INFO *info, my_off_t pos, const uchar *record)
297 {
298   uchar *rec_buff;
299   int error;
300   ulong reclength,extra;
301 
302   extra= (ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
303 	  MI_DYN_DELETE_BLOCK_HEADER);
304   reclength= (info->s->base.pack_reclength+
305 	      _my_calc_total_blob_length(info,record)+ extra);
306   if (!(rec_buff=(uchar*) my_malloc(mi_key_memory_record_buffer,
307                                     reclength, MYF(0))))
308   {
309     set_my_errno(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
310     return(-1);
311   }
312   reclength=_mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
313 			 record);
314   error=update_dynamic_record(info,pos,
315 			      rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
316 			      reclength);
317   my_free(rec_buff);
318   return(error);
319 }
320 
321 
_mi_delete_dynamic_record(MI_INFO * info)322 int _mi_delete_dynamic_record(MI_INFO *info)
323 {
324   return delete_dynamic_record(info,info->lastpos,0);
325 }
326 
327 
328 	/* Write record to data-file */
329 
write_dynamic_record(MI_INFO * info,const uchar * record,ulong reclength)330 static int write_dynamic_record(MI_INFO *info, const uchar *record,
331 				ulong reclength)
332 {
333   int flag;
334   ulong length;
335   my_off_t filepos;
336   DBUG_ENTER("write_dynamic_record");
337 
338   flag=0;
339 
340   /*
341     Check if we have enough room for the new record.
342     First we do simplified check to make usual case faster.
343     Then we do more precise check for the space left.
344     Though it still is not absolutely precise, as
345     we always use MI_MAX_DYN_BLOCK_HEADER while it can be
346     less in the most of the cases.
347   */
348 
349   if (unlikely(info->s->base.max_data_file_length -
350                info->state->data_file_length <
351                reclength + MI_MAX_DYN_BLOCK_HEADER))
352   {
353     if (info->s->base.max_data_file_length - info->state->data_file_length +
354         info->state->empty - info->state->del * MI_MAX_DYN_BLOCK_HEADER <
355         reclength + MI_MAX_DYN_BLOCK_HEADER)
356     {
357       set_my_errno(HA_ERR_RECORD_FILE_FULL);
358       DBUG_RETURN(1);
359     }
360   }
361 
362   do
363   {
364     if (_mi_find_writepos(info,reclength,&filepos,&length))
365       goto err;
366     if (_mi_write_part_record(info,filepos,length,
367                               (info->append_insert_at_end ?
368                                HA_OFFSET_ERROR : info->s->state.dellink),
369 			      (uchar**) &record,&reclength,&flag))
370       goto err;
371   } while (reclength);
372 
373   DBUG_RETURN(0);
374 err:
375   DBUG_RETURN(1);
376 }
377 
378 
379 	/* Get a block for data ; The given data-area must be used !! */
380 
_mi_find_writepos(MI_INFO * info,ulong reclength,my_off_t * filepos,ulong * length)381 static int _mi_find_writepos(MI_INFO *info,
382 			     ulong reclength, /* record length */
383 			     my_off_t *filepos, /* Return file pos */
384 			     ulong *length)   /* length of block at filepos */
385 {
386   MI_BLOCK_INFO block_info;
387   ulong tmp;
388   DBUG_ENTER("_mi_find_writepos");
389 
390   if (info->s->state.dellink != HA_OFFSET_ERROR &&
391       !info->append_insert_at_end)
392   {
393     /* Deleted blocks exists;  Get last used block */
394     *filepos=info->s->state.dellink;
395     block_info.second_read=0;
396     info->rec_cache.seek_not_done=1;
397     if (!(_mi_get_block_info(&block_info,info->dfile,info->s->state.dellink) &
398 	   BLOCK_DELETED))
399     {
400       DBUG_PRINT("error",("Delete link crashed"));
401       set_my_errno(HA_ERR_WRONG_IN_RECORD);
402       DBUG_RETURN(-1);
403     }
404     info->s->state.dellink=block_info.next_filepos;
405     info->state->del--;
406     info->state->empty-= block_info.block_len;
407     *length= block_info.block_len;
408   }
409   else
410   {
411     /* No deleted blocks;  Allocate a new block */
412     *filepos=info->state->data_file_length;
413     if ((tmp=reclength+3 + MY_TEST(reclength >= (65520-3))) <
414 	info->s->base.min_block_length)
415       tmp= info->s->base.min_block_length;
416     else
417       tmp= ((tmp+MI_DYN_ALIGN_SIZE-1) &
418 	    (~ (ulong) (MI_DYN_ALIGN_SIZE-1)));
419     if (info->state->data_file_length >
420 	(info->s->base.max_data_file_length - tmp))
421     {
422       set_my_errno(HA_ERR_RECORD_FILE_FULL);
423       DBUG_RETURN(-1);
424     }
425     if (tmp > MI_MAX_BLOCK_LENGTH)
426       tmp=MI_MAX_BLOCK_LENGTH;
427     *length= tmp;
428     info->state->data_file_length+= tmp;
429     info->s->state.split++;
430     info->update|=HA_STATE_WRITE_AT_END;
431   }
432   DBUG_RETURN(0);
433 } /* _mi_find_writepos */
434 
435 
436 
437 /*
438   Unlink a deleted block from the deleted list.
439   This block will be combined with the preceding or next block to form
440   a big block.
441 */
442 
unlink_deleted_block(MI_INFO * info,MI_BLOCK_INFO * block_info)443 static my_bool unlink_deleted_block(MI_INFO *info, MI_BLOCK_INFO *block_info)
444 {
445   DBUG_ENTER("unlink_deleted_block");
446   if (block_info->filepos == info->s->state.dellink)
447   {
448     /* First deleted block;  We can just use this ! */
449     info->s->state.dellink=block_info->next_filepos;
450   }
451   else
452   {
453     MI_BLOCK_INFO tmp;
454     tmp.second_read=0;
455     /* Unlink block from the previous block */
456     if (!(_mi_get_block_info(&tmp,info->dfile,block_info->prev_filepos)
457 	  & BLOCK_DELETED))
458       DBUG_RETURN(1);				/* Something is wrong */
459     mi_sizestore(tmp.header+4,block_info->next_filepos);
460     if (info->s->file_write(info, tmp.header+4,8,
461 		  block_info->prev_filepos+4, MYF(MY_NABP)))
462       DBUG_RETURN(1);
463     /* Unlink block from next block */
464     if (block_info->next_filepos != HA_OFFSET_ERROR)
465     {
466       if (!(_mi_get_block_info(&tmp,info->dfile,block_info->next_filepos)
467 	    & BLOCK_DELETED))
468 	DBUG_RETURN(1);				/* Something is wrong */
469       mi_sizestore(tmp.header+12,block_info->prev_filepos);
470       if (info->s->file_write(info, tmp.header+12,8,
471 		    block_info->next_filepos+12,
472 		    MYF(MY_NABP)))
473 	DBUG_RETURN(1);
474     }
475   }
476   /* We now have one less deleted block */
477   info->state->del--;
478   info->state->empty-= block_info->block_len;
479   info->s->state.split--;
480 
481   /*
482     If this was a block that we where accessing through table scan
483     (mi_rrnd() or mi_scan(), then ensure that we skip over this block
484     when doing next mi_rrnd() or mi_scan().
485   */
486   if (info->nextpos == block_info->filepos)
487     info->nextpos+=block_info->block_len;
488   DBUG_RETURN(0);
489 }
490 
491 
492 /*
493   Add a backward link to delete block
494 
495   SYNOPSIS
496     update_backward_delete_link()
497     info		MyISAM handler
498     delete_block	Position to delete block to update.
499 			If this is 'HA_OFFSET_ERROR', nothing will be done
500     filepos		Position to block that 'delete_block' should point to
501 
502   RETURN
503     0  ok
504     1  error.  In this case my_error is set.
505 */
506 
update_backward_delete_link(MI_INFO * info,my_off_t delete_block,my_off_t filepos)507 static int update_backward_delete_link(MI_INFO *info, my_off_t delete_block,
508 				       my_off_t filepos)
509 {
510   MI_BLOCK_INFO block_info;
511   DBUG_ENTER("update_backward_delete_link");
512 
513   if (delete_block != HA_OFFSET_ERROR)
514   {
515     block_info.second_read=0;
516     if (_mi_get_block_info(&block_info,info->dfile,delete_block)
517 	& BLOCK_DELETED)
518     {
519       uchar buff[8];
520       mi_sizestore(buff,filepos);
521       if (info->s->file_write(info,buff, 8, delete_block+12, MYF(MY_NABP)))
522 	DBUG_RETURN(1);				/* Error on write */
523     }
524     else
525     {
526       set_my_errno(HA_ERR_WRONG_IN_RECORD);
527       DBUG_RETURN(1);				/* Wrong delete link */
528     }
529   }
530   DBUG_RETURN(0);
531 }
532 
533 	/* Delete datarecord from database */
534 	/* info->rec_cache.seek_not_done is updated in cmp_record */
535 
delete_dynamic_record(MI_INFO * info,my_off_t filepos,uint second_read)536 static int delete_dynamic_record(MI_INFO *info, my_off_t filepos,
537 				 uint second_read)
538 {
539   uint length,b_type;
540   MI_BLOCK_INFO block_info,del_block;
541   int error;
542   my_bool remove_next_block;
543   DBUG_ENTER("delete_dynamic_record");
544 
545   /* First add a link from the last block to the new one */
546   error= update_backward_delete_link(info, info->s->state.dellink, filepos);
547 
548   block_info.second_read=second_read;
549   do
550   {
551     /* Remove block at 'filepos' */
552     if ((b_type=_mi_get_block_info(&block_info,info->dfile,filepos))
553 	& (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
554 	   BLOCK_FATAL_ERROR) ||
555 	(length=(uint) (block_info.filepos-filepos) +block_info.block_len) <
556 	MI_MIN_BLOCK_LENGTH)
557     {
558       set_my_errno(HA_ERR_WRONG_IN_RECORD);
559       DBUG_RETURN(1);
560     }
561     /* Check if next block is a delete block */
562     del_block.second_read=0;
563     remove_next_block=0;
564     if (_mi_get_block_info(&del_block,info->dfile,filepos+length) &
565 	BLOCK_DELETED && del_block.block_len+length < MI_DYN_MAX_BLOCK_LENGTH)
566     {
567       /* We can't remove this yet as this block may be the head block */
568       remove_next_block=1;
569       length+=del_block.block_len;
570     }
571 
572     block_info.header[0]=0;
573     mi_int3store(block_info.header+1,length);
574     mi_sizestore(block_info.header+4,info->s->state.dellink);
575     if (b_type & BLOCK_LAST)
576       memset(block_info.header + 12, 255, 8);
577     else
578       mi_sizestore(block_info.header+12,block_info.next_filepos);
579     if (info->s->file_write(info,(uchar*) block_info.header,20,filepos,
580 		  MYF(MY_NABP)))
581       DBUG_RETURN(1);
582     info->s->state.dellink = filepos;
583     info->state->del++;
584     info->state->empty+=length;
585     filepos=block_info.next_filepos;
586 
587     /* Now it's safe to unlink the deleted block directly after this one */
588     if (remove_next_block && unlink_deleted_block(info,&del_block))
589       error=1;
590   } while (!(b_type & BLOCK_LAST));
591 
592   DBUG_RETURN(error);
593 }
594 
595 
596 	/* Write a block to datafile */
597 
_mi_write_part_record(MI_INFO * info,my_off_t filepos,ulong length,my_off_t next_filepos,uchar ** record,ulong * reclength,int * flag)598 int _mi_write_part_record(MI_INFO *info,
599 			  my_off_t filepos,	/* points at empty block */
600 			  ulong length,		/* length of block */
601 			  my_off_t next_filepos,/* Next empty block */
602 			  uchar **record,	/* pointer to record ptr */
603 			  ulong *reclength,	/* length of *record */
604 			  int *flag)		/* *flag == 0 if header */
605 {
606   ulong head_length,res_length,extra_length,long_block,del_length;
607   uchar *pos,*record_end;
608   my_off_t  next_delete_block;
609   uchar temp[MI_SPLIT_LENGTH+MI_DYN_DELETE_BLOCK_HEADER];
610   DBUG_ENTER("_mi_write_part_record");
611 
612   next_delete_block=HA_OFFSET_ERROR;
613 
614   res_length=extra_length=0;
615   if (length > *reclength + MI_SPLIT_LENGTH)
616   {						/* Splitt big block */
617     res_length=MY_ALIGN(length- *reclength - MI_EXTEND_BLOCK_LENGTH,
618 			MI_DYN_ALIGN_SIZE);
619     length-= res_length;			/* Use this for first part */
620   }
621   long_block= (length < 65520L && *reclength < 65520L) ? 0 : 1;
622   if (length == *reclength+ 3 + long_block)
623   {
624     /* Block is exactly of the right length */
625     temp[0]=(uchar) (1+ *flag)+(uchar) long_block;	/* Flag is 0 or 6 */
626     if (long_block)
627     {
628       mi_int3store(temp+1,*reclength);
629       head_length=4;
630     }
631     else
632     {
633       mi_int2store(temp+1,*reclength);
634       head_length=3;
635     }
636   }
637   else if (length-long_block < *reclength+4)
638   {						/* To short block */
639     if (next_filepos == HA_OFFSET_ERROR)
640       next_filepos= (info->s->state.dellink != HA_OFFSET_ERROR &&
641                      !info->append_insert_at_end ?
642                      info->s->state.dellink : info->state->data_file_length);
643     if (*flag == 0)				/* First block */
644     {
645       if (*reclength > MI_MAX_BLOCK_LENGTH)
646       {
647 	head_length= 16;
648 	temp[0]=13;
649 	mi_int4store(temp+1,*reclength);
650 	mi_int3store(temp+5,length-head_length);
651 	mi_sizestore((uchar*) temp+8,next_filepos);
652       }
653       else
654       {
655 	head_length=5+8+long_block*2;
656 	temp[0]=5+(uchar) long_block;
657 	if (long_block)
658 	{
659 	  mi_int3store(temp+1,*reclength);
660 	  mi_int3store(temp+4,length-head_length);
661 	  mi_sizestore((uchar*) temp+7,next_filepos);
662 	}
663 	else
664 	{
665 	  mi_int2store(temp+1,*reclength);
666 	  mi_int2store(temp+3,length-head_length);
667 	  mi_sizestore((uchar*) temp+5,next_filepos);
668 	}
669       }
670     }
671     else
672     {
673       head_length=3+8+long_block;
674       temp[0]=11+(uchar) long_block;
675       if (long_block)
676       {
677 	mi_int3store(temp+1,length-head_length);
678 	mi_sizestore((uchar*) temp+4,next_filepos);
679       }
680       else
681       {
682 	mi_int2store(temp+1,length-head_length);
683 	mi_sizestore((uchar*) temp+3,next_filepos);
684       }
685     }
686   }
687   else
688   {					/* Block with empty info last */
689     head_length=4+long_block;
690     extra_length= length- *reclength-head_length;
691     temp[0]= (uchar) (3+ *flag)+(uchar) long_block; /* 3,4 or 9,10 */
692     if (long_block)
693     {
694       mi_int3store(temp+1,*reclength);
695       temp[4]= (uchar) (extra_length);
696     }
697     else
698     {
699       mi_int2store(temp+1,*reclength);
700       temp[3]= (uchar) (extra_length);
701     }
702     length=	  *reclength+head_length;	/* Write only what is needed */
703   }
704   DBUG_DUMP("header",(uchar*) temp,head_length);
705 
706 	/* Make a long block for one write */
707   record_end= *record+length-head_length;
708   del_length=(res_length ? MI_DYN_DELETE_BLOCK_HEADER : 0);
709   memmove((uchar*) (*record - head_length),(uchar*) temp, head_length);
710   memcpy(temp,record_end,(size_t) (extra_length+del_length));
711   memset(record_end, 0, extra_length);
712 
713   if (res_length)
714   {
715     /* Check first if we can join this block with the next one */
716     MI_BLOCK_INFO del_block;
717     my_off_t next_block=filepos+length+extra_length+res_length;
718 
719     del_block.second_read=0;
720     if (next_block < info->state->data_file_length &&
721 	info->s->state.dellink != HA_OFFSET_ERROR)
722     {
723       if ((_mi_get_block_info(&del_block,info->dfile,next_block)
724 	   & BLOCK_DELETED) &&
725 	  res_length + del_block.block_len < MI_DYN_MAX_BLOCK_LENGTH)
726       {
727 	if (unlink_deleted_block(info,&del_block))
728 	  goto err;
729 	res_length+=del_block.block_len;
730       }
731     }
732 
733     /* Create a delete link of the last part of the block */
734     pos=record_end+extra_length;
735     pos[0]= '\0';
736     mi_int3store(pos+1,res_length);
737     mi_sizestore(pos+4,info->s->state.dellink);
738     memset(pos + 12, 255, 8);			/* End link */
739     next_delete_block=info->s->state.dellink;
740     info->s->state.dellink= filepos+length+extra_length;
741     info->state->del++;
742     info->state->empty+=res_length;
743     info->s->state.split++;
744   }
745   if (info->opt_flag & WRITE_CACHE_USED &&
746       info->update & HA_STATE_WRITE_AT_END)
747   {
748     if (info->update & HA_STATE_EXTEND_BLOCK)
749     {
750       info->update&= ~HA_STATE_EXTEND_BLOCK;
751       if (my_block_write(&info->rec_cache,(uchar*) *record-head_length,
752 			 length+extra_length+del_length,filepos))
753       goto err;
754     }
755     else if (my_b_write(&info->rec_cache,(uchar*) *record-head_length,
756 			length+extra_length+del_length))
757       goto err;
758   }
759   else
760   {
761     info->rec_cache.seek_not_done=1;
762     if (info->s->file_write(info,(uchar*) *record-head_length,length+extra_length+
763 		  del_length,filepos,info->s->write_flag))
764       goto err;
765   }
766   memcpy(record_end,temp,(size_t) (extra_length+del_length));
767   *record=record_end;
768   *reclength-=(length-head_length);
769   *flag=6;
770 
771   if (del_length)
772   {
773     /* link the next delete block to this */
774     if (update_backward_delete_link(info, next_delete_block,
775 				    info->s->state.dellink))
776       goto err;
777   }
778 
779   DBUG_RETURN(0);
780 err:
781   DBUG_PRINT("exit",("errno: %d",my_errno()));
782   DBUG_RETURN(1);
783 } /*_mi_write_part_record */
784 
785 
786 	/* update record from datafile */
787 
update_dynamic_record(MI_INFO * info,my_off_t filepos,uchar * record,ulong reclength)788 static int update_dynamic_record(MI_INFO *info, my_off_t filepos, uchar *record,
789 				 ulong reclength)
790 {
791   int flag;
792   uint error;
793   ulong length;
794   MI_BLOCK_INFO block_info;
795   DBUG_ENTER("update_dynamic_record");
796 
797   flag=block_info.second_read=0;
798   block_info.next_filepos= HA_OFFSET_ERROR;
799   /*
800      Check if we have enough room for the record.
801      First we do simplified check to make usual case faster.
802      Then we do more precise check for the space left.
803      Though it still is not absolutely precise, as
804      we always use MI_MAX_DYN_BLOCK_HEADER while it can be
805      less in the most of the cases.
806   */
807 
808   /*
809     compare with just the reclength as we're going
810     to get some space from the old replaced record
811   */
812   if (unlikely(info->s->base.max_data_file_length -
813         info->state->data_file_length < reclength))
814   {
815     /*
816        let's read the old record's block to find out the length of the
817        old record
818     */
819     if ((error=_mi_get_block_info(&block_info,info->dfile,filepos))
820         & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR | BLOCK_FATAL_ERROR))
821     {
822       DBUG_PRINT("error",("Got wrong block info"));
823       if (!(error & BLOCK_FATAL_ERROR))
824         set_my_errno(HA_ERR_WRONG_IN_RECORD);
825       goto err;
826     }
827 
828     /*
829       if new record isn't longer, we can go on safely
830     */
831     if (block_info.rec_len < reclength)
832     {
833       if (info->s->base.max_data_file_length - info->state->data_file_length +
834           info->state->empty - info->state->del * MI_MAX_DYN_BLOCK_HEADER <
835           reclength - block_info.rec_len + MI_MAX_DYN_BLOCK_HEADER)
836       {
837         set_my_errno(HA_ERR_RECORD_FILE_FULL);
838         goto err;
839       }
840     }
841     block_info.second_read=0;
842   }
843 
844   while (reclength > 0)
845   {
846     if (filepos != info->s->state.dellink)
847     {
848       block_info.next_filepos= HA_OFFSET_ERROR;
849       if ((error=_mi_get_block_info(&block_info,info->dfile,filepos))
850 	  & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
851 	     BLOCK_FATAL_ERROR))
852       {
853 	DBUG_PRINT("error",("Got wrong block info"));
854 	if (!(error & BLOCK_FATAL_ERROR))
855 	  set_my_errno(HA_ERR_WRONG_IN_RECORD);
856 	goto err;
857       }
858       length=(ulong) (block_info.filepos-filepos) + block_info.block_len;
859       if (length < reclength)
860       {
861 	uint tmp=MY_ALIGN(reclength - length + 3 +
862 			  MY_TEST(reclength >= 65520L),MI_DYN_ALIGN_SIZE);
863 	/* Don't create a block bigger than MI_MAX_BLOCK_LENGTH */
864 	tmp= MY_MIN(length+tmp, MI_MAX_BLOCK_LENGTH)-length;
865 	/* Check if we can extend this block */
866 	if (block_info.filepos + block_info.block_len ==
867 	    info->state->data_file_length &&
868 	    info->state->data_file_length <
869 	    info->s->base.max_data_file_length-tmp)
870 	{
871 	  /* extend file */
872 	  DBUG_PRINT("info",("Extending file with %d bytes",tmp));
873 	  if (info->nextpos == info->state->data_file_length)
874 	    info->nextpos+= tmp;
875 	  info->state->data_file_length+= tmp;
876 	  info->update|= HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK;
877 	  length+=tmp;
878 	}
879 	else if (length < MI_MAX_BLOCK_LENGTH - MI_MIN_BLOCK_LENGTH)
880 	{
881 	  /*
882 	    Check if next block is a deleted block
883 	    Above we have MI_MIN_BLOCK_LENGTH to avoid the problem where
884 	    the next block is so small it can't be splited which could
885 	    casue problems
886 	  */
887 
888 	  MI_BLOCK_INFO del_block;
889 	  del_block.second_read=0;
890 	  if (_mi_get_block_info(&del_block,info->dfile,
891 				 block_info.filepos + block_info.block_len) &
892 	      BLOCK_DELETED)
893 	  {
894 	    /* Use; Unlink it and extend the current block */
895 	    DBUG_PRINT("info",("Extending current block"));
896 	    if (unlink_deleted_block(info,&del_block))
897 	      goto err;
898 	    if ((length+=del_block.block_len) > MI_MAX_BLOCK_LENGTH)
899 	    {
900 	      /*
901 		New block was too big, link overflow part back to
902 		delete list
903 	      */
904 	      my_off_t next_pos;
905 	      ulong rest_length= length-MI_MAX_BLOCK_LENGTH;
906 	      set_if_bigger(rest_length, MI_MIN_BLOCK_LENGTH);
907 	      next_pos= del_block.filepos+ del_block.block_len - rest_length;
908 
909 	      if (update_backward_delete_link(info, info->s->state.dellink,
910 					      next_pos))
911 		DBUG_RETURN(1);
912 
913 	      /* create delete link for data that didn't fit into the page */
914 	      del_block.header[0]=0;
915 	      mi_int3store(del_block.header+1, rest_length);
916 	      mi_sizestore(del_block.header+4,info->s->state.dellink);
917 	      memset(del_block.header + 12, 255, 8);
918 	      if (info->s->file_write(info,(uchar*) del_block.header,20, next_pos,
919 			    MYF(MY_NABP)))
920 		DBUG_RETURN(1);
921 	      info->s->state.dellink= next_pos;
922 	      info->s->state.split++;
923 	      info->state->del++;
924 	      info->state->empty+= rest_length;
925 	      length-= rest_length;
926 	    }
927 	  }
928 	}
929       }
930     }
931     else
932     {
933       if (_mi_find_writepos(info,reclength,&filepos,&length))
934 	goto err;
935     }
936     if (_mi_write_part_record(info,filepos,length,block_info.next_filepos,
937 			      &record,&reclength,&flag))
938       goto err;
939     if ((filepos=block_info.next_filepos) == HA_OFFSET_ERROR)
940     {
941       /* Start writing data on deleted blocks */
942       filepos=info->s->state.dellink;
943     }
944   }
945 
946   if (block_info.next_filepos != HA_OFFSET_ERROR)
947   {
948     /*
949       delete_dynamic_record() may change data file position.
950       IO cache must be notified as it may still have cached
951       data, which has to be flushed later.
952     */
953     info->rec_cache.seek_not_done= 1;
954     if (delete_dynamic_record(info,block_info.next_filepos,1))
955       goto err;
956   }
957   DBUG_RETURN(0);
958 err:
959   DBUG_RETURN(1);
960 }
961 
962 
963 	/* Pack a record. Return new reclength */
964 
_mi_rec_pack(MI_INFO * info,uchar * to,const uchar * from)965 uint _mi_rec_pack(MI_INFO *info, uchar *to,
966                   const uchar *from)
967 {
968   uint		length,new_length,flag,bit,i;
969   uchar		*pos,*end,*startpos,*packpos;
970   enum en_fieldtype type;
971   MI_COLUMNDEF *rec;
972   MI_BLOB	*blob;
973   DBUG_ENTER("_mi_rec_pack");
974 
975   flag=0 ; bit=1;
976   startpos=packpos=to; to+= info->s->base.pack_bits; blob=info->blobs;
977   rec=info->s->rec;
978 
979   for (i=info->s->base.fields ; i-- > 0; from+= length,rec++)
980   {
981     length=(uint) rec->length;
982     if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL)
983     {
984       if (type == FIELD_BLOB)
985       {
986 	if (!blob->length)
987 	  flag|=bit;
988 	else
989 	{
990 	  char *temp_pos;
991 	  size_t tmp_length=length-portable_sizeof_char_ptr;
992 	  memcpy((uchar*) to,from,tmp_length);
993 	  memcpy(&temp_pos,from+tmp_length,sizeof(char*));
994 	  memcpy(to+tmp_length,temp_pos,(size_t) blob->length);
995 	  to+=tmp_length+blob->length;
996 	}
997 	blob++;
998       }
999       else if (type == FIELD_SKIP_ZERO)
1000       {
1001 	if (memcmp((uchar*) from,zero_string,length) == 0)
1002 	  flag|=bit;
1003 	else
1004 	{
1005 	  memcpy((uchar*) to,from,(size_t) length); to+=length;
1006 	}
1007       }
1008       else if (type == FIELD_SKIP_ENDSPACE ||
1009 	       type == FIELD_SKIP_PRESPACE)
1010       {
1011 	pos= (uchar*) from; end= (uchar*) from + length;
1012 	if (type == FIELD_SKIP_ENDSPACE)
1013 	{					/* Pack trailing spaces */
1014 	  while (end > from && *(end-1) == ' ')
1015 	    end--;
1016 	}
1017 	else
1018 	{					/* Pack pref-spaces */
1019 	  while (pos < end && *pos == ' ')
1020 	    pos++;
1021 	}
1022 	new_length=(uint) (end-pos);
1023 	if (new_length +1 + MY_TEST(rec->length > 255 && new_length > 127)
1024 	    < length)
1025 	{
1026 	  if (rec->length > 255 && new_length > 127)
1027 	  {
1028             to[0]= (uchar) ((new_length & 127) + 128);
1029             to[1]= (uchar) (new_length >> 7);
1030             to+=2;
1031           }
1032           else
1033             *to++= (uchar) new_length;
1034 	  memcpy((uchar*) to,pos,(size_t) new_length); to+=new_length;
1035 	  flag|=bit;
1036 	}
1037 	else
1038 	{
1039 	  memcpy(to,from,(size_t) length); to+=length;
1040 	}
1041       }
1042       else if (type == FIELD_VARCHAR)
1043       {
1044         uint pack_length= HA_VARCHAR_PACKLENGTH(rec->length -1);
1045 	uint tmp_length;
1046         if (pack_length == 1)
1047         {
1048           tmp_length= (uint) *(uchar*) from;
1049           *to++= *from;
1050         }
1051         else
1052         {
1053           tmp_length= uint2korr(from);
1054           store_key_length_inc(to,tmp_length);
1055         }
1056         memcpy(to, from+pack_length,tmp_length);
1057         to+= tmp_length;
1058         continue;
1059       }
1060       else
1061       {
1062 	memcpy(to,from,(size_t) length); to+=length;
1063 	continue;				/* Normal field */
1064       }
1065       if ((bit= bit << 1) >= 256)
1066       {
1067         *packpos++= (uchar) flag;
1068 	bit=1; flag=0;
1069       }
1070     }
1071     else
1072     {
1073       memcpy(to,from,(size_t) length); to+=length;
1074     }
1075   }
1076   if (bit != 1)
1077     *packpos= (uchar) flag;
1078   if (info->s->calc_checksum)
1079     *to++= (uchar) info->checksum;
1080   DBUG_PRINT("exit",("packed length: %d",(int) (to-startpos)));
1081   DBUG_RETURN((uint) (to-startpos));
1082 } /* _mi_rec_pack */
1083 
1084 
1085 
1086 /*
1087   Check if a record was correctly packed. Used only by myisamchk
1088   Returns 0 if record is ok.
1089 */
1090 
_mi_rec_check(MI_INFO * info,const uchar * record,uchar * rec_buff,ulong packed_length,my_bool with_checksum)1091 my_bool _mi_rec_check(MI_INFO *info,const uchar *record, uchar *rec_buff,
1092                       ulong packed_length, my_bool with_checksum)
1093 {
1094   uint		length,new_length,flag,bit,i;
1095   uchar		*pos,*end,*packpos,*to;
1096   enum en_fieldtype type;
1097   MI_COLUMNDEF *rec;
1098   DBUG_ENTER("_mi_rec_check");
1099 
1100   packpos=rec_buff; to= rec_buff+info->s->base.pack_bits;
1101   rec=info->s->rec;
1102   flag= *packpos; bit=1;
1103 
1104   for (i=info->s->base.fields ; i-- > 0; record+= length, rec++)
1105   {
1106     length=(uint) rec->length;
1107     if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL)
1108     {
1109       if (type == FIELD_BLOB)
1110       {
1111 	uint blob_length=
1112 	  _mi_calc_blob_length(length-portable_sizeof_char_ptr,record);
1113 	if (!blob_length && !(flag & bit))
1114 	  goto err;
1115 	if (blob_length)
1116 	  to+=length - portable_sizeof_char_ptr+ blob_length;
1117       }
1118       else if (type == FIELD_SKIP_ZERO)
1119       {
1120 	if (memcmp((uchar*) record,zero_string,length) == 0)
1121 	{
1122 	  if (!(flag & bit))
1123 	    goto err;
1124 	}
1125 	else
1126 	  to+=length;
1127       }
1128       else if (type == FIELD_SKIP_ENDSPACE ||
1129 	       type == FIELD_SKIP_PRESPACE)
1130       {
1131 	pos= (uchar*) record; end= (uchar*) record + length;
1132 	if (type == FIELD_SKIP_ENDSPACE)
1133 	{					/* Pack trailing spaces */
1134 	  while (end > record && *(end-1) == ' ')
1135 	    end--;
1136 	}
1137 	else
1138 	{					/* Pack pre-spaces */
1139 	  while (pos < end && *pos == ' ')
1140 	    pos++;
1141 	}
1142 	new_length=(uint) (end-pos);
1143 	if (new_length +1 + MY_TEST(rec->length > 255 && new_length > 127)
1144 	    < length)
1145 	{
1146 	  if (!(flag & bit))
1147 	    goto err;
1148 	  if (rec->length > 255 && new_length > 127)
1149 	  {
1150             /* purecov: begin inspected */
1151             if (to[0] != (uchar) ((new_length & 127) + 128) ||
1152                 to[1] != (uchar) (new_length >> 7))
1153               goto err;
1154             to+=2;
1155             /* purecov: end */
1156           }
1157           else if (*to++ != (uchar) new_length)
1158 	    goto err;
1159 	  to+=new_length;
1160 	}
1161 	else
1162 	  to+=length;
1163       }
1164       else if (type == FIELD_VARCHAR)
1165       {
1166         uint pack_length= HA_VARCHAR_PACKLENGTH(rec->length -1);
1167 	uint tmp_length;
1168         if (pack_length == 1)
1169         {
1170           tmp_length= (uint) *(uchar*) record;
1171           to+= 1+ tmp_length;
1172           continue;
1173         }
1174         else
1175         {
1176           tmp_length= uint2korr(record);
1177           to+= get_pack_length(tmp_length)+tmp_length;
1178         }
1179         continue;
1180       }
1181       else
1182       {
1183 	to+=length;
1184 	continue;				/* Normal field */
1185       }
1186       if ((bit= bit << 1) >= 256)
1187       {
1188 	flag= *++packpos;
1189 	bit=1;
1190       }
1191     }
1192     else
1193       to+= length;
1194   }
1195   if (packed_length != (uint) (to - rec_buff) + MY_TEST(info->s->calc_checksum) ||
1196       (bit != 1 && (flag & ~(bit - 1))))
1197     goto err;
1198   if (with_checksum && ((uchar) info->checksum != (uchar) *to))
1199   {
1200     DBUG_PRINT("error",("wrong checksum for row"));
1201     goto err;
1202   }
1203   DBUG_RETURN(0);
1204 
1205 err:
1206   DBUG_RETURN(1);
1207 }
1208 
1209 
1210 
1211 	/* Unpacks a record */
1212 	/* Returns -1 and my_errno =HA_ERR_RECORD_DELETED if reclength isn't */
1213 	/* right. Returns reclength (>0) if ok */
1214 
_mi_rec_unpack(MI_INFO * info,uchar * to,uchar * from,ulong found_length)1215 ulong _mi_rec_unpack(MI_INFO *info, uchar *to, uchar *from,
1216 		     ulong found_length)
1217 {
1218   uint flag,bit,length,rec_length,min_pack_length;
1219   enum en_fieldtype type;
1220   uchar *from_end,*to_end,*packpos;
1221   MI_COLUMNDEF *rec,*end_field;
1222   DBUG_ENTER("_mi_rec_unpack");
1223 
1224   to_end=to + info->s->base.reclength;
1225   from_end=from+found_length;
1226   flag= (uchar) *from; bit=1; packpos=from;
1227   if (found_length < info->s->base.min_pack_length)
1228     goto err;
1229   from+= info->s->base.pack_bits;
1230   min_pack_length=info->s->base.min_pack_length - info->s->base.pack_bits;
1231 
1232   for (rec=info->s->rec , end_field=rec+info->s->base.fields ;
1233        rec < end_field ; to+= rec_length, rec++)
1234   {
1235     rec_length=rec->length;
1236     if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL &&
1237 	(type != FIELD_CHECK))
1238     {
1239       if (type == FIELD_VARCHAR)
1240       {
1241         uint pack_length= HA_VARCHAR_PACKLENGTH(rec_length-1);
1242         if (pack_length == 1)
1243         {
1244           length= (uint) *(uchar*) from;
1245           if (length > rec_length-1)
1246             goto err;
1247           *to= *from++;
1248         }
1249         else
1250         {
1251           get_key_length(length, from);
1252           if (length > rec_length-2)
1253             goto err;
1254           int2store(to,length);
1255         }
1256         if (from+length > from_end)
1257           goto err;
1258         memcpy(to+pack_length, from, length);
1259         from+= length;
1260         min_pack_length--;
1261         continue;
1262       }
1263       if (flag & bit)
1264       {
1265 	if (type == FIELD_BLOB || type == FIELD_SKIP_ZERO)
1266 	  memset(to, 0, rec_length);
1267 	else if (type == FIELD_SKIP_ENDSPACE ||
1268 		 type == FIELD_SKIP_PRESPACE)
1269 	{
1270 	  if (rec->length > 255 && *from & 128)
1271 	  {
1272 	    if (from + 1 >= from_end)
1273 	      goto err;
1274 	    length= (*from & 127)+ ((uint) (uchar) *(from+1) << 7); from+=2;
1275 	  }
1276 	  else
1277 	  {
1278 	    if (from == from_end)
1279 	      goto err;
1280 	    length= (uchar) *from++;
1281 	  }
1282 	  min_pack_length--;
1283 	  if (length >= rec_length ||
1284 	      min_pack_length + length > (uint) (from_end - from))
1285 	    goto err;
1286 	  if (type == FIELD_SKIP_ENDSPACE)
1287 	  {
1288 	    memcpy(to,(uchar*) from,(size_t) length);
1289 	    memset(to + length, ' ', rec_length-length);
1290 	  }
1291 	  else
1292 	  {
1293 	    memset(to, ' ', rec_length-length);
1294 	    memcpy(to+rec_length-length,(uchar*) from,(size_t) length);
1295 	  }
1296 	  from+=length;
1297 	}
1298       }
1299       else if (type == FIELD_BLOB)
1300       {
1301 	uint size_length=rec_length- portable_sizeof_char_ptr;
1302 	ulong blob_length=_mi_calc_blob_length(size_length,from);
1303         ulong from_left= (ulong) (from_end - from);
1304         if (from_left < size_length ||
1305             from_left - size_length < blob_length ||
1306             from_left - size_length - blob_length < min_pack_length)
1307           goto err;
1308 	memcpy(to, from, (size_t) size_length);
1309 	from+=size_length;
1310 	memcpy(to+size_length, &from, sizeof(char*));
1311 	from+=blob_length;
1312       }
1313       else
1314       {
1315 	if (type == FIELD_SKIP_ENDSPACE || type == FIELD_SKIP_PRESPACE)
1316 	  min_pack_length--;
1317 	if (min_pack_length + rec_length > (uint) (from_end - from))
1318 	  goto err;
1319 	memcpy(to,(uchar*) from,(size_t) rec_length); from+=rec_length;
1320       }
1321       if ((bit= bit << 1) >= 256)
1322       {
1323 	flag= (uchar) *++packpos; bit=1;
1324       }
1325     }
1326     else
1327     {
1328       if (min_pack_length > (uint) (from_end - from))
1329 	goto err;
1330       min_pack_length-=rec_length;
1331       memcpy(to, (uchar*) from, (size_t) rec_length);
1332       from+=rec_length;
1333     }
1334   }
1335   if (info->s->calc_checksum)
1336     from++;
1337   if (to == to_end && from == from_end && (bit == 1 || !(flag & ~(bit-1))))
1338     DBUG_RETURN(found_length);
1339 
1340 err:
1341   set_my_errno(HA_ERR_WRONG_IN_RECORD);
1342   DBUG_PRINT("error",("to_end: 0x%lx -> 0x%lx  from_end: 0x%lx -> 0x%lx",
1343 		      (long) to, (long) to_end, (long) from, (long) from_end));
1344   DBUG_DUMP("from",(uchar*) info->rec_buff,info->s->base.min_pack_length);
1345   DBUG_RETURN(MY_FILE_ERROR);
1346 } /* _mi_rec_unpack */
1347 
1348 
1349 	/* Calc length of blob. Update info in blobs->length */
1350 
_my_calc_total_blob_length(MI_INFO * info,const uchar * record)1351 ulong _my_calc_total_blob_length(MI_INFO *info, const uchar *record)
1352 {
1353   ulong length;
1354   MI_BLOB *blob,*end;
1355 
1356   for (length=0, blob= info->blobs, end=blob+info->s->base.blobs ;
1357        blob != end;
1358        blob++)
1359   {
1360     blob->length=_mi_calc_blob_length(blob->pack_length,record + blob->offset);
1361     length+=blob->length;
1362   }
1363   return length;
1364 }
1365 
1366 
_mi_calc_blob_length(uint length,const uchar * pos)1367 ulong _mi_calc_blob_length(uint length, const uchar *pos)
1368 {
1369   switch (length) {
1370   case 1:
1371     return (uint) (uchar) *pos;
1372   case 2:
1373     return (uint) uint2korr(pos);
1374   case 3:
1375     return uint3korr(pos);
1376   case 4:
1377     return uint4korr(pos);
1378   default:
1379     break;
1380   }
1381   return 0; /* Impossible */
1382 }
1383 
1384 
_mi_store_blob_length(uchar * pos,uint pack_length,uint length)1385 void _mi_store_blob_length(uchar *pos,uint pack_length,uint length)
1386 {
1387   switch (pack_length) {
1388   case 1:
1389     *pos= (uchar) length;
1390     break;
1391   case 2:
1392     int2store(pos,length);
1393     break;
1394   case 3:
1395     int3store(pos,length);
1396     break;
1397   case 4:
1398     int4store(pos,length);
1399   default:
1400     break;
1401   }
1402   return;
1403 }
1404 
1405 
1406 /*
1407   Read record from datafile.
1408 
1409   SYNOPSIS
1410     _mi_read_dynamic_record()
1411       info                      MI_INFO pointer to table.
1412       filepos                   From where to read the record.
1413       buf                       Destination for record.
1414 
1415   NOTE
1416 
1417     If a write buffer is active, it needs to be flushed if its contents
1418     intersects with the record to read. We always check if the position
1419     of the first byte of the write buffer is lower than the position
1420     past the last byte to read. In theory this is also true if the write
1421     buffer is completely below the read segment. That is, if there is no
1422     intersection. But this case is unusual. We flush anyway. Only if the
1423     first byte in the write buffer is above the last byte to read, we do
1424     not flush.
1425 
1426     A dynamic record may need several reads. So this check must be done
1427     before every read. Reading a dynamic record starts with reading the
1428     block header. If the record does not fit into the free space of the
1429     header, the block may be longer than the header. In this case a
1430     second read is necessary. These one or two reads repeat for every
1431     part of the record.
1432 
1433   RETURN
1434     0           OK
1435     -1          Error
1436 */
1437 
_mi_read_dynamic_record(MI_INFO * info,my_off_t filepos,uchar * buf)1438 int _mi_read_dynamic_record(MI_INFO *info, my_off_t filepos, uchar *buf)
1439 {
1440   int block_of_record;
1441   uint b_type, left_length= 0;
1442   uchar *to= NULL;
1443   MI_BLOCK_INFO block_info;
1444   File file;
1445   DBUG_ENTER("mi_read_dynamic_record");
1446 
1447   if (filepos != HA_OFFSET_ERROR)
1448   {
1449     file=info->dfile;
1450     block_of_record= 0;   /* First block of record is numbered as zero. */
1451     block_info.second_read= 0;
1452     do
1453     {
1454       /* A corrupted table can have wrong pointers. (Bug# 19835) */
1455       if (filepos == HA_OFFSET_ERROR)
1456         goto panic;
1457       if (info->opt_flag & WRITE_CACHE_USED &&
1458 	  info->rec_cache.pos_in_file < filepos + MI_BLOCK_INFO_HEADER_LENGTH &&
1459 	  flush_io_cache(&info->rec_cache))
1460 	goto err;
1461       info->rec_cache.seek_not_done=1;
1462       if ((b_type= _mi_get_block_info(&block_info, file, filepos))
1463 	  & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
1464 	     BLOCK_FATAL_ERROR))
1465       {
1466 	if (b_type & (BLOCK_SYNC_ERROR | BLOCK_DELETED))
1467 	  set_my_errno(HA_ERR_RECORD_DELETED);
1468 	goto err;
1469       }
1470       if (block_of_record++ == 0)			/* First block */
1471       {
1472 	if (block_info.rec_len > (uint) info->s->base.max_pack_length)
1473 	  goto panic;
1474 	if (info->s->base.blobs)
1475 	{
1476 	  if (!(to=mi_alloc_rec_buff(info, block_info.rec_len,
1477 				     &info->rec_buff)))
1478 	    goto err;
1479 	}
1480 	else
1481 	  to= info->rec_buff;
1482 	left_length=block_info.rec_len;
1483       }
1484       if (left_length < block_info.data_len || ! block_info.data_len)
1485 	goto panic;			/* Wrong linked record */
1486       /* copy information that is already read */
1487       {
1488         uint offset= (uint) (block_info.filepos - filepos);
1489         uint prefetch_len= (sizeof(block_info.header) - offset);
1490         filepos+= sizeof(block_info.header);
1491 
1492         if (prefetch_len > block_info.data_len)
1493           prefetch_len= block_info.data_len;
1494         if (prefetch_len)
1495         {
1496           memcpy((uchar*) to, block_info.header + offset, prefetch_len);
1497           block_info.data_len-= prefetch_len;
1498           left_length-= prefetch_len;
1499           to+= prefetch_len;
1500         }
1501       }
1502       /* read rest of record from file */
1503       if (block_info.data_len)
1504       {
1505         if (info->opt_flag & WRITE_CACHE_USED &&
1506             info->rec_cache.pos_in_file < filepos + block_info.data_len &&
1507             flush_io_cache(&info->rec_cache))
1508           goto err;
1509         /*
1510           What a pity that this method is not called 'file_pread' and that
1511           there is no equivalent without seeking. We are at the right
1512           position already. :(
1513         */
1514         if (info->s->file_read(info, (uchar*) to, block_info.data_len,
1515                                filepos, MYF(MY_NABP)))
1516           goto panic;
1517         left_length-=block_info.data_len;
1518         to+=block_info.data_len;
1519       }
1520       filepos= block_info.next_filepos;
1521     } while (left_length);
1522 
1523     info->update|= HA_STATE_AKTIV;	/* We have a aktive record */
1524     fast_mi_writeinfo(info);
1525     DBUG_RETURN(_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
1526 		MY_FILE_ERROR ? 0 : -1);
1527   }
1528   fast_mi_writeinfo(info);
1529   DBUG_RETURN(-1);			/* Wrong data to read */
1530 
1531 panic:
1532   set_my_errno(HA_ERR_WRONG_IN_RECORD);
1533 err:
1534   (void) _mi_writeinfo(info,0);
1535   DBUG_RETURN(-1);
1536 }
1537 
1538 	/* compare unique constraint between stored rows */
1539 
_mi_cmp_dynamic_unique(MI_INFO * info,MI_UNIQUEDEF * def,const uchar * record,my_off_t pos)1540 int _mi_cmp_dynamic_unique(MI_INFO *info, MI_UNIQUEDEF *def,
1541 			   const uchar *record, my_off_t pos)
1542 {
1543   uchar *rec_buff,*old_record;
1544   int error;
1545   DBUG_ENTER("_mi_cmp_dynamic_unique");
1546 
1547   if (!(old_record= my_malloc(mi_key_memory_record_buffer,
1548                               info->s->base.reclength,
1549                               MYF(0))))
1550     DBUG_RETURN(1);
1551 
1552   /* Don't let the compare destroy blobs that may be in use */
1553   rec_buff=info->rec_buff;
1554   if (info->s->base.blobs)
1555     info->rec_buff=0;
1556   error=_mi_read_dynamic_record(info,pos,old_record);
1557   if (!error)
1558     error=mi_unique_comp(def, record, old_record, def->null_are_equal);
1559   if (info->s->base.blobs)
1560   {
1561     my_free(mi_get_rec_buff_ptr(info, info->rec_buff));
1562     info->rec_buff=rec_buff;
1563   }
1564   my_free(old_record);
1565   DBUG_RETURN(error);
1566 }
1567 
1568 
1569 	/* Compare of record one disk with packed record in memory */
1570 
_mi_cmp_dynamic_record(MI_INFO * info,const uchar * record)1571 int _mi_cmp_dynamic_record(MI_INFO *info, const uchar *record)
1572 {
1573   uint flag,reclength,b_type;
1574   my_off_t filepos;
1575   uchar *buffer;
1576   MI_BLOCK_INFO block_info;
1577   DBUG_ENTER("_mi_cmp_dynamic_record");
1578 
1579   if (info->opt_flag & WRITE_CACHE_USED)
1580   {
1581     info->update&= ~(HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK);
1582     if (flush_io_cache(&info->rec_cache))
1583       DBUG_RETURN(-1);
1584   }
1585   info->rec_cache.seek_not_done=1;
1586 
1587 	/* If nobody have touched the database we don't have to test rec */
1588 
1589   buffer=info->rec_buff;
1590   if ((info->opt_flag & READ_CHECK_USED))
1591   {						/* If check isn't disabled  */
1592     if (info->s->base.blobs)
1593     {
1594       if (!(buffer=(uchar*) my_malloc(mi_key_memory_record_buffer,
1595                                       info->s->base.pack_reclength+
1596 				      _my_calc_total_blob_length(info,record),
1597                                       MYF(0))))
1598 	DBUG_RETURN(-1);
1599     }
1600     reclength=_mi_rec_pack(info,buffer,record);
1601     record= buffer;
1602 
1603     filepos=info->lastpos;
1604     flag=block_info.second_read=0;
1605     block_info.next_filepos=filepos;
1606     while (reclength > 0)
1607     {
1608       if ((b_type=_mi_get_block_info(&block_info,info->dfile,
1609 				    block_info.next_filepos))
1610 	  & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
1611 	     BLOCK_FATAL_ERROR))
1612       {
1613 	if (b_type & (BLOCK_SYNC_ERROR | BLOCK_DELETED))
1614 	  set_my_errno(HA_ERR_RECORD_CHANGED);
1615 	goto err;
1616       }
1617       if (flag == 0)				/* First block */
1618       {
1619 	flag=1;
1620 	if (reclength != block_info.rec_len)
1621 	{
1622 	  set_my_errno(HA_ERR_RECORD_CHANGED);
1623 	  goto err;
1624 	}
1625       } else if (reclength < block_info.data_len)
1626       {
1627 	set_my_errno(HA_ERR_WRONG_IN_RECORD);
1628 	goto err;
1629       }
1630       reclength-=block_info.data_len;
1631       if (_mi_cmp_buffer(info->dfile,record,block_info.filepos,
1632 			 block_info.data_len))
1633       {
1634 	set_my_errno(HA_ERR_RECORD_CHANGED);
1635 	goto err;
1636       }
1637       flag=1;
1638       record+=block_info.data_len;
1639     }
1640   }
1641   set_my_errno(0);
1642 err:
1643   if (buffer != info->rec_buff)
1644     my_free((uchar*) buffer);
1645   DBUG_RETURN(my_errno());
1646 }
1647 
1648 
1649 	/* Compare file to buffert */
1650 
_mi_cmp_buffer(File file,const uchar * buff,my_off_t filepos,uint length)1651 static int _mi_cmp_buffer(File file, const uchar *buff, my_off_t filepos,
1652 			  uint length)
1653 {
1654   uint next_length;
1655   uchar temp_buff[IO_SIZE*2];
1656   DBUG_ENTER("_mi_cmp_buffer");
1657 
1658   next_length= IO_SIZE*2 - (uint) (filepos & (IO_SIZE-1));
1659 
1660   while (length > IO_SIZE*2)
1661   {
1662     if (mysql_file_pread(file, temp_buff, next_length, filepos, MYF(MY_NABP)) ||
1663 	memcmp(buff, temp_buff, next_length))
1664       goto err;
1665     filepos+=next_length;
1666     buff+=next_length;
1667     length-= next_length;
1668     next_length=IO_SIZE*2;
1669   }
1670   if (mysql_file_pread(file, temp_buff, length, filepos, MYF(MY_NABP)))
1671     goto err;
1672   DBUG_RETURN(memcmp(buff,temp_buff,length));
1673 err:
1674   DBUG_RETURN(1);
1675 }
1676 
1677 
1678 /*
1679   Read record from datafile.
1680 
1681   SYNOPSIS
1682     _mi_read_rnd_dynamic_record()
1683       info                      MI_INFO pointer to table.
1684       buf                       Destination for record.
1685       filepos                   From where to read the record.
1686       skip_deleted_blocks       If to repeat reading until a non-deleted
1687                                 record is found.
1688 
1689   NOTE
1690 
1691     If a write buffer is active, it needs to be flushed if its contents
1692     intersects with the record to read. We always check if the position
1693     of the first byte of the write buffer is lower than the position
1694     past the last byte to read. In theory this is also true if the write
1695     buffer is completely below the read segment. That is, if there is no
1696     intersection. But this case is unusual. We flush anyway. Only if the
1697     first byte in the write buffer is above the last byte to read, we do
1698     not flush.
1699 
1700     A dynamic record may need several reads. So this check must be done
1701     before every read. Reading a dynamic record starts with reading the
1702     block header. If the record does not fit into the free space of the
1703     header, the block may be longer than the header. In this case a
1704     second read is necessary. These one or two reads repeat for every
1705     part of the record.
1706 
1707   RETURN
1708     0           OK
1709     != 0        Error
1710 */
1711 
_mi_read_rnd_dynamic_record(MI_INFO * info,uchar * buf,my_off_t filepos,my_bool skip_deleted_blocks)1712 int _mi_read_rnd_dynamic_record(MI_INFO *info, uchar *buf,
1713 				my_off_t filepos,
1714 				my_bool skip_deleted_blocks)
1715 {
1716   int block_of_record, info_read, save_errno;
1717   uint left_len,b_type;
1718   uchar *to= NULL;
1719   MI_BLOCK_INFO block_info;
1720   MYISAM_SHARE *share=info->s;
1721   DBUG_ENTER("_mi_read_rnd_dynamic_record");
1722 
1723   DBUG_EXECUTE_IF("catch_file_offset_deviation",
1724 		 {
1725                    if (filepos)
1726 		     DBUG_RETURN(HA_ERR_RECORD_DELETED);
1727                  });
1728 
1729   info_read=0;
1730 
1731   if (info->lock_type == F_UNLCK)
1732   {
1733     if (share->tot_locks == 0)
1734     {
1735       if (my_lock(share->kfile,F_RDLCK,0L,F_TO_EOF,
1736 		  MYF(MY_SEEK_NOT_DONE) | info->lock_wait))
1737 	DBUG_RETURN(my_errno());
1738     }
1739   }
1740   else
1741     info_read=1;				/* memory-keyinfoblock is ok */
1742 
1743   block_of_record= 0;   /* First block of record is numbered as zero. */
1744   block_info.second_read= 0;
1745   left_len=1;
1746   do
1747   {
1748     if (filepos >= info->state->data_file_length)
1749     {
1750       if (!info_read)
1751       {						/* Check if changed */
1752 	info_read=1;
1753 	info->rec_cache.seek_not_done=1;
1754 	if (mi_state_info_read_dsk(share->kfile,&share->state,1))
1755 	  goto panic;
1756       }
1757       if (filepos >= info->state->data_file_length)
1758       {
1759 	set_my_errno(HA_ERR_END_OF_FILE);
1760 	goto err;
1761       }
1762     }
1763     if (info->opt_flag & READ_CACHE_USED)
1764     {
1765       if (_mi_read_cache(&info->rec_cache,(uchar*) block_info.header,filepos,
1766 			 sizeof(block_info.header),
1767 			 (!block_of_record && skip_deleted_blocks ?
1768                           READING_NEXT : 0) | READING_HEADER))
1769 	goto panic;
1770       b_type=_mi_get_block_info(&block_info,-1,filepos);
1771     }
1772     else
1773     {
1774       if (info->opt_flag & WRITE_CACHE_USED &&
1775 	  info->rec_cache.pos_in_file < filepos + MI_BLOCK_INFO_HEADER_LENGTH &&
1776 	  flush_io_cache(&info->rec_cache))
1777 	DBUG_RETURN(my_errno());
1778       info->rec_cache.seek_not_done=1;
1779       b_type=_mi_get_block_info(&block_info,info->dfile,filepos);
1780     }
1781 
1782     if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
1783 		  BLOCK_FATAL_ERROR))
1784     {
1785       if ((b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
1786 	  && skip_deleted_blocks)
1787       {
1788 	filepos=block_info.filepos+block_info.block_len;
1789 	block_info.second_read=0;
1790 	continue;		/* Search after next_record */
1791       }
1792       if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
1793       {
1794         /*
1795           If we're not on the first block of a record and
1796           the block is marked as deleted or out of sync,
1797           something's gone wrong: the record is damaged.
1798         */
1799         if (block_of_record != 0)
1800           goto panic;
1801 
1802 	set_my_errno(HA_ERR_RECORD_DELETED);
1803 	info->lastpos=block_info.filepos;
1804 	info->nextpos=block_info.filepos+block_info.block_len;
1805       }
1806       goto err;
1807     }
1808     if (block_of_record == 0)				/* First block */
1809     {
1810       if (block_info.rec_len > (uint) share->base.max_pack_length)
1811 	goto panic;
1812       info->lastpos=filepos;
1813       if (share->base.blobs)
1814       {
1815 	if (!(to= mi_alloc_rec_buff(info, block_info.rec_len,
1816 				    &info->rec_buff)))
1817 	  goto err;
1818       }
1819       else
1820 	to= info->rec_buff;
1821       left_len=block_info.rec_len;
1822     }
1823     if (left_len < block_info.data_len)
1824       goto panic;				/* Wrong linked record */
1825 
1826     /* copy information that is already read */
1827     {
1828       uint offset=(uint) (block_info.filepos - filepos);
1829       uint tmp_length= (sizeof(block_info.header) - offset);
1830       filepos=block_info.filepos;
1831 
1832       if (tmp_length > block_info.data_len)
1833 	tmp_length= block_info.data_len;
1834       if (tmp_length)
1835       {
1836 	memcpy((uchar*) to, block_info.header+offset,tmp_length);
1837 	block_info.data_len-=tmp_length;
1838 	left_len-=tmp_length;
1839 	to+=tmp_length;
1840 	filepos+=tmp_length;
1841       }
1842     }
1843     /* read rest of record from file */
1844     if (block_info.data_len)
1845     {
1846       if (info->opt_flag & READ_CACHE_USED)
1847       {
1848 	if (_mi_read_cache(&info->rec_cache,(uchar*) to,filepos,
1849 			   block_info.data_len,
1850 			   (!block_of_record && skip_deleted_blocks) ?
1851                            READING_NEXT : 0))
1852 	  goto panic;
1853       }
1854       else
1855       {
1856         if (info->opt_flag & WRITE_CACHE_USED &&
1857             info->rec_cache.pos_in_file <
1858             block_info.filepos + block_info.data_len &&
1859             flush_io_cache(&info->rec_cache))
1860           goto err;
1861         /* mysql_file_seek(info->dfile, filepos, MY_SEEK_SET, MYF(0)); */
1862         if (mysql_file_read(info->dfile, (uchar*) to, block_info.data_len,
1863                             MYF(MY_NABP)))
1864 	{
1865 	  if (my_errno() == -1)
1866 	    set_my_errno(HA_ERR_WRONG_IN_RECORD);	/* Unexpected end of file */
1867 	  goto err;
1868 	}
1869       }
1870     }
1871     /*
1872       Increment block-of-record counter. If it was the first block,
1873       remember the position behind the block for the next call.
1874     */
1875     if (block_of_record++ == 0)
1876     {
1877       info->nextpos= block_info.filepos + block_info.block_len;
1878       skip_deleted_blocks= 0;
1879     }
1880     left_len-=block_info.data_len;
1881     to+=block_info.data_len;
1882     filepos=block_info.next_filepos;
1883   } while (left_len);
1884 
1885   info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED;
1886   fast_mi_writeinfo(info);
1887   if (_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
1888       MY_FILE_ERROR)
1889     DBUG_RETURN(0);
1890   DBUG_RETURN(my_errno());			/* Wrong record */
1891 
1892 panic:
1893   set_my_errno(HA_ERR_WRONG_IN_RECORD);		/* Something is fatal wrong */
1894 err:
1895   save_errno=my_errno();
1896   (void) _mi_writeinfo(info,0);
1897   set_my_errno(save_errno);
1898   DBUG_RETURN(save_errno);
1899 }
1900 
1901 
1902 	/* Read and process header from a dynamic-record-file */
1903 
_mi_get_block_info(MI_BLOCK_INFO * info,File file,my_off_t filepos)1904 uint _mi_get_block_info(MI_BLOCK_INFO *info, File file, my_off_t filepos)
1905 {
1906   uint return_val=0;
1907   uchar *header=info->header;
1908 
1909   if (file >= 0)
1910   {
1911     /*
1912       We do not use mysql_file_pread() here because we want to have the file
1913       pointer set to the end of the header after this function.
1914       mysql_file_pread() may leave the file pointer untouched.
1915     */
1916     mysql_file_seek(file, filepos, MY_SEEK_SET, MYF(0));
1917     if (mysql_file_read(file, header, sizeof(info->header), MYF(0)) !=
1918 	sizeof(info->header))
1919       goto err;
1920   }
1921   DBUG_DUMP("header",header,MI_BLOCK_INFO_HEADER_LENGTH);
1922   if (info->second_read)
1923   {
1924     if (info->header[0] <= 6 || info->header[0] == 13)
1925       return_val=BLOCK_SYNC_ERROR;
1926   }
1927   else
1928   {
1929     if (info->header[0] > 6 && info->header[0] != 13)
1930       return_val=BLOCK_SYNC_ERROR;
1931   }
1932   info->next_filepos= HA_OFFSET_ERROR; /* Dummy if no next block */
1933 
1934   switch (info->header[0]) {
1935   case 0:
1936     if ((info->block_len=(uint) mi_uint3korr(header+1)) <
1937 	MI_MIN_BLOCK_LENGTH ||
1938 	(info->block_len & (MI_DYN_ALIGN_SIZE -1)))
1939       goto err;
1940     info->filepos=filepos;
1941     info->next_filepos=mi_sizekorr(header+4);
1942     info->prev_filepos=mi_sizekorr(header+12);
1943 #if SIZEOF_OFF_T == 4
1944     if ((mi_uint4korr(header+4) != 0 &&
1945 	 (mi_uint4korr(header+4) != (ulong) ~0 ||
1946 	  info->next_filepos != (ulong) ~0)) ||
1947 	(mi_uint4korr(header+12) != 0 &&
1948 	 (mi_uint4korr(header+12) != (ulong) ~0 ||
1949 	  info->prev_filepos != (ulong) ~0)))
1950       goto err;
1951 #endif
1952     return return_val | BLOCK_DELETED;		/* Deleted block */
1953 
1954   case 1:
1955     info->rec_len=info->data_len=info->block_len=mi_uint2korr(header+1);
1956     info->filepos=filepos+3;
1957     return return_val | BLOCK_FIRST | BLOCK_LAST;
1958   case 2:
1959     info->rec_len=info->data_len=info->block_len=mi_uint3korr(header+1);
1960     info->filepos=filepos+4;
1961     return return_val | BLOCK_FIRST | BLOCK_LAST;
1962 
1963   case 13:
1964     info->rec_len=mi_uint4korr(header+1);
1965     info->block_len=info->data_len=mi_uint3korr(header+5);
1966     info->next_filepos=mi_sizekorr(header+8);
1967     info->second_read=1;
1968     info->filepos=filepos+16;
1969     return return_val | BLOCK_FIRST;
1970 
1971   case 3:
1972     info->rec_len=info->data_len=mi_uint2korr(header+1);
1973     info->block_len=info->rec_len+ (uint) header[3];
1974     info->filepos=filepos+4;
1975     return return_val | BLOCK_FIRST | BLOCK_LAST;
1976   case 4:
1977     info->rec_len=info->data_len=mi_uint3korr(header+1);
1978     info->block_len=info->rec_len+ (uint) header[4];
1979     info->filepos=filepos+5;
1980     return return_val | BLOCK_FIRST | BLOCK_LAST;
1981 
1982   case 5:
1983     info->rec_len=mi_uint2korr(header+1);
1984     info->block_len=info->data_len=mi_uint2korr(header+3);
1985     info->next_filepos=mi_sizekorr(header+5);
1986     info->second_read=1;
1987     info->filepos=filepos+13;
1988     return return_val | BLOCK_FIRST;
1989   case 6:
1990     info->rec_len=mi_uint3korr(header+1);
1991     info->block_len=info->data_len=mi_uint3korr(header+4);
1992     info->next_filepos=mi_sizekorr(header+7);
1993     info->second_read=1;
1994     info->filepos=filepos+15;
1995     return return_val | BLOCK_FIRST;
1996 
1997     /* The following blocks are identical to 1-6 without rec_len */
1998   case 7:
1999     info->data_len=info->block_len=mi_uint2korr(header+1);
2000     info->filepos=filepos+3;
2001     return return_val | BLOCK_LAST;
2002   case 8:
2003     info->data_len=info->block_len=mi_uint3korr(header+1);
2004     info->filepos=filepos+4;
2005     return return_val | BLOCK_LAST;
2006 
2007   case 9:
2008     info->data_len=mi_uint2korr(header+1);
2009     info->block_len=info->data_len+ (uint) header[3];
2010     info->filepos=filepos+4;
2011     return return_val | BLOCK_LAST;
2012   case 10:
2013     info->data_len=mi_uint3korr(header+1);
2014     info->block_len=info->data_len+ (uint) header[4];
2015     info->filepos=filepos+5;
2016     return return_val | BLOCK_LAST;
2017 
2018   case 11:
2019     info->data_len=info->block_len=mi_uint2korr(header+1);
2020     info->next_filepos=mi_sizekorr(header+3);
2021     info->second_read=1;
2022     info->filepos=filepos+11;
2023     return return_val;
2024   case 12:
2025     info->data_len=info->block_len=mi_uint3korr(header+1);
2026     info->next_filepos=mi_sizekorr(header+4);
2027     info->second_read=1;
2028     info->filepos=filepos+12;
2029     return return_val;
2030   }
2031 
2032 err:
2033   set_my_errno(HA_ERR_WRONG_IN_RECORD);	 /* Garbage */
2034   return BLOCK_ERROR;
2035 }
2036