3 * dvd.c -- Routines specific to DVD devices (and
4 * possibly other removable hard media).
11 Copyright (C) 2005-2006 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 void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name);
32 * Write the current volume/part filename to archive_name.
34 void make_mounted_dvd_filename(DEVICE *dev, POOL_MEM &archive_name)
36 pm_strcpy(archive_name, dev->device->mount_point);
37 add_file_and_part_name(dev, archive_name);
40 void make_spooled_dvd_filename(DEVICE *dev, POOL_MEM &archive_name)
42 /* Use the working directory if spool directory is not defined */
43 if (dev->device->spool_directory) {
44 pm_strcpy(archive_name, dev->device->spool_directory);
46 pm_strcpy(archive_name, working_directory);
48 add_file_and_part_name(dev, archive_name);
51 static void add_file_and_part_name(DEVICE *dev, POOL_MEM &archive_name)
55 if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) {
56 pm_strcat(archive_name, "/");
59 pm_strcat(archive_name, dev->VolCatInfo.VolCatName);
60 /* if part > 1, append .# to the filename (where # is the part number) */
62 pm_strcat(archive_name, ".");
63 bsnprintf(partnumber, sizeof(partnumber), "%d", dev->part);
64 pm_strcat(archive_name, partnumber);
66 Dmsg2(400, "Exit add_file_part_name: arch=%s, part=%d\n",
67 archive_name.c_str(), dev->part);
70 /* Update the free space on the device */
71 bool DEVICE::update_freespace()
73 POOL_MEM ocmd(PM_FNAME);
82 if (!is_dvd() || is_freespace_ok()) {
86 /* The device must be mounted in order to dvd-freespace to work */
89 sm_check(__FILE__, __LINE__, false);
90 icmd = device->free_space_command;
95 clear_freespace_ok(); /* No valid freespace */
97 Dmsg2(29, "ERROR: update_free_space_dev: free_space=%s, free_space_errno=%d (!icmd)\n",
98 edit_uint64(free_space, ed1), free_space_errno);
99 Mmsg(errmsg, _("No FreeSpace command defined.\n"));
103 edit_mount_codes(ocmd, icmd);
105 Dmsg1(29, "update_freespace: cmd=%s\n", ocmd.c_str());
107 results = get_pool_memory(PM_MESSAGE);
109 /* Try at most 3 times to get the free space on the device. This should perhaps be configurable. */
114 Dmsg1(20, "Run freespace prog=%s\n", ocmd.c_str());
115 status = run_program_full_output(ocmd.c_str(), max_open_wait/2, results);
116 Dmsg2(500, "Freespace status=%d result=%s\n", status, results);
118 free = str_to_int64(results);
119 Dmsg1(400, "Free space program run: Freespace=%s\n", results);
122 free_space_errno = 0;
123 set_freespace_ok(); /* have valid freespace */
131 free_space_errno = EPIPE;
132 clear_freespace_ok(); /* no valid freespace */
133 Mmsg2(errmsg, _("Cannot run free space command. Results=%s ERR=%s\n"),
134 results, be.strerror(status));
137 Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
138 "free_space_errno=%d ERR=%s\n", print_name(),
139 edit_uint64(free_space, ed1), free_space_errno,
145 dev_errno = free_space_errno;
146 Dmsg4(40, "Cannot get free space on device %s. free_space=%s, "
147 "free_space_errno=%d ERR=%s\n",
148 print_name(), edit_uint64(free_space, ed1),
149 free_space_errno, errmsg);
153 free_pool_memory(results);
154 Dmsg4(29, "leave update_freespace: free_space=%s freespace_ok=%d free_space_errno=%d have_media=%d\n",
155 edit_uint64(free_space, ed1), !!is_freespace_ok(), free_space_errno, !!have_media());
156 sm_check(__FILE__, __LINE__, false);
161 * Note!!!! Part numbers now begin at 1. The part number is
162 * suppressed from the first part, which is just the Volume
163 * name. Each subsequent part is the Volumename.partnumber.
165 * Write a part (Vol, Vol.2, ...) from the spool to the DVD
166 * This routine does not update the part number, so normally, you
167 * should call open_next_part()
169 * It is also called from truncate_dvd to "blank" the medium, as
170 * well as from block.c when the DVD is full to write the last part.
172 bool dvd_write_part(DCR *dcr)
174 DEVICE *dev = dcr->dev;
175 POOL_MEM archive_name(PM_FNAME);
178 * Don't write empty part files.
179 * This is only useful when growisofs does not support write beyond
182 * - 3.9 GB on the volume, dvd-freespace reports 0.4 GB free
183 * - Write 0.2 GB on the volume, Bacula thinks it could still
184 * append data, it creates a new empty part.
185 * - dvd-freespace reports 0 GB free, as the 4GB boundary has
187 * - Bacula thinks he must finish to write to the device, so it
188 * tries to write the last part (0-byte), but dvd-writepart fails...
190 * There is one exception: when recycling a volume, we write a blank part
191 * file, so, then, we need to accept to write it.
193 if (dev->part_size == 0 && !dev->truncating) {
194 Dmsg2(29, "dvd_write_part: device is %s, won't write blank part %d\n", dev->print_name(), dev->part);
195 /* Delete spool file */
196 make_spooled_dvd_filename(dev, archive_name);
197 unlink(archive_name.c_str());
198 dev->set_part_spooled(false);
199 Dmsg1(29, "========= unlink(%s)\n", archive_name.c_str());
200 sm_check(__FILE__, __LINE__, false);
204 POOL_MEM ocmd(PM_FNAME);
205 POOL_MEM results(PM_MESSAGE);
211 dev->clear_freespace_ok(); /* need to update freespace */
213 sm_check(__FILE__, __LINE__, false);
214 Dmsg3(29, "dvd_write_part: device is %s, part is %d, is_mounted=%d\n", dev->print_name(), dev->part, dev->is_mounted());
215 icmd = dev->device->write_part_command;
217 dev->edit_mount_codes(ocmd, icmd);
220 * original line follows
221 * timeout = dev->max_open_wait + (dev->max_part_size/(1350*1024/2));
222 * I modified this for a longer timeout; pre-formatting, blanking and
223 * writing can take quite a while
226 /* Explanation of the timeout value, when writing the first part,
228 * 9 GB, write speed 1x: 6990 seconds (almost 2 hours...)
229 * Overhead: 900 seconds (starting, initializing, finalizing,probably
230 * reloading 15 minutes)
232 * A reasonable last-exit timeout would be 16000 seconds. Quite long -
233 * almost 4.5 hours, but hopefully, that timeout will only ever be needed
234 * in case of a serious emergency.
237 if (dev->part == 1) {
240 timeout = dev->max_open_wait + (dev->part_size/(1350*1024/4));
243 Dmsg2(20, "Write part: cmd=%s timeout=%d\n", ocmd.c_str(), timeout);
244 status = run_program_full_output(ocmd.c_str(), timeout, results.c_str());
245 Dmsg2(20, "Write part status=%d result=%s\n", status, results.c_str());
247 dev->blank_dvd = false;
249 Jmsg2(dcr->jcr, M_FATAL, 0, _("Error writing part %d to the DVD: ERR=%s\n"),
250 dev->part, results.c_str());
251 Mmsg1(dev->errmsg, _("Error while writing current part to the DVD: %s"),
253 Dmsg1(100, "%s\n", dev->errmsg);
254 dev->dev_errno = EIO;
255 if (!dev->truncating) {
256 mark_volume_in_error(dcr);
258 sm_check(__FILE__, __LINE__, false);
261 Jmsg(dcr->jcr, M_INFO, 0, _("Part %d (%lld bytes) written to DVD.\n"), dev->part, dev->part_size);
262 Dmsg3(400, "dvd_write_part: Part %d (%lld bytes) written to DVD\nResults: %s\n",
263 dev->part, dev->part_size, results.c_str());
265 dev->num_dvd_parts++; /* there is now one more part on DVD */
266 dev->VolCatInfo.VolCatParts = dev->num_dvd_parts;
267 dcr->VolCatInfo.VolCatParts = dev->num_dvd_parts;
268 Dmsg1(100, "Update num_parts=%d\n", dev->num_dvd_parts);
270 /* Delete spool file */
271 make_spooled_dvd_filename(dev, archive_name);
272 unlink(archive_name.c_str());
273 dev->set_part_spooled(false);
274 Dmsg1(29, "========= unlink(%s)\n", archive_name.c_str());
275 sm_check(__FILE__, __LINE__, false);
277 /* growisofs umounted the device, so remount it (it will update the free space) */
278 dev->clear_mounted();
280 Jmsg(dcr->jcr, M_INFO, 0, _("Remaining free space %s on %s\n"),
281 edit_uint64_with_commas(dev->free_space, ed1), dev->print_name());
282 sm_check(__FILE__, __LINE__, false);
287 * Open the next part file.
289 * - Increment part number
290 * - Reopen the device
292 int dvd_open_next_part(DCR *dcr)
294 DEVICE *dev = dcr->dev;
296 Dmsg6(29, "Enter: == open_next_part part=%d npart=%d dev=%s vol=%s mode=%d file_addr=%d\n",
297 dev->part, dev->num_dvd_parts, dev->print_name(),
298 dev->VolCatInfo.VolCatName, dev->openmode, dev->file_addr);
299 if (!dev->is_dvd()) {
300 Dmsg1(100, "Device %s is not dvd!!!!\n", dev->print_name());
304 /* When appending, do not open a new part if the current is empty */
305 if (dev->can_append() && (dev->part > dev->num_dvd_parts) &&
306 (dev->part_size == 0)) {
307 Dmsg0(29, "open_next_part exited immediately (dev->part_size == 0).\n");
311 dev->close_part(dcr); /* close current part */
314 * If we have a spooled part open, write it to the
315 * DVD before opening the next part.
317 if (dev->is_part_spooled()) {
318 Dmsg2(100, "Before open next write previous. part=%d num_parts=%d\n",
319 dev->part, dev->num_dvd_parts);
320 if (!dvd_write_part(dcr)) {
321 Dmsg0(29, "Error in dvd_write part.\n");
326 dev->part_start += dev->part_size;
328 Dmsg2(29, "Inc part=%d num_dvd_parts=%d\n", dev->part, dev->num_dvd_parts);
330 /* Are we working on a part past what is written in the DVD? */
331 if (dev->num_dvd_parts < dev->part) {
332 POOL_MEM archive_name(PM_FNAME);
335 * First check what is on DVD. If our part is there, we
336 * are in trouble, so bail out.
337 * NB: This is however not a problem if we are writing the first part.
338 * It simply means that we are over writing an existing volume...
340 if (dev->num_dvd_parts > 0) {
341 make_mounted_dvd_filename(dev, archive_name); /* makes dvd name */
342 Dmsg1(100, "Check if part on DVD: %s\n", archive_name.c_str());
343 if (stat(archive_name.c_str(), &buf) == 0) {
344 /* bad news bail out */
345 dev->set_part_spooled(false);
346 Mmsg1(&dev->errmsg, _("Next Volume part already exists on DVD. Cannot continue: %s\n"),
347 archive_name.c_str());
353 Dmsg2(400, "num_dvd_parts=%d part=%d\n", dev->num_dvd_parts, dev->part);
354 make_spooled_dvd_filename(dev, archive_name); /* makes spool name */
356 /* Check if the next part exists in spool directory . */
357 Dmsg1(100, "Check if part on spool: %s\n", archive_name.c_str());
358 if ((stat(archive_name.c_str(), &buf) == 0) || (errno != ENOENT)) {
359 Dmsg1(29, "======= Part %s is in the way, deleting it...\n", archive_name.c_str());
360 /* Then try to unlink it */
361 if (unlink(archive_name.c_str()) < 0) {
363 dev->set_part_spooled(false);
364 dev->dev_errno = errno;
365 Mmsg2(dev->errmsg, _("open_next_part can't unlink existing part %s, ERR=%s\n"),
366 archive_name.c_str(), be.strerror());
373 Dmsg2(400, "Call dev->open(vol=%s, mode=%d)\n", dcr->VolCatInfo.VolCatName,
376 /* Open next part. Note, this sets part_size for part opened. */
377 if (dev->open(dcr, OPEN_READ_ONLY) < 0) {
380 dev->set_labeled(); /* all next parts are "labeled" */
386 * Open the first part file.
388 * - Reopen the device
390 static bool dvd_open_first_part(DCR *dcr, int mode)
392 DEVICE *dev = dcr->dev;
394 Dmsg5(29, "Enter: ==== open_first_part dev=%s Vol=%s mode=%d num_dvd_parts=%d append=%d\n", dev->print_name(),
395 dev->VolCatInfo.VolCatName, dev->openmode, dev->num_dvd_parts, dev->can_append());
398 dev->close_part(dcr);
400 Dmsg2(400, "Call dev->open(vol=%s, mode=%d)\n", dcr->VolCatInfo.VolCatName,
402 Dmsg0(100, "Set part=1\n");
406 if (dev->open(dcr, mode) < 0) {
407 Dmsg0(400, "open dev() failed\n");
410 Dmsg2(400, "Leave open_first_part state=%s append=%d\n", dev->is_open()?"open":"not open", dev->can_append());
417 * Do an lseek on a DVD handling all the different parts
419 boffset_t lseek_dvd(DCR *dcr, boffset_t offset, int whence)
421 DEVICE *dev = dcr->dev;
423 char ed1[50], ed2[50];
425 Dmsg5(400, "Enter lseek_dvd fd=%d off=%s w=%d part=%d nparts=%d\n", dev->fd,
426 edit_int64(offset, ed1), whence, dev->part, dev->num_dvd_parts);
430 Dmsg2(400, "lseek_dvd SEEK_SET to %s (part_start=%s)\n",
431 edit_int64(offset, ed1), edit_uint64(dev->part_start, ed2));
432 if ((uint64_t)offset >= dev->part_start) {
433 if ((uint64_t)offset == dev->part_start ||
434 (uint64_t)offset < dev->part_start+dev->part_size) {
435 /* We are staying in the current part, just seek */
436 #if defined(HAVE_WIN32)
437 pos = _lseeki64(dev->fd, offset-dev->part_start, SEEK_SET);
439 pos = lseek(dev->fd, offset-dev->part_start, SEEK_SET);
444 return pos + dev->part_start;
447 /* Load next part, and start again */
448 Dmsg0(100, "lseek open next part\n");
449 if (dvd_open_next_part(dcr) < 0) {
450 Dmsg0(400, "lseek_dvd failed while trying to open the next part\n");
453 Dmsg2(100, "Recurse lseek after open next part=%d num_part=%d\n",
454 dev->part, dev->num_dvd_parts);
455 return lseek_dvd(dcr, offset, SEEK_SET);
459 * pos < dev->part_start :
460 * We need to access a previous part,
461 * so just load the first one, and seek again
462 * until the right one is loaded
464 Dmsg0(100, "lseek open first part\n");
465 if (!dvd_open_first_part(dcr, dev->openmode)) {
466 Dmsg0(400, "lseek_dvd failed while trying to open the first part\n");
469 Dmsg2(100, "Recurse lseek after open first part=%d num_part=%d\n",
470 dev->part, dev->num_dvd_parts);
471 return lseek_dvd(dcr, offset, SEEK_SET); /* system lseek */
475 Dmsg1(400, "lseek_dvd SEEK_CUR to %s\n", edit_int64(offset, ed1));
476 if ((pos = lseek(dev->fd, (off_t)0, SEEK_CUR)) < 0) {
477 Dmsg0(400, "Seek error.\n");
480 pos += dev->part_start;
482 Dmsg1(400, "lseek_dvd SEEK_CUR returns %s\n", edit_uint64(pos, ed1));
485 Dmsg1(400, "do lseek_dvd SEEK_SET %s\n", edit_uint64(pos, ed1));
486 return lseek_dvd(dcr, pos, SEEK_SET);
490 Dmsg1(400, "lseek_dvd SEEK_END to %s\n", edit_int64(offset, ed1));
492 * Bacula does not use offsets for SEEK_END
493 * Also, Bacula uses seek_end only when it wants to
494 * append to the volume, so for a dvd that means
495 * that the volume must be spooled since the DVD
496 * itself is read-only (as currently implemented).
498 if (offset > 0) { /* Not used by bacula */
499 Dmsg1(400, "lseek_dvd SEEK_END called with an invalid offset %s\n",
500 edit_uint64(offset, ed1));
504 /* If we are already on a spooled part and have the
505 * right part number, simply seek
507 if (dev->is_part_spooled() && dev->part > dev->num_dvd_parts) {
508 if ((pos = lseek(dev->fd, (off_t)0, SEEK_END)) < 0) {
511 Dmsg1(400, "lseek_dvd SEEK_END returns %s\n",
512 edit_uint64(pos + dev->part_start, ed1));
513 return pos + dev->part_start;
517 * Load the first part, then load the next until we reach the last one.
518 * This is the only way to be sure we compute the right file address.
520 * Save previous openmode, and open all but last part read-only
523 int modesave = dev->openmode;
524 if (!dvd_open_first_part(dcr, OPEN_READ_ONLY)) {
525 Dmsg0(400, "lseek_dvd failed while trying to open the first part\n");
528 if (dev->num_dvd_parts > 0) {
529 while (dev->part < dev->num_dvd_parts) {
530 if (dvd_open_next_part(dcr) < 0) {
531 Dmsg0(400, "lseek_dvd failed while trying to open the next part\n");
535 dev->openmode = modesave;
536 if (dvd_open_next_part(dcr) < 0) {
537 Dmsg0(400, "lseek_dvd failed while trying to open the next part\n");
541 return lseek_dvd(dcr, 0, SEEK_END);
545 Dmsg0(400, "Seek call error.\n");
551 bool dvd_close_job(DCR *dcr)
553 DEVICE *dev = dcr->dev;
558 * If the device is a dvd and WritePartAfterJob
559 * is set to yes, open the next part, so, in case of a device
560 * that requires mount, it will be written to the device.
562 if (dev->is_dvd() && jcr->write_part_after_job && (dev->part_size > 0)) {
563 Dmsg1(400, "Writing last part=%d write_partafter_job is set.\n",
565 if (dev->part < dev->num_dvd_parts+1) {
566 Jmsg3(jcr, M_FATAL, 0, _("Error writing. Current part less than total number of parts (%d/%d, device=%s)\n"),
567 dev->part, dev->num_dvd_parts, dev->print_name());
568 dev->dev_errno = EIO;
572 if (ok && !dvd_write_part(dcr)) {
573 Jmsg2(jcr, M_FATAL, 0, _("Unable to write last on %s: ERR=%s\n"),
574 dev->print_name(), dev->bstrerror());
575 dev->dev_errno = EIO;
582 void dvd_remove_empty_part(DCR *dcr)
584 DEVICE *dev = dcr->dev;
586 /* Remove the last part file if it is empty */
587 if (dev->is_dvd() && dev->num_dvd_parts > 0) {
589 uint32_t part_save = dev->part;
590 POOL_MEM archive_name(PM_FNAME);
593 dev->part = dev->num_dvd_parts;
594 make_spooled_dvd_filename(dev, archive_name);
595 /* Check that the part file is empty */
596 status = stat(archive_name.c_str(), &statp);
597 if (status == 0 && statp.st_size == 0) {
598 Dmsg3(100, "Unlink empty part in close call make_dvd_filename. part=%d num=%d vol=%s\n",
599 part_save, dev->num_dvd_parts, dev->VolCatInfo.VolCatName);
600 Dmsg1(100, "unlink(%s)\n", archive_name.c_str());
601 unlink(archive_name.c_str());
602 if (part_save == dev->part) {
603 dev->set_part_spooled(false); /* no spooled part left */
605 } else if (status < 0) {
606 if (part_save == dev->part) {
607 dev->set_part_spooled(false); /* spool doesn't exit */
610 dev->part = part_save; /* restore part number */
614 bool truncate_dvd(DCR *dcr)
616 DEVICE* dev = dcr->dev;
618 dev->clear_freespace_ok(); /* need to update freespace */
619 dev->close_part(dcr);
621 if (!dev->unmount(1)) {
622 Dmsg0(400, "truncate_dvd: Failed to unmount DVD\n");
626 /* If necessary, delete its spool file. */
627 if (dev->is_part_spooled()) {
628 POOL_MEM archive_name(PM_FNAME);
629 /* Delete spool file */
630 make_spooled_dvd_filename(dev, archive_name);
631 unlink(archive_name.c_str());
632 dev->set_part_spooled(false);
635 /* Set num_dvd_parts to zero (on disk) */
637 dev->num_dvd_parts = 0;
638 dcr->VolCatInfo.VolCatParts = 0;
639 dev->VolCatInfo.VolCatParts = 0;
641 Dmsg0(400, "truncate_dvd: Opening first part (1)...\n");
643 dev->truncating = true;
644 /* This creates a zero length spool file and sets part=1 */
645 if (!dvd_open_first_part(dcr, CREATE_READ_WRITE)) {
646 Dmsg0(400, "truncate_dvd: Error while opening first part (1).\n");
647 dev->truncating = false;
651 dev->close_part(dcr);
653 Dmsg0(400, "truncate_dvd: Opening first part (2)...\n");
656 * Now actually truncate the DVD which is done by writing
657 * a zero length part to the DVD/
659 if (!dvd_write_part(dcr)) {
660 Dmsg0(400, "truncate_dvd: Error while writing to DVD.\n");
661 dev->truncating = false;
664 dev->truncating = false;
666 /* Set num_dvd_parts to zero (on disk) */
668 dev->num_dvd_parts = 0;
669 dcr->VolCatInfo.VolCatParts = 0;
670 dev->VolCatInfo.VolCatParts = 0;
671 /* Clear the size of the volume */
672 dev->VolCatInfo.VolCatBytes = 0;
673 dcr->VolCatInfo.VolCatBytes = 0;
676 if (!dir_update_volume_info(dcr, false)) {
684 * Checks if we can write on a non-blank DVD: meaning that it just have been
685 * truncated (there is only one zero-sized file on the DVD).
687 * Note! Normally if we can mount the device, which should be the case
688 * when we get here, it is not a blank DVD. Hence we check if
689 * if all files are of zero length (i.e. no data), in which case we allow it.
692 bool check_can_write_on_non_blank_dvd(DCR *dcr)
694 DEVICE* dev = dcr->dev;
696 struct dirent *entry, *result;
698 struct stat filestat;
701 name_max = pathconf(".", _PC_NAME_MAX);
702 if (name_max < 1024) {
706 if (!(dp = opendir(dev->device->mount_point))) {
708 dev->dev_errno = errno;
709 Dmsg3(29, "check_can_write_on_non_blank_dvd: failed to open dir %s (dev=%s), ERR=%s\n",
710 dev->device->mount_point, dev->print_name(), be.strerror());
714 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
716 if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
717 dev->dev_errno = EIO;
718 Dmsg2(129, "check_can_write_on_non_blank_dvd: no more files in dir %s (dev=%s)\n",
719 dev->device->mount_point, dev->print_name());
722 Dmsg2(99, "check_can_write_on_non_blank_dvd: found %s (versus %s)\n",
723 result->d_name, dev->VolCatInfo.VolCatName);
724 if (strcmp(result->d_name, ".") && strcmp(result->d_name, "..") &&
725 strcmp(result->d_name, ".keep")) {
726 /* Found a file, checking it is empty */
727 POOL_MEM filename(PM_FNAME);
728 pm_strcpy(filename, dev->device->mount_point);
729 if (!IsPathSeparator(filename.c_str()[strlen(filename.c_str())-1])) {
730 pm_strcat(filename, "/");
732 pm_strcat(filename, result->d_name);
733 if (stat(filename.c_str(), &filestat) < 0) {
735 dev->dev_errno = errno;
736 Dmsg2(29, "check_can_write_on_non_blank_dvd: cannot stat file (file=%s), ERR=%s\n",
737 filename.c_str(), be.strerror());
741 Dmsg2(99, "check_can_write_on_non_blank_dvd: size of %s is %lld\n",
742 filename.c_str(), filestat.st_size);
743 if (filestat.st_size != 0) {
753 Dmsg1(29, "OK can_write_on_non_blank_dvd: OK=%d\n", ok);
758 * Mount a DVD device, then scan to find out how many parts
761 int find_num_dvd_parts(DCR *dcr)
763 DEVICE *dev = dcr->dev;
766 if (!dev->is_dvd()) {
772 struct dirent *entry, *result;
774 int len = strlen(dcr->VolCatInfo.VolCatName);
776 /* Now count the number of parts */
777 name_max = pathconf(".", _PC_NAME_MAX);
778 if (name_max < 1024) {
782 if (!(dp = opendir(dev->device->mount_point))) {
784 dev->dev_errno = errno;
785 Dmsg3(29, "find_num_dvd_parts: failed to open dir %s (dev=%s), ERR=%s\n",
786 dev->device->mount_point, dev->print_name(), be.strerror());
790 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000);
792 Dmsg1(100, "Looking for Vol=%s\n", dcr->VolCatInfo.VolCatName);
796 if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) {
797 dev->dev_errno = EIO;
798 Dmsg2(129, "find_num_dvd_parts: failed to find suitable file in dir %s (dev=%s)\n",
799 dev->device->mount_point, dev->print_name());
802 flen = strlen(result->d_name);
805 result->d_name[len] = 0;
806 if (strcmp(dcr->VolCatInfo.VolCatName, result->d_name) == 0) {
808 Dmsg1(100, "find_num_dvd_parts: found part: %s\n", result->d_name);
813 Dmsg2(129, "find_num_dvd_parts: ignoring %s in %s\n",
814 result->d_name, dev->device->mount_point);
819 Dmsg1(29, "find_num_dvd_parts = %d\n", num_parts);
823 dev->set_freespace_ok();
824 if (dev->is_mounted()) {