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