Imported Upstream version 2.6.0
[debian/amanda] / device-src / vfs-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
23 #include "vfs-device.h"
24 #include "fsusage.h"
25 #include "amanda.h"
26 #include "util.h"
27 #include <regex.h>
28
29 /* This regex will match all VfsDevice files in a directory. We use it
30    for cleanup and verification. Note that this regex does NOT match
31    the volume label. */
32 #define VFS_DEVICE_FILE_REGEX "^[0-9]+[\\.-]"
33
34 /* The name of the volume lockfile. Should be the same as that
35    generated by lockfile_name(0). */
36 #define VOLUME_LOCKFILE_NAME "00000-lock"
37
38 /* Possible (abstracted) results from a system I/O operation. */
39 typedef enum {
40     RESULT_SUCCESS,
41     RESULT_ERROR,        /* Undefined error. */
42     RESULT_NO_DATA,      /* End of File, while reading */
43     RESULT_NO_SPACE,     /* Out of space. Sometimes we don't know if
44                             it was this or I/O error, but this is the
45                             preferred explanation. */
46     RESULT_MAX
47 } IoResult;
48
49 /* here are local prototypes */
50 static void vfs_device_init (VfsDevice * o);
51 static void vfs_device_class_init (VfsDeviceClass * c);
52 static void vfs_device_finalize (GObject * o);
53
54 static gboolean vfs_device_start(Device * pself, DeviceAccessMode mode,
55                                  char * label, char * timestamp);
56 static gboolean vfs_device_open_device (Device * pself,
57                                         char * device_name);
58 static gboolean vfs_device_start_file (Device * pself, const dumpfile_t * ji);
59 static gboolean vfs_device_finish_file (Device * pself);
60 static dumpfile_t * vfs_device_seek_file (Device * self, guint file);
61 static gboolean vfs_device_seek_block (Device * self, guint64 block);
62 static gboolean vfs_device_property_get (Device * pself, DevicePropertyId ID,
63                                          GValue * val);
64 static gboolean vfs_device_property_set (Device * pself, DevicePropertyId ID,
65                                          GValue * val);
66 static gboolean vfs_device_recycle_file (Device * pself, guint filenum);
67 static Device * vfs_device_factory(char * device_type,
68                                    char * device_name);
69 static ReadLabelStatusFlags vfs_device_read_label(Device * dself);
70 static gboolean vfs_device_write_block(Device * self, guint size,
71                                        gpointer data, gboolean last_block);
72 static int vfs_device_read_block(Device * self, gpointer data, int * size_req);
73 static IoResult vfs_device_robust_write(VfsDevice * self,  char *buf,
74                                               int count);
75 static IoResult vfs_device_robust_read(VfsDevice * self, char *buf,
76                                              int *count);
77
78 /* Various helper functions. */
79 static void release_file(VfsDevice * self);
80 static gboolean check_is_dir(const char * name, gboolean printmsg);
81 static char* file_number_to_file_name(VfsDevice * self, guint file);
82 static gboolean file_number_to_file_name_functor(const char * filename,
83                                                  gpointer datap);
84 //static char* lockfile_name(VfsDevice * self, guint file);
85 static gboolean open_lock(VfsDevice * self, int file, gboolean exclusive);
86 static void promote_volume_lock(VfsDevice * self);
87 static void demote_volume_lock(VfsDevice * self);
88 static gboolean delete_vfs_files(VfsDevice * self);
89 static gboolean delete_vfs_files_functor(const char * filename,
90                                          gpointer self);
91 static gboolean check_dir_empty_functor(const char * filename,
92                                         gpointer self);
93 static gboolean clear_and_prepare_label(VfsDevice * self, char * label,
94                                         char * timestamp);
95 static gint get_last_file_number(VfsDevice * self);
96 static gboolean get_last_file_number_functor(const char * filename,
97                                              gpointer datap);
98 static char * make_new_file_name(VfsDevice * self, const dumpfile_t * ji);
99 static gboolean try_unlink(const char * file);
100
101 /* pointer to the classes of our parents */
102 static DeviceClass *parent_class = NULL;
103
104 void vfs_device_register(void) {
105     static const char * device_prefix_list[] = { "file", NULL };
106     register_device(vfs_device_factory, device_prefix_list);
107 }
108
109 GType
110 vfs_device_get_type (void)
111 {
112     static GType type = 0;
113
114     if G_UNLIKELY(type == 0) {
115         static const GTypeInfo info = {
116             sizeof (VfsDeviceClass),
117             (GBaseInitFunc) NULL,
118             (GBaseFinalizeFunc) NULL,
119             (GClassInitFunc) vfs_device_class_init,
120             (GClassFinalizeFunc) NULL,
121             NULL /* class_data */,
122             sizeof (VfsDevice),
123             0 /* n_preallocs */,
124             (GInstanceInitFunc) vfs_device_init,
125             NULL
126         };
127
128         type = g_type_register_static (TYPE_DEVICE, "VfsDevice",
129                                        &info, (GTypeFlags)0);
130     }
131     
132     return type;
133 }
134
135 static void 
136 vfs_device_init (VfsDevice * self) {
137     Device * o;
138     DeviceProperty prop;
139     GValue response;
140
141     self->dir_handle = NULL;
142     self->dir_name = self->file_name = NULL;
143     self->file_lock_name = self->volume_lock_name = NULL;
144     self->file_lock_fd = self->volume_lock_fd = self->open_file_fd = -1;
145     self->block_size = VFS_DEVICE_DEFAULT_BLOCK_SIZE;
146     self->volume_bytes = 0; 
147     self->volume_limit = 0;
148
149     /* Register Properties */
150     o = DEVICE(self);
151     bzero(&response, sizeof(response));
152     prop.base = &device_property_concurrency;
153     prop.access = PROPERTY_ACCESS_GET_MASK;
154     g_value_init(&response, CONCURRENCY_PARADIGM_TYPE);
155     g_value_set_enum(&response, CONCURRENCY_PARADIGM_RANDOM_ACCESS);
156     device_add_property(o, &prop, &response);
157     g_value_unset(&response);
158
159     prop.base = &device_property_streaming;
160     g_value_init(&response, STREAMING_REQUIREMENT_TYPE);
161     g_value_set_enum(&response, STREAMING_REQUIREMENT_NONE);
162     device_add_property(o, &prop, &response);
163     g_value_unset(&response);
164
165     prop.base = &device_property_min_block_size;
166     g_value_init(&response, G_TYPE_UINT);
167     g_value_set_uint(&response, VFS_DEVICE_MIN_BLOCK_SIZE);
168     device_add_property(o, &prop, &response);
169
170     prop.base = &device_property_max_block_size;
171     g_value_set_uint(&response, VFS_DEVICE_MAX_BLOCK_SIZE);
172     device_add_property(o, &prop, &response);
173     g_value_unset(&response);
174
175     prop.base = &device_property_appendable;
176     g_value_init(&response, G_TYPE_BOOLEAN);
177     g_value_set_boolean(&response, TRUE);
178     device_add_property(o, &prop, &response);
179
180     prop.base = &device_property_partial_deletion;
181     device_add_property(o, &prop, &response);
182     g_value_unset(&response);
183
184     /* This one is handled by Device's get_property handler. */
185     prop.base = &device_property_canonical_name;
186     device_add_property(o, &prop, NULL);
187
188     prop.base = &device_property_medium_access_type;
189     g_value_init(&response, MEDIA_ACCESS_MODE_TYPE);
190     g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE);
191     device_add_property(o, &prop, &response);
192     g_value_unset(&response);
193
194     /* These are dynamic, handled in vfs_device_property_xxx */
195     prop.base = &device_property_block_size;
196     prop.access = PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START;
197     device_add_property(o, &prop, NULL);
198
199     prop.base = &device_property_max_volume_usage;
200     prop.access =
201         (PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK) &
202         (~ PROPERTY_ACCESS_SET_INSIDE_FILE_WRITE);
203     device_add_property(o, &prop, NULL);
204 }
205
206 static void 
207 vfs_device_class_init (VfsDeviceClass * c G_GNUC_UNUSED)
208 {
209     GObjectClass *g_object_class = (GObjectClass*) c;
210     DeviceClass *device_class = (DeviceClass *)c;
211
212     parent_class = g_type_class_ref(TYPE_DEVICE);
213
214     device_class->open_device = vfs_device_open_device;
215     device_class->start = vfs_device_start;
216     device_class->start_file = vfs_device_start_file;
217     device_class->read_label = vfs_device_read_label;
218     device_class->write_block = vfs_device_write_block;
219     device_class->read_block = vfs_device_read_block;
220     device_class->finish_file = vfs_device_finish_file;
221     device_class->seek_file = vfs_device_seek_file;
222     device_class->seek_block = vfs_device_seek_block;
223     device_class->property_get = vfs_device_property_get;
224     device_class->property_set = vfs_device_property_set;
225     device_class->recycle_file = vfs_device_recycle_file;
226     g_object_class->finalize = vfs_device_finalize;
227 }
228
229 /* Drops everything associated with the volume file: Its name and fd,
230    its lock, and its lock's name and fd. */
231 static void release_file(VfsDevice * self) {
232     /* Doesn't hurt. */
233     robust_close(self->open_file_fd);
234     amfree(self->file_name);
235
236     if (self->file_lock_fd > 0) {
237         amfunlock(self->file_lock_fd, self->file_lock_name);
238         close(self->file_lock_fd);
239         amfree(self->file_lock_name);
240     }
241     self->file_lock_fd = self->open_file_fd = -1;
242 }
243
244 static void vfs_device_finalize(GObject * obj_self) {
245     VfsDevice *self = VFS_DEVICE (obj_self);
246     Device * d_self = (Device*)self;
247
248     if (d_self->access_mode != ACCESS_NULL) {
249         device_finish(d_self);
250     }
251
252     if(G_OBJECT_CLASS(parent_class)->finalize)
253         (* G_OBJECT_CLASS(parent_class)->finalize)(obj_self);
254
255     amfree(self->dir_name);
256
257     if(self->dir_handle) {
258         closedir (self->dir_handle);
259         self->dir_handle = NULL;
260     }
261
262     release_file(self);
263
264     if (self->volume_lock_fd >= 0) {
265         amfunlock(self->volume_lock_fd, self->volume_lock_name);
266         close(self->volume_lock_fd);
267     }
268
269     amfree(self->volume_lock_name);
270 }
271
272 static Device * vfs_device_factory(char * device_type,
273                                    char * device_name) {
274     Device * rval;
275     g_assert(0 == strcmp(device_type, "file"));
276     rval = DEVICE(g_object_new(TYPE_VFS_DEVICE, NULL));
277     if (!device_open_device(rval, device_name)) {
278         g_object_unref(rval);
279         return NULL;
280     } else {
281         return rval;
282     }
283 }
284
285 static gboolean check_is_dir(const char * name, gboolean printmsg) {
286     struct stat dir_status;
287     
288     if (stat(name, &dir_status) < 0) {
289 #ifdef EINTR
290         if (errno == EINTR) {
291             return check_is_dir(name, printmsg);
292         }
293 #endif /* EINTR */
294         if (printmsg) {
295             g_fprintf(stderr, "Error checking directory %s: %s\n",
296                     name, strerror(errno));
297         }
298         return FALSE;
299     } else if (!S_ISDIR(dir_status.st_mode)) {
300         if (printmsg) {
301             g_fprintf(stderr, "VFS Device path %s is not a directory.\n",
302                     name);
303         }
304         return FALSE;
305     } else {
306         return TRUE;
307     }
308 }
309
310 typedef struct {
311     VfsDevice * self;
312     int count;
313     char * result;
314 } fnfn_data;
315
316 /* A SearchDirectoryFunctor. */
317 static gboolean file_number_to_file_name_functor(const char * filename,
318                                                  gpointer datap) {
319     char * result_tmp;
320     struct stat file_status;
321     fnfn_data *data = (fnfn_data*)datap;
322     
323     result_tmp = vstralloc(data->self->dir_name, "/", filename, NULL);    
324     
325     /* Just to be thorough, let's check that it's a real
326        file. */
327     if (0 != stat(result_tmp, &file_status)) {
328         g_fprintf(stderr, "Cannot stat file %s (%s), ignoring it.\n", 
329                 result_tmp, strerror(errno));
330     } else if (!S_ISREG(file_status.st_mode)) {
331         g_fprintf(stderr, "%s is not a regular file, ignoring it.\n",
332                 result_tmp);
333     } else {
334         data->count ++;
335         if (data->result == NULL) {
336             data->result = result_tmp;
337             result_tmp = NULL;
338         }
339     }
340     amfree(result_tmp);
341     return TRUE;
342 }
343
344 /* This function finds the filename for a given file number. We search
345  * for a filesystem file matching the regex /^0*$device_file\./; if
346  * there is more than one such file we make a warning and take an
347  * arbitrary one. */
348 static char * file_number_to_file_name(VfsDevice * self, guint device_file) {
349     char * regex;
350     fnfn_data data;
351
352     g_return_val_if_fail(self != NULL, NULL);
353     data.self = self;
354     data.count = 0;
355     data.result = NULL;
356
357     regex = g_strdup_printf("^0*%u\\.", device_file);
358
359     search_directory(self->dir_handle, regex,
360                      file_number_to_file_name_functor, &data);
361
362     amfree(regex);
363
364     if (data.count == 0) {
365         g_assert(data.result == NULL);
366         return NULL;
367     } else if (data.count > 1) {
368         g_fprintf(stderr,
369                 "Found multiple names for file number %d, choosing file %s.\n",
370                 device_file, data.result);
371         return data.result;
372     } else {
373         g_assert(data.result != NULL);
374         return data.result;
375     }
376     g_assert_not_reached();
377 }
378
379 /* This function returns the dynamically-allocated lockfile name for a
380    given file number. */
381 /*
382 static char * lockfile_name(VfsDevice * self, guint number) {
383     return g_strdup_printf("%s/%05d-lock", self->dir_name, number);
384 }
385 */
386
387 /* Does what you expect. If the lock already exists, it is released
388  * and regained, in case the mode is changing.
389  * The file field has several options:
390  * - file > 0: Open a lock on a real volume file.
391  * - file = 0: Open the volume lock as a volume file (for setup).
392  * - file < 0: Open the volume lock as a volume lock (persistantly).
393  */
394 static gboolean open_lock(G_GNUC_UNUSED VfsDevice * self,
395                           G_GNUC_UNUSED int file,
396                           G_GNUC_UNUSED gboolean exclusive) {
397
398     /* At the moment, file locking is horribly broken. */
399     return TRUE;
400
401 /*
402     int fd;
403     char * name;
404     if (file < 0) {
405         if (self->volume_lock_name == NULL) {
406             self->volume_lock_name = lockfile_name(self, 0);
407         } else if (self->volume_lock_fd >= 0) {
408             amfunlock(self->volume_lock_fd, self->volume_lock_name);
409             close(self->volume_lock_fd);
410         }
411         name = self->volume_lock_name;
412     } else {
413         if (self->file_lock_fd >= 0 && self->file_lock_name != NULL) {
414             amfunlock(self->file_lock_fd, self->file_lock_name);
415         }
416         amfree(self->file_lock_name);
417         close(self->file_lock_fd);
418         name = self->file_lock_name = lockfile_name(self, file);
419     }
420         
421
422     fd = robust_open(name, O_CREAT | O_WRONLY, VFS_DEVICE_CREAT_MODE);
423
424     if (fd < 0) {
425         g_fprintf(stderr, "Can't open lock file %s: %s\n",
426                 name, strerror(errno));
427         return FALSE;
428     }
429
430     if (exclusive) {
431         amflock(fd, name);
432     } else {
433         amroflock(fd, name);
434     }
435
436     if (file < 0) {
437         self->volume_lock_fd = fd;
438     } else {
439         self->file_lock_fd = fd;
440     }
441     return TRUE;
442 */
443 }
444
445 /* For now, does it the bad way. */
446 static void promote_volume_lock(VfsDevice * self) {
447     amfunlock(self->volume_lock_fd, self->volume_lock_name);
448     amflock(self->volume_lock_fd, self->volume_lock_name);
449 }
450
451 static void demote_volume_lock(VfsDevice * self) {
452     amfunlock(self->volume_lock_fd, self->volume_lock_name);
453     amroflock(self->volume_lock_fd, self->volume_lock_name);
454 }
455
456 /* A SearchDirectoryFunctor */
457 static gboolean update_volume_size_functor(const char * filename,
458                                            gpointer user_data) {
459     char * full_filename;
460     struct stat stat_buf;
461     VfsDevice * self = user_data;
462     g_return_val_if_fail(IS_VFS_DEVICE(self), FALSE);
463     
464     full_filename = vstralloc(self->dir_name, "/", filename, NULL);
465
466     if (stat(full_filename, &stat_buf) < 0) {
467         /* Log it and keep going. */
468         g_fprintf(stderr, "Couldn't stat file %s: %s\n",
469                 full_filename, strerror(errno));
470         amfree(full_filename);
471         return TRUE;
472     }
473
474     amfree(full_filename);
475     self->volume_bytes += stat_buf.st_size;
476
477     return TRUE;
478 }
479
480 static void update_volume_size(VfsDevice * self) {
481     self->volume_bytes = 0;
482     search_directory(self->dir_handle, "^[0-9]+\\.",
483                      update_volume_size_functor, self);
484
485 }
486
487 static gboolean 
488 vfs_device_open_device (Device * pself, char * device_name) {
489     VfsDevice * self;
490     dumpfile_t * rval;
491     
492     self = VFS_DEVICE(pself);
493     g_return_val_if_fail (self != NULL, FALSE);
494     g_return_val_if_fail (device_name != NULL, FALSE);
495
496     /* We don't have to free this ourselves; it will be freed by
497      * vfs_device_finalize whether we succeed here or not. */
498     self->dir_name = g_strconcat(device_name, "/data/", NULL);
499     if (!check_is_dir(self->dir_name, TRUE)) {
500         return FALSE;
501     }
502
503     /* Next open the directory itself. */
504     self->dir_handle = opendir(self->dir_name);
505     if (self->dir_handle == NULL) {
506         g_fprintf(stderr, "Couldn't open directory %s for reading: %s\n",
507                 device_name, strerror(errno));
508         return FALSE;
509     }
510
511     if (!open_lock(self, -1, FALSE))
512         return FALSE;
513
514     /* Not an error if this fails. Note that we ignore the class hierarchy.
515      */
516     rval = vfs_device_seek_file(pself, 0);
517     amfree(rval);
518
519     if (parent_class->open_device) {
520         /* Will call vfs_device_read_label. */
521         return (parent_class->open_device)(pself, device_name);
522     } else {
523         return TRUE;
524     }
525 }
526
527 /* A SearchDirectoryFunctor */
528 static gboolean delete_vfs_files_functor(const char * filename,
529                                          gpointer user_data) {
530     VfsDevice * self;
531     char * path_name;
532
533     self = VFS_DEVICE(user_data);
534     g_return_val_if_fail(self != NULL, FALSE);
535
536     /* Skip the volume lock. */
537     if (strcmp(filename, VOLUME_LOCKFILE_NAME) == 0)
538         return TRUE;
539
540     path_name = vstralloc(self->dir_name, "/", filename, NULL);
541     if (unlink(path_name) != 0) {
542         g_fprintf(stderr, "Error unlinking %s: %s\n", path_name,
543                 strerror(errno));
544     }
545     amfree(path_name);
546     return TRUE;
547 }
548
549 /* delete_vfs_files deletes all VfsDevice files in the directory except the
550    volume lockfile. */
551 static gboolean delete_vfs_files(VfsDevice * self) {
552     g_assert(self != NULL);
553     g_assert(self->dir_handle != NULL);
554
555     /* This function assumes that the volume is locked! */
556     search_directory(self->dir_handle, VFS_DEVICE_FILE_REGEX,
557                      delete_vfs_files_functor, self);
558     return TRUE;
559 }
560
561 /* This is a functor suitable for search_directory. It simply prints a
562    warning. It also dodges the volume lockfile. */
563 static gboolean check_dir_empty_functor(const char * filename,
564                                         gpointer user_data) {
565     VfsDevice * self;
566     char * path_name;
567
568     self = VFS_DEVICE(user_data);
569     g_return_val_if_fail(self != NULL, FALSE);
570
571     if (strcmp(filename, VOLUME_LOCKFILE_NAME) == 0)
572         return TRUE;
573
574     path_name = vstralloc(self->dir_name, "/", filename, NULL);
575
576     g_fprintf(stderr, "Found spurious storage file %s\n", path_name);
577
578     amfree(path_name);
579     return TRUE;
580 }
581
582 /* This function is used to write volume and dump headers. */
583 static gboolean write_amanda_header(VfsDevice * self,
584                                     const dumpfile_t * header) {
585     char * label_buffer;
586     IoResult result;
587     
588     g_return_val_if_fail(header != NULL, FALSE);
589     g_return_val_if_fail(self != NULL, FALSE);
590     label_buffer = build_header(header, VFS_DEVICE_LABEL_SIZE);
591     if (strlen(label_buffer)+1 > VFS_DEVICE_LABEL_SIZE) {
592         amfree(label_buffer);
593         g_fprintf(stderr, "Amanda header header won't fit on VFS device!\n");
594         return FALSE;
595     }
596
597     result = vfs_device_robust_write(self, label_buffer, VFS_DEVICE_LABEL_SIZE);
598     amfree(label_buffer);
599     return (result == RESULT_SUCCESS);
600 }
601
602 /* clear_and_label will erase the contents of the directory, and write
603  * this label in its place. This function assumes we already have a volume
604  * label write lock in place (e.g., promote_lock() has been called.) */
605 static gboolean clear_and_prepare_label(VfsDevice * self, char * label,
606                                         char * timestamp) {
607     dumpfile_t * label_header;
608
609     release_file(self);
610
611     /* Delete any extant data, except our volume lock. */
612     if (!delete_vfs_files(self)) {
613         return FALSE;
614     }
615
616     /* Print warnings about any remaining files. */
617     search_directory(self->dir_handle, VFS_DEVICE_FILE_REGEX,
618                      check_dir_empty_functor, self);
619
620     self->file_name = g_strdup_printf("%s/00000.%s", self->dir_name, label);
621
622     self->open_file_fd = robust_open(self->file_name,
623                                      O_CREAT | O_EXCL | O_WRONLY,
624                                      VFS_DEVICE_CREAT_MODE);
625     if (self->open_file_fd < 0) {
626         g_fprintf(stderr, "Can't open file %s: %s\n", self->file_name,
627                 strerror(errno));
628         return FALSE;
629     }
630
631     label_header = make_tapestart_header(DEVICE(self), label, timestamp);
632     if (write_amanda_header(self, label_header)) {
633         amfree(label_header);
634         self->volume_bytes = VFS_DEVICE_LABEL_SIZE;
635         return TRUE;
636     } else {
637         amfree(label_header);
638         return FALSE;
639     }
640 }
641
642 static ReadLabelStatusFlags vfs_device_read_label(Device * dself) {
643     dumpfile_t * amanda_header;
644     VfsDevice * self;
645
646     self = VFS_DEVICE(dself);
647     g_return_val_if_fail(self != NULL, ~READ_LABEL_STATUS_SUCCESS);
648
649     amanda_header = vfs_device_seek_file(dself, 0);
650     if (amanda_header == NULL) {
651         /* This means an error occured getting locks or opening the header
652          * file. */
653         return (READ_LABEL_STATUS_DEVICE_ERROR |
654                 READ_LABEL_STATUS_VOLUME_ERROR |
655                 READ_LABEL_STATUS_VOLUME_UNLABELED);
656     }
657
658     if (amanda_header->type != F_TAPESTART) {
659         /* This is an error, and should not happen. */
660         g_fprintf(stderr, "Got a bad volume label\n");
661         amfree(amanda_header);
662         return READ_LABEL_STATUS_VOLUME_ERROR;
663     }
664
665     dself->volume_label = g_strdup(amanda_header->name);
666     dself->volume_time = g_strdup(amanda_header->datestamp);
667     amfree(amanda_header);
668
669     update_volume_size(self);
670
671     if (parent_class->read_label) {
672         return (parent_class->read_label)(dself);
673     } else {
674         return READ_LABEL_STATUS_SUCCESS;
675     }
676 }
677
678 static gboolean vfs_device_write_block(Device * pself, guint size,
679                                        gpointer data, gboolean last_block) {
680     VfsDevice * self = VFS_DEVICE(pself);
681     IoResult result;
682     g_return_val_if_fail(self != NULL, FALSE);
683     g_return_val_if_fail(last_block || size >= (guint)self->block_size, FALSE);
684     g_return_val_if_fail(pself->in_file, FALSE);
685     g_assert(self->open_file_fd >= 0);
686
687     if (self->volume_limit > 0 &&
688         self->volume_bytes + size > self->volume_limit) {
689         /* Simulate EOF. */
690         pself->is_eof = TRUE;
691         return FALSE;
692     }
693
694     result = vfs_device_robust_write(self, data, size);
695     if (result == RESULT_SUCCESS) {
696         self->volume_bytes += size;
697         if (parent_class->write_block) {
698             (parent_class->write_block)(pself, size, data, last_block);
699         }
700         return TRUE;
701     } else {
702         return FALSE;
703     }
704 }
705
706 static int
707 vfs_device_read_block(Device * pself, gpointer data, int * size_req) {
708     VfsDevice * self;
709     int size;
710     IoResult result;
711     
712     self = VFS_DEVICE(pself);
713     g_return_val_if_fail (self != NULL, -1);
714
715     if (data == NULL || *size_req < self->block_size) {
716         /* Just a size query. */
717         *size_req = self->block_size;
718         return 0;
719     }
720
721     size = self->block_size;
722     result = vfs_device_robust_read(self, data, &size);
723     switch (result) {
724     case RESULT_SUCCESS:
725         *size_req = size;
726         return size;
727     case RESULT_NO_DATA:
728         pself->is_eof = TRUE;
729         /* FALLTHROUGH */
730     default:
731         return -1;
732     }
733
734     g_assert_not_reached();
735 }
736
737 static gboolean vfs_device_start(Device * pself,
738                                  DeviceAccessMode mode, char * label,
739                                  char * timestamp) {
740     VfsDevice * self;
741     self = VFS_DEVICE(pself);
742     g_return_val_if_fail(self != NULL, FALSE);
743     g_return_val_if_fail(parent_class->start != NULL, FALSE);
744     
745     if (mode == ACCESS_WRITE) {
746         promote_volume_lock(self);
747         if (!clear_and_prepare_label(self, label, timestamp)) {
748             demote_volume_lock(self);
749             return FALSE;
750         }
751         demote_volume_lock(self);
752     }
753
754     release_file(self);
755  
756     if (parent_class->start) {
757         return parent_class->start(pself, mode, label, timestamp);
758     } else {
759         return TRUE;
760     }
761 }
762
763 typedef struct {
764     VfsDevice * self;
765     int rval;
766 } glfn_data;
767
768 /* A SearchDirectoryFunctor. */
769 static gboolean get_last_file_number_functor(const char * filename,
770                                              gpointer datap) {
771     guint64 file;
772     glfn_data * data = (glfn_data*)datap;
773     g_return_val_if_fail(IS_VFS_DEVICE(data->self), FALSE);
774     file = g_ascii_strtoull(filename, NULL, 10); /* Guaranteed to work. */
775     if (file > G_MAXINT) {
776         g_fprintf(stderr, "Super-large device file %s found, ignoring.\n",
777                filename);
778         return TRUE;
779     }
780     /* This condition is needlessly complex due to sign issues. */
781     if (data->rval < 0 || ((guint)data->rval) < file) {
782         data->rval = file;
783     }
784     return TRUE;
785 }
786
787 static gint get_last_file_number(VfsDevice * self) {
788     glfn_data data;
789     int count;
790     data.self = self;
791     data.rval = -1;
792     
793     count = search_directory(self->dir_handle, "^[0-9]+\\.",
794                              get_last_file_number_functor, &data);
795
796     if (count <= 0) {
797         /* Somebody deleted something important while we weren't looking. */
798         g_fprintf(stderr, "Error identifying VFS device contents!\n");
799         return -1;
800     } else {
801         g_assert(data.rval >= 0);
802     }
803     
804     return data.rval;
805 }
806
807 typedef struct {
808     VfsDevice * self;
809     guint request;
810     int best_found;
811 } gnfn_data;
812
813 /* A SearchDirectoryFunctor. */
814 static gboolean get_next_file_number_functor(const char * filename,
815                                              gpointer datap) {
816     guint file;
817     gnfn_data * data = (gnfn_data*)datap;
818     g_return_val_if_fail(IS_VFS_DEVICE(data->self), FALSE);
819     file = g_ascii_strtoull(filename, NULL, 10); /* Guaranteed to work. */
820     if (file > G_MAXINT) {
821         g_fprintf(stderr, "Super-large device file %s found, ignoring.\n",
822                filename);
823         return TRUE;
824     }
825     /* This condition is needlessly complex due to sign issues. */
826     if (file >= data->request &&
827         (data->best_found < 0 || file < (guint)data->best_found)) {
828         data->best_found = file;
829     }
830     return TRUE;
831 }
832
833 /* Returns the file number equal to or greater than the given requested
834  * file number. */
835 static gint get_next_file_number(VfsDevice * self, guint request) {
836     gnfn_data data;
837     int count;
838     data.self = self;
839     data.request = request;
840     data.best_found = -1;
841     
842     count = search_directory(self->dir_handle, "^[0-9]+\\.",
843                              get_next_file_number_functor, &data);
844
845     if (count <= 0) {
846         /* Somebody deleted something important while we weren't looking. */
847         g_fprintf(stderr, "Error identifying VFS device contents!\n");
848         return -1;
849     }
850     
851     /* Could be -1. */
852     return data.best_found;
853 }
854
855 /* Finds the file number, acquires a lock, and returns the new file name. */
856 static
857 char * make_new_file_name(VfsDevice * self, const dumpfile_t * ji) {
858     char * rval;
859     char *base, *sanitary_base;
860     int fileno;
861
862     for (;;) {
863         fileno = 1 + get_last_file_number(self);
864         if (fileno <= 0)
865             return NULL;
866     
867         if (open_lock(self, fileno, TRUE)) {
868             break;
869         } else {
870             continue;
871         }
872     }
873
874     base = g_strdup_printf("%05d.%s.%s.%d", fileno, ji->name, ji->disk,
875                            ji->dumplevel);
876     sanitary_base = sanitise_filename(base);
877     amfree(base);
878     rval = g_strdup_printf("%s/%s", self->dir_name, sanitary_base);
879     amfree(sanitary_base);
880     return rval;
881 }
882
883 static gboolean 
884 vfs_device_start_file (Device * pself, const dumpfile_t * ji) {
885     VfsDevice * self;
886
887     self = VFS_DEVICE(pself);
888     g_return_val_if_fail (self != NULL, FALSE);
889     g_return_val_if_fail (ji != NULL, FALSE);
890
891     if (self->volume_limit > 0 &&
892         self->volume_bytes + VFS_DEVICE_LABEL_SIZE > self->volume_limit) {
893         /* No more room. */
894         return FALSE;
895     }
896
897     /* The basic idea here is thus:
898        1) Try to get a lock on the next filenumber.
899        2) If that fails, update our idea of "next filenumber" and try again.
900        3) Then open the file itself.
901        4) Write the label.
902        5) Chain up. */
903
904     self->file_name = make_new_file_name(self, ji);
905     if (self->file_name == NULL)
906         return FALSE;
907
908     self->open_file_fd = robust_open(self->file_name,
909                                      O_CREAT | O_EXCL | O_RDWR,
910                                      VFS_DEVICE_CREAT_MODE);
911     if (self->open_file_fd < 0) {
912         g_fprintf(stderr, "Can't create file %s: %s\n", self->file_name,
913                 strerror(errno));
914         release_file(self);
915         return FALSE;
916     }
917
918     
919     if (!write_amanda_header(self, ji)) {
920         release_file(self);
921         return FALSE;
922     }
923
924     self->volume_bytes += VFS_DEVICE_LABEL_SIZE;
925
926     if (parent_class->start_file) {
927         parent_class->start_file(pself, ji);
928     }
929     return TRUE;
930 }
931
932 static gboolean 
933 vfs_device_finish_file (Device * pself) {
934     VfsDevice * self;
935     self = VFS_DEVICE(pself);
936     g_return_val_if_fail(self != NULL, FALSE);
937
938     release_file(self);
939     
940     if (parent_class->finish_file) {
941         return parent_class->finish_file(pself);
942     } else {
943         return TRUE;
944     }
945     g_assert_not_reached();
946 }
947
948 /* This function is used for two purposes, rather than one. In
949  * addition to its documented behavior, we also use it to open the
950  * volume label for reading at startup. In that second case, we avoid
951  * FdDevice-related side effects. */
952 static dumpfile_t * 
953 vfs_device_seek_file (Device * pself, guint requested_file) {
954     VfsDevice * self;
955     int file;
956     dumpfile_t * rval;
957     char header_buffer[VFS_DEVICE_LABEL_SIZE];
958     int header_buffer_size = sizeof(header_buffer);
959     IoResult result;
960
961     self = VFS_DEVICE(pself);
962     g_return_val_if_fail (self != NULL, NULL);
963
964     pself->in_file = FALSE;
965     
966     release_file(self);
967
968     if (requested_file > 0) {
969         file = get_next_file_number(self, requested_file);
970     } else {
971         file = requested_file;
972     }
973
974     if (file < 0) {
975         /* Did they request one past the end? */
976         char * tmp_file_name;
977         tmp_file_name = file_number_to_file_name(self, requested_file - 1);
978         if (tmp_file_name != NULL) {
979             free(tmp_file_name);
980             return make_tapeend_header();
981         } else {
982             return NULL;
983         }
984     }
985
986     if (!open_lock(self, file, FALSE)) {
987         return NULL;
988     }
989
990     self->file_name = file_number_to_file_name(self, file);
991     if (self->file_name == NULL) {
992         release_file(self);
993         return NULL;
994     }
995
996     self->open_file_fd = robust_open(self->file_name, O_RDONLY, 0);
997     if (self->open_file_fd <= 0) {
998         g_fprintf(stderr, "Couldn't open file %s: %s\n", self->file_name,
999                 strerror(errno));
1000         amfree(self->file_name);
1001         release_file(self);
1002         return NULL;
1003     }
1004
1005     result = vfs_device_robust_read(self, header_buffer,
1006                                     &header_buffer_size);
1007     if (result != RESULT_SUCCESS) {
1008         g_fprintf(stderr, "Problem reading Amanda header.\n");
1009         release_file(self);
1010         return NULL;
1011     }
1012
1013     rval = malloc(sizeof(*rval));
1014     parse_file_header(header_buffer, rval, header_buffer_size);
1015     if (file > 0) {
1016         switch (rval->type) {
1017         case F_DUMPFILE:
1018         case F_CONT_DUMPFILE:
1019         case F_SPLIT_DUMPFILE:
1020             /* Chain up. */
1021             if (parent_class->seek_file) {
1022                 parent_class->seek_file(pself, file);
1023             }
1024             return rval;
1025         default:
1026             amfree(rval);
1027             release_file(self);
1028             return NULL;
1029         }
1030     } else if (file == 0) {
1031         return rval;
1032     } else {
1033         amfree(rval);
1034         return NULL;
1035     }
1036 }
1037
1038 static gboolean 
1039 vfs_device_seek_block (Device * pself, guint64 block) {
1040     VfsDevice * self;
1041     off_t result;
1042
1043     self = VFS_DEVICE(pself);
1044     g_return_val_if_fail (self != NULL, FALSE);
1045     g_return_val_if_fail (self->open_file_fd >= 0, FALSE);
1046     g_assert(sizeof(off_t) >= sizeof(guint64));
1047
1048     /* Pretty simple. We figure out the blocksize and use that. */
1049     result = lseek(self->open_file_fd,
1050                    (block) * self->block_size + VFS_DEVICE_LABEL_SIZE,
1051                    SEEK_SET);
1052     return (result != (off_t)(-1));
1053 }
1054
1055 static gboolean
1056 vfs_device_property_get (Device * pself, DevicePropertyId ID, GValue * val) {
1057     VfsDevice * self;
1058     self = VFS_DEVICE(pself);
1059     g_return_val_if_fail(self != NULL, FALSE);
1060     if (ID == PROPERTY_BLOCK_SIZE) {
1061         g_value_unset_init(val, G_TYPE_INT);
1062         g_value_set_int(val, self->block_size);
1063         return TRUE;
1064     } else if (ID == PROPERTY_MAX_VOLUME_USAGE) {
1065         g_value_unset_init(val, G_TYPE_UINT64);
1066         g_value_set_uint64(val, self->volume_limit);
1067         return TRUE;
1068     } else if (ID == PROPERTY_FREE_SPACE) {
1069         QualifiedSize qsize;
1070         struct fs_usage fsusage;
1071         guint64 bytes_avail;
1072
1073         if (get_fs_usage(self->dir_name, NULL, &fsusage) == 0) {
1074             if (fsusage.fsu_bavail_top_bit_set)
1075                 bytes_avail = 0;
1076             else
1077                 bytes_avail = fsusage.fsu_bavail * fsusage.fsu_blocksize;
1078             if (self->volume_limit && (guint64)self->volume_limit < bytes_avail / 1024)
1079                 bytes_avail = (guint64)self->volume_limit * 1024;
1080
1081             qsize.accuracy = SIZE_ACCURACY_REAL;
1082             qsize.bytes = bytes_avail;
1083         } else {
1084             g_warning(_("get_fs_usage('%s') failed: %s"), self->dir_name, strerror(errno));
1085             qsize.accuracy = SIZE_ACCURACY_UNKNOWN;
1086             qsize.bytes = 0;
1087         }
1088         g_value_unset_init(val, QUALIFIED_SIZE_TYPE);
1089         g_value_set_boxed(val, &qsize);
1090         return TRUE;
1091     } else {
1092         if (parent_class->property_get) {
1093             return parent_class->property_get(pself, ID, val);
1094         } else {
1095             return FALSE;
1096         }
1097     }
1098     g_assert_not_reached();
1099 }
1100
1101 static gboolean 
1102 vfs_device_property_set (Device * pself, DevicePropertyId ID, GValue * val) {
1103     VfsDevice * self;
1104     self = VFS_DEVICE(pself);
1105     g_return_val_if_fail(self != NULL, FALSE);
1106     if (ID == PROPERTY_BLOCK_SIZE) {
1107         int block_size = g_value_get_int(val);
1108         g_return_val_if_fail(block_size > 0, FALSE);
1109         self->block_size = block_size;
1110         return TRUE;
1111     } else if (ID == PROPERTY_MAX_VOLUME_USAGE) {
1112         self->volume_limit = g_value_get_uint64(val);
1113         return TRUE;
1114     } else {
1115         if (parent_class->property_get) {
1116             return parent_class->property_get(pself, ID, val);
1117         } else {
1118             return FALSE;
1119         }
1120     }
1121     g_assert_not_reached();
1122 }
1123
1124 static gboolean try_unlink(const char * file) {
1125     if (unlink(file) < 0) {
1126         g_fprintf(stderr, "Can't unlink file %s: %s\n", file, strerror(errno));
1127         return FALSE;
1128     } else {
1129         return TRUE;
1130     }
1131 }
1132
1133 static gboolean 
1134 vfs_device_recycle_file (Device * pself, guint filenum) {
1135     VfsDevice * self;
1136     struct stat file_status;
1137     off_t file_size;
1138
1139     self = VFS_DEVICE(pself);
1140     g_return_val_if_fail(self != NULL, FALSE);
1141     g_return_val_if_fail(!(pself->in_file), FALSE);
1142
1143     /* Game Plan:
1144      * 1) Get a write lock on the file in question.
1145      * 2) Unlink the file in question.
1146      * 3) Unlink the lock.
1147      * 4) Release the lock.
1148      * FIXME: Is it OK to unlink the lockfile?
1149      */
1150
1151     self->file_name = file_number_to_file_name(self, filenum);
1152
1153     if (self->file_name == NULL)
1154         return FALSE;
1155
1156     if (!open_lock(self, filenum, TRUE))
1157         return FALSE;
1158
1159     if (0 != stat(self->file_name, &file_status)) {
1160         fprintf(stderr, "Cannot stat file %s (%s), so not removing.\n",
1161                 self->file_name, strerror(errno));
1162         return FALSE;
1163     }
1164     file_size = file_status.st_size;
1165     
1166     if (!try_unlink(self->file_name) ||
1167         !try_unlink(self->file_lock_name)) {
1168         release_file(self);
1169         return FALSE;
1170     }
1171
1172     self->volume_bytes -= file_size;
1173     release_file(self);
1174     return TRUE;
1175 }
1176
1177 static IoResult vfs_device_robust_read(VfsDevice * self, char *buf,
1178                                              int *count) {
1179     int fd = self->open_file_fd;
1180     int want = *count, got = 0;
1181
1182     while (got < want) {
1183         int result;
1184         result = read(fd, buf + got, want - got);
1185         if (result > 0) {
1186             got += result;
1187         } else if (result == 0) {
1188             /* end of file */
1189             if (got == 0) {
1190                 return RESULT_NO_DATA;
1191             } else {
1192                 *count = got;
1193                 return RESULT_SUCCESS;
1194             }
1195         } else if (0
1196 #ifdef EAGAIN
1197                 || errno == EAGAIN
1198 #endif
1199 #ifdef EWOULDBLOCK
1200                 || errno == EWOULDBLOCK
1201 #endif
1202 #ifdef EINTR
1203                 || errno == EINTR
1204 #endif
1205                    ) {
1206             /* Try again. */
1207             continue;
1208         } else {
1209             /* Error occured. */
1210             g_fprintf(stderr, "Error reading fd %d: %s\n", fd, strerror(errno));
1211             *count = got;
1212             return -1;
1213         }
1214     }
1215
1216     *count = got;
1217     return RESULT_SUCCESS;
1218 }
1219
1220 static IoResult
1221 vfs_device_robust_write(VfsDevice * self,  char *buf, int count) {
1222     int fd = self->open_file_fd;
1223     int rval = 0;
1224
1225     while (rval < count) {
1226         int result;
1227         result = write(fd, buf + rval, count - rval);
1228         if (result > 0) {
1229             rval += result;
1230             continue;
1231         } else if (0
1232 #ifdef EAGAIN
1233                 || errno == EAGAIN
1234 #endif
1235 #ifdef EWOULDBLOCK
1236                 || errno == EWOULDBLOCK
1237 #endif
1238 #ifdef EINTR
1239                 || errno == EINTR
1240 #endif
1241                    ) {
1242             /* Try again. */
1243             continue;
1244         } else if (0
1245 #ifdef EFBIG
1246                    || errno == EFBIG
1247 #endif
1248 #ifdef ENOSPC
1249                    || errno == ENOSPC
1250 #endif
1251                    ) {
1252             /* We are definitely out of space. */
1253             return RESULT_NO_SPACE;
1254         } else {
1255             /* Error occured. Note that here we handle EIO as an error. */
1256             g_fprintf(stderr, "Error writing device fd %d: %s\n",
1257                     fd, strerror(errno));
1258             
1259             return RESULT_ERROR;
1260         }
1261     }
1262     return RESULT_SUCCESS;
1263 }