Imported Upstream version 2.6.1
[debian/amanda] / device-src / vfs-device.c
index a69b584f69943c85a7489f99caa5c3a42ad70e66..667c5489bc236e170b0f4f8a23fd3427d400bea2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 Zmanda, Inc.  All Rights Reserved.
+ * Copyright (c) 2005-2008 Zmanda Inc.  All Rights Reserved.
  * 
  * This library is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License version 2.1 as 
  * along with this library; if not, write to the Free Software Foundation,
  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
  * 
- * Contact information: Zmanda Inc., 505 N Mathlida Ave, Suite 120
- * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
+ * Contact information: Zmanda Inc., 465 S Mathlida Ave, Suite 300
+ * Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
  */
 
-#include <string.h> /* memset() */
-
 #include "amanda.h"
-#include "vfs-device.h"
+#include <string.h> /* memset() */
 #include "fsusage.h"
 #include "util.h"
+#include "device.h"
 #include <regex.h>
 
+/*
+ * Type checking and casting macros
+ */
+#define TYPE_VFS_DEVICE        (vfs_device_get_type())
+#define VFS_DEVICE(obj)        G_TYPE_CHECK_INSTANCE_CAST((obj), vfs_device_get_type(), VfsDevice)
+#define VFS_DEVICE_CONST(obj)  G_TYPE_CHECK_INSTANCE_CAST((obj), vfs_device_get_type(), VfsDevice const)
+#define VFS_DEVICE_CLASS(klass)        G_TYPE_CHECK_CLASS_CAST((klass), vfs_device_get_type(), VfsDeviceClass)
+#define IS_VFS_DEVICE(obj)     G_TYPE_CHECK_INSTANCE_TYPE((obj), vfs_device_get_type ())
+
+#define VFS_DEVICE_GET_CLASS(obj)      G_TYPE_INSTANCE_GET_CLASS((obj), vfs_device_get_type(), VfsDeviceClass)
+static GType   vfs_device_get_type     (void);
+
+/*
+ * Main object structure
+ */
+typedef struct {
+    Device __parent__;
+
+    /*< private >*/
+    char * dir_name;
+    char * file_name;
+    int file_lock_fd;
+    char * file_lock_name;
+    int volume_lock_fd;
+    char * volume_lock_name;
+    int open_file_fd;
+
+    /* Properties */
+    guint64 volume_bytes;
+    guint64 volume_limit;
+} VfsDevice;
+
+/*
+ * Class definition
+ */
+typedef struct {
+    DeviceClass __parent__;
+} VfsDeviceClass;
+
+
 /* This regex will match all VfsDevice files in a directory. We use it
    for cleanup and verification. Note that this regex does NOT match
    the volume label. */
    generated by lockfile_name(0). */
 #define VOLUME_LOCKFILE_NAME "00000-lock"
 
