void dvd_remove_empty_part(DCR *dcr) { DEVICE *dev = dcr->dev; /* Remove the last part file if it is empty */ if (dev->is_dvd() && dev->num_dvd_parts > 0) { struct stat statp; uint32_t part_save = dev->part; POOL_MEM archive_name(PM_FNAME); int status; dev->part = dev->num_dvd_parts; make_spooled_dvd_filename(dev, archive_name); /* Check that the part file is empty */ status = stat(archive_name.c_str(), &statp); if (status == 0 && statp.st_size == 0) { Dmsg3(100, "Unlink empty part in close call make_dvd_filename. part=%d num=%d vol=%s\n", part_save, dev->num_dvd_parts, dev->getVolCatName()); Dmsg1(100, "unlink(%s)\n", archive_name.c_str()); unlink(archive_name.c_str()); if (part_save == dev->part) { dev->set_part_spooled(false); /* no spooled part left */ } } else if (status < 0) { if (part_save == dev->part) { dev->set_part_spooled(false); /* spool doesn't exit */ } } dev->part = part_save; /* restore part number */ } }
/* * Open a device. */ void DEVICE::open_device(DCR *dcr, int omode) { POOL_MEM archive_name(PM_FNAME); get_autochanger_loaded_slot(dcr); /* * Handle opening of File Archive (not a tape) */ pm_strcpy(archive_name, dev_name); /* * If this is a virtual autochanger (i.e. changer_res != NULL) we simply use * the device name, assuming it has been appropriately setup by the "autochanger". */ if (!device->changer_res || device->changer_command[0] == 0) { if (VolCatInfo.VolCatName[0] == 0) { Mmsg(errmsg, _("Could not open file device %s. No Volume name given.\n"), print_name()); clear_opened(); return; } if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) { pm_strcat(archive_name, "/"); } pm_strcat(archive_name, getVolCatName()); } mount(dcr, 1); /* do mount if required */ open_mode = omode; set_mode(omode); /* * If creating file, give 0640 permissions */ Dmsg3(100, "open disk: mode=%s open(%s, 0x%x, 0640)\n", mode_to_str(omode), archive_name.c_str(), oflags); if ((m_fd = d_open(archive_name.c_str(), oflags, 0640)) < 0) { berrno be; dev_errno = errno; Mmsg2(errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(), be.bstrerror()); Dmsg1(100, "open failed: %s", errmsg); } if (m_fd >= 0) { dev_errno = 0; file = 0; file_addr = 0; } Dmsg1(100, "open dev: disk fd=%d opened\n", m_fd); }
/* * Edit codes into (Un)MountCommand, Write(First)PartCommand * %% = % * %a = archive device name * %e = erase (set if cannot mount and first part) * %n = part number * %m = mount point * %v = last part name * * omsg = edited output message * imsg = input string containing edit codes (%x) * */ void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg) { const char *p; const char *str; char add[20]; POOL_MEM archive_name(PM_FNAME); omsg.c_str()[0] = 0; Dmsg1(800, "edit_mount_codes: %s\n", imsg); for (p=imsg; *p; p++) { if (*p == '%') { switch (*++p) { case '%': str = "%"; break; case 'a': str = dev_name; break; case 'e': if (num_dvd_parts == 0) { if (truncating || blank_dvd) { str = "2"; } else { str = "1"; } } else { str = "0"; } break; case 'n': bsnprintf(add, sizeof(add), "%d", part); str = add; break; case 'm': str = device->mount_point; break; default: add[0] = '%'; add[1] = *p; add[2] = 0; str = add; break; } } else { add[0] = *p; add[1] = 0; str = add; } Dmsg1(1900, "add_str %s\n", str); pm_strcat(omsg, (char *)str); Dmsg1(1800, "omsg=%s\n", omsg.c_str()); } }
/* * Edit codes into (Un)MountCommand * %% = % * %a = archive device name * %m = mount point * * omsg = edited output message * imsg = input string containing edit codes (%x) * */ void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg) { const char *p; const char *str; char add[20]; POOL_MEM archive_name(PM_FNAME); omsg.c_str()[0] = 0; Dmsg1(800, "edit_mount_codes: %s\n", imsg); for (p=imsg; *p; p++) { if (*p == '%') { switch (*++p) { case '%': str = "%"; break; case 'a': str = dev_name; break; case 'm': str = device->mount_point; break; default: add[0] = '%'; add[1] = *p; add[2] = 0; str = add; break; } } else { add[0] = *p; add[1] = 0; str = add; } Dmsg1(1900, "add_str %s\n", str); pm_strcat(omsg, (char *)str); Dmsg1(1800, "omsg=%s\n", omsg.c_str()); } }
bool truncate_dvd(DCR *dcr) { DEVICE* dev = dcr->dev; dev->clear_freespace_ok(); /* need to update freespace */ dev->close_part(dcr); if (!dev->unmount(1)) { Dmsg0(400, "truncate_dvd: Failed to unmount DVD\n"); return false; } /* If necessary, delete its spool file. */ if (dev->is_part_spooled()) { POOL_MEM archive_name(PM_FNAME); /* Delete spool file */ make_spooled_dvd_filename(dev, archive_name); unlink(archive_name.c_str()); dev->set_part_spooled(false); } /* Set num_dvd_parts to zero (on disk) */ dev->part = 0; dev->num_dvd_parts = 0; dcr->VolCatInfo.VolCatParts = 0; dev->VolCatInfo.VolCatParts = 0; Dmsg0(400, "truncate_dvd: Opening first part (1)...\n"); dev->truncating = true; /* This creates a zero length spool file and sets part=1 */ if (!dvd_open_first_part(dcr, CREATE_READ_WRITE)) { Dmsg0(400, "truncate_dvd: Error while opening first part (1).\n"); dev->truncating = false; return false; } dev->close_part(dcr); Dmsg0(400, "truncate_dvd: Opening first part (2)...\n"); /* * Now actually truncate the DVD which is done by writing * a zero length part to the DVD/ */ if (!dvd_write_part(dcr)) { Dmsg0(400, "truncate_dvd: Error while writing to DVD.\n"); dev->truncating = false; return false; } dev->truncating = false; /* Set num_dvd_parts to zero (on disk) */ dev->part = 0; dev->num_dvd_parts = 0; dcr->VolCatInfo.VolCatParts = 0; dev->VolCatInfo.VolCatParts = 0; /* Clear the size of the volume */ dev->VolCatInfo.VolCatBytes = 0; dcr->VolCatInfo.VolCatBytes = 0; /* Update catalog */ if (!dir_update_volume_info(dcr, false, true)) { return false; } return true; }
/* * Open the next part file. * - Close the fd * - Increment part number * - Reopen the device */ int dvd_open_next_part(DCR *dcr) { DEVICE *dev = dcr->dev; Dmsg6(29, "Enter: == open_next_part part=%d npart=%d dev=%s vol=%s mode=%d file_addr=%d\n", dev->part, dev->num_dvd_parts, dev->print_name(), dev->getVolCatName(), dev->openmode, dev->file_addr); if (!dev->is_dvd()) { Dmsg1(100, "Device %s is not dvd!!!!\n", dev->print_name()); return -1; } /* When appending, do not open a new part if the current is empty */ if (dev->can_append() && (dev->part > dev->num_dvd_parts) && (dev->part_size == 0)) { Dmsg0(29, "open_next_part exited immediately (dev->part_size == 0).\n"); return dev->fd(); } dev->close_part(dcr); /* close current part */ /* * If we have a spooled part open, write it to the * DVD before opening the next part. */ if (dev->is_part_spooled()) { Dmsg2(100, "Before open next write previous. part=%d num_parts=%d\n", dev->part, dev->num_dvd_parts); if (!dvd_write_part(dcr)) { Dmsg0(29, "Error in dvd_write part.\n"); return -1; } } dev->part_start += dev->part_size; dev->part++; Dmsg2(29, "Inc part=%d num_dvd_parts=%d\n", dev->part, dev->num_dvd_parts); /* Are we working on a part past what is written in the DVD? */ if (dev->num_dvd_parts < dev->part) { POOL_MEM archive_name(PM_FNAME); struct stat buf; /* * First check what is on DVD. If our part is there, we * are in trouble, so bail out. * NB: This is however not a problem if we are writing the first part. * It simply means that we are over writing an existing volume... */ if (dev->num_dvd_parts > 0) { make_mounted_dvd_filename(dev, archive_name); /* makes dvd name */ Dmsg1(100, "Check if part on DVD: %s\n", archive_name.c_str()); if (stat(archive_name.c_str(), &buf) == 0) { /* bad news bail out */ dev->set_part_spooled(false); Mmsg1(&dev->errmsg, _("Next Volume part already exists on DVD. Cannot continue: %s\n"), archive_name.c_str()); return -1; } } #ifdef neeeded Dmsg2(400, "num_dvd_parts=%d part=%d\n", dev->num_dvd_parts, dev->part); make_spooled_dvd_filename(dev, archive_name); /* makes spool name */ /* Check if the next part exists in spool directory . */ Dmsg1(100, "Check if part on spool: %s\n", archive_name.c_str()); if ((stat(archive_name.c_str(), &buf) == 0) || (errno != ENOENT)) { Dmsg1(29, "======= Part %s is in the way, deleting it...\n", archive_name.c_str()); /* Then try to unlink it */ if (unlink(archive_name.c_str()) < 0) { berrno be; dev->set_part_spooled(false); dev->dev_errno = errno; Mmsg2(dev->errmsg, _("open_next_part can't unlink existing part %s, ERR=%s\n"), archive_name.c_str(), be.bstrerror()); return -1; } } #endif } Dmsg2(400, "Call dev->open(vol=%s, mode=%d)\n", dcr->getVolCatName(), dev->openmode); /* Open next part. Note, this sets part_size for part opened. */ if (dev->open(dcr, OPEN_READ_ONLY) < 0) { return -1; } dev->set_labeled(); /* all next parts are "labeled" */ return dev->fd(); }
/* * Note!!!! Part numbers now begin at 1. The part number is * suppressed from the first part, which is just the Volume * name. Each subsequent part is the Volumename.partnumber. * * Write a part (Vol, Vol.2, ...) from the spool to the DVD * This routine does not update the part number, so normally, you * should call open_next_part() * * It is also called from truncate_dvd to "blank" the medium, as * well as from block.c when the DVD is full to write the last part. */ bool dvd_write_part(DCR *dcr) { DEVICE *dev = dcr->dev; POOL_MEM archive_name(PM_FNAME); /* * Don't write empty part files. * This is only useful when growisofs does not support write beyond * the 4GB boundary. * Example : * - 3.9 GB on the volume, dvd-freespace reports 0.4 GB free * - Write 0.2 GB on the volume, Bacula thinks it could still * append data, it creates a new empty part. * - dvd-freespace reports 0 GB free, as the 4GB boundary has * been crossed * - Bacula thinks he must finish to write to the device, so it * tries to write the last part (0-byte), but dvd-writepart fails... * * There is one exception: when recycling a volume, we write a blank part * file, so, then, we need to accept to write it. */ if (dev->part_size == 0 && !dev->truncating) { Dmsg2(29, "dvd_write_part: device is %s, won't write blank part %d\n", dev->print_name(), dev->part); /* Delete spool file */ make_spooled_dvd_filename(dev, archive_name); unlink(archive_name.c_str()); dev->set_part_spooled(false); Dmsg1(29, "========= unlink(%s)\n", archive_name.c_str()); Dsm_check(400); return true; } POOL_MEM ocmd(PM_FNAME); POOL_MEM results(PM_MESSAGE); char* icmd; int status; int timeout; char ed1[50]; dev->clear_freespace_ok(); /* need to update freespace */ Dsm_check(400); Dmsg3(29, "dvd_write_part: device is %s, part is %d, is_mounted=%d\n", dev->print_name(), dev->part, dev->is_mounted()); icmd = dev->device->write_part_command; dev->edit_mount_codes(ocmd, icmd); /* * original line follows * timeout = dev->max_open_wait + (dev->max_part_size/(1350*1024/2)); * I modified this for a longer timeout; pre-formatting, blanking and * writing can take quite a while */ /* Explanation of the timeout value, when writing the first part, * by Arno Lehmann : * 9 GB, write speed 1x: 6990 seconds (almost 2 hours...) * Overhead: 900 seconds (starting, initializing, finalizing,probably * reloading 15 minutes) * Sum: 15780. * A reasonable last-exit timeout would be 16000 seconds. Quite long - * almost 4.5 hours, but hopefully, that timeout will only ever be needed * in case of a serious emergency. */ if (dev->part == 1) { timeout = 16000; } else { timeout = dev->max_open_wait + (dev->part_size/(1350*1024/4)); } Dmsg2(20, "Write part: cmd=%s timeout=%d\n", ocmd.c_str(), timeout); status = run_program_full_output(ocmd.c_str(), timeout, results.addr()); Dmsg2(20, "Write part status=%d result=%s\n", status, results.c_str()); dev->blank_dvd = false; if (status != 0) { Jmsg2(dcr->jcr, M_FATAL, 0, _("Error writing part %d to the DVD: ERR=%s\n"), dev->part, results.c_str()); Mmsg1(dev->errmsg, _("Error while writing current part to the DVD: %s"), results.c_str()); Dmsg1(100, "%s\n", dev->errmsg); dev->dev_errno = EIO; if (!dev->truncating) { dcr->mark_volume_in_error(); } Dsm_check(400); return false; } Jmsg(dcr->jcr, M_INFO, 0, _("Part %d (%lld bytes) written to DVD.\n"), dev->part, dev->part_size); Dmsg3(400, "dvd_write_part: Part %d (%lld bytes) written to DVD\nResults: %s\n", dev->part, dev->part_size, results.c_str()); dev->num_dvd_parts++; /* there is now one more part on DVD */ dev->VolCatInfo.VolCatParts = dev->num_dvd_parts; dcr->VolCatInfo.VolCatParts = dev->num_dvd_parts; Dmsg1(100, "Update num_parts=%d\n", dev->num_dvd_parts); /* Delete spool file */ make_spooled_dvd_filename(dev, archive_name); unlink(archive_name.c_str()); dev->set_part_spooled(false); Dmsg1(29, "========= unlink(%s)\n", archive_name.c_str()); Dsm_check(400); /* growisofs umounted the device, so remount it (it will update the free space) */ dev->clear_mounted(); dev->mount(1); Jmsg(dcr->jcr, M_INFO, 0, _("Remaining free space %s on %s\n"), edit_uint64_with_commas(dev->free_space, ed1), dev->print_name()); Dsm_check(400); return true; }
bool win32_file_device::d_truncate(DCR *dcr) { struct stat st; if (ftruncate(m_fd, 0) != 0) { berrno be; Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"), print_name(), be.bstrerror()); return false; } /* * Check for a successful ftruncate() and issue a work-around for devices * (mostly cheap NAS) that don't support truncation. * Workaround supplied by Martin Schmid as a solution to bug #1011. * 1. close file * 2. delete file * 3. open new file with same mode * 4. change ownership to original */ if (fstat(m_fd, &st) != 0) { berrno be; Mmsg2(errmsg, _("Unable to stat device %s. ERR=%s\n"), print_name(), be.bstrerror()); return false; } if (st.st_size != 0) { /* ftruncate() didn't work */ POOL_MEM archive_name(PM_FNAME); pm_strcpy(archive_name, dev_name); if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) { pm_strcat(archive_name, "/"); } pm_strcat(archive_name, dcr->VolumeName); Mmsg2(errmsg, _("Device %s doesn't support ftruncate(). Recreating file %s.\n"), print_name(), archive_name.c_str()); /* * Close file and blow it away */ ::close(m_fd); ::unlink(archive_name.c_str()); /* * Recreate the file -- of course, empty */ oflags = O_CREAT | O_RDWR | O_BINARY; if ((m_fd = ::open(archive_name.c_str(), oflags, st.st_mode)) < 0) { berrno be; dev_errno = errno; Mmsg2(errmsg, _("Could not reopen: %s, ERR=%s\n"), archive_name.c_str(), be.bstrerror()); Dmsg1(100, "reopen failed: %s", errmsg); Emsg0(M_FATAL, 0, errmsg); return false; } /* * Reset proper owner */ chown(archive_name.c_str(), st.st_uid, st.st_gid); } return true; }
static void note_archive_failure(FILE *fp, int error) { static char cnt = 0; if (++cnt > 3) return; /* already warned in all possible output files, but warn again after it overflows */ if (fp != NULL && !ferror(fp)) { fprintf(fp, "%s: previous line may not have been appended to %s correctly\n", program_name, archive_name()); fprintf(fp, "\terrno %d: %s\n", error, strerror(error)); } }
FILE *open_archive() { return(fopen(archive_name(), "a")); }
/* * Truncate a volume. If this is aligned disk, we * truncate both volumes. */ bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */ { struct stat st; DEVICE *dev = this; Dmsg1(100, "truncate %s\n", print_name()); switch (dev_type) { case B_VTL_DEV: case B_VTAPE_DEV: case B_TAPE_DEV: /* maybe we should rewind and write and eof ???? */ return true; /* we don't really truncate tapes */ case B_FILE_DEV: Dmsg1(100, "Truncate fd=%d\n", dev->m_fd); if (ftruncate(dev->m_fd, 0) != 0) { berrno be; Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"), print_name(), be.bstrerror()); return false; } /* * Check for a successful ftruncate() and issue a work-around for devices * (mostly cheap NAS) that don't support truncation. * Workaround supplied by Martin Schmid as a solution to bug #1011. * 1. close file * 2. delete file * 3. open new file with same mode * 4. change ownership to original */ if (fstat(dev->m_fd, &st) != 0) { berrno be; Mmsg2(errmsg, _("Unable to stat device %s. ERR=%s\n"), print_name(), be.bstrerror()); return false; } if (st.st_size != 0) { /* ftruncate() didn't work */ POOL_MEM archive_name(PM_FNAME); pm_strcpy(archive_name, dev_name); if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) { pm_strcat(archive_name, "/"); } pm_strcat(archive_name, dcr->VolumeName); Mmsg2(errmsg, _("Device %s doesn't support ftruncate(). Recreating file %s.\n"), print_name(), archive_name.c_str()); /* Close file and blow it away */ ::close(dev->m_fd); ::unlink(archive_name.c_str()); /* Recreate the file -- of course, empty */ dev->set_mode(CREATE_READ_WRITE); if ((dev->m_fd = ::open(archive_name.c_str(), mode, st.st_mode)) < 0) { berrno be; dev_errno = errno; Mmsg2(errmsg, _("Could not reopen: %s, ERR=%s\n"), archive_name.c_str(), be.bstrerror()); Dmsg1(40, "reopen failed: %s", errmsg); Emsg0(M_FATAL, 0, errmsg); return false; } /* Reset proper owner */ chown(archive_name.c_str(), st.st_uid, st.st_gid); } return true; } return false; }
static void macho_symfile_read_all_oso (std::vector<oso_el> *oso_vector_ptr, struct objfile *main_objfile, symfile_add_flags symfile_flags) { int ix; oso_el *oso; /* Sort oso by name so that files from libraries are gathered. */ std::sort (oso_vector_ptr->begin (), oso_vector_ptr->end (), oso_el_compare_name); for (ix = 0; ix < oso_vector_ptr->size (); ++ix) { int pfx_len; oso = &(*oso_vector_ptr)[ix]; /* Check if this is a library name. */ pfx_len = get_archive_prefix_len (oso->name); if (pfx_len > 0) { int last_ix; oso_el *oso2; int ix2; std::string archive_name (oso->name, pfx_len); /* Compute number of oso for this archive. */ for (last_ix = ix; last_ix < oso_vector_ptr->size (); last_ix++) { oso2 = &(*oso_vector_ptr)[last_ix]; if (strncmp (oso2->name, archive_name.c_str (), pfx_len) != 0) break; } /* Open the archive and check the format. */ gdb_bfd_ref_ptr archive_bfd (gdb_bfd_open (archive_name.c_str (), gnutarget, -1)); if (archive_bfd == NULL) { warning (_("Could not open OSO archive file \"%s\""), archive_name.c_str ()); ix = last_ix; continue; } if (!bfd_check_format (archive_bfd.get (), bfd_archive)) { warning (_("OSO archive file \"%s\" not an archive."), archive_name.c_str ()); ix = last_ix; continue; } gdb_bfd_ref_ptr member_bfd (gdb_bfd_openr_next_archived_file (archive_bfd.get (), NULL)); if (member_bfd == NULL) { warning (_("Could not read archive members out of " "OSO archive \"%s\""), archive_name.c_str ()); ix = last_ix; continue; } /* Load all oso in this library. */ while (member_bfd != NULL) { const char *member_name = member_bfd->filename; int member_len = strlen (member_name); /* If this member is referenced, add it as a symfile. */ for (ix2 = ix; ix2 < last_ix; ix2++) { oso2 = &(*oso_vector_ptr)[ix2]; if (oso2->name && strlen (oso2->name) == pfx_len + member_len + 2 && !memcmp (member_name, oso2->name + pfx_len + 1, member_len)) { macho_add_oso_symfile (oso2, member_bfd, bfd_get_filename (member_bfd), main_objfile, symfile_flags); oso2->name = NULL; break; } } member_bfd = gdb_bfd_openr_next_archived_file (archive_bfd.get (), member_bfd.get ()); } for (ix2 = ix; ix2 < last_ix; ix2++) { oso_el *oso2 = &(*oso_vector_ptr)[ix2]; if (oso2->name != NULL) warning (_("Could not find specified archive member " "for OSO name \"%s\""), oso->name); } ix = last_ix; } else { gdb_bfd_ref_ptr abfd (gdb_bfd_open (oso->name, gnutarget, -1)); if (abfd == NULL) warning (_("`%s': can't open to read symbols: %s."), oso->name, bfd_errmsg (bfd_get_error ())); else macho_add_oso_symfile (oso, abfd, oso->name, main_objfile, symfile_flags); ix++; } } }