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