a69b584f69943c85a7489f99caa5c3a42ad70e66
[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 "amanda.h"
24 #include "vfs-device.h"
25 #include "fsusage.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         pself->in_file = FALSE;
730         return -1;
731     default:
732         return -1;
733     }
734
735     g_assert_not_reached();
736 }
737
738 static gboolean vfs_device_start(Device * pself,
739                                  DeviceAccessMode mode, char * label,
740                                  char * timestamp) {
741     VfsDevice * self;
742     self = VFS_DEVICE(pself);
743     g_return_val_if_fail(self != NULL, FALSE);
744     g_return_val_if_fail(parent_class->start != NULL, FALSE);
745     
746     if (mode == ACCESS_WRITE) {
747         promote_volume_lock(self);
748         if (!clear_and_prepare_label(self, label, timestamp)) {
749             demote_volume_lock(self);
750             return FALSE;
751         }
752         demote_volume_lock(self);
753     }
754
755     release_file(self);
756  
757     if (parent_class->start) {
758         return parent_class->start(pself, mode, label, timestamp);
759     } else {
760         return TRUE;
761     }
762 }
763
764 typedef struct {
765     VfsDevice * self;
766     int rval;
767 } glfn_data;
768
769 /* A SearchDirectoryFunctor. */
770 static gboolean get_last_file_number_functor(const char * filename,
771                                              gpointer datap) {
772     guint64 file;
773     glfn_data * data = (glfn_data*)datap;
774     g_return_val_if_fail(IS_VFS_DEVICE(data->self), FALSE);
775     file = g_ascii_strtoull(filename, NULL, 10); /* Guaranteed to work. */
776     if (file > G_MAXINT) {
777         g_fprintf(stderr, "Super-large device file %s found, ignoring.\n",
778                filename);
779         return TRUE;
780     }
781     /* This condition is needlessly complex due to sign issues. */
782     if (data->rval < 0 || ((guint)data->rval) < file) {
783         data->rval = file;
784     }
785     return TRUE;
786 }
787
788 static gint get_last_file_number(VfsDevice * self) {
789     glfn_data data;
790     int count;
791     data.self = self;
792     data.rval = -1;
793     
794     count = search_directory(self->dir_handle, "^[0-9]+\\.",
795                              get_last_file_number_functor, &data);
796
797     if (count <= 0) {
798         /* Somebody deleted something important while we weren't looking. */
799         g_fprintf(stderr, "Error identifying VFS device contents!\n");
800         return -1;
801     } else {
802         g_assert(data.rval >= 0);
803     }
804     
805     return data.rval;
806 }
807
808 typedef struct {
809     VfsDevice * self;
810     guint request;
811     int best_found;
812 } gnfn_data;
813
814 /* A SearchDirectoryFunctor. */
815 static gboolean get_next_file_number_functor(const char * filename,
816                                              gpointer datap) {
817     guint file;
818     gnfn_data * data = (gnfn_data*)datap;
819     g_return_val_if_fail(IS_VFS_DEVICE(data->self), FALSE);
820     file = g_ascii_strtoull(filename, NULL, 10); /* Guaranteed to work. */
821     if (file > G_MAXINT) {
822         g_fprintf(stderr, "Super-large device file %s found, ignoring.\n",
823                filename);
824         return TRUE;
825     }
826     /* This condition is needlessly complex due to sign issues. */
827     if (file >= data->request &&
828         (data->best_found < 0 || file < (guint)data->best_found)) {
829         data->best_found = file;
830     }
831     return TRUE;
832 }
833
834 /* Returns the file number equal to or greater than the given requested
835  * file number. */
836 static gint get_next_file_number(VfsDevice * self, guint request) {
837     gnfn_data data;
838     int count;
839     data.self = self;
840     data.request = request;
841     data.best_found = -1;
842     
843     count = search_directory(self->dir_handle, "^[0-9]+\\.",
844                              get_next_file_number_functor, &data);
845
846     if (count <= 0) {
847         /* Somebody deleted something important while we weren't looking. */
848         g_fprintf(stderr, "Error identifying VFS device contents!\n");
849         return -1;
850     }
851     
852     /* Could be -1. */
853     return data.best_found;
854 }
855
856 /* Finds the file number, acquires a lock, and returns the new file name. */
857 static
858 char * make_new_file_name(VfsDevice * self, const dumpfile_t * ji) {
859     char * rval;
860     char *base, *sanitary_base;
861     int fileno;
862
863     for (;;) {
864         fileno = 1 + get_last_file_number(self);
865         if (fileno <= 0)
866             return NULL;
867     
868         if (open_lock(self, fileno, TRUE)) {
869             break;
870         } else {
871             continue;
872         }
873     }
874
875     /* record that we're at this filenum now */
876     DEVICE(self)->file = fileno;
877
878     base = g_strdup_printf("%05d.%s.%s.%d", fileno, ji->name, ji->disk,
879                            ji->dumplevel);
880     sanitary_base = sanitise_filename(base);
881     amfree(base);
882     rval = g_strdup_printf("%s/%s", self->dir_name, sanitary_base);
883     amfree(sanitary_base);
884     return rval;
885 }
886
887 static gboolean 
888 vfs_device_start_file (Device * pself, const dumpfile_t * ji) {
889     VfsDevice * self;
890
891     self = VFS_DEVICE(pself);
892     g_return_val_if_fail (self != NULL, FALSE);
893     g_return_val_if_fail (ji != NULL, FALSE);
894
895     if (self->volume_limit > 0 &&
896         self->volume_bytes + VFS_DEVICE_LABEL_SIZE > self->volume_limit) {
897         /* No more room. */
898         return FALSE;
899     }
900
901     /* The basic idea here is thus:
902        1) Try to get a lock on the next filenumber.
903        2) If that fails, update our idea of "next filenumber" and try again.
904        3) Then open the file itself.
905        4) Write the label.
906        5) Chain up. */
907
908     self->file_name = make_new_file_name(self, ji);
909     if (self->file_name == NULL)
910         return FALSE;
911
912     self->open_file_fd = robust_open(self->file_name,
913                                      O_CREAT | O_EXCL | O_RDWR,
914                                      VFS_DEVICE_CREAT_MODE);
915     if (self->open_file_fd < 0) {
916         g_fprintf(stderr, "Can't create file %s: %s\n", self->file_name,
917                 strerror(errno));
918         release_file(self);
919         return FALSE;
920     }
921
922     
923     if (!write_amanda_header(self, ji)) {
924         release_file(self);
925         return FALSE;
926     }
927
928     self->volume_bytes += VFS_DEVICE_LABEL_SIZE;
929     /* make_new_file_name set pself->file for us, but the parent class will increment it, so decrement it now */
930     pself->file--;
931
932     if (parent_class->start_file) {
933         parent_class->start_file(pself, ji);
934     }
935     return TRUE;
936 }
937
938 static gboolean 
939 vfs_device_finish_file (Device * pself) {
940     VfsDevice * self;
941     self = VFS_DEVICE(pself);
942     g_return_val_if_fail(self != NULL, FALSE);
943
944     release_file(self);
945     
946     if (parent_class->finish_file) {
947         return parent_class->finish_file(pself);
948     } else {
949         return TRUE;
950     }
951     g_assert_not_reached();
952 }
953
954 /* This function is used for two purposes, rather than one. In
955  * addition to its documented behavior, we also use it to open the
956  * volume label for reading at startup. In that second case, we avoid
957  * FdDevice-related side effects. */
958 static dumpfile_t * 
959 vfs_device_seek_file (Device * pself, guint requested_file) {
960     VfsDevice * self;
961     int file;
962     dumpfile_t * rval;
963     char header_buffer[VFS_DEVICE_LABEL_SIZE];
964     int header_buffer_size = sizeof(header_buffer);
965     IoResult result;
966
967     self = VFS_DEVICE(pself);
968     g_return_val_if_fail (self != NULL, NULL);
969
970     pself->in_file = FALSE;
971     
972     release_file(self);
973
974     if (requested_file > 0) {
975         file = get_next_file_number(self, requested_file);
976     } else {
977         file = requested_file;
978     }
979
980     if (file < 0) {
981         /* Did they request one past the end? */
982         char * tmp_file_name;
983         tmp_file_name = file_number_to_file_name(self, requested_file - 1);
984         if (tmp_file_name != NULL) {
985             free(tmp_file_name);
986             return make_tapeend_header();
987         } else {
988             return NULL;
989         }
990     }
991
992     if (!open_lock(self, file, FALSE)) {
993         return NULL;
994     }
995
996     self->file_name = file_number_to_file_name(self, file);
997     if (self->file_name == NULL) {
998         release_file(self);
999         return NULL;
1000     }
1001
1002     self->open_file_fd = robust_open(self->file_name, O_RDONLY, 0);
1003     if (self->open_file_fd <= 0) {
1004         g_fprintf(stderr, "Couldn't open file %s: %s\n", self->file_name,
1005                 strerror(errno));
1006         amfree(self->file_name);
1007         release_file(self);
1008         return NULL;
1009     }
1010
1011     result = vfs_device_robust_read(self, header_buffer,
1012                                     &header_buffer_size);
1013     if (result != RESULT_SUCCESS) {
1014         g_fprintf(stderr, "Problem reading Amanda header.\n");
1015         release_file(self);
1016         return NULL;
1017     }
1018
1019     rval = malloc(sizeof(*rval));
1020     parse_file_header(header_buffer, rval, header_buffer_size);
1021     if (file > 0) {
1022         switch (rval->type) {
1023         case F_DUMPFILE:
1024         case F_CONT_DUMPFILE:
1025         case F_SPLIT_DUMPFILE:
1026             /* Chain up. */
1027             if (parent_class->seek_file) {
1028                 parent_class->seek_file(pself, file);
1029             }
1030             return rval;
1031         default:
1032             amfree(rval);
1033             release_file(self);
1034             return NULL;
1035         }
1036     } else if (file == 0) {
1037         return rval;
1038     } else {
1039         amfree(rval);
1040         return NULL;
1041     }
1042 }
1043
1044 static gboolean 
1045 vfs_device_seek_block (Device * pself, guint64 block) {
1046     VfsDevice * self;
1047     off_t result;
1048
1049     self = VFS_DEVICE(pself);
1050     g_return_val_if_fail (self != NULL, FALSE);
1051     g_return_val_if_fail (self->open_file_fd >= 0, FALSE);
1052     g_assert(sizeof(off_t) >= sizeof(guint64));
1053
1054     /* Pretty simple. We figure out the blocksize and use that. */
1055     result = lseek(self->open_file_fd,
1056                    (block) * self->block_size + VFS_DEVICE_LABEL_SIZE,
1057                    SEEK_SET);
1058     return (result != (off_t)(-1));
1059 }
1060
1061 static gboolean
1062 vfs_device_property_get (Device * pself, DevicePropertyId ID, GValue * val) {
1063     VfsDevice * self;
1064     self = VFS_DEVICE(pself);
1065     g_return_val_if_fail(self != NULL, FALSE);
1066     if (ID == PROPERTY_BLOCK_SIZE) {
1067         g_value_unset_init(val, G_TYPE_INT);
1068         g_value_set_int(val, self->block_size);
1069         return TRUE;
1070     } else if (ID == PROPERTY_MAX_VOLUME_USAGE) {
1071         g_value_unset_init(val, G_TYPE_UINT64);
1072         g_value_set_uint64(val, self->volume_limit);
1073         return TRUE;
1074     } else if (ID == PROPERTY_FREE_SPACE) {
1075         QualifiedSize qsize;
1076         struct fs_usage fsusage;
1077         guint64 bytes_avail;
1078
1079         if (get_fs_usage(self->dir_name, NULL, &fsusage) == 0) {
1080             if (fsusage.fsu_bavail_top_bit_set)
1081                 bytes_avail = 0;
1082             else
1083                 bytes_avail = fsusage.fsu_bavail * fsusage.fsu_blocksize;
1084             if (self->volume_limit && (guint64)self->volume_limit < bytes_avail / 1024)
1085                 bytes_avail = (guint64)self->volume_limit * 1024;
1086
1087             qsize.accuracy = SIZE_ACCURACY_REAL;
1088             qsize.bytes = bytes_avail;
1089         } else {
1090             g_warning(_("get_fs_usage('%s') failed: %s"), self->dir_name, strerror(errno));
1091             qsize.accuracy = SIZE_ACCURACY_UNKNOWN;
1092             qsize.bytes = 0;
1093         }
1094         g_value_unset_init(val, QUALIFIED_SIZE_TYPE);
1095         g_value_set_boxed(val, &qsize);
1096         return TRUE;
1097     } else {
1098         if (parent_class->property_get) {
1099             return parent_class->property_get(pself, ID, val);
1100         } else {
1101             return FALSE;
1102         }
1103     }
1104     g_assert_not_reached();
1105 }
1106
1107 static gboolean 
1108 vfs_device_property_set (Device * pself, DevicePropertyId ID, GValue * val) {
1109     VfsDevice * self;
1110     self = VFS_DEVICE(pself);
1111     g_return_val_if_fail(self != NULL, FALSE);
1112     if (ID == PROPERTY_BLOCK_SIZE) {
1113         int block_size = g_value_get_int(val);
1114         g_return_val_if_fail(block_size > 0, FALSE);
1115         self->block_size = block_size;
1116         return TRUE;
1117     } else if (ID == PROPERTY_MAX_VOLUME_USAGE) {
1118         self->volume_limit = g_value_get_uint64(val);
1119         return TRUE;
1120     } else {
1121         if (parent_class->property_set) {
1122             return parent_class->property_set(pself, ID, val);
1123         } else {
1124             return FALSE;
1125         }
1126     }
1127     g_assert_not_reached();
1128 }
1129
1130 static gboolean try_unlink(const char * file) {
1131     if (unlink(file) < 0) {
1132         g_fprintf(stderr, "Can't unlink file %s: %s\n", file, strerror(errno));
1133         return FALSE;
1134     } else {
1135         return TRUE;
1136     }
1137 }
1138
1139 static gboolean 
1140 vfs_device_recycle_file (Device * pself, guint filenum) {
1141     VfsDevice * self;
1142     struct stat file_status;
1143     off_t file_size;
1144
1145     self = VFS_DEVICE(pself);
1146     g_return_val_if_fail(self != NULL, FALSE);
1147     g_return_val_if_fail(!(pself->in_file), FALSE);
1148
1149     /* Game Plan:
1150      * 1) Get a write lock on the file in question.
1151      * 2) Unlink the file in question.
1152      * 3) Unlink the lock.
1153      * 4) Release the lock.
1154      * FIXME: Is it OK to unlink the lockfile?
1155      */
1156
1157     self->file_name = file_number_to_file_name(self, filenum);
1158
1159     if (self->file_name == NULL)
1160         return FALSE;
1161
1162     if (!open_lock(self, filenum, TRUE))
1163         return FALSE;
1164
1165     if (0 != stat(self->file_name, &file_status)) {
1166         fprintf(stderr, "Cannot stat file %s (%s), so not removing.\n",
1167                 self->file_name, strerror(errno));
1168         return FALSE;
1169     }
1170     file_size = file_status.st_size;
1171     
1172     if (!try_unlink(self->file_name) ||
1173         !try_unlink(self->file_lock_name)) {
1174         release_file(self);
1175         return FALSE;
1176     }
1177
1178     self->volume_bytes -= file_size;
1179     release_file(self);
1180     return TRUE;
1181 }
1182
1183 static IoResult vfs_device_robust_read(VfsDevice * self, char *buf,
1184                                              int *count) {
1185     int fd = self->open_file_fd;
1186     int want = *count, got = 0;
1187
1188     while (got < want) {
1189         int result;
1190         result = read(fd, buf + got, want - got);
1191         if (result > 0) {
1192             got += result;
1193         } else if (result == 0) {
1194             /* end of file */
1195             if (got == 0) {
1196                 return RESULT_NO_DATA;
1197             } else {
1198                 *count = got;
1199                 return RESULT_SUCCESS;
1200             }
1201         } else if (0
1202 #ifdef EAGAIN
1203                 || errno == EAGAIN
1204 #endif
1205 #ifdef EWOULDBLOCK
1206                 || errno == EWOULDBLOCK
1207 #endif
1208 #ifdef EINTR
1209                 || errno == EINTR
1210 #endif
1211                    ) {
1212             /* Try again. */
1213             continue;
1214         } else {
1215             /* Error occured. */
1216             g_fprintf(stderr, "Error reading fd %d: %s\n", fd, strerror(errno));
1217             *count = got;
1218             return -1;
1219         }
1220     }
1221
1222     *count = got;
1223     return RESULT_SUCCESS;
1224 }
1225
1226 static IoResult
1227 vfs_device_robust_write(VfsDevice * self,  char *buf, int count) {
1228     int fd = self->open_file_fd;
1229     int rval = 0;
1230
1231     while (rval < count) {
1232         int result;
1233         result = write(fd, buf + rval, count - rval);
1234         if (result > 0) {
1235             rval += result;
1236             continue;
1237         } else if (0
1238 #ifdef EAGAIN
1239                 || errno == EAGAIN
1240 #endif
1241 #ifdef EWOULDBLOCK
1242                 || errno == EWOULDBLOCK
1243 #endif
1244 #ifdef EINTR
1245                 || errno == EINTR
1246 #endif
1247                    ) {
1248             /* Try again. */
1249             continue;
1250         } else if (0
1251 #ifdef EFBIG
1252                    || errno == EFBIG
1253 #endif
1254 #ifdef ENOSPC
1255                    || errno == ENOSPC
1256 #endif
1257                    ) {
1258             /* We are definitely out of space. */
1259             return RESULT_NO_SPACE;
1260         } else {
1261             /* Error occured. Note that here we handle EIO as an error. */
1262             g_fprintf(stderr, "Error writing device fd %d: %s\n",
1263                     fd, strerror(errno));
1264             
1265             return RESULT_ERROR;
1266         }
1267     }
1268     return RESULT_SUCCESS;
1269 }