/amanda/tags/3_3_0beta1/device-src/vfs-device.c
C | 1505 lines | 1096 code | 259 blank | 150 comment | 180 complexity | f4a40b0e0bfaf7b63740cbbf78ccc2d7 MD5 | raw file
- /*
- * Copyright (c) 2007, 2008, 2009, 2010, 2011 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 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 "amanda.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. */
- #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"
- #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_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;
- 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_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_recycle_file (Device * pself, guint filenum);
- 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);
- 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(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 vfs_device_set_enforce_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_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);
- }
- GType
- vfs_device_get_type (void)
- {
- static GType type = 0;
- if G_UNLIKELY(type == 0) {
- static const GTypeInfo info = {
- sizeof (VfsDeviceClass),
- (GBaseInitFunc) vfs_device_base_init,
- (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 * dself = DEVICE(self);
- GValue response;
- self->dir_name = self->file_name = NULL;
- self->open_file_fd = -1;
- self->volume_bytes = 0;
- self->volume_limit = 0;
- self->leom = TRUE;
- self->enforce_volume_limit = 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 */
- bzero(&response, sizeof(response));
- g_value_init(&response, CONCURRENCY_PARADIGM_TYPE);
- g_value_set_enum(&response, CONCURRENCY_PARADIGM_RANDOM_ACCESS);
- device_set_simple_property(dself, PROPERTY_CONCURRENCY,
- &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
- g_value_unset(&response);
- g_value_init(&response, STREAMING_REQUIREMENT_TYPE);
- g_value_set_enum(&response, STREAMING_REQUIREMENT_NONE);
- device_set_simple_property(dself, PROPERTY_STREAMING,
- &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
- g_value_unset(&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);
- 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);
- g_value_init(&response, G_TYPE_BOOLEAN);
- g_value_set_boolean(&response, TRUE);
- device_set_simple_property(dself, PROPERTY_FULL_DELETION,
- &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
- g_value_unset(&response);
- g_value_init(&response, G_TYPE_BOOLEAN);
- g_value_set_boolean(&response, TRUE);
- device_set_simple_property(dself, PROPERTY_LEOM,
- &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
- g_value_unset(&response);
- g_value_init(&response, G_TYPE_BOOLEAN);
- g_value_set_boolean(&response, TRUE);
- device_set_simple_property(dself, PROPERTY_ENFORCE_MAX_VOLUME_USAGE,
- &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
- g_value_unset(&response);
- 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);
- g_value_init(&response, MEDIA_ACCESS_MODE_TYPE);
- g_value_set_enum(&response, MEDIA_ACCESS_MODE_READ_WRITE);
- device_set_simple_property(dself, PROPERTY_MEDIUM_ACCESS_TYPE,
- &response, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
- g_value_unset(&response);
- }
- static void
- vfs_device_class_init (VfsDeviceClass * c)
- {
- GObjectClass *g_object_class = (GObjectClass*) c;
- DeviceClass *device_class = DEVICE_CLASS(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->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;
- }
- 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_ENFORCE_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_enforce_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
- vfs_device_set_enforce_max_volume_usage_fn(Device *p_self,
- DevicePropertyBase *base, GValue *val,
- PropertySurety surety, PropertySource source)
- {
- VfsDevice *self = VFS_DEVICE(p_self);
- self->enforce_volume_limit = g_value_get_boolean(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. */
- if (self->open_file_fd != -1)
- robust_close(self->open_file_fd);
- amfree(self->file_name);
- 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);
- release_file(self);
- }
- 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));
- device_open_device(rval, device_name, device_type, device_node);
- return rval;
- }
- 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(self, name);
- }
- #endif /* EINTR */
- 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)) {
- device_set_error(dself,
- vstrallocf(_("VFS Device path %s is not a directory"), name),
- DEVICE_STATUS_DEVICE_ERROR);
- 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_warning(_("Cannot stat file %s (%s), ignoring it"), result_tmp, strerror(errno));
- } else if (!S_ISREG(file_status.st_mode)) {
- g_warning(_("%s is not a regular file, ignoring it"), 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;
- data.self = self;
- data.count = 0;
- data.result = NULL;
- regex = g_strdup_printf("^0*%u\\.", device_file);
- search_vfs_directory(self, 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_warning("Found multiple names for file number %d, choosing file %s",
- 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;
- }
- /* For now, does it the bad way. */
- static void promote_volume_lock(VfsDevice * self G_GNUC_UNUSED) {
- }
- static void demote_volume_lock(VfsDevice * self G_GNUC_UNUSED) {
- }
- /* A SearchDirectoryFunctor */
- static gboolean update_volume_size_functor(const char * filename,
- gpointer user_data) {
- char * full_filename;
- struct stat stat_buf;
- 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_warning(_("Couldn't stat file %s: %s"), full_filename, strerror(errno));
- amfree(full_filename);
- return TRUE;
- }
- amfree(full_filename);
- self->volume_bytes += stat_buf.st_size;
- return TRUE;
- }
- static void update_volume_size(VfsDevice * self) {
- self->volume_bytes = 0;
- search_vfs_directory(self, "^[0-9]+\\.",
- update_volume_size_functor, self);
- }
- static void
- vfs_device_open_device (Device * pself, char * device_name, char * device_type, char * device_node) {
- VfsDevice * self;
- self = VFS_DEVICE(pself);
- 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_node, "/data/", NULL);
- if (parent_class->open_device) {
- parent_class->open_device(pself, device_name, device_type, device_node);
- }
- }
- /* A SearchDirectoryFunctor */
- 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);
- d_self = DEVICE(self);
- /* Skip the volume lock. */
- if (strcmp(filename, VOLUME_LOCKFILE_NAME) == 0)
- return TRUE;
- path_name = vstralloc(self->dir_name, "/", filename, NULL);
- if (unlink(path_name) != 0) {
- 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. */
- void delete_vfs_files(VfsDevice * self) {
- g_assert(self != NULL);
- /* This function assumes that the volume is locked! */
- 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 = VFS_DEVICE(user_data);
- char * path_name;
- if (strcmp(filename, VOLUME_LOCKFILE_NAME) == 0)
- return TRUE;
- path_name = vstralloc(self->dir_name, "/", filename, NULL);
- g_warning(_("Found spurious storage file %s"), path_name);
- amfree(path_name);
- return TRUE;
- }
- /* This function is used to write volume and dump headers. */
- static gboolean write_amanda_header(VfsDevice * self,
- const dumpfile_t * header) {
- char * label_buffer;
- IoResult result;
- 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);
- 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);
- }
- /* clear_and_label will erase the contents of the directory, and write
- * this label in its place. This function assumes we already have a volume
- * label write lock in place (e.g., promote_lock() has been called.) */
- 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. */
- delete_vfs_files(self);
- /* Print warnings about any remaining files. */
- 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);
- self->open_file_fd = robust_open(self->file_name,
- O_CREAT | O_EXCL | O_WRONLY,
- VFS_DEVICE_CREAT_MODE);
- if (self->open_file_fd < 0) {
- 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)) {
- /* 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 DeviceStatusFlags vfs_device_read_label(Device * dself) {
- VfsDevice * self = VFS_DEVICE(dself);
- dumpfile_t * amanda_header;
- g_assert(self != NULL);
- 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. */
- 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;
- }
- /* 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. */
- device_set_error(dself,
- stralloc(_("Got a bad volume label")),
- DEVICE_STATUS_VOLUME_ERROR);
- amfree(amanda_header);
- return dself->status;
- }
- /* 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);
- return dself->status;
- }
- static gboolean vfs_device_write_block(Device * pself, guint size, gpointer data) {
- VfsDevice * self = VFS_DEVICE(pself);
- IoResult result;
- if (device_in_error(self)) return FALSE;
- g_assert(self->open_file_fd >= 0);
- 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) {
- /* 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
- vfs_device_read_block(Device * pself, gpointer data, int * size_req) {
- VfsDevice * self;
- int size;
- IoResult result;
- self = VFS_DEVICE(pself);
- if (device_in_error(self)) return -1;
- if (data == NULL || (gsize)*size_req < pself->block_size) {
- /* Just a size query. */
- g_assert(pself->block_size < INT_MAX);
- *size_req = (int)pself->block_size;
- return 0;
- }
- 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 * dself,
- DeviceAccessMode mode, char * label,
- char * timestamp) {
- 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);
- 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 {
- VfsDevice * self;
- int rval;
- } glfn_data;
- /* A SearchDirectoryFunctor. */
- static gboolean get_last_file_number_functor(const char * filename,
- gpointer datap) {
- guint64 file;
- glfn_data * data = (glfn_data*)datap;
- file = g_ascii_strtoull(filename, NULL, 10); /* Guaranteed to work. */
- if (file > G_MAXINT) {
- g_warning(_("Super-large device file %s found, ignoring"), filename);
- return TRUE;
- }
- /* This condition is needlessly complex due to sign issues. */
- if (data->rval < 0 || ((guint)data->rval) < file) {
- data->rval = file;
- }
- return TRUE;
- }
- 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_vfs_directory(self, "^[0-9]+\\.",
- get_last_file_number_functor, &data);
- if (count <= 0) {
- /* Somebody deleted something important while we weren't looking. */
- 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;
- }
- typedef struct {
- VfsDevice * self;
- guint request;
- int best_found;
- } gnfn_data;
- /* A SearchDirectoryFunctor. */
- static gboolean get_next_file_number_functor(const char * filename,
- gpointer datap) {
- guint file;
- gnfn_data * data = (gnfn_data*)datap;
- file = g_ascii_strtoull(filename, NULL, 10); /* Guaranteed to work. */
- if (file > G_MAXINT) {
- g_warning(_("Super-large device file %s found, ignoring"), filename);
- return TRUE;
- }
- /* This condition is needlessly complex due to sign issues. */
- if (file >= data->request &&
- (data->best_found < 0 || file < (guint)data->best_found)) {
- data->best_found = file;
- }
- return TRUE;
- }
- /* Returns the file number equal to or greater than the given requested
- * file number. */
- 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_vfs_directory(self, "^[0-9]+\\.",
- get_next_file_number_functor, &data);
- if (count <= 0) {
- /* Somebody deleted something important while we weren't looking. */
- 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;
- }
- /* Finds the file number, acquires a lock, and returns the new file name. */
- static
- char * make_new_file_name(VfsDevice * self, const dumpfile_t * ji) {
- char * rval;
- char *base, *sanitary_base;
- int fileno;
- for (;;) {
- fileno = 1 + get_last_file_number(self);
- if (fileno <= 0)
- return NULL;
- if (open_lock(self, fileno, TRUE)) {
- break;
- } else {
- continue;
- }
- }
- /* record that we're at this filenum now */
- DEVICE(self)->file = fileno;
- base = g_strdup_printf("%05d.%s.%s.%d", fileno, ji->name, ji->disk,
- ji->dumplevel);
- sanitary_base = sanitise_filename(base);
- amfree(base);
- rval = g_strdup_printf("%s/%s", self->dir_name, sanitary_base);
- amfree(sanitary_base);
- return rval;
- }
- static gboolean
- vfs_device_start_file (Device * dself, dumpfile_t * ji) {
- VfsDevice * self = VFS_DEVICE(dself);
- dself->is_eom = 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:
- 1) Try to get a lock on the next filenumber.
- 2) If that fails, update our idea of "next filenumber" and try again.
- 3) Then open the file itself.
- 4) Write the label.
- 5) Chain up. */
- self->file_name = make_new_file_name(self, ji);
- 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) {
- 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;
- self->checked_bytes_used += VFS_DEVICE_LABEL_SIZE;
- dself->in_file = TRUE;
- dself->block = 0;
- /* make_new_file_name set pself->file for us */
- return TRUE;
- }
- static gboolean
- vfs_device_finish_file(Device * dself) {
- VfsDevice * self = VFS_DEVICE(dself);
- if (device_in_error(self)) return FALSE;
- release_file(self);
- 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 * 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;
- if (device_in_error(self)) return NULL;
- dself->in_file = FALSE;
- dself->is_eof = FALSE;
- dself->block = 0;
- release_file(self);
- if (requested_file > 0) {
- file = get_next_file_number(self, requested_file);
- } else {
- file = requested_file;
- }
- if (file < 0) {
- /* Did they request one past the end? */
- char * tmp_file_name;
- 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);
- 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) {
- 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) {
- device_set_error(dself,
- vstrallocf(_("Problem reading Amanda header: %s"), device_error(dself)),
- DEVICE_STATUS_VOLUME_ERROR);
- release_file(self);
- return NULL;
- }
- rval = g_new(dumpfile_t, 1);
- parse_file_header(header_buffer, rval, header_buffer_size);
- switch (rval->type) {
- case F_DUMPFILE:
- case F_CONT_DUMPFILE:
- case F_SPLIT_DUMPFILE:
- 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;
- }
- /* update our state */
- dself->in_file = TRUE;
- dself->file = file;
- return rval;
- }
- static gboolean
- vfs_device_seek_block (Device * pself, guint64 block) {
- VfsDevice * self;
- off_t result;
- self = VFS_DEVICE(pself);
- 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) * pself->block_size + VFS_DEVICE_LABEL_SIZE,
- SEEK_SET);
- pself->block = block;
- if (result == (off_t)(-1)) {
- device_set_error(pself,
- vstrallocf(_("Error seeking within file: %s"), strerror(errno)),
- DEVICE_STATUS_DEVICE_ERROR);
- return FALSE;
- }
- return TRUE;
- }
- static gboolean try_unlink(const char * file) {
- if (unlink(file) < 0) {
- return FALSE;
- } else {
- return TRUE;
- }
- }
- 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->enforce_volume_limit && 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->enforce_volume_limit && (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;
- if (device_in_error(self)) return FALSE;
- /* Game Plan:
- * 1) Get a write lock on the file in question.
- * 2) Unlink the file in question.
- * 3) Unlink the lock.
- * 4) Release the lock.
- * FIXME: Is it OK to unlink the lockfile?
- */
- self->file_name = file_number_to_file_name(self, filenum);
- 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, FALSE)) {
- device_set_error(dself,
- stralloc(_("could not acquire lock")),
- DEVICE_STATUS_DEVICE_ERROR);
- return FALSE;
- }
- if (0 != stat(self->file_name, &file_status)) {
- 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)) {
- device_set_error(dself,
- vstrallocf(_("Unlink of %s failed: %s"), self->file_name, strerror(errno)),
- DEVICE_STATUS_VOLUME_ERROR);
- release_file(self);
- return FALSE;
- }
- self->volume_bytes -= file_size;
- release_file(self);
- 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) {
- int result;
- result = read(fd, buf + got, want - got);
- if (result > 0) {
- got += result;
- } else if (result == 0) {
- /* end of file */
- if (got == 0) {
- return RESULT_NO_DATA;
- } else {
- *count = got;
- return RESULT_SUCCESS;
- }
- } else if (0
- #ifdef EAGAIN
- || errno == EAGAIN
- #endif
- #ifdef EWOULDBLOCK
- || errno == EWOULDBLOCK
- #endif
- #ifdef EINTR
- || errno == EINTR
- #endif
- ) {
- /* Try again. */
- continue;
- } else {
- /* Error occured. */
- device_set_error(d_self,
- vstrallocf(_("Error reading fd %d: %s"), fd, strerror(errno)),
- DEVICE_STATUS_VOLUME_ERROR);
- *count = got;
- return RESULT_ERROR;
- }
- }
- *count = got;
- return RESULT_SUCCESS;
- }
- 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) {
- int result;
- result = write(fd, buf + rval, count - rval);
- if (result > 0) {
- rval += result;
- continue;
- } else if (0
- #ifdef EAGAIN
- || errno == EAGAIN
- #endif
- #ifdef EWOULDBLOCK
- || errno == EWOULDBLOCK
- #endif
- #ifdef EINTR
- || errno == EINTR
- #endif
- ) {
- /* Try again. */
- continue;
- } else if (0
- #ifdef EFBIG
- || errno == EFBIG
- #endif
- #ifdef ENOSPC
- || errno == ENOSPC
- #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. */
- 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 */