diff --git a/src/fs.c b/src/fs.c index 8f55f81..a7f83c0 100644 --- a/src/fs.c +++ b/src/fs.c @@ -11,7 +11,9 @@ #include "fs.h" #include "includes.h" +#include "index.h" #include "objects.h" +#include "utils.h" int local_repo_exist() { @@ -19,30 +21,133 @@ int local_repo_exist() return stat(LOCAL_REPO, &buffer) == 0; } +int index_exist() +{ + struct stat buffer; + return stat(INDEX_FILE, &buffer) == 0; +} + +void dump_index(struct index *index) +{ + printf("index:\n"); + printf("\tentries_size: %ld\n", index->entries_size); + printf("\tentries:\n"); + struct entry *current = index->first_entry; + for(int i = 0; i < index->entries_size; i ++) + { + printf("\t\tfilename: %s, checksum: %s\n", current->filename, current->checksum); + current = current->next; + } +} + +int load_index(struct index *index) +{ + if(!local_repo_exist() || !index_exist()) + { + return REPO_NOT_INITIALIZED; + } + + FILE* index_file = fopen(INDEX_FILE, "r"); + struct stat buffer; + stat(INDEX_FILE, &buffer); + + char* file_content = calloc(sizeof(char), buffer.st_size + 1); + fread(file_content, buffer.st_size, 1, index_file); + fclose(index_file); + + char* current_line; + current_line = strtok(file_content, "\n"); + index->entries_size = strtol(current_line, NULL, 10); + + for(int i = 0; i < index->entries_size; i++) + { + current_line = strtok(NULL, "\n"); + int j = 0; + while(current_line[j] != ' ') + { + j++; + } + + char* checksum = calloc(sizeof(char), j + 1); + char* filename = calloc(sizeof(char), strlen(current_line) - j); + strncat(checksum, current_line, j); + strncat(filename, current_line + j + 1, strlen(current_line) - j); + + struct entry *entry = malloc(sizeof(struct entry)); + entry->checksum = checksum; + entry->filename = filename; + entry->next = NULL; + + if(i == 0) + { + index->first_entry = entry; + } else + { + entry->previous = index->last_entry; + index->last_entry->next = entry; + } + index->last_entry = entry; + debug_print("checksum: %s, filename: %s", entry->checksum, entry->filename); + } + + free(file_content); + + return FS_OK; +} + +int save_index(struct index *index) +{ + if(!local_repo_exist()) + { + return REPO_NOT_INITIALIZED; + } + + FILE *index_file = fopen(INDEX_FILE, "w"); + if(index_exist == NULL) + { + return FS_ERROR; + } + + fprintf(index_file, "%li\n", index->entries_size); + + struct entry *current = index->first_entry; + for(int i = 0; i < index->entries_size; i++) + { + fprintf(index_file, "%s %s\n", current->checksum, current->filename); + current = current->next; + } + + fclose(index_file); +} + int write_object(struct object *obj) { if(!local_repo_exist()) { - return -2; + return REPO_NOT_INITIALIZED; } - int result = 0; + int result = FS_OK; struct stat buffer; - if (stat(LOCAL_REPO"/objects", &buffer) != 0) + if (stat(OBJECTS_REPO, &buffer) != 0) { - mkdir(LOCAL_REPO"/objects", DEFAULT_DIR_MODE); + mkdir(OBJECTS_REPO, DEFAULT_DIR_MODE); } - DIR *objects_dir = opendir(LOCAL_REPO"/objects"); + DIR *objects_dir = opendir(OBJECTS_REPO); 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); + int save_file_fd = openat(objects_dir_fd, checksum, O_CREAT | O_WRONLY | O_TRUNC, DEFAULT_FILE_MODE); if(save_file_fd == -1) { - error_print("Object %s already exists", checksum); - defer(-1); + if (errno == EACCES) + { + debug_print("Object %s already exists", checksum); + defer(OBJECT_ALREADY_EXIST); + } + defer(FS_ERROR); } FILE* save_file = fdopen(save_file_fd, "w"); @@ -53,7 +158,7 @@ int write_object(struct object *obj) int res = compress_object(obj, compressed, &comp_size); if (res != Z_OK) { - defer(res); + defer(COMPRESSION_ERROR); } fwrite(compressed, comp_size, 1, save_file); free(compressed); @@ -68,25 +173,29 @@ int read_object(char *checksum, struct object *obj) { if(!local_repo_exist()) { - return -2; + return REPO_NOT_INITIALIZED; } - int result = 0; + int result = FS_OK; struct stat buffer; - if (stat(LOCAL_REPO"/objects", &buffer) != 0) + if (stat(OBJECTS_REPO, &buffer) != 0) { error_print("Object dir does not exist"); - return -1; + return OBJECT_DOES_NOT_EXIST; } - DIR *objects_dir = opendir(LOCAL_REPO"/objects"); + DIR *objects_dir = opendir(OBJECTS_REPO); 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); + if(errno == EACCES) + { + error_print("Object %s does not exist", checksum); + defer(OBJECT_DOES_NOT_EXIST); + } + defer(FS_ERROR); } fstatat(objects_dir_fd, checksum, &buffer, 0); char* file_content = malloc(buffer.st_size); @@ -96,9 +205,9 @@ int read_object(char *checksum, struct object *obj) fclose(save_file); int res = uncompress_object(obj, file_content, buffer.st_size); - if(res != 0) + if(res != Z_OK) { - defer(res); + defer(COMPRESSION_ERROR); } free(file_content); @@ -110,12 +219,15 @@ defer: int main(void) { - struct object obj = {0}; - obj.content = "Hello, world!\n"; - obj.size = strlen(obj.content); - obj.object_type = "blob"; + struct index index = {0}; + load_index(&index); - return write_object(&obj); + // 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); @@ -124,5 +236,14 @@ int main(void) // debug_print("Content is %.*s", (int)obj.size, obj.content); // free_object(&obj); - // return 0; + + add_to_index(&index, "src/fs.c"); + + dump_index(&index); + + save_index(&index); + + free_index(&index); + + return 0; } \ No newline at end of file diff --git a/src/fs.h b/src/fs.h index 249e271..d28898a 100644 --- a/src/fs.h +++ b/src/fs.h @@ -4,9 +4,23 @@ #include "objects.h" #define LOCAL_REPO ".cgit" +#define INDEX_FILE LOCAL_REPO"/index" +#define OBJECTS_REPO LOCAL_REPO"/objects" #define DEFAULT_DIR_MODE 0755 #define DEFAULT_FILE_MODE 0444 +#define FS_OK (0) +#define FS_ERROR (-1) +#define COMPRESSION_ERROR (-2) +#define REPO_NOT_INITIALIZED (-10) +#define OBJECT_ALREADY_EXIST (-20) +#define OBJECT_DOES_NOT_EXIST (-21) +#define FILE_NOT_FOUND (-22) +#define ENTRY_NOT_FOUND (-23) + +int local_repo_exist(); +int index_exist(); + int write_object(struct object *obj); int read_object(char *checksum, struct object *obj); diff --git a/src/index.c b/src/index.c new file mode 100644 index 0000000..42c266f --- /dev/null +++ b/src/index.c @@ -0,0 +1,133 @@ +#include +#include + +#include "index.h" +#include "includes.h" +#include "fs.h" + +void free_entry(struct entry *entry) +{ + if (entry->checksum != NULL) + free(entry->checksum); + + if (entry->filename != NULL) + free(entry->filename); + +} + +void free_index(struct index *index) +{ + struct entry *current = index->first_entry; + struct entry *next; + while(current != NULL) + { + free_entry(current); + next = current->next; + free(current); + current = next; + } +} + +struct entry *find_entry(struct index *index, char* filename) +{ + struct entry *current = index->first_entry; + for (int i = 0; i < index->entries_size; i ++) + { + if (strncmp(filename, current->filename, strlen(filename)) == 0) { + return current; + } + current = current->next; + } + + return NULL; +} + +int add_to_index(struct index *index, char *filename) +{ + if(!local_repo_exist() || !index_exist()) + { + return REPO_NOT_INITIALIZED; + } + + int result = FS_OK; + int new = 0; + + struct entry *entry = find_entry(index, filename); + if (entry == NULL) + { + new = 1; + entry = calloc(sizeof(struct entry), 1); + } else { + free_entry(entry); + } + + struct object object = {0}; + if (blob_from_file(filename, &object) != 0) + { + return FILE_NOT_FOUND; + } + + entry->filename = calloc(sizeof(char), strlen(filename) + 1); + strncat(entry->filename, filename, strlen(filename)); + entry->checksum = calloc(sizeof(char), DIGEST_LENGTH * 2 + 1); + hash_object(&object, entry->checksum); + int res = write_object(&object); + if (res != FS_OK && res != OBJECT_ALREADY_EXIST) + { + defer(FS_ERROR); + } + + if(new) + { + if(index->entries_size == 0) + { + index->first_entry = entry; + index->last_entry = entry; + } else + { + entry->previous = index->last_entry; + index->last_entry->next = entry; + index->last_entry = entry; + } + index->entries_size = index->entries_size + 1; + } + +defer: + free_object(&object); + return result; +} + +int remove_from_index(struct index *index, char *filename) +{ + if (!local_repo_exist() || !index_exist()) + { + return REPO_NOT_INITIALIZED; + } + + struct entry *entry = find_entry(index, filename); + if (entry == NULL) + { + return ENTRY_NOT_FOUND; + } + + if(index->first_entry == entry) + { + index->first_entry = entry->next; + } else + { + entry->previous->next = entry->next; + } + + if (index->last_entry == entry) + { + index->last_entry = entry->previous; + } else + { + entry->next->previous = entry->previous; + } + + index->entries_size = index->entries_size - 1; + free_entry(entry); + free(entry); + return FS_OK; +} \ No newline at end of file diff --git a/src/index.h b/src/index.h new file mode 100644 index 0000000..74f632e --- /dev/null +++ b/src/index.h @@ -0,0 +1,28 @@ +#ifndef INDEX_H +#define INDEX_H + +#include + +// Index file should follow the format +// number of entries\n +// entry1\n +// entry2\n +// ... +struct entry { + char *checksum; + char *filename; + struct entry *previous; + struct entry *next; +}; + +struct index { + size_t entries_size; + struct entry *first_entry; + struct entry *last_entry; +}; + +void free_index(struct index *index); +int add_to_index(struct index *index, char *filename); +int remove_from_index(struct index *index, char *filename); + +#endif // INDEX_H \ No newline at end of file diff --git a/src/objects.c b/src/objects.c index 3467525..afb35fe 100644 --- a/src/objects.c +++ b/src/objects.c @@ -1,22 +1,16 @@ #include #include #include +#include #include #include #include #include #include "includes.h" +#include "utils.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); @@ -51,7 +45,7 @@ int full_object(struct object *obj, char *buffer, size_t buffer_size) return 0; } -void hash_object(struct object *obj, char* result) +void hash_object(struct object *obj, char *result) { unsigned char md_buffer[DIGEST_LENGTH] = {0}; size_t data_size = object_size(obj); @@ -62,7 +56,7 @@ void hash_object(struct object *obj, char* result) for (int i = 0; i < DIGEST_LENGTH; i++) { - sprintf((result + 2 * i), "%.2x", md_buffer[i]); + sprintf((result + (2 * i)), "%.2x", md_buffer[i]); } free(data); @@ -113,11 +107,27 @@ int compress_object(struct object *obj, char* compressed, uLongf *comp_size) return res; } +int blob_from_file(char *filename, struct object *object) +{ + FILE* file = fopen(filename, "r"); + if (file == NULL) + return -1; + + struct stat file_info; + if (stat(filename, &file_info) != 0) + return -1; + + object->object_type = "blob"; + object->size = file_info.st_size; + object->content = realloc(object->content, object->size); + fread(object->content, 1, object->size, file); + fclose(file); + + return 0; +} + void free_object(struct object *obj) { - if (obj->object_type != NULL) - free(obj->object_type); - if (obj->content != NULL) free(obj->content); } diff --git a/src/objects.h b/src/objects.h index 65a48e6..5023e53 100644 --- a/src/objects.h +++ b/src/objects.h @@ -2,6 +2,7 @@ #define OBJECTS_H 1 #include +#include #define DIGEST_LENGTH 20 #define HEADER_MAX_SIZE 20 @@ -18,6 +19,7 @@ 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); +int blob_from_file(char *filename, struct object *object); void free_object(struct object *obj); #endif // OBJECTS_H diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..83b58af --- /dev/null +++ b/src/utils.c @@ -0,0 +1,11 @@ +#include + +#include "utils.h" + +int decimal_len(size_t size) +{ + if (size == 0) + return 1; + + return floor(log10(size)) + 1; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..e4ef1e2 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,8 @@ +#ifndef UTILS_H +#define UTILS_H 1 + +#include + +int decimal_len(size_t size); + +#endif // UTILS_H \ No newline at end of file