Implemented improved method for detecting identical files.

This commit is contained in:
LoRd_MuldeR 2022-03-04 20:05:52 +01:00
parent ca16052112
commit c607046831
Signed by: mulder
GPG Key ID: 2B5913365F57E03F
5 changed files with 63 additions and 103 deletions

View File

@ -67,7 +67,7 @@ int encrypt(const char *const passphrase, const CHR *const input_path, const CHR
goto clean_up; goto clean_up;
} }
const uint64_t file_size = get_file_size(file_in); const uint64_t file_size = get_size(file_in);
if (file_size == UINT64_MAX) if (file_size == UINT64_MAX)
{ {
FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr); FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr);
@ -252,7 +252,7 @@ int decrypt(const char *const passphrase, const CHR *const input_path, const CHR
goto clean_up; goto clean_up;
} }
const uint64_t file_size = get_file_size(file_in); const uint64_t file_size = get_size(file_in);
if (file_size == UINT64_MAX) if (file_size == UINT64_MAX)
{ {
FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr); FPUTS(T("I/O error: Failed to determine size of input file!\n\n"), stderr);

View File

@ -144,11 +144,12 @@ static void sigint_handler(const int sig)
int MAIN(const int argc, CHR *const argv[]) int MAIN(const int argc, CHR *const argv[])
{ {
int result = EXIT_FAILURE;
const CHR *input_file = NULL, *output_file = NULL;
char *passphrase_buffer = NULL;
init_terminal(); init_terminal();
setup_signal_handler(SIGINT, sigint_handler); setup_signal_handler(SIGINT, sigint_handler);
int result = EXIT_FAILURE;
CHR *input_file = NULL, *output_file = NULL;
char *passphrase_buffer = NULL;
FPRINTF(stderr, T("SlunkCrypt Utility (%") T(PRIstr) T("-%") T(PRIstr) T("), by LoRd_MuldeR <MuldeR2@GMX.de>\n"), OS_TYPE, CPU_ARCH); FPRINTF(stderr, T("SlunkCrypt Utility (%") T(PRIstr) T("-%") T(PRIstr) T("), by LoRd_MuldeR <MuldeR2@GMX.de>\n"), OS_TYPE, CPU_ARCH);
FPRINTF(stderr, T("Using libSlunkCrypt v%u.%u.%u [%") T(PRIstr) T("]\n\n"), SLUNKCRYPT_VERSION_MAJOR, SLUNKCRYPT_VERSION_MINOR, SLUNKCRYPT_VERSION_PATCH, SLUNKCRYPT_BUILD); FPRINTF(stderr, T("Using libSlunkCrypt v%u.%u.%u [%") T(PRIstr) T("]\n\n"), SLUNKCRYPT_VERSION_MAJOR, SLUNKCRYPT_VERSION_MINOR, SLUNKCRYPT_VERSION_PATCH, SLUNKCRYPT_BUILD);
@ -219,23 +220,23 @@ int MAIN(const int argc, CHR *const argv[])
} }
} }
input_file = absolute_path(argv[PW_FROM_ENV ? 2U : 3U]); input_file = argv[PW_FROM_ENV ? 2U : 3U];
if ((!input_file) || (!input_file[0U])) if (!input_file[0U])
{ {
FPUTS(T("Error: Input file path could not be resolved!\n\n"), stderr); FPUTS(T("Error: The specified input file name must not be empty!\n\n"), stderr);
goto clean_up; goto clean_up;
} }
output_file = absolute_path(argv[PW_FROM_ENV ? 3U : 4U]); output_file = argv[PW_FROM_ENV ? 3U : 4U];
if ((!output_file) || (!output_file[0U])) if (!output_file[0U])
{ {
FPUTS(T("Error: Output file path could not be resolved!\n\n"), stderr); FPUTS(T("Error: The specified output file name must not be empty!\n\n"), stderr);
goto clean_up; goto clean_up;
} }
if (!path_compare(input_file, output_file)) if (same_file(input_file, output_file) > 0)
{ {
FPUTS(T("Error: Input and output files must not be the same path! (effectively)\n\n"), stderr); FPUTS(T("Error: The input and output files must not be the same!\n\n"), stderr);
goto clean_up; goto clean_up;
} }
@ -316,16 +317,6 @@ clean_up:
free(passphrase_buffer); free(passphrase_buffer);
} }
if (input_file)
{
free(input_file);
}
if (output_file)
{
free(output_file);
}
return result; return result;
} }

