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