]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
Add Protect fields to the catalog and the SD/DIR protocol
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:56:59 +0000 (13:56 +0200)
27 files changed:
bacula/src/cats/cats.h
bacula/src/cats/make_mysql_tables.in
bacula/src/cats/make_postgresql_tables.in
bacula/src/cats/make_sqlite3_tables.in
bacula/src/cats/sql_get.c
bacula/src/cats/sql_list.c
bacula/src/cats/sql_update.c
bacula/src/cats/update_mysql_tables.in
bacula/src/cats/update_postgresql_tables.in
bacula/src/dird/backup.c
bacula/src/dird/catreq.c
bacula/src/dird/msgchan.c
bacula/src/dird/next_vol.c
bacula/src/dird/protos.h
bacula/src/dird/ua_output.c
bacula/src/dird/ua_status.c
bacula/src/dird/vbackup.c
bacula/src/jcr.h
bacula/src/lib/bsys.c
bacula/src/stored/askdir.c
bacula/src/stored/dev.h
bacula/src/stored/file_dev.c
bacula/src/stored/file_dev.h
bacula/src/stored/reserve.c
bacula/src/stored/tape_dev.h
bacula/updatedb/update_mysql_tables_1025_to_1026.in
bacula/updatedb/update_postgresql_tables_1025_to_1026.in

index aa13dca0967aee926b20323895ae86d19452cfad..e88b755e14b425b16cb98dca6bbcdd818bd259a8 100644 (file)
@@ -401,6 +401,7 @@ public:
    DBId_t StorageId;
    char Name[MAX_NAME_LENGTH];        /* Device name */
    int AutoChanger;                   /* Set if autochanger */
+   int UseProtect;                    /* Set if the Storage Daemon can handle Protect */
 
    /* Not in database */
    bool created;                      /* set if created by db_create ... */
@@ -457,6 +458,8 @@ public:
    uint32_t MaxVolJobs;               /* Max Jobs on Volume */
    uint32_t MaxVolFiles;              /* Max files on Volume */
    int32_t  Recycle;                  /* recycle yes/no */
+   int32_t  Protect;                  /* Protect yes/no */
+   int32_t  UseProtect;               /* Use Protect feature yes/no */
    int32_t  Slot;                     /* slot in changer */
    int32_t  Enabled;                  /* 0=disabled, 1=enabled, 2=archived */
    int32_t  InChanger;                /* Volume currently in changer */
index 3c4f04e8170189c852b6c579e1e1fd3865ac1250..175f9a3bf0d45a247c4579eda5e57fe896611c77 100644 (file)
@@ -479,8 +479,8 @@ CREATE TABLE Media (
    ScratchPoolId INTEGER UNSIGNED DEFAULT 0,
    RecyclePoolId INTEGER UNSIGNED DEFAULT 0,
    Comment BLOB,
-   Worm TINYINT DEFAULT 0,
-   UseWorm TINYINT DEFAULT 0,
+   Protect TINYINT DEFAULT 0,
+   UseProtect TINYINT DEFAULT 0,
    PRIMARY KEY(MediaId),
    UNIQUE (VolumeName(128)),
    INDEX (PoolId),
index a1da714e25bd06cbf7dd5884bb98b77d8cbea3bb..e2a9005e8c545137294883274b8cdc13738ac25c 100644 (file)
@@ -418,6 +418,8 @@ CREATE TABLE media
     scratchpoolid     integer    default 0,
     recyclepoolid     integer    default 0,
     comment          text,
+    protect           smallint    default 0,
+    useprotect        smallint    default 0,
     primary key (mediaid)
 );
 
index cc897a7ada9c5e9c13c4cf949e60fc2b89a31358..34b97947f5eb2ab956bae593bf707fa05b7085bd 100644 (file)
@@ -425,8 +425,8 @@ CREATE TABLE Media (
    ScratchPoolId INTEGER UNSIGNED REFERENCES Pool DEFAULT 0,
    RecyclePoolId INTEGER UNSIGNED REFERENCES Pool DEFAULT 0,
    Comment TEXT,
-   Worm TINYINT DEFAULT 0,
-   UseWorm TINYINT DEFAULT 0,
+   Protect TINYINT DEFAULT 0,
+   UseProtect TINYINT DEFAULT 0,
    PRIMARY KEY(MediaId)
    );
 
index fa3243152a56e986e30b0120959ba31f4f34b1f3..3aaec9e2027dd7fcb8ea2f63b3e6ff35c7b94ec8 100644 (file)
@@ -1362,24 +1362,31 @@ bool BDB::bdb_get_media_record(JCR *jcr, MEDIA_DBR *mr)
       return true;
    }
    if (mr->MediaId != 0) {               /* find by id */
-      Mmsg(filter, "WHERE MediaId=%s", edit_int64(mr->MediaId, ed1));
+      Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
+         "VolBlocks,VolBytes,VolABytes,VolHoleBytes,VolHoles,VolMounts,"
+         "VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
+         "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
+         "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
+         "EndFile,EndBlock,VolType,VolParts,VolCloudParts,LastPartBytes,"
+         "LabelType,LabelDate,StorageId,"
+         "Enabled,LocationId,RecycleCount,InitialWrite,"
+         "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge,CacheRetention,Pool.Name,Protect,UseProtect "
+         "FROM Media JOIN Pool USING (PoolId) WHERE MediaId=%s",
+         edit_int64(mr->MediaId, ed1));
    } else {                           /* find by name */
       bdb_escape_string(jcr, esc, mr->VolumeName, strlen(mr->VolumeName));
-      Mmsg(filter, "WHERE VolumeName='%s'", esc);
+      Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
+         "VolBlocks,VolBytes,VolABytes,VolHoleBytes,VolHoles,VolMounts,"
+         "VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
+         "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs,"
+         "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger,"
+         "EndFile,EndBlock,VolType,VolParts,VolCloudParts,LastPartBytes,"
+         "LabelType,LabelDate,StorageId,"
+         "Enabled,LocationId,RecycleCount,InitialWrite,"
+         "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime,ActionOnPurge,CacheRetention,Pool.Name,Protect,UseProtect "
+         "FROM Media JOIN Pool USING (PoolId) WHERE VolumeName='%s'", esc);
    }
 