+#define VFS_DEVICE_MIN_BLOCK_SIZE (1)
+#define VFS_DEVICE_MAX_BLOCK_SIZE (INT_MAX)
+#define VFS_DEVICE_DEFAULT_BLOCK_SIZE (DISK_BLOCK_BYTES)
+#define VFS_DEVICE_LABEL_SIZE (32768)
+
+/* This looks dangerous, but is actually modified by the umask. */
+#define VFS_DEVICE_CREAT_MODE 0666
+
 /* Possible (abstracted) results from a system I/O operation. */
 typedef enum {
     RESULT_SUCCESS,
@@ -46,29 +93,27 @@ typedef enum {
     RESULT_MAX
 } IoResult;
 
+void vfs_device_register(void);
+
 /* here are local prototypes */
 static void vfs_device_init (VfsDevice * o);
 static void vfs_device_class_init (VfsDeviceClass * c);
+static void vfs_device_base_init (VfsDeviceClass * c);
 static void vfs_device_finalize (GObject * o);
 
 static gboolean vfs_device_start(Device * pself, DeviceAccessMode mode,
                                  char * label, char * timestamp);
-static gboolean vfs_device_open_device (Device * pself,
-                                        char * device_name);
-static gboolean vfs_device_start_file (Device * pself, const dumpfile_t * ji);
+static gboolean vfs_device_finish (Device * pself);
+static void vfs_device_open_device (Device * pself, char * device_name,
+                               char * device_type, char * device_node);
+static gboolean vfs_device_start_file (Device * pself, dumpfile_t * ji);
 static gboolean vfs_device_finish_file (Device * pself);
 static dumpfile_t * vfs_device_seek_file (Device * self, guint file);
 static gboolean vfs_device_seek_block (Device * self, guint64 block);
-static gboolean vfs_device_property_get (Device * pself, DevicePropertyId ID,
-                                         GValue * val);
-static gboolean vfs_device_property_set (Device * pself, DevicePropertyId ID,
-                                         GValue * val);
 static gboolean vfs_device_recycle_file (Device * pself, guint filenum);
-static Device * vfs_device_factory(char * device_type,
-                                   char * device_name);
-static ReadLabelStatusFlags vfs_device_read_label(Device * dself);
-static gboolean vfs_device_write_block(Device * self, guint size,
-                                       gpointer data, gboolean last_block);
+static Device * vfs_device_factory(char * device_name, char * device_type, char * device_node);
+static DeviceStatusFlags vfs_device_read_label(Device * dself);
+static gboolean vfs_device_write_block(Device * self, guint size, gpointer data);
 static int vfs_device_read_block(Device * self, gpointer data, int * size_req);
 static IoResult vfs_device_robust_write(VfsDevice * self,  char *buf,
                                               int count);
@@ -77,21 +122,29 @@ static IoResult vfs_device_robust_read(VfsDevice * self, char *buf,
 
 /* Various helper functions. */
 static void release_file(VfsDevice * self);
-static gboolean check_is_dir(const char * name, gboolean printmsg);
+static gboolean check_is_dir(Device * d_self, const char * name);
 static char* file_number_to_file_name(VfsDevice * self, guint file);
 static gboolean file_number_to_file_name_functor(const char * filename,
                                                  gpointer datap);
+static gboolean vfs_device_set_max_volume_usage_fn(Device *p_self,
+                           DevicePropertyBase *base, GValue *val,
+                           PropertySurety surety, PropertySource source);
+gboolean vfs_device_get_free_space_fn(struct Device *p_self,
+                           DevicePropertyBase *base, GValue *val,
+                           PropertySurety *surety, PropertySource *source);
 //static char* lockfile_name(VfsDevice * self, guint file);
 static gboolean open_lock(VfsDevice * self, int file, gboolean exclusive);
 static void promote_volume_lock(VfsDevice * self);
 static void demote_volume_lock(VfsDevice * self);
-static gboolean delete_vfs_files(VfsDevice * self);
+static void delete_vfs_files(VfsDevice * self);
 static gboolean delete_vfs_files_functor(const char * filename,
                                          gpointer self);
 static gboolean check_dir_empty_functor(const char * filename,
                                         gpointer self);
 static gboolean clear_and_prepare_label(VfsDevice * self, char * label,
                                         char * timestamp);
+static int search_vfs_directory(VfsDevice *self, const char * regex,
+                       SearchDirectoryFunctor functor, gpointer user_data);
 static gint get_last_file_number(VfsDevice * self);
 static gboolean get_last_file_number_functor(const char * filename,
                                              gpointer datap);
@@ -106,7 +159,7 @@ void vfs_device_register(void) {
     register_device(vfs_device_factory, device_prefix_list);
 }
 
-GType
+static GType
 vfs_device_get_type (void)
 {
     static GType type = 0;
@@ -114,7 +167,7 @@ vfs_device_get_type (void)
     if G_UNLIKELY(type == 0) {
         static const GTypeInfo info = {
             sizeof (VfsDeviceClass),
-            (GBaseInitFunc) NULL,
+            (GBaseInitFunc) vfs_device_base_init,
             (GBaseFinalizeFunc) NULL,
             (GClassInitFunc) vfs_device_class_init,
             (GClassFinalizeFunc) NULL,
@@ -134,77 +187,57 @@ vfs_device_get_type (void)
 
 static void 
 vfs_device_init (VfsDevice * self) {
-    Device * o;
-    DeviceProperty prop;
+    Device * dself = DEVICE(self);
     GValue response;
 
-    self->dir_handle = NULL;
     self->dir_name = self->file_name = NULL;
     self->file_lock_name = self->volume_lock_name = NULL;
     self->file_lock_fd = self->volume_lock_fd = self->open_file_fd = -1;
-    self->block_size = VFS_DEVICE_DEFAULT_BLOCK_SIZE;
     self->volume_bytes = 0; 
     self->volume_limit = 0;
 
     /* Register Properties */
-    o = DEVICE(self);
     bzero(&response, sizeof(response));
-    prop.base = &device_property_concurrency;
-    prop.access = PROPERTY_ACCESS_GET_MASK;
+
     g_value_init(&response, CONCURRENCY_PARADIGM_TYPE);
     g_value_set_enum(&response, CONCURRENCY_PARADIGM_RANDOM_ACCESS);
-    device_add_property(o, &prop, &response);
+    device_set_simple_property(dself, PROPERTY_CONCURRENCY,
+           &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
     g_value_unset(&response);
 
-    prop.base = &device_property_streaming;
     g_value_init(&response, STREAMING_REQUIREMENT_TYPE);
     g_value_set_enum(&response, STREAMING_REQUIREMENT_NONE);
-    device_add_property(o, &prop, &response);
+    device_set_simple_property(dself, PROPERTY_STREAMING,
+           &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
     g_value_unset(&response);
 
-    prop.base = &device_property_min_block_size;
-    g_value_init(&response, G_TYPE_UINT);
-    g_value_set_uint(&response, VFS_DEVICE_MIN_BLOCK_SIZE);
-    device_add_property(o, &prop, &response);
-
-    prop.base = &device_property_max_block_size;
-    g_value_set_uint(&response, VFS_DEVICE_MAX_BLOCK_SIZE);
-    device_add_property(o, &prop, &response);
+    g_value_init(&response, G_TYPE_BOOLEAN);
+    g_value_set_boolean(&response, TRUE);
+    device_set_simple_property(dself, PROPERTY_APPENDABLE,
+           &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
     g_value_unset(&response);
 
-    prop.base = &device_property_appendable;
     g_value_init(&response, G_TYPE_BOOLEAN);
     g_value_set_boolean(&response, TRUE);
-    device_add_property(o, &prop, &response);
-
-    prop.base = &device_property_partial_deletion;
-    device_add_property(o, &prop, &response);
+    device_set_simple_property(dself, PROPERTY_PARTIAL_DELETION,
+           &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
     g_value_unset(&response);
 
-    /* This one is handled by Device's get_property handler. */
-    prop.base = &device_property_canonical_name;
-    device_add_property(o, &prop, NULL);
+    g_value_init(&response, G_TYPE_BOOLEAN);
+    g_value_set_boolean(&response, FALSE);
+    device_set_simple_property(dself, PROPERTY_COMPRESSION,
+           &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
+    g_value_unset(&response);
 
-    prop.base = &device_property_medium_access_type;
     g_value_init(&response, MEDIA_ACCESS_MODE_TYPE);
     g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE);
-    device_add_property(o, &prop, &response);
+    device_set_simple_property(dself, PROPERTY_MEDIUM_ACCESS_TYPE,
+           &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
     g_value_unset(&response);
-
-    /* These are dynamic, handled in vfs_device_property_xxx */
-    prop.base = &device_property_block_size;
-    prop.access = PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START;
-    device_add_property(o, &prop, NULL);
-
-    prop.base = &device_property_max_volume_usage;
-    prop.access =
-        (PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK) &
-        (~ PROPERTY_ACCESS_SET_INSIDE_FILE_WRITE);
-    device_add_property(o, &prop, NULL);
 }
 
 static void 
-vfs_device_class_init (VfsDeviceClass * c G_GNUC_UNUSED)
+vfs_device_class_init (VfsDeviceClass * c)
 {
     GObjectClass *g_object_class = (GObjectClass*) c;
     DeviceClass *device_class = (DeviceClass *)c;
@@ -220,12 +253,84 @@ vfs_device_class_init (VfsDeviceClass * c G_GNUC_UNUSED)
     device_class->finish_file = vfs_device_finish_file;
     device_class->seek_file = vfs_device_seek_file;
     device_class->seek_block = vfs_device_seek_block;
-    device_class->property_get = vfs_device_property_get;
-    device_class->property_set = vfs_device_property_set;
     device_class->recycle_file = vfs_device_recycle_file;
+    device_class->finish = vfs_device_finish;
     g_object_class->finalize = vfs_device_finalize;
 }
 
+static void
+vfs_device_base_init (VfsDeviceClass * c)
+{
+    DeviceClass *device_class = (DeviceClass *)c;
+
+    device_class_register_property(device_class, PROPERTY_FREE_SPACE,
+           PROPERTY_ACCESS_GET_MASK,
+           vfs_device_get_free_space_fn,
+           NULL);
+
+    device_class_register_property(device_class, PROPERTY_MAX_VOLUME_USAGE,
+           (PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK) &
+                       (~ PROPERTY_ACCESS_SET_INSIDE_FILE_WRITE),
+           device_simple_property_get_fn,
+           vfs_device_set_max_volume_usage_fn);
+
+    device_class_register_property(device_class, PROPERTY_COMPRESSION,
+           PROPERTY_ACCESS_GET_MASK,
+           device_simple_property_get_fn,
+           NULL);
+}
+
+gboolean
+vfs_device_set_max_volume_usage_fn(Device *p_self,
+    DevicePropertyBase *base, GValue *val,
+    PropertySurety surety, PropertySource source)
+{
+    VfsDevice *self = VFS_DEVICE(p_self);
+
+    self->volume_limit = g_value_get_uint64(val);
+
+    return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+gboolean
+vfs_device_get_free_space_fn(struct Device *p_self,
+    DevicePropertyBase *base G_GNUC_UNUSED, GValue *val,
+    PropertySurety *surety, PropertySource *source)
+{
+    VfsDevice *self = VFS_DEVICE(p_self);
+    QualifiedSize qsize;
+    struct fs_usage fsusage;
+    guint64 bytes_avail;
+
+    if (get_fs_usage(self->dir_name, NULL, &fsusage) == 0) {
+       if (fsusage.fsu_bavail_top_bit_set)
+           bytes_avail = 0;
+       else
+           bytes_avail = fsusage.fsu_bavail * fsusage.fsu_blocksize;
+       if (self->volume_limit && (guint64)self->volume_limit < bytes_avail / 1024)
+           bytes_avail = (guint64)self->volume_limit * 1024;
+
+       qsize.accuracy = SIZE_ACCURACY_REAL;
+       qsize.bytes = bytes_avail;
+       if (surety)
+           *surety = PROPERTY_SURETY_GOOD;
+    } else {
+       g_warning(_("get_fs_usage('%s') failed: %s"), self->dir_name, strerror(errno));
+       qsize.accuracy = SIZE_ACCURACY_UNKNOWN;
+       qsize.bytes = 0;
+       if (surety)
+           *surety = PROPERTY_SURETY_BAD;
+    }
+
+    g_value_unset_init(val, QUALIFIED_SIZE_TYPE);
+    g_value_set_boxed(val, &qsize);
+
+    if (source)
+       *source = PROPERTY_SOURCE_DETECTED;
+
+    return TRUE;
+}
+
 /* Drops everything associated with the volume file: Its name and fd,
    its lock, and its lock's name and fd. */
 static void release_file(VfsDevice * self) {
@@ -254,11 +359,6 @@ static void vfs_device_finalize(GObject * obj_self) {
 
     amfree(self->dir_name);
 
-    if(self->dir_handle) {
-        closedir (self->dir_handle);
-        self->dir_handle = NULL;
-    }
-
     release_file(self);
 
     if (self->volume_lock_fd >= 0) {
@@ -269,38 +369,31 @@ static void vfs_device_finalize(GObject * obj_self) {
     amfree(self->volume_lock_name);
 }
 
-static Device * vfs_device_factory(char * device_type,
-                                   char * device_name) {
+static Device * vfs_device_factory(char * device_name, char * device_type, char * device_node) {
     Device * rval;
     g_assert(0 == strcmp(device_type, "file"));
     rval = DEVICE(g_object_new(TYPE_VFS_DEVICE, NULL));
-    if (!device_open_device(rval, device_name)) {
-        g_object_unref(rval);
-        return NULL;
-    } else {
-        return rval;
-    }
+    device_open_device(rval, device_name, device_type, device_node);
+    return rval;
 }
 
-static gboolean check_is_dir(const char * name, gboolean printmsg) {
+static gboolean check_is_dir(Device * d_self, const char * name) {
     struct stat dir_status;
     
     if (stat(name, &dir_status) < 0) {
 #ifdef EINTR
         if (errno == EINTR) {
-            return check_is_dir(name, printmsg);
+            return check_is_dir(d_self, name);
         }
 #endif /* EINTR */
-        if (printmsg) {
-            g_fprintf(stderr, "Error checking directory %s: %s\n",
-                    name, strerror(errno));
-        }
+       device_set_error(d_self,
+           vstrallocf(_("Error checking directory %s: %s"), name, strerror(errno)),
+           DEVICE_STATUS_DEVICE_ERROR);
         return FALSE;
     } else if (!S_ISDIR(dir_status.st_mode)) {
-        if (printmsg) {
-            g_fprintf(stderr, "VFS Device path %s is not a directory.\n",
-                    name);
-        }
+       device_set_error(d_self,
+                   vstrallocf(_("VFS Device path %s is not a directory"), name),
+                   DEVICE_STATUS_DEVICE_ERROR);
         return FALSE;
     } else {
         return TRUE;
@@ -325,11 +418,9 @@ static gboolean file_number_to_file_name_functor(const char * filename,
     /* Just to be thorough, let's check that it's a real
        file. */
     if (0 != stat(result_tmp, &file_status)) {
-        g_fprintf(stderr, "Cannot stat file %s (%s), ignoring it.\n", 
-                result_tmp, strerror(errno));
+       g_warning(_("Cannot stat file %s (%s), ignoring it"), result_tmp, strerror(errno));
     } else if (!S_ISREG(file_status.st_mode)) {
-        g_fprintf(stderr, "%s is not a regular file, ignoring it.\n",
-                result_tmp);
+       g_warning(_("%s is not a regular file, ignoring it"), result_tmp);
     } else {
         data->count ++;
         if (data->result == NULL) {
@@ -349,15 +440,14 @@ static char * file_number_to_file_name(VfsDevice * self, guint device_file) {
     char * regex;
     fnfn_data data;
 
-    g_return_val_if_fail(self != NULL, NULL);
     data.self = self;
     data.count = 0;
     data.result = NULL;
 
     regex = g_strdup_printf("^0*%u\\.", device_file);
 
-    search_directory(self->dir_handle, regex,
-                     file_number_to_file_name_functor, &data);
+    search_vfs_directory(self, regex,
+                         file_number_to_file_name_functor, &data);
 
     amfree(regex);
 
@@ -365,8 +455,7 @@ static char * file_number_to_file_name(VfsDevice * self, guint device_file) {
         g_assert(data.result == NULL);
         return NULL;
     } else if (data.count > 1) {
-        g_fprintf(stderr,
-                "Found multiple names for file number %d, choosing file %s.\n",
+       g_warning("Found multiple names for file number %d, choosing file %s",
                 device_file, data.result);
         return data.result;
     } else {
@@ -401,6 +490,7 @@ static gboolean open_lock(G_GNUC_UNUSED VfsDevice * self,
 /*
     int fd;
     char * name;
+    Device *d_self = DEVICE(self);
     if (file < 0) {
         if (self->volume_lock_name == NULL) {
             self->volume_lock_name = lockfile_name(self, 0);
@@ -422,8 +512,9 @@ static gboolean open_lock(G_GNUC_UNUSED VfsDevice * self,
     fd = robust_open(name, O_CREAT | O_WRONLY, VFS_DEVICE_CREAT_MODE);
 
     if (fd < 0) {
-        g_fprintf(stderr, "Can't open lock file %s: %s\n",
-                name, strerror(errno));
+       device_set_error(d_self,
+           vstrallocf(_("Can't open lock file %s: %s"), name, strerror(errno)),
+           DEVICE_STATUS_DEVICE_ERROR);
         return FALSE;
     }
 
@@ -459,14 +550,12 @@ static gboolean update_volume_size_functor(const char * filename,
     char * full_filename;
     struct stat stat_buf;
     VfsDevice * self = user_data;
-    g_return_val_if_fail(IS_VFS_DEVICE(self), FALSE);
-    
+
     full_filename = vstralloc(self->dir_name, "/", filename, NULL);
 
     if (stat(full_filename, &stat_buf) < 0) {
         /* Log it and keep going. */
-        g_fprintf(stderr, "Couldn't stat file %s: %s\n",
-                full_filename, strerror(errno));
+       g_warning(_("Couldn't stat file %s: %s"), full_filename, strerror(errno));
         amfree(full_filename);
         return TRUE;
     }
@@ -479,48 +568,26 @@ static gboolean update_volume_size_functor(const char * filename,
 
 static void update_volume_size(VfsDevice * self) {
     self->volume_bytes = 0;
-    search_directory(self->dir_handle, "^[0-9]+\\.",
-                     update_volume_size_functor, self);
+    search_vfs_directory(self, "^[0-9]+\\.",
+                         update_volume_size_functor, self);
 
 }
 
-static gboolean 
-vfs_device_open_device (Device * pself, char * device_name) {
+static void
+vfs_device_open_device (Device * pself, char * device_name, char * device_type, char * device_node) {
     VfsDevice * self;
-    dumpfile_t * rval;
-    
     self = VFS_DEVICE(pself);
-    g_return_val_if_fail (self != NULL, FALSE);
-    g_return_val_if_fail (device_name != NULL, FALSE);
+
+    pself->min_block_size = VFS_DEVICE_MIN_BLOCK_SIZE;
+    pself->max_block_size = VFS_DEVICE_MAX_BLOCK_SIZE;
+    pself->block_size = VFS_DEVICE_DEFAULT_BLOCK_SIZE;
 
     /* We don't have to free this ourselves; it will be freed by
      * vfs_device_finalize whether we succeed here or not. */
-    self->dir_name = g_strconcat(device_name, "/data/", NULL);
-    if (!check_is_dir(self->dir_name, TRUE)) {
-        return FALSE;
-    }
-
-    /* Next open the directory itself. */
-    self->dir_handle = opendir(self->dir_name);
-    if (self->dir_handle == NULL) {
-        g_fprintf(stderr, "Couldn't open directory %s for reading: %s\n",
-                device_name, strerror(errno));
-        return FALSE;
-    }
-
-    if (!open_lock(self, -1, FALSE))
-        return FALSE;
-
-    /* Not an error if this fails. Note that we ignore the class hierarchy.
-     */
-    rval = vfs_device_seek_file(pself, 0);
-    amfree(rval);
+    self->dir_name = g_strconcat(device_node, "/data/", NULL);
 
     if (parent_class->open_device) {
-        /* Will call vfs_device_read_label. */
-        return (parent_class->open_device)(pself, device_name);
-    } else {
-        return TRUE;
+        parent_class->open_device(pself, device_name, device_type, device_node);
     }
 }
 
@@ -528,10 +595,11 @@ vfs_device_open_device (Device * pself, char * device_name) {
 static gboolean delete_vfs_files_functor(const char * filename,
                                          gpointer user_data) {
     VfsDevice * self;
+    Device * d_self;
     char * path_name;
 
     self = VFS_DEVICE(user_data);
-    g_return_val_if_fail(self != NULL, FALSE);
+    d_self = DEVICE(self);
 
     /* Skip the volume lock. */
     if (strcmp(filename, VOLUME_LOCKFILE_NAME) == 0)
@@ -539,8 +607,7 @@ static gboolean delete_vfs_files_functor(const char * filename,
 
     path_name = vstralloc(self->dir_name, "/", filename, NULL);
     if (unlink(path_name) != 0) {
-        g_fprintf(stderr, "Error unlinking %s: %s\n", path_name,
-                strerror(errno));
+       g_warning(_("Error unlinking %s: %s"), path_name, strerror(errno));
     }
     amfree(path_name);
     return TRUE;
@@ -548,14 +615,12 @@ static gboolean delete_vfs_files_functor(const char * filename,
 
 /* delete_vfs_files deletes all VfsDevice files in the directory except the
    volume lockfile. */
-static gboolean delete_vfs_files(VfsDevice * self) {
+static void delete_vfs_files(VfsDevice * self) {
     g_assert(self != NULL);
-    g_assert(self->dir_handle != NULL);
 
     /* This function assumes that the volume is locked! */
-    search_directory(self->dir_handle, VFS_DEVICE_FILE_REGEX,
-                     delete_vfs_files_functor, self);
-    return TRUE;
+    search_vfs_directory(self, VFS_DEVICE_FILE_REGEX,
+                         delete_vfs_files_functor, self);
 }
 
 /* This is a functor suitable for search_directory. It simply prints a
@@ -564,16 +629,17 @@ static gboolean check_dir_empty_functor(const char * filename,
                                         gpointer user_data) {
     VfsDevice * self;
     char * path_name;
+    Device *d_self;
 
     self = VFS_DEVICE(user_data);
-    g_return_val_if_fail(self != NULL, FALSE);
+    d_self = DEVICE(self);
 
     if (strcmp(filename, VOLUME_LOCKFILE_NAME) == 0)
         return TRUE;
 
     path_name = vstralloc(self->dir_name, "/", filename, NULL);
 
-    g_fprintf(stderr, "Found spurious storage file %s\n", path_name);
+    g_warning(_("Found spurious storage file %s"), path_name);
 
     amfree(path_name);
     return TRUE;
@@ -584,17 +650,21 @@ static gboolean write_amanda_header(VfsDevice * self,
                                     const dumpfile_t * header) {
     char * label_buffer;
     IoResult result;
-    
-    g_return_val_if_fail(header != NULL, FALSE);
-    g_return_val_if_fail(self != NULL, FALSE);
+    Device *d_self = DEVICE(self);
+
+    g_assert(header != NULL);
+
     label_buffer = build_header(header, VFS_DEVICE_LABEL_SIZE);
     if (strlen(label_buffer)+1 > VFS_DEVICE_LABEL_SIZE) {
         amfree(label_buffer);
-        g_fprintf(stderr, "Amanda header header won't fit on VFS device!\n");
+       device_set_error(d_self,
+           stralloc(_("Amanda file header won't fit in a single block!")),
+           DEVICE_STATUS_DEVICE_ERROR);
         return FALSE;
     }
 
     result = vfs_device_robust_write(self, label_buffer, VFS_DEVICE_LABEL_SIZE);
+    /* vfs_device_robust_write sets error status if necessary */
     amfree(label_buffer);
     return (result == RESULT_SUCCESS);
 }
@@ -605,17 +675,16 @@ static gboolean write_amanda_header(VfsDevice * self,
 static gboolean clear_and_prepare_label(VfsDevice * self, char * label,
                                         char * timestamp) {
     dumpfile_t * label_header;
+    Device *d_self = DEVICE(self);
 
     release_file(self);
 
     /* Delete any extant data, except our volume lock. */
-    if (!delete_vfs_files(self)) {
-        return FALSE;
-    }
+    delete_vfs_files(self);
 
     /* Print warnings about any remaining files. */
-    search_directory(self->dir_handle, VFS_DEVICE_FILE_REGEX,
-                     check_dir_empty_functor, self);
+    search_vfs_directory(self, VFS_DEVICE_FILE_REGEX,
+                         check_dir_empty_functor, self);
 
     self->file_name = g_strdup_printf("%s/00000.%s", self->dir_name, label);
 
@@ -623,84 +692,132 @@ static gboolean clear_and_prepare_label(VfsDevice * self, char * label,
                                      O_CREAT | O_EXCL | O_WRONLY,
                                      VFS_DEVICE_CREAT_MODE);
     if (self->open_file_fd < 0) {
-        g_fprintf(stderr, "Can't open file %s: %s\n", self->file_name,
-                strerror(errno));
+       device_set_error(d_self,
+           vstrallocf(_("Can't open file %s: %s"), self->file_name, strerror(errno)),
+           DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
         return FALSE;
     }
 
     label_header = make_tapestart_header(DEVICE(self), label, timestamp);
-    if (write_amanda_header(self, label_header)) {
-        amfree(label_header);
-        self->volume_bytes = VFS_DEVICE_LABEL_SIZE;
-        return TRUE;
-    } else {
+    if (!write_amanda_header(self, label_header)) {
+       /* write_amanda_header sets error status if necessary */
         amfree(label_header);
         return FALSE;
     }
+    amfree(label_header);
+    self->volume_bytes = VFS_DEVICE_LABEL_SIZE;
+    return TRUE;
 }
 
-static ReadLabelStatusFlags vfs_device_read_label(Device * dself) {
+/* Just like search_directory, but returns -1 in the event of an error */
+static int
+search_vfs_directory(
+    VfsDevice *self,
+    const char * regex,
+    SearchDirectoryFunctor functor,
+    gpointer user_data)
+{
+    Device *dself = DEVICE(self);
+    DIR *dir_handle;
+    int result = -1;
+
+    dir_handle = opendir(self->dir_name);
+    if (dir_handle == NULL) {
+       device_set_error(dself,
+               vstrallocf(_("Couldn't open device %s (directory %s) for reading: %s"),
+                       dself->device_name, self->dir_name, strerror(errno)),
+               DEVICE_STATUS_DEVICE_ERROR);
+        goto error;
+    }
+
+    /* TODO: is this the right moment to acquire a lock?? */
+
+    result = search_directory(dir_handle, regex, functor, user_data);
+
+error:
+    if (dir_handle)
+       closedir(dir_handle);
+    return result;
+}
+
+static DeviceStatusFlags vfs_device_read_label(Device * dself) {
     dumpfile_t * amanda_header;
     VfsDevice * self;
 
     self = VFS_DEVICE(dself);
-    g_return_val_if_fail(self != NULL, ~READ_LABEL_STATUS_SUCCESS);
+    g_assert(self != NULL);
+
+    if (!check_is_dir(dself, self->dir_name)) {
+       /* error message set by check_is_dir */
+        return FALSE;
+    }
+
+    amfree(dself->volume_label);
+    amfree(dself->volume_time);
+    amfree(dself->volume_header);
 
-    amanda_header = vfs_device_seek_file(dself, 0);
+    if (device_in_error(self)) return dself->status;
+
+    amanda_header = dself->volume_header = vfs_device_seek_file(dself, 0);
     if (amanda_header == NULL) {
         /* This means an error occured getting locks or opening the header
          * file. */
-        return (READ_LABEL_STATUS_DEVICE_ERROR |
-                READ_LABEL_STATUS_VOLUME_ERROR |
-                READ_LABEL_STATUS_VOLUME_UNLABELED);
+       device_set_error(dself,
+               stralloc("Error loading device header -- unlabeled volume?"),
+                 DEVICE_STATUS_DEVICE_ERROR
+               | DEVICE_STATUS_VOLUME_ERROR
+               | DEVICE_STATUS_VOLUME_UNLABELED);
+       return dself->status;
     }
 
     if (amanda_header->type != F_TAPESTART) {
         /* This is an error, and should not happen. */
-        g_fprintf(stderr, "Got a bad volume label\n");
+       device_set_error(dself,
+               stralloc(_("Got a bad volume label")),
+               DEVICE_STATUS_VOLUME_ERROR);
         amfree(amanda_header);
-        return READ_LABEL_STATUS_VOLUME_ERROR;
+        return dself->status;
     }
 
     dself->volume_label = g_strdup(amanda_header->name);
     dself->volume_time = g_strdup(amanda_header->datestamp);
-    amfree(amanda_header);
+    /* dself->volume_header is already set */
+
+    device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
 
     update_volume_size(self);
 
-    if (parent_class->read_label) {
-        return (parent_class->read_label)(dself);
-    } else {
-        return READ_LABEL_STATUS_SUCCESS;
-    }
+    return dself->status;
 }
 
-static gboolean vfs_device_write_block(Device * pself, guint size,
-                                       gpointer data, gboolean last_block) {
+static gboolean vfs_device_write_block(Device * pself, guint size, gpointer data) {
     VfsDevice * self = VFS_DEVICE(pself);
     IoResult result;
-    g_return_val_if_fail(self != NULL, FALSE);
-    g_return_val_if_fail(last_block || size >= (guint)self->block_size, FALSE);
-    g_return_val_if_fail(pself->in_file, FALSE);
+
+    if (device_in_error(self)) return FALSE;
+
     g_assert(self->open_file_fd >= 0);
 
     if (self->volume_limit > 0 &&
         self->volume_bytes + size > self->volume_limit) {
         /* Simulate EOF. */
         pself->is_eof = TRUE;
+       device_set_error(pself,
+           stralloc(_("No space left on device")),
+           DEVICE_STATUS_VOLUME_ERROR);
         return FALSE;
     }
 
     result = vfs_device_robust_write(self, data, size);
-    if (result == RESULT_SUCCESS) {
-        self->volume_bytes += size;
-        if (parent_class->write_block) {
-            (parent_class->write_block)(pself, size, data, last_block);
-        }
-        return TRUE;
-    } else {
+    if (result != RESULT_SUCCESS) {
+       /* vfs_device_robust_write set error status appropriately */
         return FALSE;
     }
+
+    self->volume_bytes += size;
+    pself->block ++;
+
+    return TRUE;
 }
 
 static int
@@ -710,25 +827,34 @@ vfs_device_read_block(Device * pself, gpointer data, int * size_req) {
     IoResult result;
     
     self = VFS_DEVICE(pself);
-    g_return_val_if_fail (self != NULL, -1);
 
-    if (data == NULL || *size_req < self->block_size) {
+    if (device_in_error(self)) return -1;
+
+    if (data == NULL || (gsize)*size_req < pself->block_size) {
         /* Just a size query. */
-        *size_req = self->block_size;
+       g_assert(pself->block_size < INT_MAX);
+        *size_req = (int)pself->block_size;
         return 0;
     }
 
-    size = self->block_size;
+    size = pself->block_size;
     result = vfs_device_robust_read(self, data, &size);
     switch (result) {
     case RESULT_SUCCESS:
         *size_req = size;
+       pself->block++;
         return size;
     case RESULT_NO_DATA:
         pself->is_eof = TRUE;
         pself->in_file = FALSE;
+       device_set_error(pself,
+           stralloc(_("EOF")),
+           DEVICE_STATUS_SUCCESS);
         return -1;
     default:
+       device_set_error(pself,
+           vstrallocf(_("Error reading from data file: %s"), strerror(errno)),
+           DEVICE_STATUS_DEVICE_ERROR);
         return -1;
     }
 
@@ -740,25 +866,53 @@ static gboolean   vfs_device_start(Device * pself,
                                  char * timestamp) {
     VfsDevice * self;
     self = VFS_DEVICE(pself);
-    g_return_val_if_fail(self != NULL, FALSE);
-    g_return_val_if_fail(parent_class->start != NULL, FALSE);
-    
+
+    if (!check_is_dir(pself, self->dir_name)) {
+       /* error message set by check_is_dir */
+        return FALSE;
+    }
+
+    pself->in_file = FALSE;
+
     if (mode == ACCESS_WRITE) {
         promote_volume_lock(self);
         if (!clear_and_prepare_label(self, label, timestamp)) {
+           /* clear_and_prepare_label sets error status if necessary */
             demote_volume_lock(self);
             return FALSE;
         }
+
+        pself->volume_label = newstralloc(pself->volume_label, label);
+        pself->volume_time = newstralloc(pself->volume_time, timestamp);
+
+       /* unset the VOLUME_UNLABELED flag, if it was set */
+       device_set_error(pself, NULL, DEVICE_STATUS_SUCCESS);
+
         demote_volume_lock(self);
+        pself->access_mode = mode;
+    } else {
+       if (pself->volume_label == NULL && device_read_label(pself) != DEVICE_STATUS_SUCCESS) {
+           /* device_read_label already set our error message */
+            return FALSE;
+       } else {
+            pself->access_mode = mode;
+        }
     }
 
     release_file(self);
  
-    if (parent_class->start) {
-        return parent_class->start(pself, mode, label, timestamp);
-    } else {
-        return TRUE;
-    }
+    return TRUE;
+}
+
+static gboolean
+vfs_device_finish (Device * pself) {
+    VfsDevice * self;
+    self = VFS_DEVICE(pself);
+
+    if (device_in_error(self)) return FALSE;
+
+    pself->access_mode = ACCESS_NULL;
+    return TRUE;
 }
 
 typedef struct {
@@ -771,11 +925,10 @@ static gboolean get_last_file_number_functor(const char * filename,
                                              gpointer datap) {
     guint64 file;
     glfn_data * data = (glfn_data*)datap;
-    g_return_val_if_fail(IS_VFS_DEVICE(data->self), FALSE);
+
     file = g_ascii_strtoull(filename, NULL, 10); /* Guaranteed to work. */
     if (file > G_MAXINT) {
-        g_fprintf(stderr, "Super-large device file %s found, ignoring.\n",
-               filename);
+       g_warning(_("Super-large device file %s found, ignoring"), filename);
         return TRUE;
     }
     /* This condition is needlessly complex due to sign issues. */
@@ -788,15 +941,18 @@ static gboolean get_last_file_number_functor(const char * filename,
 static gint get_last_file_number(VfsDevice * self) {
     glfn_data data;
     int count;
+    Device *d_self = DEVICE(self);
     data.self = self;
     data.rval = -1;
     
-    count = search_directory(self->dir_handle, "^[0-9]+\\.",
-                             get_last_file_number_functor, &data);
+    count = search_vfs_directory(self, "^[0-9]+\\.",
+                                 get_last_file_number_functor, &data);
 
     if (count <= 0) {
         /* Somebody deleted something important while we weren't looking. */
-        g_fprintf(stderr, "Error identifying VFS device contents!\n");
+       device_set_error(d_self,
+           stralloc(_("Error identifying VFS device contents!")),
+           DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
         return -1;
     } else {
         g_assert(data.rval >= 0);
@@ -816,11 +972,10 @@ static gboolean get_next_file_number_functor(const char * filename,
                                              gpointer datap) {
     guint file;
     gnfn_data * data = (gnfn_data*)datap;
-    g_return_val_if_fail(IS_VFS_DEVICE(data->self), FALSE);
+
     file = g_ascii_strtoull(filename, NULL, 10); /* Guaranteed to work. */
     if (file > G_MAXINT) {
-        g_fprintf(stderr, "Super-large device file %s found, ignoring.\n",
-               filename);
+       g_warning(_("Super-large device file %s found, ignoring"), filename);
         return TRUE;
     }
     /* This condition is needlessly complex due to sign issues. */
@@ -836,16 +991,19 @@ static gboolean get_next_file_number_functor(const char * filename,
 static gint get_next_file_number(VfsDevice * self, guint request) {
     gnfn_data data;
     int count;
+    Device *d_self = DEVICE(self);
     data.self = self;
     data.request = request;
     data.best_found = -1;
     
-    count = search_directory(self->dir_handle, "^[0-9]+\\.",
-                             get_next_file_number_functor, &data);
+    count = search_vfs_directory(self, "^[0-9]+\\.",
+                                 get_next_file_number_functor, &data);
 
     if (count <= 0) {
         /* Somebody deleted something important while we weren't looking. */
-        g_fprintf(stderr, "Error identifying VFS device contents!\n");
+       device_set_error(d_self,
+           stralloc(_("Error identifying VFS device contents!")),
+           DEVICE_STATUS_DEVICE_ERROR | DEVICE_STATUS_VOLUME_ERROR);
         return -1;
     }
     
@@ -885,16 +1043,21 @@ char * make_new_file_name(VfsDevice * self, const dumpfile_t * ji) {
 }
 
 static gboolean 
-vfs_device_start_file (Device * pself, const dumpfile_t * ji) {
+vfs_device_start_file (Device * pself, dumpfile_t * ji) {
     VfsDevice * self;
-
     self = VFS_DEVICE(pself);
-    g_return_val_if_fail (self != NULL, FALSE);
-    g_return_val_if_fail (ji != NULL, FALSE);
+
+    if (device_in_error(self)) return FALSE;
+
+    /* set the blocksize in the header to 32k, since the VFS header is always
+     * 32k regardless of the block_size setting */
+    ji->blocksize = 32768;
 
     if (self->volume_limit > 0 &&
         self->volume_bytes + VFS_DEVICE_LABEL_SIZE > self->volume_limit) {
-        /* No more room. */
+       device_set_error(pself,
+               stralloc(_("No space left on device")),
+               DEVICE_STATUS_DEVICE_ERROR);
         return FALSE;
     }
 
@@ -906,32 +1069,37 @@ vfs_device_start_file (Device * pself, const dumpfile_t * ji) {
        5) Chain up. */
 
     self->file_name = make_new_file_name(self, ji);
-    if (self->file_name == NULL)
+    if (self->file_name == NULL) {
+       device_set_error(pself,
+               stralloc(_("Could not create header filename")),
+               DEVICE_STATUS_DEVICE_ERROR);
         return FALSE;
+    }
 
     self->open_file_fd = robust_open(self->file_name,
                                      O_CREAT | O_EXCL | O_RDWR,
                                      VFS_DEVICE_CREAT_MODE);
     if (self->open_file_fd < 0) {
-        g_fprintf(stderr, "Can't create file %s: %s\n", self->file_name,
-                strerror(errno));
+       device_set_error(pself,
+               vstrallocf(_("Can't create file %s: %s"), self->file_name, strerror(errno)),
+               DEVICE_STATUS_DEVICE_ERROR);
         release_file(self);
         return FALSE;
     }
 
     
     if (!write_amanda_header(self, ji)) {
+       /* write_amanda_header sets error status if necessary */
         release_file(self);
         return FALSE;
     }
 
+    /* handle some accounting business */
     self->volume_bytes += VFS_DEVICE_LABEL_SIZE;
-    /* make_new_file_name set pself->file for us, but the parent class will increment it, so decrement it now */
-    pself->file--;
+    pself->in_file = TRUE;
+    pself->block = 0;
+    /* make_new_file_name set pself->file for us */
 
-    if (parent_class->start_file) {
-        parent_class->start_file(pself, ji);
-    }
     return TRUE;
 }
 
@@ -939,16 +1107,13 @@ static gboolean
 vfs_device_finish_file (Device * pself) {
     VfsDevice * self;
     self = VFS_DEVICE(pself);
-    g_return_val_if_fail(self != NULL, FALSE);
+
+    if (device_in_error(self)) return FALSE;
 
     release_file(self);
-    
-    if (parent_class->finish_file) {
-        return parent_class->finish_file(pself);
-    } else {
-        return TRUE;
-    }
-    g_assert_not_reached();
+
+    pself->in_file = FALSE;
+    return TRUE;
 }
 
 /* This function is used for two purposes, rather than one. In
@@ -965,9 +1130,12 @@ vfs_device_seek_file (Device * pself, guint requested_file) {
     IoResult result;
 
     self = VFS_DEVICE(pself);
-    g_return_val_if_fail (self != NULL, NULL);
+
+    if (device_in_error(self)) return NULL;
 
     pself->in_file = FALSE;
+    pself->is_eof = FALSE;
+    pself->block = 0;
     
     release_file(self);
 
@@ -983,26 +1151,37 @@ vfs_device_seek_file (Device * pself, guint requested_file) {
         tmp_file_name = file_number_to_file_name(self, requested_file - 1);
         if (tmp_file_name != NULL) {
             free(tmp_file_name);
+           pself->file = requested_file; /* other attributes are already correct */
             return make_tapeend_header();
         } else {
+           device_set_error(pself,
+               stralloc(_("Attempt to read past tape-end file")),
+               DEVICE_STATUS_SUCCESS);
             return NULL;
         }
     }
 
     if (!open_lock(self, file, FALSE)) {
+       device_set_error(pself,
+           stralloc(_("could not acquire lock")),
+           DEVICE_STATUS_DEVICE_ERROR);
         return NULL;
     }
 
     self->file_name = file_number_to_file_name(self, file);
     if (self->file_name == NULL) {
+       device_set_error(pself,
+           vstrallocf(_("File %d not found"), file),
+           DEVICE_STATUS_VOLUME_ERROR);
         release_file(self);
         return NULL;
     }
 
     self->open_file_fd = robust_open(self->file_name, O_RDONLY, 0);
-    if (self->open_file_fd <= 0) {
-        g_fprintf(stderr, "Couldn't open file %s: %s\n", self->file_name,
-                strerror(errno));
+    if (self->open_file_fd < 0) {
+       device_set_error(pself,
+           vstrallocf(_("Couldn't open file %s: %s"), self->file_name, strerror(errno)),
+           DEVICE_STATUS_DEVICE_ERROR);
         amfree(self->file_name);
         release_file(self);
         return NULL;
@@ -1011,34 +1190,42 @@ vfs_device_seek_file (Device * pself, guint requested_file) {
     result = vfs_device_robust_read(self, header_buffer,
                                     &header_buffer_size);
     if (result != RESULT_SUCCESS) {
-        g_fprintf(stderr, "Problem reading Amanda header.\n");
+       device_set_error(pself,
+           vstrallocf(_("Problem reading Amanda header: %s"), device_error(pself)),
+           DEVICE_STATUS_VOLUME_ERROR);
         release_file(self);
         return NULL;
     }
 
-    rval = malloc(sizeof(*rval));
+    rval = g_new(dumpfile_t, 1);
     parse_file_header(header_buffer, rval, header_buffer_size);
-    if (file > 0) {
-        switch (rval->type) {
+    switch (rval->type) {
         case F_DUMPFILE:
         case F_CONT_DUMPFILE:
         case F_SPLIT_DUMPFILE:
-            /* Chain up. */
-            if (parent_class->seek_file) {
-                parent_class->seek_file(pself, file);
-            }
-            return rval;
+           break;
+
+       case F_TAPESTART:
+           /* file 0 should have a TAPESTART header; vfs_device_read_label
+            * uses this */
+           if (requested_file == 0)
+               break;
+           /* FALLTHROUGH */
+
         default:
+           device_set_error(pself,
+               stralloc(_("Invalid amanda header while reading file header")),
+               DEVICE_STATUS_VOLUME_ERROR);
             amfree(rval);
             release_file(self);
             return NULL;
-        }
-    } else if (file == 0) {
-        return rval;
-    } else {
-        amfree(rval);
-        return NULL;
     }
+
+    /* update our state */
+    pself->in_file = TRUE;
+    pself->file = file;
+
+    return rval;
 }
 
 static gboolean 
@@ -1047,89 +1234,30 @@ vfs_device_seek_block (Device * pself, guint64 block) {
     off_t result;
 
     self = VFS_DEVICE(pself);
-    g_return_val_if_fail (self != NULL, FALSE);
-    g_return_val_if_fail (self->open_file_fd >= 0, FALSE);
+
+    g_assert(self->open_file_fd >= 0);
     g_assert(sizeof(off_t) >= sizeof(guint64));
+    if (device_in_error(self)) return FALSE;
 
     /* Pretty simple. We figure out the blocksize and use that. */
     result = lseek(self->open_file_fd,
-                   (block) * self->block_size + VFS_DEVICE_LABEL_SIZE,
+                   (block) * pself->block_size + VFS_DEVICE_LABEL_SIZE,
                    SEEK_SET);
-    return (result != (off_t)(-1));
-}
 
-static gboolean
-vfs_device_property_get (Device * pself, DevicePropertyId ID, GValue * val) {
-    VfsDevice * self;
-    self = VFS_DEVICE(pself);
-    g_return_val_if_fail(self != NULL, FALSE);
-    if (ID == PROPERTY_BLOCK_SIZE) {
-        g_value_unset_init(val, G_TYPE_INT);
-        g_value_set_int(val, self->block_size);
-        return TRUE;
-    } else if (ID == PROPERTY_MAX_VOLUME_USAGE) {
-        g_value_unset_init(val, G_TYPE_UINT64);
-        g_value_set_uint64(val, self->volume_limit);
-        return TRUE;
-    } else if (ID == PROPERTY_FREE_SPACE) {
-       QualifiedSize qsize;
-       struct fs_usage fsusage;
-       guint64 bytes_avail;
-
-       if (get_fs_usage(self->dir_name, NULL, &fsusage) == 0) {
-           if (fsusage.fsu_bavail_top_bit_set)
-               bytes_avail = 0;
-           else
-               bytes_avail = fsusage.fsu_bavail * fsusage.fsu_blocksize;
-           if (self->volume_limit && (guint64)self->volume_limit < bytes_avail / 1024)
-               bytes_avail = (guint64)self->volume_limit * 1024;
-
-           qsize.accuracy = SIZE_ACCURACY_REAL;
-           qsize.bytes = bytes_avail;
-       } else {
-           g_warning(_("get_fs_usage('%s') failed: %s"), self->dir_name, strerror(errno));
-           qsize.accuracy = SIZE_ACCURACY_UNKNOWN;
-           qsize.bytes = 0;
-       }
-       g_value_unset_init(val, QUALIFIED_SIZE_TYPE);
-       g_value_set_boxed(val, &qsize);
-       return TRUE;
-    } else {
-        if (parent_class->property_get) {
-            return parent_class->property_get(pself, ID, val);
-        } else {
-            return FALSE;
-        }
-    }
-    g_assert_not_reached();
-}
+    pself->block = block;
 
-static gboolean 
-vfs_device_property_set (Device * pself, DevicePropertyId ID, GValue * val) {
-    VfsDevice * self;
-    self = VFS_DEVICE(pself);
-    g_return_val_if_fail(self != NULL, FALSE);
-    if (ID == PROPERTY_BLOCK_SIZE) {
-        int block_size = g_value_get_int(val);
-        g_return_val_if_fail(block_size > 0, FALSE);
-        self->block_size = block_size;
-        return TRUE;
-    } else if (ID == PROPERTY_MAX_VOLUME_USAGE) {
-        self->volume_limit = g_value_get_uint64(val);
-        return TRUE;
-    } else {
-        if (parent_class->property_set) {
-            return parent_class->property_set(pself, ID, val);
-        } else {
-            return FALSE;
-        }
+    if (result == (off_t)(-1)) {
+       device_set_error(pself,
+           vstrallocf(_("Error seeking within file: %s"), strerror(errno)),
+           DEVICE_STATUS_DEVICE_ERROR);
+       return FALSE;
     }
-    g_assert_not_reached();
+
+    return TRUE;
 }
 
 static gboolean try_unlink(const char * file) {
     if (unlink(file) < 0) {
-        g_fprintf(stderr, "Can't unlink file %s: %s\n", file, strerror(errno));
         return FALSE;
     } else {
         return TRUE;
@@ -1143,8 +1271,8 @@ vfs_device_recycle_file (Device * pself, guint filenum) {
     off_t file_size;
 
     self = VFS_DEVICE(pself);
-    g_return_val_if_fail(self != NULL, FALSE);
-    g_return_val_if_fail(!(pself->in_file), FALSE);
+
+    if (device_in_error(self)) return FALSE;
 
     /* Game Plan:
      * 1) Get a write lock on the file in question.
@@ -1155,22 +1283,41 @@ vfs_device_recycle_file (Device * pself, guint filenum) {
      */
 
     self->file_name = file_number_to_file_name(self, filenum);
-
-    if (self->file_name == NULL)
+    if (self->file_name == NULL) {
+       device_set_error(pself,
+           vstrallocf(_("File %d not found"), filenum),
+           DEVICE_STATUS_VOLUME_ERROR);
         return FALSE;
+    }
 
-    if (!open_lock(self, filenum, TRUE))
+    if (!open_lock(self, filenum, FALSE)) {
+       device_set_error(pself,
+           stralloc(_("could not acquire lock")),
+           DEVICE_STATUS_DEVICE_ERROR);
         return FALSE;
+    }
 
     if (0 != stat(self->file_name, &file_status)) {
-        fprintf(stderr, "Cannot stat file %s (%s), so not removing.\n",
-                self->file_name, strerror(errno));
+       device_set_error(pself,
+           vstrallocf(_("Cannot stat file %s (%s), so not removing"),
+                                   self->file_name, strerror(errno)),
+           DEVICE_STATUS_VOLUME_ERROR);
         return FALSE;
     }
     file_size = file_status.st_size;
     
-    if (!try_unlink(self->file_name) ||
-        !try_unlink(self->file_lock_name)) {
+    if (!try_unlink(self->file_name)) {
+       device_set_error(pself,
+           vstrallocf(_("Unlink of %s failed: %s"), self->file_name, strerror(errno)),
+           DEVICE_STATUS_VOLUME_ERROR);
+        release_file(self);
+        return FALSE;
+    }
+
+    if (!try_unlink(self->file_lock_name)) {
+       device_set_error(pself,
+           vstrallocf(_("Unlink of %s failed: %s"), self->file_lock_name, strerror(errno)),
+           DEVICE_STATUS_VOLUME_ERROR);
         release_file(self);
         return FALSE;
     }
@@ -1183,6 +1330,7 @@ vfs_device_recycle_file (Device * pself, guint filenum) {
 static IoResult vfs_device_robust_read(VfsDevice * self, char *buf,
                                              int *count) {
     int fd = self->open_file_fd;
+    Device *d_self = DEVICE(self);
     int want = *count, got = 0;
 
     while (got < want) {
@@ -1213,9 +1361,11 @@ static IoResult vfs_device_robust_read(VfsDevice * self, char *buf,
             continue;
         } else {
             /* Error occured. */
-            g_fprintf(stderr, "Error reading fd %d: %s\n", fd, strerror(errno));
+           device_set_error(d_self,
+               vstrallocf(_("Error reading fd %d: %s"), fd, strerror(errno)),
+               DEVICE_STATUS_VOLUME_ERROR);
             *count = got;
-            return -1;
+            return RESULT_ERROR;
         }
     }
 
@@ -1226,6 +1376,7 @@ static IoResult vfs_device_robust_read(VfsDevice * self, char *buf,
 static IoResult
 vfs_device_robust_write(VfsDevice * self,  char *buf, int count) {
     int fd = self->open_file_fd;
+    Device *d_self = DEVICE(self);
     int rval = 0;
 
     while (rval < count) {
@@ -1256,12 +1407,15 @@ vfs_device_robust_write(VfsDevice * self,  char *buf, int count) {
 #endif
                    ) {
             /* We are definitely out of space. */
+           device_set_error(d_self,
+                   vstrallocf(_("No space left on device: %s"), strerror(errno)),
+                   DEVICE_STATUS_VOLUME_ERROR);
             return RESULT_NO_SPACE;
         } else {
             /* Error occured. Note that here we handle EIO as an error. */
-            g_fprintf(stderr, "Error writing device fd %d: %s\n",
-                    fd, strerror(errno));
-            
+           device_set_error(d_self,
+                   vstrallocf(_("Error writing device fd %d: %s"), fd, strerror(errno)),
+                   DEVICE_STATUS_VOLUME_ERROR);
             return RESULT_ERROR;
         }
     }