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