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.
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.
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.
24 * Author: Sam Couter <sam@couter.id.au>
28 #include "vfs-device.h"
31 * Type checking and casting macros
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)
40 /* Forward declaration */
41 static GType dvdrw_device_get_type(void);
44 * Main object structure
46 typedef struct _DvdRwDevice DvdRwDevice;
57 gboolean unlabelled_when_unmountable;
58 gchar *growisofs_command;
60 gchar *umount_command;
66 typedef struct _DvdRwDeviceClass DvdRwDeviceClass;
67 struct _DvdRwDeviceClass {
68 VfsDeviceClass __parent__;
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)
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)
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)
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)
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)
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)
95 /* GObject housekeeping */
97 dvdrw_device_register(void);
100 dvdrw_device_factory(char *device_name, char *device_type, char *device_node);
103 dvdrw_device_class_init (DvdRwDeviceClass *c);
106 dvdrw_device_init (DvdRwDevice *self);
110 dvdrw_device_set_mount_point_fn(Device *self,
111 DevicePropertyBase *base, GValue *val,
112 PropertySurety surety, PropertySource source);
115 dvdrw_device_set_keep_cache_fn(Device *self,
116 DevicePropertyBase *base, GValue *val,
117 PropertySurety surety, PropertySource source);
120 dvdrw_device_set_unlabelled_when_unmountable_fn(Device *self,
121 DevicePropertyBase *base, GValue *val,
122 PropertySurety surety, PropertySource source);
125 dvdrw_device_set_growisofs_command_fn(Device *self,
126 DevicePropertyBase *base, GValue *val,
127 PropertySurety surety, PropertySource source);
130 dvdrw_device_set_mount_command_fn(Device *self,
131 DevicePropertyBase *base, GValue *val,
132 PropertySurety surety, PropertySource source);
135 dvdrw_device_set_umount_command_fn(Device *self,
136 DevicePropertyBase *base, GValue *val,
137 PropertySurety surety, PropertySource source);
141 dvdrw_device_open_device(Device *dself, char *device_name, char *device_type, char *device_node);
143 static DeviceStatusFlags
144 dvdrw_device_read_label(Device *dself);
147 dvdrw_device_start(Device *dself, DeviceAccessMode mode, char *label, char *timestamp);
150 dvdrw_device_finish(Device *dself);
153 dvdrw_device_finalize(GObject *gself);
155 /* Helper functions */
157 check_access_mode(DvdRwDevice *self, DeviceAccessMode mode);
160 check_readable(DvdRwDevice *self);
162 static DeviceStatusFlags
163 mount_disc(DvdRwDevice *self, gboolean report_error);
166 unmount_disc(DvdRwDevice *self);
169 burn_disc(DvdRwDevice *self);
171 static DeviceStatusFlags
172 execute_command(DvdRwDevice *self, gchar **argv, gint *status);
175 dvdrw_device_get_type (void)
177 static GType type = 0;
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 */,
189 (GInstanceInitFunc) dvdrw_device_init,
193 type = g_type_register_static (TYPE_VFS_DEVICE, "DvdRwDevice",
194 &info, (GTypeFlags)0);
201 dvdrw_device_register(void)
203 const char *device_prefix_list[] = { "dvdrw", NULL };
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");
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");
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");
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");
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");
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");
229 register_device(dvdrw_device_factory, device_prefix_list);
233 dvdrw_device_factory(char *device_name, char *device_type, char *device_node)
237 g_assert(0 == strncmp(device_type, "dvdrw", strlen("dvdrw")));
239 device = DEVICE(g_object_new(TYPE_DVDRW_DEVICE, NULL));
240 device_open_device(device, device_name, device_type, device_node);
246 dvdrw_device_class_init (DvdRwDeviceClass *c)
248 DeviceClass *device_class = DEVICE_CLASS(c);
249 GObjectClass *g_object_class = G_OBJECT_CLASS(c);
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;
256 g_object_class->finalize = dvdrw_device_finalize;
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);
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);
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);
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);
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);
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);
290 dvdrw_device_init (DvdRwDevice *self)
292 Device *dself = DEVICE(self);
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;
306 bzero(&val, sizeof(val));
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);
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);
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);
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);
334 dvdrw_device_set_mount_point_fn(Device *dself, DevicePropertyBase *base,
335 GValue *val, PropertySurety surety, PropertySource source)
337 DvdRwDevice *self = DVDRW_DEVICE(dself);
339 amfree(self->mount_point);
340 amfree(self->mount_data);
342 self->mount_point = g_value_dup_string(val);
343 self->mount_data = g_strconcat(self->mount_point, "/data/", NULL);
345 device_clear_volume_details(dself);
347 return device_simple_property_set_fn(dself, base, val, surety, source);
351 dvdrw_device_set_keep_cache_fn(Device *dself, DevicePropertyBase *base,
352 GValue *val, PropertySurety surety, PropertySource source)
354 DvdRwDevice *self = DVDRW_DEVICE(dself);
356 self->keep_cache = g_value_get_boolean(val);
358 return device_simple_property_set_fn(dself, base, val, surety, source);
362 dvdrw_device_set_unlabelled_when_unmountable_fn(Device *dself, DevicePropertyBase *base,
363 GValue *val, PropertySurety surety, PropertySource source)
365 DvdRwDevice *self = DVDRW_DEVICE(dself);
367 self->unlabelled_when_unmountable = g_value_get_boolean(val);
369 return device_simple_property_set_fn(dself, base, val, surety, source);
373 dvdrw_device_set_growisofs_command_fn(Device *dself, DevicePropertyBase *base,
374 GValue *val, PropertySurety surety, PropertySource source)
376 DvdRwDevice *self = DVDRW_DEVICE(dself);
378 self->growisofs_command = g_value_dup_string(val);
380 return device_simple_property_set_fn(dself, base, val, surety, source);
384 dvdrw_device_set_mount_command_fn(Device *dself, DevicePropertyBase *base,
385 GValue *val, PropertySurety surety, PropertySource source)
387 DvdRwDevice *self = DVDRW_DEVICE(dself);
389 self->mount_command = g_value_dup_string(val);
391 return device_simple_property_set_fn(dself, base, val, surety, source);
395 dvdrw_device_set_umount_command_fn(Device *dself, DevicePropertyBase *base,
396 GValue *val, PropertySurety surety, PropertySource source)
398 DvdRwDevice *self = DVDRW_DEVICE(dself);
400 self->umount_command = g_value_dup_string(val);
402 return device_simple_property_set_fn(dself, base, val, surety, source);
406 dvdrw_device_open_device(Device *dself, char *device_name, char *device_type, char *device_node)
408 DvdRwDevice *self = DVDRW_DEVICE(dself);
409 DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
413 g_debug("Opening device: %s", device_node);
415 bzero(&val, sizeof(val));
417 colon = index(device_node, ':');
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);
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);
429 parent_class->open_device(dself, device_name, device_type, device_node);
432 static DeviceStatusFlags
433 dvdrw_device_read_label(Device *dself)
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)));
442 g_debug("Reading label from media at %s", self->mount_point);
444 if (device_in_error(dself)) return DEVICE_STATUS_DEVICE_ERROR;
445 if (!check_readable(self)) return DEVICE_STATUS_DEVICE_ERROR;
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
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");
463 return DEVICE_STATUS_VOLUME_UNLABELED;
466 amfree(vself->dir_name);
467 vself->dir_name = g_strdup(self->mount_data);
468 status = parent_class->read_label(dself);
478 dvdrw_device_start(Device *dself, DeviceAccessMode mode, char *label, char *timestamp)
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)));
484 g_debug("Start DVDRW device");
486 if (device_in_error(dself)) return FALSE;
487 if (!check_access_mode(self, mode)) return FALSE;
489 dself->access_mode = mode;
491 /* We'll replace this with our own value */
492 amfree(vself->dir_name);
494 if (mode == ACCESS_READ) {
495 if (mount_disc(self, TRUE) != DEVICE_STATUS_SUCCESS) {
499 vself->dir_name = g_strdup(self->mount_data);
500 } else if (mode == ACCESS_WRITE) {
501 vself->dir_name = g_strdup(self->cache_data);
504 return parent_class->start(dself, mode, label, timestamp);
508 dvdrw_device_finish(Device *dself)
510 DvdRwDevice *self = DVDRW_DEVICE(dself);
511 VfsDevice *vself = VFS_DEVICE(dself);
513 DeviceClass *parent_class = DEVICE_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(dself)));
514 DeviceAccessMode mode;
516 g_debug("Finish DVDRW device");
518 /* Save access mode before parent class messes with it */
519 mode = dself->access_mode;
521 result = parent_class->finish(dself);
523 if (mode == ACCESS_READ) {
527 if (!result || device_in_error(dself)) {
531 if (mode == ACCESS_WRITE) {
532 result = burn_disc(self);
534 if (result && !self->keep_cache) {
535 delete_vfs_files(vself);
545 burn_disc(DvdRwDevice *self)
549 char *burn_argv[] = {NULL, "-use-the-force-luke",
550 "-Z", self->dvdrw_device,
551 "-J", "-R", "-pad", "-quiet",
552 self->cache_dir, NULL};
554 if (self->growisofs_command == NULL) {
555 burn_argv[0] = "growisofs";
557 burn_argv[0] = self->growisofs_command;
560 g_debug("Burning media in %s", self->dvdrw_device);
561 if (execute_command(self, burn_argv, &status) != DEVICE_STATUS_SUCCESS) {
564 g_debug("Burn completed successfully");
570 dvdrw_device_finalize(GObject *gself)
572 DvdRwDevice *self = DVDRW_DEVICE(gself);
573 GObjectClass *parent_class = G_OBJECT_CLASS(g_type_class_peek_parent(DVDRW_DEVICE_GET_CLASS(gself)));
575 if (parent_class->finalize) {
576 parent_class->finalize(gself);
579 amfree(self->dvdrw_device);
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);
591 check_access_mode(DvdRwDevice *self, DeviceAccessMode mode)
593 Device *dself = DEVICE(self);
595 if (mode == ACCESS_READ) {
596 return check_readable(self);
597 } else if (mode == ACCESS_WRITE) {
601 device_set_error(dself,
602 stralloc(_("DVDRW device can only be opened in READ or WRITE mode")),
603 DEVICE_STATUS_DEVICE_ERROR);
609 check_readable(DvdRwDevice *self)
611 Device *dself = DEVICE(self);
613 bzero(&value, sizeof(value));
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);
626 static DeviceStatusFlags
627 mount_disc(DvdRwDevice *self, gboolean report_error)
629 Device *dself = DEVICE(self);
630 gchar *mount_argv[] = { NULL, self->mount_point, NULL };
631 DeviceStatusFlags status;
634 return DEVICE_STATUS_SUCCESS;
637 if (self->mount_command == NULL) {
638 mount_argv[0] = "mount";
640 mount_argv[0] = self->mount_command;
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 */
648 if (execute_command(report_error ? self : NULL, mount_argv, NULL) == DEVICE_STATUS_SUCCESS) {
650 device_set_error(dself, NULL, DEVICE_STATUS_SUCCESS);
651 self->mounted = TRUE;
652 return DEVICE_STATUS_SUCCESS;
658 self->mounted = TRUE;
659 return DEVICE_STATUS_SUCCESS;
663 unmount_disc(DvdRwDevice *self)
665 gchar *unmount_argv[] = { NULL, self->mount_point, NULL };
666 DeviceStatusFlags status;
668 if (! self->mounted) {
672 if (self->umount_command == NULL) {
673 unmount_argv[0] = "umount";
675 unmount_argv[0] = self->umount_command;
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;
685 static DeviceStatusFlags
686 execute_command(DvdRwDevice *self, gchar **argv, gint *result)
688 Device *dself = DEVICE(self);
689 gchar *std_output = NULL;
690 gchar *std_error = NULL;
692 GError *error = NULL;
695 /* g_debug("Executing: %s", argv[0]); */
697 g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
698 &std_output, &std_error, &errnum, &error);
700 /* g_debug("Execution complete"); */
702 if (WIFSIGNALED(errnum)) {
704 } else if (WIFEXITED(errnum)) {
705 success = (WEXITSTATUS(errnum) == 0);
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");
715 device_set_error(dself, error_message, DEVICE_STATUS_DEVICE_ERROR);
730 if (result != NULL) {
734 return DEVICE_STATUS_DEVICE_ERROR;
737 return DEVICE_STATUS_SUCCESS;