3 * record.c -- tape record handling functions
5 * Kern Sibbald, April MMI
9 Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
11 This program is free software; you can redistribute it and/or
12 modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation; either version 2 of
14 the License, or (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public
22 License along with this program; if not, write to the Free
23 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
32 extern int debug_level;
35 * Convert a FileIndex into a printable
36 * ASCII string. Not reentrant.
38 char *FI_to_ascii(int fi)
42 sprintf(buf, "%d", fi);
57 sprintf(buf, "unknown: %d", fi);
64 * Convert a Stream ID into a printable
65 * ASCII string. Not reentrant.
67 char *stream_to_ascii(int stream)
71 case STREAM_UNIX_ATTRIBUTES:
73 case STREAM_FILE_DATA:
75 case STREAM_MD5_SIGNATURE:
77 case STREAM_GZIP_DATA:
80 sprintf(buf, "%d", stream);
86 * Return a new record entity
88 DEV_RECORD *new_record(void)
92 rec = (DEV_RECORD *) get_memory(sizeof(DEV_RECORD));
93 memset(rec, 0, sizeof(DEV_RECORD));
94 rec->data = (char *) get_pool_memory(PM_MESSAGE);
99 * Free the record entity
102 void free_record(DEV_RECORD *rec)
104 Dmsg0(150, "Enter free_record.\n");
105 free_pool_memory(rec->data);
106 Dmsg0(150, "Data buf is freed.\n");
107 free_pool_memory(rec);
108 Dmsg0(150, "Leave free_record.\n");
112 * Read a record from a block
113 * if necessary, read the block from the device without locking
114 * if necessary, handle getting a new Volume
116 * Returns: 0 on failure
119 int read_record(DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *record)
121 Dmsg2(90, "read_record() dev=%x state=%x\n", dev, dev->state);
123 while (!read_record_from_block(block, record)) {
124 Dmsg2(90, "!read_record_from_block data_len=%d rem=%d\n", record->data_len,
126 if (!read_block_from_dev(dev, block)) {
127 /**** ****FIXME**** handle getting a new Volume */
128 Dmsg0(200, "===== Got read block I/O error ======\n");
132 Dmsg4(90, "read_record FI=%s SessId=%d Strm=%s len=%d\n",
133 FI_to_ascii(record->FileIndex), record->VolSessionId,
134 stream_to_ascii(record->Stream), record->data_len);
140 * Write a record to block
141 * if necessary, write the block to the device with locking
142 * if necessary, handle getting a new Volume
144 * Returns: 0 on failure
147 int write_record_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *record)
149 Dmsg2(190, "write_device() dev=%x state=%x\n", dev, dev->state);
151 while (!write_record_to_block(block, record)) {
152 Dmsg2(190, "!write_record data_len=%d rem=%d\n", record->data_len,
154 if (!write_block_to_device(jcr, dev, block)) {
155 Dmsg0(90, "Got write_block_to_dev error.\n");
159 Dmsg4(190, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
160 FI_to_ascii(record->FileIndex), record->VolSessionId,
161 stream_to_ascii(record->Stream), record->data_len);
166 * Write a Record to the block
168 * Returns: 0 on failure (none or partially written)
169 * 1 on success (all bytes written)
171 * and remainder returned in packet.
173 * We require enough room for the header, and we deal with
174 * two special cases. 1. Only part of the record may have
175 * been transferred the last time (when remainder is
176 * non-zero), and 2. The remaining bytes to write may not
177 * all fit into the block.
179 int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
184 sm_check(__FILE__, __LINE__, False);
185 remlen = block->buf_len - block->binbuf;
187 ASSERT(block->binbuf == (uint32_t) (block->bufp - block->buf));
190 Dmsg6(190, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n\
191 rem=%d remainder=%d\n",
192 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
193 stream_to_ascii(rec->Stream), rec->data_len,
194 remlen, rec->remainder);
196 if (rec->remainder == 0) {
197 /* Require enough room to write a full header */
198 if (remlen >= RECHDR_LENGTH) {
199 ser_begin(block->bufp, RECHDR_LENGTH);
200 ser_uint32(rec->VolSessionId);
201 ser_uint32(rec->VolSessionTime);
202 ser_int32(rec->FileIndex);
203 ser_int32(rec->Stream);
204 ser_uint32(rec->data_len);
205 ASSERT(ser_length(block->bufp) == RECHDR_LENGTH);
207 block->bufp += RECHDR_LENGTH;
208 block->binbuf += RECHDR_LENGTH;
209 remlen -= RECHDR_LENGTH;
210 rec->remainder = rec->data_len;
212 rec->remainder = rec->data_len + RECHDR_LENGTH;
213 sm_check(__FILE__, __LINE__, False);
218 * We are here to write unwritten bytes from a previous
219 * time. Presumably we have a new buffer (possibly
220 * containing a volume label), so the new header
221 * should be able to fit in the block -- otherwise we have
222 * an error. Note, we may have to continue splitting the
223 * data record though.
225 * First, write the header, then write as much as
226 * possible of the data record.
228 ser_begin(block->bufp, RECHDR_LENGTH);
229 ser_uint32(rec->VolSessionId);
230 ser_uint32(rec->VolSessionTime);
231 ser_int32(rec->FileIndex);
232 if (rec->remainder > rec->data_len) {
233 ser_int32(rec->Stream); /* normal full header */
234 ser_uint32(rec->data_len);
235 rec->remainder = rec->data_len; /* must still do data record */
237 ser_int32(-rec->Stream); /* mark this as a continuation record */
238 ser_uint32(rec->remainder); /* bytes to do */
240 ASSERT(ser_length(block->bufp) == RECHDR_LENGTH);
242 /* Require enough room to write a full header */
243 ASSERT(remlen >= RECHDR_LENGTH);
245 block->bufp += RECHDR_LENGTH;
246 block->binbuf += RECHDR_LENGTH;
247 remlen -= RECHDR_LENGTH;
250 sm_check(__FILE__, __LINE__, False);
251 return 0; /* partial transfer */
254 /* Now deal with data record.
255 * Part of it may have already been transferred, and we
256 * may not have enough room to transfer the whole this time.
258 if (rec->remainder > 0) {
259 /* Write as much of data as possible */
260 if (remlen >= rec->remainder) {
261 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
263 block->bufp += rec->remainder;
264 block->binbuf += rec->remainder;
266 memcpy(block->bufp, rec->data+rec->data_len-rec->remainder,
268 if (!sm_check_rtn(__FILE__, __LINE__, False)) {
269 /* We damaged a buffer */
270 Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n\
271 rem=%d remainder=%d\n",
272 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
273 stream_to_ascii(rec->Stream), rec->data_len,
274 remlen, rec->remainder);
275 Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
276 block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
278 Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
279 block->buf, block->bufp-block->buf);
281 Emsg0(M_ABORT, 0, "Damaged buffer\n");
284 block->bufp += remlen;
285 block->binbuf += remlen;
286 rec->remainder -= remlen;
287 return 0; /* did partial transfer */
290 rec->remainder = 0; /* did whole transfer */
291 sm_check(__FILE__, __LINE__, False);
297 * Read a Record from the block
298 * Returns: 0 on failure
301 int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec)
308 remlen = block->binbuf;
311 * Get the header. There is always a full header,
312 * otherwise we find it in the next block.
314 if (remlen >= RECHDR_LENGTH) {
315 Dmsg3(90, "read_record_block: remlen=%d data_len=%d rem=%d\n",
316 remlen, rec->data_len, rec->remainder);
317 /* memcpy(rec->ser_buf, block->bufp, RECHDR_LENGTH); */
319 unser_begin(block->bufp, RECHDR_LENGTH);
320 unser_uint32(rec->VolSessionId);
321 unser_uint32(rec->VolSessionTime);
322 unser_int32(rec->FileIndex);
324 unser_uint32(data_bytes);
326 ASSERT(unser_length(block->bufp) == RECHDR_LENGTH);
327 block->bufp += RECHDR_LENGTH;
328 block->binbuf -= RECHDR_LENGTH;
329 remlen -= RECHDR_LENGTH;
331 if (Stream < 0) { /* continuation record? */
332 Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n",
334 /* ****FIXME**** add code to verify that this
335 * is the correct continuation of the previous
338 if (!rec->remainder) { /* if we didn't read previously */
339 rec->data_len = 0; /* simply return data */
341 rec->Stream = -Stream; /* set correct Stream */
342 } else { /* Regular record */
343 rec->Stream = Stream;
344 rec->data_len = 0; /* transfer to beginning of data */
346 Dmsg6(90, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%d\n\
347 remlen=%d data_len=%d\n",
348 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
349 stream_to_ascii(rec->Stream), data_bytes, remlen, rec->data_len);
351 Dmsg0(90, "read_record_block: nothing\n");
352 if (!rec->remainder) {
353 rec->remainder = 1; /* set to expect continuation */
354 rec->data_len = 0; /* no data transferred */
359 ASSERT(data_bytes < MAX_BLOCK_LENGTH); /* temp sanity check */
361 rec->data = (char *) check_pool_memory_size(rec->data, rec->data_len+data_bytes);
364 * At this point, we have read the header, now we
365 * must transfer as much of the data record as
366 * possible taking into account: 1. A partial
367 * data record may have previously been transferred,
368 * 2. The current block may not contain the whole data
371 if (remlen >= data_bytes) {
372 /* Got whole record */
373 memcpy(rec->data+rec->data_len, block->bufp, data_bytes);
374 block->bufp += data_bytes;
375 block->binbuf -= data_bytes;
376 rec->data_len += data_bytes;
379 memcpy(rec->data+rec->data_len, block->bufp, remlen);
380 block->bufp += remlen;
381 block->binbuf -= remlen;
382 rec->data_len += remlen;
383 rec->remainder = 1; /* partial record transferred */
384 Dmsg1(90, "read_record_block: partial xfered=%d\n", rec->data_len);
388 Dmsg4(90, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
389 FI_to_ascii(rec->FileIndex), rec->VolSessionId,
390 stream_to_ascii(rec->Stream), rec->data_len);
391 return 1; /* transferred full record */