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);
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);
333 dvdrw_device_set_mount_point_fn(Device *dself, DevicePropertyBase *base,
334 GValue *val, PropertySurety surety, PropertySource source)
336 DvdRwDevice *self = DVDRW_DEVICE(dself);
338 amfree(self->mount_point);
339 amfree(self->mount_data);
341 self->mount_point = g_value_dup_string(val);
342 self->mount_data = g_strconcat(self->mount_point, "/data/", NULL);
344 device_clear_volume_details(dself);
346 return device_simple_property_set_fn(dself, base, val, surety, source);
350 dvdrw_device_set_keep_cache_fn(Device *dself, DevicePropertyBase *base,
351 GValue *val, PropertySurety surety, PropertySource source)
353 DvdRwDevice *self = DVDRW_DEVICE(dself);
355 self->keep_cache = g_value_get_boolean(val);
357 return device_simple_property_set_fn(dself, base, val, surety, source);
361 dvdrw_device_set_unlabelled_when_unmountable_fn(Device *dself, DevicePropertyBase *base,
362 GValue *val, PropertySurety surety, PropertySource source)
364 DvdRwDevice *self = DVDRW_DEVICE(dself);
366 self->unlabelled_when_unmountable = g_value_get_boolean(val);
368 return device_simple_property_set_fn(dself, base, val, surety, source);
372 dvdrw_device_set_growisofs_command_fn(Device *dself, DevicePropertyBase *base,
373 GValue *val, PropertySurety surety, PropertySource source)
375 DvdRwDevice *self = DVDRW_DEVICE(dself);
377 self->growisofs_command = g_value_dup_string(val);
379 return device_simple_property_set_fn(dself, base, val, surety, source);
383 dvdrw_device_set_mount_command_fn(Device *dself, DevicePropertyBase *base,
384 GValue *val, PropertySurety surety, PropertySource source)
386 DvdRwDevice *self = DVDRW_DEVICE(dself);
388 self->mount_command = g_value_dup_string(val);
390 return device_simple_property_set_fn(dself, base, val, surety, source);
394 dvdrw_device_set_umount_command_fn(Device *dself, DevicePropertyBase *base,
395 GValue *val, PropertySurety surety, PropertySource source)
397 DvdRwDevice *self = DVDRW_DEVICE(dself);
399 self->umount_command = g_value_dup_string(val);
401 return device_simple_property_set_fn(dself, base, val, surety, source);
405 dvdrw_device_open_device(Device *dself, char *device_name, char *device_type, char *device_node)
407 DvdRwDevice *self = DVDRW_DEVICE(dself);
408 DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
412 g_debug("Opening device: %s", device_node);
414 bzero(&val, sizeof(val));
416 colon = index(device_node, ':');
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);
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);
428 parent_class->open_device(dself, device_name, device_type, device_node);
431 static DeviceStatusFlags
432 dvdrw_device_read_label(Device *dself)
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)));
441 g_debug("Reading label from media at %s", self->mount_point);
443 if (device_in_error(dself)) return DEVICE_STATUS_DEVICE_ERROR;
444 if (!check_readable(self)) return DEVICE_STATUS_DEVICE_ERROR;
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
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");
462 return DEVICE_STATUS_VOLUME_UNLABELED;
465 amfree(vself->dir_name);
466 vself->dir_name = g_strdup(self->mount_data);
467 status = parent_class->read_label(dself);
477 dvdrw_device_start(Device *dself, DeviceAccessMode mode, char *label, char *timestamp)
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)));
483 g_debug("Start DVDRW device");
485 if (device_in_error(dself)) return FALSE;
486 if (!check_access_mode(self, mode)) return FALSE;
488 dself->access_mode = mode;
490 /* We'll replace this with our own value */
491 amfree(vself->dir_name);
493 if (mode == ACCESS_READ) {
494 if (mount_disc(self, TRUE) != DEVICE_STATUS_SUCCESS) {
498 vself->dir_name = g_strdup(self->mount_data);
499 } else if (mode == ACCESS_WRITE) {
500 vself->dir_name = g_strdup(self->cache_data);
503 return parent_class->start(dself, mode, label, timestamp);
507 dvdrw_device_finish(Device *dself)
509 DvdRwDevice *self = DVDRW_DEVICE(dself);
510 VfsDevice *vself = VFS_DEVICE(dself);
512 DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
513 DeviceAccessMode mode;
515 g_debug("Finish DVDRW device");
517 /* Save access mode before parent class messes with it */
518 mode = dself->access_mode;
520 result = parent_class->finish(dself);
522 if (mode == ACCESS_READ) {
526 if (!result || device_in_error(dself)) {
530 if (mode == ACCESS_WRITE) {
531 result = burn_disc(self);
533 if (result && !self->keep_cache) {
534 delete_vfs_files(vself);
544 burn_disc(DvdRwDevice *self)
548 char *burn_argv[] = {NULL, "-use-the-force-luke",
549 "-Z", self->dvdrw_device,
550 "-J", "-R", "-pad", "-quiet",
551 self->cache_dir, NULL};
553 if (self->growisofs_command == NULL) {
554 burn_argv[0] = "growisofs";
556 burn_argv[0] = self->growisofs_command;
559 g_debug("Burning media in %s", self->dvdrw_device);
560 if (execute_command(self, burn_argv, &status) != DEVICE_STATUS_SUCCESS) {
563 g_debug("Burn completed successfully");
569 dvdrw_device_finalize(GObject *gself)
571 DvdRwDevice *self = DVDRW_DEVICE(gself);
572 GObjectClass *parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(gself)));
574 if (parent_class->finalize) {
575 parent_class->finalize(gself);
578 amfree(self->dvdrw_device);
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);
590 check_access_mode(DvdRwDevice *self, DeviceAccessMode mode)
592 Device *dself = DEVICE(self);
594 if (mode == ACCESS_READ) {
595 return check_readable(self);
596 } else if (mode == ACCESS_WRITE) {
600 device_set_error(dself,
601 stralloc(_("DVDRW device can only be opened in READ or WRITE mode")),
602 DEVICE_STATUS_DEVICE_ERROR);
608 check_readable(DvdRwDevice *self)
610 Device *dself = DEVICE(self);
612 bzero(&value, sizeof(value));
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);
625 static DeviceStatusFlags
626 mount_disc(DvdRwDevice *self, gboolean report_error)
628 Device *dself = DEVICE(self);
629 gchar *mount_argv[] = { NULL, self->mount_point, NULL };
630 DeviceStatusFlags status;
633 return DEVICE_STATUS_SUCCESS;
636 if (self->mount_command == NULL) {
637 mount_argv[0] = "mount";
639 mount_argv[0] = self->mount_command;
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 */
647 if (execute_command(report_error ? self : NULL, mount_argv, NULL) == DEVICE_STATUS_SUCCESS) {
649 device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
650 self->mounted = TRUE;
651 return DEVICE_STATUS_SUCCESS;
657 self->mounted = TRUE;
658 return DEVICE_STATUS_SUCCESS;
662 unmount_disc(DvdRwDevice *self)
664 gchar *unmount_argv[] = { NULL, self->mount_point, NULL };
665 DeviceStatusFlags status;
667 if (! self->mounted) {
671 if (self->umount_command == NULL) {
672 unmount_argv[0] = "umount";
674 unmount_argv[0] = self->umount_command;
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;
684 static DeviceStatusFlags
685 execute_command(DvdRwDevice *self, gchar **argv, gint *result)
687 Device *dself = DEVICE(self);
688 gchar *std_output = NULL;
689 gchar *std_error = NULL;
691 GError *error = NULL;
694 /* g_debug("Executing: %s", argv[0]); */
696 g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
697 &std_output, &std_error, &errnum, &error);
699 /* g_debug("Execution complete"); */
701 if (WIFSIGNALED(errnum)) {
703 } else if (WIFEXITED(errnum)) {
704 success = (WEXITSTATUS(errnum) == 0);
710 gchar *error_message = vstrallocf(_("DVDRW device cannot execute '%s': %s (status: %d) (stderr: %s)"),
711 argv[0], error ? error->message : _("Unknown error"), errnum, std_error ? std_error: "No stderr");
714 device_set_error(dself, error_message, DEVICE_STATUS_DEVICE_ERROR);
729 if (result != NULL) {
733 return DEVICE_STATUS_DEVICE_ERROR;
736 return DEVICE_STATUS_SUCCESS;