-   Mmsg(cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,"
-      "VolBlocks,VolBytes,VolABytes,VolHoleBytes,VolHoles,VolMounts,"
-      "VolErrors,VolWrites,Media.MaxVolBytes,Media.VolCapacityBytes,"
-      "MediaType,VolStatus,Media.PoolId,Media.VolRetention,Media.VolUseDuration,Media.MaxVolJobs,"
-      "Media.MaxVolFiles,Media.Recycle,Slot,FirstWritten,LastWritten,InChanger,"
-      "EndFile,EndBlock,VolType,VolParts,VolCloudParts,LastPartBytes,"
-      "Media.LabelType,LabelDate,StorageId,"
-      "Media.Enabled,LocationId,RecycleCount,InitialWrite,"
-      "Media.ScratchPoolId,Media.RecyclePoolId,VolReadTime,VolWriteTime,Media.ActionOnPurge,"
-      "Media.CacheRetention,Pool.Name "
-      "FROM Media JOIN Pool USING (PoolId) %s", filter.c_str());
-
    if (QueryDB(jcr, cmd)) {
       char ed1[50];
       if (sql_num_rows() > 1) {
@@ -1442,7 +1449,8 @@ bool BDB::bdb_get_media_record(JCR *jcr, MEDIA_DBR *mr)
             mr->ActionOnPurge = str_to_int32(row[43]);
             mr->CacheRetention = str_to_int64(row[44]);
             bstrncpy(mr->Pool, row[45], sizeof(mr->Pool));
-
+            mr->Protect = str_to_int64(row[46]);
+            mr->UseProtect = str_to_int64(row[47]);
             ok = true;
          }
       } else {
index 561a239add71ac2426275fcf30f7d5747eebf407..de61189e4067c79577d5623d0ec132cfb52581b0 100644 (file)
@@ -490,7 +490,7 @@ void BDB::bdb_list_media_records(JCR *jcr, MEDIA_DBR *mdbr,
             "EndFile,EndBlock,VolType,Media.LabelType,StorageId,DeviceId,"
             "MediaAddressing,VolReadTime,VolWriteTime,"
             "LocationId,RecycleCount,InitialWrite,Media.ScratchPoolId,Media.RecyclePoolId, "
-            "Media.ActionOnPurge,%s AS ExpiresIn, Comment"
+            "Media.ActionOnPurge,%s AS ExpiresIn, Comment, Protect, UseProtect"
            " FROM Media %s WHERE Media.VolumeName='%s' %s",
               expiresin,
               join,
@@ -508,7 +508,7 @@ void BDB::bdb_list_media_records(JCR *jcr, MEDIA_DBR *mdbr,
             "EndFile,EndBlock,VolType,Media.LabelType,StorageId,DeviceId,"
             "MediaAddressing,VolReadTime,VolWriteTime,"
             "LocationId,RecycleCount,InitialWrite,Media.ScratchPoolId,Media.RecyclePoolId, "
-            "Media.ActionOnPurge,%s AS ExpiresIn, Comment"
+            "Media.ActionOnPurge,%s AS ExpiresIn, Comment, Protect, UseProtect"
             " FROM Media %s WHERE Media.PoolId=%s %s ORDER BY MediaId",
               expiresin,
               join,
index 6acdd271b294cd5c394cbd3b8b2ca582b8e61988..f367cd3886f055c4ae2ac42b61b86bb576a9b550 100644 (file)
@@ -99,9 +99,18 @@ bool BDB::bdb_update_job_start_record(JCR *jcr, JOB_DBR *jr)
    strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
    JobTDate = (btime_t)stime;
 
+   if (!is_name_valid(jr->WriteDevice, NULL)) {
+      *jr->WriteDevice = 0;     // Not valid, not used
+   }
+   if (!is_name_valid(jr->LastReadDevice, NULL)) {
+      *jr->LastReadDevice = 0;     // Not valid, not used
+   }
+   
    bdb_lock();
    Mmsg(cmd, "UPDATE Job SET JobStatus='%c',Level='%c',StartTime='%s',"
-"ClientId=%s,JobTDate=%s,PoolId=%s,FileSetId=%s,RealStartTime='%s' WHERE JobId=%s",
+        "ClientId=%s,JobTDate=%s,PoolId=%s,FileSetId=%s,RealStartTime='%s',"
+        "isVirtualFull=%d,LastReadStorageId=%d,LastReadDevice='%s',"
+        "WriteStorageId=%d,WriteDevice='%s',StatusInfo='%s',Encrypted=%d WHERE JobId=%s",
       (char)(jcr->JobStatus),
       (char)(jr->JobLevel), dt,
       edit_int64(jr->ClientId, ed1),
@@ -109,6 +118,13 @@ bool BDB::bdb_update_job_start_record(JCR *jcr, JOB_DBR *jr)
       edit_int64(jr->PoolId, ed3),
       edit_int64(jr->FileSetId, ed4),
       dt,
+      jr->isVirtualFull,
+      jr->LastReadStorageId,
+      jr->LastReadDevice,
+      jr->WriteStorageId,
+      jr->WriteDevice,
+      jr->StatusInfo,
+      jr->Encrypted,
       edit_int64(jr->JobId, ed5));
 
    stat = UpdateDB(jcr, cmd, false);
@@ -392,7 +408,7 @@ int BDB::bdb_update_media_record(JCR *jcr, MEDIA_DBR *mr)
         "LabelType=%d,StorageId=%s,PoolId=%s,VolRetention=%s,VolUseDuration=%s,"
         "MaxVolJobs=%d,MaxVolFiles=%d,Enabled=%d,LocationId=%s,"
         "ScratchPoolId=%s,RecyclePoolId=%s,RecycleCount=%d,Recycle=%d,"
-        "ActionOnPurge=%d,CacheRetention=%s,EndBlock=%u"
+        "ActionOnPurge=%d,CacheRetention=%s,EndBlock=%u,Protect=%d,UseProtect=%d"
         " WHERE VolumeName='%s'",
         mr->VolJobs, mr->VolFiles, mr->VolBlocks,
         edit_uint64(mr->VolBytes, ed1),
@@ -419,7 +435,7 @@ int BDB::bdb_update_media_record(JCR *jcr, MEDIA_DBR *mr)
         edit_uint64(mr->RecyclePoolId, ed15),
         mr->RecycleCount,mr->Recycle, mr->ActionOnPurge,
         edit_uint64(mr->CacheRetention, ed16),
-        mr->EndBlock,
+        mr->EndBlock,mr->Protect,mr->UseProtect,
         esc_name);
 
    Dmsg1(dbglevel1, "%s\n", cmd);
index d422d7a5469e19e630e219c7432b619b7d0fde55..c0b893616dcd67a5e3f78afc580fc316f2f9b118 100644 (file)
@@ -706,8 +706,8 @@ CREATE INDEX meta_attachmentowner ON MetaAttachment (AttachmentTenant(255),Attac
 CREATE INDEX meta_attachmentemailid ON MetaAttachment (AttachmentEmailId(255));
 
 ALTER TABLE Media
-    MODIFY COLUMN Worm TINYINT DEFAULT 0,
-    MODIFY COLUMN UseWorm TINYINT DEFAULT 0;
+    MODIFY COLUMN Protect TINYINT DEFAULT 0,
+    MODIFY COLUMN UseProtect TINYINT DEFAULT 0;
 
 ALTER TABLE Object
     ADD COLUMN FileIndex integer not null default 0,
@@ -729,8 +729,8 @@ ALTER TABLE Job
     ADD COLUMN Encrypted         int        default 0;
 
 ALTER TABLE Media
-    ADD COLUMN Worm     smallint default 0,
-    ADD COLUMN UseWorm  smallint default 0;
+    ADD COLUMN Protect     smallint default 0,
+    ADD COLUMN UseProtect  smallint default 0;
 
 INSERT INTO Events (EventsCode, EventsType, EventsTime, EventsDaemon, EventsSource, EventsRef, EventsText) VALUES
   ('DU0001', 'catalog_update', NOW(), '*SHELL*', 'update_bacula_tables', 'pid$$', 'Catalog schema was updated to 1026');
index 0bca86b0ec9fbcc7be1a701a6b117d4802b5cb28..9376d86dde127774b1d067d4dfcfcc540c0f2912 100644 (file)
@@ -758,8 +758,8 @@ ALTER TABLE Job ADD COLUMN WriteDevice       text        default ''
 ALTER TABLE Job ADD COLUMN StatusInfo        text        default '';
 ATLER TABLE Job ADD COLUMN Encrypted         int         default 0;
 
-ALTER TABLE Media ADD COLUMN Worm              smallint    default 0;
-ALTER TABLE Media ADD COLUMN IsWorm            smallint    default 0;
+ALTER TABLE Media ADD COLUMN Protect              smallint    default 0;
+ALTER TABLE Media ADD COLUMN IsProtect            smallint    default 0;
 
 INSERT INTO Events (EventsCode, EventsType, EventsTime, EventsDaemon, EventsSource, EventsRef, EventsText) VALUES
   ('DU0001', 'catalog_update', NOW(), '*SHELL*', 'update_bacula_tables', 'pid$$', 'Catalog schema was updated to 1026');
index 61496fab7989c16b9cb7f00cd6c76e110f9e99d7..2bf4e1be1050b0ffe3d6251ec7b89b552618ccf9 100644 (file)
@@ -719,6 +719,8 @@ bool do_backup(JCR *jcr)
     */
    jcr->start_time = time(NULL);
    jcr->jr.StartTime = jcr->start_time;
+   bstrncpy(jcr->jr.WriteDevice, jcr->write_dev, sizeof(jcr->jr.WriteDevice));
+
    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
       Jmsg(jcr, M_FATAL, 0, "[DE0008] %s", db_strerror(jcr->db));
    }
index d3b14d71001e53d2af2e7d03b59fa8799f778d23..21ad3287c2d082edc35a9967e49c05936f189d66 100644 (file)
@@ -38,7 +38,7 @@
  */
 
 /* Requests from the Storage daemon */
-static char Find_media[] = "CatReq JobId=%ld FindMedia=%d pool_name=%127s media_type=%127s vol_type=%d create=%d\n";
+static char Find_media[] = "CatReq JobId=%ld FindMedia=%d pool_name=%127s media_type=%127s vol_type=%d create=%d use_protect=%d\n";
 static char Get_Vol_Info[] = "CatReq JobId=%ld GetVolInfo VolName=%127s write=%d\n";
 
 static char Update_media[] = "CatReq JobId=%ld UpdateMedia VolName=%s"
@@ -47,7 +47,7 @@ static char Update_media[] = "CatReq JobId=%ld UpdateMedia VolName=%s"
    " VolErrors=%u VolWrites=%lld MaxVolBytes=%lld EndTime=%lld VolStatus=%10s"
    " Slot=%d relabel=%d InChanger=%d VolReadTime=%llu VolWriteTime=%llu"
    " VolFirstWritten=%lld VolType=%u VolParts=%d VolCloudParts=%d"
-   " LastPartBytes=%lld Enabled=%d Recycle=%d\n";
+   " LastPartBytes=%lld Enabled=%d Recycle=%d Protect=%d UseProtect=%d\n";
 
 static char FileEvent_add[] = "%c %d %127s %127s 0";
 /* Full format when coming from the Verify Job */
@@ -67,7 +67,7 @@ static char OK_media[] = "1000 OK VolName=%s VolJobs=%u VolFiles=%u"
    " MaxVolJobs=%u MaxVolFiles=%u InChanger=%d VolReadTime=%s"
    " VolWriteTime=%s EndFile=%u EndBlock=%u VolType=%u LabelType=%d"
    " MediaId=%s ScratchPoolId=%s VolParts=%d VolCloudParts=%d"
-   " LastPartBytes=%lld Enabled=%d MaxPoolBytes=%s PoolBytes=%s Recycle=%d\n";
+   " LastPartBytes=%lld Enabled=%d MaxPoolBytes=%s PoolBytes=%s Recycle=%d Protect=%d UseProtect=%d\n";
 
 static char OK_create[] = "1000 OK CreateJobMedia\n";
 
@@ -120,7 +120,7 @@ static int send_volume_info_to_storage_daemon(JCR *jcr, BSOCK *sd, MEDIA_DBR *mr
       mr->Enabled,
       edit_uint64(pr.MaxPoolBytes, ed11),
       edit_uint64(pr.PoolBytes, ed12),
-      mr->Recycle);
+      mr->Recycle, mr->Protect, mr->UseProtect);
    unbash_spaces(mr->VolumeName);
    Dmsg2(100, "Vol Info for %s: %s", jcr->Job, sd->msg);
    return stat;
@@ -187,8 +187,8 @@ void catalog_request(JCR *jcr, BSOCK *bs)
    utime_t VolFirstWritten;
    utime_t VolLastWritten;
    int n;
-   int can_create=0;
-   int Enabled, Recycle;
+   int can_create=0, use_protect=0;
+   int Enabled, Recycle, Protect, UseProtect;
    JobId_t JobId = 0;
    STORE *wstore = jcr->store_mngr->get_wstore();
 
@@ -212,8 +212,8 @@ void catalog_request(JCR *jcr, BSOCK *bs)
    /*
     * Find next appendable medium for SD
     */
-   n = sscanf(bs->msg, Find_media, &JobId, &index, &pool_name, &mr.MediaType, &mr.VolType, &can_create);
-   if (n == 6) {
+   n = sscanf(bs->msg, Find_media, &JobId, &index, &pool_name, &mr.MediaType, &mr.VolType, &can_create, &use_protect);
+   if (n == 7) {
       POOL_MEM errmsg;
       POOL_DBR pr;
       bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
@@ -225,7 +225,7 @@ void catalog_request(JCR *jcr, BSOCK *bs)
          mr.ScratchPoolId = pr.ScratchPoolId;
          ok = find_next_volume_for_append(jcr, &mr, index,
                                           can_create?fnv_create_vol : fnv_no_create_vol,
-                                          fnv_prune, errmsg);
+                                          fnv_prune, use_protect, errmsg);
          Dmsg3(050, "find_media ok=%d idx=%d vol=%s\n", ok, index, mr.VolumeName);
       } else {
          /* Report problem finding pool */
@@ -316,8 +316,8 @@ void catalog_request(JCR *jcr, BSOCK *bs)
       &VolLastWritten, &sdmr.VolStatus, &sdmr.Slot, &label, &sdmr.InChanger,
       &sdmr.VolReadTime, &sdmr.VolWriteTime, &VolFirstWritten,
       &sdmr.VolType, &sdmr.VolParts, &sdmr.VolCloudParts,
-      &sdmr.LastPartBytes, &Enabled, &Recycle);
-    if (n == 27) {
+      &sdmr.LastPartBytes, &Enabled, &Recycle, &Protect, &UseProtect);
+    if (n == 29) {
       db_lock(jcr->db);
       Dmsg3(400, "Update media %s oldStat=%s newStat=%s\n", sdmr.VolumeName,
          mr.VolStatus, sdmr.VolStatus);
@@ -403,6 +403,8 @@ void catalog_request(JCR *jcr, BSOCK *bs)
       mr.LastPartBytes = sdmr.LastPartBytes;
       mr.Enabled       = Enabled;  /* byte assignment */
       mr.Recycle       = Recycle;  /* byte assignment */
+      mr.Protect          = Protect;    /* byte assignment */
+      mr.UseProtect       = UseProtect;  /* byte assignment */
       bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus));
       mr.VolReadTime  = sdmr.VolReadTime;
       mr.VolWriteTime = sdmr.VolWriteTime;
