Imported Upstream version 3.2.0
[debian/amanda] / device-src / dvdrw-device.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 2009 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: Sam Couter <sam@couter.id.au>
24  */
25
26 #include "amanda.h"
27 #include "vfs-device.h"
28
29 /*
30  * Type checking and casting macros
31  */
32 #define TYPE_DVDRW_DEVICE       (dvdrw_device_get_type())
33 #define DVDRW_DEVICE(obj)       G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DVDRW_DEVICE, DvdRwDevice)
34 #define DVDRW_DEVICE_CONST(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_DVDRW_DEVICE, DvdRwDevice const)
35 #define DVDRW_DEVICE_CLASS(klass)       G_TYPE_CHECK_CLASS_CAST((klass), TYPE_DVDRW_DEVICE, DvdRwDeviceClass)
36 #define IS_DVDRW_DEVICE(obj)    G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_DVDRW_DEVICE)
37 #define DVDRW_DEVICE_GET_CLASS(obj)     G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_DVDRW_DEVICE, DvdRwDeviceClass)
38
39 /* Forward declaration */
40 static GType dvdrw_device_get_type(void);
41
42 /*
43  * Main object structure
44  */
45 typedef struct _DvdRwDevice DvdRwDevice;
46 struct _DvdRwDevice {
47     VfsDevice __parent__;
48
49     gchar *dvdrw_device;
50     gchar *cache_dir;
51     gchar *cache_data;
52     gchar *mount_point;
53     gchar *mount_data;
54     gboolean mounted;
55     gboolean keep_cache;
56     gboolean unlabelled_when_unmountable;
57     gchar *growisofs_command;
58     gchar *mount_command;
59     gchar *umount_command;
60 };
61
62 /*
63  * Class definition
64  */
65 typedef struct _DvdRwDeviceClass DvdRwDeviceClass;
66 struct _DvdRwDeviceClass {
67     VfsDeviceClass __parent__;
68 };
69
70 /* Where the DVD-RW can be mounted */
71 static DevicePropertyBase device_property_dvdrw_mount_point;
72 #define PROPERTY_DVDRW_MOUNT_POINT (device_property_dvdrw_mount_point.ID)
73
74 /* Should the on-disk version be kept after the optical disc has been written? */
75 static DevicePropertyBase device_property_dvdrw_keep_cache;
76 #define PROPERTY_DVDRW_KEEP_CACHE (device_property_dvdrw_keep_cache.ID)
77
78 /* Should a mount failure (eg, a freshly formatted disc) when reading a label be treated like an unlabelled volume? */
79 static DevicePropertyBase device_property_dvdrw_unlabelled_when_unmountable;
80 #define PROPERTY_DVDRW_UNLABELLED_WHEN_UNMOUNTABLE (device_property_dvdrw_unlabelled_when_unmountable.ID)
81
82 /* Where to find the growisofs command */
83 static DevicePropertyBase device_property_dvdrw_growisofs_command;
84 #define PROPERTY_DVDRW_GROWISOFS_COMMAND (device_property_dvdrw_growisofs_command.ID)
85
86 /* Where to find the filesystem mount command */
87 static DevicePropertyBase device_property_dvdrw_mount_command;
88 #define PROPERTY_DVDRW_MOUNT_COMMAND (device_property_dvdrw_mount_command.ID)
89
90 /* Where to find the filesystem unmount command */
91 static DevicePropertyBase device_property_dvdrw_umount_command;
92 #define PROPERTY_DVDRW_UMOUNT_COMMAND (device_property_dvdrw_umount_command.ID)
93
94 /* GObject housekeeping */
95 void
96 dvdrw_device_register(void);
97
98 static Device*
99 dvdrw_device_factory(char *device_name, char *device_type, char *device_node);
100
101 static void
102 dvdrw_device_class_init (DvdRwDeviceClass *c);
103
104 static void
105 dvdrw_device_init (DvdRwDevice *self);
106
107 /* Properties */
108 static gboolean
109 dvdrw_device_set_mount_point_fn(Device *self,
110     DevicePropertyBase *base, GValue *val,
111     PropertySurety surety, PropertySource source);
112
113 static gboolean
114 dvdrw_device_set_keep_cache_fn(Device *self,
115     DevicePropertyBase *base, GValue *val,
116     PropertySurety surety, PropertySource source);
117
118 static gboolean
119 dvdrw_device_set_unlabelled_when_unmountable_fn(Device *self,
120     DevicePropertyBase *base, GValue *val,
121     PropertySurety surety, PropertySource source);
122
123 static gboolean
124 dvdrw_device_set_growisofs_command_fn(Device *self,
125     DevicePropertyBase *base, GValue *val,
126     PropertySurety surety, PropertySource source);
127
128 static gboolean
129 dvdrw_device_set_mount_command_fn(Device *self,
130     DevicePropertyBase *base, GValue *val,
131     PropertySurety surety, PropertySource source);
132
133 static gboolean
134 dvdrw_device_set_umount_command_fn(Device *self,
135     DevicePropertyBase *base, GValue *val,
136     PropertySurety surety, PropertySource source);
137
138 /* Methods */
139 static void
140 dvdrw_device_open_device(Device *dself, char *device_name, char *device_type, char *device_node);
141
142 static DeviceStatusFlags
143 dvdrw_device_read_label(Device *dself);
144
145 static gboolean
146 dvdrw_device_start(Device *dself, DeviceAccessMode mode, char *label, char *timestamp);
147
148 static gboolean
149 dvdrw_device_finish(Device *dself);
150
151 static void
152 dvdrw_device_finalize(GObject *gself);
153
154 /* Helper functions */
155 static gboolean
156 check_access_mode(DvdRwDevice *self, DeviceAccessMode mode);
157
158 static gboolean
159 check_readable(DvdRwDevice *self);
160
161 static DeviceStatusFlags
162 mount_disc(DvdRwDevice *self, gboolean report_error);
163
164 static void
165 unmount_disc(DvdRwDevice *self);
166
167 static gboolean
168 burn_disc(DvdRwDevice *self);
169
170 static DeviceStatusFlags
171 execute_command(DvdRwDevice *self, gchar **argv, gint *status);
172
173 static GType
174 dvdrw_device_get_type (void)
175 {
176     static GType type = 0;
177
178     if G_UNLIKELY(type == 0) {
179         static const GTypeInfo info = {
180             sizeof (VfsDeviceClass),
181             (GBaseInitFunc) NULL,
182             (GBaseFinalizeFunc) NULL,
183             (GClassInitFunc) dvdrw_device_class_init,
184             (GClassFinalizeFunc) NULL,
185             NULL /* class_data */,
186             sizeof (VfsDevice),
187             0 /* n_preallocs */,
188             (GInstanceInitFunc) dvdrw_device_init,
189             NULL
190         };
191
192         type = g_type_register_static (TYPE_VFS_DEVICE, "DvdRwDevice",
193                                        &info, (GTypeFlags)0);
194     }
195
196     return type;
197 }
198
199 void
200 dvdrw_device_register(void)
201 {
202     const char *device_prefix_list[] = { "dvdrw", NULL };
203
204     device_property_fill_and_register(&device_property_dvdrw_mount_point,
205         G_TYPE_STRING, "dvdrw_mount_point",
206         "Directory to mount DVD-RW for reading");
207
208     device_property_fill_and_register(&device_property_dvdrw_keep_cache,
209         G_TYPE_BOOLEAN, "dvdrw_keep_cache",
210         "Keep on-disk cache after DVD-RW has been written");
211
212     device_property_fill_and_register(&device_property_dvdrw_unlabelled_when_unmountable,
213         G_TYPE_BOOLEAN, "dvdrw_unlabelled_when_unmountable",
214         "Treat unmountable volumes as unlabelled when reading label");
215
216     device_property_fill_and_register(&device_property_dvdrw_growisofs_command,
217         G_TYPE_BOOLEAN, "dvdrw_growisofs_command",
218         "The location of the growisofs command used to write the DVD-RW");
219
220     device_property_fill_and_register(&device_property_dvdrw_mount_command,
221         G_TYPE_BOOLEAN, "dvdrw_mount_command",
222         "The location of the mount command used to mount the DVD-RW filesystem for reading");
223
224     device_property_fill_and_register(&device_property_dvdrw_umount_command,
225         G_TYPE_BOOLEAN, "dvdrw_umount_command",
226         "The location of the umount command used to unmount the DVD-RW filesystem after reading");
227
228     register_device(dvdrw_device_factory, device_prefix_list);
229 }
230
231 static Device *
232 dvdrw_device_factory(char *device_name, char *device_type, char *device_node)
233 {
234     Device *device;
235
236     g_assert(0 == strncmp(device_type, "dvdrw", strlen("dvdrw")));
237
238     device = DEVICE(g_object_new(TYPE_DVDRW_DEVICE, NULL));
239     device_open_device(device, device_name, device_type, device_node);
240
241     return device;
242 }
243
244 static void
245 dvdrw_device_class_init (DvdRwDeviceClass *c)
246 {
247     DeviceClass *device_class = DEVICE_CLASS(c);
248     GObjectClass *g_object_class = G_OBJECT_CLASS(c);
249
250     device_class->open_device = dvdrw_device_open_device;
251     device_class->read_label = dvdrw_device_read_label;
252     device_class->start = dvdrw_device_start;
253     device_class->finish = dvdrw_device_finish;
254
255     g_object_class->finalize = dvdrw_device_finalize;
256
257     device_class_register_property(device_class, PROPERTY_DVDRW_MOUNT_POINT,
258         PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
259         device_simple_property_get_fn,
260         dvdrw_device_set_mount_point_fn);
261
262     device_class_register_property(device_class, PROPERTY_DVDRW_KEEP_CACHE,
263         PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
264         device_simple_property_get_fn,
265         dvdrw_device_set_keep_cache_fn);
266
267     device_class_register_property(device_class, PROPERTY_DVDRW_UNLABELLED_WHEN_UNMOUNTABLE,
268         PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
269         device_simple_property_get_fn,
270         dvdrw_device_set_unlabelled_when_unmountable_fn);
271
272     device_class_register_property(device_class, PROPERTY_DVDRW_GROWISOFS_COMMAND,
273         PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
274         device_simple_property_get_fn,
275         dvdrw_device_set_growisofs_command_fn);
276
277     device_class_register_property(device_class, PROPERTY_DVDRW_MOUNT_COMMAND,
278         PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
279         device_simple_property_get_fn,
280         dvdrw_device_set_mount_command_fn);
281
282     device_class_register_property(device_class, PROPERTY_DVDRW_UMOUNT_COMMAND,
283         PROPERTY_ACCESS_GET_MASK | PROPERTY_ACCESS_SET_BEFORE_START,
284         device_simple_property_get_fn,
285         dvdrw_device_set_umount_command_fn);
286 }
287
288 static void
289 dvdrw_device_init (DvdRwDevice *self)
290 {
291     Device *dself = DEVICE(self);
292     GValue val;
293
294     self->dvdrw_device = NULL;
295     self->cache_dir = NULL;
296     self->cache_data = NULL;
297     self->mount_point = NULL;
298     self->mount_data = NULL;
299     self->mounted = FALSE;
300     self->keep_cache = FALSE;
301     self->growisofs_command = NULL;
302     self->mount_command = NULL;
303     self->umount_command = NULL;
304
305     bzero(&val, sizeof(val));
306
307     g_value_init(&val, G_TYPE_BOOLEAN);
308     g_value_set_boolean(&val, FALSE);
309     device_set_simple_property(dself, PROPERTY_APPENDABLE,
310         &val, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
311     g_value_unset(&val);
312
313     g_value_init(&val, G_TYPE_BOOLEAN);
314     g_value_set_boolean(&val, FALSE);
315     device_set_simple_property(dself, PROPERTY_PARTIAL_DELETION,
316         &val, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
317     g_value_unset(&val);
318
319     g_value_init(&val, G_TYPE_BOOLEAN);
320     g_value_set_boolean(&val, FALSE);
321     device_set_simple_property(dself, PROPERTY_FULL_DELETION,
322         &val, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
323     g_value_unset(&val);
324
325     g_value_init(&val, G_TYPE_BOOLEAN);
326     g_value_set_boolean(&val, TRUE);
327     device_set_simple_property(dself, PROPERTY_LEOM,
328         &val, PROPERTY_SURETY_GOOD, PROPERTY_SOURCE_DETECTED);
329     g_value_unset(&val);
330 }
331
332 static gboolean
333 dvdrw_device_set_mount_point_fn(Device *dself, DevicePropertyBase *base,
334     GValue *val, PropertySurety surety, PropertySource source)
335 {
336     DvdRwDevice *self = DVDRW_DEVICE(dself);
337
338     amfree(self->mount_point);
339     amfree(self->mount_data);
340
341     self->mount_point = g_value_dup_string(val);
342     self->mount_data = g_strconcat(self->mount_point, "/data/", NULL);
343
344     device_clear_volume_details(dself);
345
346     return device_simple_property_set_fn(dself, base, val, surety, source);
347 }
348
349 static gboolean
350 dvdrw_device_set_keep_cache_fn(Device *dself, DevicePropertyBase *base,
351     GValue *val, PropertySurety surety, PropertySource source)
352 {
353     DvdRwDevice *self = DVDRW_DEVICE(dself);
354
355     self->keep_cache = g_value_get_boolean(val);
356
357     return device_simple_property_set_fn(dself, base, val, surety, source);
358 }
359
360 static gboolean
361 dvdrw_device_set_unlabelled_when_unmountable_fn(Device *dself, DevicePropertyBase *base,
362     GValue *val, PropertySurety surety, PropertySource source)
363 {
364     DvdRwDevice *self = DVDRW_DEVICE(dself);
365
366     self->unlabelled_when_unmountable = g_value_get_boolean(val);
367
368     return device_simple_property_set_fn(dself, base, val, surety, source);
369 }
370
371 static gboolean
372 dvdrw_device_set_growisofs_command_fn(Device *dself, DevicePropertyBase *base,
373     GValue *val, PropertySurety surety, PropertySource source)
374 {
375     DvdRwDevice *self = DVDRW_DEVICE(dself);
376
377     self->growisofs_command = g_value_dup_string(val);
378
379     return device_simple_property_set_fn(dself, base, val, surety, source);
380 }
381
382 static gboolean
383 dvdrw_device_set_mount_command_fn(Device *dself, DevicePropertyBase *base,
384     GValue *val, PropertySurety surety, PropertySource source)
385 {
386     DvdRwDevice *self = DVDRW_DEVICE(dself);
387
388     self->mount_command = g_value_dup_string(val);
389
390     return device_simple_property_set_fn(dself, base, val, surety, source);
391 }
392
393 static gboolean
394 dvdrw_device_set_umount_command_fn(Device *dself, DevicePropertyBase *base,
395     GValue *val, PropertySurety surety, PropertySource source)
396 {
397     DvdRwDevice *self = DVDRW_DEVICE(dself);
398
399     self->umount_command = g_value_dup_string(val);
400
401     return device_simple_property_set_fn(dself, base, val, surety, source);
402 }
403
404 static void
405 dvdrw_device_open_device(Device *dself, char *device_name, char *device_type, char *device_node)
406 {
407     DvdRwDevice *self = DVDRW_DEVICE(dself);
408     DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
409     GValue val;
410     char *colon;
411
412     g_debug("Opening device: %s", device_node);
413
414     bzero(&val, sizeof(val));
415
416     colon = index(device_node, ':');
417     if (!colon) {
418         device_set_error(dself,
419             stralloc(_("DVDRW device requires cache directory and DVD-RW device separated by a colon (:) in tapedev")),
420             DEVICE_STATUS_DEVICE_ERROR);
421         return;
422     }
423
424     self->cache_dir = g_strndup(device_node, colon - device_node);
425     self->cache_data = g_strconcat(self->cache_dir, "/data/", NULL);
426     self->dvdrw_device = g_strdup(colon + 1);
427
428     parent_class->open_device(dself, device_name, device_type, device_node);
429 }
430
431 static DeviceStatusFlags
432 dvdrw_device_read_label(Device *dself)
433 {
434     DvdRwDevice *self = DVDRW_DEVICE(dself);
435     VfsDevice *vself = VFS_DEVICE(dself);
436     gboolean mounted = FALSE;
437     DeviceStatusFlags status;
438     struct stat dir_status;
439     DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
440
441     g_debug("Reading label from media at %s", self->mount_point);
442
443     if (device_in_error(dself)) return DEVICE_STATUS_DEVICE_ERROR;
444     if (!check_readable(self)) return DEVICE_STATUS_DEVICE_ERROR;
445
446     if (!self->mounted) {
447         status = mount_disc(self, !self->unlabelled_when_unmountable);
448         if (status != DEVICE_STATUS_SUCCESS) {
449             /* Not mountable. May be freshly formatted or corrupted, drive may be empty. */
450             return (self->unlabelled_when_unmountable)
451                 ? DEVICE_STATUS_VOLUME_UNLABELED
452                 : status;
453         }
454         mounted = TRUE;
455     }
456
457     if ((stat(self->mount_data, &dir_status) < 0) && (errno == ENOENT)) {
458         /* No data directory, consider the DVD unlabelled */
459         g_debug("Media contains no data directory and therefore no label");
460         unmount_disc(self);
461
462         return DEVICE_STATUS_VOLUME_UNLABELED;
463     }
464
465     amfree(vself->dir_name);
466     vself->dir_name = g_strdup(self->mount_data);
467     status = parent_class->read_label(dself);
468
469     if (mounted) {
470         unmount_disc(self);
471     }
472
473     return status;
474 }
475
476 static gboolean
477 dvdrw_device_start(Device *dself, DeviceAccessMode mode, char *label, char *timestamp)
478 {
479     DvdRwDevice *self = DVDRW_DEVICE(dself);
480     VfsDevice *vself = VFS_DEVICE(dself);
481     DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
482
483     g_debug("Start DVDRW device");
484
485     if (device_in_error(dself)) return FALSE;
486     if (!check_access_mode(self, mode)) return FALSE;
487
488     dself->access_mode = mode;
489
490     /* We'll replace this with our own value */
491     amfree(vself->dir_name);
492
493     if (mode == ACCESS_READ) {
494         if (mount_disc(self, TRUE) != DEVICE_STATUS_SUCCESS) {
495             return FALSE;
496         }
497
498         vself->dir_name = g_strdup(self->mount_data);
499     } else if (mode == ACCESS_WRITE) {
500         vself->dir_name = g_strdup(self->cache_data);
501     }
502
503     return parent_class->start(dself, mode, label, timestamp);
504 }
505
506 static gboolean
507 dvdrw_device_finish(Device *dself)
508 {
509     DvdRwDevice *self = DVDRW_DEVICE(dself);
510     VfsDevice *vself = VFS_DEVICE(dself);
511     gboolean result;
512     DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
513     DeviceAccessMode mode;
514
515     g_debug("Finish DVDRW device");
516
517     /* Save access mode before parent class messes with it */
518     mode = dself->access_mode;
519
520     result = parent_class->finish(dself);
521
522     if (mode == ACCESS_READ) {
523         unmount_disc(self);
524     }
525
526     if (!result || device_in_error(dself)) {
527         return FALSE;
528     }
529
530     if (mode == ACCESS_WRITE) {
531         result = burn_disc(self);
532
533         if (result && !self->keep_cache) {
534             delete_vfs_files(vself);
535         }
536
537         return result;
538     }
539
540     return TRUE;
541 }
542
543 static gboolean
544 burn_disc(DvdRwDevice *self)
545 {
546     gint status;
547
548     char *burn_argv[] = {NULL, "-use-the-force-luke",
549         "-Z", self->dvdrw_device,
550         "-J", "-R", "-pad", "-quiet",
551         self->cache_dir, NULL};
552
553     if (self->growisofs_command == NULL) {
554         burn_argv[0] = "growisofs";
555     } else {
556         burn_argv[0] = self->growisofs_command;
557     }
558
559     g_debug("Burning media in %s", self->dvdrw_device);
560     if (execute_command(self, burn_argv, &status) != DEVICE_STATUS_SUCCESS) {
561         return FALSE;
562     }
563     g_debug("Burn completed successfully");
564
565     return TRUE;
566 }
567
568 static void
569 dvdrw_device_finalize(GObject *gself)
570 {
571     DvdRwDevice *self = DVDRW_DEVICE(gself);
572     GObjectClass *parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(gself)));
573
574     if (parent_class->finalize) {
575         parent_class->finalize(gself);
576     }
577
578     amfree(self->dvdrw_device);
579
580     amfree(self->cache_dir);
581     amfree(self->cache_data);
582     amfree(self->mount_point);
583     amfree(self->mount_data);
584     amfree(self->growisofs_command);
585     amfree(self->mount_command);
586     amfree(self->umount_command);
587 }
588
589 static gboolean
590 check_access_mode(DvdRwDevice *self, DeviceAccessMode mode)
591 {
592     Device *dself = DEVICE(self);
593
594     if (mode == ACCESS_READ) {
595         return check_readable(self);
596     } else if (mode == ACCESS_WRITE) {
597         return TRUE;
598     }
599
600     device_set_error(dself,
601         stralloc(_("DVDRW device can only be opened in READ or WRITE mode")),
602         DEVICE_STATUS_DEVICE_ERROR);
603
604     return FALSE;
605 }
606
607 static gboolean
608 check_readable(DvdRwDevice *self)
609 {
610     Device *dself = DEVICE(self);
611     GValue value;
612     bzero(&value, sizeof(value));
613
614     if (! device_get_simple_property(dself, PROPERTY_DVDRW_MOUNT_POINT, &value, NULL, NULL)) {
615         device_set_error(dself,
616             stralloc(_("DVDRW device requires DVDRW_MOUNT_POINT to open device for reading")),
617             DEVICE_STATUS_DEVICE_ERROR);
618
619         return FALSE;
620     }
621
622     return TRUE;
623 }
624
625 static DeviceStatusFlags
626 mount_disc(DvdRwDevice *self, gboolean report_error)
627 {
628     Device *dself = DEVICE(self);
629     gchar *mount_argv[] = { NULL, self->mount_point, NULL };
630     DeviceStatusFlags status;
631
632     if (self->mounted) {
633         return DEVICE_STATUS_SUCCESS;
634     }
635
636     if (self->mount_command == NULL) {
637         mount_argv[0] = "mount";
638     } else {
639         mount_argv[0] = self->mount_command;
640     }
641
642     g_debug("Mounting media at %s", self->mount_point);
643     status = execute_command(report_error ? self : NULL, mount_argv, NULL);
644     if (status != DEVICE_STATUS_SUCCESS) {
645         /* Wait a few seconds and try again - The tray may still be out after burning */
646         sleep(3);
647         if (execute_command(report_error ? self : NULL, mount_argv, NULL) == DEVICE_STATUS_SUCCESS) {
648             /* Clear error */
649             device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
650             self->mounted = TRUE;
651             return DEVICE_STATUS_SUCCESS;
652         } else {
653             return status;
654         }
655     }
656
657     self->mounted = TRUE;
658     return DEVICE_STATUS_SUCCESS;
659 }
660
661 static void
662 unmount_disc(DvdRwDevice *self)
663 {
664     gchar *unmount_argv[] = { NULL, self->mount_point, NULL };
665     DeviceStatusFlags status;
666
667     if (! self->mounted) {
668         return;
669     }
670
671     if (self->umount_command == NULL) {
672         unmount_argv[0] = "umount";
673     } else {
674         unmount_argv[0] = self->umount_command;
675     }
676
677     g_debug("Unmounting media at %s", self->mount_point);
678     status = execute_command(NULL, unmount_argv, NULL);
679     if (status == DEVICE_STATUS_SUCCESS) {
680         self->mounted = FALSE;
681     }
682 }
683
684 static DeviceStatusFlags
685 execute_command(DvdRwDevice *self, gchar **argv, gint *result)
686 {
687     Device *dself = DEVICE(self);
688     gchar *std_output = NULL;
689     gchar *std_error = NULL;
690     gint errnum = 0;
691     GError *error = NULL;
692     gboolean success;
693     int signum = 0;
694
695     /* g_debug("Executing: %s", argv[0]); */
696
697     g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
698         &std_output, &std_error, &errnum, &error);
699
700     /* g_debug("Execution complete"); */
701
702     if (WIFSIGNALED(errnum)) {
703         success = FALSE;
704         signum = WTERMSIG(errnum);
705     } else if (WIFEXITED(errnum)) {
706         success = (WEXITSTATUS(errnum) == 0);
707     } else {
708         success = FALSE;
709     }
710
711     if (!success) {
712         gchar *error_message = vstrallocf(_("DVDRW device cannot execute '%s': %s (status: %d) (stderr: %s)"),
713             argv[0], error ? error->message : _("Unknown error"), errnum, std_error ? std_error: "No stderr");
714
715         if (dself != NULL) {
716             device_set_error(dself, error_message, DEVICE_STATUS_DEVICE_ERROR);
717         }
718
719         if (std_output) {
720             g_free(std_output);
721         }
722
723         if (std_error) {
724             g_free(std_error);
725         }
726
727         if (error) {
728             g_error_free(error);
729         }
730
731         if (result != NULL) {
732             *result = errnum;
733         }
734
735         return DEVICE_STATUS_DEVICE_ERROR;
736     }
737
738     return DEVICE_STATUS_SUCCESS;
739 }