1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2001-2012 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2018 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /*
24  * Kern Sibbald, March MMI
25  * added BB02 format October MMII
26  */
27 /**
28  @file
29  * tape block handling functions
30  */
31 
32 #include "include/bareos.h"
33 #include "stored/stored.h"
34 #include "stored/crc32/crc32.h"
35 #include "stored/dev.h"
36 #include "stored/device.h"
37 #include "stored/label.h"
38 #include "stored/socket_server.h"
39 #include "stored/spool.h"
40 #include "lib/berrno.h"
41 #include "lib/edit.h"
42 #include "include/jcr.h"
43 
44 namespace storagedaemon {
45 
46 static bool TerminateWritingVolume(DeviceControlRecord* dcr);
47 static bool DoNewFileBookkeeping(DeviceControlRecord* dcr);
48 static void RereadLastBlock(DeviceControlRecord* dcr);
49 
50 bool forge_on = false; /* proceed inspite of I/O errors */
51 
52 /**
53  * Dump the block header, then walk through
54  * the block printing out the record headers.
55  */
DumpBlock(DeviceBlock * b,const char * msg)56 void DumpBlock(DeviceBlock* b, const char* msg)
57 {
58   ser_declare;
59   char* p;
60   char Id[BLKHDR_ID_LENGTH + 1];
61   uint32_t CheckSum, BlockCheckSum;
62   uint32_t block_len;
63   uint32_t BlockNumber;
64   uint32_t VolSessionId, VolSessionTime, data_len;
65   int32_t FileIndex;
66   int32_t Stream;
67   int bhl, rhl;
68   char buf1[100], buf2[100];
69 
70   UnserBegin(b->buf, BLKHDR1_LENGTH);
71   unser_uint32(CheckSum);
72   unser_uint32(block_len);
73   unser_uint32(BlockNumber);
74   UnserBytes(Id, BLKHDR_ID_LENGTH);
75   ASSERT(UnserLength(b->buf) == BLKHDR1_LENGTH);
76   Id[BLKHDR_ID_LENGTH] = 0;
77   if (Id[3] == '2') {
78     unser_uint32(VolSessionId);
79     unser_uint32(VolSessionTime);
80     bhl = BLKHDR2_LENGTH;
81     rhl = RECHDR2_LENGTH;
82   } else {
83     VolSessionId = VolSessionTime = 0;
84     bhl = BLKHDR1_LENGTH;
85     rhl = RECHDR1_LENGTH;
86   }
87 
88   if (block_len > 4000000) {
89     Dmsg3(20, "Dump block %s 0x%x blocksize too big %u\n", msg, b, block_len);
90     return;
91   }
92 
93   BlockCheckSum =
94       crc32_fast((uint8_t*)b->buf + BLKHDR_CS_LENGTH, block_len - BLKHDR_CS_LENGTH);
95   Pmsg6(000,
96         _("Dump block %s %x: size=%d BlkNum=%d\n"
97           "               Hdrcksum=%x cksum=%x\n"),
98         msg, b, block_len, BlockNumber, CheckSum, BlockCheckSum);
99   p = b->buf + bhl;
100   while (p < (b->buf + block_len + WRITE_RECHDR_LENGTH)) {
101     UnserBegin(p, WRITE_RECHDR_LENGTH);
102     if (rhl == RECHDR1_LENGTH) {
103       unser_uint32(VolSessionId);
104       unser_uint32(VolSessionTime);
105     }
106     unser_int32(FileIndex);
107     unser_int32(Stream);
108     unser_uint32(data_len);
109     Pmsg6(000, _("   Rec: VId=%u VT=%u FI=%s Strm=%s len=%d p=%x\n"),
110           VolSessionId, VolSessionTime, FI_to_ascii(buf1, FileIndex),
111           stream_to_ascii(buf2, Stream, FileIndex), data_len, p);
112     p += data_len + rhl;
113   }
114 }
115 
116 /**
117  * Create a new block structure.
118  * We pass device so that the block can inherit the
119  * min and max block sizes.
120  */
new_block(Device * dev)121 DeviceBlock* new_block(Device* dev)
122 {
123   DeviceBlock* block = (DeviceBlock*)GetMemory(sizeof(DeviceBlock));
124 
125   memset(block, 0, sizeof(DeviceBlock));
126 
127   if (dev->max_block_size == 0) {
128     block->buf_len = dev->device->label_block_size;
129     Dmsg1(100,
130           "created new block of blocksize %d (dev->device->label_block_size) "
131           "as dev->max_block_size is zero\n",
132           block->buf_len);
133   } else {
134     block->buf_len = dev->max_block_size;
135     Dmsg1(100, "created new block of blocksize %d (dev->max_block_size)\n",
136           block->buf_len);
137   }
138   block->dev = dev;
139   block->block_len = block->buf_len; /* default block size */
140   block->buf = GetMemory(block->buf_len);
141   EmptyBlock(block);
142   block->BlockVer = BLOCK_VER; /* default write version */
143   Dmsg1(650, "Returning new block=%x\n", block);
144   return block;
145 }
146 
147 /**
148  * Duplicate an existing block (eblock)
149  */
dup_block(DeviceBlock * eblock)150 DeviceBlock* dup_block(DeviceBlock* eblock)
151 {
152   DeviceBlock* block = (DeviceBlock*)GetMemory(sizeof(DeviceBlock));
153   int buf_len = SizeofPoolMemory(eblock->buf);
154 
155   memcpy(block, eblock, sizeof(DeviceBlock));
156   block->buf = GetMemory(buf_len);
157   memcpy(block->buf, eblock->buf, buf_len);
158   return block;
159 }
160 
161 /**
162  * Only the first block checksum error was reported.
163  *   If there are more, report it now.
164  */
PrintBlockReadErrors(JobControlRecord * jcr,DeviceBlock * block)165 void PrintBlockReadErrors(JobControlRecord* jcr, DeviceBlock* block)
166 {
167   if (block->read_errors > 1) {
168     Jmsg(jcr, M_ERROR, 0, _("%d block read errors not printed.\n"),
169          block->read_errors);
170   }
171 }
172 
173 /**
174  * Free block
175  */
FreeBlock(DeviceBlock * block)176 void FreeBlock(DeviceBlock* block)
177 {
178   if (block) {
179     Dmsg1(999, "FreeBlock buffer %x\n", block->buf);
180     FreeMemory(block->buf);
181     Dmsg1(999, "FreeBlock block %x\n", block);
182     FreeMemory((POOLMEM*)block);
183   }
184 }
185 
186 /**
187  * Empty the block -- for writing
188  */
EmptyBlock(DeviceBlock * block)189 void EmptyBlock(DeviceBlock* block)
190 {
191   block->binbuf = WRITE_BLKHDR_LENGTH;
192   block->bufp = block->buf + block->binbuf;
193   block->read_len = 0;
194   block->write_failed = false;
195   block->block_read = false;
196   block->FirstIndex = block->LastIndex = 0;
197 }
198 
199 /**
200  * Create block header just before write. The space
201  * in the buffer should have already been reserved by
202  * init_block.
203  */
SerBlockHeader(DeviceBlock * block,bool DoChecksum)204 static uint32_t SerBlockHeader(DeviceBlock* block, bool DoChecksum)
205 {
206   ser_declare;
207   uint32_t CheckSum = 0;
208   uint32_t block_len = block->binbuf;
209 
210   Dmsg1(1390, "SerBlockHeader: block_len=%d\n", block_len);
211   SerBegin(block->buf, BLKHDR2_LENGTH);
212   ser_uint32(CheckSum);
213   ser_uint32(block_len);
214   ser_uint32(block->BlockNumber);
215   SerBytes(WRITE_BLKHDR_ID, BLKHDR_ID_LENGTH);
216   if (BLOCK_VER >= 2) {
217     ser_uint32(block->VolSessionId);
218     ser_uint32(block->VolSessionTime);
219   }
220 
221   /*
222    * Checksum whole block except for the checksum
223    */
224   if (DoChecksum) {
225     CheckSum = crc32_fast((uint8_t*)block->buf + BLKHDR_CS_LENGTH,
226                       block_len - BLKHDR_CS_LENGTH);
227   }
228   Dmsg1(1390, "ser_bloc_header: checksum=%x\n", CheckSum);
229   SerBegin(block->buf, BLKHDR2_LENGTH);
230   ser_uint32(CheckSum); /* now add checksum to block header */
231   return CheckSum;
232 }
233 
234 /**
235  * UnSerialize the block header for reading block.
236  * This includes setting all the buffer pointers correctly.
237  *
238  * Returns: false on failure (not a block)
239  *          true  on success
240  */
unSerBlockHeader(JobControlRecord * jcr,Device * dev,DeviceBlock * block)241 static inline bool unSerBlockHeader(JobControlRecord* jcr,
242                                     Device* dev,
243                                     DeviceBlock* block)
244 {
245   ser_declare;
246   char Id[BLKHDR_ID_LENGTH + 1];
247   uint32_t CheckSum, BlockCheckSum;
248   uint32_t block_len;
249   uint32_t block_end;
250   uint32_t BlockNumber;
251   int bhl;
252 
253   UnserBegin(block->buf, BLKHDR_LENGTH);
254   unser_uint32(CheckSum);
255   unser_uint32(block_len);
256   unser_uint32(BlockNumber);
257   UnserBytes(Id, BLKHDR_ID_LENGTH);
258   ASSERT(UnserLength(block->buf) == BLKHDR1_LENGTH);
259 
260   Id[BLKHDR_ID_LENGTH] = 0;
261   if (Id[3] == '1') {
262     bhl = BLKHDR1_LENGTH;
263     block->BlockVer = 1;
264     block->bufp = block->buf + bhl;
265     if (!bstrncmp(Id, BLKHDR1_ID, BLKHDR_ID_LENGTH)) {
266       dev->dev_errno = EIO;
267       Mmsg4(dev->errmsg,
268             _("Volume data error at %u:%u! Wanted ID: \"%s\", got \"%s\". "
269               "Buffer discarded.\n"),
270             dev->file, dev->block_num, BLKHDR1_ID, Id);
271       if (block->read_errors == 0 || verbose >= 2) {
272         Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
273       }
274       block->read_errors++;
275       return false;
276     }
277   } else if (Id[3] == '2') {
278     unser_uint32(block->VolSessionId);
279     unser_uint32(block->VolSessionTime);
280     bhl = BLKHDR2_LENGTH;
281     block->BlockVer = 2;
282     block->bufp = block->buf + bhl;
283     if (!bstrncmp(Id, BLKHDR2_ID, BLKHDR_ID_LENGTH)) {
284       dev->dev_errno = EIO;
285       Mmsg4(dev->errmsg,
286             _("Volume data error at %u:%u! Wanted ID: \"%s\", got \"%s\". "
287               "Buffer discarded.\n"),
288             dev->file, dev->block_num, BLKHDR2_ID, Id);
289       if (block->read_errors == 0 || verbose >= 2) {
290         Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
291       }
292       block->read_errors++;
293       return false;
294     }
295   } else {
296     dev->dev_errno = EIO;
297     Mmsg4(dev->errmsg,
298           _("Volume data error at %u:%u! Wanted ID: \"%s\", got \"%s\". Buffer "
299             "discarded.\n"),
300           dev->file, dev->block_num, BLKHDR2_ID, Id);
301     Dmsg1(50, "%s", dev->errmsg);
302     if (block->read_errors == 0 || verbose >= 2) {
303       Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
304     }
305     block->read_errors++;
306     unser_uint32(block->VolSessionId);
307     unser_uint32(block->VolSessionTime);
308     return false;
309   }
310 
311   /*
312    * Sanity check
313    */
314   if (block_len > MAX_BLOCK_LENGTH) {
315     dev->dev_errno = EIO;
316     Mmsg3(dev->errmsg,
317           _("Volume data error at %u:%u! Block length %u is insane (too "
318             "large), probably due to a bad archive.\n"),
319           dev->file, dev->block_num, block_len);
320     if (block->read_errors == 0 || verbose >= 2) {
321       Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
322     }
323     block->read_errors++;
324     return false;
325   }
326 
327   Dmsg1(390, "unSerBlockHeader block_len=%d\n", block_len);
328   /*
329    * Find end of block or end of buffer whichever is smaller
330    */
331   if (block_len > block->read_len) {
332     block_end = block->read_len;
333   } else {
334     block_end = block_len;
335   }
336   block->binbuf = block_end - bhl;
337   block->block_len = block_len;
338   block->BlockNumber = BlockNumber;
339   Dmsg3(390, "Read binbuf = %d %d block_len=%d\n", block->binbuf, bhl,
340         block_len);
341   if (block_len <= block->read_len && dev->DoChecksum()) {
342     BlockCheckSum = crc32_fast((uint8_t*)block->buf + BLKHDR_CS_LENGTH,
343                            block_len - BLKHDR_CS_LENGTH);
344     if (BlockCheckSum != CheckSum) {
345       dev->dev_errno = EIO;
346       Mmsg6(dev->errmsg,
347             _("Volume data error at %u:%u!\n"
348               "Block checksum mismatch in block=%u len=%d: calc=%x blk=%x\n"),
349             dev->file, dev->block_num, (unsigned)BlockNumber, block_len,
350             BlockCheckSum, CheckSum);
351       if (block->read_errors == 0 || verbose >= 2) {
352         Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
353         DumpBlock(block, "with checksum error");
354       }
355       block->read_errors++;
356       if (!forge_on) { return false; }
357     }
358   }
359   return true;
360 }
361 
RereadLastBlock(DeviceControlRecord * dcr)362 static void RereadLastBlock(DeviceControlRecord* dcr)
363 {
364 #define CHECK_LAST_BLOCK
365 #ifdef CHECK_LAST_BLOCK
366   bool ok = true;
367   Device* dev = dcr->dev;
368   JobControlRecord* jcr = dcr->jcr;
369   DeviceBlock* block = dcr->block;
370   /*
371    * If the device is a tape and it supports backspace record,
372    *   we backspace over one or two eof marks depending on
373    *   how many we just wrote, then over the last record,
374    *   then re-read it and verify that the block number is
375    *   correct.
376    */
377   if (dev->IsTape() && dev->HasCap(CAP_BSR)) {
378     /*
379      * Now back up over what we wrote and read the last block
380      */
381     if (!dev->bsf(1)) {
382       BErrNo be;
383       ok = false;
384       Jmsg(jcr, M_ERROR, 0, _("Backspace file at EOT failed. ERR=%s\n"),
385            be.bstrerror(dev->dev_errno));
386     }
387     if (ok && dev->HasCap(CAP_TWOEOF) && !dev->bsf(1)) {
388       BErrNo be;
389       ok = false;
390       Jmsg(jcr, M_ERROR, 0, _("Backspace file at EOT failed. ERR=%s\n"),
391            be.bstrerror(dev->dev_errno));
392     }
393     /*
394      * Backspace over record
395      */
396     if (ok && !dev->bsr(1)) {
397       BErrNo be;
398       ok = false;
399       Jmsg(jcr, M_ERROR, 0, _("Backspace record at EOT failed. ERR=%s\n"),
400            be.bstrerror(dev->dev_errno));
401       /*
402        *  On FreeBSD systems, if the user got here, it is likely that his/her
403        *    tape drive is "frozen".  The correct thing to do is a
404        *    rewind(), but if we do that, higher levels in cleaning up, will
405        *    most likely write the EOS record over the beginning of the
406        *    tape.  The rewind *is* done later in mount.c when another
407        *    tape is requested. Note, the clrerror() call in bsr()
408        *    calls ioctl(MTCERRSTAT), which *should* fix the problem.
409        */
410     }
411     if (ok) {
412       DeviceBlock* lblock = new_block(dev);
413       /*
414        * Note, this can destroy dev->errmsg
415        */
416       dcr->block = lblock;
417       if (DeviceControlRecord::ReadStatus::Ok !=
418           dcr->ReadBlockFromDev(NO_BLOCK_NUMBER_CHECK)) {
419         Jmsg(jcr, M_ERROR, 0, _("Re-read last block at EOT failed. ERR=%s"),
420              dev->errmsg);
421       } else {
422         /*
423          * If we wrote block and the block numbers don't agree
424          *  we have a possible problem.
425          */
426         if (lblock->BlockNumber != dev->LastBlock) {
427           if (dev->LastBlock > (lblock->BlockNumber + 1)) {
428             Jmsg(jcr, M_FATAL, 0,
429                  _("Re-read of last block: block numbers differ by more than "
430                    "one.\n"
431                    "Probable tape misconfiguration and data loss. Read "
432                    "block=%u Want block=%u.\n"),
433                  lblock->BlockNumber, dev->LastBlock);
434           } else {
435             Jmsg(jcr, M_ERROR, 0,
436                  _("Re-read of last block OK, but block numbers differ. Read "
437                    "block=%u Want block=%u.\n"),
438                  lblock->BlockNumber, dev->LastBlock);
439           }
440         } else {
441           Jmsg(jcr, M_INFO, 0, _("Re-read of last block succeeded.\n"));
442         }
443       }
444       FreeBlock(lblock);
445       dcr->block = block;
446     }
447   }
448 #endif
449 }
450 
451 /**
452  * If this routine is called, we do our bookkeeping and
453  * then assure that the volume will not be written any more.
454  */
TerminateWritingVolume(DeviceControlRecord * dcr)455 static bool TerminateWritingVolume(DeviceControlRecord* dcr)
456 {
457   Device* dev = dcr->dev;
458   bool ok = true;
459 
460   /* Create a JobMedia record to indicated end of tape */
461   dev->VolCatInfo.VolCatFiles = dev->file;
462   if (!dcr->DirCreateJobmediaRecord(false)) {
463     Dmsg0(50, "Error from create JobMedia\n");
464     dev->dev_errno = EIO;
465     Mmsg2(dev->errmsg,
466           _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
467           dcr->getVolCatName(), dcr->jcr->Job);
468     Jmsg(dcr->jcr, M_FATAL, 0, "%s", dev->errmsg);
469     ok = false;
470   }
471   dcr->block->write_failed = true;
472   if (!dev->weof(1)) { /* end the tape */
473     dev->VolCatInfo.VolCatErrors++;
474     Jmsg(dcr->jcr, M_ERROR, 0,
475          _("Error writing final EOF to tape. This Volume may not be readable.\n"
476            "%s"),
477          dev->errmsg);
478     ok = false;
479     Dmsg0(50, "Error writing final EOF to volume.\n");
480   }
481   if (ok) {
482     ok = WriteAnsiIbmLabels(dcr, ANSI_EOV_LABEL, dev->VolHdr.VolumeName);
483   }
484   bstrncpy(dev->VolCatInfo.VolCatStatus, "Full",
485            sizeof(dev->VolCatInfo.VolCatStatus));
486   dev->VolCatInfo.VolCatFiles = dev->file; /* set number of files */
487 
488   if (!dcr->DirUpdateVolumeInfo(false, true)) {
489     Mmsg(dev->errmsg, _("Error sending Volume info to Director.\n"));
490     ok = false;
491     Dmsg0(50, "Error updating volume info.\n");
492   }
493   Dmsg1(50, "DirUpdateVolumeInfo Terminate writing -- %s\n",
494         ok ? "OK" : "ERROR");
495 
496   /*
497    * Walk through all attached dcrs setting flag to call
498    * SetNewFileParameters() when that dcr is next used.
499    */
500   DeviceControlRecord* mdcr;
501   foreach_dlist (mdcr, dev->attached_dcrs) {
502     if (mdcr->jcr->JobId == 0) { continue; }
503     mdcr->NewFile = true; /* set reminder to do set_new_file_params */
504   }
505   /*
506    * Set new file/block parameters for current dcr
507    */
508   SetNewFileParameters(dcr);
509 
510   if (ok && dev->HasCap(CAP_TWOEOF) && !dev->weof(1)) { /* end the tape */
511     dev->VolCatInfo.VolCatErrors++;
512     /*
513      * This may not be fatal since we already wrote an EOF
514      */
515     Jmsg(dcr->jcr, M_ERROR, 0, "%s", dev->errmsg);
516     Dmsg0(50, "Writing second EOF failed.\n");
517   }
518 
519   dev->SetAteot(); /* no more writing this tape */
520   Dmsg1(50, "*** Leave TerminateWritingVolume -- %s\n", ok ? "OK" : "ERROR");
521   return ok;
522 }
523 
524 /**
525  * Do bookkeeping when a new file is created on a Volume. This is
526  *  also done for disk files to generate the jobmedia records for
527  *  quick seeking.
528  */
DoNewFileBookkeeping(DeviceControlRecord * dcr)529 static bool DoNewFileBookkeeping(DeviceControlRecord* dcr)
530 {
531   Device* dev = dcr->dev;
532   JobControlRecord* jcr = dcr->jcr;
533 
534   /*
535    * Create a JobMedia record so restore can seek
536    */
537   if (!dcr->DirCreateJobmediaRecord(false)) {
538     Dmsg0(50, "Error from create_job_media.\n");
539     dev->dev_errno = EIO;
540     Jmsg2(jcr, M_FATAL, 0,
541           _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
542           dcr->getVolCatName(), jcr->Job);
543     TerminateWritingVolume(dcr);
544     dev->dev_errno = EIO;
545     return false;
546   }
547   dev->VolCatInfo.VolCatFiles = dev->file;
548   if (!dcr->DirUpdateVolumeInfo(false, false)) {
549     Dmsg0(50, "Error from update_vol_info.\n");
550     TerminateWritingVolume(dcr);
551     dev->dev_errno = EIO;
552     return false;
553   }
554   Dmsg0(100, "DirUpdateVolumeInfo max file size -- OK\n");
555 
556   /*
557    * Walk through all attached dcrs setting flag to call
558    * SetNewFileParameters() when that dcr is next used.
559    */
560   DeviceControlRecord* mdcr;
561   foreach_dlist (mdcr, dev->attached_dcrs) {
562     if (mdcr->jcr->JobId == 0) { continue; }
563     mdcr->NewFile = true; /* set reminder to do set_new_file_params */
564   }
565   /*
566    * Set new file/block parameters for current dcr
567    */
568   SetNewFileParameters(dcr);
569   return true;
570 }
571 
572 #ifdef DEBUG_BLOCK_CHECKSUM
573 static const bool debug_block_checksum = true;
574 #else
575 static const bool debug_block_checksum = false;
576 #endif
577 
578 #ifdef NO_TAPE_WRITE_TEST
579 static const bool no_tape_write_test = true;
580 #else
581 static const bool no_tape_write_test = false;
582 #endif
583 
584 /**
585  * Write a block to the device
586  *
587  * Returns: true  on success or EOT
588  *          false on hard error
589  */
WriteBlockToDev()590 bool DeviceControlRecord::WriteBlockToDev()
591 {
592   ssize_t status = 0;
593   uint32_t wlen; /* length to write */
594   int hit_max1, hit_max2;
595   bool ok = true;
596   DeviceControlRecord* dcr = this;
597   uint32_t checksum;
598 
599   if (no_tape_write_test) {
600     EmptyBlock(block);
601     return true;
602   }
603   if (JobCanceled(jcr)) {
604     Dmsg0(100, "return WriteBlockToDev, job is canceled\n");
605     return false;
606   }
607 
608   ASSERT(block->binbuf == ((uint32_t)(block->bufp - block->buf)));
609 
610   wlen = block->binbuf;
611   if (wlen <= WRITE_BLKHDR_LENGTH) { /* Does block have data in it? */
612     Dmsg0(100, "return WriteBlockToDev no data to write\n");
613     return true;
614   }
615 
616   /* DumpBlock(block, "before write"); */
617   if (dev->AtWeot()) {
618     Dmsg0(100, "return WriteBlockToDev with ST_WEOT\n");
619     dev->dev_errno = ENOSPC;
620     Jmsg1(jcr, M_FATAL, 0, _("Cannot write block. Device at EOM. dev=%s\n"),
621           dev->print_name());
622     Dmsg1(100, "Attempt to write on read-only Volume. dev=%s\n",
623           dev->print_name());
624     return false;
625   }
626   if (!dev->CanAppend()) {
627     dev->dev_errno = EIO;
628     Jmsg1(jcr, M_FATAL, 0, _("Attempt to write on read-only Volume. dev=%s\n"),
629           dev->print_name());
630     Dmsg1(100, "Attempt to write on read-only Volume. dev=%s\n",
631           dev->print_name());
632     return false;
633   }
634 
635   if (!dev->IsOpen()) {
636     Jmsg1(jcr, M_FATAL, 0, _("Attempt to write on closed device=%s\n"),
637           dev->print_name());
638     Dmsg1(100, "Attempt to write on closed device=%s\n", dev->print_name());
639     return false;
640   }
641 
642   /*
643    * Clear to the end of the buffer if it is not full,
644    * and on devices with CAP_ADJWRITESIZE set, apply min and fixed blocking.
645    */
646   if (wlen != block->buf_len) {
647     uint32_t blen = wlen; /* current buffer length */
648 
649     Dmsg2(250, "binbuf=%d buf_len=%d\n", block->binbuf, block->buf_len);
650 
651     if (!dev->HasCap(CAP_ADJWRITESIZE)) {
652       Dmsg1(400, "%s: block write size is not adjustable", dev->print_name());
653     } else {
654       /* (dev->HasCap(CAP_ADJWRITESIZE)) */
655       if (dev->min_block_size == dev->max_block_size) {
656         /*
657          * Fixed block size
658          */
659         wlen = block->buf_len; /* fixed block size already rounded */
660       } else if (wlen < dev->min_block_size) {
661         /*
662          * Min block size
663          */
664         wlen =
665             ((dev->min_block_size + TAPE_BSIZE - 1) / TAPE_BSIZE) * TAPE_BSIZE;
666       } else {
667         /*
668          * Ensure size is rounded
669          */
670         wlen = ((wlen + TAPE_BSIZE - 1) / TAPE_BSIZE) * TAPE_BSIZE;
671       }
672 
673       if (wlen - blen > 0) {
674         memset(block->bufp, 0, wlen - blen); /* clear garbage */
675       }
676     }
677   }
678 
679   Dmsg5(400,
680         "dev=%s: writing %d bytes as block of %d bytes. Block sizes: min=%d, "
681         "max=%d\n",
682         dev->print_name(), block->binbuf, wlen, dev->min_block_size,
683         dev->max_block_size);
684 
685   checksum = SerBlockHeader(block, dev->DoChecksum());
686 
687   /*
688    * Limit maximum Volume size to value specified by user
689    */
690   hit_max1 =
691       (dev->max_volume_size > 0) &&
692       ((dev->VolCatInfo.VolCatBytes + block->binbuf)) >= dev->max_volume_size;
693   hit_max2 = (dev->VolCatInfo.VolCatMaxBytes > 0) &&
694              ((dev->VolCatInfo.VolCatBytes + block->binbuf)) >=
695                  dev->VolCatInfo.VolCatMaxBytes;
696 
697   if (hit_max1 || hit_max2) {
698     char ed1[50];
699     uint64_t max_cap;
700 
701     Dmsg0(100, "==== Output bytes Triggered medium max capacity.\n");
702     if (hit_max1) {
703       max_cap = dev->max_volume_size;
704     } else {
705       max_cap = dev->VolCatInfo.VolCatMaxBytes;
706     }
707     Jmsg(jcr, M_INFO, 0,
708          _("User defined maximum volume capacity %s exceeded on device %s.\n"),
709          edit_uint64_with_commas(max_cap, ed1), dev->print_name());
710     TerminateWritingVolume(dcr);
711     RereadLastBlock(dcr); /* DEBUG */
712     dev->dev_errno = ENOSPC;
713 
714     return false;
715   }
716 
717   /*
718    * Limit maximum File size on volume to user specified value
719    */
720   if ((dev->max_file_size > 0) &&
721       (dev->file_size + block->binbuf) >= dev->max_file_size) {
722     dev->file_size = 0; /* reset file size */
723 
724     if (!dev->weof(1)) { /* write eof */
725       Dmsg0(50, "WEOF error in max file size.\n");
726       Jmsg(jcr, M_FATAL, 0, _("Unable to write EOF. ERR=%s\n"),
727            dev->bstrerror());
728       TerminateWritingVolume(dcr);
729       dev->dev_errno = ENOSPC;
730       return false;
731     }
732     if (!WriteAnsiIbmLabels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolumeName)) {
733       return false;
734     }
735 
736     if (!DoNewFileBookkeeping(dcr)) {
737       /*
738        * Error message already sent
739        */
740       return false;
741     }
742   }
743 
744   dev->VolCatInfo.VolCatWrites++;
745   Dmsg1(1300, "Write block of %u bytes\n", wlen);
746 #ifdef DEBUG_BLOCK_ZEROING
747   uint32_t* bp = (uint32_t*)block->buf;
748   if (bp[0] == 0 && bp[1] == 0 && bp[2] == 0 && block->buf[12] == 0) {
749     Jmsg0(jcr, M_ABORT, 0, _("Write block header zeroed.\n"));
750   }
751 #endif
752 
753   /*
754    * Do write here,
755    * make a somewhat feeble attempt to recover
756    * from the OS telling us it is busy.
757    */
758   int retry = 0;
759   errno = 0;
760   status = 0;
761   do {
762     if (retry > 0 && status == -1 && errno == EBUSY) {
763       BErrNo be;
764       Dmsg4(100, "===== write retry=%d status=%d errno=%d: ERR=%s\n", retry,
765             status, errno, be.bstrerror());
766       Bmicrosleep(5, 0); /* pause a bit if busy or lots of errors */
767       dev->clrerror(-1);
768     }
769     status = dev->write(block->buf, (size_t)wlen);
770   } while (status == -1 && (errno == EBUSY) && retry++ < 3);
771 
772   if (debug_block_checksum) {
773     uint32_t achecksum = SerBlockHeader(block, dev->DoChecksum());
774     if (checksum != achecksum) {
775       Jmsg2(jcr, M_ERROR, 0,
776             _("Block checksum changed during write: before=%ud after=%ud\n"),
777             checksum, achecksum);
778       DumpBlock(block, "with checksum error");
779     }
780   }
781 
782 #ifdef DEBUG_BLOCK_ZEROING
783   if (bp[0] == 0 && bp[1] == 0 && bp[2] == 0 && block->buf[12] == 0) {
784     Jmsg0(jcr, M_ABORT, 0, _("Write block header zeroed.\n"));
785   }
786 #endif
787 
788   if (status != (ssize_t)wlen) {
789     /*
790      * Some devices simply report EIO when the volume is full.
791      * With a little more thought we may be able to check
792      * capacity and distinguish real errors and EOT
793      * conditions.  In any case, we probably want to
794      * simulate an End of Medium.
795      */
796     if (status == -1) {
797       BErrNo be;
798       dev->clrerror(-1);
799       if (dev->dev_errno == 0) { dev->dev_errno = ENOSPC; /* out of space */ }
800       if (dev->dev_errno != ENOSPC) {
801         dev->VolCatInfo.VolCatErrors++;
802         Jmsg4(jcr, M_ERROR, 0,
803               _("Write error at %u:%u on device %s. ERR=%s.\n"), dev->file,
804               dev->block_num, dev->print_name(), be.bstrerror());
805       }
806     } else {
807       dev->dev_errno = ENOSPC; /* out of space */
808     }
809 
810     if (dev->dev_errno == ENOSPC) {
811       Jmsg(jcr, M_INFO, 0,
812            _("End of Volume \"%s\" at %u:%u on device %s. Write of %u bytes "
813              "got %d.\n"),
814            dev->getVolCatName(), dev->file, dev->block_num, dev->print_name(),
815            wlen, status);
816     } else {
817       BErrNo be;
818 
819       be.SetErrno(dev->dev_errno);
820       Mmsg5(dev->errmsg,
821             _("Write error on fd=%d at file:blk %u:%u on device %s. ERR=%s.\n"),
822             dev->fd(), dev->file, dev->block_num, dev->print_name(),
823             be.bstrerror());
824     }
825 
826     GeneratePluginEvent(jcr, bsdEventWriteError, dcr);
827 
828     if (dev->dev_errno != ENOSPC) { Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg); }
829 
830     if (debug_level >= 100) {
831       BErrNo be;
832 
833       be.SetErrno(dev->dev_errno);
834       Dmsg7(100,
835             "=== Write error. fd=%d size=%u rtn=%d dev_blk=%d blk_blk=%d "
836             "errno=%d: ERR=%s\n",
837             dev->fd(), wlen, status, dev->block_num, block->BlockNumber,
838             dev->dev_errno, be.bstrerror(dev->dev_errno));
839     }
840 
841     ok = TerminateWritingVolume(dcr);
842     if (!ok && !forge_on) { return false; }
843     if (ok) { RereadLastBlock(dcr); }
844     return false;
845   }
846 
847   /*
848    * We successfully wrote the block, now do housekeeping
849    */
850   Dmsg2(1300, "VolCatBytes=%d newVolCatBytes=%d\n",
851         (int)dev->VolCatInfo.VolCatBytes,
852         (int)(dev->VolCatInfo.VolCatBytes + wlen));
853   dev->VolCatInfo.VolCatBytes += wlen;
854   dev->VolCatInfo.VolCatBlocks++;
855   dev->EndBlock = dev->block_num;
856   dev->EndFile = dev->file;
857   dev->LastBlock = block->BlockNumber;
858   block->BlockNumber++;
859 
860   /*
861    * Update dcr values
862    */
863   if (dev->IsTape()) {
864     dcr->EndBlock = dev->EndBlock;
865     dcr->EndFile = dev->EndFile;
866     dev->block_num++;
867   } else {
868     /*
869      * Save address of block just written
870      */
871     uint64_t addr = dev->file_addr + wlen - 1;
872     dcr->EndBlock = (uint32_t)addr;
873     dcr->EndFile = (uint32_t)(addr >> 32);
874     dev->block_num = dcr->EndBlock;
875     dev->file = dcr->EndFile;
876   }
877   dcr->VolMediaId = dev->VolCatInfo.VolMediaId;
878   if (dcr->VolFirstIndex == 0 && block->FirstIndex > 0) {
879     dcr->VolFirstIndex = block->FirstIndex;
880   }
881   if (block->LastIndex > 0) { dcr->VolLastIndex = block->LastIndex; }
882   dcr->WroteVol = true;
883   dev->file_addr += wlen; /* update file address */
884   dev->file_size += wlen;
885 
886   Dmsg2(1300, "WriteBlock: wrote block %d bytes=%d\n", dev->block_num, wlen);
887   EmptyBlock(block);
888   return true;
889 }
890 
891 
892 /**
893  * Write a block to the device, with locking and unlocking
894  *
895  * Returns: true  on success
896  *        : false on failure
897  *
898  */
WriteBlockToDevice()899 bool DeviceControlRecord::WriteBlockToDevice()
900 {
901   bool status = true;
902   DeviceControlRecord* dcr = this;
903 
904   if (dcr->spooling) {
905     status = WriteBlockToSpoolFile(dcr);
906     return status;
907   }
908 
909   if (!dcr->IsDevLocked()) { /* device already locked? */
910     /*
911      * Note, do not change this to dcr->r_dlock
912      */
913     dev->rLock(); /* no, lock it */
914   }
915 
916   /*
917    * If a new volume has been mounted since our last write
918    * Create a JobMedia record for the previous volume written,
919    * and set new parameters to write this volume
920    *
921    * The same applies for if we are in a new file.
922    */
923   if (dcr->NewVol || dcr->NewFile) {
924     if (JobCanceled(jcr)) {
925       status = false;
926       Dmsg0(100, "Canceled\n");
927       goto bail_out;
928     }
929     /* Create a jobmedia record for this job */
930     if (!dcr->DirCreateJobmediaRecord(false)) {
931       dev->dev_errno = EIO;
932       Jmsg2(jcr, M_FATAL, 0,
933             _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
934             dcr->getVolCatName(), jcr->Job);
935       SetNewVolumeParameters(dcr);
936       status = false;
937       Dmsg0(100, "cannot create media record\n");
938       goto bail_out;
939     }
940     if (dcr->NewVol) {
941       /*
942        * Note, setting a new volume also handles any pending new file
943        */
944       SetNewVolumeParameters(dcr);
945     } else {
946       SetNewFileParameters(dcr);
947     }
948   }
949 
950   if (!dcr->WriteBlockToDev()) {
951     if (JobCanceled(jcr) || jcr->is_JobType(JT_SYSTEM)) {
952       status = false;
953     } else {
954       status = FixupDeviceBlockWriteError(dcr);
955     }
956   }
957 
958 bail_out:
959   if (!dcr->IsDevLocked()) { /* did we lock dev above? */
960     /*
961      * Note, do not change this to dcr->dunlock
962      */
963     dev->Unlock(); /* unlock it now */
964   }
965   return status;
966 }
967 
968 /**
969  * Read block with locking
970  */
ReadBlockFromDevice(bool check_block_numbers)971 DeviceControlRecord::ReadStatus DeviceControlRecord::ReadBlockFromDevice(
972     bool check_block_numbers)
973 {
974   ReadStatus status;
975 
976   Dmsg0(250, "Enter ReadBlockFromDevice\n");
977   dev->rLock();
978   status = ReadBlockFromDev(check_block_numbers);
979   dev->Unlock();
980   Dmsg0(250, "Leave ReadBlockFromDevice\n");
981   return status;
982 }
983 
984 /**
985  * Read the next block into the block structure and unserialize
986  *  the block header.  For a file, the block may be partially
987  *  or completely in the current buffer.
988  */
ReadBlockFromDev(bool check_block_numbers)989 DeviceControlRecord::ReadStatus DeviceControlRecord::ReadBlockFromDev(
990     bool check_block_numbers)
991 {
992   ssize_t status;
993   int looping;
994   int retry;
995   DeviceControlRecord* dcr = this;
996 
997   if (JobCanceled(jcr)) {
998     Mmsg(dev->errmsg, _("Job failed or canceled.\n"));
999     block->read_len = 0;
1000     return ReadStatus::Error;
1001   }
1002 
1003   if (dev->AtEot()) {
1004     Mmsg(dev->errmsg, _("Attempt to read past end of tape or file.\n"));
1005     block->read_len = 0;
1006     return ReadStatus::EndOfTape;
1007   }
1008   looping = 0;
1009   Dmsg1(250, "Full read in ReadBlockFromDevice() len=%d\n", block->buf_len);
1010 
1011   if (!dev->IsOpen()) {
1012     Mmsg4(dev->errmsg,
1013           _("Attempt to read closed device: fd=%d at file:blk %u:%u on device "
1014             "%s\n"),
1015           dev->fd(), dev->file, dev->block_num, dev->print_name());
1016     Jmsg(dcr->jcr, M_WARNING, 0, "%s", dev->errmsg);
1017     block->read_len = 0;
1018     return ReadStatus::Error;
1019   }
1020 
1021 reread:
1022   if (looping > 1) {
1023     dev->dev_errno = EIO;
1024     Mmsg1(dev->errmsg, _("Block buffer size looping problem on device %s\n"),
1025           dev->print_name());
1026     Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
1027     block->read_len = 0;
1028     return ReadStatus::Error;
1029   }
1030 
1031   retry = 0;
1032   errno = 0;
1033   status = 0;
1034 
1035   do {
1036     if (retry) {
1037       BErrNo be;
1038       Dmsg4(100, "===== read retry=%d status=%d errno=%d: ERR=%s\n", retry,
1039             status, errno, be.bstrerror());
1040       Bmicrosleep(10, 0); /* pause a bit if busy or lots of errors */
1041       dev->clrerror(-1);
1042     }
1043     status = dev->read(block->buf, (size_t)block->buf_len);
1044 
1045   } while (status == -1 && (errno == EBUSY || errno == EINTR || errno == EIO) &&
1046            retry++ < 3);
1047 
1048   if (status < 0) {
1049     BErrNo be;
1050 
1051     dev->clrerror(-1);
1052     Dmsg1(250, "Read device got: ERR=%s\n", be.bstrerror());
1053     block->read_len = 0;
1054     Mmsg5(dev->errmsg,
1055           _("Read error on fd=%d at file:blk %u:%u on device %s. ERR=%s.\n"),
1056           dev->fd(), dev->file, dev->block_num, dev->print_name(),
1057           be.bstrerror());
1058 
1059     GeneratePluginEvent(jcr, bsdEventReadError, dcr);
1060 
1061     Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
1062     if (device->eof_on_error_is_eot && dev->AtEof()) {
1063       dev->SetEot();
1064       return ReadStatus::EndOfTape;
1065     }
1066     return ReadStatus::Error;
1067   }
1068 
1069   Dmsg3(250, "Read device got %d bytes at %u:%u\n", status, dev->file,
1070         dev->block_num);
1071 
1072   if (status == 0) { /* EOF (Berkley I/O Conventions) */
1073     dev->block_num = 0;
1074     block->read_len = 0;
1075     Mmsg3(dev->errmsg, _("Read zero bytes at %u:%u on device %s.\n"), dev->file,
1076           dev->block_num, dev->print_name());
1077     if (dev->AtEof()) { /* EOF already set before means end of tape */
1078       dev->SetEot();
1079       return ReadStatus::EndOfTape;
1080     }
1081     dev->SetAteof();
1082     return ReadStatus::EndOfFile;
1083   }
1084 
1085   /*
1086    * successful read (status > 0)
1087    */
1088 
1089   block->read_len = status; /* save length read */
1090   if (block->read_len == 80 && (dcr->VolCatInfo.LabelType != B_BAREOS_LABEL ||
1091                                 dcr->device->label_type != B_BAREOS_LABEL)) {
1092     /* ***FIXME*** should check label */
1093     Dmsg2(100, "Ignore 80 byte ANSI label at %u:%u\n", dev->file,
1094           dev->block_num);
1095     dev->ClearEof();
1096     goto reread; /* skip ANSI/IBM label */
1097   }
1098 
1099   if (block->read_len < BLKHDR2_LENGTH) {
1100     dev->dev_errno = EIO;
1101     Mmsg4(dev->errmsg,
1102           _("Volume data error at %u:%u! Very short block of %d bytes on "
1103             "device %s discarded.\n"),
1104           dev->file, dev->block_num, block->read_len, dev->print_name());
1105     Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
1106     dev->SetShortBlock();
1107     block->read_len = block->binbuf = 0;
1108     Dmsg2(200, "set block=%p binbuf=%d\n", block, block->binbuf);
1109     return ReadStatus::Error;
1110   }
1111 
1112   // BlockNumber = block->BlockNumber + 1;
1113   if (!unSerBlockHeader(jcr, dev, block)) {
1114     if (forge_on) {
1115       dev->file_addr += block->read_len;
1116       dev->file_size += block->read_len;
1117       goto reread;
1118     }
1119     return ReadStatus::Error;
1120   }
1121 
1122   /*
1123    * If the block is bigger than the buffer, we Reposition for
1124    *  re-reading the block, allocate a buffer of the correct size,
1125    *  and go re-read.
1126    */
1127   if (block->block_len > block->buf_len) {
1128     dev->dev_errno = EIO;
1129     Mmsg2(
1130         dev->errmsg,
1131         _("Block length %u is greater than buffer %u. Attempting recovery.\n"),
1132         block->block_len, block->buf_len);
1133     Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
1134     Pmsg1(000, "%s", dev->errmsg);
1135     /*
1136      * Attempt to Reposition to re-read the block
1137      */
1138     if (dev->IsTape()) {
1139       Dmsg0(250, "BootStrapRecord for reread; block too big for buffer.\n");
1140       if (!dev->bsr(1)) {
1141         Mmsg(dev->errmsg, "%s", dev->bstrerror());
1142         Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
1143         block->read_len = 0;
1144         return ReadStatus::Error;
1145       }
1146     } else {
1147       Dmsg0(250, "Seek to beginning of block for reread.\n");
1148       boffset_t pos =
1149           dev->lseek(dcr, (boffset_t)0, SEEK_CUR); /* get curr pos */
1150       pos -= block->read_len;
1151       dev->lseek(dcr, pos, SEEK_SET);
1152       dev->file_addr = pos;
1153     }
1154     Mmsg1(dev->errmsg, _("Setting block buffer size to %u bytes.\n"),
1155           block->block_len);
1156     Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
1157     Pmsg1(000, "%s", dev->errmsg);
1158     /*
1159      * Set new block length
1160      */
1161     dev->max_block_size = block->block_len;
1162     block->buf_len = block->block_len;
1163     FreeMemory(block->buf);
1164     block->buf = GetMemory(block->buf_len);
1165     EmptyBlock(block);
1166     looping++;
1167     goto reread; /* re-read block with correct block size */
1168   }
1169 
1170   if (block->block_len > block->read_len) {
1171     dev->dev_errno = EIO;
1172     Mmsg4(dev->errmsg,
1173           _("Volume data error at %u:%u! Short block of %d bytes on device %s "
1174             "discarded.\n"),
1175           dev->file, dev->block_num, block->read_len, dev->print_name());
1176     Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
1177     dev->SetShortBlock();
1178     block->read_len = block->binbuf = 0;
1179     return ReadStatus::Error;
1180   }
1181 
1182   dev->ClearShortBlock();
1183   dev->ClearEof();
1184   dev->VolCatInfo.VolCatReads++;
1185   dev->VolCatInfo.VolCatRBytes += block->read_len;
1186 
1187   dev->EndBlock = dev->block_num;
1188   dev->EndFile = dev->file;
1189   dev->block_num++;
1190 
1191   /*
1192    * Update dcr values
1193    */
1194   if (dev->IsTape()) {
1195     dcr->EndBlock = dev->EndBlock;
1196     dcr->EndFile = dev->EndFile;
1197   } else {
1198     /*
1199      * We need to take care about a short block in EndBlock/File computation
1200      */
1201     uint32_t len = MIN(block->read_len, block->block_len);
1202     uint64_t addr = dev->file_addr + len - 1;
1203     dcr->EndBlock = (uint32_t)addr;
1204     dcr->EndFile = (uint32_t)(addr >> 32);
1205     dev->block_num = dev->EndBlock = dcr->EndBlock;
1206     dev->file = dev->EndFile = dcr->EndFile;
1207   }
1208   dcr->VolMediaId = dev->VolCatInfo.VolMediaId;
1209   dev->file_addr += block->read_len;
1210   dev->file_size += block->read_len;
1211 
1212   /*
1213    * If we read a short block on disk,
1214    * seek to beginning of next block. This saves us
1215    * from shuffling blocks around in the buffer. Take a
1216    * look at this from an efficiency stand point later, but
1217    * it should only happen once at the end of each job.
1218    *
1219    * I've been lseek()ing negative relative to SEEK_CUR for 30
1220    *   years now. However, it seems that with the new off_t definition,
1221    *   it is not possible to seek negative amounts, so we use two
1222    *   lseek(). One to get the position, then the second to do an
1223    *   absolute positioning -- so much for efficiency.  KES Sep 02.
1224    */
1225   Dmsg0(250, "At end of read block\n");
1226   if (block->read_len > block->block_len && !dev->IsTape()) {
1227     char ed1[50];
1228     boffset_t pos = dev->lseek(dcr, (boffset_t)0, SEEK_CUR); /* get curr pos */
1229     Dmsg1(250, "Current lseek pos=%s\n", edit_int64(pos, ed1));
1230     pos -= (block->read_len - block->block_len);
1231     dev->lseek(dcr, pos, SEEK_SET);
1232     Dmsg3(250, "Did lseek pos=%s blk_size=%d rdlen=%d\n", edit_int64(pos, ed1),
1233           block->block_len, block->read_len);
1234     dev->file_addr = pos;
1235     dev->file_size = pos;
1236   }
1237   Dmsg2(250, "Exit read_block read_len=%d block_len=%d\n", block->read_len,
1238         block->block_len);
1239   block->block_read = true;
1240   return ReadStatus::Ok;
1241 }
1242 
1243 } /* namespace storagedaemon */
1244