From 70644cf67ef721c8d1780ece5bc03e6068739cc5 Mon Sep 17 00:00:00 2001 From: womax Date: Tue, 28 May 2024 20:08:41 +0200 Subject: [PATCH] Basic object manipulation --- .gitignore | 5 ++ Makefile | 13 +++++ src/cgit.c | 12 +++++ src/fs.c | 128 ++++++++++++++++++++++++++++++++++++++++++++ src/fs.h | 11 ++++ src/includes.h | 24 +++++++++ src/objects.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++ src/objects.h | 23 ++++++++ 8 files changed, 357 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 src/cgit.c create mode 100644 src/fs.c create mode 100644 src/fs.h create mode 100644 src/includes.h create mode 100644 src/objects.c create mode 100644 src/objects.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66959b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +sample/ +.vscode/ +.cgit/ +build/ +*.out \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..41a4190 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +SRC := $(wildcard src/*.c) +CFLAGS := -lcrypto -lm -lz + +DEBUG = false +ifeq ($(DEBUG), true) + DEBUG_FLAG = -DDEBUG -ggdb +endif + +all: $(SRC) + gcc -o build/cgit $(SRC) $(CFLAGS) $(DEBUG_FLAG) + +run: all + build/cgit \ No newline at end of file diff --git a/src/cgit.c b/src/cgit.c new file mode 100644 index 0000000..6cd387d --- /dev/null +++ b/src/cgit.c @@ -0,0 +1,12 @@ +// Requirements +// commit +// branching +// revert +// pull/push + +#include + +int main(int argc, char** argv) { + printf("%s", "Hello World!\n"); + return 0; +} \ No newline at end of file diff --git a/src/fs.c b/src/fs.c new file mode 100644 index 0000000..e525ecf --- /dev/null +++ b/src/fs.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "includes.h" +#include "fs.h" +#include "objects.h" + +int local_repo_exist() +{ + struct stat buffer; + return stat(LOCAL_REPO, &buffer) == 0; +} + +int write_object(struct object *obj) +{ + if(!local_repo_exist()) + { + return -2; + } + int result = 0; + + struct stat buffer; + if (stat(LOCAL_REPO"/objects", &buffer) != 0) + { + mkdir(LOCAL_REPO"/objects", DEFAULT_DIR_MODE); + } + + DIR *objects_dir = opendir(LOCAL_REPO"/objects"); + int objects_dir_fd = dirfd(objects_dir); + + char checksum[DIGEST_LENGTH * 2]; + hash_object(obj, checksum); + + int save_file_fd = openat(objects_dir_fd, checksum, O_CREAT | O_WRONLY | O_TRUNC, 0774); + if(save_file_fd == -1) { + error_print("Object %s already exists", checksum); + defer(-1); + } + FILE* save_file = fdopen(save_file_fd, "w"); + + size_t data_size = object_size(obj); + uLong comp_size = compressBound(data_size); + char *compressed = malloc(comp_size); + + int res = compress_object(obj, compressed, &comp_size); + if (res != Z_OK) + { + defer(res); + } + fwrite(compressed, comp_size, 1, save_file); + free(compressed); + fclose(save_file); + +defer: + closedir(objects_dir); + return result; +} + +int read_object(char *checksum, struct object *obj) +{ + if(!local_repo_exist()) + { + return -2; + } + int result = 0; + + struct stat buffer; + if (stat(LOCAL_REPO"/objects", &buffer) != 0) + { + error_print("Object dir does not exist"); + return -1; + } + + DIR *objects_dir = opendir(LOCAL_REPO"/objects"); + int objects_dir_fd = dirfd(objects_dir); + + int save_file_fd = openat(objects_dir_fd, checksum, O_RDONLY, DEFAULT_FILE_MODE); + if (save_file_fd == -1) + { + error_print("Object %s does not exist", checksum); + defer(-1); + } + fstatat(objects_dir_fd, checksum, &buffer, 0); + char* file_content = malloc(buffer.st_size); + + FILE *save_file = fdopen(save_file_fd, "r"); + fread(file_content, 1, buffer.st_size, save_file); + fclose(save_file); + + int res = uncompress_object(obj, file_content, buffer.st_size); + if(res != 0) + { + defer(res); + } + + free(file_content); + +defer: + closedir(objects_dir); + return result; +} + +int main(void) +{ + struct object obj = {0}; + obj.content = "Hello, world!\n"; + obj.size = strlen(obj.content); + obj.object_type = "blob"; + + return write_object(&obj); + + // read_object("af5626b4a114abcb82d63db7c8082c3c4756e51b", &obj); + + // debug_print("Object type is \"%s\"", obj.object_type); + // debug_print("Content size is %li", obj.size); + // debug_print("Content is %.*s", (int)obj.size, obj.content); + + // free_object(&obj); + // return 0; +} \ No newline at end of file diff --git a/src/fs.h b/src/fs.h new file mode 100644 index 0000000..5bd136f --- /dev/null +++ b/src/fs.h @@ -0,0 +1,11 @@ +#ifndef FS_H +#define FS_H 1 + +#define LOCAL_REPO ".cgit" +#define DEFAULT_DIR_MODE 0755 +#define DEFAULT_FILE_MODE 0444 + +int write_object(struct object *obj); +int read_object(char *checksum, struct object *obj); + +#endif // FS_H diff --git a/src/includes.h b/src/includes.h new file mode 100644 index 0000000..c5eb143 --- /dev/null +++ b/src/includes.h @@ -0,0 +1,24 @@ +#ifndef INCLUDES_H +#define INCLUDES_H + +#ifdef DEBUG +#define debug_print(X, ...) \ + printf("[DEBUG] %s:%d: ", __FILE__, __LINE__); \ + printf(X __VA_OPT__(,) __VA_ARGS__); \ + printf("\n"); \ + +#define error_print(X, ...) \ + printf("[ERROR] %s:%d: ", __FILE__, __LINE__); \ + printf(X __VA_OPT__(,) __VA_ARGS__); \ + printf("\n"); \ + +#else +#define debug_print(X, ...) +#define error_print(X, ...) +#endif + +#define defer(X) \ + result = X; \ + goto defer; \ + +#endif // INCLUDES_H \ No newline at end of file diff --git a/src/objects.c b/src/objects.c new file mode 100644 index 0000000..42f8e1a --- /dev/null +++ b/src/objects.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "includes.h" +#include "objects.h" + +int decimal_len(size_t size) +{ + if (size == 0) + return 1; + + return floor(log10(size)) + 1; +} + +size_t header_size(struct object *obj) +{ + return strlen(obj->object_type) + 2 + decimal_len(obj->size); +} + +size_t object_size(struct object *obj) +{ + return header_size(obj) + obj->size; +} + +int full_object(struct object *obj, char *buffer, size_t buffer_size) +{ + char* hr_size = malloc(decimal_len(obj->size) + 1); + sprintf(hr_size, "%li", obj->size); + + size_t data_size = object_size(obj); + assert(data_size <= buffer_size); + + char *writing = buffer; + memcpy(writing, obj->object_type, strlen(obj->object_type)); + writing += strlen(obj->object_type); + memcpy(writing, " ", 1); + writing ++; + memcpy(writing, hr_size, strlen(hr_size)); + writing += strlen(hr_size); + memcpy(writing, "\0", 1); + writing ++; + memcpy(writing, obj->content, obj->size); + + free(hr_size); + + return 0; +} + +void hash_object(struct object *obj, char* result) +{ + unsigned char md_buffer[DIGEST_LENGTH] = {0}; + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + + char* hr_size = malloc(decimal_len(obj->size) + 1); + sprintf(hr_size, "%li", obj->size); + + EVP_DigestInit(ctx, EVP_sha1()); + EVP_DigestUpdate(ctx, obj->object_type, strlen(obj->object_type)); + EVP_DigestUpdate(ctx, " ", 1); + EVP_DigestUpdate(ctx, hr_size, decimal_len(obj->size)); + EVP_DigestUpdate(ctx, "\0", 1); + EVP_DigestUpdate(ctx, obj->content, obj->size); + + EVP_DigestFinal(ctx, md_buffer, NULL); + free(hr_size); + + for (int i = 0; i < DIGEST_LENGTH; i++) + { + sprintf((result + 2 * i), "%.2x", md_buffer[i]); + } + + EVP_MD_CTX_free(ctx); +} + +int uncompress_object(struct object *obj, char* compressed, uLongf comp_size) +{ + uLongf def_size = HEADER_MAX_SIZE; + uLongf content_size = 0; + char* deflated = malloc(def_size); + int res = uncompress((Bytef *) deflated, &def_size, (Bytef *)compressed, comp_size); + if (res != Z_OK && res != Z_BUF_ERROR) + { + return res; + } + int header_size = strlen(deflated) + 1; + def_size = header_size; + strtok(deflated, " "); + char *hr_size = strtok(NULL, " "); + content_size = strtol(hr_size, NULL, 10); + def_size += content_size; + deflated = realloc(deflated, def_size); + res = uncompress((Bytef *) deflated, &def_size, (Bytef *)compressed, comp_size); + if(res != Z_OK) { + return res; + } + char *content_type_tmp = strtok(deflated, " "); + + obj->size = content_size; + obj->object_type = malloc(strlen(content_type_tmp) + 1); + strncpy(obj->object_type, content_type_tmp, strlen(content_type_tmp) + 1); + obj->content = malloc(obj->size); + memcpy(obj->content, deflated + header_size, obj->size); + free(deflated); + + return 0; +} + +int compress_object(struct object *obj, char* compressed, uLongf *comp_size) +{ + size_t data_size = object_size(obj); + char* data = malloc(data_size); + full_object(obj, data, data_size); + + int res = compress((Bytef *)compressed, comp_size, (Bytef *)data, data_size); + free(data); + + return res; +} + +void free_object(struct object *obj) +{ + if (obj->object_type != NULL) + free(obj->object_type); + + if (obj->content != NULL) + free(obj->content); +} + +// int main(void) +// { +// char result[SHA_DIGEST_LENGTH * 2] = {0}; + +// char* str = "Hello, world!\n"; +// hash_object(str, strlen(str), "blob", result); +// printf("%s\n", result); +// } \ No newline at end of file diff --git a/src/objects.h b/src/objects.h new file mode 100644 index 0000000..65a48e6 --- /dev/null +++ b/src/objects.h @@ -0,0 +1,23 @@ +#ifndef OBJECTS_H +#define OBJECTS_H 1 + +#include + +#define DIGEST_LENGTH 20 +#define HEADER_MAX_SIZE 20 + +struct object +{ + char* content; + size_t size; + char* object_type; +}; + +size_t object_size(struct object *obj); +int full_object(struct object *obj, char* buffer, size_t buffer_size); +int uncompress_object(struct object *obj, char* compressed, uLongf comp_size); +int compress_object(struct object *obj, char* compressed, uLongf *comp_size); +void hash_object(struct object *obj, char* result); +void free_object(struct object *obj); + +#endif // OBJECTS_H