index 75b818d7b82f4ea08ef0ac63cecf633b2775d603..f6dbbda179d2b00a1525d07d243502623814843a 100644 (file)
@@ -51,7 +51,7 @@ static char use_device[] = "use device=%s\n";
 
 /* Response from Storage daemon */
 static char OKjob[]      = "3000 OK Job SDid=%d SDtime=%d Authorization=%100s\n";
-static char OK_device[]  = "3000 OK use device device=%s\n";
+static char OK_device[]  = "3000 OK use device device=%127s protect=%d encrypt=%d\n";
 
 /* Storage Daemon requests */
 static char Job_start[]  = "3010 Job %127s start\n";
@@ -286,6 +286,7 @@ bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore, bool wait,
     */
    /* Do read side of storage daemon */
    if (ok && rstore) {
+      int protect=0, encrypt=0;
       /* For the moment, only migrate, copy and vbackup have rpool */
       if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY) ||
            (jcr->is_JobType(JT_BACKUP) && jcr->is_JobLevel(L_VIRTUAL_FULL))) {
@@ -323,18 +324,21 @@ bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore, bool wait,
       sd->signal(BNET_EOD);              /* end of Storages */
       if (bget_dirmsg(jcr, sd, BSOCK_TYPE_SD) > 0) {
          Dmsg1(100, "<stored: %s", sd->msg);
-         ok = sscanf(sd->msg, OK_device, device_name.c_str()) == 1;
+         ok = sscanf(sd->msg, OK_device, device_name.c_str(), &protect, &encrypt) == 3;
       } else {
          ok = false;
       }
       if (ok) {
          Jmsg(jcr, M_INFO, 0, _("Using Device \"%s\" to read.\n"), device_name.c_str());
          pm_strcpy(jcr->read_dev, device_name.c_str());
+         jcr->SD_set_worm = protect;
+         jcr->jr.Encrypted = encrypt;
       }
    }
 
    /* Do write side of storage daemon */
    if (ok && wstore) {
+      int protect=0, encrypt=0;
       pm_strcpy(pool_type, jcr->pool->pool_type);
       pm_strcpy(pool_name, jcr->pool->name());
       bash_spaces(pool_type);
@@ -362,13 +366,15 @@ bool start_storage_daemon_job(JCR *jcr, alist *rstore, alist *wstore, bool wait,
       sd->signal(BNET_EOD);              /* end of Storages */
       if (bget_dirmsg(jcr, sd, BSOCK_TYPE_SD) > 0) {
          Dmsg1(100, "<stored: %s", sd->msg);
-         ok = sscanf(sd->msg, OK_device, device_name.c_str()) == 1;
+         ok = sscanf(sd->msg, OK_device, device_name.c_str(), &protect, &encrypt) == 3;
       } else {
          ok = false;
       }
       if (ok) {
          Jmsg(jcr, M_INFO, 0, _("Using Device \"%s\" to write.\n"), device_name.c_str());
          pm_strcpy(jcr->write_dev, device_name.c_str());
+         jcr->SD_set_worm = protect;
+         jcr->jr.Encrypted = encrypt;
       }
    }
    if (!ok) {
index d38e9c1b1db655eccd35e3aba50f3d68a2639011..cebea5dafc0aeaae5009062d9f2d420173829e2b 100644 (file)
@@ -97,9 +97,10 @@ static void set_volume_to_exclude_list(JCR *jcr, int index, MEDIA_DBR *mr)
  *   jcr->pool
  *   MEDIA_DBR mr with PoolId set
  *   create -- whether or not to create a new volume
+ *   use_protect -- whether or not the device will mark the volume as protected (0 no, -1 don't know, 1 yes)
  */
 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
-                                bool create, bool prune, POOL_MEM &errmsg)
+                                bool create, bool prune, int use_protect, POOL_MEM &errmsg)
 {
    int retry = 0;
    bool ok;
@@ -107,9 +108,9 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
    STORE *store = jcr->store_mngr->get_wstore();
 
    bstrncpy(mr->MediaType, store->media_type, sizeof(mr->MediaType));
-   Dmsg6(dbglvl, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s index=%d create=%d prune=%d\n",
+   Dmsg7(dbglvl, "find_next_vol_for_append: JobId=%u PoolId=%d, MediaType=%s index=%d create=%d prune=%d protect=%d\n",
          (uint32_t)jcr->JobId, (int)mr->PoolId, mr->MediaType, index,
-         create, prune);
+         create, prune, use_protect);
    /*
     * If we are using an Autochanger, restrict Volume
     *   search to the Autochanger on the first pass
@@ -228,6 +229,10 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
       }
       Dmsg2(dbglvl, "VolJobs=%d FirstWritten=%d\n", mr->VolJobs, mr->FirstWritten);
       if (ok) {
+         /* If the current device uses the Protect feature, we need to keep track of it */
+         if (use_protect == 1) {
+            mr->UseProtect = 1;
+         }
          /* If we can use the volume, check if it is expired */
          if (has_volume_expired(jcr, mr)) {
             if (retry++ < 200) {            /* sanity check */
@@ -324,7 +329,7 @@ bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
          expired = true;
       }
    }
-   /* Here, if the volume is WORM, we cannot change the status if a storage
+   /* Here, if the volume is Protect, we cannot change the status if a storage
     * is not connected
     */
    if (expired) {
@@ -337,6 +342,12 @@ bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
       }
    }
    Dmsg2(dbglvl, "Vol=%s expired=%d\n", mr->VolumeName, expired);
+   /* Special case, we have marked the volume as Expired, and the Storage
+    * Daemon wants to mark it as Protect
+    */
+   if (expired && mr->UseProtect) {
+      return false;
+   }
    return expired;
 }
 
