#include #include #include #include #include #include #include #include #include #include #include "fs.h" #include "includes.h" #include "tree.h" #include "objects.h" #include "utils.h" #include "commit.h" int local_repo_exist() { struct stat buffer; return stat(LOCAL_REPO, &buffer) == 0; } int index_exist() { struct stat buffer; return stat(INDEX_FILE, &buffer) == 0; } int heads_dir_exist() { struct stat buffer; return stat(HEADS_DIR, &buffer) == 0; } int head_file_exist(size_t *head_size) { struct stat buffer; int result = stat(HEAD_FILE, &buffer) == 0; if(head_size != NULL) *head_size = buffer.st_size; return result; } int init_repo() { if(local_repo_exist()) { return 0; } mkdir(LOCAL_REPO, DEFAULT_DIR_MODE); struct stat buffer = {0}; if (stat(OBJECTS_DIR, &buffer) != 0) mkdir(OBJECTS_DIR, DEFAULT_DIR_MODE); if (!index_exist()) { int fd = open(INDEX_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644); FILE *index = fdopen(fd, "w"); fprintf(index, "0\n"); fclose(index); } if (!head_file_exist(NULL)) { open(HEAD_FILE, O_CREAT, 0644); } if (stat(REFS_DIR, &buffer) != 0) { mkdir(REFS_DIR, DEFAULT_DIR_MODE); } if (!heads_dir_exist()) { mkdir(HEADS_DIR, DEFAULT_DIR_MODE); } } int blob_from_file(char *filename, struct object *object) { FILE* file = fopen(filename, "r"); if (file == NULL) { error_print("File %s not found", filename); return FILE_NOT_FOUND; } struct stat file_info; if (stat(filename, &file_info) != 0) return -1; object->object_type = str_to_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 FS_OK; } int write_object(struct object *obj) { if(!local_repo_exist()) { return REPO_NOT_INITIALIZED; } int result = FS_OK; struct stat buffer; if (stat(OBJECTS_DIR, &buffer) != 0) { mkdir(OBJECTS_DIR, DEFAULT_DIR_MODE); } DIR *objects_dir = opendir(OBJECTS_DIR); 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, DEFAULT_FILE_MODE); if(save_file_fd == -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"); 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(COMPRESSION_ERROR); } 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 REPO_NOT_INITIALIZED; } int result = FS_OK; struct stat buffer; if (stat(OBJECTS_DIR, &buffer) != 0) { error_print("Object dir does not exist"); return OBJECT_DOES_NOT_EXIST; } DIR *objects_dir = opendir(OBJECTS_DIR); int objects_dir_fd = dirfd(objects_dir); fstatat(objects_dir_fd, checksum, &buffer, 0); char file_content[buffer.st_size]; int save_file_fd = openat(objects_dir_fd, checksum, O_RDONLY, DEFAULT_FILE_MODE); if (save_file_fd == -1) { if(errno == EACCES) { error_print("Object %s does not exist", checksum); defer(OBJECT_DOES_NOT_EXIST); } error_print("Cannot open file %s", checksum); defer(FS_ERROR); } FILE *save_file = fdopen(save_file_fd, "r"); fread(file_content, 1, buffer.st_size, save_file); fclose(save_file); result = uncompress_object(obj, file_content, buffer.st_size); defer: closedir(objects_dir); return result; } int remove_object(char *checksum) { if(!local_repo_exist()) { return REPO_NOT_INITIALIZED; } int result = FS_OK; char path[strlen(OBJECTS_DIR) + strlen(checksum) + 2]; sprintf(path, "%s/%s", OBJECTS_DIR, checksum); remove(path); } int create_dir(char *dir) { struct stat buffer = {0}; int result = FS_OK; if(stat(dir, &buffer) == 0) { if (!S_ISREG(buffer.st_mode)) { result = remove(dir); if (result != 0) return FS_ERROR; } } else { result = mkdir(dir, DEFAULT_DIR_MODE); if (result != 0) return FS_ERROR; } } void remove_dir(char *dir) { char cmd[strlen(dir) + strlen("rm -rf ") + 1]; sprintf(cmd, "rm -rf %s", dir); FILE *p = popen(cmd, "r"); pclose(p); } int init_tmp_diff_dir(char* dir) { int dirlen = 0; if (*dir != '\0') dirlen = strlen(dir); char path_a[dirlen + 8], path_b[dirlen + 8]; sprintf(path_a, "/tmp/a/%s", dir); sprintf(path_b, "/tmp/b/%s", dir); if (create_dir(path_a) != FS_OK) return FS_ERROR; if (create_dir(path_b) != FS_OK) return FS_ERROR; return FS_OK; } int tmp_dump(struct object *obj, char* filename) { if (obj == NULL) { open(filename, O_CREAT, 0644); return FS_OK; } FILE *file = fopen(filename, "w"); fwrite(obj->content, obj->size, 1, file); fclose(file); return FS_OK; } int dump_tree(char *cwd, struct tree *tree) { struct entry *current = tree->first_entry; while(current != NULL) { size_t cwd_len = 0; if (*cwd != '\0') cwd_len = strlen(cwd); size_t filename_size = cwd_len + 2 + strlen(current->filename); char filename[filename_size]; sprintf(filename, "%s/%s", cwd, current->filename); struct object obj = {0}; read_object(current->checksum, &obj); if(current->type == BLOB) { tmp_dump(&obj, filename); } else if (current->type == TREE) { struct tree subtree = {0}; get_tree(obj.content, &subtree); create_dir(filename); dump_tree(filename, &subtree); free_tree(&subtree); } free_object(&obj); current = current->next; } return FS_OK; } int load_tree(char* checksum, struct tree *tree) { struct object object; int res = read_object(checksum, &object); if (res != FS_OK) return res; if (object.object_type != TREE) { error_print("Object %s is not a tree", checksum); return WRONG_OBJECT_TYPE; } get_tree(object.content, tree); free_object(&object); return 0; } int load_index(struct tree *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(buffer.st_size + 1, sizeof(char)); fread(file_content, buffer.st_size, 1, index_file); fclose(index_file); get_tree(file_content, index); free(file_content); return FS_OK; } int save_index(struct tree *tree) { if(!local_repo_exist()) { return REPO_NOT_INITIALIZED; } FILE *index_file = fopen(INDEX_FILE, "w"); if(index_exist == NULL) { return FS_ERROR; } struct object object; tree_to_object(tree, &object); fwrite(object.content, object.size, 1, index_file); fclose(index_file); free_object(&object); } int get_head_commit_checksum(char* checksum) { size_t head_size = 0; if(!local_repo_exist() || !head_file_exist(&head_size) || !heads_dir_exist) { return REPO_NOT_INITIALIZED; } if(head_size == 0) return NO_CURRENT_HEAD; FILE *head_file = NULL; head_file = fopen(HEAD_FILE, "r"); char head_path[head_size + 1]; memset(head_path, 0, head_size + 1); fread(head_path, head_size, 1, head_file); fclose(head_file); struct stat buffer = {0}; if (stat(head_path, &buffer) != 0) return NO_CURRENT_HEAD; memset(checksum, 0, buffer.st_size + 1); head_file = fopen(head_path, "r"); fread(checksum, buffer.st_size, 1, head_file); fclose(head_file); return 0; } int get_last_commit(struct object *commit) { char commit_checksum[DIGEST_LENGTH * 2 + 1]; if (get_head_commit_checksum(commit_checksum) == NO_CURRENT_HEAD) return FS_OK; int res = read_object(commit_checksum, commit); if (res != 0) return FS_ERROR; if (commit->object_type != COMMIT) return WRONG_OBJECT_TYPE; return FS_OK; } int update_current_branch_head(char *new_head) { size_t head_size = 0; if(!local_repo_exist() || !head_file_exist(&head_size) || !heads_dir_exist) { return REPO_NOT_INITIALIZED; } FILE *file; if(head_size != 0) { FILE *head_file = fopen(HEAD_FILE, "r"); char branch[head_size]; fread(branch, head_size, 1, head_file); fclose(head_file); file = fopen(branch, "w"); } else { FILE *head_file = fopen(HEAD_FILE, "w"); fprintf(head_file, "%s/master", HEADS_DIR); fclose(head_file); file = fopen(HEADS_DIR"/master", "w"); } fwrite(new_head, strlen(new_head), 1, file); fclose(file); return 0; } int branch_exist(char *branch) { if(!local_repo_exist() || !heads_dir_exist()) return REPO_NOT_INITIALIZED; DIR *dir = opendir(HEADS_DIR); struct dirent *ent; if (dir != NULL) { while((ent = readdir(dir)) != NULL) { if (strcmp(ent->d_name, branch) == 0) { closedir(dir); return 1; } } closedir(dir); } else return FS_ERROR; return 0; } int new_branch(char* branch_name) { if(branch_exist(branch_name)) return BRANCH_ALREADY_EXIST; char path[strlen(HEADS_DIR) + strlen(branch_name) + 2]; sprintf(path, "%s/%s", HEADS_DIR, branch_name); char old_head[DIGEST_LENGTH * 2 + 1]; get_head_commit_checksum(old_head); FILE *branch_head = fopen(path, "w"); fprintf(branch_head, "%s", old_head); fclose(branch_head); FILE *head_file = fopen(HEAD_FILE, "w"); fprintf(head_file, "%s", path); fclose(head_file); return FS_OK; } int reset_to(char* commit_checksum) { int res = diff_commit_with_working_tree(commit_checksum, 0); if(res != FS_OK) return res; FILE *p = popen("patch -p0 -R < "LOCAL_REPO"/last.diff > /dev/null", "w"); pclose(p); return FS_OK; } int checkout_branch(char *branch) { if(!branch_exist(branch)) { return BRANCH_DOES_NOT_EXIST; } char branch_path[strlen(HEADS_DIR) + strlen(branch) + 2]; sprintf(branch_path, "%s/%s", HEADS_DIR, branch); char commit_checksum[DIGEST_LENGTH * 2 + 1] = {0}; FILE *branch_head = fopen(branch_path, "r"); fread(commit_checksum, DIGEST_LENGTH * 2, 1, branch_head); fclose(branch_head); debug_print("Checking out on %s", commit_checksum); reset_to(commit_checksum); FILE *head_file = fopen(HEAD_FILE, "w"); fprintf(head_file, "%s", branch_path); fclose(head_file); } int is_file_ignored(char *filename) { struct stat buffer = {0}; if (stat(IGNORE_FILE, &buffer) != 0) { return 0; } char content[buffer.st_size + 1]; memset(content, 0, buffer.st_size + 1); FILE *ignore_file = fopen(IGNORE_FILE, "r"); fread(content, buffer.st_size, 1, ignore_file); fclose(ignore_file); char *current_line = strtok(content, "\n"); while(current_line != NULL) { if(strncmp(current_line, filename, strlen(current_line)) == 0) { return 1; } char alt[strlen(current_line) + 3]; sprintf(alt, "./%s", current_line); if(strncmp(alt, filename, strlen(alt)) == 0) return 1; current_line = strtok(NULL, "\n"); } return 0; } int add_file_to_index(struct tree *index, char *filename) { if (is_file_ignored(filename)) { return 0; } struct stat st = {0}; if (stat(filename, &st) != 0) { return FILE_NOT_FOUND; } if (!S_ISREG(st.st_mode)) { DIR *dp; struct dirent *ep; dp = opendir(filename); if (dp != NULL) { while ((ep = readdir(dp)) != NULL) { if (strcmp(ep->d_name, "..") != 0 && strcmp(ep->d_name, ".") != 0) { char path[strlen(ep->d_name) + strlen(filename) + 2]; if (strcmp(filename, "./") == 0) sprintf(path, "%s", ep->d_name); else if (strcmp(filename, ".") == 0) sprintf(path, "%s", ep->d_name); else if(filename[strlen(filename) - 1] == '/') sprintf(path, "%s%s", filename, ep->d_name); else sprintf(path, "%s/%s", filename, ep->d_name); add_file_to_index(index, path); } } closedir(dp); return 0; } } else { if (add_to_index(index, filename) == FILE_NOT_FOUND) return FILE_NOT_FOUND; } } int remove_file_from_index(struct tree *index, char *filename) { if (is_file_ignored(filename)) return 0; struct stat st = {0}; if (stat(filename, &st) != 0) return FILE_NOT_FOUND; if (!S_ISREG(st.st_mode)) { DIR *dp; struct dirent *ep; dp = opendir(filename); if (dp != NULL) { while ((ep = readdir(dp)) != NULL) { if (strcmp(ep->d_name, "..") != 0 && strcmp(ep->d_name, ".") != 0) { char path[strlen(ep->d_name) + strlen(filename) + 2]; if (strcmp(filename, "./") == 0) sprintf(path, "%s", ep->d_name); else if(filename[strlen(filename) - 1] == '/') sprintf(path, "%s%s", filename, ep->d_name); else sprintf(path, "%s/%s", filename, ep->d_name); remove_file_from_index(index, path); } } closedir(dp); return 0; } } else { remove_from_index(index, filename, 1); } } int dump_log() { struct object current_obj = {0}; get_last_commit(¤t_obj); if (current_obj.size == 0) return 0; struct commit current = {0}; commit_from_object(¤t, ¤t_obj); FILE *log_file = fopen(LOG_FILE, "w"); char checksum[DIGEST_LENGTH * 2 + 1]; hash_object(¤t_obj, checksum); fprintf(log_file, "commit %s HEAD\n", checksum); fprintf(log_file, "Author: %s\n", current.author); fprintf(log_file, "Tree: %s\n", current.tree); fprintf(log_file, "\n"); while (strcmp(current.parent, " ") != 0) { free_object(¤t_obj); read_object(current.parent, ¤t_obj); free_commit(¤t); commit_from_object(¤t, ¤t_obj); checksum[DIGEST_LENGTH * 2 + 1]; hash_object(¤t_obj, checksum); fprintf(log_file, "commit %s\n", checksum); fprintf(log_file, "Author: %s\n", current.author); fprintf(log_file, "Tree: %s\n", current.tree); fprintf(log_file, "\n"); } fclose(log_file); return 0; } int dump_branches() { if(!heads_dir_exist()) { return REPO_NOT_INITIALIZED; } DIR *heads_dir = opendir(HEADS_DIR); struct dirent *ep; FILE *dump = fopen(TMP"/branches", "w"); if (heads_dir != NULL) { while ((ep = readdir(heads_dir)) != NULL) { if (strcmp(ep->d_name, "..") != 0 && strcmp(ep->d_name, ".") != 0) fprintf(dump, "%s\n", ep->d_name); } fclose(dump); closedir(heads_dir); return 0; } else return FS_ERROR; }