1 /* $Id: posix-tape.c,v 1.7 2006/09/30 12:35:01 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.7 2006/09/30 12:35:01 fredette Exp $");
38
39 /* includes: */
40 #include <tme/generic/tape.h>
41 #include <tme/threads.h>
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 *
_tme_posix_tape_control_new(struct tme_posix_tape * posix_tape)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
_tme_posix_tape_callout(struct tme_posix_tape * posix_tape,int new_callouts)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
_tme_posix_tape_segments_close(struct tme_posix_tape * posix_tape)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
_tme_posix_tape_unload(struct tme_posix_tape * posix_tape)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
_tme_posix_tape_segment_open(struct tme_posix_tape * posix_tape,struct tme_posix_tape_segment * segment)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
_tme_posix_tape_rewind(struct tme_posix_tape * posix_tape)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
_tme_posix_tape_mark_skip(struct tme_posix_tape * posix_tape,unsigned int count,int forward)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
_tme_posix_tape_xfer1(struct tme_posix_tape * posix_tape,int * _flags,unsigned long * _count_xfer,unsigned long * _count_bytes,int xfer_read)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
_tme_posix_tape_xfer0(struct tme_posix_tape * posix_tape,int flags,unsigned long count_xfer,unsigned long * _count_bytes)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
_tme_posix_tape_release(struct tme_tape_connection * conn_tape,int * _flags,unsigned long * _count_xfer)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
_tme_posix_tape_read(struct tme_tape_connection * conn_tape,int * _flags,unsigned long * _count_xfer,unsigned long * _count_bytes,const tme_uint8_t ** _buffer)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
_tme_posix_tape_write(struct tme_tape_connection * conn_tape,int flags,unsigned long count_xfer,unsigned long * _count_bytes,tme_uint8_t ** _buffer)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
_tme_posix_tape_control(struct tme_tape_connection * conn_tape,unsigned int control,...)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
__tme_posix_tape_command(struct tme_posix_tape * posix_tape,const char * const * args,char ** _output)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
_tme_posix_tape_command(struct tme_element * element,const char * const * args,char ** _output)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
_tme_posix_tape_connection_break(struct tme_connection * conn,unsigned int state)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
_tme_posix_tape_connection_make(struct tme_connection * conn,unsigned int state)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
_tme_posix_tape_connections_new(struct tme_element * element,const char * const * args,struct tme_connection ** _conns,char ** _output)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: */
TME_ELEMENT_SUB_NEW_DECL(tme_host_posix,tape)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 }
1373