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