/// <summary> /// Syncs a cache with this backup store for the given bsname. /// Moves blobs only present in cache to the blobstore tied to this BackupStore /// Does not trigger a save of either cache or this backup set /// </summary> /// <param name="cache"></param> /// <param name="bsname"></param> /// <param name="dstbset"></param> /// <param name="cachebset"></param> public (BackupSet dstbset, BackupSet cachebset) SyncCache(BackupStore cache, string bsname, BackupSet?dstbset = null, BackupSet?cachebset = null) { int cacheindex = 0; int dstindex = 0; BackupRecord?cachebr = null; BackupRecord?dstbr = null; BackupSetReference cachebsname = new(bsname, false, true, false); if (dstbset == null) { dstbset = LoadBackupSet(new BackupSetReference(bsname, false, false, false)); } if (cachebset == null) { cachebset = cache.LoadBackupSet(new BackupSetReference(bsname, false, false, false)); } if (cachebset.Backups.Count > 0 && dstbset.Backups.Count > 0) { while (cachebset.Backups.Count > cacheindex && dstbset.Backups.Count > dstindex) { if (!cachebset.Backups[cacheindex].hash.SequenceEqual(dstbset.Backups[dstindex].hash)) { if (cachebr == null) { cachebr = cache.GetBackupRecord(cachebsname, cachebset.Backups[cacheindex].hash); } if (dstbr == null) { dstbr = GetBackupRecord(new BackupSetReference(bsname, false, false, false), dstbset.Backups[dstindex].hash); } if (cachebr.BackupTime < dstbr.BackupTime) { if (cachebset.Backups[cacheindex].shallow) { // Remove shallow backups from cache not present in dst cache.Dependencies.Blobs.DecrementReferenceCount(cachebsname, cachebset.Backups[cacheindex].hash, BlobLocation.BlobType.BackupRecord, false); cachebset.Backups.RemoveAt(cacheindex); } else { // Add non shallow backups from cache not present in dst dstbset.Backups.Insert(dstindex, (cachebset.Backups[cacheindex].hash, false)); cache.Dependencies.Blobs.TransferBackup(Dependencies.Blobs, new BackupSetReference(bsname, false, false, false), cachebset.Backups[cacheindex].hash, true); // After transfer, make the cache backup shallow // Since no clean way to only get file references and not "parent" references, // we delete the entire backup data from cache, then add it back shallow // TODO: Means to iterate through blobs only including files cache.Dependencies.Blobs.DecrementReferenceCount(cachebsname, cachebset.Backups[cacheindex].hash, BlobLocation.BlobType.BackupRecord, true); Dependencies.Blobs.TransferBackup(cache.Dependencies.Blobs, cachebsname, dstbset.Backups[dstindex].hash, false); dstindex += 1; // After insert and increment j still referes to the same backup (dstbr) cacheindex += 1; } cachebr = null; } else // (srcbr.BackupTime > dstbr.BackupTime) { // Add (as shallow) backups in dst not present in cache cachebset.Backups.Insert(cacheindex, (dstbset.Backups[dstindex].hash, true)); Dependencies.Blobs.TransferBackup(cache.Dependencies.Blobs, cachebsname, dstbset.Backups[dstindex].hash, false); cacheindex += 1; dstindex += 1; dstbr = null; } } else { cacheindex += 1; cachebr = null; dstindex += 1; dstbr = null; } } } // Handle backups "dangling" after merge while (cacheindex < cachebset.Backups.Count) { if (cachebset.Backups[cacheindex].shallow) { // Remove shallow backups from cache not present in dst cache.Dependencies.Blobs.DecrementReferenceCount(cachebsname, cachebset.Backups[cacheindex].hash, BlobLocation.BlobType.BackupRecord, false); cachebset.Backups.RemoveAt(cacheindex); } else { // Add non shallow backups from cache not present in dst dstbset.Backups.Add((cachebset.Backups[cacheindex].hash, false)); cache.Dependencies.Blobs.TransferBackup(Dependencies.Blobs, new BackupSetReference(bsname, false, false, false), cachebset.Backups[cacheindex].hash, true); dstindex += 1; // After insert and increment j still referes to the same backup (dstbr) cacheindex += 1; } } while (dstindex < dstbset.Backups.Count) { // Add (as shallow) backups in dst not present in cache cachebset.Backups.Add((dstbset.Backups[dstindex].hash, true)); Dependencies.Blobs.TransferBackup(cache.Dependencies.Blobs, cachebsname, dstbset.Backups[dstindex].hash, false); cacheindex += 1; dstindex += 1; } Dependencies.Blobs.CacheBlobList(bsname, cache.Dependencies.Blobs); return(dstbset, cachebset); }
/// <summary> /// Loads a BackupSet from a file. /// </summary> /// <param name="backuplistfile"></param> /// <param name="blobs"></param> /// <returns>A previously stored BackupStore object</returns> public BackupSet LoadBackupSet(string bsname) { return(BackupSet.deserialize(Dependencies.LoadBackupSetData(bsname))); }
/// <summary> /// Attempts to save the BackupStore to disk. /// If saving fails an error is thrown. /// </summary> /// <param name="path"></param> public void SaveBackupSet(BackupSet bset, string bsname) { // NOTE: This overwrites the previous file every time. // This should be okay as the serialized BackupStore filesize should always be small. Dependencies.StoreBackupSetData(bsname, bset.serialize()); }
/// <summary> /// /// </summary> /// <param name="bsname"></param> /// <param name="message"></param> /// <param name="metadatatreehash"></param> /// <param name="shallow"></param> /// <returns>The hash of the new backup</returns> public byte[] AddBackup(string bsname, string message, byte[] metadatatreehash, bool shallow, DateTime backupTime, BackupSet bset = null) { if (bset == null) { bset = LoadBackupSet(bsname); } BackupRecord newbackup = new BackupRecord(message, metadatatreehash, backupTime); byte[] brbytes = newbackup.serialize(); byte[] backuphash = BlobStore.StoreData(new List <BlobStore>(1) { Dependencies.Blobs }, bsname, brbytes); bset.Backups.Add((backuphash, shallow)); return(backuphash); }