index 9987333b7c874166d94283b5abc6cfd912ccb6a2..acf2c5a3f84e11c92610b451c6d24ee9846c9639 100644 (file)
@@ -186,7 +186,7 @@ bool has_quota_reached(JCR *jcr, MEDIA_DBR *mr);
 bool has_quota_reached(JCR *jcr);
 void set_storageid_in_mr(STORE *store, MEDIA_DBR *mr);
 int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index,
-                                bool create, bool purge, POOL_MEM &errmsg);
+                                bool create, bool purge, int use_worm, POOL_MEM &errmsg);
 bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr);
 void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason);
 bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr,
index 183415de63c100d65d48108b10bdc547a4029915..5a1c5137600e6fcb8e67b4dfad43d37592987b7d 100644 (file)
@@ -1198,7 +1198,7 @@ static bool list_nextvol(UAContext *ua, int ndays)
       get_job_storage(&store, job, run);
       set_storageid_in_mr(store.store, &mr);
       /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
-      if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune, errmsg)) {
+      if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune, -1 /* no worm */, errmsg)) {
          ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).%s\n"),
                        job->name(), pr.Name, level_to_str(edl, sizeof(edl), run->level), errmsg.c_str());
       } else {
index df13099b30fc624de778383058c6ab6f3fc690da..93ca3e978c0f898815941cfd4d0b4ca44a881b1e 100644 (file)
@@ -752,7 +752,7 @@ static void prt_runtime(UAContext *ua, sched_pkt *sp, OutputWriter *ow)
          set_storageid_in_mr(jcr->store_mngr->get_wstore(), &mr);
          Dmsg0(250, "call find_next_volume_for_append\n");
          /* no need to set ScratchPoolId, since we use fnv_no_create_vol */
-         ok = find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_no_prune, errmsg);
+         ok = find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_no_prune, -1, errmsg);
       }
       if (!ok) {
          bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName));
