From d5ec3d9c3ca596cc46c32c60092e6bf4c9bfee21 Mon Sep 17 00:00:00 2001 From: Eric Bollengier Date: Tue, 13 Sep 2022 17:42:51 +0200 Subject: [PATCH] Add tools to synchronize Read-Only/Immutable flags with the Storage Daemon - Change the interface of the immutable functions to not modify the device - Add Protect fields to the catalog and the SD/DIR protocol --- bacula/src/cats/sql_create.c | 9 +- bacula/src/dird/job.c | 2 +- bacula/src/dird/next_vol.c | 1 - bacula/src/dird/ua_cmds.c | 3 +- bacula/src/dird/ua_label.c | 7 +- bacula/src/dird/ua_update.c | 193 ++++++++++++++++++++++++++++++++- bacula/src/stored/block_util.c | 33 ++++-- bacula/src/stored/dev.h | 26 ++--- bacula/src/stored/dircmd.c | 80 +++++++++++++- bacula/src/stored/file_dev.c | 158 ++++++++++++++------------- bacula/src/stored/file_dev.h | 24 ++-- bacula/src/stored/label.c | 7 ++ bacula/src/stored/mount.c | 2 +- bacula/src/stored/vol_mgr.c | 2 +- 14 files changed, 417 insertions(+), 130 deletions(-) diff --git a/bacula/src/cats/sql_create.c b/bacula/src/cats/sql_create.c index 48a8d76d6..07a91dc8a 100644 --- a/bacula/src/cats/sql_create.c +++ b/bacula/src/cats/sql_create.c @@ -442,9 +442,9 @@ int BDB::bdb_create_media_record(JCR *jcr, MEDIA_DBR *mr) "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), @@ -474,7 +474,8 @@ int BDB::bdb_create_media_record(JCR *jcr, MEDIA_DBR *mr) edit_int64(mr->RecyclePoolId, ed13), mr->Enabled, mr->ActionOnPurge, - edit_uint64(mr->CacheRetention, ed14) + edit_uint64(mr->CacheRetention, ed14), + mr->UseProtect ); diff --git a/bacula/src/dird/job.c b/bacula/src/dird/job.c index f9142c799..b59400833 100644 --- a/bacula/src/dird/job.c +++ b/bacula/src/dird/job.c @@ -1979,7 +1979,7 @@ bool run_console_command(JCR *jcr, const char *cmd) 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); diff --git a/bacula/src/dird/next_vol.c b/bacula/src/dird/next_vol.c index cebea5daf..d60df3c80 100644 --- a/bacula/src/dird/next_vol.c +++ b/bacula/src/dird/next_vol.c @@ -316,7 +316,6 @@ bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr) } } } - /* Check if the Pool quota is respected */ if (!expired && use_max_pool_bytes(jcr)) { POOL_DBR pr; diff --git a/bacula/src/dird/ua_cmds.c b/bacula/src/dird/ua_cmds.c index c3810efd0..fa16e9606 100644 --- a/bacula/src/dird/ua_cmds.c +++ b/bacula/src/dird/ua_cmds.c @@ -166,7 +166,7 @@ static struct cmdstruct commands[] = { /* C { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true}, { NT_("mount"), mount_cmd, _("Mount storage"), NT_("storage= slot= drive= [ device= ] [ jobid= | job= ]"), false}, - + { NT_("prune"), prunecmd, _("Prune expired records from catalog"), NT_("files | jobs | snapshot [client=] | client= | \n" "\t[expired] [all | allpools | allfrompool] [pool=] [mediatype=] volume= [yes]"), @@ -242,6 +242,7 @@ static struct cmdstruct commands[] = { /* C "\n\t maxvolbytes= maxvolfiles= maxvoljobs=" "\n\t enabled= recyclepool= actiononpurge=" "\n\t allfrompool= fromallpools frompool" + "\n\t volumeprotect [pool= | volume= | storage= | device=]" "\njobid= [client= | starttime= | priority= |" "\n\t pool= | comment= | reviewed="),true}, { NT_("use"), use_cmd, _("Use catalog xxx"), NT_("catalog="), false}, diff --git a/bacula/src/dird/ua_label.c b/bacula/src/dird/ua_label.c index c2d2275ef..d12191814 100644 --- a/bacula/src/dird/ua_label.c +++ b/bacula/src/dird/ua_label.c @@ -681,6 +681,7 @@ static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, 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))) { @@ -712,14 +713,15 @@ static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, 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)); @@ -732,6 +734,7 @@ static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, 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"), diff --git a/bacula/src/dird/ua_update.c b/bacula/src/dird/ua_update.c index 927ed9224..988091def 100644 --- a/bacula/src/dird/ua_update.c +++ b/bacula/src/dird/ua_update.c @@ -33,6 +33,7 @@ static int update_volume(UAContext *ua); 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. @@ -48,6 +49,8 @@ static bool update_stats(UAContext *ua); * 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) { @@ -61,6 +64,7 @@ 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)) { @@ -89,6 +93,8 @@ int update_cmd(UAContext *ua, const char *cmd) case 8: update_snapshot(ua); return 1; + case 9: + return update_volumeprotect_cmd(ua); default: break; } @@ -99,6 +105,7 @@ int update_cmd(UAContext *ua, const char *cmd) 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); @@ -115,6 +122,8 @@ int update_cmd(UAContext *ua, const char *cmd) case 4: update_snapshot(ua); break; + case 5: + return update_volumeprotect_cmd(ua); default: break; } @@ -734,7 +743,7 @@ static int update_volume(UAContext *ua) 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; } @@ -742,7 +751,7 @@ static int update_volume(UAContext *ua) 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)) { @@ -757,7 +766,7 @@ static int update_volume(UAContext *ua) 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; @@ -817,7 +826,7 @@ static int update_volume(UAContext *ua) 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; } @@ -1099,3 +1108,179 @@ static bool update_job(UAContext *ua) } 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; +} diff --git a/bacula/src/stored/block_util.c b/bacula/src/stored/block_util.c index 10e8fac76..75ed08921 100644 --- a/bacula/src/stored/block_util.c +++ b/bacula/src/stored/block_util.c @@ -718,30 +718,43 @@ bool is_user_volume_size_reached(DCR *dcr, bool quiet) } 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());; } } diff --git a/bacula/src/stored/dev.h b/bacula/src/stored/dev.h index 92b19b14a..1e4f937e3 100644 --- a/bacula/src/stored/dev.h +++ b/bacula/src/stored/dev.h @@ -342,7 +342,7 @@ public: 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 */ @@ -571,11 +571,11 @@ public: 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; @@ -606,16 +606,16 @@ public: 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 "";}; diff --git a/bacula/src/stored/dircmd.c b/bacula/src/stored/dircmd.c index 23ceecc16..66991ed62 100644 --- a/bacula/src/stored/dircmd.c +++ b/bacula/src/stored/dircmd.c @@ -86,11 +86,12 @@ static bool bootstrap_cmd(JCR *jcr); 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, @@ -139,6 +140,7 @@ static struct dir_cmds cmds[] = { {"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}, @@ -1109,9 +1111,9 @@ static void label_volume_if_ok(DCR *dcr, char *oldname, } 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); @@ -1191,7 +1193,7 @@ static bool read_label(DCR *dcr) * 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; @@ -1269,7 +1271,7 @@ static DCR *find_device(JCR *jcr, POOL_MEM &devname, * 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; @@ -1334,6 +1336,72 @@ static DCR *find_any_device(JCR *jcr, POOL_MEM &devname, 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 diff --git a/bacula/src/stored/file_dev.c b/bacula/src/stored/file_dev.c index bdb3ec037..1b4d7ce5c 100644 --- a/bacula/src/stored/file_dev.c +++ b/bacula/src/stored/file_dev.c @@ -208,14 +208,13 @@ bool file_dev::open_device(DCR *dcr, int omode) 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()); } } } @@ -223,9 +222,8 @@ bool file_dev::open_device(DCR *dcr, int omode) /* 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); } } @@ -254,45 +252,50 @@ bool file_dev::open_device(DCR *dcr, int omode) 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; } @@ -321,7 +324,7 @@ bool DEVICE::truncate(DCR *dcr) /* 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; @@ -329,9 +332,10 @@ bool DEVICE::truncate(DCR *dcr) } 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; } } @@ -625,7 +629,7 @@ void file_dev::get_volume_fpath(const char *vol_name, POOLMEM **fname) 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. @@ -684,10 +688,11 @@ bool file_dev::check_volume_protection_time(const char *vol_name) } 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); @@ -713,7 +718,7 @@ bool file_dev::check_for_attr(const char *vol_name, int attr) 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; } @@ -722,7 +727,6 @@ bool file_dev::check_for_attr(const char *vol_name, int attr) 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; } @@ -730,7 +734,6 @@ bool file_dev::check_for_attr(const char *vol_name, int attr) 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"; @@ -752,7 +755,7 @@ bool file_dev::check_for_attr(const char *vol_name, int attr) #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; @@ -768,14 +771,13 @@ bool file_dev::modify_fattr(const char *vol_name, int attr, bool set) 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; } @@ -783,25 +785,35 @@ bool file_dev::modify_fattr(const char *vol_name, int attr, bool set) 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; } @@ -810,11 +822,9 @@ bool file_dev::modify_fattr(const char *vol_name, int attr, bool set) 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; } @@ -831,22 +841,22 @@ 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 @@ -855,14 +865,14 @@ bool file_dev::append_open_needed(const char *vol_name) 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) @@ -870,28 +880,28 @@ 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) @@ -899,16 +909,16 @@ 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) @@ -918,7 +928,7 @@ 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 @@ -928,7 +938,7 @@ bool file_dev::check_for_read_only(const char *vol) 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? } diff --git a/bacula/src/stored/file_dev.h b/bacula/src/stored/file_dev.h index ebe4330db..b30e998b9 100644 --- a/bacula/src/stored/file_dev.h +++ b/bacula/src/stored/file_dev.h @@ -26,12 +26,10 @@ 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); @@ -44,16 +42,18 @@ public: 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(); }; diff --git a/bacula/src/stored/label.c b/bacula/src/stored/label.c index d30160ad7..6b49caedf 100644 --- a/bacula/src/stored/label.c +++ b/bacula/src/stored/label.c @@ -658,6 +658,7 @@ bool DEVICE::rewrite_volume_label(DCR *dcr, bool recycle) 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++; @@ -676,9 +677,15 @@ bool DEVICE::rewrite_volume_label(DCR *dcr, bool recycle) 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()); diff --git a/bacula/src/stored/mount.c b/bacula/src/stored/mount.c index 3baf37c27..6f8f2d8a4 100644 --- a/bacula/src/stored/mount.c +++ b/bacula/src/stored/mount.c @@ -297,7 +297,7 @@ read_volume: /* 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; diff --git a/bacula/src/stored/vol_mgr.c b/bacula/src/stored/vol_mgr.c index ef0576470..5873f3e0e 100644 --- a/bacula/src/stored/vol_mgr.c +++ b/bacula/src/stored/vol_mgr.c @@ -845,7 +845,7 @@ bool DCR::can_i_write_volume() } 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"), -- 2.47.3