3 * dvd.c -- Routines specific to DVD devices (and
4 * possibly other removable hard media).
11 Copyright (C) 2005 Kern Sibbald
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License
15 version 2 as amended with additional clauses defined in the
16 file LICENSE in the main source directory.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 the file LICENSE for additional details.
28 /* Forward referenced functions */
29 static char *edit_device_codes_dev(DEVICE *dev, char *omsg, const char *imsg);
30 static bool do_mount_dev(DEVICE* dev, int mount, int dotimeout);
31 static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name);
34 * Write the current volume/part filename to archive_name.
36 void make_mounted_dvd_filename(DEVICE *dev, POOL_MEM &archive_name)
38 pm_strcpy(archive_name, dev->device->mount_point);
39 add_file_and_part_name(dev, archive_name);
40 dev->set_part_spooled(false);
43 void make_spooled_dvd_filename(DEVICE *dev, POOL_MEM &archive_name)
45 /* Use the working directory if spool directory is not defined */
46 if (dev->device->spool_directory) {
47 pm_strcpy(archive_name, dev->device->spool_directory);
49 pm_strcpy(archive_name, working_directory);
51 add_file_and_part_name(dev, archive_name);
52 dev->set_part_spooled(true);
55 static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name)
58 if (archive_name.c_str()[strlen(archive_name.c_str())-1] != '/') {
59 pm_strcat(archive_name, "/");
62 pm_strcat(archive_name, dev->VolCatInfo.VolCatName);
63 /* if part > 1, append .# to the filename (where # is the part number) */
65 pm_strcat(archive_name, ".");
66 bsnprintf(partnumber, sizeof(partnumber), "%d", dev->part);
67 pm_strcat(archive_name, partnumber);
69 Dmsg1(100, "Exit make_dvd_filename: arch=%s\n", archive_name.c_str());
73 * If timeout, wait until the mount command returns 0.
74 * If !timeout, try to mount the device only once.
76 bool mount_dev(DEVICE* dev, int timeout)
78 Dmsg0(900, "Enter mount_dev\n");
79 if (dev->is_mounted()) {
81 } else if (dev->requires_mount()) {
82 return do_mount_dev(dev, 1, timeout);
88 * If timeout, wait until the unmount command returns 0.
89 * If !timeout, try to unmount the device only once.
91 bool unmount_dev(DEVICE *dev, int timeout)
93 Dmsg0(900, "Enter unmount_dev\n");
94 if (dev->is_mounted()) {
95 return do_mount_dev(dev, 0, timeout);
100 /* (Un)mount the device */
101 static bool do_mount_dev(DEVICE* dev, int mount, int dotimeout)
103 POOL_MEM ocmd(PM_FNAME);
109 if (dev->is_mounted()) {
112 icmd = dev->device->mount_command;
114 if (!dev->is_mounted()) {
117 icmd = dev->device->unmount_command;
120 edit_device_codes_dev(dev, ocmd.c_str(), icmd);
122 Dmsg2(200, "do_mount_dev: cmd=%s mounted=%d\n", ocmd.c_str(), !!dev->is_mounted());
125 /* Try at most 1 time to (un)mount the device. This should perhaps be configurable. */
130 results = get_memory(2000);
132 /* If busy retry each second */
133 while ((status = run_program_full_output(ocmd.c_str(),
134 dev->max_open_wait/2, results)) != 0) {
135 if (fnmatch("*is already mounted on", results, 0) == 0) {
139 /* Sometimes the device cannot be mounted because it is already mounted.
140 * Try to unmount it, then remount it */
142 Dmsg1(400, "Trying to unmount the device %s...\n", dev->print_name());
143 do_mount_dev(dev, 0, 0);
148 Dmsg2(40, "Device %s cannot be mounted. ERR=%s\n", dev->print_name(), results);
149 Mmsg(dev->errmsg, "Device %s cannot be mounted. ERR=%s\n",
150 dev->print_name(), results);
152 * Now, just to be sure it is not mounted, try to read the
156 struct dirent *entry, *result;
160 name_max = pathconf(".", _PC_NAME_MAX);
161 if (name_max < 1024) {
165 if (!(dp = opendir(dev->device->mount_point))) {
167 dev->dev_errno = errno;
168 Dmsg3(29, "open_mounted_dev: failed to open dir %s (dev=%s), ERR=%s\n", dev->device->mount_point, dev->dev_name, be.strerror());
172 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
174 if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
175 dev->dev_errno = ENOENT;
176 Dmsg2(29, "open_mounted_dev: failed to find suitable file in dir %s (dev=%s)\n", dev->device->mount_point, dev->dev_name);
184 mount = 1; /* If we got more than . and .. */
185 break; /* there must be something mounted */
188 free_pool_memory(results);
192 dev->set_mounted(mount); /* set/clear mounted flag */
193 free_pool_memory(results);
197 /* Update the free space on the device */
198 void update_free_space_dev(DEVICE* dev)
200 POOL_MEM ocmd(PM_FNAME);
207 icmd = dev->device->free_space_command;
211 dev->free_space_errno = 0;
213 Dmsg2(29, "update_free_space_dev: free_space=%d, free_space_errno=%d (!icmd)\n", dev->free_space, dev->free_space_errno);
217 edit_device_codes_dev(dev, ocmd.c_str(), icmd);
219 Dmsg1(29, "update_free_space_dev: cmd=%s\n", ocmd.c_str());
221 results = get_pool_memory(PM_MESSAGE);
224 /* Try at most 3 times to get the free space on the device. This should perhaps be configurable. */
228 if (run_program_full_output(ocmd.c_str(), dev->max_open_wait/2, results) == 0) {
229 Dmsg1(100, "Free space program run : %s\n", results);
230 free = str_to_int64(results);
232 dev->free_space = free;
233 dev->free_space_errno = 1;
235 Mmsg0(dev->errmsg, "");
240 dev->free_space_errno = -EPIPE;
241 Mmsg1(dev->errmsg, "Cannot run free space command (%s)\n", results);
244 Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
245 "free_space_errno=%d ERR=%s\n", dev->dev_name,
246 edit_uint64(dev->free_space, ed1), dev->free_space_errno,
252 dev->dev_errno = -dev->free_space_errno;
253 Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
254 "free_space_errno=%d ERR=%s\n",
255 dev->dev_name, edit_uint64(dev->free_space, ed1),
256 dev->free_space_errno, dev->errmsg);
260 free_pool_memory(results);
261 Dmsg3(29, "update_free_space_dev: free_space=%s, free_space_errno=%d have_media=%d\n",
262 edit_uint64(dev->free_space, ed1), dev->free_space_errno, dev->have_media());
267 * Write a part (Vol, Vol.1, ...) from the spool to the DVD
269 static bool dvd_write_part(DCR *dcr)
271 DEVICE *dev = dcr->dev;
272 Dmsg1(29, "dvd_write_part: device is %s\n", dev->print_name());
274 if (!unmount_dev(dev, 1)) {
275 Dmsg0(29, "dvd_write_part: unable to unmount the device\n");
278 POOL_MEM ocmd(PM_FNAME);
279 POOL_MEM results(PM_MESSAGE);
286 results.c_str()[0] = 0;
287 icmd = dev->device->write_part_command;
290 * Note! part is used to control whether or not we create a
291 * new filesystem. If the device could be mounted, it is because
292 * it already has a filesystem, so we artificially set part=1
293 * to avoid zapping an existing filesystem.
296 if (dev->is_mounted() && dev->part < 2) {
297 dev->part = 2; /* do not wipe out existing filesystem */
299 edit_device_codes_dev(dev, ocmd.c_str(), icmd);
303 * Wait at most the time a maximum size part is written in DVD 0.5x speed
304 * FIXME: Minimum speed should be in device configuration
306 timeout = dev->max_open_wait + (dev->max_part_size/(1350*1024/2));
308 Dmsg2(29, "dvd_write_part: cmd=%s timeout=%d\n", ocmd.c_str(), timeout);
310 status = run_program_full_output(ocmd.c_str(), timeout, results.c_str());
312 Mmsg1(dev->errmsg, "Error while writing current part to the DVD: %s",
314 Dmsg1(000, "%s", dev->errmsg);
315 dev->dev_errno = EIO;
319 POOL_MEM archive_name(PM_FNAME);
320 /* Delete spool file */
321 make_spooled_dvd_filename(dev, archive_name);
322 unlink(archive_name.c_str());
323 Dmsg1(29, "unlink(%s)\n", archive_name.c_str());
324 update_free_space_dev(dev);
325 Jmsg(dcr->jcr, M_INFO, 0, _("Remaining free space %s on %s\n"),
326 edit_uint64_with_commas(dev->free_space, ed1), dev->print_name());
330 /* Open the next part file.
332 * - Increment part number
333 * - Reopen the device
335 int open_next_part(DCR *dcr)
337 DEVICE *dev = dcr->dev;
339 Dmsg5(29, "Enter: ==== open_next_part part=%d npart=%d dev=%s vol=%s mode=%d\n",
340 dev->part, dev->num_parts, dev->print_name(),
341 dev->VolCatInfo.VolCatName, dev->openmode);
342 if (!dev->is_dvd()) {
343 Dmsg1(000, "Device %s is not dvd!!!!\n", dev->print_name());
347 /* When appending, do not open a new part if the current is empty */
348 if (dev->can_append() && (dev->part >= dev->num_parts) &&
349 (dev->part_size == 0)) {
350 Dmsg0(29, "open_next_part exited immediately (dev->part_size == 0).\n");
362 * If we have a part open for write, then write it to
363 * DVD before opening the next part.
365 if (dev->is_dvd() && (dev->part >= dev->num_parts) && dev->can_append()) {
366 if (!dvd_write_part(dcr)) {
371 if (dev->part > dev->num_parts) {
372 Dmsg2(000, "In open_next_part: part=%d nump=%d\n", dev->part, dev->num_parts);
373 ASSERT(dev->part <= dev->num_parts);
375 dev->part_start += dev->part_size;
378 Dmsg2(29, "part=%d num_parts=%d\n", dev->part, dev->num_parts);
379 /* I think this dev->can_append() should not be there */
380 if ((dev->num_parts < dev->part) && dev->can_append()) {
381 POOL_MEM archive_name(PM_FNAME);
384 * First check what is on DVD. If out part is there, we
385 * are in trouble, so bail out.
387 make_mounted_dvd_filename(dev, archive_name); /* makes dvd name */
388 if (stat(archive_name.c_str(), &buf) == 0) {
389 /* bad news bail out */
390 Mmsg1(&dev->errmsg, _("Next Volume part already exists on DVD. Cannot continue: %s\n"),
391 archive_name.c_str());
395 dev->num_parts = dev->part;
396 dev->VolCatInfo.VolCatParts = dev->part;
397 make_spooled_dvd_filename(dev, archive_name); /* makes spool name */
399 /* Check if the next part exists in spool directory . */
400 if ((stat(archive_name.c_str(), &buf) == 0) || (errno != ENOENT)) {
401 Dmsg1(29, "open_next_part %s is in the way, moving it away...\n", archive_name.c_str());
402 /* Then try to unlink it */
403 if (unlink(archive_name.c_str()) < 0) {
405 dev->dev_errno = errno;
406 Mmsg2(dev->errmsg, _("open_next_part can't unlink existing part %s, ERR=%s\n"),
407 archive_name.c_str(), be.strerror());
412 if (dev->num_parts < dev->part) {
413 dev->num_parts = dev->part;
414 dev->VolCatInfo.VolCatParts = dev->part;
416 Dmsg2(50, "Call dev->open(vol=%s, mode=%d\n", dev->VolCatInfo.VolCatName,
419 if (dev->open(dcr, dev->openmode) < 0) {
422 dev->set_labeled(); /* all next parts are "labeled" */
426 /* Open the first part file.
428 * - Reopen the device
430 * I don't see why this is necessary unless the current
433 int open_first_part(DCR *dcr, int mode)
435 DEVICE *dev = dcr->dev;
437 Dmsg3(29, "Enter: ==== open_first_part dev=%s Vol=%s mode=%d\n", dev->dev_name,
438 dev->VolCatInfo.VolCatName, dev->openmode);
449 Dmsg2(50, "Call dev->open(vol=%s, mode=%d)\n", dcr->VolCatInfo.VolCatName,
451 if (dev->open(dcr, mode) < 0) {
452 Dmsg0(50, "open dev() failed\n");
455 Dmsg1(50, "Leave open_first_part state=%s\n", dev->is_open()?"open":"not open");
460 /* Protected version of lseek, which opens the right part if necessary */
461 off_t lseek_dev(DEVICE *dev, off_t offset, int whence)
466 Dmsg3(100, "Enter lseek_dev fd=%d part=%d nparts=%d\n", dev->fd,
467 dev->part, dev->num_parts);
468 if (!dev->is_dvd()) {
469 Dmsg0(100, "Using sys lseek\n");
470 return lseek(dev->fd, offset, whence);
473 dcr = (DCR *)dev->attached_dcrs->first(); /* any dcr will do */
476 Dmsg1(100, "lseek_dev SEEK_SET to %d\n", (int)offset);
477 if ((uint64_t)offset >= dev->part_start) {
478 offset -= dev->part_start; /* adjust for start of this part */
479 if (offset == 0 || (uint64_t)offset < dev->part_size) {
480 /* We are staying in the current part, just seek */
481 if ((pos = lseek(dev->fd, offset, SEEK_SET)) < 0) {
484 return pos + dev->part_start;
487 /* Load next part, and start again */
488 if (open_next_part(dcr) < 0) {
489 Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
492 return lseek_dev(dev, offset, SEEK_SET);
496 * pos < dev->part_start :
497 * We need to access a previous part,
498 * so just load the first one, and seek again
499 * until the right one is loaded
501 if (open_first_part(dcr, dev->openmode) < 0) {
502 Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
505 return lseek_dev(dev, offset, SEEK_SET);
509 Dmsg1(100, "lseek_dev SEEK_CUR to %d\n", (int)offset);
510 if ((pos = lseek(dev->fd, (off_t)0, SEEK_CUR)) < 0) {
513 pos += dev->part_start;
516 } else { /* Not used in Bacula, but should work */
517 return lseek_dev(dev, pos, SEEK_SET);
521 Dmsg1(100, "lseek_dev SEEK_END to %d\n", (int)offset);
523 * Bacula does not use offsets for SEEK_END
524 * Also, Bacula uses seek_end only when it wants to
525 * append to the volume, so for a dvd that means
526 * that the volume must be spooled since the DVD
527 * itself is read-only (as currently implemented).
529 if (offset > 0) { /* Not used by bacula */
530 Dmsg1(100, "lseek_dev SEEK_END called with an invalid offset %d\n", (int)offset);
534 /* If we are already on a spooled part and have the
535 * right part number, simply seek
537 if (dev->is_part_spooled() && dev->part == dev->num_parts) {
538 if ((pos = lseek(dev->fd, (off_t)0, SEEK_END)) < 0) {
541 return pos + dev->part_start;
545 * Load the first part, then load the next until we reach the last one.
546 * This is the only way to be sure we compute the right file address.
548 * Save previous openmode, and open all but last part read-only
551 int modesave = dev->openmode;
552 /* Works because num_parts > 0. */
553 if (open_first_part(dcr, OPEN_READ_ONLY) < 0) {
554 Dmsg0(100, "lseek_dev failed while trying to open the first part\n");
557 while (dev->part < (dev->num_parts-1)) {
558 if (open_next_part(dcr) < 0) {
559 Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
563 dev->openmode = modesave;
564 if (open_next_part(dcr) < 0) {
565 Dmsg0(100, "lseek_dev failed while trying to open the next part\n");
568 return lseek_dev(dev, 0, SEEK_END);
577 bool dvd_close_job(DCR *dcr)
579 DEVICE *dev = dcr->dev;
583 /* If the device is a dvd and WritePartAfterJob
584 * is set to yes, open the next part, so, in case of a device
585 * that requires mount, it will be written to the device.
587 if (dev->is_dvd() && jcr->write_part_after_job && (dev->part_size > 0)) {
588 Dmsg1(100, "Writing last part=%d write_partafter_job is set.\n",
590 if (dev->part < dev->num_parts) {
591 Jmsg3(jcr, M_FATAL, 0, _("Error while writing, current part number is less than the total number of parts (%d/%d, device=%s)\n"),
592 dev->part, dev->num_parts, dev->print_name());
593 dev->dev_errno = EIO;
597 /* This should be !dvd_write_part(dcr) */
598 // if (ok && open_next_part(dcr) < 0) {
599 if (ok && !dvd_write_part(dcr)) {
600 Jmsg2(jcr, M_FATAL, 0, _("Unable to write part %s: ERR=%s\n"),
601 dev->print_name(), strerror_dev(dev));
602 dev->dev_errno = EIO;
606 Dmsg1(200, "Set VolCatParts=%d\n", dev->num_parts);
607 dev->VolCatInfo.VolCatParts = dev->num_parts;
613 * Edit codes into (Un)MountCommand, Write(First)PartCommand
615 * %a = archive device name
617 * %v = last part name
619 * omsg = edited output message
620 * imsg = input string containing edit codes (%x)
623 static char *edit_device_codes_dev(DEVICE* dev, char *omsg, const char *imsg)
629 POOL_MEM archive_name(PM_FNAME);
632 Dmsg1(800, "edit_device_codes: %s\n", imsg);
633 for (p=imsg; *p; p++) {
640 bsnprintf(add, sizeof(add), "%d", dev->part);
647 str = dev->device->mount_point;
650 make_spooled_dvd_filename(dev, archive_name);
651 str = archive_name.c_str();
665 Dmsg1(1900, "add_str %s\n", str);
666 pm_strcat(&omsg, (char *)str);
667 Dmsg1(1800, "omsg=%s\n", omsg);