index 7dc2c15fe0015e9bc709ca4025461f5cd9036280..d0bb49f5188b676a7f2a5db7132e21a857b7fb9b 100644 (file)
@@ -70,6 +70,7 @@ bool do_vbackup_init(JCR *jcr)
    Dmsg2(dbglevel, "Read pool=%s (From %s)\n", jcr->rpool->name(), jcr->rpool_source);
 
    jcr->start_time = time(NULL);
+   jcr->jr.isVirtualFull = 1;
    jcr->jr.StartTime = jcr->start_time;
    jcr->jr.JobLevel = L_FULL;      /* we want this to appear as a Full backup */
    if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
index a37414239ff93423de5751edce9d38c8844ab02f..5fef5c234dd148c271facb73e0affdfd5dbc368e 100644 (file)
@@ -368,6 +368,7 @@ public:
 
 #ifdef DIRECTOR_DAEMON
    /* Director Daemon specific data part of JCR */
+   bool SD_set_worm;                  /* Initiate the procedure to set the WORM attribute */
    bool SD_msg_chan_started;          /* True if the msg thread is started */
    pthread_t SD_msg_chan;             /* Message channel thread id */
    pthread_cond_t term_wait;          /* Wait for job termination */
index 829fc48a6901e1aad66188563992d1e107c2127d..b151c155530cfb8f889033b0e781b975af2af3a5 100644 (file)
@@ -1955,6 +1955,35 @@ void b_uname(POOLMEM *&un)
    }
 }
 
