3 * record.c -- tape record handling functions
5 * Kern Sibbald, April MMI
11 Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of
16 the License, or (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
23 You should have received a copy of the GNU General Public
24 License along with this program; if not, write to the Free
25 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
34 extern int debug_level;
37 * Convert a FileIndex into a printable
38 * ASCII string. Not reentrant.
39 * If the FileIndex is negative, it flags the
40 * record as a Label, otherwise it is simply
41 * the FileIndex of the current file.
43 char *FI_to_ascii(int fi)
47 sprintf(buf, "%d", fi);
62 sprintf(buf, "unknown: %d", fi);
69 * Convert a Stream ID into a printable
70 * ASCII string. Not reentrant.
72 char *stream_to_ascii(int stream)
76 case STREAM_UNIX_ATTRIBUTES:
78 case STREAM_FILE_DATA:
80 case STREAM_MD5_SIGNATURE:
82 case STREAM_GZIP_DATA:
85 sprintf(buf, "%d", stream);
91 * Return a new record entity
93 DEV_RECORD *new_record(void)
97 rec = (DEV_RECORD *) get_memory(sizeof(DEV_RECORD));
98 memset(rec, 0, sizeof(DEV_RECORD));
99 rec->data = get_pool_memory(PM_MESSAGE);
104 * Free the record entity
107 void free_record(DEV_RECORD *rec)
109 Dmsg0(150, "Enter free_record.\n");
110 free_pool_memory(rec->data);
111 Dmsg0(150, "Data buf is freed.\n");
112 free_pool_memory((POOLMEM *)rec);
113 Dmsg0(150, "Leave free_record.\n");
117 * Read a record from a block
118 * if necessary, read the block from the device without locking
119 * if necessary, handle getting a new Volume
121 * Returns: 0 on failure
124 int read_record(DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *record)
126 Dmsg2(90, "read_record() dev=%x state=%x\n", dev, dev->state);
128 while (!read_record_from_block(block, record)) {
129 Dmsg2(90, "!read_record_from_block data_len=%d rem=%d\n", record->data_len,
131 if (!read_block_from_dev(dev, block)) {
132 Dmsg0(200, "===== Got read block I/O error ======\n");
136 Dmsg4(90, "read_record FI=%s SessId=%d Strm=%s len=%d\n",
137 FI_to_ascii(record->FileIndex), record->VolSessionId,
138 stream_to_ascii(record->Stream), record->data_len);
144 * Write a record to block
145 * if necessary, write the block to the device with locking
146 * if necessary, handle getting a new Volume
148 * Returns: 0 on failure
151 int write_record_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *record)
153 Dmsg2(190, "write_device() dev=%x state=%x\n", dev, dev->state);
155 while (!write_record_to_block(block, record)) {
156 Dmsg2(190, "!write_record data_len=%d rem=%d\n", record->data_len,
158 if (!write_block_to_device(jcr, dev, block)) {
159 Dmsg0(90, "Got write_block_to_dev error.\n");
163 Dmsg4(190, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
164 FI_to_ascii(record->FileIndex), record->VolSessionId,
165 stream_to_ascii(record->Stream), record->data_len);
170 * Write a Record to the block
172 * Returns: 0 on failure (none or partially written)
173 * 1 on success (all bytes written)
175 * and remainder returned in packet.
177 * We require enough room for the header, and we deal with
178 * two special cases. 1. Only part of the record may have
179 * been transferred the last time (when remainder is
180 * non-zero), and 2. The remaining bytes to write may not
181 * all fit into the block.
183 int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
188 sm_check(__FILE__, __LINE__, False);
189 remlen = block->buf_len - block->binbuf;
191 ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
194 Dmsg6(190, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n\
195 rem=%d remainder=%d\n",
196 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
197 stream_to_ascii(rec->Stream), rec->data_len,
198 remlen, rec->remainder);
201 * If rec->remainder is non-zero, we have been called a
202 * second (or subsequent) time to finish writing a record
203 * that did not previously fit into the block.
205 if (rec->remainder == 0) {
206 /* Require enough room to write a full header */
207 if (remlen >= RECHDR_LENGTH) {
208 ser_begin(block->bufp, RECHDR_LENGTH);
209 ser_uint32(rec->VolSessionId);
210 ser_uint32(rec->VolSessionTime);
211 ser_int32(rec->FileIndex);
212 ser_int32(rec->Stream);
213 ser_uint32(rec->data_len);
214 ASSERT(ser_length(block->bufp) == RECHDR_LENGTH);
216 block->bufp += RECHDR_LENGTH;
217 block->binbuf += RECHDR_LENGTH;
218 remlen -= RECHDR_LENGTH;
219 rec->remainder = rec->data_len;
221 rec->remainder = rec->data_len + RECHDR_LENGTH;
222 sm_check(__FILE__, __LINE__, False);
227 * We are here to write unwritten bytes from a previous
228 * time. Presumably we have a new buffer (possibly
229 * containing a volume label), so the new header
230 * should be able to fit in the block -- otherwise we have
231 * an error. Note, we may have to continue splitting the
232 * data record though.
234 * First, write the header, then write as much as
235 * possible of the data record.
237 * Every time we write a header and it is a continuation
238 * of a previous partially written record, we store the
239 * Stream as -Stream in the record header.
241 ser_begin(block->bufp, RECHDR_LENGTH);
242 ser_uint32(rec->VolSessionId);
243 ser_uint32(rec->VolSessionTime);
244 ser_int32(rec->FileIndex);
245 if (rec->remainder > rec->data_len) {
246 ser_int32(rec->Stream); /* normal full header */
247 ser_uint32(rec->data_len);
248 rec->remainder = rec->data_len; /* must still do data record */
250 ser_int32(-rec->Stream); /* mark this as a continuation record */
251 ser_uint32(rec->remainder); /* bytes to do */
253 ASSERT(ser_length(block->bufp) == RECHDR_LENGTH);
255 /* Require enough room to write a full header */
256 ASSERT(remlen >= RECHDR_LENGTH);
258 block->bufp += RECHDR_LENGTH;
259 block->binbuf += RECHDR_LENGTH;
260 remlen -= RECHDR_LENGTH;
263 sm_check(__FILE__, __LINE__, False);
264 return 0; /* partial transfer */
268 * Now deal with data record.
269 * Part of it may have already been transferred, and we
270 * may not have enough room to transfer the whole this time.
272 if (rec->remainder > 0) {
273 /* Write as much of data as possible */
274 if (remlen >= rec->remainder) {
275 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
277 block->bufp += rec->remainder;
278 block->binbuf += rec->remainder;
280 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
282 if (!sm_check_rtn(__FILE__, __LINE__, False)) {
283 /* We damaged a buffer */
284 Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n\
285 rem=%d remainder=%d\n",
286 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
287 stream_to_ascii(rec->Stream), rec->data_len,
288 remlen, rec->remainder);
289 Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
290 block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
292 Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
293 block->buf, block->bufp-block->buf);
295 Emsg0(M_ABORT, 0, "Damaged buffer\n");
298 block->bufp += remlen;
299 block->binbuf += remlen;
300 rec->remainder -= remlen;
301 return 0; /* did partial transfer */
304 rec->remainder = 0; /* did whole transfer */
305 sm_check(__FILE__, __LINE__, False);
311 * Read a Record from the block
312 * Returns: 0 on failure
315 int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
319 uint32_t VolSessionId;
320 uint32_t VolSessionTime;
325 remlen = block->binbuf;
328 * Get the header. There is always a full header,
329 * otherwise we find it in the next block.
331 if (remlen >= RECHDR_LENGTH) {
332 Dmsg3(90, "read_record_block: remlen=%d data_len=%d rem=%d\n",
333 remlen, rec->data_len, rec->remainder);
335 unser_begin(block->bufp, RECHDR_LENGTH);
336 unser_uint32(VolSessionId);
337 unser_uint32(VolSessionTime);
338 unser_int32(FileIndex);
340 unser_uint32(data_bytes);
342 ASSERT(unser_length(block->bufp) == RECHDR_LENGTH);
343 block->bufp += RECHDR_LENGTH;
344 block->binbuf -= RECHDR_LENGTH;
345 remlen -= RECHDR_LENGTH;
348 * if Stream is negative, it means that this is a continuation
349 * of a previous partially written record.
351 if (Stream < 0) { /* continuation record? */
352 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
354 if (!rec->remainder) { /* if we didn't read previously */
355 rec->data_len = 0; /* return data as if no continuation */
356 } else if (rec->VolSessionId != VolSessionId ||
357 rec->VolSessionTime != VolSessionTime ||
358 rec->Stream != -Stream) {
359 return 0; /* This is from some other Session */
361 rec->Stream = -Stream; /* set correct Stream */
362 } else { /* Regular record */
363 rec->Stream = Stream;
364 rec->data_len = 0; /* transfer to beginning of data */
366 rec->VolSessionId = VolSessionId;
367 rec->VolSessionTime = VolSessionTime;
368 rec->FileIndex = FileIndex;
370 Dmsg6(90, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%d\n\
371 remlen=%d data_len=%d\n",
372 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
373 stream_to_ascii(rec->Stream), data_bytes, remlen, rec->data_len);
376 * No more records in this block because the number
377 * of remaining bytes are less than a record header
378 * length, so return empty handed, but indicate that
379 * he must read again. By returning, we allow the
380 * higher level routine to fetch the next block and
383 Dmsg0(90, "read_record_block: nothing\n");
384 if (!rec->remainder) {
385 rec->remainder = 1; /* set to expect continuation */
386 rec->data_len = 0; /* no data transferred */
391 ASSERT(data_bytes < MAX_BLOCK_LENGTH); /* temp sanity check */
393 rec->data = check_pool_memory_size(rec->data, rec->data_len+data_bytes);
396 * At this point, we have read the header, now we
397 * must transfer as much of the data record as
398 * possible taking into account: 1. A partial
399 * data record may have previously been transferred,
400 * 2. The current block may not contain the whole data
403 if (remlen >= data_bytes) {
404 /* Got whole record */
405 memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
406 block->bufp += data_bytes;
407 block->binbuf -= data_bytes;
408 rec->data_len += data_bytes;
411 memcpy(rec->data+rec->data_len, block->bufp, remlen);
412 block->bufp += remlen;
413 block->binbuf -= remlen;
414 rec->data_len += remlen;
415 rec->remainder = 1; /* partial record transferred */
416 Dmsg1(90, "read_record_block: partial xfered=%d\n", rec->data_len);
420 Dmsg4(90, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
421 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
422 stream_to_ascii(rec->Stream), rec->data_len);
423 return 1; /* transferred full record */