From: Eric Bollengier Date: Tue, 13 Sep 2022 15:42:51 +0000 (+0200) Subject: Add Protect fields to the catalog and the SD/DIR protocol X-Git-Tag: Beta-15.0.0~413 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=909d6761c286786f3f85cbde6840f2be463ee6cd;p=thirdparty%2Fbacula.git Add Protect fields to the catalog and the SD/DIR protocol --- diff --git a/bacula/src/cats/cats.h b/bacula/src/cats/cats.h index aa13dca09..e88b755e1 100644 --- a/bacula/src/cats/cats.h +++ b/bacula/src/cats/cats.h @@ -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 */ diff --git a/bacula/src/cats/make_mysql_tables.in b/bacula/src/cats/make_mysql_tables.in index 3c4f04e81..175f9a3bf 100644 --- a/bacula/src/cats/make_mysql_tables.in +++ b/bacula/src/cats/make_mysql_tables.in @@ -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), diff --git a/bacula/src/cats/make_postgresql_tables.in b/bacula/src/cats/make_postgresql_tables.in index a1da714e2..e2a9005e8 100644 --- a/bacula/src/cats/make_postgresql_tables.in +++ b/bacula/src/cats/make_postgresql_tables.in @@ -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) ); diff --git a/bacula/src/cats/make_sqlite3_tables.in b/bacula/src/cats/make_sqlite3_tables.in index cc897a7ad..34b97947f 100644 --- a/bacula/src/cats/make_sqlite3_tables.in +++ b/bacula/src/cats/make_sqlite3_tables.in @@ -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) ); diff --git a/bacula/src/cats/sql_get.c b/bacula/src/cats/sql_get.c index fa3243152..3aaec9e20 100644 --- a/bacula/src/cats/sql_get.c +++ b/bacula/src/cats/sql_get.c @@ -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 { diff --git a/bacula/src/cats/sql_list.c b/bacula/src/cats/sql_list.c index 561a239ad..de61189e4 100644 --- a/bacula/src/cats/sql_list.c +++ b/bacula/src/cats/sql_list.c @@ -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, diff --git a/bacula/src/cats/sql_update.c b/bacula/src/cats/sql_update.c index 6acdd271b..f367cd388 100644 --- a/bacula/src/cats/sql_update.c +++ b/bacula/src/cats/sql_update.c @@ -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); diff --git a/bacula/src/cats/update_mysql_tables.in b/bacula/src/cats/update_mysql_tables.in index d422d7a54..c0b893616 100644 --- a/bacula/src/cats/update_mysql_tables.in +++ b/bacula/src/cats/update_mysql_tables.in @@ -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'); diff --git a/bacula/src/cats/update_postgresql_tables.in b/bacula/src/cats/update_postgresql_tables.in index 0bca86b0e..9376d86dd 100644 --- a/bacula/src/cats/update_postgresql_tables.in +++ b/bacula/src/cats/update_postgresql_tables.in @@ -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'); diff --git a/bacula/src/dird/backup.c b/bacula/src/dird/backup.c index 61496fab7..2bf4e1be1 100644 --- a/bacula/src/dird/backup.c +++ b/bacula/src/dird/backup.c @@ -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)); } diff --git a/bacula/src/dird/catreq.c b/bacula/src/dird/catreq.c index d3b14d710..21ad3287c 100644 --- a/bacula/src/dird/catreq.c +++ b/bacula/src/dird/catreq.c @@ -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; diff --git a/bacula/src/dird/msgchan.c b/bacula/src/dird/msgchan.c index 75b818d7b..f6dbbda17 100644 --- a/bacula/src/dird/msgchan.c +++ b/bacula/src/dird/msgchan.c @@ -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, "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, "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) { diff --git a/bacula/src/dird/next_vol.c b/bacula/src/dird/next_vol.c index d38e9c1b1..cebea5daf 100644 --- a/bacula/src/dird/next_vol.c +++ b/bacula/src/dird/next_vol.c @@ -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; } diff --git a/bacula/src/dird/protos.h b/bacula/src/dird/protos.h index 9987333b7..acf2c5a3f 100644 --- a/bacula/src/dird/protos.h +++ b/bacula/src/dird/protos.h @@ -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, diff --git a/bacula/src/dird/ua_output.c b/bacula/src/dird/ua_output.c index 183415de6..5a1c51376 100644 --- a/bacula/src/dird/ua_output.c +++ b/bacula/src/dird/ua_output.c @@ -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 { diff --git a/bacula/src/dird/ua_status.c b/bacula/src/dird/ua_status.c index df13099b3..93ca3e978 100644 --- a/bacula/src/dird/ua_status.c +++ b/bacula/src/dird/ua_status.c @@ -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)); diff --git a/bacula/src/dird/vbackup.c b/bacula/src/dird/vbackup.c index 7dc2c15fe..d0bb49f51 100644 --- a/bacula/src/dird/vbackup.c +++ b/bacula/src/dird/vbackup.c @@ -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)) { diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index a37414239..5fef5c234 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -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 */ diff --git a/bacula/src/lib/bsys.c b/bacula/src/lib/bsys.c index 829fc48a6..b151c1555 100644 --- a/bacula/src/lib/bsys.c +++ b/bacula/src/lib/bsys.c @@ -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 diff --git a/bacula/src/stored/askdir.c b/bacula/src/stored/askdir.c index 995953e33..4aadc4f35 100644 --- a/bacula/src/stored/askdir.c +++ b/bacula/src/stored/askdir.c @@ -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, "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. diff --git a/bacula/src/stored/dev.h b/bacula/src/stored/dev.h index 83b85bac5..2a0781ee8 100644 --- a/bacula/src/stored/dev.h +++ b/bacula/src/stored/dev.h @@ -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();}; diff --git a/bacula/src/stored/file_dev.c b/bacula/src/stored/file_dev.c index bf207c1c5..9c5199446 100644 --- a/bacula/src/stored/file_dev.c +++ b/bacula/src/stored/file_dev.c @@ -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) diff --git a/bacula/src/stored/file_dev.h b/bacula/src/stored/file_dev.h index c02975729..ebe4330db 100644 --- a/bacula/src/stored/file_dev.h +++ b/bacula/src/stored/file_dev.h @@ -54,6 +54,7 @@ public: int set_writable(); int set_readonly(); int set_atime(btime_t val); + int use_protect(); }; #endif /* __FILE_DEV_ */ diff --git a/bacula/src/stored/reserve.c b/bacula/src/stored/reserve.c index a24b3414e..3d3b24832 100644 --- a/bacula/src/stored/reserve.c +++ b/bacula/src/stored/reserve.c @@ -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); diff --git a/bacula/src/stored/tape_dev.h b/bacula/src/stored/tape_dev.h index e0cf8b15d..031e3980a 100644 --- a/bacula/src/stored/tape_dev.h +++ b/bacula/src/stored/tape_dev.h @@ -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; }; diff --git a/bacula/updatedb/update_mysql_tables_1025_to_1026.in b/bacula/updatedb/update_mysql_tables_1025_to_1026.in index 6ca606263..605f9e402 100644 --- a/bacula/updatedb/update_mysql_tables_1025_to_1026.in +++ b/bacula/updatedb/update_mysql_tables_1025_to_1026.in @@ -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'); diff --git a/bacula/updatedb/update_postgresql_tables_1025_to_1026.in b/bacula/updatedb/update_postgresql_tables_1025_to_1026.in index e46cf105b..35246d233 100644 --- a/bacula/updatedb/update_postgresql_tables_1025_to_1026.in +++ b/bacula/updatedb/update_postgresql_tables_1025_to_1026.in @@ -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');