+/* Set atime/mtime for a given file
+ * Must be called on open file
+ */
+int set_own_time(int fd, const char *path, btime_t atime, btime_t mtime)
+{
+#ifdef HAVE_FUTIMES
+   struct timeval times[2];
+   times[0].tv_sec = atime;
+   times[0].tv_usec = 0;
+   times[1].tv_sec = mtime;
+   times[1].tv_usec = 0;
+
+   if (fd > 0) {
+      if (futimes(fd, times) == 0) {
+         return 0;
+      }
+   }
+#endif
+   struct utimbuf ut;
+   ut.actime = atime;
+   ut.modtime = mtime;
+
+   if (utime(path, &ut) == 0) {
+      return 0;
+   }
+   return -1;
+}
+
+
 /* Get path/fname from argument, use realpath to get the full name if
  * available
  */
@@ -2028,33 +2057,6 @@ void get_path_and_fname(const char *name, char **path, char **fname)
       free(cpath);
       free(cargv0);
    }
-
-/* Set atime/mtime for a given file
- * Must be called on open file
- */
-int set_own_time(int fd, const char *path, btime_t atime, btime_t mtime)
-{
-#ifdef HAVE_FUTIMES
-   struct timeval times[2];
-   times[0].tv_sec = atime;
-   times[0].tv_usec = 0;
-   times[1].tv_sec = mtime;
-   times[1].tv_usec = 0;
-
-   if (fd > 0) {
-      if (futimes(fd, times) == 0) {
-         return 0;
-      }
-   }
-#endif
-   struct utimbuf ut;
-   ut.actime = atime;
-   ut.modtime = mtime;
-
-   if (utime(path, &ut) == 0) {
-      return 0;
-   }
-   return -1;
 }
 
 #ifdef TEST_PROGRAM
index 995953e33b8c186e76e2c8c949d6f3d47bcdbbe5..4aadc4f35d7bc282b1a4a443246ca83fc5b410c7 100644 (file)
@@ -29,7 +29,7 @@
 static const int dbglvl = 200;
 
 /* Requests sent to the Director */
-static char Find_media[]   = "CatReq JobId=%ld FindMedia=%d pool_name=%s media_type=%s vol_type=%d create=%d\n";
+static char Find_media[]   = "CatReq JobId=%ld FindMedia=%d pool_name=%s media_type=%s vol_type=%d create=%d use_protect=%d\n";
 static char Get_Vol_Info[] = "CatReq JobId=%ld GetVolInfo VolName=%s write=%d\n";
 static char Update_media[] = "CatReq JobId=%ld UpdateMedia VolName=%s"
    " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%s VolABytes=%s"
@@ -37,7 +37,7 @@ static char Update_media[] = "CatReq JobId=%ld UpdateMedia VolName=%s"
    " VolErrors=%u VolWrites=%u MaxVolBytes=%s EndTime=%s VolStatus=%s"
    " Slot=%d relabel=%d InChanger=%d VolReadTime=%s VolWriteTime=%s"
    " VolFirstWritten=%s VolType=%u VolParts=%d VolCloudParts=%d"
-   " LastPartBytes=%lld Enabled=%d Recycle=%d\n";
+   " LastPartBytes=%lld Enabled=%d Recycle=%d Protect=%d UseProtect=%d\n";
 static char Create_jobmedia[] = "CatReq JobId=%ld CreateJobMedia\n";
 static char FileAttributes[] = "UpdCat JobId=%ld FileAttributes ";
 
