bool gfapi_device::d_truncate(DCR *dcr) { struct stat st; if (m_gfd) { if (glfs_ftruncate(m_gfd, 0) != 0) { berrno be; Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"), print_name(), be.bstrerror()); Emsg0(M_FATAL, 0, errmsg); return false; } /* * Check for a successful glfs_truncate() and issue work-around when truncation doesn't work. * * 1. close file * 2. delete file * 3. open new file with same mode * 4. change ownership to original */ if (glfs_fstat(m_gfd, &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) { /* glfs_truncate() didn't work */ glfs_close(m_gfd); glfs_unlink(m_glfs, getVolCatName()); set_mode(CREATE_READ_WRITE); /* * Recreate the file -- of course, empty */ m_gfd = glfs_creat(m_glfs, getVolCatName(), oflags, st.st_mode); if (!m_gfd) { berrno be; dev_errno = errno; Mmsg2(errmsg, _("Could not reopen: %s, ERR=%s\n"), getVolCatName(), be.bstrerror()); Emsg0(M_FATAL, 0, errmsg); return false; } /* * Reset proper owner */ glfs_chown(m_glfs, getVolCatName(), st.st_uid, st.st_gid); } } return true; }
boffset_t object_store_device::d_lseek(DCR *dcr, boffset_t offset, int whence) { switch (whence) { case SEEK_SET: m_offset = offset; break; case SEEK_CUR: m_offset += offset; break; case SEEK_END: { size_t filesize; filesize = object_store_get_file_size(m_ctx, getVolCatName()); if (filesize >= 0) { m_offset = filesize + offset; } else { return -1; } break; } default: return -1; } return m_offset; }
bool DCR::dir_get_volume_info(enum get_vol_info_rw writing) { Dmsg0(100, "Fake dir_get_volume_info\n"); setVolCatName(VolumeName); Dmsg1(500, "Vol=%s\n", getVolCatName()); return 1; }
/* * Open the device with the operating system and * initialize buffer pointers. * * Returns: true on success * false on error * * Note, for a tape, the VolName is the name we give to the * volume (not really used here), but for a file, the * VolName represents the name of the file to be created/opened. * In the case of a file, the full name is the device name * (archive_name) with the VolName concatenated. */ bool DEVICE::open(DCR *dcr, int omode) { char preserve[ST_BYTES]; clear_all_bits(ST_MAX, preserve); if (is_open()) { if (open_mode == omode) { return true; } else { d_close(m_fd); clear_opened(); Dmsg0(100, "Close fd for mode change.\n"); if (bit_is_set(ST_LABEL, state)) set_bit(ST_LABEL, preserve); if (bit_is_set(ST_APPENDREADY, state)) set_bit(ST_APPENDREADY, preserve); if (bit_is_set(ST_READREADY, state)) set_bit(ST_READREADY, preserve); } } if (dcr) { dcr->setVolCatName(dcr->VolumeName); VolCatInfo = dcr->VolCatInfo; /* structure assign */ } Dmsg4(100, "open dev: type=%d dev_name=%s vol=%s mode=%s\n", dev_type, print_name(), getVolCatName(), mode_to_str(omode)); clear_bit(ST_LABEL, state); clear_bit(ST_APPENDREADY, state); clear_bit(ST_READREADY, state); clear_bit(ST_EOT, state); clear_bit(ST_WEOT, state); clear_bit(ST_EOF, state); label_type = B_BAREOS_LABEL; /* * We are about to open the device so let any plugin know we are. */ if (dcr && generate_plugin_event(dcr->jcr, bsdEventDeviceOpen, dcr) != bRC_OK) { Dmsg0(100, "open_dev: bsdEventDeviceOpen failed\n"); return false; } Dmsg1(100, "call open_device mode=%s\n", mode_to_str(omode)); open_device(dcr, omode); /* * Reset any important state info */ clone_bits(ST_MAX, preserve, state); Dmsg2(100, "preserve=0x%x fd=%d\n", preserve, m_fd); return m_fd >= 0; }
/** * Get Volume info for a specific volume from the Director's Database * * Returns: true on success (Director guarantees that Pool and MediaType * are correct and VolStatus==Append or * VolStatus==Recycle) * false on failure * * Volume information returned in dcr->VolCatInfo */ bool SD_DCR::dir_get_volume_info(enum get_vol_info_rw writing) { bool ok; BSOCK *dir = jcr->dir_bsock; P(vol_info_mutex); setVolCatName(VolumeName); bash_spaces(getVolCatName()); dir->fsend(Get_Vol_Info, jcr->Job, getVolCatName(), (writing == GET_VOL_INFO_FOR_WRITE) ? 1 : 0); Dmsg1(dbglvl, ">dird %s", dir->msg); unbash_spaces(getVolCatName()); ok = do_get_volume_info(this); V(vol_info_mutex); return ok; }
/* * 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); }
/* * Walk through all attached jcrs indicating the File has changed */ void DEVICE::notify_newfile_in_attached_dcrs() { Dmsg1(140, "Notify dcrs of file change. Volume=%s\n", getVolCatName()); Lock_dcrs(); DCR *mdcr; foreach_dlist(mdcr, attached_dcrs) { if (mdcr->jcr->JobId == 0) { continue; /* ignore console */ } Dmsg1(140, "Notify JobI=%d\n", mdcr->jcr->JobId); mdcr->NewFile = true; } Unlock_dcrs(); }
/* * Write data to a volume using libdroplet. */ ssize_t object_store_device::d_write(int fd, const void *buffer, size_t count) { if (m_vfd) { dpl_status_t status; status = dpl_pwrite(m_vfd, (char *)buffer, count, m_offset); switch (status) { case DPL_SUCCESS: m_offset += count; return count; default: Mmsg2(errmsg, _("Failed to write %s using dpl_write(): ERR=%s.\n"), getVolCatName(), dpl_status_str(status)); return droplet_errno_to_system_errno(status); } } else { errno = EBADF; return -1; } }
/* * Walk through all attached jcrs indicating the volume has changed * Note: If you have the new VolumeName, it is passed here, * otherwise pass a NULL. */ void DEVICE::notify_newvol_in_attached_dcrs(const char *newVolumeName) { Dmsg2(140, "Notify dcrs of vol change. oldVolume=%s NewVolume=%s\n", getVolCatName(), newVolumeName?"*None*":newVolumeName); Lock_dcrs(); DCR *mdcr; foreach_dlist(mdcr, attached_dcrs) { if (mdcr->jcr->JobId == 0) { continue; /* ignore console */ } mdcr->NewVol = true; mdcr->NewFile = true; if (newVolumeName && mdcr->VolumeName != newVolumeName) { bstrncpy(mdcr->VolumeName, newVolumeName, sizeof(mdcr->VolumeName)); Dmsg2(140, "Set NewVol=%s in JobId=%d\n", mdcr->VolumeName, mdcr->jcr->JobId); } } Unlock_dcrs(); }
/* * Read data from a volume using libdroplet. */ ssize_t object_store_device::d_read(int fd, void *buffer, size_t count) { if (m_vfd) { unsigned int buflen; dpl_status_t status; buflen = count; status = dpl_pread(m_vfd, count, m_offset, (char **)&buffer, &buflen); switch (status) { case DPL_SUCCESS: m_offset += buflen; return buflen; default: Mmsg2(errmsg, _("Failed to read %s using dpl_read(): ERR=%s.\n"), getVolCatName(), dpl_status_str(status)); return droplet_errno_to_system_errno(status); } } else { errno = EBADF; return -1; } }
/* * Open the device with the operating system and * initialize buffer pointers. * * Returns: true on success * false on error * * Note, for a tape, the VolName is the name we give to the * volume (not really used here), but for a file, the * VolName represents the name of the file to be created/opened. * In the case of a file, the full name is the device name * (archive_name) with the VolName concatenated. */ bool DEVICE::open(DCR *dcr, int omode) { int preserve = 0; if (is_open()) { if (openmode == omode) { return true; } else { Dmsg1(200, "Close fd=%d for mode change in open().\n", m_fd); d_close(m_fd); clear_opened(); preserve = state & (ST_LABEL|ST_APPEND|ST_READ); } } if (dcr) { dcr->setVolCatName(dcr->VolumeName); VolCatInfo = dcr->VolCatInfo; /* structure assign */ } state &= ~(ST_NOSPACE|ST_LABEL|ST_APPEND|ST_READ|ST_EOT|ST_WEOT|ST_EOF); label_type = B_BACULA_LABEL; if (is_tape() || is_fifo()) { open_tape_device(dcr, omode); } else if (is_ftp()) { open_device(dcr, omode); } else { Dmsg1(100, "call open_file_device mode=%s\n", mode_to_str(omode)); open_file_device(dcr, omode); } state |= preserve; /* reset any important state info */ Dmsg2(100, "preserve=0x%x fd=%d\n", preserve, m_fd); Dmsg7(100, "open dev: fd=%d dev=%p dcr=%p vol=%s type=%d dev_name=%s mode=%s\n", m_fd, getVolCatName(), this, dcr, dev_type, print_name(), mode_to_str(omode)); return m_fd >= 0; }
bool object_store_device::d_truncate(DCR *dcr) { /* * libdroplet doesn't have a truncate function so unlink the volume and create a new empty one. */ if (m_vfd) { dpl_status_t status; dpl_vfile_flag_t dpl_flags; dpl_option_t dpl_options; status = dpl_close(m_vfd); switch (status) { case DPL_SUCCESS: m_vfd = NULL; break; default: Mmsg2(errmsg, _("Failed to close %s using dpl_close(): ERR=%s.\n"), getVolCatName(), dpl_status_str(status)); return false; } status = dpl_unlink(m_ctx, getVolCatName()); switch (status) { case DPL_SUCCESS: break; default: Mmsg2(errmsg, _("Failed to unlink %s using dpl_unlink(): ERR=%s.\n"), getVolCatName(), dpl_status_str(status)); return false; } /* * Create some options for libdroplet. * * DPL_OPTION_NOALLOC - we provide the buffer to copy the data into * no need to let the library allocate memory we * need to free after copying the data. */ memset(&dpl_options, 0, sizeof(dpl_options)); dpl_options.mask |= DPL_OPTION_NOALLOC; dpl_flags = DPL_VFILE_FLAG_CREAT | DPL_VFILE_FLAG_RDWR; status = dpl_open(m_ctx, /* context */ getVolCatName(), /* locator */ dpl_flags, /* flags */ &dpl_options, /* options */ NULL, /* condition */ NULL, /* metadata */ NULL, /* sysmd */ NULL, /* query_params */ NULL, /* stream_status */ &m_vfd); switch (status) { case DPL_SUCCESS: break; default: Mmsg2(errmsg, _("Failed to open %s using dpl_open(): ERR=%s.\n"), getVolCatName(), dpl_status_str(status)); return false; } } return true; }
/* * Open a volume using libdroplet. */ int object_store_device::d_open(const char *pathname, int flags, int mode) { dpl_status_t status; dpl_vfile_flag_t dpl_flags; dpl_option_t dpl_options; #if 1 Mmsg1(errmsg, _("Object Storage devices are not yet supported, please disable %s\n"), dev_name); return -1; #endif /* * Initialize the droplet library when its not done previously. */ P(mutex); if (droplet_reference_count == 0) { status = dpl_init(); if (status != DPL_SUCCESS) { V(mutex); return -1; } dpl_set_log_func(object_store_logfunc); droplet_reference_count++; } V(mutex); if (!m_object_configstring) { int len; char *bp, *next_option; bool done; if (!dev_options) { Mmsg0(errmsg, _("No device options configured\n")); Emsg0(M_FATAL, 0, errmsg); return -1; } m_object_configstring = bstrdup(dev_options); bp = m_object_configstring; while (bp) { next_option = strchr(bp, ','); if (next_option) { *next_option++ = '\0'; } done = false; for (int i = 0; !done && device_options[i].name; i++) { /* * Try to find a matching device option. */ if (bstrncasecmp(bp, device_options[i].name, device_options[i].compare_size)) { switch (device_options[i].type) { case argument_profile: m_profile = bp + device_options[i].compare_size; done = true; break; case argument_bucket: m_object_bucketname = bp + device_options[i].compare_size; done = true; break; default: break; } } } if (!done) { Mmsg1(errmsg, _("Unable to parse device option: %s\n"), bp); Emsg0(M_FATAL, 0, errmsg); goto bail_out; } bp = next_option; } if (!m_profile) { Mmsg0(errmsg, _("No droplet profile configured\n")); Emsg0(M_FATAL, 0, errmsg); goto bail_out; } /* * Strip any .profile prefix from the libdroplet profile name. */ len = strlen(m_profile); if (len > 8 && bstrcasecmp(m_profile + (len - 8), ".profile")) { m_profile[len - 8] = '\0'; } } /* * See if we need to setup a new context for this device. */ if (!m_ctx) { char *bp; /* * See if this is a path. */ bp = strrchr(m_object_configstring, '/'); if (!bp) { /* * Only a profile name. */ m_ctx = dpl_ctx_new(NULL, m_object_configstring); } else { if (bp == m_object_configstring) { /* * Profile in root of filesystem */ m_ctx = dpl_ctx_new("/", bp + 1); } else { /* * Profile somewhere else. */ *bp++ = '\0'; m_ctx = dpl_ctx_new(m_object_configstring, bp); } } /* * If we failed to allocate a new context fail the open. */ if (!m_ctx) { Mmsg1(errmsg, _("Failed to create a new context using config %s\n"), dev_options); return -1; } /* * Login if that is needed for this backend. */ status = dpl_login(m_ctx); switch (status) { case DPL_SUCCESS: break; case DPL_ENOTSUPP: /* * Backend doesn't support login which is fine. */ break; default: Mmsg2(errmsg, _("Failed to login for voume %s using dpl_login(): ERR=%s.\n"), getVolCatName(), dpl_status_str(status)); return -1; } /* * If a bucketname was defined set it in the context. */ if (m_object_bucketname) { m_ctx->cur_bucket = m_object_bucketname; } } /* * See if we don't have a file open already. */ if (m_vfd) { dpl_close(m_vfd); m_vfd = NULL; } /* * Create some options for libdroplet. * * DPL_OPTION_NOALLOC - we provide the buffer to copy the data into * no need to let the library allocate memory we * need to free after copying the data. */ memset(&dpl_options, 0, sizeof(dpl_options)); dpl_options.mask |= DPL_OPTION_NOALLOC; if (flags & O_CREAT) { dpl_flags = DPL_VFILE_FLAG_CREAT | DPL_VFILE_FLAG_RDWR; status = dpl_open(m_ctx, /* context */ getVolCatName(), /* locator */ dpl_flags, /* flags */ &dpl_options, /* options */ NULL, /* condition */ NULL, /* metadata */ NULL, /* sysmd */ NULL, /* query_params */ NULL, /* stream_status */ &m_vfd); } else { dpl_flags = DPL_VFILE_FLAG_RDWR; status = dpl_open(m_ctx, /* context */ getVolCatName(), /* locator */ dpl_flags, /* flags */ &dpl_options, /* options */ NULL, /* condition */ NULL, /* metadata */ NULL, /* sysmd */ NULL, /* query_params */ NULL, /* stream_status */ &m_vfd); } switch (status) { case DPL_SUCCESS: m_offset = 0; return 0; default: Mmsg2(errmsg, _("Failed to open %s using dpl_open(): ERR=%s.\n"), getVolCatName(), dpl_status_str(status)); m_vfd = NULL; return droplet_errno_to_system_errno(status); } bail_out: return -1; }
/* * Open a volume using gfapi. */ int gfapi_device::d_open(const char *pathname, int flags, int mode) { int status; POOL_MEM virtual_filename(PM_FNAME); /* * Parse the gluster URI. */ if (!m_gfapi_volume) { m_gfapi_volume = bstrdup(dev_name); if (!parse_gfapi_devicename(m_gfapi_volume, &m_transport, &m_servername, &m_volumename, &m_basedir, &m_serverport)) { Mmsg1(errmsg, _("Unable to parse device URI %s.\n"), dev_name); Emsg0(M_FATAL, 0, errmsg); goto bail_out; } } /* * See if we need to setup a Gluster context. */ if (!m_glfs) { m_glfs = glfs_new(m_volumename); if (!m_glfs) { Mmsg1(errmsg, _("Unable to create new Gluster context for volumename %s.\n"), m_volumename); Emsg0(M_FATAL, 0, errmsg); goto bail_out; } status = glfs_set_volfile_server(m_glfs, (m_transport) ? m_transport : "tcp", m_servername, m_serverport); if (status < 0) { Mmsg3(errmsg, _("Unable to initialize Gluster management server for transport %s, servername %s, serverport %d\n"), (m_transport) ? m_transport : "tcp", m_servername, m_serverport); Emsg0(M_FATAL, 0, errmsg); goto bail_out; } status = glfs_init(m_glfs); if (status < 0) { Mmsg1(errmsg, _("Unable to initialize Gluster for volumename %s.\n"), m_volumename); Emsg0(M_FATAL, 0, errmsg); goto bail_out; } } /* * See if we don't have a file open already. */ if (m_gfd) { glfs_close(m_gfd); m_gfd = NULL; } /* * See if we store in an explicit directory. */ if (m_basedir) { struct stat st; /* * Make sure the dir exists if one is defined. */ Mmsg(virtual_filename, "/%s", m_basedir); if (glfs_stat(m_glfs, virtual_filename.c_str(), &st) != 0) { switch (errno) { case ENOENT: if (!gfapi_makedir(m_glfs, virtual_filename.c_str())) { Mmsg1(errmsg, _("Specified glusterfs direcory %s cannot be created.\n"), virtual_filename.c_str()); Emsg0(M_FATAL, 0, errmsg); goto bail_out; } break; default: goto bail_out; } } else { if (!S_ISDIR(st.st_mode)) { Mmsg1(errmsg, _("Specified glusterfs direcory %s is not a directory.\n"), virtual_filename.c_str()); Emsg0(M_FATAL, 0, errmsg); goto bail_out; } } Mmsg(virtual_filename, "/%s/%s", m_basedir, getVolCatName()); } else { Mmsg(virtual_filename, "%s", getVolCatName()); } /* * See if the O_CREAT flag is set as glfs_open doesn't support that flag and you have to call glfs_creat then. */ if (flags & O_CREAT) { m_gfd = glfs_creat(m_glfs, virtual_filename.c_str(), flags, mode); } else { m_gfd = glfs_open(m_glfs, virtual_filename.c_str(), flags); } if (!m_gfd) { goto bail_out; } return 0; bail_out: /* * Cleanup the Gluster context. */ if (m_glfs) { glfs_fini(m_glfs); m_glfs = NULL; } return -1; }