View File

@ -56,9 +56,8 @@ static void trim_end_of_line(char *const buffer)
char *read_passphrase(const CHR *const file_name) char *read_passphrase(const CHR *const file_name)
{ {
char *buffer = NULL;
CHR *passphrase_path = NULL;
FILE *passphrase_file = NULL; FILE *passphrase_file = NULL;
char *buffer = NULL;
if ((!file_name) || (!file_name[0U])) if ((!file_name) || (!file_name[0U]))
{ {
@ -66,20 +65,10 @@ char *read_passphrase(const CHR *const file_name)
goto finish; goto finish;
} }
if (STRICMP(file_name, T("-"))) passphrase_file = STRICMP(file_name, T("-")) ? FOPEN(file_name, T("rb")) : stdin;
{
passphrase_path = absolute_path(file_name);
if ((!passphrase_path) || (!passphrase_path[0U]))
{
FPUTS(T("Error: Passphrase input file path could not be resolved!\n\n"), stderr);
goto finish;
}
}
passphrase_file = passphrase_path ? FOPEN(passphrase_path, T("rb")): stdin;
if (!passphrase_file) if (!passphrase_file)
{ {
FPRINTF(stderr, T("Error: Failed to open passphrase file \"%") T(PRISTR) T("\" for reading!\n\n%") T(PRISTR) T("\n\n"), passphrase_path, STRERROR(errno)); FPRINTF(stderr, T("Error: Failed to open passphrase file \"%") T(PRISTR) T("\" for reading!\n\n%") T(PRISTR) T("\n\n"), file_name, STRERROR(errno));
goto finish; goto finish;
} }
@ -108,11 +97,6 @@ finish:
fclose(passphrase_file); fclose(passphrase_file);
} }
if (passphrase_path)
{
free(passphrase_path);
}
return buffer; return buffer;
} }

View File