@@ -50,7 +50,8 @@ static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%lu"
    " Slot=%ld MaxVolJobs=%lu MaxVolFiles=%lu InChanger=%ld"
    " VolReadTime=%lld VolWriteTime=%lld EndFile=%lu EndBlock=%lu"
    " VolType=%lu LabelType=%ld MediaId=%lld ScratchPoolId=%lld"
-   " VolParts=%d VolCloudParts=%d LastPartBytes=%lld Enabled=%d MaxPoolBytes=%lld PoolBytes=%lld Recycle=%d\n";
+   " VolParts=%d VolCloudParts=%d LastPartBytes=%lld Enabled=%d MaxPoolBytes=%lld PoolBytes=%lld Recycle=%d"
+   " Protect=%d UseProtect=%d\n";
 
 
 static char OK_create[] = "1000 OK CreateJobMedia\n";
@@ -199,7 +200,7 @@ static bool do_get_volume_info(DCR *dcr)
     BSOCK *dir = jcr->dir_bsock;
     VOLUME_CAT_INFO vol;
     int n;
-    int32_t Enabled, Recycle;
+    int32_t Enabled, Recycle, Protect, UseProtect;
     int32_t InChanger;
 
     dcr->setVolCatInfo(false);
@@ -221,9 +222,9 @@ static bool do_get_volume_info(DCR *dcr)
                &vol.EndFile, &vol.EndBlock, &vol.VolCatType,
                &vol.LabelType, &vol.VolMediaId, &vol.VolScratchPoolId,
                &vol.VolCatParts, &vol.VolCatCloudParts,
-               &vol.VolLastPartBytes, &Enabled, &vol.MaxPoolBytes, &vol.PoolBytes, &Recycle);
+               &vol.VolLastPartBytes, &Enabled, &vol.MaxPoolBytes, &vol.PoolBytes, &Recycle, &Protect, &UseProtect);
     Dmsg2(dbglvl, "<dird n=%d %s", n, dir->msg);
-    if (n != 33) {
+    if (n != 35) {
        Dmsg1(dbglvl, "get_volume_info failed: ERR=%s", dir->msg);
        /*
         * Note, we can get an error here either because there is
@@ -237,6 +238,8 @@ static bool do_get_volume_info(DCR *dcr)
     vol.InChanger = InChanger;        /* bool in structure */
     vol.VolEnabled = Enabled;         /* bool in structure */
     vol.VolRecycle = Recycle;         /* bool in structure */
+    vol.Protect = Protect;            /* bool in structure */
+    vol.UseProtect = UseProtect;      /* bool in structure */
     vol.is_valid = true;
     vol.VolCatBytes = vol.VolCatAmetaBytes + vol.VolCatAdataBytes;
     unbash_spaces(vol.VolCatName);
@@ -337,7 +340,7 @@ bool dir_find_next_appendable_volume(DCR *dcr)
        bash_spaces(dcr->media_type);
        bash_spaces(dcr->pool_name);
        dir->fsend(Find_media, jcr->JobId, vol_index, dcr->pool_name, dcr->media_type,
-                  dcr->dev->dev_type, can_create);
+                  dcr->dev->dev_type, can_create, dcr->dev->use_protect());
        unbash_spaces(dcr->media_type);
        unbash_spaces(dcr->pool_name);
        Dmsg1(dbglvl, ">dird %s", dir->msg);
@@ -349,6 +352,13 @@ bool dir_find_next_appendable_volume(DCR *dcr)
              Dmsg1(dbglvl, "Got same vol = %s\n", lastVolume);
              break;
           }
+          /* Here, the Director *asks* us to mark the volume in ReadOnly */
+          if (dcr->dev->use_protect() &&
+              (strcmp(dcr->VolCatInfo.VolCatStatus, "Used") == 0 || strcmp(dcr->VolCatInfo.VolCatStatus, "Full") == 0))
+          {
+             Dmsg1(dbglvl, "Need to mark %s in read-only/immutable\n", dcr->VolumeName);
+             break;
+          }
           /* If VolCatAdataBytes, we have ALIGNED_DEV */
           if (dcr->VolCatInfo.VolCatType == 0 && dcr->VolCatInfo.VolCatAdataBytes != 0) {
              dcr->VolCatInfo.VolCatType = B_ALIGNED_DEV;
@@ -476,6 +486,7 @@ bool dir_update_volume_info(DCR *dcr, bool label, bool update_LastWritten,
    if (dev->is_worm() && vol.VolRecycle) {
       Jmsg(jcr, M_INFO, 0, _("WORM cassette detected: setting Recycle=No on Volume=\"%s\"\n"), vol.VolCatName);
       vol.VolRecycle = false;
+      vol.Protect = true;
    }
    pm_strcpy(VolumeName, vol.VolCatName);
    bash_spaces(VolumeName);
@@ -513,7 +524,10 @@ bool dir_update_volume_info(DCR *dcr, bool label, bool update_LastWritten,
          vol.VolCatCloudParts,
          vol.VolLastPartBytes,
          Enabled,
-         Recycle);
+         Recycle,
+         vol.Protect,
+         dev->use_protect()
+         );
        Dmsg1(100, ">dird %s", dir->msg);
       /*
        * We sent info directly from dev to the Director.
index 83b85bac512d5ff83d4793b419db5d078761f2f1..2a0781ee8169a3fd1ed97fbaa66282a926306054 100644 (file)
@@ -233,6 +233,8 @@ struct VOLUME_CAT_INFO {
    utime_t  VolFirstWritten;          /* Time of first write */
    utime_t  VolLastWritten;           /* Time of last write */
    bool     InChanger;                /* Set if vol in current magazine */
+   bool     Protect;                  /* Set if the vol is Readonly, worm or immutable */
+   bool     UseProtect;               /* Set if the device can set the volume Readonly, worm or immutable */
    bool     is_valid;                 /* set if this data is valid */
    bool     VolEnabled;               /* set if volume enabled */
    bool     VolRecycle;               /* set if volume can be recycled */
@@ -612,6 +614,7 @@ public:
    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 int use_protect() { return 0; };
    virtual const char *print_type() = 0;        /* in dev.c */
    virtual const char *print_driver_type() { return "";};
    virtual const char *print_full_type() { return print_type();};
index bf207c1c5ec09683a31fa573dc7a18546949fc57..9c51994461fefce41a3bbe86f6c7b5dffacec3f1 100644 (file)
@@ -37,6 +37,13 @@ static const int dbglvl = 100;
 /* Imported functions */
 const char *mode_to_str(int mode);
 
+int file_dev::use_protect()
+{
+   if (device->set_vol_immutable || device->set_vol_read_only) {
+      return 1;
+   }
+   return 0;
+}
 
 /* default primitives are designed for file */
 int DEVICE::d_open(const char *pathname, int flags)
index c029757298656c2d20e17b02eac5f713b4dfdf00..ebe4330dbac06c47602ec4df4d48f7b5936c3710 100644 (file)
@@ -54,6 +54,7 @@ public:
    int set_writable();
    int set_readonly();
    int set_atime(btime_t val);
+   int use_protect();
 };
 
 #endif /* __FILE_DEV_ */
