|
|
1.1.1.2 ! root 1: /* $Id: posix-tape.c,v 1.7 2006/09/30 12:35:01 fredette Exp $ */ 1.1 root 2: 3: /* host/posix/posix-tape.c - implementation of tapes on a POSIX system: */ 4: 5: /* 6: * Copyright (c) 2003 Matt Fredette 7: * All rights reserved. 8: * 9: * Redistribution and use in source and binary forms, with or without 10: * modification, are permitted provided that the following conditions 11: * are met: 12: * 1. Redistributions of source code must retain the above copyright 13: * notice, this list of conditions and the following disclaimer. 14: * 2. Redistributions in binary form must reproduce the above copyright 15: * notice, this list of conditions and the following disclaimer in the 16: * documentation and/or other materials provided with the distribution. 17: * 3. All advertising materials mentioning features or use of this software 18: * must display the following acknowledgement: 19: * This product includes software developed by Matt Fredette. 20: * 4. The name of the author may not be used to endorse or promote products 21: * derived from this software without specific prior written permission. 22: * 23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 27: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 28: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 31: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33: * POSSIBILITY OF SUCH DAMAGE. 34: */ 35: 36: #include <tme/common.h> 1.1.1.2 ! root 37: _TME_RCSID("$Id: posix-tape.c,v 1.7 2006/09/30 12:35:01 fredette Exp $"); 1.1 root 38: 39: /* includes: */ 40: #include <tme/generic/tape.h> 1.1.1.2 ! root 41: #include <tme/threads.h> 1.1 root 42: #include <fcntl.h> 43: #include <stdlib.h> 44: #include <strings.h> 45: #include <sys/stat.h> 46: #include <sys/uio.h> 47: #ifdef HAVE_STDARG_H 48: #include <stdarg.h> 49: #else /* HAVE_STDARG_H */ 50: #include <varargs.h> 51: #endif /* HAVE_STDARG_H */ 52: 53: /* macros: */ 54: 55: /* tape flags: */ 56: #define TME_POSIX_TAPE_FLAG_RO TME_BIT(0) 57: #define TME_POSIX_TAPE_FLAG_DIRTY TME_BIT(1) 58: 59: /* the size of the control callout ring: */ 60: #define TME_POSIX_TAPE_CONTROL_RING_SIZE (16) 61: 62: /* the callout flags: */ 63: #define TME_POSIX_TAPE_CALLOUT_CHECK (0) 64: #define TME_POSIX_TAPE_CALLOUT_RUNNING TME_BIT(0) 65: #define TME_POSIX_TAPE_CALLOUTS_MASK (-2) 66: #define TME_POSIX_TAPE_CALLOUT_CONTROL TME_BIT(1) 67: 68: /* types: */ 69: 70: /* a posix tape control: */ 71: struct tme_posix_tape_control { 72: 73: /* the control: */ 74: unsigned int tme_posix_tape_control_which; 75: }; 76: 77: /* a posix tape segment: */ 78: struct tme_posix_tape_segment { 79: 80: /* tape segments are kept on a doubly linked list: */ 81: struct tme_posix_tape_segment *tme_posix_tape_segment_next; 82: struct tme_posix_tape_segment *tme_posix_tape_segment_prev; 83: 84: /* the filename of this tape segment: */ 85: char *tme_posix_tape_segment_filename; 86: 87: /* the file descriptor of this tape segment: */ 88: int tme_posix_tape_segment_fd; 89: 90: /* this is nonzero iff this tape segment is a real tape: */ 91: int tme_posix_tape_segment_real_tape; 92: }; 93: 94: /* a posix tape: */ 95: struct tme_posix_tape { 96: 97: /* backpointer to our element: */ 98: struct tme_element *tme_posix_tape_element; 99: 100: /* our mutex: */ 101: tme_mutex_t tme_posix_tape_mutex; 102: 103: /* our flags: */ 104: int tme_posix_tape_flags; 105: 106: /* the tape segments: */ 107: struct tme_posix_tape_segment *tme_posix_tape_segments; 108: 109: /* the connection: */ 110: struct tme_tape_connection *tme_posix_tape_connection; 111: 112: /* the callout flags: */ 113: int tme_posix_tape_callout_flags; 114: 115: /* the control ring buffer: */ 116: struct tme_posix_tape_control tme_posix_tape_controls[TME_POSIX_TAPE_CONTROL_RING_SIZE]; 117: unsigned int tme_posix_tape_control_head; 118: unsigned int tme_posix_tape_control_tail; 119: 120: /* the current tape segment: */ 121: struct tme_posix_tape_segment *tme_posix_tape_segment_current; 122: 123: /* the block sizes: */ 124: unsigned long tme_posix_tape_block_size_min; 125: unsigned long tme_posix_tape_block_size_max; 126: unsigned long tme_posix_tape_block_size_fixed; 127: 128: /* the tape buffer: */ 129: unsigned long tme_posix_tape_buffer_size; 130: tme_uint8_t *tme_posix_tape_buffer_data; 131: unsigned long tme_posix_tape_buffer_flags; 132: unsigned long tme_posix_tape_buffer_count_xfer; 133: }; 134: 135: /* this allocates the next control in the ring buffer: */ 136: struct tme_posix_tape_control * 137: _tme_posix_tape_control_new(struct tme_posix_tape *posix_tape) 138: { 139: int old_head; 140: struct tme_posix_tape_control *control; 141: 142: /* abort if the ring buffer overflows: */ 143: old_head = posix_tape->tme_posix_tape_control_head; 144: posix_tape->tme_posix_tape_control_head 145: = ((old_head 146: + 1) 147: & (TME_POSIX_TAPE_CONTROL_RING_SIZE 148: - 1)); 149: if ((posix_tape->tme_posix_tape_control_head 150: == posix_tape->tme_posix_tape_control_tail) 151: && (posix_tape->tme_posix_tape_connection 152: != NULL)) { 153: abort(); 154: } 155: 156: /* return the control: */ 157: control = &posix_tape->tme_posix_tape_controls[old_head]; 158: return (control); 159: } 160: 161: /* the posix tape callout function. it must be called with the mutex locked: */ 162: static void 163: _tme_posix_tape_callout(struct tme_posix_tape *posix_tape, 164: int new_callouts) 165: { 166: struct tme_tape_connection *conn_tape; 167: unsigned int old_tail; 168: struct tme_posix_tape_control *control; 169: int callouts, later_callouts; 170: int rc; 171: 172: /* add in any new callouts: */ 173: posix_tape->tme_posix_tape_callout_flags |= new_callouts; 174: 175: /* if this function is already running in another thread, simply 176: return now. the other thread will do our work: */ 177: if (posix_tape->tme_posix_tape_callout_flags 178: & TME_POSIX_TAPE_CALLOUT_RUNNING) { 179: return; 180: } 181: 182: /* callouts are now running: */ 183: posix_tape->tme_posix_tape_callout_flags 184: |= TME_POSIX_TAPE_CALLOUT_RUNNING; 185: 186: /* assume that we won't need any later callouts: */ 187: later_callouts = 0; 188: 189: /* loop while callouts are needed: */ 190: for (; ((callouts 191: = posix_tape->tme_posix_tape_callout_flags) 192: & TME_POSIX_TAPE_CALLOUTS_MASK); ) { 193: 194: /* clear the needed callouts: */ 195: posix_tape->tme_posix_tape_callout_flags 196: = (callouts 197: & ~TME_POSIX_TAPE_CALLOUTS_MASK); 198: callouts 199: &= TME_POSIX_TAPE_CALLOUTS_MASK; 200: 201: /* get the tape connection: */ 202: conn_tape = posix_tape->tme_posix_tape_connection; 203: 204: /* if we need to call out a control: */ 205: if (callouts & TME_POSIX_TAPE_CALLOUT_CONTROL) { 206: 207: /* there must be a control to call out: */ 208: old_tail = posix_tape->tme_posix_tape_control_tail; 209: assert (old_tail 210: != posix_tape->tme_posix_tape_control_head); 211: control 212: = &posix_tape->tme_posix_tape_controls[old_tail]; 213: 214: /* unlock the mutex: */ 215: tme_mutex_unlock(&posix_tape->tme_posix_tape_mutex); 216: 217: /* do the callout: */ 218: rc = TME_OK; 219: if (conn_tape != NULL) { 220: switch (control->tme_posix_tape_control_which) { 221: case TME_TAPE_CONTROL_LOAD: 222: case TME_TAPE_CONTROL_UNLOAD: 223: rc = ((*conn_tape->tme_tape_connection_control) 224: (conn_tape, 225: control->tme_posix_tape_control_which)); 226: break; 227: default: 228: rc = TME_OK; 229: assert(FALSE); 230: } 231: } 232: 233: /* lock the mutex: */ 234: tme_mutex_lock(&posix_tape->tme_posix_tape_mutex); 235: 236: /* if the callout was unsuccessful, remember that at some later 237: time this callout should be attempted again: */ 238: if (rc != TME_OK) { 239: later_callouts |= TME_POSIX_TAPE_CALLOUT_CONTROL; 240: } 241: 242: /* otherwise, this callout was successful: */ 243: else { 244: 245: /* advance the tail pointer: */ 246: posix_tape->tme_posix_tape_control_tail 247: = ((old_tail 248: + 1) 249: & (TME_POSIX_TAPE_CONTROL_RING_SIZE 250: - 1)); 251: 252: /* if there are more controls to callout, call them out now: */ 253: if (posix_tape->tme_posix_tape_control_tail 254: != posix_tape->tme_posix_tape_control_head) { 255: posix_tape->tme_posix_tape_callout_flags 256: |= TME_POSIX_TAPE_CALLOUT_CONTROL; 257: } 258: } 259: } 260: } 261: 262: /* put in any later callouts, and clear that callouts are running: */ 263: posix_tape->tme_posix_tape_callout_flags = later_callouts; 264: } 265: 266: /* this closes and frees all tape segments: */ 267: static void 268: _tme_posix_tape_segments_close(struct tme_posix_tape *posix_tape) 269: { 270: struct tme_posix_tape_segment *segment; 271: 272: /* while we have segments: */ 273: for (; posix_tape->tme_posix_tape_segments != NULL; ) { 274: 275: /* get the next segment and remove it from the list: */ 276: segment = posix_tape->tme_posix_tape_segments; 277: posix_tape->tme_posix_tape_segments = segment->tme_posix_tape_segment_next; 278: 279: /* if the segment file descriptor is open, close it: */ 280: if (segment->tme_posix_tape_segment_fd >= 0) { 281: close(segment->tme_posix_tape_segment_fd); 282: } 283: 284: /* free the segment filename: */ 285: tme_free(segment->tme_posix_tape_segment_filename); 286: 287: /* free the segment itself: */ 288: tme_free(segment); 289: } 290: 291: /* there is no current segment: */ 292: posix_tape->tme_posix_tape_segment_current = NULL; 293: } 294: 295: /* this unloads a tape: */ 296: static int 297: _tme_posix_tape_unload(struct tme_posix_tape *posix_tape) 298: { 299: 300: /* close and free all tape segments: */ 301: _tme_posix_tape_segments_close(posix_tape); 302: 303: return (TME_OK); 304: } 305: 306: /* this opens a tape segment and makes it the current segment: */ 307: static int 308: _tme_posix_tape_segment_open(struct tme_posix_tape *posix_tape, 309: struct tme_posix_tape_segment *segment) 310: { 311: 312: /* there is no current segment: */ 313: posix_tape->tme_posix_tape_segment_current = NULL; 314: 315: /* open the segment: */ 316: segment->tme_posix_tape_segment_fd 317: = open(segment->tme_posix_tape_segment_filename, 318: ((posix_tape->tme_posix_tape_flags & TME_POSIX_TAPE_FLAG_RO) 319: ? O_RDONLY 320: : O_RDWR)); 321: 322: /* if the open failed: */ 323: if (segment->tme_posix_tape_segment_fd < 0) { 324: return (errno); 325: } 326: 327: /* set the current segment: */ 328: posix_tape->tme_posix_tape_segment_current = segment; 329: return (TME_OK); 330: } 331: 332: /* this rewinds a tape: */ 333: static int 334: _tme_posix_tape_rewind(struct tme_posix_tape *posix_tape) 335: { 336: struct tme_posix_tape_segment *segment; 337: int rc; 338: 339: /* assume this will succeed: */ 340: rc = TME_OK; 341: 342: /* if there is a current segment and it's not the first 343: segment: */ 344: segment = posix_tape->tme_posix_tape_segment_current; 345: if (segment != NULL 346: && segment != posix_tape->tme_posix_tape_segments) { 347: 348: /* this must be a normal file: */ 349: assert (!segment->tme_posix_tape_segment_real_tape); 350: 351: /* if the segment is open, close it: */ 352: if (segment->tme_posix_tape_segment_fd >= 0) { 353: close(segment->tme_posix_tape_segment_fd); 354: segment->tme_posix_tape_segment_fd = -1; 355: } 356: } 357: 358: /* get the first segment: */ 359: segment = posix_tape->tme_posix_tape_segments; 360: 361: /* if this is a real tape: */ 362: if (segment->tme_posix_tape_segment_real_tape) { 363: 364: /* the tape must be open: */ 365: assert (segment->tme_posix_tape_segment_fd >= 0); 366: 367: /* XXX TBD */ 368: abort(); 369: } 370: 371: /* otherwise, this is a normal file: */ 372: else { 373: 374: /* if the file isn't open, open it, else 375: seek it to the beginning: */ 376: if (segment->tme_posix_tape_segment_fd < 0) { 377: rc = _tme_posix_tape_segment_open(posix_tape, 378: segment); 379: } 380: else { 381: if (lseek(segment->tme_posix_tape_segment_fd, 382: 0, SEEK_SET) != 0) { 383: rc = errno; 384: } 385: } 386: } 387: 388: return (rc); 389: } 390: 391: /* this skips file marks: */ 392: static int 393: _tme_posix_tape_mark_skip(struct tme_posix_tape *posix_tape, 394: unsigned int count, 395: int forward) 396: { 397: struct tme_posix_tape_segment *segment; 398: int rc; 399: 400: segment = posix_tape->tme_posix_tape_segment_current; 401: 402: /* if we are at end-of-media, or if we're skipping no marks, do 403: nothing: */ 404: if (segment == NULL 405: || count == 0) { 406: return (TME_OK); 407: } 408: 409: /* this segment must be a normal file: */ 410: assert (!segment->tme_posix_tape_segment_real_tape); 411: 412: /* if this segment is open, close it: */ 413: if (segment->tme_posix_tape_segment_fd >= 0) { 414: close(segment->tme_posix_tape_segment_fd); 415: segment->tme_posix_tape_segment_fd = -1; 416: } 417: 418: /* skip the marks: */ 419: for (; segment != NULL && count-- > 0; ) { 420: segment 421: = (forward 422: ? segment->tme_posix_tape_segment_next 423: : segment->tme_posix_tape_segment_prev); 424: } 425: 426: /* for now, if we are skipping in reverse we must have a segment - 427: what do we do otherwise? */ 428: assert (forward || segment != NULL); 429: 430: /* clear the active segment pointer: */ 431: posix_tape->tme_posix_tape_segment_current = NULL; 432: 433: /* if we have a segment, open it: */ 434: if (segment != NULL) { 435: rc = _tme_posix_tape_segment_open(posix_tape, 436: segment); 437: assert (rc == TME_OK); 438: } 439: 440: /* if we are skipping in reverse, we must leave the tape on the 441: beginning of media side of the file mark: */ 442: if (!forward) { 443: rc = (lseek(segment->tme_posix_tape_segment_fd, 444: 0, 445: SEEK_END) < 0); 446: assert (rc == 0); 447: } 448: 449: return (TME_OK); 450: } 451: 452: /* this finishes a transfer. it must be called with the mutex locked: */ 453: static int 454: _tme_posix_tape_xfer1(struct tme_posix_tape *posix_tape, 455: int *_flags, 456: unsigned long *_count_xfer, 457: unsigned long *_count_bytes, 458: int xfer_read) 459: { 460: struct tme_posix_tape_segment *segment; 461: unsigned long count_xfer, count_bytes_user, count_bytes_tape; 462: unsigned long xfer_factor, block_size; 463: long rc; 464: 465: /* assume that we will return no flags: */ 466: *_flags = 0; 467: 468: /* get the transfer count: */ 469: count_xfer = posix_tape->tme_posix_tape_buffer_count_xfer; 470: 471: /* get any forced block size: */ 472: block_size = posix_tape->tme_posix_tape_block_size_min; 473: if (block_size != posix_tape->tme_posix_tape_block_size_min) { 474: block_size = 0; 475: } 476: 477: /* if the request is for one or more blocks at a fixed block size: */ 478: if (posix_tape->tme_posix_tape_buffer_flags 479: & TME_TAPE_FLAG_FIXED) { 480: 481: /* the device must be in fixed block size mode: */ 482: xfer_factor = posix_tape->tme_posix_tape_block_size_fixed; 483: if (xfer_factor == 0) { 484: assert (block_size != 0); 485: xfer_factor = block_size; 486: } 487: else { 488: assert (block_size == 0 489: || block_size == xfer_factor); 490: block_size = xfer_factor; 491: } 492: } 493: 494: /* otherwise, the request is for one block at a variable block size: */ 495: else { 496: 497: /* the device might be forcing a block size: */ 498: xfer_factor = 1; 499: if (block_size == 0) { 500: block_size = count_xfer; 501: } 502: } 503: 504: /* calculate the byte count for the user: */ 505: count_bytes_user = count_xfer * xfer_factor; 506: 507: /* calculate the byte count for the tape. this is 508: the byte count for the user rounded up to the 509: block size: */ 510: count_bytes_tape = count_bytes_user % block_size; 511: count_bytes_tape 512: = (count_bytes_user 513: + (count_bytes_tape 514: ? (block_size 515: - count_bytes_tape) 516: : 0)); 517: 518: /* get the current segment: */ 519: segment = posix_tape->tme_posix_tape_segment_current; 520: 521: /* if this is a read: */ 522: if (xfer_read) { 523: 524: /* if we are out of segments: */ 525: if (segment == NULL) { 526: 527: /* act like we read zero bytes: */ 528: rc = 0; 529: } 530: 531: /* otherwise, do the read: */ 532: else { 533: 534: /* do the read: */ 535: rc = read(segment->tme_posix_tape_segment_fd, 536: posix_tape->tme_posix_tape_buffer_data, 537: count_bytes_user); 538: 539: /* if this segment is not a real tape, and we're expected to 540: transfer more bytes than the user requested, seek over the 541: extra bytes, leaving the medium "positioned after the block": */ 542: if (!segment->tme_posix_tape_segment_real_tape 543: && count_bytes_tape > count_bytes_user) { 544: lseek(segment->tme_posix_tape_segment_fd, 545: (count_bytes_tape - count_bytes_user), 546: SEEK_CUR); 547: } 548: } 549: } 550: 551: /* otherwise, this is a write: */ 552: else { 553: 554: /* we must have a segment and it must be a real tape: */ 555: assert (segment != NULL 556: && segment->tme_posix_tape_segment_real_tape); 557: 558: /* do the write: */ 559: rc = write(segment->tme_posix_tape_segment_fd, 560: posix_tape->tme_posix_tape_buffer_data, 561: count_bytes_user); 562: } 563: 564: /* if the transfer got an error: */ 565: if (rc < 0) { 566: 567: /* return the error: */ 568: /* XXX is this sufficient? */ 569: *_count_bytes = 0; 570: *_count_xfer = 0; 571: return (errno); 572: } 573: 574: /* otherwise, we transferred some or none of the data: */ 575: else { 576: 577: /* return the final byte count: */ 578: *_count_bytes = rc; 579: 580: /* if the request was for one or more blocks at a fixed block size: */ 581: if (posix_tape->tme_posix_tape_buffer_flags 582: & TME_TAPE_FLAG_FIXED) { 583: 584: /* return the number of blocks successfully transferred: */ 585: *_count_xfer = (rc / xfer_factor); 586: } 587: 588: /* otherwise, the request is for one block at a variable block size: */ 589: else { 590: 591: /* return the actual size of the block transferred: */ 592: 593: /* if the user asked for what we thought was a whole block, but 594: we transferred less, how much we did transfer is the actual 595: block size: */ 596: if (count_bytes_user == block_size 597: && (unsigned long) rc < block_size) { 598: *_count_xfer = rc; 599: } 600: 601: /* otherwise, if this segment is a real tape, we don't know how 602: long the block really was. for now, we just return what we 603: are using as the block size: */ 604: /* XXX it may be possible to do an ioctl to get the true 605: residual: */ 606: else if (segment->tme_posix_tape_segment_real_tape) { 607: *_count_xfer = block_size; 608: } 609: 610: /* otherwise, this segment isn't a real tape, so we can return 611: what we are using as the block size: */ 612: else { 613: *_count_xfer = block_size; 614: } 615: } 616: 617: /* if we didn't read as much from the tape as we were supposed to: */ 618: if ((unsigned long) rc < count_bytes_tape) { 619: 620: /* if we read an exact multiple of the block size (including 621: zero blocks), the read was short because of a file mark. 622: return the mark (EOF) condition: */ 623: if ((rc % block_size) == 0) { 624: 625: /* return the mark condition: */ 626: *_flags |= TME_TAPE_FLAG_MARK; 627: 628: /* if this segment was not a real tape device, skip the file 629: mark - i.e., move to the next segment: */ 630: if (segment != NULL 631: && !segment->tme_posix_tape_segment_real_tape) { 632: rc = _tme_posix_tape_mark_skip(posix_tape, 633: 1, TRUE); 634: assert (rc == TME_OK); 635: } 636: } 637: 638: /* otherwise, the read was short because we read a partial 639: block. return the incorrect length indication (ILI) 640: condition: */ 641: else { 642: *_flags |= TME_TAPE_FLAG_ILI; 643: } 644: } 645: } 646: 647: return (TME_OK); 648: } 649: 650: /* this starts a transfer. it must be called with the mutex locked: */ 651: static int 652: _tme_posix_tape_xfer0(struct tme_posix_tape *posix_tape, 653: int flags, 654: unsigned long count_xfer, 655: unsigned long *_count_bytes) 656: { 657: unsigned long xfer_factor, count_bytes; 658: int old_flags; 659: unsigned long old_count_xfer, old_count_bytes; 660: int rc; 661: 662: /* if the buffer is dirty: */ 663: if (posix_tape->tme_posix_tape_flags 664: & TME_POSIX_TAPE_FLAG_DIRTY) { 665: 666: /* write out the buffer: */ 667: rc = _tme_posix_tape_xfer1(posix_tape, 668: &old_flags, 669: &old_count_xfer, 670: &old_count_bytes, 671: FALSE); 672: assert (rc == TME_OK); 673: 674: /* this buffer is no longer dirty: */ 675: posix_tape->tme_posix_tape_flags 676: &= ~TME_POSIX_TAPE_FLAG_DIRTY; 677: } 678: 679: /* set the parameters of this transfer: */ 680: posix_tape->tme_posix_tape_buffer_flags = flags; 681: posix_tape->tme_posix_tape_buffer_count_xfer = count_xfer; 682: 683: /* if the request is for one or more blocks at a fixed block size: */ 684: if (posix_tape->tme_posix_tape_buffer_flags 685: & TME_TAPE_FLAG_FIXED) { 686: 687: /* it is an error if the device isn't in fixed block size mode: */ 688: xfer_factor = posix_tape->tme_posix_tape_block_size_fixed; 689: if (xfer_factor == 0) { 690: xfer_factor = posix_tape->tme_posix_tape_block_size_min; 691: if (xfer_factor != posix_tape->tme_posix_tape_block_size_max) { 692: return (EINVAL); 693: } 694: } 695: } 696: 697: /* otherwise, the request is for one block at a variable block size: */ 698: else { 699: xfer_factor = 1; 700: } 701: 702: /* calculate the byte count: */ 703: count_bytes = count_xfer * xfer_factor; 704: 705: /* if the buffer isn't big enough: */ 706: if (posix_tape->tme_posix_tape_buffer_size 707: < count_bytes) { 708: 709: /* resize the buffer: */ 710: posix_tape->tme_posix_tape_buffer_size 711: = count_bytes; 712: posix_tape->tme_posix_tape_buffer_data 713: = tme_renew(tme_uint8_t, 714: posix_tape->tme_posix_tape_buffer_data, 715: posix_tape->tme_posix_tape_buffer_size); 716: } 717: 718: /* done: */ 719: *_count_bytes = count_bytes; 720: return (TME_OK); 721: } 722: 723: /* this releases the current buffer: */ 724: static int 725: _tme_posix_tape_release(struct tme_tape_connection *conn_tape, 726: int *_flags, 727: unsigned long *_count_xfer) 728: { 729: struct tme_posix_tape *posix_tape; 730: unsigned long count_bytes; 731: int rc; 732: 733: /* recover our data structure: */ 734: posix_tape = (struct tme_posix_tape *) conn_tape->tme_tape_connection.tme_connection_element->tme_element_private; 735: 736: /* assume that this call will succeed: */ 737: rc = TME_OK; 738: 739: /* lock the mutex: */ 740: tme_mutex_lock(&posix_tape->tme_posix_tape_mutex); 741: 742: /* if the buffer is dirty: */ 743: rc = TME_OK; 744: if (posix_tape->tme_posix_tape_flags 745: & TME_POSIX_TAPE_FLAG_DIRTY) { 746: 747: /* write out the buffer: */ 748: rc = _tme_posix_tape_xfer1(posix_tape, 749: _flags, 750: _count_xfer, 751: &count_bytes, 752: FALSE); 753: 754: /* this buffer is no longer dirty: */ 755: posix_tape->tme_posix_tape_flags 756: &= ~TME_POSIX_TAPE_FLAG_DIRTY; 757: } 758: 759: /* unlock the mutex: */ 760: tme_mutex_unlock(&posix_tape->tme_posix_tape_mutex); 761: 762: return (rc); 763: } 764: 765: /* this returns a read buffer: */ 766: static int 767: _tme_posix_tape_read(struct tme_tape_connection *conn_tape, 768: int *_flags, 769: unsigned long *_count_xfer, 770: unsigned long *_count_bytes, 771: const tme_uint8_t **_buffer) 772: { 773: struct tme_posix_tape *posix_tape; 774: int rc; 775: 776: /* recover our data structure: */ 777: posix_tape = (struct tme_posix_tape *) conn_tape->tme_tape_connection.tme_connection_element->tme_element_private; 778: 779: /* lock the mutex: */ 780: tme_mutex_lock(&posix_tape->tme_posix_tape_mutex); 781: 782: /* start the transfer: */ 783: rc = _tme_posix_tape_xfer0(posix_tape, 784: *_flags, 785: *_count_xfer, 786: _count_bytes); 787: *_buffer = posix_tape->tme_posix_tape_buffer_data; 788: 789: /* if the start succeeded, finish the transfer: */ 790: if (rc == TME_OK) { 791: rc = _tme_posix_tape_xfer1(posix_tape, 792: _flags, 793: _count_xfer, 794: _count_bytes, 795: TRUE); 796: } 797: 798: /* unlock the mutex: */ 799: tme_mutex_unlock(&posix_tape->tme_posix_tape_mutex); 800: 801: return (rc); 802: } 803: 804: /* this returns a write buffer: */ 805: static int 806: _tme_posix_tape_write(struct tme_tape_connection *conn_tape, 807: int flags, 808: unsigned long count_xfer, 809: unsigned long *_count_bytes, 810: tme_uint8_t **_buffer) 811: { 812: struct tme_posix_tape *posix_tape; 813: int rc; 814: 815: /* recover our data structure: */ 816: posix_tape = (struct tme_posix_tape *) conn_tape->tme_tape_connection.tme_connection_element->tme_element_private; 817: 818: /* lock the mutex: */ 819: tme_mutex_lock(&posix_tape->tme_posix_tape_mutex); 820: 821: /* start the transfer: */ 822: rc = _tme_posix_tape_xfer0(posix_tape, 823: flags, 824: count_xfer, 825: _count_bytes); 826: *_buffer = posix_tape->tme_posix_tape_buffer_data; 827: 828: /* unlock the mutex: */ 829: tme_mutex_unlock(&posix_tape->tme_posix_tape_mutex); 830: 831: return (rc); 832: } 833: 834: /* the tape control handler: */ 835: #ifdef HAVE_STDARG_H 836: static int _tme_posix_tape_control(struct tme_tape_connection *conn_tape, 837: unsigned int control, 838: ...) 839: #else /* HAVE_STDARG_H */ 840: static int _tme_posix_tape_control(conn_tape, control, va_alist) 841: struct tme_tape_connection *conn_tape; 842: unsigned int control; 843: va_dcl 844: #endif /* HAVE_STDARG_H */ 845: { 846: struct tme_posix_tape *posix_tape; 847: unsigned long *sizes; 848: const unsigned long *csizes; 849: unsigned int count; 850: va_list control_args; 851: int *_flags; 852: int rc; 853: 854: /* recover our data structure: */ 855: posix_tape = (struct tme_posix_tape *) conn_tape->tme_tape_connection.tme_connection_element->tme_element_private; 856: 857: /* lock the mutex: */ 858: tme_mutex_lock(&posix_tape->tme_posix_tape_mutex); 859: 860: /* start the variable arguments: */ 861: #ifdef HAVE_STDARG_H 862: va_start(control_args, control); 863: #else /* HAVE_STDARG_H */ 864: va_start(control_args); 865: #endif /* HAVE_STDARG_H */ 866: 867: /* dispatch on the sequence type: */ 868: switch (control) { 869: 870: case TME_TAPE_CONTROL_LOAD: 871: _flags = va_arg(control_args, int *); 872: *_flags = (posix_tape->tme_posix_tape_segments != NULL); 873: rc = TME_OK; 874: break; 875: 876: case TME_TAPE_CONTROL_UNLOAD: 877: rc = _tme_posix_tape_unload(posix_tape); 878: break; 879: 880: case TME_TAPE_CONTROL_MARK_WRITE: 881: abort(); 882: 883: case TME_TAPE_CONTROL_DENSITY_GET: 884: abort(); 885: 886: case TME_TAPE_CONTROL_DENSITY_SET: 887: abort(); 888: 889: case TME_TAPE_CONTROL_BLOCK_SIZE_GET: 890: sizes = va_arg(control_args, unsigned long *); 891: sizes[0] = posix_tape->tme_posix_tape_block_size_min; 892: sizes[1] = posix_tape->tme_posix_tape_block_size_max; 893: sizes[2] = posix_tape->tme_posix_tape_block_size_fixed; 894: rc = TME_OK; 895: break; 896: 897: case TME_TAPE_CONTROL_BLOCK_SIZE_SET: 898: csizes = va_arg(control_args, const unsigned long *); 899: 900: /* the minimum block size must be less than or equal to 901: the maximum block size: */ 902: if (csizes[0] > csizes[1]) { 903: return (EINVAL); 904: } 905: 906: /* if we aren't given a fixed block size: */ 907: if (csizes[2] == 0) { 908: 909: /* if the minimum and maximum block sizes are the same, 910: we are in fixed block size mode: */ 911: if (csizes[0] == csizes[1]) { 912: posix_tape->tme_posix_tape_block_size_fixed = csizes[0]; 913: } 914: 915: /* otherwise, we are not in fixed block size mode: */ 916: else { 917: posix_tape->tme_posix_tape_block_size_fixed = 0; 918: } 919: } 920: 921: /* otherwise, we are given a fixed block size. it must 922: be within the minimum and maximum block sizes: */ 923: else { 924: if (csizes[2] < csizes[0] 925: || csizes[2] > csizes[1]) { 926: return (EINVAL); 927: } 928: posix_tape->tme_posix_tape_block_size_fixed = csizes[2]; 929: } 930: 931: /* set the minimum and maximum block sizes: */ 932: posix_tape->tme_posix_tape_block_size_min = csizes[0]; 933: posix_tape->tme_posix_tape_block_size_max = csizes[1]; 934: rc = TME_OK; 935: break; 936: 937: case TME_TAPE_CONTROL_MARK_SKIPF: 938: case TME_TAPE_CONTROL_MARK_SKIPR: 939: count = va_arg(control_args, unsigned int); 940: rc = _tme_posix_tape_mark_skip(posix_tape, 941: count, 942: (control 943: == TME_TAPE_CONTROL_MARK_SKIPF)); 944: break; 945: 946: case TME_TAPE_CONTROL_REWIND: 947: rc = _tme_posix_tape_rewind(posix_tape); 948: break; 949: 950: default: 951: abort(); 952: } 953: 954: /* end the variable arguments: */ 955: va_end(control_args); 956: 957: /* unlock the mutex: */ 958: tme_mutex_unlock(&posix_tape->tme_posix_tape_mutex); 959: 960: return (rc); 961: } 962: 963: /* our internal command function: */ 964: static int 965: __tme_posix_tape_command(struct tme_posix_tape *posix_tape, 966: const char * const * args, 967: char **_output) 968: { 969: struct tme_posix_tape_segment *segment, **_prev; 970: struct tme_posix_tape_control *control; 971: struct stat statbuf; 972: int flags; 973: int arg_i; 974: int usage; 975: int rc; 976: int new_callouts; 977: 978: /* assume we won't need any new callouts: */ 979: new_callouts = 0; 980: 981: /* check the command: */ 982: usage = FALSE; 983: arg_i = 1; 984: 985: /* the "load" command: */ 986: if (TME_ARG_IS(args[arg_i], "load")) { 987: arg_i++; 988: 989: /* if a tape is currently loaded, it must be unloaded first: */ 990: if (posix_tape->tme_posix_tape_segments != NULL) { 991: tme_output_append_error(_output, 992: _("%s: tape already loaded; must unload first"), 993: args[0]); 994: return (EBUSY); 995: } 996: 997: /* check for flags, which must all come before the tape segment 998: filenames: */ 999: flags = 0; 1000: for (;;) { 1001: 1002: /* the "read-only" flag: */ 1003: if (TME_ARG_IS(args[arg_i], "read-only")) { 1004: flags |= TME_POSIX_TAPE_FLAG_RO; 1005: arg_i++; 1006: } 1007: 1008: else { 1009: break; 1010: } 1011: } 1012: 1013: /* all of the remaining arguments must be tape segment filenames. 1014: assume that all segments open successfully: */ 1015: rc = TME_OK; 1016: 1017: /* open the tape segments: */ 1018: segment = NULL; 1019: posix_tape->tme_posix_tape_segments = NULL; 1020: for (_prev = &posix_tape->tme_posix_tape_segments; 1021: ; 1022: _prev = &segment->tme_posix_tape_segment_next) { 1023: 1024: /* stop if we're out of filenames: */ 1025: if (args[arg_i] == NULL) { 1026: break; 1027: } 1028: 1029: /* allocate a new tape segment and link it in: */ 1030: segment = tme_new0(struct tme_posix_tape_segment, 1); 1031: segment->tme_posix_tape_segment_filename 1032: = tme_strdup(args[arg_i]); 1033: segment->tme_posix_tape_segment_next = NULL; 1034: segment->tme_posix_tape_segment_prev = *_prev; 1035: *_prev = segment; 1036: 1037: /* open the segment file: */ 1038: segment->tme_posix_tape_segment_fd 1039: = open(segment->tme_posix_tape_segment_filename, 1040: O_RDONLY); 1041: if (segment->tme_posix_tape_segment_fd < 0) { 1042: rc = errno; 1043: break; 1044: } 1045: 1046: /* stat the segment file: */ 1047: if (fstat(segment->tme_posix_tape_segment_fd, 1048: &statbuf) < 0) { 1049: rc = errno; 1050: break; 1051: } 1052: 1053: /* if this is a block device: */ 1054: if (S_ISBLK(statbuf.st_mode)) { 1055: tme_output_append_error(_output, 1056: _("cannot use a block device: %s"), 1057: args[arg_i]); 1058: rc = EINVAL; 1059: break; 1060: } 1061: 1062: /* if this is a character device: */ 1063: else if (S_ISCHR(statbuf.st_mode)) { 1064: 1065: /* if this is not the only segment: */ 1066: if (posix_tape->tme_posix_tape_segments != segment 1067: || args[arg_i + 1] != NULL) { 1068: tme_output_append_error(_output, 1069: _("a real device must be the only tape segment: ")); 1070: rc = EINVAL; 1071: break; 1072: } 1073: 1074: /* this is a real tape: */ 1075: segment->tme_posix_tape_segment_real_tape = TRUE; 1076: } 1077: 1078: /* otherwise, this is a regular file: */ 1079: else { 1080: 1081: /* this tape must be read-only: */ 1082: flags |= TME_POSIX_TAPE_FLAG_RO; 1083: } 1084: 1085: /* if this is not the first segment, close the file: */ 1086: if (posix_tape->tme_posix_tape_segments != segment) { 1087: close(segment->tme_posix_tape_segment_fd); 1088: segment->tme_posix_tape_segment_fd = -1; 1089: } 1090: 1091: /* advance to the next segment filename: */ 1092: arg_i++; 1093: } 1094: 1095: /* if we got an error: */ 1096: if (rc != TME_OK) { 1097: 1098: /* append any segment filename to the error: */ 1099: if (segment != NULL) { 1100: tme_output_append_error(_output, 1101: "%s", 1102: segment->tme_posix_tape_segment_filename); 1103: } 1104: 1105: /* close and free the segments: */ 1106: _tme_posix_tape_segments_close(posix_tape); 1107: } 1108: 1109: /* otherwise, if we opened no segments: */ 1110: else if (posix_tape->tme_posix_tape_segments == NULL) { 1111: tme_output_append_error(_output, 1112: "%s %s load [read-only] { %s | %s [ .. %s ] }", 1113: _("usage:"), 1114: args[0], 1115: _("DEVICE"), 1116: _("FILENAME"), 1117: _("FILENAME")); 1118: rc = EINVAL; 1119: } 1120: 1121: /* otherwise, a tape has now been loaded: */ 1122: else { 1123: 1124: /* a tape has now been loaded: */ 1125: posix_tape->tme_posix_tape_flags 1126: = flags; 1127: 1128: /* the first segment is still open. make it the current segment: */ 1129: posix_tape->tme_posix_tape_segment_current 1130: = posix_tape->tme_posix_tape_segments; 1131: 1132: /* call out a LOAD control: */ 1133: control = _tme_posix_tape_control_new(posix_tape); 1134: control->tme_posix_tape_control_which = TME_TAPE_CONTROL_LOAD; 1135: new_callouts |= TME_POSIX_TAPE_CALLOUT_CONTROL; 1136: } 1137: } 1138: 1139: /* the "unload" command: */ 1140: else if (TME_ARG_IS(args[arg_i], "unload")) { 1141: 1142: /* if no tape is currently loaded: */ 1143: if (posix_tape->tme_posix_tape_segments == NULL) { 1144: tme_output_append_error(_output, 1145: _("%s: no tape loaded"), 1146: args[0]); 1147: return (ENXIO); 1148: } 1149: 1150: /* we must have no arguments: */ 1151: if (args[arg_i + 1] != NULL) { 1152: tme_output_append_error(_output, 1153: "%s %s unload", 1154: _("usage:"), 1155: args[0]); 1156: return (EINVAL); 1157: } 1158: 1159: /* unload the tape: */ 1160: _tme_posix_tape_unload(posix_tape); 1161: 1162: /* call out an UNLOAD control: */ 1163: control = _tme_posix_tape_control_new(posix_tape); 1164: control->tme_posix_tape_control_which = TME_TAPE_CONTROL_UNLOAD; 1165: new_callouts |= TME_POSIX_TAPE_CALLOUT_CONTROL; 1166: rc = TME_OK; 1167: } 1168: 1169: /* any other command: */ 1170: else { 1171: if (args[arg_i] != NULL) { 1172: tme_output_append_error(_output, 1173: "%s '%s', ", 1174: _("unknown command"), 1175: args[1]); 1176: } 1177: tme_output_append_error(_output, 1178: _("available %s commands: %s"), 1179: args[0], 1180: "load unload"); 1181: return (EINVAL); 1182: } 1183: 1184: /* make any new callouts: */ 1185: _tme_posix_tape_callout(posix_tape, new_callouts); 1186: 1187: return (rc); 1188: } 1189: 1190: /* our command function: */ 1191: static int 1192: _tme_posix_tape_command(struct tme_element *element, 1193: const char * const * args, 1194: char **_output) 1195: { 1196: struct tme_posix_tape *posix_tape; 1197: int rc; 1198: 1199: /* recover our data structure: */ 1200: posix_tape = (struct tme_posix_tape *) element->tme_element_private; 1201: 1202: /* lock the mutex: */ 1203: tme_mutex_lock(&posix_tape->tme_posix_tape_mutex); 1204: 1205: /* call the internal command function: */ 1206: rc = __tme_posix_tape_command(posix_tape, 1207: args, 1208: _output); 1209: 1210: /* unlock the mutex: */ 1211: tme_mutex_unlock(&posix_tape->tme_posix_tape_mutex); 1212: 1213: return (rc); 1214: } 1215: 1216: /* this breaks a posix tape connection: */ 1217: static int 1218: _tme_posix_tape_connection_break(struct tme_connection *conn, 1219: unsigned int state) 1220: { 1221: abort(); 1222: } 1223: 1224: /* this makes a posix tape connection: */ 1225: static int 1226: _tme_posix_tape_connection_make(struct tme_connection *conn, 1227: unsigned int state) 1228: { 1229: struct tme_posix_tape *posix_tape; 1230: struct tme_tape_connection *conn_tape; 1231: 1232: /* recover our data structure: */ 1233: posix_tape = (struct tme_posix_tape *) conn->tme_connection_element->tme_element_private; 1234: 1235: /* both sides must be tape connections: */ 1236: assert(conn->tme_connection_type == TME_CONNECTION_TAPE); 1237: assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_TAPE); 1238: 1239: /* we're always set up to answer calls across the connection, 1240: so we only have to do work when the connection has gone full, 1241: namely taking the other side of the connection: */ 1242: if (state == TME_CONNECTION_FULL) { 1243: 1244: /* lock the mutex: */ 1245: tme_mutex_lock(&posix_tape->tme_posix_tape_mutex); 1246: 1247: /* save this connection to our list of connections: */ 1248: conn_tape = (struct tme_tape_connection *) conn->tme_connection_other; 1249: posix_tape->tme_posix_tape_connection = conn_tape; 1250: 1251: /* unlock the mutex: */ 1252: tme_mutex_unlock(&posix_tape->tme_posix_tape_mutex); 1253: } 1254: 1255: return (TME_OK); 1256: } 1257: 1258: /* this makes a new connection side for a posix tape: */ 1259: static int 1260: _tme_posix_tape_connections_new(struct tme_element *element, 1261: const char * const *args, 1262: struct tme_connection **_conns, 1263: char **_output) 1264: { 1265: struct tme_posix_tape *posix_tape; 1266: struct tme_tape_connection *conn_tape; 1267: struct tme_connection *conn; 1268: 1269: /* recover our data structure: */ 1270: posix_tape = (struct tme_posix_tape *) element->tme_element_private; 1271: 1272: /* if we already have a connection, there's nothing to do: */ 1273: if (posix_tape->tme_posix_tape_connection != NULL) { 1274: return (TME_OK); 1275: } 1276: 1277: /* create our side of a tape connection: */ 1278: conn_tape = tme_new0(struct tme_tape_connection, 1); 1279: conn = &conn_tape->tme_tape_connection; 1280: 1281: /* fill in the generic connection: */ 1282: conn->tme_connection_next = *_conns; 1283: conn->tme_connection_type = TME_CONNECTION_TAPE; 1284: conn->tme_connection_score = tme_tape_connection_score; 1285: conn->tme_connection_make = _tme_posix_tape_connection_make; 1286: conn->tme_connection_break = _tme_posix_tape_connection_break; 1287: 1288: /* fill in the tape connection: */ 1289: conn_tape->tme_tape_connection_read 1290: = _tme_posix_tape_read; 1291: conn_tape->tme_tape_connection_write 1292: = _tme_posix_tape_write; 1293: conn_tape->tme_tape_connection_release 1294: = _tme_posix_tape_release; 1295: conn_tape->tme_tape_connection_control 1296: = _tme_posix_tape_control; 1297: 1298: /* return the connection side possibility: */ 1299: *_conns = conn; 1300: return (TME_OK); 1301: } 1302: 1303: /* the new posix tape function: */ 1304: TME_ELEMENT_SUB_NEW_DECL(tme_host_posix,tape) { 1305: struct tme_posix_tape *posix_tape; 1306: int flags; 1307: int arg_i; 1308: int usage; 1309: 1310: /* check our arguments: */ 1311: flags = 0; 1312: arg_i = 1; 1313: usage = FALSE; 1314: 1315: /* loop reading our arguments: */ 1316: for (;;) { 1317: 1318: if (0) { 1319: } 1320: 1321: /* if we've run out of arguments: */ 1322: else if (args[arg_i + 0] == NULL) { 1323: 1324: break; 1325: } 1326: 1327: /* this is a bad argument: */ 1328: else { 1329: tme_output_append_error(_output, 1330: "%s %s", 1331: args[arg_i], 1332: _("unexpected")); 1333: usage = TRUE; 1334: break; 1335: } 1336: } 1337: 1338: if (usage) { 1339: tme_output_append_error(_output, 1340: "%s %s", 1341: _("usage:"), 1342: args[0]); 1343: return (EINVAL); 1344: } 1345: 1346: /* start the tape structure: */ 1347: posix_tape = tme_new0(struct tme_posix_tape, 1); 1348: posix_tape->tme_posix_tape_element = element; 1349: tme_mutex_init(&posix_tape->tme_posix_tape_mutex); 1350: posix_tape->tme_posix_tape_flags = flags; 1351: posix_tape->tme_posix_tape_segments = NULL; 1352: posix_tape->tme_posix_tape_segment_current = NULL; 1353: 1354: /* XXX these are fake values for now: */ 1355: posix_tape->tme_posix_tape_block_size_min = 512; 1356: posix_tape->tme_posix_tape_block_size_max = 32768; 1357: posix_tape->tme_posix_tape_block_size_fixed = 0; 1358: 1359: /* start the tape buffer. the 16384 isn't magic, it's just a 1360: starting point: */ 1361: posix_tape->tme_posix_tape_buffer_size = 16384; 1362: posix_tape->tme_posix_tape_buffer_data 1363: = tme_new(tme_uint8_t, 1364: posix_tape->tme_posix_tape_buffer_size); 1365: 1366: /* fill the element: */ 1367: element->tme_element_private = posix_tape; 1368: element->tme_element_connections_new = _tme_posix_tape_connections_new; 1369: element->tme_element_command = _tme_posix_tape_command; 1370: 1371: return (TME_OK); 1372: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.