1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /*
24   Functions to handle space-packed-records and blobs
25 
26   A row may be stored in one or more linked blocks.
27   The block size is between MI_MIN_BLOCK_LENGTH and MI_MAX_BLOCK_LENGTH.
28   Each block is aligned on MI_DYN_ALIGN_SIZE.
29   The reson for the max block size is to not have too many different types
30   of blocks.  For the differnet block types, look at _mi_get_block_info()
31 */
32 
33 #include "myisamdef.h"
34 
35 /* Enough for comparing if number is zero */
36 static char zero_string[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
37 
38 static int write_dynamic_record(MI_INFO *info,const uchar *record,
39 				ulong reclength);
40 static int _mi_find_writepos(MI_INFO *info,ulong reclength,my_off_t *filepos,
41 			     ulong *length);
42 static int update_dynamic_record(MI_INFO *info,my_off_t filepos,uchar *record,
43 				 ulong reclength);
44 static int delete_dynamic_record(MI_INFO *info,my_off_t filepos,
45 				 uint second_read);
46 static int _mi_cmp_buffer(File file, const uchar *buff, my_off_t filepos,
47 			  uint length);
48 
49 	/* Interface function from MI_INFO */
50 
51 /*
52   Create mmaped area for MyISAM handler
53 
54   SYNOPSIS
55     mi_dynmap_file()
56     info		MyISAM handler
57 
58   RETURN
59     0  ok
60     1  error.
61 */
62 
mi_dynmap_file(MI_INFO * info,my_off_t size)63 my_bool mi_dynmap_file(MI_INFO *info, my_off_t size)
64 {
65   DBUG_ENTER("mi_dynmap_file");
66   if (size == 0 || size > (my_off_t) (~((size_t) 0)))
67   {
68     if (size)
69       DBUG_PRINT("warning", ("File is too large for mmap"));
70     else
71       DBUG_PRINT("warning", ("Do not mmap zero-length"));
72     DBUG_RETURN(1);
73   }
74   /*
75     I wonder if it is good to use MAP_NORESERVE. From the Linux man page:
76     MAP_NORESERVE
77       Do not reserve swap space for this mapping. When swap space is
78       reserved, one has the guarantee that it is possible to modify the
79       mapping. When swap space is not reserved one might get SIGSEGV
80       upon a write if no physical memory is available.
81   */
82   info->s->file_map= (uchar*)
83                   my_mmap(0, (size_t) size,
84                           info->s->mode==O_RDONLY ? PROT_READ :
85                           PROT_READ | PROT_WRITE,
86                           MAP_SHARED | MAP_NORESERVE,
87                           info->dfile, 0L);
88   if (info->s->file_map == (uchar*) MAP_FAILED)
89   {
90     info->s->file_map= NULL;
91     DBUG_RETURN(1);
92   }
93 #if defined(HAVE_MADVISE)
94   madvise((char*) info->s->file_map, size, MADV_RANDOM);
95 #endif
96   info->s->mmaped_length= size;
97   info->s->file_read= mi_mmap_pread;
98   info->s->file_write= mi_mmap_pwrite;
99   DBUG_RETURN(0);
100 }
101 
102 
103 /*
104   Destroy mmaped area for MyISAM handler
105 
106   SYNOPSIS
107     mi_munmap_file()
108     info                  MyISAM handler
109 
110   RETURN
111     0  ok
112    !0  error.
113 */
114 
mi_munmap_file(MI_INFO * info)115 int mi_munmap_file(MI_INFO *info)
116 {
117   int ret;
118   DBUG_ENTER("mi_unmap_file");
119   if ((ret= my_munmap((void*) info->s->file_map,
120                       (size_t)info->s->mmaped_length)))
121     DBUG_RETURN(ret);
122   info->s->file_read= mi_nommap_pread;
123   info->s->file_write= mi_nommap_pwrite;
124   info->s->file_map= 0;
125   info->s->mmaped_length= 0;
126   DBUG_RETURN(0);
127 }
128 
129 
130 /*
131   Resize mmaped area for MyISAM handler
132 
133   SYNOPSIS
134     mi_remap_file()
135     info		MyISAM handler
136 
137   RETURN
138 */
139 
mi_remap_file(MI_INFO * info,my_off_t size)140 void mi_remap_file(MI_INFO *info, my_off_t size)
141 {
142   if (info->s->file_map)
143   {
144     mi_munmap_file(info);
145     mi_dynmap_file(info, size);
146   }
147 }
148 
149 
150 /*
151   Read bytes from MySAM handler, using mmap or pread
152 
153   SYNOPSIS
154     mi_mmap_pread()
155     info		MyISAM handler
156     Buffer              Input buffer
157     Count               Count of bytes for read
158     offset              Start position
159     MyFlags
160 
161   RETURN
162     0  ok
163 */
164 
mi_mmap_pread(MI_INFO * info,uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)165 size_t mi_mmap_pread(MI_INFO *info, uchar *Buffer,
166                     size_t Count, my_off_t offset, myf MyFlags)
167 {
168   DBUG_PRINT("info", ("mi_read with mmap %d\n", info->dfile));
169   if (info->s->concurrent_insert)
170     mysql_rwlock_rdlock(&info->s->mmap_lock);
171 
172   /*
173     The following test may fail in the following cases:
174     - We failed to remap a memory area (fragmented memory?)
175     - This thread has done some writes, but not yet extended the
176     memory mapped area.
177   */
178 
179   if (info->s->mmaped_length >= offset + Count)
180   {
181     memcpy(Buffer, info->s->file_map + offset, Count);
182     if (info->s->concurrent_insert)
183       mysql_rwlock_unlock(&info->s->mmap_lock);
184     return 0;
185   }
186   else
187   {
188     if (info->s->concurrent_insert)
189       mysql_rwlock_unlock(&info->s->mmap_lock);
190     return mysql_file_pread(info->dfile, Buffer, Count, offset, MyFlags);
191   }
192 }
193 
194 
195         /* wrapper for mysql_file_pread in case if mmap isn't used */
196 
mi_nommap_pread(MI_INFO * info,uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)197 size_t mi_nommap_pread(MI_INFO *info, uchar *Buffer,
198                        size_t Count, my_off_t offset, myf MyFlags)
199 {
200   return mysql_file_pread(info->dfile, Buffer, Count, offset, MyFlags);
201 }
202 
203 
204 /*
205   Write bytes to MySAM handler, using mmap or pwrite
206 
207   SYNOPSIS
208     mi_mmap_pwrite()
209     info		MyISAM handler
210     Buffer              Output buffer
211     Count               Count of bytes for write
212     offset              Start position
213     MyFlags
214 
215   RETURN
216     0  ok
217     !=0  error.  In this case return error from pwrite
218 */
219 
mi_mmap_pwrite(MI_INFO * info,const uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)220 size_t mi_mmap_pwrite(MI_INFO *info, const uchar *Buffer,
221                       size_t Count, my_off_t offset, myf MyFlags)
222 {
223   DBUG_PRINT("info", ("mi_write with mmap %d\n", info->dfile));
224   if (info->s->concurrent_insert)
225     mysql_rwlock_rdlock(&info->s->mmap_lock);
226 
227   /*
228     The following test may fail in the following cases:
229     - We failed to remap a memory area (fragmented memory?)
230     - This thread has done some writes, but not yet extended the
231     memory mapped area.
232   */
233 
234   if (info->s->mmaped_length >= offset + Count)
235   {
236     memcpy(info->s->file_map + offset, Buffer, Count);
237     if (info->s->concurrent_insert)
238       mysql_rwlock_unlock(&info->s->mmap_lock);
239     return 0;
240   }
241   else
242   {
243     info->s->nonmmaped_inserts++;
244     if (info->s->concurrent_insert)
245       mysql_rwlock_unlock(&info->s->mmap_lock);
246     return mysql_file_pwrite(info->dfile, Buffer, Count, offset, MyFlags);
247   }
248 
249 }
250 
251 
252         /* wrapper for mysql_file_pwrite in case if mmap isn't used */
253 
mi_nommap_pwrite(MI_INFO * info,const uchar * Buffer,size_t Count,my_off_t offset,myf MyFlags)254 size_t mi_nommap_pwrite(MI_INFO *info, const uchar *Buffer,
255                       size_t Count, my_off_t offset, myf MyFlags)
256 {
257   return mysql_file_pwrite(info->dfile, Buffer, Count, offset, MyFlags);
258 }
259 
260 
_mi_write_dynamic_record(MI_INFO * info,const uchar * record)261 int _mi_write_dynamic_record(MI_INFO *info, const uchar *record)
262 {
263   ulong reclength=_mi_rec_pack(info,info->rec_buff,record);
264   return (write_dynamic_record(info,info->rec_buff,reclength));
265 }
266 
_mi_update_dynamic_record(MI_INFO * info,my_off_t pos,const uchar * record)267 int _mi_update_dynamic_record(MI_INFO *info, my_off_t pos, const uchar *record)
268 {
269   uint length=_mi_rec_pack(info,info->rec_buff,record);
270   return (update_dynamic_record(info,pos,info->rec_buff,length));
271 }
272 
_mi_write_blob_record(MI_INFO * info,const uchar * record)273 int _mi_write_blob_record(MI_INFO *info, const uchar *record)
274 {
275   uchar *rec_buff;
276   int error;
277   ulong reclength,reclength2,extra;
278 
279   extra= (ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
280 	  MI_DYN_DELETE_BLOCK_HEADER+1);
281   reclength= (info->s->base.pack_reclength +
282 	      _my_calc_total_blob_length(info,record)+ extra);
283   if (!(rec_buff=(uchar*) my_malloc(mi_key_memory_record_buffer,
284                                     reclength, MYF(0))))
285   {
286     set_my_errno(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
287     return(-1);
288   }
289   reclength2= _mi_rec_pack(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
290 			   record);
291   DBUG_PRINT("info",("reclength: %lu  reclength2: %lu",
292 		     reclength, reclength2));
293   assert(reclength2 <= reclength);
294   error=write_dynamic_record(info,rec_buff+ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER),
295 			     reclength2);
296   my_free(rec_buff);
297   return(error);
298 }
299 
300 
_mi_update_blob_record(MI_INFO * info,my_off_t pos,const uchar * record)301 int _mi_update_blob_record(MI_INFO *info, my_off_t pos, const uchar *record)
302 {
303   uchar *rec_buff;
304   int error;
305   ulong reclength,extra;
306 
307   extra= (ALIGN_SIZE(MI_MAX_DYN_BLOCK_HEADER)+MI_SPLIT_LENGTH+
308 	  MI_DYN_DELETE_BLOCK_HEADER);
309   reclength= (info->s->base.pack_reclength+
310 	      _my_calc_total_blob_length(info,record)+ extra);
311   if (!(rec_buff=(uchar*) my_malloc(mi_key_memory_record_buffer,
312                                     reclength, MYF(0))))
313   {
314     set_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_free(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       set_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       set_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       set_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       set_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       set_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       memset(block_info.header + 12, 255, 8);
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   memmove((uchar*) (*record - head_length),(uchar*) temp, head_length);
715   memcpy(temp,record_end,(size_t) (extra_length+del_length));
716   memset(record_end, 0, 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     memset(pos + 12, 255, 8);			/* 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   block_info.next_filepos= HA_OFFSET_ERROR;
804   /*
805      Check if we have enough room for the record.
806      First we do simplified check to make usual case faster.
807      Then we do more precise check for the space left.
808      Though it still is not absolutely precise, as
809      we always use MI_MAX_DYN_BLOCK_HEADER while it can be
810      less in the most of the cases.
811   */
812 
813   /*
814     compare with just the reclength as we're going
815     to get some space from the old replaced record
816   */
817   if (unlikely(info->s->base.max_data_file_length -
818         info->state->data_file_length < reclength))
819   {
820     /*
821        let's read the old record's block to find out the length of the
822        old record
823     */
824     if ((error=_mi_get_block_info(&block_info,info->dfile,filepos))
825         & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR | BLOCK_FATAL_ERROR))
826     {
827       DBUG_PRINT("error",("Got wrong block info"));
828       if (!(error & BLOCK_FATAL_ERROR))
829         set_my_errno(HA_ERR_WRONG_IN_RECORD);
830       goto err;
831     }
832 
833     /*
834       if new record isn't longer, we can go on safely
835     */
836     if (block_info.rec_len < reclength)
837     {
838       if (info->s->base.max_data_file_length - info->state->data_file_length +
839           info->state->empty - info->state->del * MI_MAX_DYN_BLOCK_HEADER <
840           reclength - block_info.rec_len + MI_MAX_DYN_BLOCK_HEADER)
841       {
842         set_my_errno(HA_ERR_RECORD_FILE_FULL);
843         goto err;
844       }
845     }
846     block_info.second_read=0;
847   }
848 
849   while (reclength > 0)
850   {
851     if (filepos != info->s->state.dellink)
852     {
853       block_info.next_filepos= HA_OFFSET_ERROR;
854       if ((error=_mi_get_block_info(&block_info,info->dfile,filepos))
855 	  & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
856 	     BLOCK_FATAL_ERROR))
857       {
858 	DBUG_PRINT("error",("Got wrong block info"));
859 	if (!(error & BLOCK_FATAL_ERROR))
860 	  set_my_errno(HA_ERR_WRONG_IN_RECORD);
861 	goto err;
862       }
863       length=(ulong) (block_info.filepos-filepos) + block_info.block_len;
864       if (length < reclength)
865       {
866 	uint tmp=MY_ALIGN(reclength - length + 3 +
867 			  MY_TEST(reclength >= 65520L),MI_DYN_ALIGN_SIZE);
868 	/* Don't create a block bigger than MI_MAX_BLOCK_LENGTH */
869 	tmp= MY_MIN(length+tmp, MI_MAX_BLOCK_LENGTH)-length;
870 	/* Check if we can extend this block */
871 	if (block_info.filepos + block_info.block_len ==
872 	    info->state->data_file_length &&
873 	    info->state->data_file_length <
874 	    info->s->base.max_data_file_length-tmp)
875 	{
876 	  /* extend file */
877 	  DBUG_PRINT("info",("Extending file with %d bytes",tmp));
878 	  if (info->nextpos == info->state->data_file_length)
879 	    info->nextpos+= tmp;
880 	  info->state->data_file_length+= tmp;
881 	  info->update|= HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK;
882 	  length+=tmp;
883 	}
884 	else if (length < MI_MAX_BLOCK_LENGTH - MI_MIN_BLOCK_LENGTH)
885 	{
886 	  /*
887 	    Check if next block is a deleted block
888 	    Above we have MI_MIN_BLOCK_LENGTH to avoid the problem where
889 	    the next block is so small it can't be splited which could
890 	    casue problems
891 	  */
892 
893 	  MI_BLOCK_INFO del_block;
894 	  del_block.second_read=0;
895 	  if (_mi_get_block_info(&del_block,info->dfile,
896 				 block_info.filepos + block_info.block_len) &
897 	      BLOCK_DELETED)
898 	  {
899 	    /* Use; Unlink it and extend the current block */
900 	    DBUG_PRINT("info",("Extending current block"));
901 	    if (unlink_deleted_block(info,&del_block))
902 	      goto err;
903 	    if ((length+=del_block.block_len) > MI_MAX_BLOCK_LENGTH)
904 	    {
905 	      /*
906 		New block was too big, link overflow part back to
907 		delete list
908 	      */
909 	      my_off_t next_pos;
910 	      ulong rest_length= length-MI_MAX_BLOCK_LENGTH;
911 	      set_if_bigger(rest_length, MI_MIN_BLOCK_LENGTH);
912 	      next_pos= del_block.filepos+ del_block.block_len - rest_length;
913 
914 	      if (update_backward_delete_link(info, info->s->state.dellink,
915 					      next_pos))
916 		DBUG_RETURN(1);
917 
918 	      /* create delete link for data that didn't fit into the page */
919 	      del_block.header[0]=0;
920 	      mi_int3store(del_block.header+1, rest_length);
921 	      mi_sizestore(del_block.header+4,info->s->state.dellink);
922 	      memset(del_block.header + 12, 255, 8);
923 	      if (info->s->file_write(info,(uchar*) del_block.header,20, next_pos,
924 			    MYF(MY_NABP)))
925 		DBUG_RETURN(1);
926 	      info->s->state.dellink= next_pos;
927 	      info->s->state.split++;
928 	      info->state->del++;
929 	      info->state->empty+= rest_length;
930 	      length-= rest_length;
931 	    }
932 	  }
933 	}
934       }
935     }
936     else
937     {
938       if (_mi_find_writepos(info,reclength,&filepos,&length))
939 	goto err;
940     }
941     if (_mi_write_part_record(info,filepos,length,block_info.next_filepos,
942 			      &record,&reclength,&flag))
943       goto err;
944     if ((filepos=block_info.next_filepos) == HA_OFFSET_ERROR)
945     {
946       /* Start writing data on deleted blocks */
947       filepos=info->s->state.dellink;
948     }
949   }
950 
951   if (block_info.next_filepos != HA_OFFSET_ERROR)
952   {
953     /*
954       delete_dynamic_record() may change data file position.
955       IO cache must be notified as it may still have cached
956       data, which has to be flushed later.
957     */
958     info->rec_cache.seek_not_done= 1;
959     if (delete_dynamic_record(info,block_info.next_filepos,1))
960       goto err;
961   }
962   DBUG_RETURN(0);
963 err:
964   DBUG_RETURN(1);
965 }
966 
967 
968 	/* Pack a record. Return new reclength */
969 
_mi_rec_pack(MI_INFO * info,uchar * to,const uchar * from)970 uint _mi_rec_pack(MI_INFO *info, uchar *to,
971                   const uchar *from)
972 {
973   uint		length,new_length,flag,bit,i;
974   uchar		*pos,*end,*startpos,*packpos;
975   enum en_fieldtype type;
976   MI_COLUMNDEF *rec;
977   MI_BLOB	*blob;
978   DBUG_ENTER("_mi_rec_pack");
979 
980   flag=0 ; bit=1;
981   startpos=packpos=to; to+= info->s->base.pack_bits; blob=info->blobs;
982   rec=info->s->rec;
983 
984   for (i=info->s->base.fields ; i-- > 0; from+= length,rec++)
985   {
986     length=(uint) rec->length;
987     if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL)
988     {
989       if (type == FIELD_BLOB)
990       {
991 	if (!blob->length)
992 	  flag|=bit;
993 	else
994 	{
995 	  char *temp_pos;
996 	  size_t tmp_length=length-portable_sizeof_char_ptr;
997 	  memcpy((uchar*) to,from,tmp_length);
998 	  memcpy(&temp_pos,from+tmp_length,sizeof(char*));
999 	  memcpy(to+tmp_length,temp_pos,(size_t) blob->length);
1000 	  to+=tmp_length+blob->length;
1001 	}
1002 	blob++;
1003       }
1004       else if (type == FIELD_SKIP_ZERO)
1005       {
1006 	if (memcmp((uchar*) from,zero_string,length) == 0)
1007 	  flag|=bit;
1008 	else
1009 	{
1010 	  memcpy((uchar*) to,from,(size_t) length); to+=length;
1011 	}
1012       }
1013       else if (type == FIELD_SKIP_ENDSPACE ||
1014 	       type == FIELD_SKIP_PRESPACE)
1015       {
1016 	pos= (uchar*) from; end= (uchar*) from + length;
1017 	if (type == FIELD_SKIP_ENDSPACE)
1018 	{					/* Pack trailing spaces */
1019 	  while (end > from && *(end-1) == ' ')
1020 	    end--;
1021 	}
1022 	else
1023 	{					/* Pack pref-spaces */
1024 	  while (pos < end && *pos == ' ')
1025 	    pos++;
1026 	}
1027 	new_length=(uint) (end-pos);
1028 	if (new_length +1 + MY_TEST(rec->length > 255 && new_length > 127)
1029 	    < length)
1030 	{
1031 	  if (rec->length > 255 && new_length > 127)
1032 	  {
1033             to[0]= (uchar) ((new_length & 127) + 128);
1034             to[1]= (uchar) (new_length >> 7);
1035             to+=2;
1036           }
1037           else
1038             *to++= (uchar) new_length;
1039 	  memcpy((uchar*) to,pos,(size_t) new_length); to+=new_length;
1040 	  flag|=bit;
1041 	}
1042 	else
1043 	{
1044 	  memcpy(to,from,(size_t) length); to+=length;
1045 	}
1046       }
1047       else if (type == FIELD_VARCHAR)
1048       {
1049         uint pack_length= HA_VARCHAR_PACKLENGTH(rec->length -1);
1050 	uint tmp_length;
1051         if (pack_length == 1)
1052         {
1053           tmp_length= (uint) *(uchar*) from;
1054           *to++= *from;
1055         }
1056         else
1057         {
1058           tmp_length= uint2korr(from);
1059           store_key_length_inc(to,tmp_length);
1060         }
1061         memcpy(to, from+pack_length,tmp_length);
1062         to+= tmp_length;
1063         continue;
1064       }
1065       else
1066       {
1067 	memcpy(to,from,(size_t) length); to+=length;
1068 	continue;				/* Normal field */
1069       }
1070       if ((bit= bit << 1) >= 256)
1071       {
1072         *packpos++= (uchar) flag;
1073 	bit=1; flag=0;
1074       }
1075     }
1076     else
1077     {
1078       memcpy(to,from,(size_t) length); to+=length;
1079     }
1080   }
1081   if (bit != 1)
1082     *packpos= (uchar) flag;
1083   if (info->s->calc_checksum)
1084     *to++= (uchar) info->checksum;
1085   DBUG_PRINT("exit",("packed length: %d",(int) (to-startpos)));
1086   DBUG_RETURN((uint) (to-startpos));
1087 } /* _mi_rec_pack */
1088 
1089 
1090 
1091 /*
1092   Check if a record was correctly packed. Used only by myisamchk
1093   Returns 0 if record is ok.
1094 */
1095 
_mi_rec_check(MI_INFO * info,const uchar * record,uchar * rec_buff,ulong packed_length,my_bool with_checksum)1096 my_bool _mi_rec_check(MI_INFO *info,const uchar *record, uchar *rec_buff,
1097                       ulong packed_length, my_bool with_checksum)
1098 {
1099   uint		length,new_length,flag,bit,i;
1100   uchar		*pos,*end,*packpos,*to;
1101   enum en_fieldtype type;
1102   MI_COLUMNDEF *rec;
1103   DBUG_ENTER("_mi_rec_check");
1104 
1105   packpos=rec_buff; to= rec_buff+info->s->base.pack_bits;
1106   rec=info->s->rec;
1107   flag= *packpos; bit=1;
1108 
1109   for (i=info->s->base.fields ; i-- > 0; record+= length, rec++)
1110   {
1111     length=(uint) rec->length;
1112     if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL)
1113     {
1114       if (type == FIELD_BLOB)
1115       {
1116 	uint blob_length=
1117 	  _mi_calc_blob_length(length-portable_sizeof_char_ptr,record);
1118 	if (!blob_length && !(flag & bit))
1119 	  goto err;
1120 	if (blob_length)
1121 	  to+=length - portable_sizeof_char_ptr+ blob_length;
1122       }
1123       else if (type == FIELD_SKIP_ZERO)
1124       {
1125 	if (memcmp((uchar*) record,zero_string,length) == 0)
1126 	{
1127 	  if (!(flag & bit))
1128 	    goto err;
1129 	}
1130 	else
1131 	  to+=length;
1132       }
1133       else if (type == FIELD_SKIP_ENDSPACE ||
1134 	       type == FIELD_SKIP_PRESPACE)
1135       {
1136 	pos= (uchar*) record; end= (uchar*) record + length;
1137 	if (type == FIELD_SKIP_ENDSPACE)
1138 	{					/* Pack trailing spaces */
1139 	  while (end > record && *(end-1) == ' ')
1140 	    end--;
1141 	}
1142 	else
1143 	{					/* Pack pre-spaces */
1144 	  while (pos < end && *pos == ' ')
1145 	    pos++;
1146 	}
1147 	new_length=(uint) (end-pos);
1148 	if (new_length +1 + MY_TEST(rec->length > 255 && new_length > 127)
1149 	    < length)
1150 	{
1151 	  if (!(flag & bit))
1152 	    goto err;
1153 	  if (rec->length > 255 && new_length > 127)
1154 	  {
1155             /* purecov: begin inspected */
1156             if (to[0] != (uchar) ((new_length & 127) + 128) ||
1157                 to[1] != (uchar) (new_length >> 7))
1158               goto err;
1159             to+=2;
1160             /* purecov: end */
1161           }
1162           else if (*to++ != (uchar) new_length)
1163 	    goto err;
1164 	  to+=new_length;
1165 	}
1166 	else
1167 	  to+=length;
1168       }
1169       else if (type == FIELD_VARCHAR)
1170       {
1171         uint pack_length= HA_VARCHAR_PACKLENGTH(rec->length -1);
1172 	uint tmp_length;
1173         if (pack_length == 1)
1174         {
1175           tmp_length= (uint) *(uchar*) record;
1176           to+= 1+ tmp_length;
1177           continue;
1178         }
1179         else
1180         {
1181           tmp_length= uint2korr(record);
1182           to+= get_pack_length(tmp_length)+tmp_length;
1183         }
1184         continue;
1185       }
1186       else
1187       {
1188 	to+=length;
1189 	continue;				/* Normal field */
1190       }
1191       if ((bit= bit << 1) >= 256)
1192       {
1193 	flag= *++packpos;
1194 	bit=1;
1195       }
1196     }
1197     else
1198       to+= length;
1199   }
1200   if (packed_length != (uint) (to - rec_buff) + MY_TEST(info->s->calc_checksum) ||
1201       (bit != 1 && (flag & ~(bit - 1))))
1202     goto err;
1203   if (with_checksum && ((uchar) info->checksum != (uchar) *to))
1204   {
1205     DBUG_PRINT("error",("wrong checksum for row"));
1206     goto err;
1207   }
1208   DBUG_RETURN(0);
1209 
1210 err:
1211   DBUG_RETURN(1);
1212 }
1213 
1214 
1215 
1216 	/* Unpacks a record */
1217 	/* Returns -1 and my_errno =HA_ERR_RECORD_DELETED if reclength isn't */
1218 	/* right. Returns reclength (>0) if ok */
1219 
_mi_rec_unpack(MI_INFO * info,uchar * to,uchar * from,ulong found_length)1220 ulong _mi_rec_unpack(MI_INFO *info, uchar *to, uchar *from,
1221 		     ulong found_length)
1222 {
1223   uint flag,bit,length,rec_length,min_pack_length;
1224   enum en_fieldtype type;
1225   uchar *from_end,*to_end,*packpos;
1226   MI_COLUMNDEF *rec,*end_field;
1227   DBUG_ENTER("_mi_rec_unpack");
1228 
1229   to_end=to + info->s->base.reclength;
1230   from_end=from+found_length;
1231   flag= (uchar) *from; bit=1; packpos=from;
1232   if (found_length < info->s->base.min_pack_length)
1233     goto err;
1234   from+= info->s->base.pack_bits;
1235   min_pack_length=info->s->base.min_pack_length - info->s->base.pack_bits;
1236 
1237   for (rec=info->s->rec , end_field=rec+info->s->base.fields ;
1238        rec < end_field ; to+= rec_length, rec++)
1239   {
1240     rec_length=rec->length;
1241     if ((type = (enum en_fieldtype) rec->type) != FIELD_NORMAL &&
1242 	(type != FIELD_CHECK))
1243     {
1244       if (type == FIELD_VARCHAR)
1245       {
1246         uint pack_length= HA_VARCHAR_PACKLENGTH(rec_length-1);
1247         if (pack_length == 1)
1248         {
1249           length= (uint) *(uchar*) from;
1250           if (length > rec_length-1)
1251             goto err;
1252           *to= *from++;
1253         }
1254         else
1255         {
1256           get_key_length(length, from);
1257           if (length > rec_length-2)
1258             goto err;
1259           int2store(to,length);
1260         }
1261         if (from+length > from_end)
1262           goto err;
1263         memcpy(to+pack_length, from, length);
1264         from+= length;
1265         min_pack_length--;
1266         continue;
1267       }
1268       if (flag & bit)
1269       {
1270 	if (type == FIELD_BLOB || type == FIELD_SKIP_ZERO)
1271 	  memset(to, 0, rec_length);
1272 	else if (type == FIELD_SKIP_ENDSPACE ||
1273 		 type == FIELD_SKIP_PRESPACE)
1274 	{
1275 	  if (rec->length > 255 && *from & 128)
1276 	  {
1277 	    if (from + 1 >= from_end)
1278 	      goto err;
1279 	    length= (*from & 127)+ ((uint) (uchar) *(from+1) << 7); from+=2;
1280 	  }
1281 	  else
1282 	  {
1283 	    if (from == from_end)
1284 	      goto err;
1285 	    length= (uchar) *from++;
1286 	  }
1287 	  min_pack_length--;
1288 	  if (length >= rec_length ||
1289 	      min_pack_length + length > (uint) (from_end - from))
1290 	    goto err;
1291 	  if (type == FIELD_SKIP_ENDSPACE)
1292 	  {
1293 	    memcpy(to,(uchar*) from,(size_t) length);
1294 	    memset(to + length, ' ', rec_length-length);
1295 	  }
1296 	  else
1297 	  {
1298 	    memset(to, ' ', rec_length-length);
1299 	    memcpy(to+rec_length-length,(uchar*) from,(size_t) length);
1300 	  }
1301 	  from+=length;
1302 	}
1303       }
1304       else if (type == FIELD_BLOB)
1305       {
1306 	uint size_length=rec_length- portable_sizeof_char_ptr;
1307 	ulong blob_length=_mi_calc_blob_length(size_length,from);
1308         ulong from_left= (ulong) (from_end - from);
1309         if (from_left < size_length ||
1310             from_left - size_length < blob_length ||
1311             from_left - size_length - blob_length < min_pack_length)
1312           goto err;
1313 	memcpy(to, from, (size_t) size_length);
1314 	from+=size_length;
1315 	memcpy(to+size_length, &from, sizeof(char*));
1316 	from+=blob_length;
1317       }
1318       else
1319       {
1320 	if (type == FIELD_SKIP_ENDSPACE || type == FIELD_SKIP_PRESPACE)
1321 	  min_pack_length--;
1322 	if (min_pack_length + rec_length > (uint) (from_end - from))
1323 	  goto err;
1324 	memcpy(to,(uchar*) from,(size_t) rec_length); from+=rec_length;
1325       }
1326       if ((bit= bit << 1) >= 256)
1327       {
1328 	flag= (uchar) *++packpos; bit=1;
1329       }
1330     }
1331     else
1332     {
1333       if (min_pack_length > (uint) (from_end - from))
1334 	goto err;
1335       min_pack_length-=rec_length;
1336       memcpy(to, (uchar*) from, (size_t) rec_length);
1337       from+=rec_length;
1338     }
1339   }
1340   if (info->s->calc_checksum)
1341     from++;
1342   if (to == to_end && from == from_end && (bit == 1 || !(flag & ~(bit-1))))
1343     DBUG_RETURN(found_length);
1344 
1345 err:
1346   set_my_errno(HA_ERR_WRONG_IN_RECORD);
1347   DBUG_PRINT("error",("to_end: 0x%lx -> 0x%lx  from_end: 0x%lx -> 0x%lx",
1348 		      (long) to, (long) to_end, (long) from, (long) from_end));
1349   DBUG_DUMP("from",(uchar*) info->rec_buff,info->s->base.min_pack_length);
1350   DBUG_RETURN(MY_FILE_ERROR);
1351 } /* _mi_rec_unpack */
1352 
1353 
1354 	/* Calc length of blob. Update info in blobs->length */
1355 
_my_calc_total_blob_length(MI_INFO * info,const uchar * record)1356 ulong _my_calc_total_blob_length(MI_INFO *info, const uchar *record)
1357 {
1358   ulong length;
1359   MI_BLOB *blob,*end;
1360 
1361   for (length=0, blob= info->blobs, end=blob+info->s->base.blobs ;
1362        blob != end;
1363        blob++)
1364   {
1365     blob->length=_mi_calc_blob_length(blob->pack_length,record + blob->offset);
1366     length+=blob->length;
1367   }
1368   return length;
1369 }
1370 
1371 
_mi_calc_blob_length(uint length,const uchar * pos)1372 ulong _mi_calc_blob_length(uint length, const uchar *pos)
1373 {
1374   switch (length) {
1375   case 1:
1376     return (uint) (uchar) *pos;
1377   case 2:
1378     return (uint) uint2korr(pos);
1379   case 3:
1380     return uint3korr(pos);
1381   case 4:
1382     return uint4korr(pos);
1383   default:
1384     break;
1385   }
1386   return 0; /* Impossible */
1387 }
1388 
1389 
_mi_store_blob_length(uchar * pos,uint pack_length,uint length)1390 void _mi_store_blob_length(uchar *pos,uint pack_length,uint length)
1391 {
1392   switch (pack_length) {
1393   case 1:
1394     *pos= (uchar) length;
1395     break;
1396   case 2:
1397     int2store(pos,length);
1398     break;
1399   case 3:
1400     int3store(pos,length);
1401     break;
1402   case 4:
1403     int4store(pos,length);
1404   default:
1405     break;
1406   }
1407   return;
1408 }
1409 
1410 
1411 /*
1412   Read record from datafile.
1413 
1414   SYNOPSIS
1415     _mi_read_dynamic_record()
1416       info                      MI_INFO pointer to table.
1417       filepos                   From where to read the record.
1418       buf                       Destination for record.
1419 
1420   NOTE
1421 
1422     If a write buffer is active, it needs to be flushed if its contents
1423     intersects with the record to read. We always check if the position
1424     of the first byte of the write buffer is lower than the position
1425     past the last byte to read. In theory this is also true if the write
1426     buffer is completely below the read segment. That is, if there is no
1427     intersection. But this case is unusual. We flush anyway. Only if the
1428     first byte in the write buffer is above the last byte to read, we do
1429     not flush.
1430 
1431     A dynamic record may need several reads. So this check must be done
1432     before every read. Reading a dynamic record starts with reading the
1433     block header. If the record does not fit into the free space of the
1434     header, the block may be longer than the header. In this case a
1435     second read is necessary. These one or two reads repeat for every
1436     part of the record.
1437 
1438   RETURN
1439     0           OK
1440     -1          Error
1441 */
1442 
_mi_read_dynamic_record(MI_INFO * info,my_off_t filepos,uchar * buf)1443 int _mi_read_dynamic_record(MI_INFO *info, my_off_t filepos, uchar *buf)
1444 {
1445   int block_of_record;
1446   uint b_type, left_length= 0;
1447   uchar *to= NULL;
1448   MI_BLOCK_INFO block_info;
1449   File file;
1450   DBUG_ENTER("mi_read_dynamic_record");
1451 
1452   if (filepos != HA_OFFSET_ERROR)
1453   {
1454     file=info->dfile;
1455     block_of_record= 0;   /* First block of record is numbered as zero. */
1456     block_info.second_read= 0;
1457     do
1458     {
1459       /* A corrupted table can have wrong pointers. (Bug# 19835) */
1460       if (filepos == HA_OFFSET_ERROR)
1461         goto panic;
1462       if (info->opt_flag & WRITE_CACHE_USED &&
1463 	  info->rec_cache.pos_in_file < filepos + MI_BLOCK_INFO_HEADER_LENGTH &&
1464 	  flush_io_cache(&info->rec_cache))
1465 	goto err;
1466       info->rec_cache.seek_not_done=1;
1467       if ((b_type= _mi_get_block_info(&block_info, file, filepos))
1468 	  & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
1469 	     BLOCK_FATAL_ERROR))
1470       {
1471 	if (b_type & (BLOCK_SYNC_ERROR | BLOCK_DELETED))
1472 	  set_my_errno(HA_ERR_RECORD_DELETED);
1473 	goto err;
1474       }
1475       if (block_of_record++ == 0)			/* First block */
1476       {
1477 	if (block_info.rec_len > (uint) info->s->base.max_pack_length)
1478 	  goto panic;
1479 	if (info->s->base.blobs)
1480 	{
1481 	  if (!(to=mi_alloc_rec_buff(info, block_info.rec_len,
1482 				     &info->rec_buff)))
1483 	    goto err;
1484 	}
1485 	else
1486 	  to= info->rec_buff;
1487 	left_length=block_info.rec_len;
1488       }
1489       if (left_length < block_info.data_len || ! block_info.data_len)
1490 	goto panic;			/* Wrong linked record */
1491       /* copy information that is already read */
1492       {
1493         uint offset= (uint) (block_info.filepos - filepos);
1494         uint prefetch_len= (sizeof(block_info.header) - offset);
1495         filepos+= sizeof(block_info.header);
1496 
1497         if (prefetch_len > block_info.data_len)
1498           prefetch_len= block_info.data_len;
1499         if (prefetch_len)
1500         {
1501           memcpy((uchar*) to, block_info.header + offset, prefetch_len);
1502           block_info.data_len-= prefetch_len;
1503           left_length-= prefetch_len;
1504           to+= prefetch_len;
1505         }
1506       }
1507       /* read rest of record from file */
1508       if (block_info.data_len)
1509       {
1510         if (info->opt_flag & WRITE_CACHE_USED &&
1511             info->rec_cache.pos_in_file < filepos + block_info.data_len &&
1512             flush_io_cache(&info->rec_cache))
1513           goto err;
1514         /*
1515           What a pity that this method is not called 'file_pread' and that
1516           there is no equivalent without seeking. We are at the right
1517           position already. :(
1518         */
1519         if (info->s->file_read(info, (uchar*) to, block_info.data_len,
1520                                filepos, MYF(MY_NABP)))
1521           goto panic;
1522         left_length-=block_info.data_len;
1523         to+=block_info.data_len;
1524       }
1525       filepos= block_info.next_filepos;
1526     } while (left_length);
1527 
1528     info->update|= HA_STATE_AKTIV;	/* We have a aktive record */
1529     fast_mi_writeinfo(info);
1530     DBUG_RETURN(_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
1531 		MY_FILE_ERROR ? 0 : -1);
1532   }
1533   fast_mi_writeinfo(info);
1534   DBUG_RETURN(-1);			/* Wrong data to read */
1535 
1536 panic:
1537   set_my_errno(HA_ERR_WRONG_IN_RECORD);
1538 err:
1539   (void) _mi_writeinfo(info,0);
1540   DBUG_RETURN(-1);
1541 }
1542 
1543 	/* compare unique constraint between stored rows */
1544 
_mi_cmp_dynamic_unique(MI_INFO * info,MI_UNIQUEDEF * def,const uchar * record,my_off_t pos)1545 int _mi_cmp_dynamic_unique(MI_INFO *info, MI_UNIQUEDEF *def,
1546 			   const uchar *record, my_off_t pos)
1547 {
1548   uchar *rec_buff,*old_record;
1549   int error;
1550   DBUG_ENTER("_mi_cmp_dynamic_unique");
1551 
1552   if (!(old_record= my_malloc(mi_key_memory_record_buffer,
1553                               info->s->base.reclength,
1554                               MYF(0))))
1555     DBUG_RETURN(1);
1556 
1557   /* Don't let the compare destroy blobs that may be in use */
1558   rec_buff=info->rec_buff;
1559   if (info->s->base.blobs)
1560     info->rec_buff=0;
1561   error=_mi_read_dynamic_record(info,pos,old_record);
1562   if (!error)
1563     error=mi_unique_comp(def, record, old_record, def->null_are_equal);
1564   if (info->s->base.blobs)
1565   {
1566     my_free(mi_get_rec_buff_ptr(info, info->rec_buff));
1567     info->rec_buff=rec_buff;
1568   }
1569   my_free(old_record);
1570   DBUG_RETURN(error);
1571 }
1572 
1573 
1574 	/* Compare of record one disk with packed record in memory */
1575 
_mi_cmp_dynamic_record(MI_INFO * info,const uchar * record)1576 int _mi_cmp_dynamic_record(MI_INFO *info, const uchar *record)
1577 {
1578   uint flag,reclength,b_type;
1579   my_off_t filepos;
1580   uchar *buffer;
1581   MI_BLOCK_INFO block_info;
1582   DBUG_ENTER("_mi_cmp_dynamic_record");
1583 
1584   if (info->opt_flag & WRITE_CACHE_USED)
1585   {
1586     info->update&= ~(HA_STATE_WRITE_AT_END | HA_STATE_EXTEND_BLOCK);
1587     if (flush_io_cache(&info->rec_cache))
1588       DBUG_RETURN(-1);
1589   }
1590   info->rec_cache.seek_not_done=1;
1591 
1592 	/* If nobody have touched the database we don't have to test rec */
1593 
1594   buffer=info->rec_buff;
1595   if ((info->opt_flag & READ_CHECK_USED))
1596   {						/* If check isn't disabled  */
1597     if (info->s->base.blobs)
1598     {
1599       if (!(buffer=(uchar*) my_malloc(mi_key_memory_record_buffer,
1600                                       info->s->base.pack_reclength+
1601 				      _my_calc_total_blob_length(info,record),
1602                                       MYF(0))))
1603 	DBUG_RETURN(-1);
1604     }
1605     reclength=_mi_rec_pack(info,buffer,record);
1606     record= buffer;
1607 
1608     filepos=info->lastpos;
1609     flag=block_info.second_read=0;
1610     block_info.next_filepos=filepos;
1611     while (reclength > 0)
1612     {
1613       if ((b_type=_mi_get_block_info(&block_info,info->dfile,
1614 				    block_info.next_filepos))
1615 	  & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
1616 	     BLOCK_FATAL_ERROR))
1617       {
1618 	if (b_type & (BLOCK_SYNC_ERROR | BLOCK_DELETED))
1619 	  set_my_errno(HA_ERR_RECORD_CHANGED);
1620 	goto err;
1621       }
1622       if (flag == 0)				/* First block */
1623       {
1624 	flag=1;
1625 	if (reclength != block_info.rec_len)
1626 	{
1627 	  set_my_errno(HA_ERR_RECORD_CHANGED);
1628 	  goto err;
1629 	}
1630       } else if (reclength < block_info.data_len)
1631       {
1632 	set_my_errno(HA_ERR_WRONG_IN_RECORD);
1633 	goto err;
1634       }
1635       reclength-=block_info.data_len;
1636       if (_mi_cmp_buffer(info->dfile,record,block_info.filepos,
1637 			 block_info.data_len))
1638       {
1639 	set_my_errno(HA_ERR_RECORD_CHANGED);
1640 	goto err;
1641       }
1642       flag=1;
1643       record+=block_info.data_len;
1644     }
1645   }
1646   set_my_errno(0);
1647 err:
1648   if (buffer != info->rec_buff)
1649     my_free((uchar*) buffer);
1650   DBUG_RETURN(my_errno());
1651 }
1652 
1653 
1654 	/* Compare file to buffert */
1655 
_mi_cmp_buffer(File file,const uchar * buff,my_off_t filepos,uint length)1656 static int _mi_cmp_buffer(File file, const uchar *buff, my_off_t filepos,
1657 			  uint length)
1658 {
1659   uint next_length;
1660   uchar temp_buff[IO_SIZE*2];
1661   DBUG_ENTER("_mi_cmp_buffer");
1662 
1663   next_length= IO_SIZE*2 - (uint) (filepos & (IO_SIZE-1));
1664 
1665   while (length > IO_SIZE*2)
1666   {
1667     if (mysql_file_pread(file, temp_buff, next_length, filepos, MYF(MY_NABP)) ||
1668 	memcmp(buff, temp_buff, next_length))
1669       goto err;
1670     filepos+=next_length;
1671     buff+=next_length;
1672     length-= next_length;
1673     next_length=IO_SIZE*2;
1674   }
1675   if (mysql_file_pread(file, temp_buff, length, filepos, MYF(MY_NABP)))
1676     goto err;
1677   DBUG_RETURN(memcmp(buff,temp_buff,length));
1678 err:
1679   DBUG_RETURN(1);
1680 }
1681 
1682 
1683 /*
1684   Read record from datafile.
1685 
1686   SYNOPSIS
1687     _mi_read_rnd_dynamic_record()
1688       info                      MI_INFO pointer to table.
1689       buf                       Destination for record.
1690       filepos                   From where to read the record.
1691       skip_deleted_blocks       If to repeat reading until a non-deleted
1692                                 record is found.
1693 
1694   NOTE
1695 
1696     If a write buffer is active, it needs to be flushed if its contents
1697     intersects with the record to read. We always check if the position
1698     of the first byte of the write buffer is lower than the position
1699     past the last byte to read. In theory this is also true if the write
1700     buffer is completely below the read segment. That is, if there is no
1701     intersection. But this case is unusual. We flush anyway. Only if the
1702     first byte in the write buffer is above the last byte to read, we do
1703     not flush.
1704 
1705     A dynamic record may need several reads. So this check must be done
1706     before every read. Reading a dynamic record starts with reading the
1707     block header. If the record does not fit into the free space of the
1708     header, the block may be longer than the header. In this case a
1709     second read is necessary. These one or two reads repeat for every
1710     part of the record.
1711 
1712   RETURN
1713     0           OK
1714     != 0        Error
1715 */
1716 
_mi_read_rnd_dynamic_record(MI_INFO * info,uchar * buf,my_off_t filepos,my_bool skip_deleted_blocks)1717 int _mi_read_rnd_dynamic_record(MI_INFO *info, uchar *buf,
1718 				my_off_t filepos,
1719 				my_bool skip_deleted_blocks)
1720 {
1721   int block_of_record, info_read, save_errno;
1722   uint left_len,b_type;
1723   uchar *to= NULL;
1724   MI_BLOCK_INFO block_info;
1725   MYISAM_SHARE *share=info->s;
1726   DBUG_ENTER("_mi_read_rnd_dynamic_record");
1727 
1728   DBUG_EXECUTE_IF("catch_file_offset_deviation",
1729 		 {
1730                    if (filepos)
1731 		     DBUG_RETURN(HA_ERR_RECORD_DELETED);
1732                  });
1733 
1734   info_read=0;
1735 
1736   if (info->lock_type == F_UNLCK)
1737   {
1738     if (share->tot_locks == 0)
1739     {
1740       if (my_lock(share->kfile,F_RDLCK,0L,F_TO_EOF,
1741 		  MYF(MY_SEEK_NOT_DONE) | info->lock_wait))
1742 	DBUG_RETURN(my_errno());
1743     }
1744   }
1745   else
1746     info_read=1;				/* memory-keyinfoblock is ok */
1747 
1748   block_of_record= 0;   /* First block of record is numbered as zero. */
1749   block_info.second_read= 0;
1750   left_len=1;
1751   do
1752   {
1753     if (filepos >= info->state->data_file_length)
1754     {
1755       if (!info_read)
1756       {						/* Check if changed */
1757 	info_read=1;
1758 	info->rec_cache.seek_not_done=1;
1759 	if (mi_state_info_read_dsk(share->kfile,&share->state,1))
1760 	  goto panic;
1761       }
1762       if (filepos >= info->state->data_file_length)
1763       {
1764 	set_my_errno(HA_ERR_END_OF_FILE);
1765 	goto err;
1766       }
1767     }
1768     if (info->opt_flag & READ_CACHE_USED)
1769     {
1770       if (_mi_read_cache(&info->rec_cache,(uchar*) block_info.header,filepos,
1771 			 sizeof(block_info.header),
1772 			 (!block_of_record && skip_deleted_blocks ?
1773                           READING_NEXT : 0) | READING_HEADER))
1774 	goto panic;
1775       b_type=_mi_get_block_info(&block_info,-1,filepos);
1776     }
1777     else
1778     {
1779       if (info->opt_flag & WRITE_CACHE_USED &&
1780 	  info->rec_cache.pos_in_file < filepos + MI_BLOCK_INFO_HEADER_LENGTH &&
1781 	  flush_io_cache(&info->rec_cache))
1782 	DBUG_RETURN(my_errno());
1783       info->rec_cache.seek_not_done=1;
1784       b_type=_mi_get_block_info(&block_info,info->dfile,filepos);
1785     }
1786 
1787     if (b_type & (BLOCK_DELETED | BLOCK_ERROR | BLOCK_SYNC_ERROR |
1788 		  BLOCK_FATAL_ERROR))
1789     {
1790       if ((b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
1791 	  && skip_deleted_blocks)
1792       {
1793 	filepos=block_info.filepos+block_info.block_len;
1794 	block_info.second_read=0;
1795 	continue;		/* Search after next_record */
1796       }
1797       if (b_type & (BLOCK_DELETED | BLOCK_SYNC_ERROR))
1798       {
1799         /*
1800           If we're not on the first block of a record and
1801           the block is marked as deleted or out of sync,
1802           something's gone wrong: the record is damaged.
1803         */
1804         if (block_of_record != 0)
1805           goto panic;
1806 
1807 	set_my_errno(HA_ERR_RECORD_DELETED);
1808 	info->lastpos=block_info.filepos;
1809 	info->nextpos=block_info.filepos+block_info.block_len;
1810       }
1811       goto err;
1812     }
1813     if (block_of_record == 0)				/* First block */
1814     {
1815       if (block_info.rec_len > (uint) share->base.max_pack_length)
1816 	goto panic;
1817       info->lastpos=filepos;
1818       if (share->base.blobs)
1819       {
1820 	if (!(to= mi_alloc_rec_buff(info, block_info.rec_len,
1821 				    &info->rec_buff)))
1822 	  goto err;
1823       }
1824       else
1825 	to= info->rec_buff;
1826       left_len=block_info.rec_len;
1827     }
1828     if (left_len < block_info.data_len)
1829       goto panic;				/* Wrong linked record */
1830 
1831     /* copy information that is already read */
1832     {
1833       uint offset=(uint) (block_info.filepos - filepos);
1834       uint tmp_length= (sizeof(block_info.header) - offset);
1835       filepos=block_info.filepos;
1836 
1837       if (tmp_length > block_info.data_len)
1838 	tmp_length= block_info.data_len;
1839       if (tmp_length)
1840       {
1841 	memcpy((uchar*) to, block_info.header+offset,tmp_length);
1842 	block_info.data_len-=tmp_length;
1843 	left_len-=tmp_length;
1844 	to+=tmp_length;
1845 	filepos+=tmp_length;
1846       }
1847     }
1848     /* read rest of record from file */
1849     if (block_info.data_len)
1850     {
1851       if (info->opt_flag & READ_CACHE_USED)
1852       {
1853 	if (_mi_read_cache(&info->rec_cache,(uchar*) to,filepos,
1854 			   block_info.data_len,
1855 			   (!block_of_record && skip_deleted_blocks) ?
1856                            READING_NEXT : 0))
1857 	  goto panic;
1858       }
1859       else
1860       {
1861         if (info->opt_flag & WRITE_CACHE_USED &&
1862             info->rec_cache.pos_in_file <
1863             block_info.filepos + block_info.data_len &&
1864             flush_io_cache(&info->rec_cache))
1865           goto err;
1866         /* mysql_file_seek(info->dfile, filepos, MY_SEEK_SET, MYF(0)); */
1867         if (mysql_file_read(info->dfile, (uchar*) to, block_info.data_len,
1868                             MYF(MY_NABP)))
1869 	{
1870 	  if (my_errno() == -1)
1871 	    set_my_errno(HA_ERR_WRONG_IN_RECORD);	/* Unexpected end of file */
1872 	  goto err;
1873 	}
1874       }
1875     }
1876     /*
1877       Increment block-of-record counter. If it was the first block,
1878       remember the position behind the block for the next call.
1879     */
1880     if (block_of_record++ == 0)
1881     {
1882       info->nextpos= block_info.filepos + block_info.block_len;
1883       skip_deleted_blocks= 0;
1884     }
1885     left_len-=block_info.data_len;
1886     to+=block_info.data_len;
1887     filepos=block_info.next_filepos;
1888   } while (left_len);
1889 
1890   info->update|= HA_STATE_AKTIV | HA_STATE_KEY_CHANGED;
1891   fast_mi_writeinfo(info);
1892   if (_mi_rec_unpack(info,buf,info->rec_buff,block_info.rec_len) !=
1893       MY_FILE_ERROR)
1894     DBUG_RETURN(0);
1895   DBUG_RETURN(my_errno());			/* Wrong record */
1896 
1897 panic:
1898   set_my_errno(HA_ERR_WRONG_IN_RECORD);		/* Something is fatal wrong */
1899 err:
1900   save_errno=my_errno();
1901   (void) _mi_writeinfo(info,0);
1902   set_my_errno(save_errno);
1903   DBUG_RETURN(save_errno);
1904 }
1905 
1906 
1907 	/* Read and process header from a dynamic-record-file */
1908 
_mi_get_block_info(MI_BLOCK_INFO * info,File file,my_off_t filepos)1909 uint _mi_get_block_info(MI_BLOCK_INFO *info, File file, my_off_t filepos)
1910 {
1911   uint return_val=0;
1912   uchar *header=info->header;
1913 
1914   if (file >= 0)
1915   {
1916     /*
1917       We do not use mysql_file_pread() here because we want to have the file
1918       pointer set to the end of the header after this function.
1919       mysql_file_pread() may leave the file pointer untouched.
1920     */
1921     mysql_file_seek(file, filepos, MY_SEEK_SET, MYF(0));
1922     if (mysql_file_read(file, header, sizeof(info->header), MYF(0)) !=
1923 	sizeof(info->header))
1924       goto err;
1925   }
1926   DBUG_DUMP("header",header,MI_BLOCK_INFO_HEADER_LENGTH);
1927   if (info->second_read)
1928   {
1929     if (info->header[0] <= 6 || info->header[0] == 13)
1930       return_val=BLOCK_SYNC_ERROR;
1931   }
1932   else
1933   {
1934     if (info->header[0] > 6 && info->header[0] != 13)
1935       return_val=BLOCK_SYNC_ERROR;
1936   }
1937   info->next_filepos= HA_OFFSET_ERROR; /* Dummy if no next block */
1938 
1939   switch (info->header[0]) {
1940   case 0:
1941     if ((info->block_len=(uint) mi_uint3korr(header+1)) <
1942 	MI_MIN_BLOCK_LENGTH ||
1943 	(info->block_len & (MI_DYN_ALIGN_SIZE -1)))
1944       goto err;
1945     info->filepos=filepos;
1946     info->next_filepos=mi_sizekorr(header+4);
1947     info->prev_filepos=mi_sizekorr(header+12);
1948 #if SIZEOF_OFF_T == 4
1949     if ((mi_uint4korr(header+4) != 0 &&
1950 	 (mi_uint4korr(header+4) != (ulong) ~0 ||
1951 	  info->next_filepos != (ulong) ~0)) ||
1952 	(mi_uint4korr(header+12) != 0 &&
1953 	 (mi_uint4korr(header+12) != (ulong) ~0 ||
1954 	  info->prev_filepos != (ulong) ~0)))
1955       goto err;
1956 #endif
1957     return return_val | BLOCK_DELETED;		/* Deleted block */
1958 
1959   case 1:
1960     info->rec_len=info->data_len=info->block_len=mi_uint2korr(header+1);
1961     info->filepos=filepos+3;
1962     return return_val | BLOCK_FIRST | BLOCK_LAST;
1963   case 2:
1964     info->rec_len=info->data_len=info->block_len=mi_uint3korr(header+1);
1965     info->filepos=filepos+4;
1966     return return_val | BLOCK_FIRST | BLOCK_LAST;
1967 
1968   case 13:
1969     info->rec_len=mi_uint4korr(header+1);
1970     info->block_len=info->data_len=mi_uint3korr(header+5);
1971     info->next_filepos=mi_sizekorr(header+8);
1972     info->second_read=1;
1973     info->filepos=filepos+16;
1974     return return_val | BLOCK_FIRST;
1975 
1976   case 3:
1977     info->rec_len=info->data_len=mi_uint2korr(header+1);
1978     info->block_len=info->rec_len+ (uint) header[3];
1979     info->filepos=filepos+4;
1980     return return_val | BLOCK_FIRST | BLOCK_LAST;
1981   case 4:
1982     info->rec_len=info->data_len=mi_uint3korr(header+1);
1983     info->block_len=info->rec_len+ (uint) header[4];
1984     info->filepos=filepos+5;
1985     return return_val | BLOCK_FIRST | BLOCK_LAST;
1986 
1987   case 5:
1988     info->rec_len=mi_uint2korr(header+1);
1989     info->block_len=info->data_len=mi_uint2korr(header+3);
1990     info->next_filepos=mi_sizekorr(header+5);
1991     info->second_read=1;
1992     info->filepos=filepos+13;
1993     return return_val | BLOCK_FIRST;
1994   case 6:
1995     info->rec_len=mi_uint3korr(header+1);
1996     info->block_len=info->data_len=mi_uint3korr(header+4);
1997     info->next_filepos=mi_sizekorr(header+7);
1998     info->second_read=1;
1999     info->filepos=filepos+15;
2000     return return_val | BLOCK_FIRST;
2001 
2002     /* The following blocks are identical to 1-6 without rec_len */
2003   case 7:
2004     info->data_len=info->block_len=mi_uint2korr(header+1);
2005     info->filepos=filepos+3;
2006     return return_val | BLOCK_LAST;
2007   case 8:
2008     info->data_len=info->block_len=mi_uint3korr(header+1);
2009     info->filepos=filepos+4;
2010     return return_val | BLOCK_LAST;
2011 
2012   case 9:
2013     info->data_len=mi_uint2korr(header+1);
2014     info->block_len=info->data_len+ (uint) header[3];
2015     info->filepos=filepos+4;
2016     return return_val | BLOCK_LAST;
2017   case 10:
2018     info->data_len=mi_uint3korr(header+1);
2019     info->block_len=info->data_len+ (uint) header[4];
2020     info->filepos=filepos+5;
2021     return return_val | BLOCK_LAST;
2022 
2023   case 11:
2024     info->data_len=info->block_len=mi_uint2korr(header+1);
2025     info->next_filepos=mi_sizekorr(header+3);
2026     info->second_read=1;
2027     info->filepos=filepos+11;
2028     return return_val;
2029   case 12:
2030     info->data_len=info->block_len=mi_uint3korr(header+1);
2031     info->next_filepos=mi_sizekorr(header+4);
2032     info->second_read=1;
2033     info->filepos=filepos+12;
2034     return return_val;
2035   }
2036 
2037 err:
2038   set_my_errno(HA_ERR_WRONG_IN_RECORD);	 /* Garbage */
2039   return BLOCK_ERROR;
2040 }
2041