Merge branch 'master' into squeeze
[debian/amanda] / device-src / dvdrw-device.c
diff --git a/device-src/dvdrw-device.c b/device-src/dvdrw-device.c
new file mode 100644 (file)
index 0000000..5bc0fc1
--- /dev/null
@@ -0,0 +1,737 @@
+/*
+ * Amanda, The Advanced Maryland Automatic Network Disk Archiver
+ * Copyright (c) 2009 University of Maryland at College Park
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  U.M. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Sam Couter <sam@couter.id.au>
+ */
+
+#include "amanda.h"
+#include "vfs-device.h"
+
+/*
+ * Type checking and casting macros
+ */
+#define TYPE_DVDRW_DEVICE      (dvdrw_device_get_type())
+#define DVDRW_DEVICE(obj)      G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DVDRW_DEVICE, DvdRwDevice)
+#define DVDRW_DEVICE_CONST(obj)        G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DVDRW_DEVICE, DvdRwDevice const)
+#define DVDRW_DEVICE_CLASS(klass)      G_TYPE_CHECK_CLASS_CAST((klass), TYPE_DVDRW_DEVICE, DvdRwDeviceClass)
+#define IS_DVDRW_DEVICE(obj)   G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_DVDRW_DEVICE)
+#define DVDRW_DEVICE_GET_CLASS(obj)    G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_DVDRW_DEVICE, DvdRwDeviceClass)
+
+/* Forward declaration */
+static GType dvdrw_device_get_type(void);
+
+/*
+ * Main object structure
+ */
+typedef struct _DvdRwDevice DvdRwDevice;
+struct _DvdRwDevice {
+    VfsDevice __parent__;
+
+    gchar *dvdrw_device;
+    gchar *cache_dir;
+    gchar *cache_data;
+    gchar *mount_point;
+    gchar *mount_data;
+    gboolean mounted;
+    gboolean keep_cache;
+    gboolean unlabelled_when_unmountable;
+    gchar *growisofs_command;
+    gchar *mount_command;
+    gchar *umount_command;
+};
+
+/*
+ * Class definition
+ */
+typedef struct _DvdRwDeviceClass DvdRwDeviceClass;
+struct _DvdRwDeviceClass {
+    VfsDeviceClass __parent__;
+};
+
+/* Where the DVD-RW can be mounted */
+static DevicePropertyBase device_property_dvdrw_mount_point;
+#define PROPERTY_DVDRW_MOUNT_POINT (device_property_dvdrw_mount_point.ID)
+
+/* Should the on-disk version be kept after the optical disc has been written? */
+static DevicePropertyBase device_property_dvdrw_keep_cache;
+#define PROPERTY_DVDRW_KEEP_CACHE (device_property_dvdrw_keep_cache.ID)
+
+/* Should a mount failure (eg, a freshly formatted disc) when reading a label be treated like an unlabelled volume? */
+static DevicePropertyBase device_property_dvdrw_unlabelled_when_unmountable;
+#define PROPERTY_DVDRW_UNLABELLED_WHEN_UNMOUNTABLE (device_property_dvdrw_unlabelled_when_unmountable.ID)
+
+/* Where to find the growisofs command */
+static DevicePropertyBase device_property_dvdrw_growisofs_command;
+#define PROPERTY_DVDRW_GROWISOFS_COMMAND (device_property_dvdrw_growisofs_command.ID)
+
+/* Where to find the filesystem mount command */
+static DevicePropertyBase device_property_dvdrw_mount_command;
+#define PROPERTY_DVDRW_MOUNT_COMMAND (device_property_dvdrw_mount_command.ID)
+
+/* Where to find the filesystem unmount command */
+static DevicePropertyBase device_property_dvdrw_umount_command;
+#define PROPERTY_DVDRW_UMOUNT_COMMAND (device_property_dvdrw_umount_command.ID)
+
+/* GObject housekeeping */
+void
+dvdrw_device_register(void);
+
+static Device*
+dvdrw_device_factory(char *device_name, char *device_type, char *device_node);
+
+static void
+dvdrw_device_class_init (DvdRwDeviceClass *c);
+
+static void
+dvdrw_device_init (DvdRwDevice *self);
+
+/* Properties */
+static gboolean
+dvdrw_device_set_mount_point_fn(Device *self,
+    DevicePropertyBase *base, GValue *val,
+    PropertySurety surety, PropertySource source);
+
+static gboolean
+dvdrw_device_set_keep_cache_fn(Device *self,
+    DevicePropertyBase *base, GValue *val,
+    PropertySurety surety, PropertySource source);
+
+static gboolean
+dvdrw_device_set_unlabelled_when_unmountable_fn(Device *self,
+    DevicePropertyBase *base, GValue *val,
+    PropertySurety surety, PropertySource source);
+
+static gboolean
+dvdrw_device_set_growisofs_command_fn(Device *self,
+    DevicePropertyBase *base, GValue *val,
+    PropertySurety surety, PropertySource source);
+
+static gboolean
+dvdrw_device_set_mount_command_fn(Device *self,
+    DevicePropertyBase *base, GValue *val,
+    PropertySurety surety, PropertySource source);
+
+static gboolean
+dvdrw_device_set_umount_command_fn(Device *self,
+    DevicePropertyBase *base, GValue *val,
+    PropertySurety surety, PropertySource source);
+
+/* Methods */
+static void
+dvdrw_device_open_device(Device *dself, char *device_name, char *device_type, char *device_node);
+
+static DeviceStatusFlags
+dvdrw_device_read_label(Device *dself);
+
+static gboolean
+dvdrw_device_start(Device *dself, DeviceAccessMode mode, char *label, char *timestamp);
+
+static gboolean
+dvdrw_device_finish(Device *dself);
+
+static void
+dvdrw_device_finalize(GObject *gself);
+
+/* Helper functions */
+static gboolean
+check_access_mode(DvdRwDevice *self, DeviceAccessMode mode);
+
+static gboolean
+check_readable(DvdRwDevice *self);
+
+static DeviceStatusFlags
+mount_disc(DvdRwDevice *self, gboolean report_error);
+
+static void
+unmount_disc(DvdRwDevice *self);
+
+static gboolean
+burn_disc(DvdRwDevice *self);
+
+static DeviceStatusFlags
+execute_command(DvdRwDevice *self, gchar **argv, gint *status);
+
+static GType
+dvdrw_device_get_type (void)
+{
+    static GType type = 0;
+
+    if G_UNLIKELY(type == 0) {
+        static const GTypeInfo info = {
+            sizeof (VfsDeviceClass),
+            (GBaseInitFunc) NULL,
+            (GBaseFinalizeFunc) NULL,
+            (GClassInitFunc) dvdrw_device_class_init,
+            (GClassFinalizeFunc) NULL,
+            NULL /* class_data */,
+            sizeof (VfsDevice),
+            0 /* n_preallocs */,
+            (GInstanceInitFunc) dvdrw_device_init,
+            NULL
+        };
+
+        type = g_type_register_static (TYPE_VFS_DEVICE, "DvdRwDevice",
+                                       &info, (GTypeFlags)0);
+    }
+
+    return type;
+}
+
+void
+dvdrw_device_register(void)
+{
+    const char *device_prefix_list[] = { "dvdrw", NULL };
+
+    device_property_fill_and_register(&device_property_dvdrw_mount_point,
+       G_TYPE_STRING, "dvdrw_mount_point",
+       "Directory to mount DVD-RW for reading");
+
+    device_property_fill_and_register(&device_property_dvdrw_keep_cache,
+       G_TYPE_BOOLEAN, "dvdrw_keep_cache",
+       "Keep on-disk cache after DVD-RW has been written");
+
+    device_property_fill_and_register(&device_property_dvdrw_unlabelled_when_unmountable,
+       G_TYPE_BOOLEAN, "dvdrw_unlabelled_when_unmountable",
+       "Treat unmountable volumes as unlabelled when reading label");
+
+    device_property_fill_and_register(&device_property_dvdrw_growisofs_command,
+       G_TYPE_BOOLEAN, "dvdrw_growisofs_command",
+       "The location of the growisofs command used to write the DVD-RW");
+
+    device_property_fill_and_register(&device_property_dvdrw_mount_command,
+       G_TYPE_BOOLEAN, "dvdrw_mount_command",
+       "The location of the mount command used to mount the DVD-RW filesystem for reading");
+
+    device_property_fill_and_register(&device_property_dvdrw_umount_command,
+       G_TYPE_BOOLEAN, "dvdrw_umount_command",
+       "The location of the umount command used to unmount the DVD-RW filesystem after reading");
+
+    register_device(dvdrw_device_factory, device_prefix_list);
+}
+
+static Device *
+dvdrw_device_factory(char *device_name, char *device_type, char *device_node)
+{
+    Device *device;
+
+    g_assert(0 == strncmp(device_type, "dvdrw", strlen("dvdrw")));
+
+    device = DEVICE(g_object_new(TYPE_DVDRW_DEVICE, NULL));
+    device_open_device(device, device_name, device_type, device_node);
+
+    return device;
+}
+
+static void
+dvdrw_device_class_init (DvdRwDeviceClass *c)
+{
+    DeviceClass *device_class = DEVICE_CLASS(c);
+    GObjectClass *g_object_class = G_OBJECT_CLASS(c);
+
+    device_class->open_device = dvdrw_device_open_device;
+    device_class->read_label = dvdrw_device_read_label;
+    device_class->start = dvdrw_device_start;
+    device_class->finish = dvdrw_device_finish;
+
+    g_object_class->finalize = dvdrw_device_finalize;
+
+    device_class_register_property(device_class, PROPERTY_DVDRW_MOUNT_POINT,
+       PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
+       device_simple_property_get_fn,
+       dvdrw_device_set_mount_point_fn);
+
+    device_class_register_property(device_class, PROPERTY_DVDRW_KEEP_CACHE,
+       PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
+       device_simple_property_get_fn,
+       dvdrw_device_set_keep_cache_fn);
+
+    device_class_register_property(device_class, PROPERTY_DVDRW_UNLABELLED_WHEN_UNMOUNTABLE,
+       PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
+       device_simple_property_get_fn,
+       dvdrw_device_set_unlabelled_when_unmountable_fn);
+
+    device_class_register_property(device_class, PROPERTY_DVDRW_GROWISOFS_COMMAND,
+       PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
+       device_simple_property_get_fn,
+       dvdrw_device_set_growisofs_command_fn);
+
+    device_class_register_property(device_class, PROPERTY_DVDRW_MOUNT_COMMAND,
+       PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
+       device_simple_property_get_fn,
+       dvdrw_device_set_mount_command_fn);
+
+    device_class_register_property(device_class, PROPERTY_DVDRW_UMOUNT_COMMAND,
+       PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
+       device_simple_property_get_fn,
+       dvdrw_device_set_umount_command_fn);
+}
+
+static void
+dvdrw_device_init (DvdRwDevice *self)
+{
+    Device *dself = DEVICE(self);
+    GValue val;
+
+    self->dvdrw_device = NULL;
+    self->cache_dir = NULL;
+    self->cache_data = NULL;
+    self->mount_point = NULL;
+    self->mount_data = NULL;
+    self->mounted = FALSE;
+    self->keep_cache = FALSE;
+    self->growisofs_command = NULL;
+    self->mount_command = NULL;
+    self->umount_command = NULL;
+
+    bzero(&val, sizeof(val));
+
+    g_value_init(&val, G_TYPE_BOOLEAN);
+    g_value_set_boolean(&val, FALSE);
+    device_set_simple_property(dself, PROPERTY_APPENDABLE,
+       &val, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
+    g_value_unset(&val);
+
+    g_value_init(&val, G_TYPE_BOOLEAN);
+    g_value_set_boolean(&val, FALSE);
+    device_set_simple_property(dself, PROPERTY_PARTIAL_DELETION,
+       &val, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
+    g_value_unset(&val);
+
+    g_value_init(&val, G_TYPE_BOOLEAN);
+    g_value_set_boolean(&val, FALSE);
+    device_set_simple_property(dself, PROPERTY_FULL_DELETION,
+       &val, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
+    g_value_unset(&val);
+
+    g_value_init(&val, G_TYPE_BOOLEAN);
+    g_value_set_boolean(&val, TRUE);
+    device_set_simple_property(dself, PROPERTY_LEOM,
+       &val, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
+    g_value_unset(&val);
+}
+
+static gboolean
+dvdrw_device_set_mount_point_fn(Device *dself, DevicePropertyBase *base,
+    GValue *val, PropertySurety surety, PropertySource source)
+{
+    DvdRwDevice *self = DVDRW_DEVICE(dself);
+
+    amfree(self->mount_point);
+    amfree(self->mount_data);
+
+    self->mount_point = g_value_dup_string(val);
+    self->mount_data = g_strconcat(self->mount_point, "/data/", NULL);
+
+    device_clear_volume_details(dself);
+
+    return device_simple_property_set_fn(dself, base, val, surety, source);
+}
+
+static gboolean
+dvdrw_device_set_keep_cache_fn(Device *dself, DevicePropertyBase *base,
+    GValue *val, PropertySurety surety, PropertySource source)
+{
+    DvdRwDevice *self = DVDRW_DEVICE(dself);
+
+    self->keep_cache = g_value_get_boolean(val);
+
+    return device_simple_property_set_fn(dself, base, val, surety, source);
+}
+
+static gboolean
+dvdrw_device_set_unlabelled_when_unmountable_fn(Device *dself, DevicePropertyBase *base,
+    GValue *val, PropertySurety surety, PropertySource source)
+{
+    DvdRwDevice *self = DVDRW_DEVICE(dself);
+
+    self->unlabelled_when_unmountable = g_value_get_boolean(val);
+
+    return device_simple_property_set_fn(dself, base, val, surety, source);
+}
+
+static gboolean
+dvdrw_device_set_growisofs_command_fn(Device *dself, DevicePropertyBase *base,
+    GValue *val, PropertySurety surety, PropertySource source)
+{
+    DvdRwDevice *self = DVDRW_DEVICE(dself);
+
+    self->growisofs_command = g_value_dup_string(val);
+
+    return device_simple_property_set_fn(dself, base, val, surety, source);
+}
+
+static gboolean
+dvdrw_device_set_mount_command_fn(Device *dself, DevicePropertyBase *base,
+    GValue *val, PropertySurety surety, PropertySource source)
+{
+    DvdRwDevice *self = DVDRW_DEVICE(dself);
+
+    self->mount_command = g_value_dup_string(val);
+
+    return device_simple_property_set_fn(dself, base, val, surety, source);
+}
+
+static gboolean
+dvdrw_device_set_umount_command_fn(Device *dself, DevicePropertyBase *base,
+    GValue *val, PropertySurety surety, PropertySource source)
+{
+    DvdRwDevice *self = DVDRW_DEVICE(dself);
+
+    self->umount_command = g_value_dup_string(val);
+
+    return device_simple_property_set_fn(dself, base, val, surety, source);
+}
+
+static void
+dvdrw_device_open_device(Device *dself, char *device_name, char *device_type, char *device_node)
+{
+    DvdRwDevice *self = DVDRW_DEVICE(dself);
+    DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
+    GValue val;
+    char *colon;
+
+    g_debug("Opening device: %s", device_node);
+
+    bzero(&val, sizeof(val));
+
+    colon = index(device_node, ':');
+    if (!colon) {
+       device_set_error(dself,
+           stralloc(_("DVDRW device requires cache directory and DVD-RW device separated by a colon (:) in tapedev")),
+           DEVICE_STATUS_DEVICE_ERROR);
+       return;
+    }
+
+    self->cache_dir = g_strndup(device_node, colon - device_node);
+    self->cache_data = g_strconcat(self->cache_dir, "/data/", NULL);
+    self->dvdrw_device = g_strdup(colon + 1);
+
+    parent_class->open_device(dself, device_name, device_type, device_node);
+}
+
+static DeviceStatusFlags
+dvdrw_device_read_label(Device *dself)
+{
+    DvdRwDevice *self = DVDRW_DEVICE(dself);
+    VfsDevice *vself = VFS_DEVICE(dself);
+    gboolean mounted = FALSE;
+    DeviceStatusFlags status;
+    struct stat dir_status;
+    DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
+
+    g_debug("Reading label from media at %s", self->mount_point);
+
+    if (device_in_error(dself)) return DEVICE_STATUS_DEVICE_ERROR;
+    if (!check_readable(self)) return DEVICE_STATUS_DEVICE_ERROR;
+
+    if (!self->mounted) {
+       status = mount_disc(self, !self->unlabelled_when_unmountable);
+       if (status != DEVICE_STATUS_SUCCESS) {
+           /* Not mountable. May be freshly formatted or corrupted, drive may be empty. */
+           return (self->unlabelled_when_unmountable)
+               ? DEVICE_STATUS_VOLUME_UNLABELED
+               : status;
+       }
+       mounted = TRUE;
+    }
+
+    if ((stat(self->mount_data, &dir_status) < 0) && (errno == ENOENT)) {
+       /* No data directory, consider the DVD unlabelled */
+       g_debug("Media contains no data directory and therefore no label");
+       unmount_disc(self);
+
+       return DEVICE_STATUS_VOLUME_UNLABELED;
+    }
+
+    amfree(vself->dir_name);
+    vself->dir_name = g_strdup(self->mount_data);
+    status = parent_class->read_label(dself);
+
+    if (mounted) {
+       unmount_disc(self);
+    }
+
+    return status;
+}
+
+static gboolean
+dvdrw_device_start(Device *dself, DeviceAccessMode mode, char *label, char *timestamp)
+{
+    DvdRwDevice *self = DVDRW_DEVICE(dself);
+    VfsDevice *vself = VFS_DEVICE(dself);
+    DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
+
+    g_debug("Start DVDRW device");
+
+    if (device_in_error(dself)) return FALSE;
+    if (!check_access_mode(self, mode)) return FALSE;
+
+    dself->access_mode = mode;
+
+    /* We'll replace this with our own value */
+    amfree(vself->dir_name);
+
+    if (mode == ACCESS_READ) {
+       if (mount_disc(self, TRUE) != DEVICE_STATUS_SUCCESS) {
+           return FALSE;
+       }
+
+       vself->dir_name = g_strdup(self->mount_data);
+    } else if (mode == ACCESS_WRITE) {
+       vself->dir_name = g_strdup(self->cache_data);
+    }
+
+    return parent_class->start(dself, mode, label, timestamp);
+}
+
+static gboolean
+dvdrw_device_finish(Device *dself)
+{
+    DvdRwDevice *self = DVDRW_DEVICE(dself);
+    VfsDevice *vself = VFS_DEVICE(dself);
+    gboolean result;
+    DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
+    DeviceAccessMode mode;
+
+    g_debug("Finish DVDRW device");
+
+    /* Save access mode before parent class messes with it */
+    mode = dself->access_mode;
+
+    result = parent_class->finish(dself);
+
+    if (mode == ACCESS_READ) {
+       unmount_disc(self);
+    }
+
+    if (!result || device_in_error(dself)) {
+       return FALSE;
+    }
+
+    if (mode == ACCESS_WRITE) {
+       result = burn_disc(self);
+
+       if (result && !self->keep_cache) {
+           delete_vfs_files(vself);
+       }
+
+       return result;
+    }
+
+    return TRUE;
+}
+
+static gboolean
+burn_disc(DvdRwDevice *self)
+{
+    gint status;
+
+    char *burn_argv[] = {NULL, "-use-the-force-luke",
+       "-Z", self->dvdrw_device,
+       "-J", "-R", "-pad", "-quiet",
+       self->cache_dir, NULL};
+
+    if (self->growisofs_command == NULL) {
+       burn_argv[0] = "growisofs";
+    } else {
+       burn_argv[0] = self->growisofs_command;
+    }
+
+    g_debug("Burning media in %s", self->dvdrw_device);
+    if (execute_command(self, burn_argv, &status) != DEVICE_STATUS_SUCCESS) {
+       return FALSE;
+    }
+    g_debug("Burn completed successfully");
+
+    return TRUE;
+}
+
+static void
+dvdrw_device_finalize(GObject *gself)
+{
+    DvdRwDevice *self = DVDRW_DEVICE(gself);
+    GObjectClass *parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(gself)));
+
+    if (parent_class->finalize) {
+       parent_class->finalize(gself);
+    }
+
+    amfree(self->dvdrw_device);
+
+    amfree(self->cache_dir);
+    amfree(self->cache_data);
+    amfree(self->mount_point);
+    amfree(self->mount_data);
+    amfree(self->growisofs_command);
+    amfree(self->mount_command);
+    amfree(self->umount_command);
+}
+
+static gboolean
+check_access_mode(DvdRwDevice *self, DeviceAccessMode mode)
+{
+    Device *dself = DEVICE(self);
+
+    if (mode == ACCESS_READ) {
+       return check_readable(self);
+    } else if (mode == ACCESS_WRITE) {
+       return TRUE;
+    }
+
+    device_set_error(dself,
+       stralloc(_("DVDRW device can only be opened in READ or WRITE mode")),
+       DEVICE_STATUS_DEVICE_ERROR);
+
+    return FALSE;
+}
+
+static gboolean
+check_readable(DvdRwDevice *self)
+{
+    Device *dself = DEVICE(self);
+    GValue value;
+    bzero(&value, sizeof(value));
+
+    if (! device_get_simple_property(dself, PROPERTY_DVDRW_MOUNT_POINT, &value, NULL, NULL)) {
+       device_set_error(dself,
+           stralloc(_("DVDRW device requires DVDRW_MOUNT_POINT to open device for reading")),
+           DEVICE_STATUS_DEVICE_ERROR);
+
+       return FALSE;
+    }
+
+    return TRUE;
+}
+
+static DeviceStatusFlags
+mount_disc(DvdRwDevice *self, gboolean report_error)
+{
+    Device *dself = DEVICE(self);
+    gchar *mount_argv[] = { NULL, self->mount_point, NULL };
+    DeviceStatusFlags status;
+
+    if (self->mounted) {
+       return DEVICE_STATUS_SUCCESS;
+    }
+
+    if (self->mount_command == NULL) {
+       mount_argv[0] = "mount";
+    } else {
+       mount_argv[0] = self->mount_command;
+    }
+
+    g_debug("Mounting media at %s", self->mount_point);
+    status = execute_command(report_error ? self : NULL, mount_argv, NULL);
+    if (status != DEVICE_STATUS_SUCCESS) {
+       /* Wait a few seconds and try again - The tray may still be out after burning */
+       sleep(3);
+       if (execute_command(report_error ? self : NULL, mount_argv, NULL) == DEVICE_STATUS_SUCCESS) {
+           /* Clear error */
+           device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
+           self->mounted = TRUE;
+           return DEVICE_STATUS_SUCCESS;
+       } else {
+           return status;
+       }
+    }
+
+    self->mounted = TRUE;
+    return DEVICE_STATUS_SUCCESS;
+}
+
+static void
+unmount_disc(DvdRwDevice *self)
+{
+    gchar *unmount_argv[] = { NULL, self->mount_point, NULL };
+    DeviceStatusFlags status;
+
+    if (! self->mounted) {
+       return;
+    }
+
+    if (self->umount_command == NULL) {
+       unmount_argv[0] = "umount";
+    } else {
+       unmount_argv[0] = self->umount_command;
+    }
+
+    g_debug("Unmounting media at %s", self->mount_point);
+    status = execute_command(NULL, unmount_argv, NULL);
+    if (status == DEVICE_STATUS_SUCCESS) {
+       self->mounted = FALSE;
+    }
+}
+
+static DeviceStatusFlags
+execute_command(DvdRwDevice *self, gchar **argv, gint *result)
+{
+    Device *dself = DEVICE(self);
+    gchar *std_output = NULL;
+    gchar *std_error = NULL;
+    gint errnum = 0;
+    GError *error = NULL;
+    gboolean success;
+
+    /* g_debug("Executing: %s", argv[0]); */
+
+    g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
+       &std_output, &std_error, &errnum, &error);
+
+    /* g_debug("Execution complete"); */
+
+    if (WIFSIGNALED(errnum)) {
+       success = FALSE;
+    } else if (WIFEXITED(errnum)) {
+       success = (WEXITSTATUS(errnum) == 0);
+    } else {
+       success = FALSE;
+    }
+
+    if (!success) {
+       gchar *error_message = vstrallocf(_("DVDRW device cannot execute '%s': %s (status: %d) (stderr: %s)"),
+           argv[0], error ? error->message : _("Unknown error"), errnum, std_error ? std_error: "No stderr");
+
+       if (dself != NULL) {
+           device_set_error(dself, error_message, DEVICE_STATUS_DEVICE_ERROR);
+       }
+
+       if (std_output) {
+           g_free(std_output);
+       }
+
+       if (std_error) {
+           g_free(std_error);
+       }
+
+       if (error) {
+           g_error_free(error);
+       }
+
+       if (result != NULL) {
+           *result = errnum;
+       }
+
+       return DEVICE_STATUS_DEVICE_ERROR;
+    }
+
+    return DEVICE_STATUS_SUCCESS;
+}