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