19 Commits

Author SHA1 Message Date
1ee5114dab Merge pull request 'feature/multi-threading' (#2) from feature/multi-threading into main
Reviewed-on: #2
2023-06-29 00:15:11 +00:00
f403b77864 Code cleanup 2023-06-29 02:03:41 +02:00
42320ebf8d Use ConcurrentDictionary 2023-06-28 13:18:33 +02:00
787721381d refactor doTheThing 2023-06-28 00:54:55 +02:00
7d7e9bac6c refactor checkIfFileWasDeleted 2023-06-28 00:54:30 +02:00
2b4019d7cf move return outside of using 2023-06-28 00:48:14 +02:00
be8180a60d refactor doTheThing 2023-06-28 00:41:38 +02:00
7f6f4c5253 Streamline code 2023-06-27 23:56:43 +02:00
e12117fba8 Small fix 2023-06-27 23:12:50 +02:00
763cde4e2d Use dictionary 2023-06-27 22:58:07 +02:00
531a4676e9 First implementation of parralel checksumming 2023-06-27 22:31:24 +02:00
bc42dd542a Merge pull request 'feature/SQlite' (#1) from feature/SQlite into main
Reviewed-on: #1
2023-06-26 16:22:27 +00:00
836b850f3f Refactor getFilehashesFromDatabase 2023-06-26 18:20:47 +02:00
944b61a1ac Refactor compareDatabases 2023-06-26 17:21:56 +02:00
6fbc53fa53 Add option to compare databases 2023-06-26 16:25:42 +02:00
b899c4c5b6 Add option to check for deleted files 2023-06-26 15:59:06 +02:00
2d42842fb2 Minor tweaks 2023-06-26 12:58:05 +02:00
f99ca8bb26 revert a6c994fa65
revert previous commit
2023-06-23 23:08:14 +00:00
a6c994fa65 Major changes
Now using an SQlite database to store the file hash plus a bunch of other information
2023-06-24 01:02:03 +02:00
3 changed files with 149 additions and 90 deletions

View File

@ -39,19 +39,8 @@ Go to the publish folder
cd src/Chksum/bin/Release/net7.0/linux-x64/publish cd src/Chksum/bin/Release/net7.0/linux-x64/publish
``` ```
Copy the libe_sqlite3.so to your /usr/local/lib or /usr/lib
```bash
cp libe_sqlite3.so /usr/local/lib
```
Run executable Run executable
```bash ```bash
LD_LIBRARY_PATH=/usr/local/lib ./Chksum ./Chksum
``` ```
Info
LD_LIBRARY_PATH=/usr/local/lib is needed to tell the executable where the library is located
Alternately you can put the libe_sqlite3.so into the same folder as the executable

View File

@ -8,7 +8,7 @@ public class Program {
Console.WriteLine("Please specify an option."); Console.WriteLine("Please specify an option.");
PrintAvailableOptions(); PrintAvailableOptions();
return; return;
} else if (args.Length > 1) { } else if (args.Length > 1 && args[0] != "compareDatabases") {
Console.WriteLine("Too many options."); Console.WriteLine("Too many options.");
return; return;
} }
@ -30,15 +30,18 @@ public class Program {
Console.ForegroundColor = ConsoleColor.Green; Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Checksum process finished"); Console.WriteLine("Checksum process finished");
break; break;
case "compareChecksums": case "compareDatabases":
Console.WriteLine("Comparing all md5 checksum files. If there is none, creating one.");
Console.ResetColor(); Console.ResetColor();
utils.compareChecksums(); utils.compareDatabases(args[1]);
break; break;
case "createDB": case "createDB":
utils.initializeDB(); utils.initializeDB();
break; break;
case "checkIfFileWasDeleted":
Console.ResetColor();
utils.checkIfFileWasDeleted();
break;
case "help": case "help":
PrintAvailableOptions(); PrintAvailableOptions();
break; break;
@ -57,6 +60,7 @@ public class Program {
"checksum", "checksum",
"compareChecksums", "compareChecksums",
"createDB", "createDB",
"checkIfFileWasDeleted",
"help" "help"
}; };

View File

@ -1,11 +1,20 @@
using System.Collections.Concurrent;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
namespace Chksum.Utils; namespace Chksum.Utils;
public class ChksumUtils { public class ChksumUtils {
private int getFileCount() { private int getTotalFileCount() {
int fileCount = Directory.GetFiles(Directory.GetCurrentDirectory()).Length; // Get file count in current directory int totalFileCount = Directory.GetFiles(Directory.GetCurrentDirectory(), "*", SearchOption.AllDirectories).Length;
return fileCount; return totalFileCount - 3; // Remove the program, datbase and library from the totalFileCount
}
private string[] indexFiles() {
string[] indexedFiles = Directory.GetFiles(Directory.GetCurrentDirectory(), "*", SearchOption.AllDirectories);
string[] filesToExclude = { "Chksum", "chksum.db", "libe_sqlite3.so" };
indexedFiles = indexedFiles.Where(file => !filesToExclude.Contains(Path.GetFileName(file))).ToArray();
return indexedFiles;
} }
public string DatabaseRoot { get; set; } = string.Empty; public string DatabaseRoot { get; set; } = string.Empty;
@ -62,54 +71,61 @@ public class ChksumUtils {
} }
} }
private string CalculateMD5(string filename) { private Dictionary<string, string> CalculateChecksums(string[] filenames) {
using (var md5 = System.Security.Cryptography.MD5.Create()) { ConcurrentDictionary<string, string> checksums = new ConcurrentDictionary<string, string>();
using (var stream = File.OpenRead(filename)) {
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
}
public void doTheThing() { Parallel.ForEach(filenames, (filename, state) => {
foreach (var directory in Directory.GetDirectories(Directory.GetCurrentDirectory())) using (var md5 = MD5.Create()) {
using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadWrite")) { using (var stream = File.OpenRead(filename)) {
Directory.SetCurrentDirectory(directory); // Set new root var hash = md5.ComputeHash(stream);
if (getFileCount() >= 1) { var checksum = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
DirectoryInfo dir = new DirectoryInfo(Directory.GetCurrentDirectory());
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files) {
string fileName = file.Name;
string absolutePathToFile = Path.GetFullPath(fileName);
string pathToFile = Path.GetRelativePath(DatabaseRoot, absolutePathToFile);
string fileHash = CalculateMD5(fileName);
if (checkIfFileMovedAndUpdatePathToFile(fileHash, fileName, pathToFile) == false && checkIfFileAlreadyExists(fileHash, fileName) == false) { lock (checksums) {
connection.Open(); checksums.TryAdd(filename, checksum);
var command = connection.CreateCommand();
command.CommandText =
@"
INSERT INTO file (filehash, filename, pathtofile)
VALUES ($filehash, $filename, $pathtofile)
";
command.Parameters.AddWithValue("$filehash", fileHash);
command.Parameters.AddWithValue("$filename", fileName);
command.Parameters.AddWithValue("$pathtofile", pathToFile);
command.ExecuteNonQuery();
} }
} }
} }
doTheThing(); });
return new Dictionary<string, string>(checksums);
}
public void doTheThing() {
using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadWrite")) {
if (getTotalFileCount() < 1) {
return;
}
connection.Open();
Dictionary<string, string> fileHashes = CalculateChecksums(indexFiles());
foreach (var file in fileHashes) {
string absolutePathToFile = file.Key;
string fileName = Path.GetFileName(absolutePathToFile);
string pathToFile = Path.GetRelativePath(DatabaseRoot, absolutePathToFile);
string fileHash = file.Value;
if (checkIfFileMovedAndUpdatePathToFile(fileHash, fileName, pathToFile) == false && checkIfFileAlreadyExistsInDatabase(fileHash, fileName) == false) {
var command = connection.CreateCommand();
command.CommandText =
@"
INSERT INTO file (filehash, filename, pathtofile)
VALUES ($filehash, $filename, $pathtofile)
";
command.Parameters.AddWithValue("$filehash", fileHash);
command.Parameters.AddWithValue("$filename", fileName);
command.Parameters.AddWithValue("$pathtofile", pathToFile);
command.ExecuteNonQuery();
}
}
} }
} }
private bool checkIfFileAlreadyExists(string fileHash, string pathToFile) { private bool checkIfFileAlreadyExistsInDatabase(string fileHash, string pathToFile) {
string filehash = string.Empty; string filehash = string.Empty;
string pathtofile = string.Empty; string pathtofile = string.Empty;
bool doesExist = false; bool doesExist = false;
using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadWrite")) { using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadOnly")) {
connection.Open(); connection.Open();
var command = connection.CreateCommand(); var command = connection.CreateCommand();
@ -128,9 +144,6 @@ public class ChksumUtils {
} }
if (fileHash == filehash) { if (fileHash == filehash) {
Console.WriteLine("Duplicate files found:");
Console.WriteLine($"\toriginal\t{pathToFile}");
Console.WriteLine($"\tduplicate\t{pathtofile}\n");
doesExist = true; doesExist = true;
} }
return doesExist; return doesExist;
@ -168,47 +181,100 @@ public class ChksumUtils {
command2.Parameters.AddWithValue("$filehash", fileHash); command2.Parameters.AddWithValue("$filehash", fileHash);
command2.ExecuteNonQuery(); command2.ExecuteNonQuery();
Console.WriteLine("File moved:"); Console.WriteLine("File moved or is a duplicate:");
Console.WriteLine($"\tfrom\t{pathToFile}"); Console.WriteLine($"\tfrom\t{pathToFile}");
Console.WriteLine($"\tto \t{pathtofile}\n"); Console.WriteLine($"\tto \t{pathtofile}\n");
wasMoved = true; wasMoved = true;
} }
return wasMoved; }
return wasMoved;
}
public void checkIfFileWasDeleted() {
string pathToFile = string.Empty;
using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadWrite")) {
connection.Open();
var selectCommand = connection.CreateCommand();
selectCommand.CommandText =
@"
Select pathtofile FROM file
";
using (var reader = selectCommand.ExecuteReader()) {
while (reader.Read()) {
pathToFile = reader.GetString(0);
if (File.Exists(pathToFile)) {
continue;
}
var deleteCommand = connection.CreateCommand();
deleteCommand.CommandText =
@"
DELETE FROM file
WHERE pathtofile = $pathtofile
";
deleteCommand.Parameters.AddWithValue("$pathtofile", pathToFile);
deleteCommand.ExecuteNonQuery();
Console.WriteLine("File deleted:");
Console.WriteLine($"\t{pathToFile}\n");
}
}
} }
} }
public void compareChecksums() { private List<string> getFilehashesFromDatabase(string connectionString) {
foreach (var directory in Directory.GetDirectories(Directory.GetCurrentDirectory())) { List<string> filehashesFromDatabase = new List<string>();
Directory.SetCurrentDirectory(directory); // Set new root
if (getFileCount() >= 1) { using (var connection = new SqliteConnection(connectionString)) {
DirectoryInfo dir = new DirectoryInfo(Directory.GetCurrentDirectory()); string filehash = string.Empty;
FileInfo[] files = dir.GetFiles();
// files.ToList().ForEach(i => Console.WriteLine(i.ToString())); // Print all files in files array connection.Open();
foreach (FileInfo file in files) {
string fileName = file.Name; var selectCommand = connection.CreateCommand();
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); selectCommand.CommandText =
string checksumFile = Directory.GetCurrentDirectory() + "/" + fileNameWithoutExtension + ".md5"; @"
string fileMd5Checksum = fileNameWithoutExtension + ".md5"; Select filehash FROM file
if (File.Exists(fileMd5Checksum)) { ";
string newFileChecksum = CalculateMD5(fileName) + " " + fileName;
string existingFileChecksum = File.ReadAllText(fileMd5Checksum); using (var reader = selectCommand.ExecuteReader()) {
string newFileName = newFileChecksum.Substring(34); while (reader.Read()) {
string existingFileName = existingFileChecksum.Substring(34); filehash = reader.GetString(0);
if (newFileChecksum.Equals(existingFileChecksum)) { filehashesFromDatabase.Add(filehash);
Console.WriteLine(newFileName + " and " + existingFileName + " are the same."); }
} else { }
Console.WriteLine(newFileName + " and " + existingFileName + " are not the same."); }
Console.WriteLine("The checksum of " + newFileName + " is " + newFileChecksum);
Console.WriteLine("The checksum of the already exting file " + existingFileName + " is " + existingFileChecksum); return filehashesFromDatabase;
// TODO Tell the user to check which file is the correct one }
}
} else { public void compareDatabases(string filePathToOtherDatabase) {
File.AppendAllText(checksumFile, CalculateMD5(fileName) + " " + fileName); List<string> filesThatDoNotExistsInTheRemote = getFilehashesFromDatabase("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadOnly").Except(getFilehashesFromDatabase("Data Source=" + filePathToOtherDatabase + ";Mode=ReadOnly")).ToList();
Console.WriteLine("Calculated checksum for: " + checksumFile);
foreach (string file in filesThatDoNotExistsInTheRemote) {
using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadOnly")) {
string filename = string.Empty;
connection.Open();
var selectCommand = connection.CreateCommand();
selectCommand.CommandText =
@"
Select filename FROM file WHERE filehash = $filehash
";
selectCommand.Parameters.AddWithValue("$filehash", file);
using (var reader = selectCommand.ExecuteReader()) {
while (reader.Read()) {
filename = reader.GetString(0);
Console.WriteLine("File not found in remote:");
Console.WriteLine($"\t{filename}\n");
} }
} }
} }
compareChecksums();
} }
} }