/*
- * Copyright (c) 2005 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
- * published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful, but
+ * Copyright (c) 2007, 2008, 2009, 2010 Zmanda, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
- * License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * 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
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
* Sunnyvale, CA 94085, 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 <regex.h>
+#include "vfs-device.h"
+
/* 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)
+
+/* Allow comfortable room for another block and a header before PEOM */
+#define EOM_EARLY_WARNING_ZONE_BLOCKS 4
+
+/* Constants for free-space monitoring */
+#define MONITOR_FREE_SPACE_EVERY_SECONDS 5
+#define MONITOR_FREE_SPACE_EVERY_KB 102400
+#define MONITOR_FREE_SPACE_CLOSELY_WITHIN_BLOCKS 128
+
+/* 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,
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_file (Device * pself);
+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 * dself);
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 gboolean vfs_device_erase (Device * pself);
+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);
/* Various helper functions. */
static void release_file(VfsDevice * self);
-static gboolean check_is_dir(const char * name, gboolean printmsg);
-static char* file_number_to_file_name(VfsDevice * self, guint file);
+static gboolean check_is_dir(VfsDevice * 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);
+static gboolean property_get_monitor_free_space_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety *surety, PropertySource *source);
+static gboolean property_set_monitor_free_space_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source);
+static gboolean property_set_leom_fn(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 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);
static char * make_new_file_name(VfsDevice * self, const dumpfile_t * ji);
static gboolean try_unlink(const char * file);
+/* return TRUE if the device is going to hit ENOSPC "soon" - this is used to
+ * detect LEOM as represented by actually running out of space on the
+ * underlying filesystem. Size is the size of the buffer that is about to
+ * be written. */
+static gboolean check_at_leom(VfsDevice *self, guint64 size);
+/* Similar, but for PEOM */
+static gboolean check_at_peom(VfsDevice *self, guint64 size);
+
/* pointer to the classes of our parents */
static DeviceClass *parent_class = NULL;
+/* device-specific properties */
+DevicePropertyBase device_property_monitor_free_space;
+#define PROPERTY_MONITOR_FREE_SPACE (device_property_monitor_free_space.ID)
+
void vfs_device_register(void) {
static const char * device_prefix_list[] = { "file", NULL };
+
+ device_property_fill_and_register(&device_property_monitor_free_space,
+ G_TYPE_BOOLEAN, "monitor_free_space",
+ "Should VFS device monitor the filesystem's available free space?");
+
register_device(vfs_device_factory, device_prefix_list);
}
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,
type = g_type_register_static (TYPE_DEVICE, "VfsDevice",
&info, (GTypeFlags)0);
}
-
+
return type;
}
-static 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->open_file_fd = -1;
+ self->volume_bytes = 0;
self->volume_limit = 0;
+ self->leom = TRUE;
+
+ self->monitor_free_space = TRUE;
+ self->checked_fs_free_bytes = G_MAXUINT64;
+ self->checked_fs_free_time = 0;
+ self->checked_fs_free_bytes = G_MAXUINT64;
/* 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);
+ 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_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_PARTIAL_DELETION,
+ &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);
+ device_set_simple_property(dself, PROPERTY_FULL_DELETION,
+ &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
+ g_value_unset(&response);
- prop.base = &device_property_partial_deletion;
- device_add_property(o, &prop, &response);
+ g_value_init(&response, G_TYPE_BOOLEAN);
+ g_value_set_boolean(&response, FALSE);
+ device_set_simple_property(dself, PROPERTY_LEOM,
+ &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)
+static void
+vfs_device_class_init (VfsDeviceClass * c)
{
GObjectClass *g_object_class = (GObjectClass*) c;
- DeviceClass *device_class = (DeviceClass *)c;
+ DeviceClass *device_class = DEVICE_CLASS(c);
parent_class = g_type_class_ref(TYPE_DEVICE);
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->erase = vfs_device_erase;
+ device_class->finish = vfs_device_finish;
+
g_object_class->finalize = vfs_device_finalize;
}
-/* 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) {
+static void
+vfs_device_base_init (VfsDeviceClass * c)
+{
+ DeviceClass *device_class = (DeviceClass *)c;
+
+ device_class_register_property(device_class, PROPERTY_MONITOR_FREE_SPACE,
+ PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_MASK,
+ property_get_monitor_free_space_fn,
+ property_set_monitor_free_space_fn);
+
+ 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);
+
+ /* add the ability to set LEOM to FALSE, for testing purposes */
+ device_class_register_property(device_class, PROPERTY_LEOM,
+ PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
+ device_simple_property_get_fn,
+ property_set_leom_fn);
+}
+
+static 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);
+}
+
+static gboolean
+property_get_monitor_free_space_fn(Device *p_self, DevicePropertyBase *base G_GNUC_UNUSED,
+ GValue *val, PropertySurety *surety, PropertySource *source)
+{
+ VfsDevice *self = VFS_DEVICE(p_self);
+
+ g_value_unset_init(val, G_TYPE_BOOLEAN);
+ g_value_set_boolean(val, self->monitor_free_space);
+
+ if (surety)
+ *surety = PROPERTY_SURETY_GOOD;
+
+ if (source)
+ *source = PROPERTY_SOURCE_DEFAULT;
+
+ return TRUE;
+}
+
+
+static gboolean
+property_set_monitor_free_space_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ VfsDevice *self = VFS_DEVICE(p_self);
+
+ self->monitor_free_space = g_value_get_boolean(val);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+static gboolean
+property_set_leom_fn(Device *p_self,
+ DevicePropertyBase *base, GValue *val,
+ PropertySurety surety, PropertySource source)
+{
+ VfsDevice *self = VFS_DEVICE(p_self);
+
+ self->leom = g_value_get_boolean(val);
+
+ return device_simple_property_set_fn(p_self, base, val, surety, source);
+}
+
+/* Drops everything associated with the volume file: Its name and fd. */
+void release_file(VfsDevice * self) {
/* Doesn't hurt. */
- robust_close(self->open_file_fd);
+ if (self->open_file_fd != -1)
+ robust_close(self->open_file_fd);
amfree(self->file_name);
- if (self->file_lock_fd > 0) {
- amfunlock(self->file_lock_fd, self->file_lock_name);
- close(self->file_lock_fd);
- amfree(self->file_lock_name);
- }
- self->file_lock_fd = self->open_file_fd = -1;
+ self->open_file_fd = -1;
}
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) {
- amfunlock(self->volume_lock_fd, self->volume_lock_name);
- close(self->volume_lock_fd);
- }
-
- 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(VfsDevice * self, const char * name) {
+ Device *dself = DEVICE(self);
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(self, name);
}
#endif /* EINTR */
- if (printmsg) {
- g_fprintf(stderr, "Error checking directory %s: %s\n",
- name, strerror(errno));
- }
+ device_set_error(dself,
+ 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(dself,
+ vstrallocf(_("VFS Device path %s is not a directory"), name),
+ DEVICE_STATUS_DEVICE_ERROR);
return FALSE;
} else {
return TRUE;
char * result_tmp;
struct stat file_status;
fnfn_data *data = (fnfn_data*)datap;
-
- result_tmp = vstralloc(data->self->dir_name, "/", filename, NULL);
-
+
+ result_tmp = vstralloc(data->self->dir_name, "/", filename, NULL);
+
/* 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) {
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);
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 {
/* At the moment, file locking is horribly broken. */
return TRUE;
-
-/*
- int fd;
- char * name;
- if (file < 0) {
- if (self->volume_lock_name == NULL) {
- self->volume_lock_name = lockfile_name(self, 0);
- } else if (self->volume_lock_fd >= 0) {
- amfunlock(self->volume_lock_fd, self->volume_lock_name);
- close(self->volume_lock_fd);
- }
- name = self->volume_lock_name;
- } else {
- if (self->file_lock_fd >= 0 && self->file_lock_name != NULL) {
- amfunlock(self->file_lock_fd, self->file_lock_name);
- }
- amfree(self->file_lock_name);
- close(self->file_lock_fd);
- name = self->file_lock_name = lockfile_name(self, file);
- }
-
-
- 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));
- return FALSE;
- }
-
- if (exclusive) {
- amflock(fd, name);
- } else {
- amroflock(fd, name);
- }
-
- if (file < 0) {
- self->volume_lock_fd = fd;
- } else {
- self->file_lock_fd = fd;
- }
- return TRUE;
-*/
}
/* For now, does it the bad way. */
-static void promote_volume_lock(VfsDevice * self) {
- amfunlock(self->volume_lock_fd, self->volume_lock_name);
- amflock(self->volume_lock_fd, self->volume_lock_name);
+static void promote_volume_lock(VfsDevice * self G_GNUC_UNUSED) {
}
-static void demote_volume_lock(VfsDevice * self) {
- amfunlock(self->volume_lock_fd, self->volume_lock_name);
- amroflock(self->volume_lock_fd, self->volume_lock_name);
+static void demote_volume_lock(VfsDevice * self G_GNUC_UNUSED) {
}
/* A SearchDirectoryFunctor */
gpointer user_data) {
char * full_filename;
struct stat stat_buf;
- VfsDevice * self = user_data;
- g_return_val_if_fail(IS_VFS_DEVICE(self), FALSE);
-
+ VfsDevice * self = VFS_DEVICE(user_data);
+
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;
}
}
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);
}
}
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)
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;
/* delete_vfs_files deletes all VfsDevice files in the directory except the
volume lockfile. */
-static gboolean delete_vfs_files(VfsDevice * self) {
+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
warning. It also dodges the volume lockfile. */
static gboolean check_dir_empty_functor(const char * filename,
gpointer user_data) {
- VfsDevice * self;
+ VfsDevice * self = VFS_DEVICE(user_data);
char * path_name;
- self = VFS_DEVICE(user_data);
- g_return_val_if_fail(self != NULL, FALSE);
-
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;
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);
- label_buffer = build_header(header, VFS_DEVICE_LABEL_SIZE);
- if (strlen(label_buffer)+1 > VFS_DEVICE_LABEL_SIZE) {
+ Device *d_self = DEVICE(self);
+
+ g_assert(header != NULL);
+
+ label_buffer = device_build_amanda_header(d_self, header, NULL);
+ if (!label_buffer) {
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);
}
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);
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 {
- amfree(label_header);
+ if (!write_amanda_header(self, label_header)) {
+ /* write_amanda_header sets error status if necessary */
+ dumpfile_free(label_header);
return FALSE;
}
+ dumpfile_free(d_self->volume_header);
+ d_self->volume_header = label_header;
+ self->volume_bytes = VFS_DEVICE_LABEL_SIZE;
+ return TRUE;
+}
+
+/* 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 ReadLabelStatusFlags vfs_device_read_label(Device * dself) {
+static DeviceStatusFlags vfs_device_read_label(Device * dself) {
+ VfsDevice * self = VFS_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);
- amanda_header = vfs_device_seek_file(dself, 0);
+ if (!check_is_dir(self, self->dir_name)) {
+ /* error message set by check_is_dir */
+ return dself->status;
+ }
+
+ amfree(dself->volume_label);
+ amfree(dself->volume_time);
+ dumpfile_free(dself->volume_header);
+ dself->volume_header = NULL;
+
+ if (device_in_error(dself)) return dself->status;
+
+ amanda_header = dself->volume_header = vfs_device_seek_file(dself, 0);
+ release_file(self);
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) {
+ /* close the fd we just opened */
+ vfs_device_finish_file(dself);
+
+ if (amanda_header->type != F_TAPESTART &&
+ amanda_header->type != F_EMPTY) {
/* 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);
+ /* self->volume_header is already set */
+
+ if (amanda_header->type == F_TAPESTART) {
+ dself->volume_label = g_strdup(amanda_header->name);
+ dself->volume_time = g_strdup(amanda_header->datestamp);
+ 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;
- return FALSE;
+ if (check_at_leom(self, size))
+ pself->is_eom = TRUE;
+
+ if (check_at_peom(self, size)) {
+ pself->is_eom = 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;
+ self->checked_bytes_used += size;
+ pself->block ++;
+
+ return TRUE;
}
static int
VfsDevice * self;
int size;
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;
}
g_assert_not_reached();
}
-static gboolean vfs_device_start(Device * pself,
+static gboolean
+vfs_device_start(Device * dself,
DeviceAccessMode mode, char * label,
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);
-
+ VfsDevice * self = VFS_DEVICE(dself);
+
+ if (!check_is_dir(self, self->dir_name)) {
+ /* error message set by check_is_dir */
+ return FALSE;
+ }
+
+ dself->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;
}
+
+ dself->volume_label = newstralloc(dself->volume_label, label);
+ dself->volume_time = newstralloc(dself->volume_time, timestamp);
+
+ /* unset the VOLUME_UNLABELED flag, if it was set */
+ device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
+
demote_volume_lock(self);
+ dself->access_mode = mode;
+ } else {
+ if (dself->volume_label == NULL && device_read_label(dself) != DEVICE_STATUS_SUCCESS) {
+ /* device_read_label already set our error message */
+ return FALSE;
+ } else {
+ dself->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);
+
+ release_file(self);
+
+ pself->access_mode = ACCESS_NULL;
+ pself->in_file = FALSE;
+
+ if (device_in_error(self)) return FALSE;
+
+ return TRUE;
}
typedef struct {
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. */
return TRUE;
}
-static gint get_last_file_number(VfsDevice * self) {
+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);
}
-
+
return data.rval;
}
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. */
/* Returns the file number equal to or greater than the given requested
* file number. */
-static gint get_next_file_number(VfsDevice * self, guint request) {
+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;
}
-
+
/* Could be -1. */
return data.best_found;
}
fileno = 1 + get_last_file_number(self);
if (fileno <= 0)
return NULL;
-
+
if (open_lock(self, fileno, TRUE)) {
break;
} else {
return rval;
}
-static gboolean
-vfs_device_start_file (Device * pself, const dumpfile_t * ji) {
- VfsDevice * self;
+static gboolean
+vfs_device_start_file (Device * dself, dumpfile_t * ji) {
+ VfsDevice * self = VFS_DEVICE(dself);
- self = VFS_DEVICE(pself);
- g_return_val_if_fail (self != NULL, FALSE);
- g_return_val_if_fail (ji != NULL, FALSE);
+ dself->is_eom = FALSE;
- if (self->volume_limit > 0 &&
- self->volume_bytes + VFS_DEVICE_LABEL_SIZE > self->volume_limit) {
- /* No more room. */
- return 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 (check_at_leom(self, VFS_DEVICE_LABEL_SIZE))
+ dself->is_eom = TRUE;
+
+ if (check_at_peom(self, VFS_DEVICE_LABEL_SIZE)) {
+ dself->is_eom = TRUE;
+ device_set_error(dself,
+ stralloc(_("No space left on device")),
+ DEVICE_STATUS_DEVICE_ERROR);
+ return FALSE;
}
/* The basic idea here is thus:
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(dself,
+ 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(dself,
+ 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--;
+ self->checked_bytes_used += VFS_DEVICE_LABEL_SIZE;
+ dself->in_file = TRUE;
+ dself->block = 0;
+ /* make_new_file_name set pself->file for us */
- if (parent_class->start_file) {
- parent_class->start_file(pself, ji);
- }
return TRUE;
}
-static gboolean
-vfs_device_finish_file (Device * pself) {
- VfsDevice * self;
- self = VFS_DEVICE(pself);
- g_return_val_if_fail(self != NULL, FALSE);
+static gboolean
+vfs_device_finish_file(Device * dself) {
+ VfsDevice * self = VFS_DEVICE(dself);
+
+ 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();
+
+ dself->in_file = FALSE;
+
+ return TRUE;
}
/* This function is used for two purposes, rather than one. In
* addition to its documented behavior, we also use it to open the
* volume label for reading at startup. In that second case, we avoid
* FdDevice-related side effects. */
-static dumpfile_t *
-vfs_device_seek_file (Device * pself, guint requested_file) {
- VfsDevice * self;
+static dumpfile_t *
+vfs_device_seek_file (Device * dself, guint requested_file) {
+ VfsDevice *self = VFS_DEVICE(dself);
int file;
dumpfile_t * rval;
char header_buffer[VFS_DEVICE_LABEL_SIZE];
int header_buffer_size = sizeof(header_buffer);
IoResult result;
- self = VFS_DEVICE(pself);
- g_return_val_if_fail (self != NULL, NULL);
+ if (device_in_error(self)) return NULL;
+
+ dself->in_file = FALSE;
+ dself->is_eof = FALSE;
+ dself->block = 0;
- pself->in_file = FALSE;
-
release_file(self);
if (requested_file > 0) {
tmp_file_name = file_number_to_file_name(self, requested_file - 1);
if (tmp_file_name != NULL) {
free(tmp_file_name);
+ dself->file = requested_file; /* other attributes are already correct */
return make_tapeend_header();
} else {
+ device_set_error(dself,
+ stralloc(_("Attempt to read past tape-end file")),
+ DEVICE_STATUS_SUCCESS);
return NULL;
}
}
if (!open_lock(self, file, FALSE)) {
+ device_set_error(dself,
+ 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(dself,
+ vstrallocf(_("File %d not found"), file),
+ file == 0 ? DEVICE_STATUS_VOLUME_UNLABELED
+ : DEVICE_STATUS_VOLUME_ERROR);
release_file(self);
- return NULL;
+ rval = g_new(dumpfile_t, 1);
+ fh_init(rval);
+ return rval;
}
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(dself,
+ 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;
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(dself,
+ vstrallocf(_("Problem reading Amanda header: %s"), device_error(dself)),
+ 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(dself,
+ 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 */
+ dself->in_file = TRUE;
+ dself->file = file;
+
+ return rval;
}
-static gboolean
+static gboolean
vfs_device_seek_block (Device * pself, guint64 block) {
VfsDevice * self;
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;
}
}
-static gboolean
-vfs_device_recycle_file (Device * pself, guint filenum) {
- VfsDevice * self;
+static gboolean
+check_at_leom(VfsDevice *self, guint64 size)
+{
+ gboolean recheck = FALSE;
+ guint64 est_avail_now;
+ struct fs_usage fsusage;
+ guint64 block_size = DEVICE(self)->block_size;
+ guint64 eom_warning_buffer = EOM_EARLY_WARNING_ZONE_BLOCKS * block_size;
+
+ if (!self->leom || !self->monitor_free_space)
+ return FALSE;
+
+ /* handle VOLUME_LIMIT */
+ if (self->volume_limit &&
+ self->volume_bytes + size + eom_warning_buffer > self->volume_limit) {
+ return TRUE;
+ }
+
+ /* handle actual filesystem available space, using some heuristics to avoid polling this
+ * too frequently */
+ est_avail_now = 0;
+ if (self->checked_fs_free_bytes >= self->checked_bytes_used + size)
+ est_avail_now = self->checked_fs_free_bytes - self->checked_bytes_used - size;
+
+ /* is it time to check again? */
+ if (est_avail_now <= block_size * MONITOR_FREE_SPACE_CLOSELY_WITHIN_BLOCKS) {
+ recheck = TRUE;
+ } else if (self->checked_bytes_used > MONITOR_FREE_SPACE_EVERY_KB * 1024) {
+ recheck = TRUE;
+ } else if (self->checked_fs_free_time + MONITOR_FREE_SPACE_EVERY_SECONDS <= time(NULL)) {
+ recheck = TRUE;
+ }
+
+ if (!recheck)
+ return FALSE;
+
+ if (get_fs_usage(self->dir_name, NULL, &fsusage) < 0 || fsusage.fsu_bavail_top_bit_set) {
+ g_warning("Filesystem cannot provide free space: %s; setting MONITOR_FREE_SPACE false",
+ fsusage.fsu_bavail_top_bit_set? "no result" : strerror(errno));
+ self->monitor_free_space = FALSE;
+ return FALSE;
+ }
+
+ self->checked_fs_free_bytes = fsusage.fsu_bavail * fsusage.fsu_blocksize;
+ self->checked_bytes_used = 0;
+ self->checked_fs_free_time = time(NULL);
+
+ if (self->checked_fs_free_bytes - size <= eom_warning_buffer) {
+ g_debug("%s: at LEOM", DEVICE(self)->device_name);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+check_at_peom(VfsDevice *self, guint64 size)
+{
+ if (self->volume_limit > 0) {
+ guint64 newtotal = self->volume_bytes + size;
+ if (newtotal > self->volume_limit) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gboolean
+vfs_device_recycle_file (Device * dself, guint filenum) {
+ VfsDevice * self = VFS_DEVICE(dself);
struct stat file_status;
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.
*/
self->file_name = file_number_to_file_name(self, filenum);
-
- if (self->file_name == NULL)
+ if (self->file_name == NULL) {
+ device_set_error(dself,
+ 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(dself,
+ 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(dself,
+ 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(dself,
+ vstrallocf(_("Unlink of %s failed: %s"), self->file_name, strerror(errno)),
+ DEVICE_STATUS_VOLUME_ERROR);
release_file(self);
return FALSE;
}
return TRUE;
}
+static gboolean
+vfs_device_erase (Device * dself) {
+ VfsDevice *self = VFS_DEVICE(dself);
+
+ if (!open_lock(self, 0, true))
+ return false;
+
+ delete_vfs_files(self);
+
+ release_file(self);
+
+ return TRUE;
+}
+
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) {
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;
}
}
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) {
#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;
}
}
return RESULT_SUCCESS;
}
+
+/* TODO: add prop */