"VolStatus,Slot,VolBytes,InChanger,VolReadTime,VolWriteTime,VolType,"
"VolParts,VolCloudParts,LastPartBytes,"
"EndFile,EndBlock,LabelType,StorageId,DeviceId,LocationId,"
-"ScratchPoolId,RecyclePoolId,Enabled,ActionOnPurge,CacheRetention)"
-"VALUES ('%s','%s',0,%u,%s,%s,%d,%s,%s,%u,%u,'%s',%d,%s,%d,%s,%s,%d,"
- "%d,%d,'%s',%d,%d,%d,%s,%s,%s,%s,%s,%d,%d,%s)",
+"ScratchPoolId,RecyclePoolId,Enabled,ActionOnPurge,CacheRetention,UseProtect)"
+"VALUES ('%s','%s',0,%lu,%s,%s,%ld,%s,%s,%lu,%lu,'%s',%ld,%s,%ld,%s,%s,%ld,"
+ "%ld,%ld,'%s',%ld,%ld,%ld,%s,%s,%s,%s,%s,%ld,%ld,%s,%ld)",
esc_name,
esc_mtype, mr->PoolId,
edit_uint64(mr->MaxVolBytes,ed1),
edit_int64(mr->RecyclePoolId, ed13),
mr->Enabled,
mr->ActionOnPurge,
- edit_uint64(mr->CacheRetention, ed14)
+ edit_uint64(mr->CacheRetention, ed14),
+ mr->UseProtect
);
if (ua->argc > 0 && ua->argk[0][0] == '.') {
ok = do_a_dot_command(ua);
} else {
- ok = do_a_command(ua);
+ ok = do_a_command(ua);
}
close_db(ua);
free_ua_context(ua);
}
}
}
-
/* Check if the Pool quota is respected */
if (!expired && use_max_pool_bytes(jcr)) {
POOL_DBR pr;
{ NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
{ NT_("mount"), mount_cmd, _("Mount storage"),
NT_("storage=<storage-name> slot=<num> drive=<num> [ device=<device-name> ] [ jobid=<id> | job=<job-name> ]"), false},
-
+
{ NT_("prune"), prunecmd, _("Prune expired records from catalog"),
NT_("files | jobs | snapshot [client=<client-name>] | client=<client-name> | \n"
"\t[expired] [all | allpools | allfrompool] [pool=<pool>] [mediatype=<type>] volume=<volume-name> [yes]"),
"\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
"\n\t enabled=<yes/no> recyclepool=<pool> actiononpurge=<action>"
"\n\t allfrompool=<pool> fromallpools frompool"
+ "\n\t volumeprotect [pool=<pool> | volume=<vol> | storage=<sd> | device=<dev>]"
"\njobid=<id> [client=<client> | starttime=<universal-time-specification> | priority=<int> |"
"\n\t pool=<pool> | comment=<str> | reviewed=<int>"),true},
{ NT_("use"), use_cmd, _("Use catalog xxx"), NT_("catalog=<catalog>"), false},
uint64_t VolBytes = 0;
uint64_t VolABytes = 0;
uint32_t VolType = 0;
+ int UseProtect = 0;
STORE *wstore = ua->jcr->store_mngr->get_wstore();
if (!(sd=open_sd_bsock(ua))) {
while (bget_dirmsg(ua->jcr, sd, BSOCK_TYPE_SD) >= 0) {
ua->send_msg("%s", sd->msg);
- if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu VolABytes=%lld VolType=%d ",
- &VolBytes, &VolABytes, &VolType) == 3) {
+ if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu VolABytes=%lld VolType=%ld UseProtect=%d",
+ &VolBytes, &VolABytes, &VolType, &UseProtect) == 4) {
ok = true;
if (media_record_exists) { /* we update it */
mr->VolBytes = VolBytes;
mr->VolABytes = VolABytes;
mr->VolType = VolType;
mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
+ mr->UseProtect = UseProtect;
set_storageid_in_mr(wstore, mr);
if (!db_update_media_record(ua->jcr, ua->db, mr)) {
ua->error_msg("%s", db_strerror(ua->db));
mr->VolType = VolType;
mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */
mr->Enabled = 1;
+ mr->UseProtect = UseProtect;
set_storageid_in_mr(wstore, mr);
if (db_create_media_record(ua->jcr, ua->db, mr)) {
ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"),
static bool update_pool(UAContext *ua);
static bool update_job(UAContext *ua);
static bool update_stats(UAContext *ua);
+static int update_volumeprotect_cmd(UAContext *ua);
/*
* Update a Pool Record in the database.
* updates long term statistics
* update jobid [starttime=...]
* updates job record
+ * update volumeprotect [pool=... volume=... storage=...]
+ * send protect command to the storage daemon
*/
int update_cmd(UAContext *ua, const char *cmd)
{
NT_("stats"), /* 6 */
NT_("snap"), /* 7 */
NT_("snapshot"),/* 8 */
+ NT_("volumeprotect"), /* 9 */
NULL};
if (!open_client_db(ua)) {
case 8:
update_snapshot(ua);
return 1;
+ case 9:
+ return update_volumeprotect_cmd(ua);
default:
break;
}
add_prompt(ua, _("Slots from autochanger"));
add_prompt(ua, _("Long term statistics"));
add_prompt(ua, _("Snapshot parameters"));
+ add_prompt(ua, _("Volume protection attributes on Storage Daemon"));
switch (do_prompt(ua, _("item"), _("Choose catalog item to update"), NULL, 0)) {
case 0:
update_volume(ua);
case 4:
update_snapshot(ua);
break;
+ case 5:
+ return update_volumeprotect_cmd(ua);
default:
break;
}
break;
case 7: /* Slot */
- ua->info_msg(_("Current Slot is: %d\n"), mr.Slot);
+ ua->info_msg(_("Current Slot is: %ld\n"), mr.Slot);
if (!get_pint(ua, _("Enter new Slot: "))) {
return 0;
}
break;
case 8: /* InChanger */
- ua->info_msg(_("Current InChanger flag is: %d\n"), mr.InChanger);
+ ua->info_msg(_("Current InChanger flag is: %ld\n"), mr.InChanger);
bsnprintf(buf, sizeof(buf), _("Set InChanger flag for Volume \"%s\": yes/no: "),
mr.VolumeName);
if (!get_yesno(ua, buf)) {
if (!db_update_media_record(ua->jcr, ua->db, &mr)) {
ua->error_msg(_("Error updating media record Slot: ERR=%s"), db_strerror(ua->db));
} else {
- ua->info_msg(_("New InChanger flag is: %d\n"), mr.InChanger);
+ ua->info_msg(_("New InChanger flag is: %ld\n"), mr.InChanger);
}
break;
return 1;
case 14:
- ua->info_msg(_("Current Enabled is: %d\n"), mr.Enabled);
+ ua->info_msg(_("Current Enabled is: %ld\n"), mr.Enabled);
if (!get_cmd(ua, _("Enter new Enabled: "))) {
return 0;
}
}
return true;
}
+
+
+
+struct media_protect {
+ DBId_t id;
+ char volname[MAX_NAME_LENGTH];
+ char storage[MAX_NAME_LENGTH];
+ char mediatype[MAX_NAME_LENGTH];
+};
+
+/*
+ * Called here to retrieve an string list from the database
+ */
+static int media_protect_list_handler(void *ctx, int num_fields, char **row)
+{
+ alist *val = (alist *)ctx;
+
+ if (row[0] && row[1] && row[2] && row[3]) {
+ struct media_protect *a = (struct media_protect *)malloc(sizeof(struct media_protect));
+ a->id = str_to_uint64(row[0]);
+ bstrncpy(a->mediatype, row[1], sizeof(a->mediatype));
+ bstrncpy(a->volname, row[2], sizeof(a->volname));
+ bstrncpy(a->storage, row[3], sizeof(a->storage));
+ val->append(a);
+ }
+
+ return 0;
+}
+
+/*
+ * Protect a volume. If the command is executed from RunScript Admin
+ * the return code of the commnand is used to update the status.
+ */
+static int update_volumeprotect_cmd(UAContext *ua)
+{
+ USTORE ustore;
+ BSOCK *sd = NULL;
+ JCR *jcr = ua->jcr;
+ char dev_name[MAX_NAME_LENGTH];
+ char prev_sd[MAX_NAME_LENGTH];
+ char *selected_dev_name = NULL;
+ struct media_protect *elt;
+ int drive, i, ret=1;
+ alist list(20, owned_by_alist);
+ POOL_MEM tmp, filter;
+
+ *prev_sd = *dev_name = 0;
+
+ for (i = 0; i < ua->argc ; i++) {
+ if (strcasecmp(ua->argk[i], "device") == 0) {
+ if (!is_name_valid(ua->argv[i], tmp.handle())) {
+ ua->error_msg(_("Invalid device name. %s"), tmp.c_str());
+ return 0;
+ }
+ bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
+
+ } else if (strcasecmp(ua->argk[i], "storage") == 0) {
+ if (!is_name_valid(ua->argv[i], tmp.handle())) {
+ ua->error_msg(_("Invalid storage name. %s"), tmp.c_str());
+ return 0;
+ }
+ Mmsg(tmp, " AND Storage.Name = '%s' ", ua->argv[i]);
+ pm_strcat(filter, tmp);
+
+ } else if (strcasecmp(ua->argk[i], "pool") == 0) {
+ if (!is_name_valid(ua->argv[i], tmp.handle())) {
+ ua->error_msg(_("Invalid pool name. %s"), tmp.c_str());
+ return 0;
+ }
+ Mmsg(tmp, " AND Pool.Name = '%s' ", ua->argv[i]);
+ pm_strcat(filter, tmp);
+
+ } else if (strcasecmp(ua->argk[i], "volume") == 0) {
+ if (!is_name_valid(ua->argv[i], tmp.handle())) {
+ ua->error_msg(_("Invalid volume name. %s"), tmp.c_str());
+ return 0;
+ }
+ Mmsg(tmp, " AND Media.VolumeName = '%s' ", ua->argv[i]);
+ pm_strcat(filter, tmp);
+ }
+ }
+
+ if (!open_client_db(ua)) {
+ return 0;
+ }
+
+ Mmsg(tmp, "SELECT Media.MediaId, Media.MediaType, Media.VolumeName, Storage.Name "
+ "FROM Media JOIN Storage USING (StorageId) JOIN Pool USING (PoolId) "
+ "WHERE UseProtect=1 AND Protect=0 AND VolStatus IN ('Used', 'Full') %s "
+ "ORDER BY Storage.Name", filter.c_str());
+
+ db_sql_query(ua->db, tmp.c_str(),
+ media_protect_list_handler, &list);
+
+ if (list.size() > 0) {
+ ua->send_msg(_("Found %d volumes with status Used/Full that must be protected\n"), list.size());
+
+ } else {
+ ua->send_msg(_("No volume found to protect\n"));
+ return 1;
+ }
+
+ foreach_alist(elt, &list) {
+ if (strcmp(prev_sd, elt->storage) != 0) {
+ ustore.store = (STORE*) GetResWithName(R_STORAGE, elt->storage);
+ pm_strcpy(ustore.store_source, "Catalog source");
+
+ if (!ustore.store) {
+ continue;
+ }
+ bstrncpy(prev_sd, elt->storage, sizeof(prev_sd));
+ jcr->store_mngr->set_wstorage(ustore.store, ustore.store_source);
+ drive = 0; //get_storage_drive(ua, ustore.store);
+
+ if (*dev_name) {
+ selected_dev_name = dev_name;
+
+ } else {
+ selected_dev_name = ustore.store->dev_name();
+ }
+
+ if (sd) {
+ sd->signal(BNET_TERMINATE);
+ free_bsock(ua->jcr->store_bsock);
+ sd = NULL;
+ }
+
+ if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
+ ua->error_msg(_("Failed to connect to Storage daemon.\n"));
+ ret = 0;
+ continue;
+ }
+ sd = jcr->store_bsock;
+ bstrncpy(prev_sd, ustore.store->hdr.name, sizeof(prev_sd));
+
+ build_connecting_info_log(_("Storage"), ustore.store->name(),
+ get_storage_address(NULL, ustore.store), ustore.store->SDport,
+ sd->tls ? true : false, tmp.addr());
+
+ ua->send_msg("%s", tmp.c_str());
+ }
+ bash_spaces(elt->mediatype);
+ bash_spaces(selected_dev_name);
+ bash_spaces(elt->volname);
+
+ sd->fsend("volumeprotect mediatype=%s device=%s volume=%s drive=%d\n",
+ elt->mediatype, selected_dev_name, elt->volname, drive);
+
+ unbash_spaces(elt->mediatype);
+ unbash_spaces(selected_dev_name);
+ unbash_spaces(elt->volname);
+
+ if (bget_dirmsg(jcr, sd, BSOCK_TYPE_UNKN) > 0 && (strncmp(sd->msg, "3000", 4) == 0)) {
+ /* Keep track of this important event */
+ ua->send_events("DC0013", EVENTS_TYPE_COMMAND, "volumeprotect storage=%s dev=%s volume=%s",
+ elt->storage, selected_dev_name, elt->volname);
+ ua->send_msg("%s", sd->msg);
+ Mmsg(tmp, "UPDATE Media SET Protect=1 WHERE MediaId=%d", elt->id);
+ db_lock(ua->db);
+ if (!db_sql_query(ua->db, tmp.c_str(), NULL, NULL)) {
+ ua->error_msg("Unable to update volume record. %s\n", ua->db->errmsg);
+ ret = 0;
+ }
+ db_unlock(ua->db);
+ } else {
+ ua->error_msg("%s", sd->msg);
+ ret = 0;
+ }
+ }
+
+ if (sd) {
+ sd->signal(BNET_TERMINATE);
+ free_bsock(ua->jcr->store_bsock);
+ }
+ return ret;
+}
}
if (dev->device->set_vol_immutable) {
+ dev->VolCatInfo.UseProtect = 1;
/* Set volume as immutable */
- if (!dev->set_immutable(dev->getVolCatName())) {
+ if (!dev->set_immutable(dev->getVolCatName(), &dev->errmsg)) {
/* We may proceed with that but warn the user */
Jmsg(dcr->jcr, M_WARNING, 0, _("Failed to set the volume %s on device %s as immutable, ERR=%s.\n"),
dev->getVolCatName(), dev->print_name(), dev->errmsg);
- } else if (!quiet) {
- Jmsg(dcr->jcr, M_INFO, 0, _("Marking Volume \"%s\" as immutable\n"),
- dev->getVolCatName());
+ } else {
+ if (!quiet) {
+ Jmsg(dcr->jcr, M_INFO, 0, _("Marking Volume \"%s\" as immutable\n"),
+ dev->getVolCatName());
+ }
+ events_send_msg(dcr->jcr, "SJ0003", EVENTS_TYPE_VOLUME, me->hdr.name, (intptr_t)dcr->jcr,
+ "Mark Volume \"%s\" as immutable", dev->getVolCatName());;
+ dev->VolCatInfo.Protect = 1;
}
}
if (dev->device->set_vol_read_only) {
+ dev->VolCatInfo.UseProtect = 1;
/* Set volume as immutable/read only */
- if (dev->set_atime(time(NULL) + dev->device->min_volume_protection_time) < 0) {
+ if (dev->set_atime(dev->m_fd, dev->getVolCatName(), time(NULL) + dev->device->min_volume_protection_time) < 0) {
Jmsg(dcr->jcr, M_WARNING, 0, _("Failed to set the volume %s on device %s in atime retention, ERR=%s.\n"),
dev->getVolCatName(), dev->print_name(), dev->errmsg);
}
- if (dev->set_readonly() < 0) {
+ if (dev->set_readonly(dev->m_fd, dev->getVolCatName()) < 0) {
+ berrno be;
/* We may proceed with that but warn the user */
Jmsg(dcr->jcr, M_WARNING, 0, _("Failed to set the volume %s on device %s in read-only, ERR=%s.\n"),
- dev->getVolCatName(), dev->print_name(), dev->errmsg);
- } else if (!quiet) {
- Jmsg(dcr->jcr, M_INFO, 0, _("Marking Volume \"%s\" as read-only\n"),
- dev->getVolCatName());
+ dev->getVolCatName(), dev->print_name(), be.bstrerror());
+ } else {
+ if (!quiet) {
+ Jmsg(dcr->jcr, M_INFO, 0, _("Marking Volume \"%s\" as read-only\n"),
+ dev->getVolCatName());
+ }
+ dev->VolCatInfo.Protect = 1;
+ events_send_msg(dcr->jcr, "SJ0003", EVENTS_TYPE_VOLUME, me->hdr.name, (intptr_t)dcr->jcr,
+ "Mark Volume \"%s\" as read-only", dev->getVolCatName());;
}
}
uint64_t min_free_space; /* Minimum free disk space */
int free_space_errno; /* indicates errno getting freespace */
bool truncating; /* if set, we are currently truncating */
-
+
utime_t vol_poll_interval; /* interval between polling Vol mount */
DEVRES *device; /* pointer to Device Resource */
VOLRES *vol; /* Pointer to Volume reservation item */
virtual bool rewind(DCR *dcr);
virtual bool truncate(DCR *dcr);
virtual int truncate_cache(DCR *dcr, const char *VolName, int64_t *size, POOLMEM *&msg) { return 0; };
- virtual bool get_cloud_volumes_list(DCR* dcr, alist *volumes, POOLMEM *&err) { pm_strcpy(err, "Not implemented"); return false;};
- virtual bool get_cloud_volume_parts_list(DCR *dcr, const char *VolumeName, ilist *parts, POOLMEM *&err) { pm_strcpy(err, "Not implemented"); return false; };
- virtual uint32_t get_cloud_upload_transfer_status(POOL_MEM &msg, bool verbose) { pm_strcpy(msg, "Not implemented"); return 0; };
+ virtual bool get_cloud_volumes_list(DCR* dcr, alist *volumes, POOLMEM *&err) { pm_strcpy(err, _("Not implemented")); return false;};
+ virtual bool get_cloud_volume_parts_list(DCR *dcr, const char *VolumeName, ilist *parts, POOLMEM *&err) { pm_strcpy(err, _("Not implemented")); return false; };
+ virtual uint32_t get_cloud_upload_transfer_status(POOL_MEM &msg, bool verbose) { pm_strcpy(msg, _("Not implemented")); return 0; };
virtual void get_api_cloud_upload_transfer_status(OutputWriter &, bool) {};
- virtual uint32_t get_cloud_download_transfer_status(POOL_MEM &msg, bool verbose) { pm_strcpy(msg, "Not implemented"); return 0; };
+ virtual uint32_t get_cloud_download_transfer_status(POOL_MEM &msg, bool verbose) { pm_strcpy(msg, _("Not implemented")); return 0; };
virtual void get_api_cloud_download_transfer_status(OutputWriter &, bool) {};
virtual bool upload_cache(DCR *dcr, const char *VolName, uint32_t truncate, POOLMEM *&err) {return true; };
virtual bool open_device(DCR *dcr, int omode) = 0;
virtual void set_ateof(); /* in dev.c */
/* Methods below are responsible for managing
* the append and immutable flags on device-specific volumes */
- virtual bool set_append_only(const char *vol_name) { return true; };
- virtual bool clear_append_only(const char *vol_name) { return true; };
- virtual bool set_immutable(const char *vol_name) { return true; };
- virtual bool clear_immutable(const char *vol_name) { return true; };
+ virtual bool set_append_only(const char *vol_name, POOLMEM **error) { pm_strcpy(error, _("Not Implemented")); return false; };
+ virtual bool clear_append_only(const char *vol_name, POOLMEM **error) { pm_strcpy(error, _("Not Implemented")); return false; };
+ virtual bool set_immutable(const char *vol_name, POOLMEM **error) { pm_strcpy(error, _("Not Implemented")); return false; };
+ virtual bool clear_immutable(const char *vol_name, POOLMEM **error) { pm_strcpy(error, _("Not Implemented")); return false; };
virtual bool check_volume_protection_time(const char *vol_name) { return true; };
virtual bool check_for_immutable(const char *vol_name) { return false; };
- virtual bool check_for_read_only(const char *vol_name) { return false; };
- virtual int set_writable() { pm_strcpy(errmsg, _("Not implemented")); return -1;};
- virtual int set_readonly() { pm_strcpy(errmsg, _("Not implemented")); return -1;};
- virtual int set_atime(btime_t val) { pm_strcpy(errmsg, _("Not implemented")); return -1;};
+ virtual bool check_for_read_only(int fd, const char *vol_name) { return false; };
+ virtual int set_writable(int fd, const char *vol_name) { errno=ENOSYS; return -1;};
+ virtual int set_readonly(int fd, const char *vol_name) { errno=ENOSYS; return -1;};
+ virtual int set_atime(int fd, const char *vol_name, btime_t val) { errno=ENOSYS; return -1;};
virtual int use_protect() { return 0; };
virtual const char *print_type() = 0; /* in dev.c */
virtual const char *print_driver_type() { return "";};
static bool cloud_list_cmd(JCR *jcr);
static bool cloud_prunecache_cmd(JCR *jcr);
static bool changer_cmd(JCR *sjcr);
+static bool volumeprotect_cmd(JCR *jcr);
static bool do_label(JCR *jcr, int relabel);
static DCR *find_device(JCR *jcr, POOL_MEM &dev_name,
- POOLMEM *media_type, int drive);
+ char *media_type, int drive);
static DCR *find_any_device(JCR *jcr, POOL_MEM &dev_name,
- POOLMEM *media_type, int drive);
+ char *media_type, int drive);
static void read_volume_label(JCR *jcr, DCR *dcr, DEVICE *dev, int Slot);
static void label_volume_if_ok(DCR *dcr, char *oldname,
char *newname, char *poolname,
{"storage", storage_cmd, 0}, /* get SD addr from Dir */
{"truncate", truncate_cache_cmd, 0},
{"upload", upload_cmd, 0},
+ {"volumeprotect", volumeprotect_cmd, 0},
{"prunecache", cloud_prunecache_cmd, 0},
{"cloudlist", cloud_list_cmd, 0}, /* List volumes/parts in the cloud */
{"unmount", unmount_cmd, 0},
} else {
type = 0;
}
- dir->fsend("3000 OK label. VolBytes=%lld VolABytes=%lld VolType=%d Volume=\"%s\" Device=%s\n",
+ dir->fsend("3000 OK label. VolBytes=%lld VolABytes=%lld VolType=%d UseProtect=%d Volume=\"%s\" Device=%s\n",
volCatBytes, dev->VolCatInfo.VolCatAdataBytes,
- type, newname, dev->print_name());
+ type, dev->use_protect(), newname, dev->print_name());
break;
case VOL_TYPE_ERROR:
dir->fsend(_("3917 Failed to label Volume: ERR=%s\n"), dcr->jcr->errmsg);
* returns it.
*/
static DCR *find_device(JCR *jcr, POOL_MEM &devname,
- POOLMEM *media_type, int drive)
+ char *media_type, int drive)
{
DEVRES *device;
AUTOCHANGER *changer;
* argument, but this is easier for the moment.
*/
static DCR *find_any_device(JCR *jcr, POOL_MEM &devname,
- POOLMEM *media_type, int drive)
+ char *media_type, int drive)
{
DEVRES *device;
AUTOCHANGER *changer;
return dcr;
}
+/*
+ * Protect command from Director
+ */
+static bool volumeprotect_cmd(JCR *jcr)
+{
+ BSOCK *dir = jcr->dir_bsock;
+ char mediatype[MAX_NAME_LENGTH];
+ char volume[MAX_NAME_LENGTH];
+ POOL_MEM device, tmp;
+ int32_t drive;
+ bool ok;
+
+ ok = sscanf(dir->msg, "volumeprotect mediatype=%127s device=%127s volume=%127s drive=%d",
+ mediatype, device.c_str(), volume, &drive) == 4;
+ if (ok) {
+ unbash_spaces(mediatype);
+ unbash_spaces(device.c_str());
+ unbash_spaces(volume);
+ DCR *dcr = find_any_device(jcr, device, mediatype, drive);
+ if (dcr) {
+ DEVICE *dev = dcr->dev;
+ /* Everything can be done without a lock because we don't touch anything
+ * inside the device structure
+ */
+ if (dev->device->set_vol_immutable) {
+ /* Set volume as immutable */
+ if (!dev->set_immutable(volume, tmp.handle())) {
+ /* We may proceed with that but warn the user */
+ dir->fsend(_("3900 Unable to set immutable flag %s\n"), tmp.c_str());
+
+ } else {
+ dir->fsend(_("3000 Mark volume \"%s\" as immutable\n"), volume);
+ events_send_msg(jcr, "SJ0003", EVENTS_TYPE_VOLUME, me->hdr.name, (intptr_t)jcr,
+ "Mark volume \"%s\" as immutable", volume);
+ }
+
+ } else if (dev->device->set_vol_read_only) {
+ /* Set volume as immutable/read only */
+ pm_strcpy(tmp, "");
+ if (dev->set_atime(-1, volume, time(NULL) + dev->device->min_volume_protection_time) < 0) {
+ berrno be;
+ Mmsg(tmp, _(" Failed to set the volume %s on device %s in atime retention, ERR=%s.\n"),
+ volume, dev->print_name(), be.bstrerror());
+ }
+ if (dev->set_readonly(-1, volume) < 0) {
+ berrno be;
+ /* We may proceed with that but warn the user */
+ dir->fsend(_("3900 Failed to set the volume %s on device %s in read-only, ERR=%s.%s\n"),
+ volume, dev->print_name(), be.bstrerror(), tmp.c_str());
+ } else {
+ dir->fsend(_("3000 Marking volume \"%s\" as read-only.\n"), volume);
+ events_send_msg(jcr, "SJ0003", EVENTS_TYPE_VOLUME, me->hdr.name, (intptr_t)jcr,
+ "Mark volume \"%s\" as read-only", volume);
+ }
+ } else {
+ dir->fsend(_("3900 Device %s not configured for ReadOnly or Immutable\n"), dev->device->hdr.name);
+ }
+ free_dcr(dcr);
+ } else {
+ dir->fsend(_("3901 Unable to find a device to perform the volumeprotect command\n"));
+ }
+ } else {
+ dir->fsend(_("3907 Error scanning \"volumeprotect\" command\n"));
+ }
+ return true;
+}
/*
* Mount command from Director
getVolCatName());
} else {
/* Flag can be cleared, volume can probably be reused */
- if (clear_immutable(getVolCatName())) {
+ if (clear_immutable(getVolCatName(), &errmsg)) {
/* It should be now possible to open the device with desired mode */
if ((m_fd = ::open(archive_name.c_str(), mode|O_CLOEXEC|append, 0640)) < 0) {
berrno be;
dev_errno = errno;
- Mmsg3(errmsg, _("Could not open(%s,%s,0640): ERR=%s\n"),
- archive_name.c_str(), mode_to_str(omode), be.bstrerror());
- Dmsg1(40, "open failed: %s", errmsg);
+ MmsgD3(40, errmsg, _("Could not open(%s,%s,0640): ERR=%s\n"),
+ archive_name.c_str(), mode_to_str(omode), be.bstrerror());
}
}
}
/* Volume is not immutable, that should succeed */
berrno be;
dev_errno = errno;
- Mmsg3(errmsg, _("Could not open(%s,%s,0640): ERR=%s\n"),
+ MmsgD3(40, errmsg, _("Could not open(%s,%s,0640): ERR=%s\n"),
archive_name.c_str(), mode_to_str(omode), be.bstrerror());
- Dmsg1(40, "open failed: %s", errmsg);
}
}
return m_fd >= 0;
}
-int file_dev::set_writable()
+int file_dev::set_writable(int fd, const char *vol_name)
{
POOL_MEM fname;
- get_volume_fpath(getVolCatName(), fname.handle());
- int ret = bchmod(m_fd, fname.c_str(), 0600);
+ get_volume_fpath(vol_name, fname.handle());
+ /* Need write access only for the storage daemon user We can extend to the
+ * group, but in many case, it's a system group, so all users with tape/disk
+ * group can have access
+ */
+ int ret = bchmod(fd, fname.c_str(), 0600);
if (ret < 0) {
berrno be;
- Mmsg(errmsg, _("Unable to change permission to 0600. ERR=%s\n"), be.bstrerror());
+ Dmsg1(DT_VOLUME|50, _("Unable to change permission to 0600. ERR=%s\n"), be.bstrerror());
}
return ret;
}
-int file_dev::set_readonly()
+int file_dev::set_readonly(int fd, const char *vol_name)
{
POOL_MEM fname;
- get_volume_fpath(getVolCatName(), fname.handle());
- int ret = bchmod(m_fd, fname.c_str(), 0400);
+ get_volume_fpath(vol_name, fname.handle());
+ /* Mark the file -r-------- */
+ int ret = bchmod(fd, fname.c_str(), 0400);
if (ret < 0) {
berrno be;
- Mmsg(errmsg, _("Unable to change permission to 0400. ERR=%s\n"), be.bstrerror());
+ Dmsg1(DT_VOLUME|50, _("Unable to change permission to 0400. ERR=%s\n"), be.bstrerror());
}
return ret;
}
-int file_dev::set_atime(btime_t val)
+int file_dev::set_atime(int fd, const char *vol_name, btime_t val)
{
struct stat sp;
int ret;
POOL_MEM fname;
- get_volume_fpath(getVolCatName(), fname.handle());
- if (bstat(m_fd, fname.c_str(), &sp) < 0) {
+ get_volume_fpath(vol_name, fname.handle());
+ if (bstat(fd, fname.c_str(), &sp) < 0) {
berrno be;
- Mmsg(errmsg, _("Unable to stat %s. ERR=%s\n"), fname.c_str(), be.bstrerror());
+ Dmsg2(DT_VOLUME|50, _("Unable to stat %s. ERR=%s\n"), fname.c_str(), be.bstrerror());
return -1;
}
- ret = set_own_time(m_fd, fname.c_str(), val, sp.st_mtime);
+ ret = set_own_time(fd, fname.c_str(), val, sp.st_mtime);
if (ret < 0) {
berrno be;
- Mmsg(errmsg, _("Unable to set atime/mtime to %s. ERR=%s\n"), fname.c_str(), be.bstrerror());
+ Dmsg2(DT_VOLUME|50, _("Unable to set atime/mtime to %s. ERR=%s\n"), fname.c_str(), be.bstrerror());
}
return ret;
}
/* Need to clear the APPEND flag before truncating */
if (dev->device->set_vol_append_only) {
- if (!clear_append_only(dcr->VolumeName)) {
+ if (!clear_append_only(dcr->VolumeName, &errmsg)) {
Mmsg2(errmsg, _("Unable to clear append_only flag for volume %s on device %s.\n"),
dcr->VolumeName, print_name());
return false;
}
if (dev->device->set_vol_read_only) {
- if (set_writable() < 0) {
+ if (set_writable(dev->m_fd, dcr->VolumeName) < 0) {
+ berrno be;
Mmsg3(errmsg, _("Unable to set write permission for volume %s on device %s. %s\n"),
- dcr->VolumeName, print_name(), dev->errmsg);
+ dcr->VolumeName, print_name(), be.bstrerror());
return false;
}
}
pm_strcat(fname, ADATA_EXTENSION);
}
- Dmsg1(DT_VOLUME|50, "Full volume path built: %s\n", *fname);
+ Dmsg1(DT_VOLUME|250, "Full volume path built: %s\n", *fname);
}
/* Check if volume can be reused or not yet.
} else { // ReadOnly, we check both and we take the biggest one
expiration_time = MAX(sp.st_atime, sp.st_mtime + device->min_volume_protection_time);
}
- char dt[50], dt2[50];
- bstrftime(dt, sizeof(dt), expiration_time);
- bstrftime(dt2, sizeof(dt2), now);
if (expiration_time > now) {
+ char dt[50], dt2[50];
+ bstrftime(dt, sizeof(dt), expiration_time);
+ bstrftime(dt2, sizeof(dt2), now);
+
Mmsg1(errmsg, _("Immutable/ReadOnly flag cannot be cleared for volume: %s, "
"because Minimum Volume Protection Time hasn't expired yet.\n"),
vol_name);
POOL_MEM fname(PM_FNAME);
if (!is_attribute_supported(attr)) {
- Mmsg2(errmsg, "File attribute 0x%0x is not supported for volume %s\n", attr, vol_name);
+ errno = ENOSYS;
return ret;
}
if ((tmp_fd = d_open(fname.c_str(), O_RDONLY|O_CLOEXEC)) < 0) {
berrno be;
Dmsg2(DT_VOLUME|50, "Failed to open %s, ERR=%s\n", fname.c_str(), be.bstrerror());
- Mmsg2(errmsg, "Failed to open %s, ERR=%s", fname.c_str(), be.bstrerror());
return ret;
}
if (ioctl_ret < 0) {
berrno be;
Dmsg2(DT_VOLUME|50, "Failed to get attributes for %s, ERR=%s\n", fname.c_str(), be.bstrerror());
- Mmsg2(errmsg, "Failed to get attributes for %s, ERR=%s", fname.c_str(), be.bstrerror());
} else {
ret = get_attr & attr;
const char *msg_str = ret ? "set" : "not set";
#endif // HAVE_FS_IOC_GETFLAGS
#ifdef HAVE_FS_IOC_SETFLAGS
-bool file_dev::modify_fattr(const char *vol_name, int attr, bool set)
+bool file_dev::modify_fattr(const char *vol_name, int attr, bool set, POOLMEM **error)
{
bool ret = false;
int tmp_fd, ioctl_ret;
POOL_MEM fname(PM_FNAME);
if (!got_caps_needed) {
- Dmsg1(DT_VOLUME|50, "Early return from modify_fattr for volume %s, do not have caps needed\n",
- vol_name);
- return true; /* We cannot set needed attributes, no work here */
+ MmsgD1(DT_VOLUME|50, error, "Early return from modify_fattr for volume %s, do not have caps needed\n",
+ vol_name);
+ return false; /* We cannot set needed attributes, no work here */
}
if (!is_attribute_supported(attr)) {
- Dmsg2(DT_VOLUME|50, "File attribute 0x%0x is not supported for volume %s\n", attr, vol_name);
- Mmsg2(errmsg, "File attribute 0x%0x is not supported for volume %s\n", attr, vol_name);
+ MmsgD2(DT_VOLUME|50, error, "File attribute 0x%0x is not supported for volume %s\n", attr, vol_name);
return ret;
}
if ((tmp_fd = d_open(fname.c_str(), O_RDONLY|O_CLOEXEC)) < 0) {
berrno be;
- Dmsg2(DT_VOLUME|50, "Failed to open %s, ERR=%s", fname.c_str(), be.bstrerror());
- Mmsg2(errmsg, "Failed to open %s, ERR=%s", fname.c_str(), be.bstrerror());
+ MmsgD2(DT_VOLUME|50, error, "Failed to open %s, ERR=%s", fname.c_str(), be.bstrerror());
return false;
}
ioctl_ret = d_ioctl(tmp_fd, FS_IOC_GETFLAGS, (char *)&get_attr);
if (ioctl_ret < 0) {
berrno be;
- Dmsg2(DT_VOLUME|50, "Failed to get attributes for %s, ERR=%s", fname.c_str(), be.bstrerror());
- Mmsg2(errmsg, "Failed to get attributes for %s, ERR=%s", fname.c_str(), be.bstrerror());
+ MmsgD2(DT_VOLUME|50, error, "Failed to get attributes for %s, ERR=%s", fname.c_str(), be.bstrerror());
+ goto bail_out;
+ }
+
+ /* The flag might be already present, so, no need to set or clear it */
+ if (set && (get_attr & attr)) {
+ ret = true;
+ goto bail_out;
+ }
+ if (!set && (get_attr & attr) == 0) {
+ ret = true;
goto bail_out;
}
if (set) {
/* Add new attribute to the currently set ones */
set_attr = get_attr | attr;
+
} else {
/* Inverse the desired attribute and later and it with the current state
- * so that we clear only desired flag and do not touch all the rest */
+ * so that we clear only desired flag and do not touch all the rest
+ */
int rev_mask = ~attr;
set_attr = get_attr & rev_mask;
}
if (ioctl_ret < 0) {
berrno be;
if (set) {
- Dmsg3(DT_VOLUME|50, "Failed to set 0x%0x attribute for %s, err: %d\n", attr, fname.c_str(), errno);
- Mmsg3(errmsg, "Failed to set 0x%0x attribute for %s, err: %d\n", attr, fname.c_str(), errno);
+ MmsgD3(DT_VOLUME|50, error, "Failed to set 0x%0x attribute for %s, err: %d\n", attr, fname.c_str(), errno);
} else {
- Dmsg3(DT_VOLUME|50, "Failed to clear 0x%0x attribute for %s, err: %d\n", attr, fname.c_str(), errno);
- Mmsg3(errmsg, "Failed to clear 0x%0x attribute for %s, err: %d\n", attr, fname.c_str(), errno);
+ MmsgD3(DT_VOLUME|50, error, "Failed to clear 0x%0x attribute for %s, err: %d\n", attr, fname.c_str(), errno);
}
goto bail_out;
}
return ret;
}
#else
-bool file_dev::modify_fattr(const char *vol_name, int attr, bool set)
+bool file_dev::modify_fattr(const char *vol_name, int attr, bool set, POOLMEM **error)
{
- Dmsg3(DT_VOLUME|50, "Returning from mocked modify_fattr() for volume: %s, attr: 0x%08x, set: %d\n",
+ MmsgD3(DT_VOLUME|50, error, "Returning from mocked modify_fattr() for volume: %s, attr: 0x%08x, set: %d\n",
vol_name, attr, set);
- return true;
+ return false;
}
#endif // HAVE_FS_IOC_SETFLAGS
-bool file_dev::set_fattr(const char *vol_name, int attr)
+bool file_dev::set_fattr(const char *vol_name, int attr, POOLMEM **error)
{
- return modify_fattr(vol_name, attr, true);
+ return modify_fattr(vol_name, attr, true, error);
}
-bool file_dev::clear_fattr(const char *vol_name, int attr)
+bool file_dev::clear_fattr(const char *vol_name, int attr, POOLMEM **error)
{
- return modify_fattr(vol_name, attr, false);
+ return modify_fattr(vol_name, attr, false, error);
}
#ifdef HAVE_APPEND_FL
return check_for_attr(vol_name, FS_APPEND_FL);
}
-bool file_dev::set_append_only(const char *vol_name)
+bool file_dev::set_append_only(const char *vol_name, POOLMEM **error)
{
- return set_fattr(vol_name, FS_APPEND_FL);
+ return set_fattr(vol_name, FS_APPEND_FL, error);
}
-bool file_dev::clear_append_only(const char *vol_name)
+bool file_dev::clear_append_only(const char *vol_name, POOLMEM **error)
{
- return clear_fattr(vol_name, FS_APPEND_FL);
+ return clear_fattr(vol_name, FS_APPEND_FL, error);
}
#else
bool file_dev::append_open_needed(const char *vol_name)
Dmsg1(DT_VOLUME|50, "Returning from mocked append_open_needed() for volume: %s\n", vol_name);
return false;
}
-bool file_dev::set_append_only(const char *vol_name)
+bool file_dev::set_append_only(const char *vol_name, POOLMEM **error)
{
- Dmsg1(DT_VOLUME|50, "Returning from mocked set_append_only() for volume: %s\n", vol_name);
- return true;
+ MmsgD1(DT_VOLUME|50, error, "Returning from mocked set_append_only() for volume: %s\n", vol_name);
+ return false;
}
-bool file_dev::clear_append_only(const char *vol_name)
+bool file_dev::clear_append_only(const char *vol_name, POOLMEM **error)
{
- Dmsg1(DT_VOLUME|50, "Returning from mocked clear_append_only() for volume: %s\n", vol_name);
- return true;
+ MmsgD1(DT_VOLUME|50, error, "Returning from mocked clear_append_only() for volume: %s\n", vol_name);
+ return false;
}
#endif // HAVE_APPEND_FL
#ifdef HAVE_IMMUTABLE_FL
-bool file_dev::set_immutable(const char *vol_name)
+bool file_dev::set_immutable(const char *vol_name, POOLMEM **error)
{
- return set_fattr(vol_name, FS_IMMUTABLE_FL);
+ return set_fattr(vol_name, FS_IMMUTABLE_FL, error);
}
-bool file_dev::clear_immutable(const char *vol_name)
+bool file_dev::clear_immutable(const char *vol_name, POOLMEM **error)
{
- return clear_fattr(vol_name, FS_IMMUTABLE_FL);
+ return clear_fattr(vol_name, FS_IMMUTABLE_FL, error);
}
bool file_dev::check_for_immutable(const char* vol_name)
return check_for_attr(vol_name, FS_IMMUTABLE_FL);
}
#else
-bool file_dev::set_immutable(const char *vol_name)
+bool file_dev::set_immutable(const char *vol_name, POOLMEM **error)
{
- Dmsg1(DT_VOLUME|50, "Returning from mocked set_immutable() for volume: %s\n", vol_name);
- return true;
+ MmsgD1(DT_VOLUME|50, error, "Returning from mocked set_immutable() for volume: %s\n", vol_name);
+ return false;
}
-bool file_dev::clear_immutable(const char *vol_name)
+bool file_dev::clear_immutable(const char *vol_name, POOLMEM **error)
{
- Dmsg1(DT_VOLUME|50, "Returning from mocked clear_immutable() for volume: %s\n", vol_name);
- return true;
+ MmsgD1(DT_VOLUME|50, error, "Returning from mocked clear_immutable() for volume: %s\n", vol_name);
+ return false;
}
bool file_dev::check_for_immutable(const char* vol_name)
}
#endif // HAVE_IMMUTABLE_FL
-bool file_dev::check_for_read_only(const char *vol)
+bool file_dev::check_for_read_only(int fd, const char *vol)
{
if (!device->set_vol_read_only) {
return false; // Feature not used
POOL_MEM fname;
get_volume_fpath(vol, fname.handle());
- if (stat(fname.c_str(), &sp) < 0) {
+ if (bstat(fd, fname.c_str(), &sp) < 0) {
return false; // Not found, no problem?
}
class file_dev : public DEVICE {
private:
void get_volume_fpath(const char *vol_name, POOLMEM **buf);
- bool modify_fattr(const char *vol_name, int attr, bool set);
+ bool modify_fattr(const char *vol_name, int attr, bool set, POOLMEM **error);
bool check_for_attr(const char *vol_name, int attr);
- bool set_fattr(const char *vol_name, int attr);
- bool clear_fattr(const char *vol_name, int attr);
- bool check_for_immutable(const char *vol_name);
- bool check_for_read_only(const char *vol_name);
+ bool set_fattr(const char *vol_name, int attr, POOLMEM **error);
+ bool clear_fattr(const char *vol_name, int attr, POOLMEM **error);
bool append_open_needed(const char *vol_name);
bool is_attribute_supported(int attr);
bool open_device(DCR *dcr, int omode);
const char *print_type();
virtual int device_specific_init(JCR *jcr, DEVRES *device);
- bool set_append_only(const char *vol_name);
- bool clear_append_only(const char *vol_name);
- bool set_immutable(const char *vol_name);
- bool clear_immutable(const char *vol_name);
+ bool set_append_only(const char *vol_name, POOLMEM **error);
+ bool clear_append_only(const char *vol_name, POOLMEM **error);
+ bool set_immutable(const char *vol_name, POOLMEM **error);
+ bool clear_immutable(const char *vol_name, POOLMEM **error);
bool check_volume_protection_time(const char *vol_name);
+ bool check_for_immutable(const char *vol_name);
+ bool check_for_read_only(int fd, const char *vol_name);
bool get_os_device_freespace();
bool is_fs_nearly_full(uint64_t threshold);
- int set_writable();
- int set_readonly();
- int set_atime(btime_t val);
+ int set_writable(int fd, const char *vol_name);
+ int set_readonly(int fd, const char *vol_name);
+ int set_atime(int fd, const char *vol_name, btime_t val);
int use_protect();
};
VolCatInfo.VolCatCloudParts = 0;
VolCatInfo.VolLastPartBytes = 0;
VolCatInfo.VolCatType = 0; /* Will be set by dir_update_volume_info() */
+ VolCatInfo.UseProtect = use_protect();
if (recycle) {
VolCatInfo.VolCatMounts++;
VolCatInfo.VolCatRecycles++;
Leave(100);
return false;
}
+ events_send_msg(dcr->jcr, "SJ0004", EVENTS_TYPE_VOLUME, me->hdr.name,
+ (intptr_t)dcr->jcr,
+ "Wrote label on %s volume=\"%s\"",
+ recycle?"recycled":"prelabeled",
+ dcr->VolumeName);
if (recycle) {
Jmsg(jcr, M_INFO, 0, _("Recycled volume \"%s\" on %s device %s, all previous data lost.\n"),
dcr->VolumeName, print_type(), print_name());
+
} else {
Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume \"%s\" on %s device %s\n"),
dcr->VolumeName, print_type(), print_name());
/* Set the append flag on the volume */
if (dev->device->set_vol_append_only) {
- if (!dev->set_append_only(getVolCatName())) {
+ if (!dev->set_append_only(getVolCatName(), &dev->errmsg)) {
Jmsg(jcr, M_WARNING, 0, _("Unable to set the APPEND flag on the volume: %s, err: %s\n"),
getVolCatName(), dev->bstrerror());
goto mount_next_vol;
}
if (dev->device->set_vol_read_only &&
- dev->check_for_read_only(VolumeName) &&
+ dev->check_for_read_only(-1, VolumeName) &&
dev->check_volume_protection_time(VolumeName) == false) {
MmsgD1(dbglvl, jcr->errmsg, _("Skipping Volume %s, "
"because Volume's Protection Period has not expired yet\n"),