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