index a24b3414ecd8fa5b7b627e9bfd331d6e336ccfa1..3d3b24832f9aae4f26eb93c5df4dd046f28e8264 100644 (file)
@@ -51,7 +51,7 @@ static char use_storage[]  = "use storage=%127s media_type=%127s "
 static char use_device[]  = "use device=%127s\n";
 
 /* Responses sent to Director daemon */
-static char OK_device[] = "3000 OK use device device=%s\n";
+static char OK_device[] = "3000 OK use device device=%s protect=%d encrypt=%d\n";
 static char NO_device[] = "3924 Device \"%s\" not in SD Device"
      " resources or no matching Media Type or is disabled.\n";
 static char BAD_use[]   = "3913 Bad use command: %s\n";
@@ -863,9 +863,13 @@ static int reserve_device(RCTX &rctx)
    if (rctx.notify_dir) {
       POOL_MEM dev_name;
       BSOCK *dir = rctx.jcr->dir_bsock;
+      int protect = 0;
+      if (rctx.device->set_vol_immutable || rctx.device->set_vol_read_only) {
+         protect = 1;
+      }
       pm_strcpy(dev_name, rctx.device->hdr.name);
       bash_spaces(dev_name);
-      ok = dir->fsend(OK_device, dev_name.c_str());  /* Return real device name */
+      ok = dir->fsend(OK_device, dev_name.c_str(), protect, 0);  /* Return real device name */
       Dmsg1(dbglvl, ">dird: %s", dir->msg);
       if (!ok) {
          dcr->unreserve_device(false);
index e0cf8b15d5922f54df36971c7da2b8d60d817393..031e3980a78bb913274e4967714fd2a595efd6f7 100644 (file)
@@ -66,6 +66,7 @@ public:
       alert_list_which which, alert_cb alert_callback);
    int delete_alerts();
    bool check_lintape_eod();
+   int use_worm() { return device->worm_command && device->control_name; };
    alist *alert_list;
 };
 
index 6ca606263983f8c2a9b74f9bfd5e1cdcf517f9fd..605f9e402744c9b1e2dbccb6c9510d63addd4496 100644 (file)
@@ -78,8 +78,8 @@ CREATE INDEX meta_attachmentowner ON MetaAttachment (AttachmentTenant(255),Attac
 CREATE INDEX meta_attachmentemailid ON MetaAttachment (AttachmentEmailId(255));
 
 ALTER TABLE Media
-    MODIFY COLUMN Worm TINYINT DEFAULT 0,
-    MODIFY COLUMN UseWorm TINYINT DEFAULT 0;
+    MODIFY COLUMN Protect TINYINT DEFAULT 0,
+    MODIFY COLUMN UseProtect TINYINT DEFAULT 0;
 
 ALTER TABLE Object
     ADD COLUMN FileIndex integer not null default 0,
@@ -101,8 +101,8 @@ ALTER TABLE Job
     ADD COLUMN Encrypted         int        default 0;
 
 ALTER TABLE Media
-    ADD COLUMN Worm     smallint default 0,
-    ADD COLUMN UseWorm  smallint default 0;
+    ADD COLUMN Protect     smallint default 0,
+    ADD COLUMN UseProtect  smallint default 0;
 
 INSERT INTO Events (EventsCode, EventsType, EventsTime, EventsDaemon, EventsSource, EventsRef, EventsText) VALUES
   ('DU0001', 'catalog_update', NOW(), '*SHELL*', 'update_bacula_tables', 'pid$$', 'Catalog schema was updated to 1026');
index e46cf105b11b474c15e18bca4863f9435e6d1940..35246d233e532a9fb7c90723c6a45ffc812ee413 100644 (file)
@@ -65,8 +65,8 @@ ALTER TABLE Job ADD COLUMN WriteDevice       text        default ''
 ALTER TABLE Job ADD COLUMN StatusInfo        text        default '';
 ATLER TABLE Job ADD COLUMN Encrypted         int         default 0;
 
-ALTER TABLE Media ADD COLUMN Worm              smallint    default 0;
-ALTER TABLE Media ADD COLUMN IsWorm            smallint    default 0;
+ALTER TABLE Media ADD COLUMN Protect              smallint    default 0;
+ALTER TABLE Media ADD COLUMN IsProtect            smallint    default 0;
 
 INSERT INTO Events (EventsCode, EventsType, EventsTime, EventsDaemon, EventsSource, EventsRef, EventsText) VALUES
   ('DU0001', 'catalog_update', NOW(), '*SHELL*', 'update_bacula_tables', 'pid$$', 'Catalog schema was updated to 1026');