From 5734fa9043f42aae8d3e27bf91b22f06f36c9008 Mon Sep 17 00:00:00 2001 From: Maksym Sadovnychyy Date: Fri, 1 Nov 2024 10:40:21 +0100 Subject: [PATCH] (feature): handlig smb paths init --- src/MaksIT.LTO.Backup/Application.cs | 317 +++++++++++++---------- src/MaksIT.LTO.Backup/Configuration.cs | 31 ++- src/MaksIT.LTO.Backup/configuration.json | 35 ++- 3 files changed, 236 insertions(+), 147 deletions(-) diff --git a/src/MaksIT.LTO.Backup/Application.cs b/src/MaksIT.LTO.Backup/Application.cs index e5e2480..f6738c8 100644 --- a/src/MaksIT.LTO.Backup/Application.cs +++ b/src/MaksIT.LTO.Backup/Application.cs @@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis; using MaksIT.LTO.Core; using MaksIT.LTO.Backup.Entities; +using System.Net; namespace MaksIT.LTO.Backup; public class Application { @@ -58,51 +59,89 @@ public class Application { Console.WriteLine("Tape ejected."); } - public void CreateDescriptor(string directoryPath, string descriptorFilePath, uint blockSize) { - var files = Directory.GetFiles(directoryPath, "*.*", SearchOption.AllDirectories); - // Define list to hold file descriptors - var descriptor = new List(); - uint currentTapeBlock = 0; + public void PathAccessWrapper(WorkingFolder workingFolder, Action myAction) { - foreach (var filePath in files) { - var fileInfo = new FileInfo(filePath); - var relativePath = Path.GetRelativePath(directoryPath, filePath); - var numberOfBlocks = (uint)((fileInfo.Length + blockSize - 1) / blockSize); + if (workingFolder.LocalPath != null) { + var localPath = workingFolder.LocalPath.Path; + var path = workingFolder.LocalPath.Path; - // Optional: Calculate a simple hash for file integrity (e.g., MD5) - using var md5 = System.Security.Cryptography.MD5.Create(); - using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); - using var bufferedStream = new BufferedStream(fileStream, (int)blockSize); + myAction(path); + } + else if (workingFolder.RemotePath != null) { + var remotePath = workingFolder.RemotePath; - byte[] buffer = new byte[blockSize]; - int bytesRead; - while ((bytesRead = bufferedStream.Read(buffer, 0, buffer.Length)) > 0) { - md5.TransformBlock(buffer, 0, bytesRead, null, 0); + if (remotePath.Protocol == "SMB") { + NetworkCredential? networkCredential = default; + + if (remotePath.PasswordCredentials != null) { + var username = remotePath.PasswordCredentials.Username; + var password = remotePath.PasswordCredentials.Password; + + networkCredential = new NetworkCredential(username, password); + } + + var smbPath = remotePath.Path; + + if (networkCredential == null) { + throw new InvalidOperationException("Network credentials are required for remote paths."); + } + + using (new NetworkConnection(smbPath, networkCredential)) { + myAction(smbPath); + } } - md5.TransformFinalBlock(Array.Empty(), 0, 0); - string fileHash = BitConverter.ToString(md5.Hash).Replace("-", "").ToLower(); + } + } - descriptor.Add(new FileDescriptor { - StartBlock = currentTapeBlock, // Position of the file on the tape - NumberOfBlocks = numberOfBlocks, // Number of blocks used by the file - FilePath = relativePath, - FileSize = fileInfo.Length, - CreationTime = fileInfo.CreationTime, - LastModifiedTime = fileInfo.LastWriteTime, - FileHash = fileHash + public void CreateDescriptor(WorkingFolder workingFolder, string descriptorFilePath, uint blockSize) { + + PathAccessWrapper(workingFolder, (directoryPath) => { + var files = Directory.GetFiles(directoryPath, "*.*", SearchOption.AllDirectories); + + // Define list to hold file descriptors + var descriptor = new List(); + uint currentTapeBlock = 0; + + foreach (var filePath in files) { + var fileInfo = new FileInfo(filePath); + var relativePath = Path.GetRelativePath(directoryPath, filePath); + var numberOfBlocks = (uint)((fileInfo.Length + blockSize - 1) / blockSize); + + // Optional: Calculate a simple hash for file integrity (e.g., MD5) + using var md5 = System.Security.Cryptography.MD5.Create(); + using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); + using var bufferedStream = new BufferedStream(fileStream, (int)blockSize); + + byte[] buffer = new byte[blockSize]; + int bytesRead; + while ((bytesRead = bufferedStream.Read(buffer, 0, buffer.Length)) > 0) { + md5.TransformBlock(buffer, 0, bytesRead, null, 0); + } + md5.TransformFinalBlock(Array.Empty(), 0, 0); + string fileHash = BitConverter.ToString(md5.Hash).Replace("-", "").ToLower(); + + descriptor.Add(new FileDescriptor { + StartBlock = currentTapeBlock, // Position of the file on the tape + NumberOfBlocks = numberOfBlocks, // Number of blocks used by the file + FilePath = relativePath, + FileSize = fileInfo.Length, + CreationTime = fileInfo.CreationTime, + LastModifiedTime = fileInfo.LastWriteTime, + FileHash = fileHash + }); + + currentTapeBlock += numberOfBlocks; + } + + // Convert descriptor list to JSON and include BlockSize + string descriptorJson = JsonSerializer.Serialize(new BackupDescriptor { + BlockSize = blockSize, + Files = descriptor }); - currentTapeBlock += numberOfBlocks; - } - - // Convert descriptor list to JSON and include BlockSize - string descriptorJson = JsonSerializer.Serialize(new BackupDescriptor { - BlockSize = blockSize, - Files = descriptor + File.WriteAllText(descriptorFilePath, descriptorJson); }); - - File.WriteAllText(descriptorFilePath, descriptorJson); } private void ZeroFillBlocks(TapeDeviceHandler handler, int blocks, uint blockSize) { @@ -115,80 +154,83 @@ public class Application { } } - public void WriteFilesToTape(string directoryPath, string descriptorFilePath, uint blockSize) { - Console.WriteLine($"Writing files to tape from: {directoryPath}."); - Console.WriteLine($"Block Size: {blockSize}."); + public void WriteFilesToTape(WorkingFolder workingFolder, string descriptorFilePath, uint blockSize) { + PathAccessWrapper(workingFolder, (directoryPath) => { + Console.WriteLine($"Writing files to tape from: {directoryPath}."); + Console.WriteLine($"Block Size: {blockSize}."); - using var handler = new TapeDeviceHandler(_tapePath); + using var handler = new TapeDeviceHandler(_tapePath); - LoadTape(handler); + LoadTape(handler); - handler.SetMediaParams(blockSize); + handler.SetMediaParams(blockSize); - handler.SetPosition(TapeDeviceHandler.TAPE_REWIND); - Thread.Sleep(2000); + handler.SetPosition(TapeDeviceHandler.TAPE_REWIND); + Thread.Sleep(2000); - handler.Prepare(TapeDeviceHandler.TAPE_TENSION); - Thread.Sleep(2000); + handler.Prepare(TapeDeviceHandler.TAPE_TENSION); + Thread.Sleep(2000); - handler.Prepare(TapeDeviceHandler.TAPE_LOCK); - Thread.Sleep(2000); + handler.Prepare(TapeDeviceHandler.TAPE_LOCK); + Thread.Sleep(2000); - handler.WaitForTapeReady(); + handler.WaitForTapeReady(); - // Read descriptor from file system - string descriptorJson = File.ReadAllText(descriptorFilePath); - var descriptor = JsonSerializer.Deserialize(descriptorJson); + // Read descriptor from file system + string descriptorJson = File.ReadAllText(descriptorFilePath); + var descriptor = JsonSerializer.Deserialize(descriptorJson); - if (descriptor == null) { - throw new InvalidOperationException("Failed to deserialize descriptor."); - } + if (descriptor == null) { + throw new InvalidOperationException("Failed to deserialize descriptor."); + } - var currentTapeBlock = (descriptorJson.Length + blockSize - 1) / blockSize; + var currentTapeBlock = (descriptorJson.Length + blockSize - 1) / blockSize; - foreach (var file in descriptor.Files) { - var filePath = Path.Combine(directoryPath, file.FilePath); - using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); - using var bufferedStream = new BufferedStream(fileStream, (int)blockSize); + foreach (var file in descriptor.Files) { + var filePath = Path.Combine(directoryPath, file.FilePath); + using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); + using var bufferedStream = new BufferedStream(fileStream, (int)blockSize); - byte[] buffer = new byte[blockSize]; - int bytesRead; - for (var i = 0; i < file.NumberOfBlocks; i++) { - bytesRead = bufferedStream.Read(buffer, 0, buffer.Length); - if (bytesRead < buffer.Length) { - // Zero-fill the remaining part of the buffer if the last block is smaller than blockSize - Array.Clear(buffer, bytesRead, buffer.Length - bytesRead); + byte[] buffer = new byte[blockSize]; + int bytesRead; + for (var i = 0; i < file.NumberOfBlocks; i++) { + bytesRead = bufferedStream.Read(buffer, 0, buffer.Length); + if (bytesRead < buffer.Length) { + // Zero-fill the remaining part of the buffer if the last block is smaller than blockSize + Array.Clear(buffer, bytesRead, buffer.Length - bytesRead); + } + handler.WriteData(buffer); + currentTapeBlock++; + Thread.Sleep(100); // Small delay between blocks } - handler.WriteData(buffer); + } + + + // write mark to indicate end of files + handler.WriteMarks(TapeDeviceHandler.TAPE_FILEMARKS, 1); + + // write descriptor to tape + var descriptorData = Encoding.UTF8.GetBytes(descriptorJson); + var descriptorBlocks = (descriptorData.Length + blockSize - 1) / blockSize; + for (int i = 0; i < descriptorBlocks; i++) { + var startIndex = i * blockSize; + var length = Math.Min(blockSize, descriptorData.Length - startIndex); + byte[] block = new byte[blockSize]; // Initialized with zeros by default + Array.Copy(descriptorData, startIndex, block, 0, length); + handler.WriteData(block); currentTapeBlock++; Thread.Sleep(100); // Small delay between blocks } - } + // write 3 0 filled blocks to indicate end of backup + ZeroFillBlocks(handler, 3, blockSize); - // write mark to indicate end of files - handler.WriteMarks(TapeDeviceHandler.TAPE_FILEMARKS, 1); + handler.Prepare(TapeDeviceHandler.TAPE_UNLOCK); + Thread.Sleep(2000); + handler.SetPosition(TapeDeviceHandler.TAPE_REWIND); + Thread.Sleep(2000); - // write descriptor to tape - var descriptorData = Encoding.UTF8.GetBytes(descriptorJson); - var descriptorBlocks = (descriptorData.Length + blockSize - 1) / blockSize; - for (int i = 0; i < descriptorBlocks; i++) { - var startIndex = i * blockSize; - var length = Math.Min(blockSize, descriptorData.Length - startIndex); - byte[] block = new byte[blockSize]; // Initialized with zeros by default - Array.Copy(descriptorData, startIndex, block, 0, length); - handler.WriteData(block); - currentTapeBlock++; - Thread.Sleep(100); // Small delay between blocks - } - - // write 3 0 filled blocks to indicate end of backup - ZeroFillBlocks(handler, 3, blockSize); - - handler.Prepare(TapeDeviceHandler.TAPE_UNLOCK); - Thread.Sleep(2000); - handler.SetPosition(TapeDeviceHandler.TAPE_REWIND); - Thread.Sleep(2000); + }); } public BackupDescriptor? FindDescriptor(uint blockSize) { @@ -256,65 +298,68 @@ public class Application { return null; } - public void RestoreDirectory(BackupDescriptor descriptor, string restoreDirectoryPath) { - Console.WriteLine("Restoring files to directory: " + restoreDirectoryPath); - Console.WriteLine("Block Size: " + descriptor.BlockSize); + public void RestoreDirectory(BackupDescriptor descriptor, WorkingFolder workingFolder) { - using var handler = new TapeDeviceHandler(_tapePath); + PathAccessWrapper(workingFolder, (restoreDirectoryPath) => { + Console.WriteLine("Restoring files to directory: " + restoreDirectoryPath); + Console.WriteLine("Block Size: " + descriptor.BlockSize); - LoadTape(handler); + using var handler = new TapeDeviceHandler(_tapePath); - handler.SetMediaParams(descriptor.BlockSize); + LoadTape(handler); - handler.SetPosition(TapeDeviceHandler.TAPE_REWIND); - Thread.Sleep(2000); + handler.SetMediaParams(descriptor.BlockSize); - handler.WaitForTapeReady(); - - foreach (var file in descriptor.Files) { - // Set position to the start block of the file - handler.SetPosition(TapeDeviceHandler.TAPE_ABSOLUTE_BLOCK, 0, file.StartBlock); + handler.SetPosition(TapeDeviceHandler.TAPE_REWIND); Thread.Sleep(2000); - var filePath = Path.Combine(restoreDirectoryPath, file.FilePath); - var directoryPath = Path.GetDirectoryName(filePath); - if (directoryPath != null) { - Directory.CreateDirectory(directoryPath); - } + handler.WaitForTapeReady(); - using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) { - var buffer = new byte[descriptor.BlockSize]; + foreach (var file in descriptor.Files) { + // Set position to the start block of the file + handler.SetPosition(TapeDeviceHandler.TAPE_ABSOLUTE_BLOCK, 0, file.StartBlock); + Thread.Sleep(2000); - for (var i = 0; i < file.NumberOfBlocks; i++) { - var bytesRead = handler.ReadData(buffer, 0, buffer.Length); - if (bytesRead < buffer.Length) { - // Zero-fill the remaining part of the buffer if the last block is smaller than blockSize - Array.Clear(buffer, bytesRead, buffer.Length - bytesRead); + var filePath = Path.Combine(restoreDirectoryPath, file.FilePath); + var directoryPath = Path.GetDirectoryName(filePath); + if (directoryPath != null) { + Directory.CreateDirectory(directoryPath); + } + + using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) { + var buffer = new byte[descriptor.BlockSize]; + + for (var i = 0; i < file.NumberOfBlocks; i++) { + var bytesRead = handler.ReadData(buffer, 0, buffer.Length); + if (bytesRead < buffer.Length) { + // Zero-fill the remaining part of the buffer if the last block is smaller than blockSize + Array.Clear(buffer, bytesRead, buffer.Length - bytesRead); + } + + var bytesToWrite = (i == file.NumberOfBlocks - 1) ? (int)(file.FileSize % descriptor.BlockSize) : buffer.Length; + fileStream.Write(buffer, 0, bytesToWrite); } + } - var bytesToWrite = (i == file.NumberOfBlocks - 1) ? (int)(file.FileSize % descriptor.BlockSize) : buffer.Length; - fileStream.Write(buffer, 0, bytesToWrite); + // check md5 checksum of restored file with the one in descriptor + using (var md5 = System.Security.Cryptography.MD5.Create()) { + using (var fileStreamRead = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { + var fileHash = md5.ComputeHash(fileStreamRead); + var fileHashString = BitConverter.ToString(fileHash).Replace("-", "").ToLower(); + + if (fileHashString != file.FileHash) { + Console.WriteLine($"Checksum mismatch for file: {filePath}"); + } + else { + Console.WriteLine($"Restored file: {filePath}"); + } + } } } - // check md5 checksum of restored file with the one in descriptor - using (var md5 = System.Security.Cryptography.MD5.Create()) { - using (var fileStreamRead = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { - var fileHash = md5.ComputeHash(fileStreamRead); - var fileHashString = BitConverter.ToString(fileHash).Replace("-", "").ToLower(); - - if (fileHashString != file.FileHash) { - Console.WriteLine($"Checksum mismatch for file: {filePath}"); - } - else { - Console.WriteLine($"Restored file: {filePath}"); - } - } - } - } - - handler.SetPosition(TapeDeviceHandler.TAPE_REWIND); - Thread.Sleep(2000); + handler.SetPosition(TapeDeviceHandler.TAPE_REWIND); + Thread.Sleep(2000); + }); } public int CheckMediaSize(string ltoGen) { diff --git a/src/MaksIT.LTO.Backup/Configuration.cs b/src/MaksIT.LTO.Backup/Configuration.cs index 140e3f0..e29bcd8 100644 --- a/src/MaksIT.LTO.Backup/Configuration.cs +++ b/src/MaksIT.LTO.Backup/Configuration.cs @@ -1,10 +1,35 @@ -namespace MaksIT.LTO.Backup; +using System.Security; + +namespace MaksIT.LTO.Backup; + +public abstract class PathBase { + public required string Path { get; set; } +} + +public class LocalPath : PathBase { + // Additional properties specific to local paths can be added here +} + +public class PasswordCredentials { + public required string Username { get; set; } + public required SecureString Password { get; set; } +} + +public class RemotePath : PathBase { + public PasswordCredentials? PasswordCredentials { get; set; } + public required string Protocol { get; set; } // e.g., SMB, FTP, etc. +} + +public class WorkingFolder { + public LocalPath? LocalPath { get; set; } + public RemotePath? RemotePath { get; set; } +} public class BackupItem { public required string Name { get; set; } public required string Barcode { get; set; } - public required string Source { get; set; } - public required string Destination { get; set; } + public required WorkingFolder Source { get; set; } + public required WorkingFolder Destination { get; set; } public required string LTOGen { get; set; } } diff --git a/src/MaksIT.LTO.Backup/configuration.json b/src/MaksIT.LTO.Backup/configuration.json index 15dfe46..b320264 100644 --- a/src/MaksIT.LTO.Backup/configuration.json +++ b/src/MaksIT.LTO.Backup/configuration.json @@ -2,18 +2,37 @@ "TapePath": "\\\\.\\Tape0", "Backups": [ { - "Name": "Test", + "Name": "Normal test", "Barcode": "", - "Source": "F:\\LTO\\Backup", - "Destination": "F:\\LTO\\Restore", - "LTOGen": "LTO5" + "LTOGen": "LTO5", + "Source": { + "LocalPath": { + "Path": "D:\\Drivers" + } + }, + "Destination": { + "LocalPath": { + "Path": "F:\\LTO\\Restore" + } + } }, { - "Name": "Drivers", + "Name": "Network test", "Barcode": "", - "Source": "D:\\Drivers", - "Destination": "F:\\LTO\\Restore", - "LTOGen": "LTO5" + "LTOGen": "LTO5", + "Source": { + "RemotePath": { + "Path": "\\\\nasrv0001.corp.maks-it.com\\LTO\\Backup", + "Credentials": { + "Username": "user", + "Password": "password" + } + } + }, + "Destination": { + "Path": "F:\\LTO\\Restore" + } } +} ] } \ No newline at end of file