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