]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
Add tools to synchronize Read-Only/Immutable flags with the Storage Daemon
authorEric Bollengier <eric@baculasystems.com>
Tue, 13 Sep 2022 15:42:51 +0000 (17:42 +0200)
committerEric Bollengier <eric@baculasystems.com>
Thu, 14 Sep 2023 11:57:00 +0000 (13:57 +0200)
 - Change the interface of the immutable functions to not modify the device
 - Add Protect fields to the catalog and the SD/DIR protocol

14 files changed:
bacula/src/cats/sql_create.c
bacula/src/dird/job.c
bacula/src/dird/next_vol.c
bacula/src/dird/ua_cmds.c
bacula/src/dird/ua_label.c
bacula/src/dird/ua_update.c
bacula/src/stored/block_util.c
bacula/src/stored/dev.h
bacula/src/stored/dircmd.c
bacula/src/stored/file_dev.c
bacula/src/stored/file_dev.h
bacula/src/stored/label.c
bacula/src/stored/mount.c
bacula/src/stored/vol_mgr.c

index 48a8d76d66dec5d28bef033429a22db503b3cc5d..07a91dc8afc290afe7a779a80ff0af419147c4f0 100644 (file)
@@ -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
           );
 
 
index f9142c79985e1e04072d14f795f4127abe87219e..b594008333ba7c94fef90810e913a8f0ca80169d 100644 (file)
@@ -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);
index cebea5dafc0aeaae5009062d9f2d420173829e2b..d60df3c80a56eaf18dda88eb7ff6c6c4012be152 100644 (file)
@@ -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;
index c3810efd010226d60a0ac8086b68c82d8311ee9b..fa16e9606d15f0eb631035cf2eddb538442a57c0 100644 (file)
@@ -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=<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]"),
@@ -242,6 +242,7 @@ static struct cmdstruct commands[] = {                                      /* C
        "\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},
index c2d2275ef44bb5e39159ee09d4486fc0542cdd42..d121918142c6c510bf9385d9fcf8df236f195190 100644 (file)
@@ -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=%",
-                 &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"),
index 927ed922419ed6f6b29751198558a4e6a5e2482c..988091defa8fa6b266780508d79aa6d1d80c5001 100644 (file)
@@ -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;
+}
index 10e8fac7667d1788db75a7f1c7398e20828fe79a..75ed089219fe2dc75befb6b151225b655656282f 100644 (file)
@@ -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());;
          }
       }
 
index 92b19b14a26e0b75cc31b74667b50a87491d3dcd..1e4f937e37e5591f90fff8dc93e5de6e4e407e09 100644 (file)
@@ -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 "";};
index 23ceecc1617778ab4b6e311d00942a857c381fb2..66991ed6215333b105a9675257af97fa7db82a16 100644 (file)
@@ -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
index bdb3ec037c13bf412dcc01604f85e28a13ab3aaa..1b4d7ce5cbf01b567ae72f956b92a2fae5d25fd4 100644 (file)
@@ -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?
    }
 
index ebe4330dbac06c47602ec4df4d48f7b5936c3710..b30e998b9e0778499f52df8501edefeef512e66d 100644 (file)
 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();
 };
 
index d30160ad7beafeaf6326116e7998ef518802d681..6b49caedf0d2dc752da8ecd61375abd844fd2e04 100644 (file)
@@ -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());
index 3baf37c2731f9a2340afd9aab81c899558cd7f58..6f8f2d8a4b94fdde2aaef861b0f002a583e3ab80 100644 (file)
@@ -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;
index ef0576470df4607930f881ee3e022f97db499ebb..5873f3e0ea02afe7dc9c19adbd826b73409b6618 100644 (file)
@@ -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"),