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