📄 vfs-device.c
字号:
/* * 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 * 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 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com */#include <string.h> /* memset() */#include "vfs-device.h"#include "fsusage.h"#include "amanda.h"#include "util.h"#include <regex.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. */#define VFS_DEVICE_FILE_REGEX "^[0-9]+[\\.-]"/* The name of the volume lockfile. Should be the same as that generated by lockfile_name(0). */#define VOLUME_LOCKFILE_NAME "00000-lock"/* Possible (abstracted) results from a system I/O operation. */typedef enum { RESULT_SUCCESS, RESULT_ERROR, /* Undefined error. */ RESULT_NO_DATA, /* End of File, while reading */ RESULT_NO_SPACE, /* Out of space. Sometimes we don't know if it was this or I/O error, but this is the preferred explanation. */ RESULT_MAX} IoResult;/* here are local prototypes */static void vfs_device_init (VfsDevice * o);static void vfs_device_class_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 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 int vfs_device_read_block(Device * self, gpointer data, int * size_req);static IoResult vfs_device_robust_write(VfsDevice * self, char *buf, int count);static IoResult vfs_device_robust_read(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 file_number_to_file_name_functor(const char * filename, gpointer datap);//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 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);/* pointer to the classes of our parents */static DeviceClass *parent_class = NULL;void vfs_device_register(void) { static const char * device_prefix_list[] = { "file", NULL }; register_device(vfs_device_factory, device_prefix_list);}GTypevfs_device_get_type (void){ static GType type = 0; if G_UNLIKELY(type == 0) { static const GTypeInfo info = { sizeof (VfsDeviceClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) vfs_device_class_init, (GClassFinalizeFunc) NULL, NULL /* class_data */, sizeof (VfsDevice), 0 /* n_preallocs */, (GInstanceInitFunc) vfs_device_init, NULL }; type = g_type_register_static (TYPE_DEVICE, "VfsDevice", &info, (GTypeFlags)0); } return type;}static void vfs_device_init (VfsDevice * self) { Device * o; DeviceProperty prop; 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); 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); 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_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); 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); 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); 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){ GObjectClass *g_object_class = (GObjectClass*) c; DeviceClass *device_class = (DeviceClass *)c; parent_class = g_type_class_ref(TYPE_DEVICE); device_class->open_device = vfs_device_open_device; device_class->start = vfs_device_start; device_class->start_file = vfs_device_start_file; device_class->read_label = vfs_device_read_label; device_class->write_block = vfs_device_write_block; device_class->read_block = vfs_device_read_block; 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; 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) { /* Doesn't hurt. */ 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;}static void vfs_device_finalize(GObject * obj_self) { VfsDevice *self = VFS_DEVICE (obj_self); Device * d_self = (Device*)self; if (d_self->access_mode != ACCESS_NULL) { device_finish(d_self); } if(G_OBJECT_CLASS(parent_class)->finalize) (* G_OBJECT_CLASS(parent_class)->finalize)(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) { 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; }}static gboolean check_is_dir(const char * name, gboolean printmsg) { struct stat dir_status; if (stat(name, &dir_status) < 0) {#ifdef EINTR if (errno == EINTR) { return check_is_dir(name, printmsg); }#endif /* EINTR */ if (printmsg) { g_fprintf(stderr, "Error checking directory %s: %s\n", name, strerror(errno)); } 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); } return FALSE; } else { return TRUE; }}typedef struct { VfsDevice * self; int count; char * result;} fnfn_data;/* A SearchDirectoryFunctor. */static gboolean file_number_to_file_name_functor(const char * filename, gpointer datap) { char * result_tmp; struct stat file_status; fnfn_data *data = (fnfn_data*)datap; 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)); } else if (!S_ISREG(file_status.st_mode)) { g_fprintf(stderr, "%s is not a regular file, ignoring it.\n", result_tmp); } else { data->count ++; if (data->result == NULL) { data->result = result_tmp; result_tmp = NULL; } } amfree(result_tmp); return TRUE;}/* This function finds the filename for a given file number. We search * for a filesystem file matching the regex /^0*$device_file\./; if * there is more than one such file we make a warning and take an * arbitrary one. */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); amfree(regex); if (data.count == 0) { 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", device_file, data.result); return data.result; } else { g_assert(data.result != NULL); return data.result; } g_assert_not_reached();}/* This function returns the dynamically-allocated lockfile name for a given file number. *//*static char * lockfile_name(VfsDevice * self, guint number) { return g_strdup_printf("%s/%05d-lock", self->dir_name, number);}*//* Does what you expect. If the lock already exists, it is released * and regained, in case the mode is changing. * The file field has several options: * - file > 0: Open a lock on a real volume file. * - file = 0: Open the volume lock as a volume file (for setup). * - file < 0: Open the volume lock as a volume lock (persistantly). */static gboolean open_lock(G_GNUC_UNUSED VfsDevice * self, G_GNUC_UNUSED int file, G_GNUC_UNUSED gboolean exclusive) { /* 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -