From 76dff7dafb18a58238f5123161870064f0d7a930 Mon Sep 17 00:00:00 2001 From: Alexander Zhylin Date: Mon, 27 Oct 2025 17:31:41 +0200 Subject: [PATCH] File hashing (not finished) --- setup.sh | 2 - src/db.c | 69 ++------ src/db.h | 2 +- src/deps.mk | 8 - src/file_p.c | 77 +++++++-- src/libs/murmur3/murmur3.c | 314 +++++++++++++++++++++++++++++++++++++ src/libs/murmur3/murmur3.h | 29 ++++ src/main.c | 2 +- src/main.h | 4 + 9 files changed, 421 insertions(+), 86 deletions(-) create mode 100644 src/libs/murmur3/murmur3.c create mode 100644 src/libs/murmur3/murmur3.h diff --git a/setup.sh b/setup.sh index a91def6..985f0f6 100755 --- a/setup.sh +++ b/setup.sh @@ -5,10 +5,8 @@ mkdir wdir mkdir wdir/storage touch wdir/file_descriptions touch wdir/welcome -touch wdir/userlist echo "Welcome to the BBS Setup!" >> wdir/welcome -echo "user 1234 15" >> wdir/userlist # Test file touch wdir/storage/testfile.txt diff --git a/src/db.c b/src/db.c index 5a15faa..75d90ac 100644 --- a/src/db.c +++ b/src/db.c @@ -45,7 +45,7 @@ static void processNotice(void *arg, const char *message) { // do nothing } -int init_connection() { +int init_db_connection() { // TODO: get from external configs conn = PQconnectdb("user=postgres password=dobro host=127.0.0.1 dbname=bbs"); @@ -56,20 +56,20 @@ int init_connection() { char *user = PQuser(conn); char *db_name = PQdb(conn); - printf("Connection established! server version: %d, user: %s, db: %s\n", server_ver, - user, db_name); + printf("Connection established! server version: %d, user: %s, db: %s\n", + server_ver, user, db_name); return exit_query(0); } int db_user_auth(i_auth_t *credentials, o_auth_t *response) { - char query[Q_LEN]; - response->is_logged = 0; - sprintf(query, - "SELECT username, password, privileges " - "FROM users " - "WHERE username= '%s'", - credentials->name); - res = PQexec(conn, query); + const char *paramValues[1]; + paramValues[0] = credentials->name; + + res = PQexecParams(conn, + "SELECT username, password, privileges " + "FROM users " + "WHERE username= $1", 1, + NULL, paramValues, NULL, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res)) return exit_query(1); @@ -81,49 +81,4 @@ int db_user_auth(i_auth_t *credentials, o_auth_t *response) { return exit_query(0); } return exit_query(2); -} - -/*s - -int f_main() { - int libpq_ver = PQlibVersion(); - printf("Version of libpq: %d\n", libpq_ver); - - conn = PQconnectdb("user=postgres password=dobro host=127.0.0.1 dbname=bbs"); - if (PQstatus(conn) != CONNECTION_OK) - terminate(1); - - PQsetNoticeProcessor(conn, processNotice, NULL); - - int server_ver = PQserverVersion(conn); - char *user = PQuser(conn); - char *db_name = PQdb(conn); - - printf("Server version: %d\n", server_ver); - printf("User: %s\n", user); - printf("Database name: %s\n", db_name); - - res = PQexec(conn, "SELECT id, username, privileges, email " - "FROM users"); - if (PQresultStatus(res) != PGRES_TUPLES_OK) - terminate(1); - - int nrows = PQntuples(res); - for (int i = 0; i < nrows; i++) { - char *id = PQgetvalue(res, i, 0); - char *name = PQgetvalue(res, i, 1); - char *privileges = PQgetvalue(res, i, 2); - char *email = PQgetvalue(res, i, 3); - printf("Id: %s, Name: %s, Privileges: %d, Email: %s, \n", id, name, - privileges[0], email); - } - - printf("Total: %d rows\n", nrows); - - clearRes(); - terminate(0); - - return 0; -} - -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/src/db.h b/src/db.h index 08e48c7..cf27150 100644 --- a/src/db.h +++ b/src/db.h @@ -8,5 +8,5 @@ typedef struct { int is_logged; } o_auth_t; -int init_connection(); +int init_db_connection(); int db_user_auth(i_auth_t *credentials, o_auth_t *response); diff --git a/src/deps.mk b/src/deps.mk index bacbafa..e69de29 100644 --- a/src/deps.mk +++ b/src/deps.mk @@ -1,8 +0,0 @@ -client.o: src/client.c src/client.h src/main.h src/file_p.h src/session.h -db.o: src/db.c src/db.h -file_p.o: src/file_p.c src/file_p.h src/main.h src/session.h -main.o: src/main.c src/main.h src/file_p.h src/server.h src/db.h -server.o: src/server.c src/main.h src/session.h src/user.h -session.o: src/session.c src/session.h src/main.h src/client.h \ - src/file_p.h src/user.h -user.o: src/user.c src/main.h src/session.h src/db.h diff --git a/src/file_p.c b/src/file_p.c index 0d57d32..9c3d63d 100644 --- a/src/file_p.c +++ b/src/file_p.c @@ -1,4 +1,5 @@ #include "file_p.h" +#include "libs/murmur3/murmur3.h" #include "main.h" #include "session.h" #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -44,7 +46,7 @@ void file_list(session *sess, server_data_t *s_d) { full_str[i] = '\a'; } } - write(sess->sd, full_str, f_len-1); // -1 : do not include \0 + write(sess->sd, full_str, f_len - 1); // -1 : do not include \0 free(full_str); } while ((current = current->next) != NULL); write(sess->sd, list_end, sizeof(list_end)); @@ -84,6 +86,25 @@ int file_send_prepare(session *sess, char *line, server_data_t *s_d) { return 0; } +int directory_exists(const char *path) { + struct stat info; + + if (stat(path, &info) != 0) { + // stat() failed, probably doesn't exist + return 0; + } + return S_ISDIR(info.st_mode); // true if it's a directory +} + +void extract_names_from_hash(uint32_t file_hash, char *dirname, char *fname) { + char hashed_full_name[9]; + sprintf(hashed_full_name, "%08x", file_hash); + strncpy(dirname, hashed_full_name, 2); + strncpy(fname, hashed_full_name + 2, 6); + dirname[2] = 0; + fname[6] = 0; +} + int file_receive_prepare(session *sess, char *line, server_data_t *s_d) { int sd = sess->sd; char fname[64]; @@ -93,8 +114,6 @@ int file_receive_prepare(session *sess, char *line, server_data_t *s_d) { sess->fname = malloc(sizeof(char) * strlen(fname)); sess->f_perm = (char)perm; strncpy(sess->fname, fname, strlen(fname) - 1); // remove the last \" - sess->fpath = malloc(strlen(sess->fname) + sizeof(STORAGE_FOLDER) + 2); - sprintf(sess->fpath, "%s/%s", STORAGE_FOLDER, sess->fname); char mes[256]; char mes_len = 0; @@ -114,19 +133,43 @@ int file_receive_prepare(session *sess, char *line, server_data_t *s_d) { clear_file_from_sess(sess); return -1; } - int file_d = open(sess->fpath, O_WRONLY | O_CREAT | O_EXCL, 0666); - if (file_d == -1) { - if (errno == EEXIST) { - mes_len = - sprintf(mes, "File with name \"%s\" is already exist\n", sess->fname); + + char hashed_dir_name[3]; + char hashed_name[7]; + int seed = FILE_HASH_SEED; + int file_d; + + for (;;) { + MurmurHash3_x86_32(sess->fname, strlen(sess->fname), FILE_HASH_SEED, + &(sess->hash)); + + extract_names_from_hash(sess->hash, hashed_dir_name, hashed_name); + + sess->fpath = malloc(sizeof(STORAGE_FOLDER) + 2 + 9); + sprintf(sess->fpath, "%s/%s/%s", STORAGE_FOLDER, hashed_dir_name, + hashed_name); + + if (!directory_exists(hashed_dir_name)) { + mkdir(hashed_dir_name, 0700); + } + chdir(hashed_dir_name); + + file_d = open(sess->fpath, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (file_d == -1) { + if (errno == EEXIST) { + seed++; + free(sess->fpath); + sess->fpath = NULL; + continue; + } + mes_len = sprintf(mes, "Can't create file with such name: \"%s\"\n", + sess->fname); write(sd, mes, mes_len); + clear_file_from_sess(sess); + return -1; } - mes_len = - sprintf(mes, "Can't create file with such name: \"%s\"\n", sess->fname); - write(sd, mes, mes_len); - clear_file_from_sess(sess); - return -1; } + mes_len = sprintf(mes, "accept"); sess->fd = file_d; sess->fsize = fsize; @@ -257,19 +300,19 @@ void get_files_descriptions(server_data_t *s_d) { /* file name */ sscanf(buf_pos, "%s\n", tmp); - buf_pos += strlen(tmp)+1; + buf_pos += strlen(tmp) + 1; fl_item->name = malloc(sizeof(char) * (strlen(tmp) + 1)); - strcpy(fl_item->name,tmp); + strcpy(fl_item->name, tmp); /* file owner */ sscanf(buf_pos, "%s\n", tmp); - buf_pos += strlen(tmp)+1; + buf_pos += strlen(tmp) + 1; fl_item->owner = malloc(sizeof(char) * (strlen(tmp) + 1)); strcpy(fl_item->owner, tmp); /* file permissions */ sscanf(buf_pos, "%s\n", tmp); - buf_pos += strlen(tmp)+1; + buf_pos += strlen(tmp) + 1; fl_item->permissions = atoi(tmp); /* file size */ diff --git a/src/libs/murmur3/murmur3.c b/src/libs/murmur3/murmur3.c new file mode 100644 index 0000000..fe03d9c --- /dev/null +++ b/src/libs/murmur3/murmur3.c @@ -0,0 +1,314 @@ +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +// Note - The x86 and x64 versions do _not_ produce the same results, as the +// algorithms are optimized for their respective platforms. You can still +// compile and run any of them on any platform, but your performance with the +// non-native version will be less than optimal. + +#include "murmur3.h" + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +#ifdef __GNUC__ +#define FORCE_INLINE __attribute__((always_inline)) inline +#else +#define FORCE_INLINE inline +#endif + +static FORCE_INLINE uint32_t rotl32 ( uint32_t x, int8_t r ) +{ + return (x << r) | (x >> (32 - r)); +} + +static FORCE_INLINE uint64_t rotl64 ( uint64_t x, int8_t r ) +{ + return (x << r) | (x >> (64 - r)); +} + +#define ROTL32(x,y) rotl32(x,y) +#define ROTL64(x,y) rotl64(x,y) + +#define BIG_CONSTANT(x) (x##LLU) + +//----------------------------------------------------------------------------- +// Block read - if your platform needs to do endian-swapping or can only +// handle aligned reads, do the conversion here + +#define getblock(p, i) (p[i]) + +//----------------------------------------------------------------------------- +// Finalization mix - force all bits of a hash block to avalanche + +static FORCE_INLINE uint32_t fmix32 ( uint32_t h ) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +//---------- + +static FORCE_INLINE uint64_t fmix64 ( uint64_t k ) +{ + k ^= k >> 33; + k *= BIG_CONSTANT(0xff51afd7ed558ccd); + k ^= k >> 33; + k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); + k ^= k >> 33; + + return k; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_32 ( const void * key, int len, + uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 4; + int i; + + uint32_t h1 = seed; + + uint32_t c1 = 0xcc9e2d51; + uint32_t c2 = 0x1b873593; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); + + for(i = -nblocks; i; i++) + { + uint32_t k1 = getblock(blocks,i); + + k1 *= c1; + k1 = ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*4); + + uint32_t k1 = 0; + + switch(len & 3) + { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; + + h1 = fmix32(h1); + + *(uint32_t*)out = h1; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_128 ( const void * key, const int len, + uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + int i; + + uint32_t h1 = seed; + uint32_t h2 = seed; + uint32_t h3 = seed; + uint32_t h4 = seed; + + uint32_t c1 = 0x239b961b; + uint32_t c2 = 0xab0e9789; + uint32_t c3 = 0x38b34ae5; + uint32_t c4 = 0xa1e38b93; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*16); + + for(i = -nblocks; i; i++) + { + uint32_t k1 = getblock(blocks,i*4+0); + uint32_t k2 = getblock(blocks,i*4+1); + uint32_t k3 = getblock(blocks,i*4+2); + uint32_t k4 = getblock(blocks,i*4+3); + + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + + h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; + + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + + h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; + + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + + h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; + + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + + h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + + uint32_t k1 = 0; + uint32_t k2 = 0; + uint32_t k3 = 0; + uint32_t k4 = 0; + + switch(len & 15) + { + case 15: k4 ^= tail[14] << 16; + case 14: k4 ^= tail[13] << 8; + case 13: k4 ^= tail[12] << 0; + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + + case 12: k3 ^= tail[11] << 24; + case 11: k3 ^= tail[10] << 16; + case 10: k3 ^= tail[ 9] << 8; + case 9: k3 ^= tail[ 8] << 0; + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + + case 8: k2 ^= tail[ 7] << 24; + case 7: k2 ^= tail[ 6] << 16; + case 6: k2 ^= tail[ 5] << 8; + case 5: k2 ^= tail[ 4] << 0; + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + + case 4: k1 ^= tail[ 3] << 24; + case 3: k1 ^= tail[ 2] << 16; + case 2: k1 ^= tail[ 1] << 8; + case 1: k1 ^= tail[ 0] << 0; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; + + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + + h1 = fmix32(h1); + h2 = fmix32(h2); + h3 = fmix32(h3); + h4 = fmix32(h4); + + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + + ((uint32_t*)out)[0] = h1; + ((uint32_t*)out)[1] = h2; + ((uint32_t*)out)[2] = h3; + ((uint32_t*)out)[3] = h4; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x64_128 ( const void * key, const int len, + const uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + int i; + + uint64_t h1 = seed; + uint64_t h2 = seed; + + uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); + uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); + + //---------- + // body + + const uint64_t * blocks = (const uint64_t *)(data); + + for(i = 0; i < nblocks; i++) + { + uint64_t k1 = getblock(blocks,i*2+0); + uint64_t k2 = getblock(blocks,i*2+1); + + k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; + + h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; + + k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; + + h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + + uint64_t k1 = 0; + uint64_t k2 = 0; + + switch(len & 15) + { + case 15: k2 ^= (uint64_t)(tail[14]) << 48; + case 14: k2 ^= (uint64_t)(tail[13]) << 40; + case 13: k2 ^= (uint64_t)(tail[12]) << 32; + case 12: k2 ^= (uint64_t)(tail[11]) << 24; + case 11: k2 ^= (uint64_t)(tail[10]) << 16; + case 10: k2 ^= (uint64_t)(tail[ 9]) << 8; + case 9: k2 ^= (uint64_t)(tail[ 8]) << 0; + k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; + + case 8: k1 ^= (uint64_t)(tail[ 7]) << 56; + case 7: k1 ^= (uint64_t)(tail[ 6]) << 48; + case 6: k1 ^= (uint64_t)(tail[ 5]) << 40; + case 5: k1 ^= (uint64_t)(tail[ 4]) << 32; + case 4: k1 ^= (uint64_t)(tail[ 3]) << 24; + case 3: k1 ^= (uint64_t)(tail[ 2]) << 16; + case 2: k1 ^= (uint64_t)(tail[ 1]) << 8; + case 1: k1 ^= (uint64_t)(tail[ 0]) << 0; + k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; h2 ^= len; + + h1 += h2; + h2 += h1; + + h1 = fmix64(h1); + h2 = fmix64(h2); + + h1 += h2; + h2 += h1; + + ((uint64_t*)out)[0] = h1; + ((uint64_t*)out)[1] = h2; +} + +//----------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/libs/murmur3/murmur3.h b/src/libs/murmur3/murmur3.h new file mode 100644 index 0000000..c85395a --- /dev/null +++ b/src/libs/murmur3/murmur3.h @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the +// public domain. The author hereby disclaims copyright to this source +// code. + +#ifndef _MURMURHASH3_H_ +#define _MURMURHASH3_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_32 (const void *key, int len, uint32_t seed, void *out); + +void MurmurHash3_x86_128(const void *key, int len, uint32_t seed, void *out); + +void MurmurHash3_x64_128(const void *key, int len, uint32_t seed, void *out); + +//----------------------------------------------------------------------------- + +#ifdef __cplusplus +} +#endif + +#endif // _MURMURHASH3_H_ \ No newline at end of file diff --git a/src/main.c b/src/main.c index ac92581..2ebf5a9 100644 --- a/src/main.c +++ b/src/main.c @@ -10,7 +10,7 @@ int main(int argc, char *argv[]) { server_data.welcome_message = get_welcome_mes(); server_data.fl_start = NULL; server_data.fl_current = NULL; - init_connection(); + init_db_connection(); get_files_descriptions(&server_data); server_data.ls = start_server(); server_main_loop(&server_data); diff --git a/src/main.h b/src/main.h index c755672..20c71d9 100644 --- a/src/main.h +++ b/src/main.h @@ -1,6 +1,7 @@ #ifndef MAIN_H #define MAIN_H #include +#include #define PORT 2000 #define MAX_CONNECTIONS 1024 #define INBUFSIZE 1024 @@ -56,6 +57,7 @@ typedef struct session { char *fpath; char f_perm; char *fdesc; + uint32_t hash; size_t fsize; size_t f_rest; long int userpos; /* hint to the position in user list */ @@ -77,4 +79,6 @@ typedef struct server_data_t { int ls; } server_data_t; +#define FILE_HASH_SEED 898 + #endif \ No newline at end of file