Imported Upstream version 2.6.0
[debian/amanda] / device-src / tape-device.c
1 /*
2  * Copyright (c) 2005 Zmanda, Inc.  All Rights Reserved.
3  * 
4  * This library is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License version 2.1 as 
6  * published by the Free Software Foundation.
7  * 
8  * This library is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
11  * License for more details.
12  * 
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this library; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
16  * 
17  * Contact information: Zmanda Inc., 505 N Mathlida Ave, Suite 120
18  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
19  */
20
21 #include <string.h> /* memset() */
22 #include "util.h"
23 #include "tape-device.h"
24 #include "tape-ops.h"
25
26 /* This is equal to 2*1024*1024*1024 - 16*1024*1024 - 1, but written 
27    explicitly to avoid overflow issues. */
28 #define RESETOFS_THRESHOLD (0x7effffff)
29
30 /* Largest possible block size on SCSI systems. */
31 #define LARGEST_BLOCK_ESTIMATE (16 * 1024 * 1024)
32
33 struct TapeDevicePrivate_s {
34     /* This holds the total number of bytes written to the device,
35        modulus RESETOFS_THRESHOLD. */
36     int write_count;
37 };
38
39 /* Possible (abstracted) results from a system I/O operation. */
40 typedef enum {
41     RESULT_SUCCESS,
42     RESULT_ERROR,        /* Undefined error. */
43     RESULT_SMALL_BUFFER, /* Tried to read with a buffer that is too
44                             small. */
45     RESULT_NO_DATA,      /* End of File, while reading */
46     RESULT_NO_SPACE,     /* Out of space. Sometimes we don't know if
47                             it was this or I/O error, but this is the
48                             preferred explanation. */
49     RESULT_MAX
50 } IoResult;
51
52 /* here are local prototypes */
53 static void tape_device_init (TapeDevice * o);
54 static void tape_device_class_init (TapeDeviceClass * c);
55 static gboolean tape_device_open_device (Device * self, char * device_name);
56 static ReadLabelStatusFlags tape_device_read_label(Device * self);
57 static gboolean tape_device_write_block(Device * self, guint size,
58                                         gpointer data, gboolean short_block);
59 static gboolean tape_device_read_block(Device * self,  gpointer buf,
60                                        int * size_req);
61 static gboolean tape_device_start (Device * self, DeviceAccessMode mode,
62                                    char * label, char * timestamp);
63 static gboolean tape_device_start_file (Device * self, const dumpfile_t * ji);
64 static dumpfile_t * tape_device_seek_file (Device * self, guint file);
65 static gboolean tape_device_seek_block (Device * self, guint64 block);
66 static gboolean tape_device_property_get (Device * self, DevicePropertyId id,
67                                           GValue * val);
68 static gboolean tape_device_property_set (Device * self, DevicePropertyId id,
69                                           GValue * val);
70 static gboolean tape_device_finish (Device * self);
71 static IoResult tape_device_robust_read (TapeDevice * self, void * buf,
72                                                int * count);
73 static IoResult tape_device_robust_write (TapeDevice * self, void * buf, int count);
74 static gboolean tape_device_fsf (TapeDevice * self, guint count);
75 static gboolean tape_device_bsf (TapeDevice * self, guint count, guint file);
76 static gboolean tape_device_fsr (TapeDevice * self, guint count);
77 static gboolean tape_device_bsr (TapeDevice * self, guint count, guint file, guint block);
78 static gboolean tape_device_eod (TapeDevice * self);
79
80 /* pointer to the class of our parent */
81 static DeviceClass *parent_class = NULL;
82
83 GType tape_device_get_type (void)
84 {
85     static GType type = 0;
86     
87     if G_UNLIKELY(type == 0) {
88         static const GTypeInfo info = {
89             sizeof (TapeDeviceClass),
90             (GBaseInitFunc) NULL,
91             (GBaseFinalizeFunc) NULL,
92             (GClassInitFunc) tape_device_class_init,
93             (GClassFinalizeFunc) NULL,
94             NULL /* class_data */,
95             sizeof (TapeDevice),
96             0 /* n_preallocs */,
97             (GInstanceInitFunc) tape_device_init,
98             NULL
99         };
100         
101         type = g_type_register_static (TYPE_DEVICE, "TapeDevice",
102                                        &info, (GTypeFlags)0);
103     }
104
105     return type;
106 }
107
108 static void 
109 tape_device_init (TapeDevice * self) {
110     Device * device_self;
111     DeviceProperty prop;
112     GValue response;
113
114     device_self = (Device*)self;
115     bzero(&response, sizeof(response));
116
117     self->private = malloc(sizeof(TapeDevicePrivate));
118
119     /* Clear all fields. */
120     self->min_block_size = self->fixed_block_size = 32768;
121     self->max_block_size = self->read_block_size = MAX_TAPE_BLOCK_BYTES;
122
123     self->fd = -1;
124     
125     self->fsf = self->bsf = self->fsr = self->bsr = self->eom =
126         self->bsf_after_eom = self->compression = self->first_file = 0;
127     self->final_filemarks = 2;
128
129     self->private->write_count = 0;
130
131     /* Register properites */
132     prop.base = &device_property_concurrency;
133     prop.access = PROPERTY_ACCESS_GET_MASK;
134     g_value_init(&response, CONCURRENCY_PARADIGM_TYPE);
135     g_value_set_enum(&response, CONCURRENCY_PARADIGM_EXCLUSIVE);
136     device_add_property(device_self, &prop, &response);
137     g_value_unset(&response);
138
139     prop.base = &device_property_streaming;
140     g_value_init(&response, STREAMING_REQUIREMENT_TYPE);
141     g_value_set_enum(&response, STREAMING_REQUIREMENT_DESIRED);
142     device_add_property(device_self, &prop, &response);
143     g_value_unset(&response);
144
145     prop.base = &device_property_appendable;
146     g_value_init(&response, G_TYPE_BOOLEAN);
147     g_value_set_boolean(&response, TRUE);
148     device_add_property(device_self, &prop, &response);
149
150     prop.base = &device_property_partial_deletion;
151     g_value_set_boolean(&response, FALSE);
152     device_add_property(device_self, &prop, &response);
153     g_value_unset(&response);
154
155     prop.base = &device_property_medium_access_type;
156     g_value_init(&response, MEDIA_ACCESS_MODE_TYPE);
157     g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE);
158     device_add_property(device_self, &prop, &response);
159     g_value_unset(&response);
160
161     prop.access = PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK;
162     prop.base = &device_property_compression;
163     device_add_property(device_self, &prop, NULL);
164
165     prop.access = PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START;
166     prop.base = &device_property_min_block_size;
167     device_add_property(device_self, &prop, NULL);
168     prop.base = &device_property_max_block_size;
169     device_add_property(device_self, &prop, NULL);
170     prop.base = &device_property_block_size;
171     device_add_property(device_self, &prop, NULL);
172     prop.base = &device_property_fsf;
173     device_add_property(device_self, &prop, NULL);
174     prop.base = &device_property_bsf;
175     device_add_property(device_self, &prop, NULL);
176     prop.base = &device_property_fsr;
177     device_add_property(device_self, &prop, NULL);
178     prop.base = &device_property_bsr;
179     device_add_property(device_self, &prop, NULL);
180     prop.base = &device_property_eom;
181     device_add_property(device_self, &prop, NULL);
182     prop.base = &device_property_bsf_after_eom;
183     device_add_property(device_self, &prop, NULL);
184     prop.base = &device_property_final_filemarks;
185     device_add_property(device_self, &prop, NULL);
186     
187     prop.access = PROPERTY_ACCESS_GET_MASK;
188     prop.base = &device_property_canonical_name;
189     device_add_property(device_self, &prop, NULL);
190 }
191
192 static void tape_device_finalize(GObject * obj_self) {
193     TapeDevice * self = TAPE_DEVICE(obj_self);
194
195     if(G_OBJECT_CLASS(parent_class)->finalize) \
196            (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
197
198     robust_close(self->fd);
199     self->fd = -1;
200     amfree(self->private);
201 }
202
203 static void 
204 tape_device_class_init (TapeDeviceClass * c)
205 {
206     DeviceClass *device_class = (DeviceClass *)c;
207     GObjectClass *g_object_class = (GObjectClass *)c;
208
209     parent_class = g_type_class_ref (TYPE_DEVICE);
210     
211     device_class->open_device = tape_device_open_device;
212     device_class->read_label = tape_device_read_label;
213     device_class->write_block = tape_device_write_block;
214     device_class->read_block = tape_device_read_block;
215     device_class->start = tape_device_start;
216     device_class->start_file = tape_device_start_file;
217     device_class->seek_file = tape_device_seek_file;
218     device_class->seek_block = tape_device_seek_block;
219     device_class->property_get = tape_device_property_get;
220     device_class->property_set = tape_device_property_set;
221     device_class->finish = tape_device_finish;
222     
223     g_object_class->finalize = tape_device_finalize;
224 }
225
226 void tape_device_register(void) {
227     static const char * device_prefix_list[] = { "tape", NULL };
228     register_device(tape_device_factory, device_prefix_list);
229 }
230
231 #ifdef O_NONBLOCK
232 /* Open the tape device, trying various combinations of O_RDWR and
233    O_NONBLOCK. */
234 static int try_open_tape_device(TapeDevice * self, char * device_name) {
235     int rval;
236     rval  = robust_open(device_name, O_RDWR | O_NONBLOCK, 0);
237     if (rval < 0 && (errno == EWOULDBLOCK || errno == EINVAL)) {
238         /* Maybe we don't support O_NONBLOCK for tape devices. */
239         rval = robust_open(device_name, O_RDWR, 0);
240     }
241     if (rval >= 0) {
242         self->write_open_errno = 0;
243     } else {
244         if (errno == EACCES || errno == EPERM) {
245             /* Device is write-protected. */
246             self->write_open_errno = errno;
247             rval = robust_open(device_name, O_RDONLY | O_NONBLOCK, 0);
248             if (rval < 0 && (errno == EWOULDBLOCK || errno == EINVAL)) {
249                 rval = robust_open(device_name, O_RDONLY, 0);
250             }
251         }
252     }
253     /* Clear O_NONBLOCK for operations from now on. */
254     fcntl(rval, F_SETFL, fcntl(rval, F_GETFL, 0) & ~O_NONBLOCK);
255     return rval;
256 }
257 #else /* !defined(O_NONBLOCK) */
258 static int try_open_tape_device(TapeDevice * self, char * device_name) {
259     int rval;
260     rval = robust_open(device_name, O_RDWR);
261     if (rval >= 0) {
262         self->write_open_errno = 0;
263     } else {
264         if (errno == EACCES || errno == EPERM) {
265             /* Device is write-protected. */
266             self->write_open_errno = errno;
267             rval = robust_open(device_name, O_RDONLY);
268         }
269     }
270     return rval;
271 }
272 #endif /* O_NONBLOCK */
273
274 static gboolean 
275 tape_device_open_device (Device * d_self, char * device_name) {
276     TapeDevice * self;
277
278     self = TAPE_DEVICE(d_self);
279     g_return_val_if_fail (self != NULL, FALSE);
280     g_return_val_if_fail (device_name != NULL, FALSE);
281
282     self->fd = try_open_tape_device(self, device_name);
283
284     if (self->fd < 0) {
285         g_fprintf(stderr, "Can't open tape device %s: %s\n",
286                 device_name, strerror(errno));
287         return FALSE;
288     }
289
290     /* Check that this is actually a tape device. */
291     if (tape_is_tape_device(self->fd) == TAPE_CHECK_FAILURE) {
292         g_fprintf(stderr, "File %s is not a tape device.\n",
293                 device_name);
294         robust_close(self->fd);
295         return FALSE;
296     }
297
298     if (tape_is_ready(self->fd) == TAPE_CHECK_FAILURE) {
299         g_fprintf(stderr,
300                   "Tape device %s is not ready or is empty.\n",
301                   device_name);
302         robust_close(self->fd);
303         return FALSE;
304     }
305
306     /* Rewind it. */
307     if (!tape_rewind(self->fd)) {
308         g_fprintf(stderr, "Error rewinding device %s\n",
309                 device_name);
310         robust_close(self->fd);
311         return FALSE;
312     }
313
314     /* Get tape drive/OS info */
315     tape_device_discover_capabilities(self);
316
317     /* And verify the above. */
318     g_assert(feature_support_flags_is_valid(self->fsf));
319     g_assert(feature_support_flags_is_valid(self->bsf));
320     g_assert(feature_support_flags_is_valid(self->fsr));
321     g_assert(feature_support_flags_is_valid(self->bsr));
322     g_assert(feature_support_flags_is_valid(self->eom));
323     g_assert(feature_support_flags_is_valid(self->bsf_after_eom));
324     g_assert(self->final_filemarks == 1 ||
325              self->final_filemarks == 2);
326
327     /* Chain up */
328     if (parent_class->open_device) {
329         if (!(parent_class->open_device)(d_self, device_name)) {
330             robust_close(self->fd);
331             return FALSE;
332         }
333     }
334
335     return TRUE;
336 }
337
338 static ReadLabelStatusFlags tape_device_read_label(Device * dself) {
339     TapeDevice * self;
340     char * header_buffer;
341     int buffer_len;
342     IoResult result;
343     dumpfile_t header;
344
345     self = TAPE_DEVICE(dself);
346     g_return_val_if_fail(self != NULL, FALSE);
347
348     if (!tape_rewind(self->fd)) {
349         g_fprintf(stderr, "Error rewinding device %s\n",
350                 dself->device_name);
351         return (READ_LABEL_STATUS_DEVICE_ERROR |
352                 READ_LABEL_STATUS_VOLUME_ERROR);
353     }   
354
355     buffer_len = self->read_block_size;
356     header_buffer = malloc(buffer_len);
357     result = tape_device_robust_read(self, header_buffer, &buffer_len);
358
359     if (result != RESULT_SUCCESS) {
360         free(header_buffer);
361         tape_rewind(self->fd);
362         /* I/O error. */
363         g_fprintf(stderr, "Error reading Amanda header.\n");
364         if (result == RESULT_NO_DATA) {
365             return (READ_LABEL_STATUS_VOLUME_ERROR |
366                     READ_LABEL_STATUS_VOLUME_UNLABELED);
367         } else {
368             return (READ_LABEL_STATUS_DEVICE_ERROR |
369                     READ_LABEL_STATUS_VOLUME_ERROR |
370                     READ_LABEL_STATUS_VOLUME_UNLABELED);
371         }
372     }
373
374     parse_file_header(header_buffer, &header, buffer_len);
375     amfree(header_buffer);
376     if (header.type != F_TAPESTART) {
377         return READ_LABEL_STATUS_VOLUME_UNLABELED;
378     }
379      
380     dself->volume_label = g_strdup(header.name);
381     dself->volume_time = g_strdup(header.datestamp);
382    
383     if (parent_class->read_label) {
384         return parent_class->read_label(dself);
385     } else {
386         return READ_LABEL_STATUS_SUCCESS;
387     }
388 }
389
390 static gboolean
391 tape_device_write_block(Device * pself, guint size,
392                         gpointer data, gboolean short_block) {
393     TapeDevice * self;
394     char *replacement_buffer = NULL;
395     IoResult result;
396
397     self = TAPE_DEVICE(pself);
398     g_return_val_if_fail (self != NULL, FALSE);
399     g_return_val_if_fail (self->fd >= 0, FALSE);
400    
401     if (short_block && self->min_block_size > size) {
402         replacement_buffer = malloc(self->min_block_size);
403         memcpy(replacement_buffer, data, size);
404         bzero(replacement_buffer+size, self->min_block_size-size);
405         
406         data = replacement_buffer;
407         size = self->min_block_size;
408     }
409
410     result = tape_device_robust_write(self, data, size);
411     if (result == RESULT_SUCCESS) {
412         if (parent_class->write_block) {
413             (parent_class->write_block)(pself, size, data, short_block);
414         }
415         amfree(replacement_buffer);
416         return TRUE;
417     } else {
418         amfree(replacement_buffer);
419         return FALSE;
420     }
421     
422     g_assert_not_reached();
423 }
424
425 static int tape_device_read_block (Device * pself, gpointer buf,
426                                    int * size_req) {
427     TapeDevice * self;
428     int size;
429     IoResult result;
430     
431     self = TAPE_DEVICE(pself);
432     g_return_val_if_fail (self != NULL, -1);
433
434     if (buf == NULL || *size_req < (int)self->read_block_size) {
435         /* Just a size query. */
436         *size_req = self->read_block_size;
437         return 0;
438     }
439
440     size = *size_req;
441     result = tape_device_robust_read(self, buf, &size);
442     switch (result) {
443     case RESULT_SUCCESS:
444         *size_req = size;
445         return size;
446     case RESULT_SMALL_BUFFER: {
447         int new_size;
448         /* If this happens, it means that we have:
449          *     (next block size) > (buffer size) >= (read_block_size)
450          * The solution is to ask for an even bigger buffer. We also play
451          * some games to refrain from reading above the SCSI limit or from
452          * integer overflow. */
453         new_size = MIN(INT_MAX/2 - 1, *size_req) * 2;
454         if (new_size > LARGEST_BLOCK_ESTIMATE &&
455             *size_req < LARGEST_BLOCK_ESTIMATE) {
456             new_size = LARGEST_BLOCK_ESTIMATE;
457         }
458         if (new_size <= *size_req) {
459             return -1;
460         } else {
461             *size_req = new_size;
462             return 0;
463         }
464     }
465     case RESULT_NO_DATA:
466         pself->is_eof = TRUE;
467         /* FALLTHROUGH */
468     default:
469         return -1;
470     }
471
472     g_assert_not_reached();
473 }
474
475 /* Just a helper function for tape_device_start(). */
476 static gboolean write_tapestart_header(TapeDevice * self, char * label,
477                                        char * timestamp) {
478      IoResult result;
479      dumpfile_t * header;
480      char * header_buf;
481      int header_size;
482      gboolean header_fits;
483      Device * d_self = (Device*)self;
484      tape_rewind(self->fd);
485     
486      header = make_tapestart_header(d_self, label, timestamp);
487      g_assert(header != NULL);
488      header_buf = device_build_amanda_header(d_self, header, &header_size,
489                                              &header_fits);
490      amfree(header);
491      g_assert(header_buf != NULL);
492                                              
493      if (!header_fits) {
494          amfree(header_buf);
495          g_fprintf(stderr, "Tapestart header won't fit in a single block!\n");
496          return FALSE;
497      }
498
499      g_assert(header_size >= (int)self->min_block_size);
500      result = tape_device_robust_write(self, header_buf, header_size);
501      amfree(header_buf);
502      return (result == RESULT_SUCCESS);
503 }
504
505 static gboolean 
506 tape_device_start (Device * d_self, DeviceAccessMode mode, char * label,
507                    char * timestamp) {
508     TapeDevice * self;
509
510     self = TAPE_DEVICE(d_self);
511     g_return_val_if_fail(self != NULL, FALSE);
512     
513     if (IS_WRITABLE_ACCESS_MODE(mode)) {
514         if (self->write_open_errno != 0) {
515             /* We tried and failed to open the device in write mode. */
516             g_fprintf(stderr, "Can't open tape device %s for writing: %s\n",
517                     d_self->device_name, strerror(self->write_open_errno));
518             return FALSE;
519         } else if (!tape_rewind(self->fd)) {
520             g_fprintf(stderr, "Couldn't rewind device: %s\n",
521                     strerror(errno));
522         }
523     }
524
525     /* Position the tape */
526     switch (mode) {
527     case ACCESS_APPEND:
528         if (!tape_device_eod(self))
529             return FALSE;
530         self->first_file = TRUE;
531         break;
532         
533     case ACCESS_READ:
534         if (!tape_rewind(self->fd)) {
535             g_fprintf(stderr, "Error rewinding device %s\n",
536                     d_self->device_name);
537             return FALSE;
538         }
539         d_self->file = 0;
540         break;
541
542     case ACCESS_WRITE:
543         if (!write_tapestart_header(self, label, timestamp)) {
544             return FALSE;
545         }
546         self->first_file = TRUE;
547         break;
548
549     default:
550         g_assert_not_reached();
551     }
552
553     if (parent_class->start) {
554         return parent_class->start(d_self, mode, label, timestamp);
555     } else {
556         return TRUE;
557     }
558 }
559
560 static gboolean tape_device_start_file(Device * d_self,
561                                        const dumpfile_t * info) {
562     TapeDevice * self;
563     IoResult result;
564     char * amanda_header;
565     int header_size;
566     gboolean header_fits;
567
568     self = TAPE_DEVICE(d_self);
569     g_return_val_if_fail(self != NULL, FALSE);
570     g_return_val_if_fail (self->fd >= 0, FALSE);
571
572     if (!(d_self->access_mode == ACCESS_APPEND && self->first_file)) {
573         if (!tape_weof(self->fd, 1)) {
574             g_fprintf(stderr, "Error writing filemark: %s\n", strerror(errno));
575             return FALSE;
576         }
577     }
578
579     self->first_file = FALSE;
580
581     /* Make the Amanda header suitable for writing to the device. */
582     /* Then write the damn thing. */
583     amanda_header = device_build_amanda_header(d_self, info,
584                                                &header_size, &header_fits);
585     g_return_val_if_fail(amanda_header != NULL, FALSE);
586     g_return_val_if_fail(header_fits, FALSE);
587     result = tape_device_robust_write(self, amanda_header, header_size);
588     amfree(amanda_header);
589     if (result == RESULT_SUCCESS) {
590         /* Chain up. */
591         if (parent_class->start_file) {
592             parent_class->start_file(d_self, info);
593         }
594         return TRUE;
595     } else {
596         return FALSE;
597     }
598 }
599
600 static dumpfile_t * 
601 tape_device_seek_file (Device * d_self, guint file) {
602     TapeDevice * self;
603     int difference;
604     char * header_buffer;
605     dumpfile_t * rval;
606     int buffer_len;
607     IoResult result;
608
609     self = TAPE_DEVICE(d_self);
610     g_return_val_if_fail(d_self != NULL, NULL);
611
612     d_self->in_file = FALSE;
613
614     difference = file - d_self->file;
615
616     /* Check if we already read a filemark. */
617     if (d_self->is_eof) {
618         difference --;
619     }
620
621     if (difference > 0) {
622         /* Seeking forwards */
623         if (!tape_device_fsf(self, difference)) {
624             tape_rewind(self->fd);
625             return NULL;
626         }
627     } else if (difference < 0) {
628         /* Seeking backwards */
629         if (!tape_device_bsf(self, -difference, d_self->file)) {
630             tape_rewind(self->fd);
631             return NULL;
632         }
633     }
634
635     buffer_len = self->read_block_size;
636     header_buffer = malloc(buffer_len);
637     d_self->is_eof = FALSE;
638     result = tape_device_robust_read(self, header_buffer, &buffer_len);
639
640     if (result != RESULT_SUCCESS) {
641         free(header_buffer);
642         tape_rewind(self->fd);
643         if (result == RESULT_NO_DATA) {
644             /* If we read 0 bytes, that means we encountered a double
645              * filemark, which indicates end of tape. This should
646              * work even with QIC tapes on operating systems with
647              * proper support. */
648             return make_tapeend_header();
649         }
650         /* I/O error. */
651         g_fprintf(stderr, "Error reading Amanda header.\n");
652         return FALSE;
653     }
654         
655     rval = malloc(sizeof(*rval));
656     parse_file_header(header_buffer, rval, buffer_len);
657     amfree(header_buffer);
658     switch (rval->type) {
659     case F_DUMPFILE:
660     case F_CONT_DUMPFILE:
661     case F_SPLIT_DUMPFILE:
662         d_self->in_file = TRUE;
663         d_self->file = file;
664         return rval;
665     default:
666         tape_rewind(self->fd);
667         amfree(rval);
668         return NULL;
669     }
670 }
671
672 static gboolean 
673 tape_device_seek_block (Device * d_self, guint64 block) {
674     TapeDevice * self;
675     int difference;
676
677     self = TAPE_DEVICE(d_self);
678     g_return_val_if_fail(d_self != NULL, FALSE);
679
680     difference = block - d_self->block;
681     
682     if (difference > 0) {
683         if (!tape_device_fsr(self, difference))
684             return FALSE;
685     } else if (difference < 0) {
686         if (!tape_device_bsr(self, difference, d_self->file, d_self->block))
687             return FALSE;
688     }
689
690     if (parent_class->seek_block) {
691         return (parent_class->seek_block)(d_self, block);
692     } else {
693         return TRUE;
694     }
695 }
696
697 /* Just checks that the flag is valid before setting it. */
698 static gboolean get_feature_flag(GValue * val, FeatureSupportFlags f) {
699     if (feature_support_flags_is_valid(f)) {
700         g_value_set_flags(val, f);
701         return TRUE;
702     } else {
703         return FALSE;
704     }
705 }
706
707 static gboolean 
708 tape_device_property_get (Device * d_self, DevicePropertyId id, GValue * val) {
709     TapeDevice * self;
710     const DevicePropertyBase * base;
711
712     self = TAPE_DEVICE(d_self);
713     g_return_val_if_fail(self != NULL, FALSE);
714
715     base = device_property_get_by_id(id);
716     g_return_val_if_fail(self != NULL, FALSE);
717
718     g_value_unset_init(val, base->type);
719
720     if (id == PROPERTY_COMPRESSION) {
721         g_value_set_boolean(val, self->compression);
722         return TRUE;
723     } else if (id == PROPERTY_MIN_BLOCK_SIZE) {
724         g_value_set_uint(val, self->min_block_size);
725         return TRUE;
726     } else if (id == PROPERTY_MAX_BLOCK_SIZE) {
727         g_value_set_uint(val, self->max_block_size);
728         return TRUE;
729     } else if (id == PROPERTY_BLOCK_SIZE) {
730         if (self->fixed_block_size == 0) {
731             g_value_set_int(val, -1);
732         } else {
733             g_value_set_int(val, self->fixed_block_size);
734         }
735         return TRUE;
736     } else if (id == PROPERTY_FSF) {
737         return get_feature_flag(val, self->fsf);
738     } else if (id == PROPERTY_BSF) {
739         return get_feature_flag(val, self->bsf);
740     } else if (id == PROPERTY_FSR) {
741         return get_feature_flag(val, self->fsr);
742     } else if (id == PROPERTY_BSR) {
743         return get_feature_flag(val, self->bsr);
744     } else if (id == PROPERTY_EOM) {
745         return get_feature_flag(val, self->eom);
746     } else if (id == PROPERTY_BSF_AFTER_EOM) {
747         return get_feature_flag(val, self->bsf_after_eom);
748     } else if (id == PROPERTY_FINAL_FILEMARKS) {
749         g_value_set_uint(val, self->final_filemarks);
750         return TRUE;
751     } else {
752         /* Chain up */
753         if (parent_class->property_get) {
754             return (parent_class->property_get)(d_self, id, val);
755         } else {
756             return FALSE;
757         }
758     }
759
760     g_assert_not_reached();
761 }
762
763 /* We don't allow overriding of flags with _GOOD surety. That way, if
764    e.g., a feature has no matching IOCTL on a given platform, we don't
765    ever try to set it. */
766 static gboolean flags_settable(FeatureSupportFlags request,
767                                FeatureSupportFlags existing) {
768     if (!feature_support_flags_is_valid(request))
769         return FALSE;
770     else if (!feature_support_flags_is_valid(existing))
771         return TRUE;
772     else if (request == existing)
773         return TRUE;
774     else if (existing & FEATURE_SURETY_GOOD)
775         return FALSE;
776     else
777         return TRUE;
778 }
779
780 /* If the access listed is NULL, and the provided flags can override the
781    existing ones, then do it and return TRUE. */
782 static gboolean try_set_feature(DeviceAccessMode mode,
783                                 FeatureSupportFlags request,
784                                 FeatureSupportFlags * existing) {
785     if (mode != ACCESS_NULL) {
786         return FALSE;
787     } else if (flags_settable(request, *existing)) {
788         *existing = request;
789         return TRUE;
790     } else {
791         return FALSE;
792     }
793 }
794  
795 static gboolean 
796 tape_device_property_set (Device * d_self, DevicePropertyId id, GValue * val) {
797     TapeDevice * self;
798     FeatureSupportFlags feature_request_flags = 0;
799     const DevicePropertyBase * base;
800
801     self = TAPE_DEVICE(d_self);
802     g_return_val_if_fail(self != NULL, FALSE);
803
804     base = device_property_get_by_id(id);
805     g_return_val_if_fail(self != NULL, FALSE);
806
807     g_return_val_if_fail(G_VALUE_HOLDS(val, base->type), FALSE);
808
809     if (base->type == FEATURE_SUPPORT_FLAGS_TYPE) {
810         feature_request_flags = g_value_get_flags(val);
811         g_return_val_if_fail(
812             feature_support_flags_is_valid(feature_request_flags), FALSE);
813     }
814
815     if (id == PROPERTY_COMPRESSION) {
816         /* We allow this property to be set at any time. This is mostly
817          * because setting compression is a hit-and-miss proposition
818          * at any time; some drives accept the mode setting but don't
819          * actually support compression, while others do support
820          * compression but do it via density settings or some other
821          * way. Set this property whenever you want, but all we'll do
822          * is report whether or not the ioctl succeeded. */
823         gboolean request = g_value_get_boolean(val);
824         if (tape_setcompression(self->fd, request)) {
825             self->compression = request;
826             device_clear_volume_details(d_self);
827             return TRUE;
828         } else {
829             return FALSE;
830         }
831     } else if (id == PROPERTY_MIN_BLOCK_SIZE) {
832         if (d_self->access_mode != ACCESS_NULL)
833             return FALSE;
834         self->min_block_size = g_value_get_uint(val);
835         device_clear_volume_details(d_self);
836         return TRUE;
837     } else if (id == PROPERTY_MAX_BLOCK_SIZE) {
838         if (d_self->access_mode != ACCESS_NULL)
839             return FALSE;
840         self->max_block_size = g_value_get_uint(val);
841         device_clear_volume_details(d_self);
842         return TRUE;
843     } else if (id == PROPERTY_BLOCK_SIZE) {
844         if (d_self->access_mode != ACCESS_NULL)
845             return FALSE;
846
847         self->fixed_block_size = g_value_get_int(val);
848         device_clear_volume_details(d_self);
849         return TRUE;
850     } else if (id == PROPERTY_FSF) {
851         return try_set_feature(d_self->access_mode,
852                                feature_request_flags,
853                                &(self->fsf));
854     } else if (id == PROPERTY_BSF) {
855         return try_set_feature(d_self->access_mode,
856                                feature_request_flags,
857                                &(self->bsf));
858     } else if (id == PROPERTY_FSR) {
859         return try_set_feature(d_self->access_mode,
860                                feature_request_flags,
861                                &(self->fsr));
862     } else if (id == PROPERTY_BSR) {
863         return try_set_feature(d_self->access_mode,
864                                feature_request_flags,
865                                &(self->bsr));
866     } else if (id == PROPERTY_EOM) {
867         /* Setting this to disabled also clears BSF after EOM. */
868         if (try_set_feature(d_self->access_mode,
869                             feature_request_flags,
870                             &(self->eom))) {
871             feature_request_flags &= ~FEATURE_SUPPORT_FLAGS_STATUS_MASK;
872             feature_request_flags |= FEATURE_STATUS_DISABLED;
873             self->bsf_after_eom = feature_request_flags;
874             return TRUE;
875         } else {
876             return FALSE;
877         }
878     } else if (id == PROPERTY_BSF_AFTER_EOM) {
879         /* You can only set this if EOM is enabled. */
880         if (self->bsf | FEATURE_STATUS_DISABLED)
881             return FALSE;
882         else
883             return try_set_feature(d_self->access_mode,
884                                    feature_request_flags,
885                                    &(self->bsf_after_eom));
886     } else if (id == PROPERTY_FINAL_FILEMARKS) {
887         guint request = g_value_get_uint(val);
888         if (request == 1 || request == 2) {
889             self->final_filemarks = request;
890             return TRUE;
891         } else {
892             return FALSE;
893         }
894     } else {
895         /* Chain up */
896         if (parent_class->property_set) {
897             return (parent_class->property_set)(d_self, id, val);
898         } else {
899             return FALSE;
900         }
901     }
902
903     g_assert_not_reached();
904 }
905
906 static gboolean 
907 tape_device_finish (Device * d_self) {
908     TapeDevice * self;
909
910     self = TAPE_DEVICE(d_self);
911     g_return_val_if_fail(self != NULL, FALSE);
912
913     /* Polish off this file, if relevant. */
914     if (d_self->in_file && IS_WRITABLE_ACCESS_MODE(d_self->access_mode)) {
915         if (!device_finish_file(d_self))
916             return FALSE;
917     }
918
919     /* Write an extra filemark, if needed. The OS will give us one for
920        sure. */
921     if (self->final_filemarks > 1 &&
922         IS_WRITABLE_ACCESS_MODE(d_self->access_mode)) {
923         if (!tape_weof(self->fd, 1)) {
924             g_fprintf(stderr, "Error writing final filemark: %s\n",
925                     strerror(errno));
926             return FALSE;
927         }
928     }
929
930     /* Rewind. */
931     if (!tape_rewind(self->fd)) {
932         g_fprintf(stderr, "Error rewinding tape: %s\n", strerror(errno));
933         return FALSE;
934     }
935
936     d_self->access_mode = ACCESS_NULL;
937
938     if (parent_class->finish) {
939         return (parent_class->finish)(d_self);
940     } else {
941         return TRUE;
942     }
943
944 }
945
946 /* Works just like read(), except for the following:
947  * 1) Retries on EINTR & friends.
948  * 2) Stores count in parameter, not return value.
949  * 3) Provides explicit return result. */
950 static IoResult
951 tape_device_robust_read (TapeDevice * self, void * buf, int * count) {
952     Device * d_self;
953     int result;
954
955     d_self = (Device*)self;
956     g_return_val_if_fail(self != NULL, RESULT_ERROR);
957     g_return_val_if_fail(*count >= 0, RESULT_ERROR);
958     /* Callers should ensure this. */
959     g_assert((guint)(*count) <= self->read_block_size);
960
961     for (;;) {
962         result = read(self->fd, buf, *count);
963         if (result > 0) {
964             /* Success. By definition, we read a full block. */
965             *count = result;
966             return RESULT_SUCCESS;
967         } else if (result == 0) {
968             return RESULT_NO_DATA;
969         } else {
970             if (0
971 #ifdef EAGAIN
972                 || errno == EAGAIN
973 #endif
974 #ifdef EWOULDBLOCK
975                 || errno == EWOULDBLOCK
976 #endif
977 #ifdef EINTR
978                 || errno == EINTR
979 #endif
980                 ) {
981                 /* Interrupted system call */
982                 continue;
983             } else if ((self->fixed_block_size == 0) &&
984                        (0
985 #ifdef ENOMEM
986                         || errno == ENOMEM /* bad user-space buffer */
987 #endif
988 #ifdef EOVERFLOW
989                         || errno == EOVERFLOW /* bad kernel-space buffer */
990 #endif
991 #ifdef EINVAL
992                         || errno == EINVAL /* ??? */
993 #endif
994                         )) {
995                 /* Buffer too small. */
996                 return RESULT_SMALL_BUFFER;
997             } else {
998                 g_fprintf(stderr, "Error reading %d bytes from %s: %s\n",
999                         *count, d_self->device_name, strerror(errno));
1000                 return RESULT_ERROR;
1001             }
1002         }
1003
1004     }
1005
1006     g_assert_not_reached();
1007 }
1008
1009 /* Kernel workaround: If needed, poke the kernel so it doesn't fail.
1010    at the 2GB boundry. Parameters are G_GNUC_UNUSED in case NEED_RESETOFS
1011    is not defined. */
1012 static void check_resetofs(TapeDevice * self G_GNUC_UNUSED,
1013                            int count G_GNUC_UNUSED) {
1014 #ifdef NEED_RESETOFS
1015     int result;
1016
1017     self->private->write_count += count;
1018     if (self->private->write_count < RESETOFS_THRESHOLD) {
1019         return;
1020     }
1021
1022     result = lseek(self->fd, 0, SEEK_SET);
1023     if (result < 0) {
1024         g_fprintf(stderr,
1025                 "Warning: lseek() failed during kernel 2GB workaround.\n");
1026     }
1027 #endif
1028 }
1029
1030 static IoResult 
1031 tape_device_robust_write (TapeDevice * self, void * buf, int count) {
1032     Device * d_self;
1033     int result;
1034
1035     g_return_val_if_fail(self != NULL, RESULT_ERROR);
1036     d_self = (Device*)self;
1037     
1038     check_resetofs(self, count);
1039
1040     for (;;) {
1041         result = write(self->fd, buf, count);
1042
1043         if (result == count) {
1044             /* Success. */
1045
1046             self->private->write_count ++;
1047             return RESULT_SUCCESS;
1048         } else if (result >= 0) {
1049             /* write() returned a short count. This should not happen. */
1050             g_fprintf(stderr,
1051                   "Mysterious short write on tape device: Tried %d, got %d.\n",
1052                     count, result);
1053             return RESULT_ERROR;
1054         } else if (0
1055 #ifdef EAGAIN
1056                    || errno == EAGAIN
1057 #endif
1058 #ifdef EWOULDBLOCK
1059                    || errno == EWOULDBLOCK
1060 #endif
1061 #ifdef EINTR
1062                    || errno == EINTR
1063 #endif
1064                    ) {
1065                 /* Interrupted system call */
1066             continue;
1067         } else if (0
1068 #ifdef ENOSPC
1069                    || errno == ENOSPC
1070 #endif
1071 #ifdef EIO
1072                    || errno == EIO
1073 #endif
1074                    ) {
1075             /* Probably EOT. Print a message if we got EIO. */
1076 #ifdef EIO
1077             if (errno == EIO) {
1078                 g_fprintf(stderr, "Got EIO on %s, assuming end of tape.\n",
1079                         d_self->device_name);
1080             }
1081 #endif
1082             return RESULT_NO_SPACE;
1083         } else {
1084             /* WTF */
1085             g_fprintf(stderr,
1086      "Kernel gave unexpected write() result of \"%s\" on device %s.\n",
1087                     strerror(errno), d_self->device_name);
1088             return RESULT_ERROR;
1089         }
1090     }
1091
1092     g_assert_not_reached();
1093 }
1094
1095 /* Reads some number of tape blocks into the bit-bucket. If the count
1096    is negative, then we read the rest of the entire file. Returns the
1097    number of blocks read, or -1 if an error occured. If we encounter
1098    EOF (as opposed to some other error) we return the number of blocks
1099    actually read. */
1100 static int drain_tape_blocks(TapeDevice * self, int count) {
1101     char * buffer;
1102     int buffer_size;
1103     int i;
1104
1105     buffer_size = self->read_block_size;
1106
1107     buffer = malloc(sizeof(buffer_size));
1108
1109     for (i = 0; i < count || count < 0;) {
1110         int result;
1111
1112         result = read(self->fd, buffer, buffer_size);
1113         if (result > 0) {
1114             i ++;
1115             continue;
1116         } else if (result == 0) {
1117             free(buffer);
1118             return i;
1119         } else {
1120             /* First check for interrupted system call. */
1121             if (0
1122 #ifdef EAGAIN
1123                 || errno == EAGAIN
1124 #endif
1125 #ifdef EWOULDBLOCK
1126                 || errno == EWOULDBLOCK
1127 #endif
1128 #ifdef EINTR
1129                 || errno == EINTR
1130 #endif
1131                 ) {
1132                 /* Interrupted system call */
1133                 continue;
1134             } else if (0
1135 #ifdef ENOSPC
1136                        || errno == ENOSPC /* bad user-space buffer */
1137 #endif
1138 #ifdef EOVERFLOW
1139                        || errno == EOVERFLOW /* bad kernel-space buffer */
1140 #endif
1141 #ifdef EINVAL
1142                        || errno == EINVAL /* ??? */
1143 #endif
1144                        ) {
1145                 /* The buffer may not be big enough. But the OS is not
1146                    100% clear. We double the buffer and try again, but
1147                    in no case allow a buffer bigger than 32 MB. */
1148                 buffer_size *= 2;
1149
1150                 if (buffer_size > 32*1024*1024) {
1151                     free(buffer);
1152                     return -1;
1153                 } else {
1154                     buffer = realloc(buffer, buffer_size);
1155                     continue;
1156                 }
1157             }
1158         }
1159     }
1160     
1161     return count;
1162 }
1163
1164 /* FIXME: Make sure that there are no cycles in reimplementation
1165    dependencies. */
1166
1167 static gboolean 
1168 tape_device_fsf (TapeDevice * self, guint count) {
1169     g_return_val_if_fail (self != NULL, (gboolean )0);
1170     g_return_val_if_fail (IS_TAPE_DEVICE (self), (gboolean )0);
1171     
1172     if (self->fsf & FEATURE_STATUS_ENABLED) {
1173         return tape_fsf(self->fd, count);
1174     } else {
1175         guint i;
1176         for (i = 0; i < count; i ++) {
1177             if (drain_tape_blocks(self, -1) < 0)
1178                 return FALSE;
1179         }
1180         return TRUE;
1181     }
1182 }
1183
1184 /* Seek back over count + 1 filemarks to the start of the given file. */
1185 static gboolean 
1186 tape_device_bsf (TapeDevice * self, guint count, guint file) {
1187     g_return_val_if_fail (self != NULL, (gboolean )0);
1188     g_return_val_if_fail (IS_TAPE_DEVICE (self), (gboolean )0);
1189
1190     if (self->bsf & FEATURE_STATUS_ENABLED) {
1191         /* The BSF operation is not very smart; it includes the
1192            filemark of the present file as part of the count, and seeks
1193            to the wrong (BOT) side of the filemark. We compensate for
1194            this by seeking one filemark too many, then FSFing back over
1195            it.
1196
1197            If this procedure fails for some reason, we can still try
1198            the backup plan. */
1199         if (tape_bsf(self->fd, count + 1) &&
1200             tape_device_fsf(self, 1))
1201             return TRUE;
1202     } /* Fall through to backup plan. */
1203
1204     /* We rewind the tape, then seek forward the given number of
1205        files. */
1206     if (!tape_rewind(self->fd))
1207         return FALSE;
1208
1209     return tape_device_fsf(self, file);
1210 }
1211
1212
1213 static gboolean 
1214 tape_device_fsr (TapeDevice * self, guint count) {
1215     g_return_val_if_fail (self != NULL, (gboolean )0);
1216     g_return_val_if_fail (IS_TAPE_DEVICE (self), (gboolean )0);
1217
1218     if (self->fsr & FEATURE_STATUS_ENABLED) {
1219         return tape_fsr(self->fd, count);
1220     } else {
1221         int result = drain_tape_blocks(self, count);
1222         return result > 0 && (int)count == result;
1223     }
1224 }
1225
1226 /* Seek back the given number of blocks to block number block within
1227  * the current file, numbered file. */
1228
1229 static gboolean 
1230 tape_device_bsr (TapeDevice * self, guint count, guint file, guint block) {
1231     g_return_val_if_fail (self != NULL, (gboolean )0);
1232     g_return_val_if_fail (IS_TAPE_DEVICE (self), (gboolean )0);
1233     
1234     g_return_val_if_fail (self != NULL, (gboolean )0);
1235     g_return_val_if_fail (IS_TAPE_DEVICE (self), (gboolean )0);
1236
1237     if (self->bsr & FEATURE_STATUS_ENABLED) {
1238         return tape_bsr(self->fd, count);
1239     } else {
1240         /* We BSF, then FSR. */
1241         if (!tape_device_bsf(self, 0, file))
1242             return FALSE;
1243         
1244         return tape_device_fsr(self, block);
1245     }
1246     g_assert_not_reached();
1247 }
1248
1249 /* Go to the right place to write more data, and update the file
1250    number if possible. */
1251 static gboolean 
1252 tape_device_eod (TapeDevice * self) {
1253     Device * d_self;
1254     g_return_val_if_fail (self != NULL, (gboolean )0);
1255     g_return_val_if_fail (IS_TAPE_DEVICE (self), (gboolean )0);
1256     d_self = (Device*)self;
1257
1258     if (self->eom & FEATURE_STATUS_ENABLED) {
1259         int result;
1260         result = tape_eod(self->fd); 
1261         if (result == TAPE_OP_ERROR) {
1262             return FALSE;
1263         } else if (result == TAPE_POSITION_UNKNOWN) {
1264             d_self->file = -1;
1265         } else {
1266             /* We drop by 1 because Device will increment the first
1267                time the user does start_file. */
1268             d_self->file = result - 1;
1269         }
1270         return TRUE;
1271     } else {
1272         int count = 0;
1273         if (!tape_rewind(self->fd))
1274             return FALSE;
1275         
1276         for (;;) {
1277             /* We alternately read a block and FSF. If the read is
1278                successful, then we are not there yet and should FSF
1279                again. */
1280             int result;
1281             result = drain_tape_blocks(self, 1);
1282             if (result == 1) {
1283                 /* More data, FSF. */
1284                 tape_device_fsf(self, 1);
1285                 count ++;
1286             } else if (result == 0) {
1287                 /* Finished. */
1288                 d_self->file = count;
1289                 return TRUE;
1290             } else {
1291                 return FALSE;
1292             }
1293         }
1294     }
1295 }
1296
1297 Device *
1298 tape_device_factory (char * device_type, char * device_name) {
1299     Device * rval;
1300     g_assert(0 == strcmp(device_type, "tape"));
1301     rval = DEVICE(g_object_new(TYPE_TAPE_DEVICE, NULL));
1302     if (!device_open_device(rval, device_name)) {
1303         g_object_unref(rval);
1304         return NULL;
1305     } else {
1306         return rval;
1307     }
1308 }