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