2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 2009 University of Maryland at College Park
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.
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.
23 * Author: Sam Couter <sam@couter.id.au>
27 #include "vfs-device.h"
30 * Type checking and casting macros
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)
39 /* Forward declaration */
40 static GType dvdrw_device_get_type(void);
43 * Main object structure
45 typedef struct _DvdRwDevice DvdRwDevice;
56 gboolean unlabelled_when_unmountable;
57 gchar *growisofs_command;
59 gchar *umount_command;
65 typedef struct _DvdRwDeviceClass DvdRwDeviceClass;
66 struct _DvdRwDeviceClass {
67 VfsDeviceClass __parent__;
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)
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)
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)
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)
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)
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)
94 /* GObject housekeeping */
96 dvdrw_device_register(void);
99 dvdrw_device_factory(char *device_name, char *device_type, char *device_node);
102 dvdrw_device_class_init (DvdRwDeviceClass *c);
105 dvdrw_device_init (DvdRwDevice *self);
109 dvdrw_device_set_mount_point_fn(Device *self,
110 DevicePropertyBase *base, GValue *val,
111 PropertySurety surety, PropertySource source);
114 dvdrw_device_set_keep_cache_fn(Device *self,
115 DevicePropertyBase *base, GValue *val,
116 PropertySurety surety, PropertySource source);
119 dvdrw_device_set_unlabelled_when_unmountable_fn(Device *self,
120 DevicePropertyBase *base, GValue *val,
121 PropertySurety surety, PropertySource source);
124 dvdrw_device_set_growisofs_command_fn(Device *self,
125 DevicePropertyBase *base, GValue *val,
126 PropertySurety surety, PropertySource source);
129 dvdrw_device_set_mount_command_fn(Device *self,
130 DevicePropertyBase *base, GValue *val,
131 PropertySurety surety, PropertySource source);
134 dvdrw_device_set_umount_command_fn(Device *self,
135 DevicePropertyBase *base, GValue *val,
136 PropertySurety surety, PropertySource source);
140 dvdrw_device_open_device(Device *dself, char *device_name, char *device_type, char *device_node);
142 static DeviceStatusFlags
143 dvdrw_device_read_label(Device *dself);
146 dvdrw_device_start(Device *dself, DeviceAccessMode mode, char *label, char *timestamp);
149 dvdrw_device_finish(Device *dself);
152 dvdrw_device_finalize(GObject *gself);
154 /* Helper functions */
156 check_access_mode(DvdRwDevice *self, DeviceAccessMode mode);
159 check_readable(DvdRwDevice *self);
161 static DeviceStatusFlags
162 mount_disc(DvdRwDevice *self, gboolean report_error);
165 unmount_disc(DvdRwDevice *self);
168 burn_disc(DvdRwDevice *self);
170 static DeviceStatusFlags
171 execute_command(DvdRwDevice *self, gchar **argv, gint *status);
174 dvdrw_device_get_type (void)
176 static GType type = 0;
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 */,
188 (GInstanceInitFunc) dvdrw_device_init,
192 type = g_type_register_static (TYPE_VFS_DEVICE, "DvdRwDevice",
193 &info, (GTypeFlags)0);
200 dvdrw_device_register(void)
202 const char *device_prefix_list[] = { "dvdrw", NULL };
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");
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");
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");
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");
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");
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");
228 register_device(dvdrw_device_factory, device_prefix_list);
232 dvdrw_device_factory(char *device_name, char *device_type, char *device_node)
236 g_assert(0 == strncmp(device_type, "dvdrw", strlen("dvdrw")));
238 device = DEVICE(g_object_new(TYPE_DVDRW_DEVICE, NULL));
239 device_open_device(device, device_name, device_type, device_node);
245 dvdrw_device_class_init (DvdRwDeviceClass *c)
247 DeviceClass *device_class = DEVICE_CLASS(c);
248 GObjectClass *g_object_class = G_OBJECT_CLASS(c);
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;
255 g_object_class->finalize = dvdrw_device_finalize;
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);
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);
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);
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);
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);
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);
289 dvdrw_device_init (DvdRwDevice *self)
291 Device *dself = DEVICE(self);
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;
305 bzero(&val, sizeof(val));
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);
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);
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);
327 dvdrw_device_set_mount_point_fn(Device *dself, DevicePropertyBase *base,
328 GValue *val, PropertySurety surety, PropertySource source)
330 DvdRwDevice *self = DVDRW_DEVICE(dself);
332 amfree(self->mount_point);
333 amfree(self->mount_data);
335 self->mount_point = g_value_dup_string(val);
336 self->mount_data = g_strconcat(self->mount_point, "/data/", NULL);
338 device_clear_volume_details(dself);
340 return device_simple_property_set_fn(dself, base, val, surety, source);
344 dvdrw_device_set_keep_cache_fn(Device *dself, DevicePropertyBase *base,
345 GValue *val, PropertySurety surety, PropertySource source)
347 DvdRwDevice *self = DVDRW_DEVICE(dself);
349 self->keep_cache = g_value_get_boolean(val);
351 return device_simple_property_set_fn(dself, base, val, surety, source);
355 dvdrw_device_set_unlabelled_when_unmountable_fn(Device *dself, DevicePropertyBase *base,
356 GValue *val, PropertySurety surety, PropertySource source)
358 DvdRwDevice *self = DVDRW_DEVICE(dself);
360 self->unlabelled_when_unmountable = g_value_get_boolean(val);
362 return device_simple_property_set_fn(dself, base, val, surety, source);
366 dvdrw_device_set_growisofs_command_fn(Device *dself, DevicePropertyBase *base,
367 GValue *val, PropertySurety surety, PropertySource source)
369 DvdRwDevice *self = DVDRW_DEVICE(dself);
371 self->growisofs_command = g_value_dup_string(val);
373 return device_simple_property_set_fn(dself, base, val, surety, source);
377 dvdrw_device_set_mount_command_fn(Device *dself, DevicePropertyBase *base,
378 GValue *val, PropertySurety surety, PropertySource source)
380 DvdRwDevice *self = DVDRW_DEVICE(dself);
382 self->mount_command = g_value_dup_string(val);
384 return device_simple_property_set_fn(dself, base, val, surety, source);
388 dvdrw_device_set_umount_command_fn(Device *dself, DevicePropertyBase *base,
389 GValue *val, PropertySurety surety, PropertySource source)
391 DvdRwDevice *self = DVDRW_DEVICE(dself);
393 self->umount_command = g_value_dup_string(val);
395 return device_simple_property_set_fn(dself, base, val, surety, source);
399 dvdrw_device_open_device(Device *dself, char *device_name, char *device_type, char *device_node)
401 DvdRwDevice *self = DVDRW_DEVICE(dself);
402 DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
406 g_debug("Opening device: %s", device_node);
408 bzero(&val, sizeof(val));
410 colon = index(device_node, ':');
412 device_set_error(dself,
413 stralloc(_("DVDRW device requires cache directory and DVD-RW device separated by a colon (:) in tapedev")),
414 DEVICE_STATUS_DEVICE_ERROR);
418 self->cache_dir = g_strndup(device_node, colon - device_node);
419 self->cache_data = g_strconcat(self->cache_dir, "/data/", NULL);
420 self->dvdrw_device = g_strdup(colon + 1);
422 parent_class->open_device(dself, device_name, device_type, device_node);
425 static DeviceStatusFlags
426 dvdrw_device_read_label(Device *dself)
428 DvdRwDevice *self = DVDRW_DEVICE(dself);
429 VfsDevice *vself = VFS_DEVICE(dself);
430 gboolean mounted = FALSE;
431 DeviceStatusFlags status;
432 struct stat dir_status;
433 DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
435 g_debug("Reading label from media at %s", self->mount_point);
437 if (device_in_error(dself)) return DEVICE_STATUS_DEVICE_ERROR;
438 if (!check_readable(self)) return DEVICE_STATUS_DEVICE_ERROR;
440 if (!self->mounted) {
441 status = mount_disc(self, !self->unlabelled_when_unmountable);
442 if (status != DEVICE_STATUS_SUCCESS) {
443 /* Not mountable. May be freshly formatted or corrupted, drive may be empty. */
444 return (self->unlabelled_when_unmountable)
445 ? DEVICE_STATUS_VOLUME_UNLABELED
451 if ((stat(self->mount_data, &dir_status) < 0) && (errno == ENOENT)) {
452 /* No data directory, consider the DVD unlabelled */
453 g_debug("Media contains no data directory and therefore no label");
456 return DEVICE_STATUS_VOLUME_UNLABELED;
459 amfree(vself->dir_name);
460 vself->dir_name = g_strdup(self->mount_data);
461 status = parent_class->read_label(dself);
471 dvdrw_device_start(Device *dself, DeviceAccessMode mode, char *label, char *timestamp)
473 DvdRwDevice *self = DVDRW_DEVICE(dself);
474 VfsDevice *vself = VFS_DEVICE(dself);
475 DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
477 g_debug("Start DVDRW device");
479 if (device_in_error(dself)) return FALSE;
480 if (!check_access_mode(self, mode)) return FALSE;
482 dself->access_mode = mode;
484 /* We'll replace this with our own value */
485 amfree(vself->dir_name);
487 if (mode == ACCESS_READ) {
488 if (mount_disc(self, TRUE) != DEVICE_STATUS_SUCCESS) {
492 vself->dir_name = g_strdup(self->mount_data);
493 } else if (mode == ACCESS_WRITE) {
494 vself->dir_name = g_strdup(self->cache_data);
497 return parent_class->start(dself, mode, label, timestamp);
501 dvdrw_device_finish(Device *dself)
503 DvdRwDevice *self = DVDRW_DEVICE(dself);
504 VfsDevice *vself = VFS_DEVICE(dself);
506 DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
507 DeviceAccessMode mode;
509 g_debug("Finish DVDRW device");
511 /* Save access mode before parent class messes with it */
512 mode = dself->access_mode;
514 if (device_in_error(dself)) {
515 if (mode == ACCESS_READ) {
516 /* Still need to do this, don't care if it works or not */
523 result = parent_class->finish(dself);
525 if (mode == ACCESS_READ) {
533 if (mode == ACCESS_WRITE) {
534 result = burn_disc(self);
536 if (result && !self->keep_cache) {
537 delete_vfs_files(vself);
547 burn_disc(DvdRwDevice *self)
551 char *burn_argv[] = {NULL, "-use-the-force-luke",
552 "-Z", self->dvdrw_device,
553 "-J", "-R", "-pad", "-quiet",
554 self->cache_dir, NULL};
556 if (self->growisofs_command == NULL) {
557 burn_argv[0] = "growisofs";
559 burn_argv[0] = self->growisofs_command;
562 g_debug("Burning media in %s", self->dvdrw_device);
563 if (execute_command(self, burn_argv, &status) != DEVICE_STATUS_SUCCESS) {
566 g_debug("Burn completed successfully");
572 dvdrw_device_finalize(GObject *gself)
574 DvdRwDevice *self = DVDRW_DEVICE(gself);
575 GObjectClass *parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(gself)));
577 if (parent_class->finalize) {
578 parent_class->finalize(gself);
581 amfree(self->dvdrw_device);
583 amfree(self->cache_dir);
584 amfree(self->cache_data);
585 amfree(self->mount_point);
586 amfree(self->mount_data);
587 amfree(self->growisofs_command);
588 amfree(self->mount_command);
589 amfree(self->umount_command);
593 check_access_mode(DvdRwDevice *self, DeviceAccessMode mode)
595 Device *dself = DEVICE(self);
597 if (mode == ACCESS_READ) {
598 return check_readable(self);
599 } else if (mode == ACCESS_WRITE) {
603 device_set_error(dself,
604 stralloc(_("DVDRW device can only be opened in READ or WRITE mode")),
605 DEVICE_STATUS_DEVICE_ERROR);
611 check_readable(DvdRwDevice *self)
613 Device *dself = DEVICE(self);
615 bzero(&value, sizeof(value));
617 if (! device_get_simple_property(dself, PROPERTY_DVDRW_MOUNT_POINT, &value, NULL, NULL)) {
618 device_set_error(dself,
619 stralloc(_("DVDRW device requires DVDRW_MOUNT_POINT to open device for reading")),
620 DEVICE_STATUS_DEVICE_ERROR);
628 static DeviceStatusFlags
629 mount_disc(DvdRwDevice *self, gboolean report_error)
631 Device *dself = DEVICE(self);
632 gchar *mount_argv[] = { NULL, self->mount_point, NULL };
633 DeviceStatusFlags status;
636 return DEVICE_STATUS_SUCCESS;
639 if (self->mount_command == NULL) {
640 mount_argv[0] = "mount";
642 mount_argv[0] = self->mount_command;
645 g_debug("Mounting media at %s", self->mount_point);
646 status = execute_command(report_error ? self : NULL, mount_argv, NULL);
647 if (status != DEVICE_STATUS_SUCCESS) {
648 /* Wait a few seconds and try again - The tray may still be out after burning */
650 if (execute_command(report_error ? self : NULL, mount_argv, NULL) == DEVICE_STATUS_SUCCESS) {
652 device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
653 self->mounted = TRUE;
654 return DEVICE_STATUS_SUCCESS;
660 self->mounted = TRUE;
661 return DEVICE_STATUS_SUCCESS;
665 unmount_disc(DvdRwDevice *self)
667 gchar *unmount_argv[] = { NULL, self->mount_point, NULL };
668 DeviceStatusFlags status;
670 if (! self->mounted) {
674 if (self->umount_command == NULL) {
675 unmount_argv[0] = "umount";
677 unmount_argv[0] = self->umount_command;
680 g_debug("Unmounting media at %s", self->mount_point);
681 status = execute_command(NULL, unmount_argv, NULL);
682 if (status == DEVICE_STATUS_SUCCESS) {
683 self->mounted = FALSE;
687 static DeviceStatusFlags
688 execute_command(DvdRwDevice *self, gchar **argv, gint *result)
690 Device *dself = DEVICE(self);
691 gchar *std_output = NULL;
692 gchar *std_error = NULL;
694 GError *error = NULL;
698 /* g_debug("Executing: %s", argv[0]); */
700 g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
701 &std_output, &std_error, &errnum, &error);
703 /* g_debug("Execution complete"); */
705 if (WIFSIGNALED(errnum)) {
707 signum = WTERMSIG(errnum);
708 } else if (WIFEXITED(errnum)) {
709 success = (WEXITSTATUS(errnum) == 0);
715 gchar *error_message = vstrallocf(_("DVDRW device cannot execute '%s': %s (status: %d) (stderr: %s)"),
716 argv[0], error ? error->message : _("Unknown error"), errnum, std_error ? std_error: "No stderr");
719 device_set_error(dself, error_message, DEVICE_STATUS_DEVICE_ERROR);
734 if (result != NULL) {
738 return DEVICE_STATUS_DEVICE_ERROR;
741 return DEVICE_STATUS_SUCCESS;