/// <summary>
        /// Checks the file manifest against the file shards on the server
        /// </summary>
        /// <param name="authorizationSessionGenerator">The generator for an authorization session</param>
        public void CheckFileManifest(Func<BackblazeB2AuthorizationSession> authorizationSessionGenerator)
        {
            this.Info("Checking file manifest");
            // Get just the file names on the server
            ListFilesAction listFilesAction = ListFilesAction.CreateListFileActionForFileNames(authorizationSessionGenerator(), Config.BucketID, true);
            BackblazeB2ActionResult<BackblazeB2ListFilesResult> listFilesActionResult = listFilesAction.Execute();
            if (listFilesActionResult.HasErrors)
            {
                throw new FailedToGetListOfFilesOnB2Exception
                {
                    BackblazeErrorDetails = listFilesActionResult.Errors,
                };
            }

            ISet<string> allShardIDsPresent = listFilesActionResult.Result.Files.Select(t => t.FileName).ToHashSet();
            IDictionary<string, ISet<Database.File>> shardIDToDatbaseFile = new Dictionary<string, ISet<Database.File>>();
            foreach (Database.File file in FileDatabaseManifestFiles)
            {
                foreach (string shardID in file.FileShardIDs)
                {
                    if (shardIDToDatbaseFile.TryGetValue(shardID, out ISet<Database.File> setOfFiles) == false)
                    {
                        setOfFiles = new HashSet<Database.File>();
                        shardIDToDatbaseFile[shardID] = setOfFiles;
                    }

                    setOfFiles.Add(file);
                }
            }

            IEnumerable<string> allShardIDsNotAccountedFor = from file in FileDatabaseManifestFiles
                                                             from shardID in file.FileShardIDs
                                                             where allShardIDsPresent.Contains(shardID) == false
                                                             select shardID;
            bool allShardIDsAccountedFor = true;
            foreach (string shardIDNotAccountedFor in allShardIDsNotAccountedFor)
            {
                allShardIDsAccountedFor = false;
                StringBuilder stringBuilder = new StringBuilder();
                foreach (Database.File file in shardIDToDatbaseFile[shardIDNotAccountedFor])
                {
                    stringBuilder.Append($"{file} | ");
                }
                this.Critical($"Shard ID {shardIDNotAccountedFor} for file(s): {stringBuilder.ToString()}");
            }

            if (allShardIDsAccountedFor)
            {
                this.Info("All Shard IDs accounted for");
            }
            else
            {
                this.Info("Some Shard IDs Are Not Accounted For");
            }
        }