@ -27,6 +27,7 @@
# include <io.h> # include <io.h>
# include <fcntl.h> # include <fcntl.h>
# define STAT_T struct _stati64 # define STAT_T struct _stati64
# define STAT(X,Y) _wstati64((X),(Y))
# define FSTAT(X,Y) _fstati64((X),(Y)) # define FSTAT(X,Y) _fstati64((X),(Y))
# define FILENO(X) _fileno((X)) # define FILENO(X) _fileno((X))
# define S_IFMT _S_IFMT # define S_IFMT _S_IFMT
@ -39,9 +40,11 @@
# include <unistd.h> # include <unistd.h>
# if defined(__USE_LARGEFILE64) && (__USE_LARGEFILE64) # if defined(__USE_LARGEFILE64) && (__USE_LARGEFILE64)
# define STAT_T struct stat64 # define STAT_T struct stat64
# define STAT(X,Y) stat64((X),(Y))
# define FSTAT(X,Y) fstat64((X),(Y)) # define FSTAT(X,Y) fstat64((X),(Y))
# else # else
# define STAT_T struct stat # define STAT_T struct stat
# define STAT(X,Y) stat((X),(Y))
# define FSTAT(X,Y) fstat((X),(Y)) # define FSTAT(X,Y) fstat((X),(Y))
# endif # endif
# define FILENO(X) fileno((X)) # define FILENO(X) fileno((X))
@ -111,30 +114,6 @@ void setup_signal_handler(const int signo, signal_handler_t* const handler)
// String functions // String functions
// ========================================================================== // ==========================================================================
char *concat_str(const char *const str, ...)
{
const char *ptr;
char *buffer;
size_t buff_size = 1U;
va_list arg;
va_start(arg, str);
for (ptr = str; ptr != NULL; ptr = va_arg(arg, const char*))
{
buff_size += strlen(ptr);
}
if ((buffer = (char*)calloc(buff_size, sizeof(char))))
{
va_end(arg);
va_start(arg, str);
for (ptr = str; ptr != NULL; ptr = va_arg(arg, const char*))
{
strcat(buffer, ptr);
}
}
va_end(arg);
return buffer;
}
char* CHR_to_utf8(const CHR *const input) char* CHR_to_utf8(const CHR *const input)
{ {
#ifdef _WIN32 #ifdef _WIN32
@ -259,52 +238,59 @@ size_t fread_ui64(uint64_t *const value, FILE *const stream)
// File functions // File functions
// ========================================================================== // ==========================================================================
#define JOIN_PATHS(X,Y) concat_str((X), "/", (Y), NULL) typedef struct
{
uint64_t dev;
uint64_t ino;
}
file_uid_t;
CHR *absolute_path(const CHR *const path) static int get_file_unique_id(const CHR *const path, file_uid_t *const file_id)
{ {
int retval = -1;
#ifdef _WIN32 #ifdef _WIN32
wchar_t *const result = _wfullpath(NULL, path, 0U); const HANDLE handle = CreateFileW(path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0U, NULL);
if (handle != INVALID_HANDLE_VALUE)
{
BY_HANDLE_FILE_INFORMATION file_info;
if ((retval = (!GetFileInformationByHandle(handle, &file_info))) == 0)
{
file_id->dev = file_info.dwVolumeSerialNumber;
file_id->ino = (((uint64_t)file_info.nFileIndexHigh) << 32) | file_info.nFileIndexLow;
}
CloseHandle(handle);
}
#else #else
char *result = realpath(path, NULL); STAT_T file_info;
if ((!result) && path[0U] && (path[0U] != '/')) if ((retval = STAT(path, &file_info)) == 0)
{ {
char *const cwd = getcwd(NULL, 0U); file_id->dev = file_info.st_dev;
if (cwd) file_id->ino = file_info.st_ino;
{
const char *path_off = path;
while ((path_off[0U] == '.') && (path_off[1U] == '/'))
{
path_off += 2U;
while (path_off[0U] == '/') ++path_off;
}
result = JOIN_PATHS(cwd, path_off);
free(cwd);
}
} }
#endif #endif
return result ? result : STRDUP(path); return retval;
} }
int path_compare(const CHR *const path0, const CHR *const path1) int same_file(const CHR *const path0, const CHR *const path1)
{ {
#ifdef _WIN32 file_uid_t file_id0, file_id1;
return _wcsicmp(path0, path1); if ((get_file_unique_id(path0, &file_id0) == 0) && (get_file_unique_id(path1, &file_id1) == 0))
#else {
return strcmp(path0, path1); return (file_id0.dev == file_id1.dev) && (file_id0.ino == file_id1.ino);
#endif }
return -1;
} }
uint64_t get_file_size(FILE* const file) uint64_t get_size(FILE *const file)
{ {
STAT_T stat; STAT_T file_info;
if (FSTAT(FILENO(file), &stat) == 0) if (FSTAT(FILENO(file), &file_info) == 0)
{ {
const uint16_t file_type = stat.st_mode & S_IFMT; const unsigned ftype = file_info.st_mode & S_IFMT;
if ((file_type != S_IFDIR) && (file_type != S_IFIFO)) if ((ftype != S_IFDIR) && (ftype != S_IFIFO))
{ {
const int64_t ssize = stat.st_size; const int64_t size = file_info.st_size;
return (ssize >= 0) ? ((uint64_t)ssize) : 0U; return (size >= 0) ? ((uint64_t)size) : 0U;
} }
return 0U; return 0U;
} }

View File

@ -24,10 +24,9 @@ size_t fread_ui64(uint64_t *const value, FILE *const stream);
char* CHR_to_utf8(const CHR *const input); char* CHR_to_utf8(const CHR *const input);
CHR *absolute_path(const CHR *const path); int same_file(const CHR*const path0, const CHR*const path1);
int path_compare(const CHR *const path0, const CHR *const path1); uint64_t get_size(FILE *const file);
const CHR *get_file_name(const CHR *path); const CHR *get_file_name(const CHR *path);
uint64_t get_file_size(FILE* const file);
uint64_t round_down(const uint64_t value, const uint64_t base); uint64_t round_down(const uint64_t value, const uint64_t base);