commit 815cfbbceffc92363de61a15613290b1b2fb52a7 Author: thematdev Date: Thu Jun 15 01:44:02 2023 +0300 First commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..68a49da --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cf63eee --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +TARGET_LIB ?= libqments.a + +BD ?= build +SRC_DIRS ?= src +INCLUDE_DIRS ?= include + +SRC_FILES := $(shell find $(SRC_DIRS) -name *.c) +OBJ_FILES := $(patsubst %.c,$(BD)/%.o,$(SRC_FILES)) + +$(BD)/$(TARGET_LIB): $(OBJ_FILES) + mkdir -p $(@D) + ar rcs $@ $(OBJ_FILES) + +$(BD)/%.o: %.c + mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ -I $(INCLUDE_DIRS) + +all: $(BD)/$(TARGET_LIB) + +clean: + rm -rfI $(BD)/ + +.PHONY: all clean diff --git a/examples/main.c b/examples/main.c new file mode 100644 index 0000000..c1e5143 --- /dev/null +++ b/examples/main.c @@ -0,0 +1,66 @@ +/* Reads text from stdio and creates comment using unix fs driver in current directory */ +/* TODO: add path, displayname as arguments */ + +#include +#include +#include +#include +#include +#include +#include + +#include "comment.h" +#include "driver.h" +#include "drivers/unix_fs/unix_fs_driver.h" + +char * +get_username() +{ + uid_t uid = geteuid(); + struct passwd *pw = getpwuid(uid); + if (pw) { + return pw->pw_name; + } + return ""; +} + +int +main(int argc, const char *argv[]) +{ + const Driver *driver = &unix_fs_driver; + char *path = "."; + + char *user_sid = get_username(); + char *user_displayname = user_sid; + int reply_id = 0; + + size_t cur_buf_size = 256; + size_t idx = 0; + + char *text = malloc(cur_buf_size); + + int ch; + while ((ch = getchar()) != EOF) { + if (idx + 1 == cur_buf_size) { + cur_buf_size *= 2; + text = realloc(text, cur_buf_size); + } + text[idx++] = ch; + } + text[idx] = '\0'; + + time_t current_time; + time(¤t_time); + + UnixFsDriverData driver_data = { path }; + + CommentHeader *header = mk_comment_header(reply_id, current_time, strlen(text), user_sid, user_displayname); + + int retval = driver->leave_comment(&driver_data, header, text); + free(text); + if (retval < 0) { + return retval; + } else { + return 0; + } +} diff --git a/include/comment.h b/include/comment.h new file mode 100644 index 0000000..19426b3 --- /dev/null +++ b/include/comment.h @@ -0,0 +1,24 @@ +#ifndef COMMENT_H +#define COMMENT_H + +#include +#include + +typedef struct { + int reply_id; /* should be 0 if comment is top-level */ + time_t creation_time; + size_t text_length; + char *user_sid, *user_displayname; +} CommentHeader; + +typedef struct { + int id; + CommentHeader *header; + char *text; +} Comment; + +/* does not copy strings given by pointers */ +CommentHeader * +mk_comment_header(int reply_id, time_t creation_time, size_t text_length, char *user_sid, char *user_displayname); + +#endif /* COMMENT_H */ diff --git a/include/driver.h b/include/driver.h new file mode 100644 index 0000000..7dfb268 --- /dev/null +++ b/include/driver.h @@ -0,0 +1,15 @@ +#ifndef DRIVER_H +#define DRIVER_H + +#include "comment.h" + +typedef struct { + /* should return (id > 0) on success, and negative number on error */ + int (*leave_comment)(void*, CommentHeader*, char*); + /* retrieve header information by id */ + int (*get_header)(void*, CommentHeader*, int); + /* retrieve text of comment by id */ + int (*get_text)(void*, char*, int); +} Driver; + +#endif /* DRIVER_H */ diff --git a/include/drivers/unix_fs/unix_fs_driver.h b/include/drivers/unix_fs/unix_fs_driver.h new file mode 100644 index 0000000..60ec7d3 --- /dev/null +++ b/include/drivers/unix_fs/unix_fs_driver.h @@ -0,0 +1,22 @@ +#ifndef UNIX_FS_DRIVER_H +#define UNIX_FS_DRIVER_H + +#include "driver.h" + +#define ID_CTRL_FILE "max_id" +#define TEXT_FILE_FMT "%s/%d.txt" +#define HEADER_FILE_FMT "%s/%d.header" + +typedef struct { + char *path; +} UnixFsDriverData; + +int unix_fs_driver_leave_comment(void *driver_data_ptr, CommentHeader *header, char *text); + +int unix_fs_driver_get_header(void *driver_data_ptr, CommentHeader *header, int id); + +int unix_fs_driver_get_text(void *driver_data_ptr, char *text, int id); + +static const Driver unix_fs_driver = { unix_fs_driver_leave_comment, unix_fs_driver_get_header, unix_fs_driver_get_text }; + +#endif /* UNIX_FS_DRIVER_H */ diff --git a/src/comment.c b/src/comment.c new file mode 100644 index 0000000..de3ecf1 --- /dev/null +++ b/src/comment.c @@ -0,0 +1,17 @@ +#include "comment.h" +#include + +CommentHeader * +mk_comment_header(int reply_id, time_t creation_time, size_t text_length, char *user_sid, char *user_displayname) +{ + CommentHeader *header = malloc(sizeof(CommentHeader)); + + header->reply_id = reply_id; + header->creation_time = creation_time; + header->text_length = text_length; + + header->user_sid = user_sid; + header->user_displayname = user_displayname; + + return header; +} diff --git a/src/drivers/unix_fs/unix_fs_driver.c b/src/drivers/unix_fs/unix_fs_driver.c new file mode 100644 index 0000000..387b2d7 --- /dev/null +++ b/src/drivers/unix_fs/unix_fs_driver.c @@ -0,0 +1,234 @@ +#define _GNU_SOURCE /* ahh, didn't know asprintf is a GNU extension */ +/* TODO: remove this bullshit */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comment.h" +#include "drivers/unix_fs/unix_fs_driver.h" + +#define READ_BUF_SZ 256 + +int +is_valid_directory(const char *path) +{ + struct stat path_stat; + if (stat(path, &path_stat) != 0) { + return 0; + } + return S_ISDIR(path_stat.st_mode); +} + +/* TODO: handle errors */ +int +serialize_header(int fd, const CommentHeader *header) +{ + write(fd, &header->reply_id, sizeof(int)); + write(fd, &header->creation_time, sizeof(time_t)); + write(fd, &header->text_length, sizeof(size_t)); + write(fd, header->user_sid, strlen(header->user_sid) + 1); + write(fd, header->user_displayname, strlen(header->user_displayname) + 1); + return 0; +} + +/* TODO: handle errors */ +/* header string buffers must be long enough */ +int +deserialize_header(int fd, CommentHeader *header) +{ + char buf[READ_BUF_SZ], *dst_bufs[2]; + int bytes_read; + size_t len, dst_idx, i; + + dst_bufs[0] = header->user_sid; + dst_bufs[1] = header->user_displayname; + dst_idx = 0; + + read(fd, &header->reply_id, sizeof(int)); + read(fd, &header->creation_time, sizeof(time_t)); + read(fd, &header->text_length, sizeof(size_t)); + + while ((bytes_read = read(fd, buf, READ_BUF_SZ))) { + for (; i < bytes_read; ++i) { + if (buf[i] != '\0') { + *(dst_bufs[dst_idx]++) = buf[i]; + } else { + dst_idx++; + } + } + } + return 0; +} + +int +unix_fs_driver_leave_comment(void *driver_data_ptr, CommentHeader *header, char *text) +{ + char *path, *header_file_path, *text_file_path, *id_ctrl_file_path; + FILE *id_ctrl_file; + size_t path_len, text_len; + int max_id; + int retval; + int header_fd, text_fd, id_ctrl_fd; + + /* assure safe free calls */ + id_ctrl_file_path = header_file_path = text_file_path = NULL; + id_ctrl_file = NULL; + + header_fd = text_fd = -1; + + path = ((UnixFsDriverData*) driver_data_ptr)->path; + if (!is_valid_directory(path)) { + retval = -EINVAL; + goto defer; + } + + path_len = strlen(path); + text_len = strlen(text); + + if (asprintf(&id_ctrl_file_path, "%s/%s", path, ID_CTRL_FILE) < 0) { + id_ctrl_file_path = NULL; + retval = -ENOMEM; + goto defer; + } + + id_ctrl_file = fopen(id_ctrl_file_path, "r+"); + + if (!id_ctrl_file) { + retval = -EIO; + goto defer; + } + + id_ctrl_fd = fileno(id_ctrl_file); + + if (lockf(id_ctrl_fd, F_LOCK, 0) < 0) { + id_ctrl_fd = -1; + retval = -EIO; + goto defer; + } + + if (fscanf(id_ctrl_file, "%d", &max_id) != 1) { + max_id = 1; + } + + if (asprintf(&header_file_path, HEADER_FILE_FMT, path, max_id) < 0) { + header_file_path = NULL; + retval = -ENOMEM; + goto defer; + } + if (asprintf(&text_file_path, TEXT_FILE_FMT, path, max_id) < 0) { + text_file_path = NULL; + retval = -ENOMEM; + goto defer; + }; + + header_fd = open(header_file_path, O_RDWR | O_CREAT, 0644); + text_fd = open(text_file_path, O_RDWR | O_CREAT, 0644); + + if (header_fd < 0 || text_fd < 0) { + retval = -EINVAL; + goto defer; + } + + if (write(text_fd, text, text_len) < text_len) { + retval = -EIO; + goto defer; + } + + retval = serialize_header(header_fd, header); + if (retval < 0) { + goto defer; + } + + if (!(id_ctrl_file = freopen(id_ctrl_file_path, "w+", id_ctrl_file))) { + retval = -EIO; + goto defer; + } + + fprintf(id_ctrl_file, "%d", max_id + 1); /* update id only on success */ + retval = max_id + 1; +defer: + if (id_ctrl_fd >= 0) { + if (lockf(id_ctrl_fd, F_ULOCK, 0) < 0) { + fprintf(stderr, "Failed to release id control lock, aborting...\n"); + abort(); + } + } + if (id_ctrl_file) fclose(id_ctrl_file); + free(id_ctrl_file_path); + free(header_file_path); + free(text_file_path); + if (header_fd >= 0) close(header_fd); + if (text_fd >= 0) close(text_fd); + return retval; +} + +int +unix_fs_driver_get_header(void *driver_data_ptr, CommentHeader *header, int id) +{ + char *path, *header_file_path; + int fd, retval; + + path = ((UnixFsDriverData*) driver_data_ptr)->path; + if (!asprintf(&header_file_path, HEADER_FILE_FMT, path, id)) { + return -ENOMEM; + } + + fd = open(header_file_path, O_RDONLY); + if (fd < 0) { + free(header_file_path); + return -EIO; + } + + retval = deserialize_header(fd, header); + free(header_file_path); + close(fd); + return retval; +} + +int +unix_fs_driver_get_text(void *driver_data_ptr, char *text, int id) +{ + char *path, *text_file_path; + int fd, retval; + off_t length; + + path = ((UnixFsDriverData*) driver_data_ptr)->path; + if (!asprintf(&text_file_path, TEXT_FILE_FMT, path, id)) { + return -ENOMEM; + } + + fd = open(text_file_path, O_RDONLY); + if (fd < 0) { + retval = -EIO; + goto defer; + } + + length = lseek(fd, 0, SEEK_END); + if (length < 0) { + retval = -EIO; + goto defer; + } + if (lseek(fd, 0, SEEK_SET) < 0) { + retval = -EIO; + goto defer; + } + + if (read(fd, text, length) < length) { + retval = -EIO; + goto defer; + } + + text[length] = '\0'; + retval = 0; + +defer: + free(text_file_path); + if (fd >= 0) close(fd); + return retval; +}