Example #2
0
        /// <summary>
        /// Prunes any shards on the server that are not accounted for in the database manifest
        /// </summary>
        /// <param name="authorizationSessionGenerator">The generator for an authorization session</param>
        public void PruneShards(Func <BackblazeB2AuthorizationSession> authorizationSessionGenerator)
        {
            this.Debug("Pruning shards");
            // Get just the file names on the server
            ListFilesAction listFilesAction = ListFilesAction.CreateListFileActionForFileNames(authorizationSessionGenerator(), Config.BucketID, true);
            BackblazeB2ActionResult <BackblazeB2ListFilesResult> listFilesActionResult = listFilesAction.Execute();

            if (listFilesActionResult.HasErrors)
            {
                throw new FailedToGetListOfFilesOnB2Exception
                      {
                          BackblazeErrorDetails = listFilesActionResult.Errors,
                      };
            }

            object lockObject = new object();
            IDictionary <string, FileResult> fileNameToFileResultMap = listFilesActionResult.Result.Files.ToDictionary(k => k.FileName, v => v);
            ISet <string> allDatabaseFileShardIds = FileDatabaseManifestFiles.SelectMany(t => t.FileShardIDs).ToHashSet();
            ISet <string> allRawFileNamesOnServer = fileNameToFileResultMap.Keys.ToHashSet();
            ISet <string> allFilesNotAccountedFor = allRawFileNamesOnServer.Except(allDatabaseFileShardIds).Where(t => t.Equals(RemoteFileDatabaseManifestName, StringComparison.OrdinalIgnoreCase) == false).ToHashSet();

            Parallel.ForEach(allFilesNotAccountedFor, fileNameNotAccountedFor =>
            {
                CancellationEventRouter.GlobalCancellationToken.ThrowIfCancellationRequested();

                FileResult fileNotAccountedFor    = fileNameToFileResultMap[fileNameNotAccountedFor];
                DeleteFileAction deleteFileAction =
                    new DeleteFileAction(authorizationSessionGenerator(), fileNotAccountedFor.FileID, fileNotAccountedFor.FileName);

                BackblazeB2ActionResult <BackblazeB2DeleteFileResult> deletionResult = deleteFileAction.Execute();
                if (deletionResult.HasErrors)
                {
                    lock (lockObject)
                    {
                        this.Critical($"Failed to prune file. Reason: {deletionResult}");
                    }
                }
                else
                {
                    lock (lockObject)
                    {
                        this.Info($"Pruned shard: {fileNameNotAccountedFor}");
                    }
                }
            });
        }
        protected IEnumerable <FileResult> GetRawB2FileNames(BackblazeB2AuthorizationSession authorizationSession)
        {
            ListFilesAction listFilesAction =
                ListFilesAction.CreateListFileActionForFileVersions(authorizationSession, Config.BucketID, true);
            BackblazeB2ActionResult <BackblazeB2ListFilesResult> listFilesActionResult = listFilesAction.Execute();

            if (listFilesActionResult.HasErrors)
            {
                throw new FailedToGetListOfFilesOnB2Exception
                      {
                          BackblazeErrorDetails = listFilesActionResult.Errors,
                      };
            }

            IEnumerable <FileResult> rawB2FileList = listFilesActionResult.Result.Files;

            return(rawB2FileList);
        }
        private IDictionary <string, FileResult> GetShardIDToFileResultMapping(
            BackblazeB2AuthorizationSession authorizationSession
            )
        {
            ListFilesAction listFilesAction = ListFilesAction.CreateListFileActionForFileNames(
                authorizationSession,
                _config.BucketID,
                true
                );
            BackblazeB2ActionResult <BackblazeB2ListFilesResult> listFilesActionResult = listFilesAction.Execute();

            if (listFilesActionResult.HasErrors)
            {
                throw new FailedToGetListOfFilesOnB2Exception
                      {
                          BackblazeErrorDetails = listFilesActionResult.Errors,
                      };
            }

            return(listFilesActionResult.Result.Files.ToDictionary(k => k.FileName, v => v));
        }
        /// <summary>
        /// Initializes this file database manifest
        /// </summary>
        private static FileDatabaseManifest GetOrCreateFileDatabaseManifest(
            BackblazeB2AuthorizationSession authorizationSession,
            Config config
            )
        {
            if (SharedFileDatabaseManifest == null)
            {
                lock (SharedFileDatabaseManifestLock)
                {
                    if (SharedFileDatabaseManifest == null)
                    {
                        // Get just the file names on the server
                        ListFilesAction listFilesAction = ListFilesAction.CreateListFileActionForFileNames(
                            authorizationSession,
                            config.BucketID,
                            true
                            );
                        BackblazeB2ActionResult <BackblazeB2ListFilesResult> listFilesActionResult = listFilesAction.Execute();
                        if (listFilesActionResult.HasErrors)
                        {
                            throw new FailedToGetListOfFilesOnB2Exception
                                  {
                                      BackblazeErrorDetails = listFilesActionResult.Errors,
                                  };
                        }
                        FileResult fileDatabaseManifestFileResult = listFilesActionResult.Result.Files
                                                                    .Where(f => f.FileName.Equals(RemoteFileDatabaseManifestName, StringComparison.Ordinal))
                                                                    .SingleOrDefault();
                        if (fileDatabaseManifestFileResult == null)
                        {
                            SharedFileDatabaseManifest = new FileDatabaseManifest
                            {
                                DataFormatVersionNumber = CurrentDatabaseDataFormatVersion,
                                Files = new Database.File[0]
                            };
                        }
                        else
                        {
                            using (MemoryStream outputStream = new MemoryStream())
                                using (DownloadFileAction manifestFileDownloadAction = new DownloadFileAction(
                                           authorizationSession,
                                           outputStream,
                                           fileDatabaseManifestFileResult.FileID
                                           ))
                                {
                                    BackblazeB2ActionResult <BackblazeB2DownloadFileResult> manifestResultOption =
                                        manifestFileDownloadAction.Execute();
                                    if (manifestResultOption.HasResult)
                                    {
                                        // Now, read string from manifest
                                        outputStream.Flush();
                                        SharedFileDatabaseManifest = DeserializeManifest(
                                            outputStream.ToArray(),
                                            config
                                            );
                                    }
                                    else
                                    {
                                        SharedFileDatabaseManifest = new FileDatabaseManifest
                                        {
                                            DataFormatVersionNumber = CurrentDatabaseDataFormatVersion,
                                            Files = new Database.File[0]
                                        };
                                    }
                                }
                        }
                    }
                }
            }

            return(SharedFileDatabaseManifest);
        }
        /// <summary>
        /// Deletes a file off the remote file system
        /// </summary>
        /// <param name="authorizationSessionGenerator">The generator function that returns an authorization session</param>
        /// <param name="file">The file to delete</param>
        public void DeleteFile(
            Func <BackblazeB2AuthorizationSession> authorizationSessionGenerator,
            Database.File file
            )
        {
            this.Debug($"Begin deleting file: {file.FileName}");
            // Remove entry in the File Manifest
            RemoveFile(file);

            // Determine if another file shares the same file shards
            IEnumerable <Database.File> filesThatShareTheSameShards = from currentFile in FileDatabaseManifestFiles
                                                                      where file.FileLength == currentFile.FileLength &&
                                                                      file.FileShardIDs.Length == currentFile.FileShardIDs.Length &&
                                                                      file.SHA1.Equals(currentFile.SHA1, StringComparison.OrdinalIgnoreCase) &&
                                                                      // Even if a single shard ID is shared by another file, that's enough to count it
                                                                      // as being equal (or at least not eligible for hard-deletion)
                                                                      file.FileShardIDs.Any(t => currentFile.FileShardIDs.Contains(t))
                                                                      select currentFile;

            // If there are no files that share the same Shard IDs
            if (filesThatShareTheSameShards.Any() == false)
            {
                // Get the raw B2 File List so we can get the B2 file IDs of the file shards
                ListFilesAction listFilesAction = ListFilesAction.CreateListFileActionForFileNames(authorizationSessionGenerator(), Config.BucketID, true);
                BackblazeB2ActionResult <BackblazeB2ListFilesResult> listFilesActionResult = listFilesAction.Execute();
                if (listFilesActionResult.HasErrors)
                {
                    throw new FailedToGetListOfFilesOnB2Exception
                          {
                              BackblazeErrorDetails = listFilesActionResult.Errors,
                          };
                }
                IEnumerable <FileResult>         rawB2FileList        = listFilesActionResult.Result.Files;
                IDictionary <string, FileResult> fileNameToFileResult = rawB2FileList.ToDictionary(k => k.FileName, v => v);

                foreach (string shardID in file.FileShardIDs)
                {
                    if (fileNameToFileResult.TryGetValue(shardID, out FileResult fileShardToDelete))
                    {
                        DeleteFileAction deleteFileAction =
                            new DeleteFileAction(authorizationSessionGenerator(), fileShardToDelete.FileID, fileShardToDelete.FileName);

                        BackblazeB2ActionResult <BackblazeB2DeleteFileResult> deletionResult = deleteFileAction.Execute();
                        if (deletionResult.HasErrors)
                        {
                            this.Critical($"Failed to delete file. Reason: {deletionResult}");
                        }
                    }
                }
            }
            else
            {
                this.Info($"File {file.FileName} shares file shares with another file. Will not perform a hard-delete. Removing from file manifest instead");
            }

            while (TryUploadFileDatabaseManifest(authorizationSessionGenerator()) == false)
            {
                Thread.Sleep(5);
            }

            this.Info($"Deleted file: {file.FileName}");
        }