baseline

yet another open-source distributed versioning control system
Log | Files | Refs

commit 058694f8c9d5f6c9dc83956faad195fda3e9d44f
Author: Mohamed Aslan <maslan@sce.carleton.ca>
Date:   Sun, 25 May 2014 05:01:41 -0400

initial import

Diffstat:
AMakefile | 16++++++++++++++++
Abaseline.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd-add.c | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd-branch.c | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd-checkout.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd-commit.c | 190+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd-help.c | 28++++++++++++++++++++++++++++
Acmd-init.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd-log.c | 34++++++++++++++++++++++++++++++++++
Acmd-version.c | 28++++++++++++++++++++++++++++
Acmd.h | 29+++++++++++++++++++++++++++++
Acommon.c | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acommon.h | 24++++++++++++++++++++++++
Aconfig.c | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig.h | 24++++++++++++++++++++++++
Adefaults.h | 24++++++++++++++++++++++++
Adircache-simple.c | 417+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adircache.h | 41+++++++++++++++++++++++++++++++++++++++++
Aobjdb-fs.c | 481+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aobjdb.h | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
20 files changed, 2087 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,16 @@ +PROG= baseline +BINDIR= /usr/bin + +SRCS= baseline.c config.c common.c +SRCS+= cmd-add.c cmd-branch.c cmd-checkout.c cmd-commit.c cmd-help.c cmd-init.c cmd-log.c cmd-version.c +SRCS+= objdb-fs.c +SRCS+= dircache-simple.c + +MAN= + +#CFLAGS+= -DDEBUG -g +CFLAGS+= -g +COPTS+= -Wall + +.include <bsd.prog.mk> + diff --git a/baseline.c b/baseline.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> /* EXIT_* */ +#include <string.h> /* strcmp(1) */ +#include <err.h> /* err(3) */ +#include "cmd.h" + +void usage(void) +{ + fprintf(stderr, "usage: baseline <command> [args]\n"); +} + +int +main(int argc, char **argv) +{ + /* TODO: use getopt(3) */ + if (argc < 2) { + usage(); + return EXIT_FAILURE; + } + if (!strcmp(argv[1], "add")) { + cmd_add(argc - 1, argv + 1); + } + else if (!strcmp(argv[1], "branch")) { + cmd_branch(argc - 1, argv + 1); + } + else if (!strcmp(argv[1], "checkout")) { + cmd_checkout(argc - 1, argv + 1); + } + else if (!strcmp(argv[1], "commit")) { + cmd_commit(argc - 1, argv + 1); + } + else if (!strcmp(argv[1], "init")) { + cmd_init(argc - 1, argv + 1); + } + else if (!strcmp(argv[1], "help")) { + cmd_help(argc - 1, argv + 1); + } + else if (!strcmp(argv[1], "log")) { + cmd_log(argc - 1, argv + 1); + } + else if (!strcmp(argv[1], "version")) { + cmd_version(argc - 1, argv + 1); + } + else { + errx(EXIT_FAILURE, "unkown commad: \'%s\'.\nFor list of available commands, type:\n\tbaseline help", argv[1]); + } + return EXIT_SUCCESS; +} + diff --git a/cmd-add.c b/cmd-add.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> /* EXIT_FAILURE */ +#include <string.h> /* strstr(3) */ +#include <limits.h> /* PATH_MAX */ +#include <unistd.h> /* getcwd(3) */ + +#include "objdb.h" +#include "dircache.h" +#include "cmd.h" + +extern int objdb_baseline_get_ops(struct objdb_ops **); +extern int dircache_simple_get_ops(struct dircache_ops **); +static int is_subdir_of(const char *, const char *); + +int +cmd_add(int argc, char **argv) +{ + char cwd[PATH_MAX]; + char *path = argv[1]; + struct stat s; + struct dircache_ops *dircache; + struct dircache_ctx *dc_ctx; + struct objdb_ops *objdb; + struct objdb_ctx *db_ctx; + objdb_baseline_get_ops(&objdb); + dircache_simple_get_ops(&dircache); + if (path == NULL) { + fprintf(stderr, "baseline: error, no path specified.\n"); + return EXIT_FAILURE; + } + if (objdb->open != NULL) + objdb->open(&db_ctx, "db", ".baseline"); + if (objdb->init != NULL) /* TO BE REMOVED */ + objdb->init(db_ctx); + if (getcwd(cwd, PATH_MAX) == NULL) { + fprintf(stderr, "baseline: error, failed to get the current working directory.\n"); + return EXIT_FAILURE; + } + if (!is_subdir_of(path, cwd)) { + fprintf(stderr, "baseline: error, can not add files from outside the repository.\n"); + return EXIT_FAILURE; + } + if (stat(path, &s) == -1) { + fprintf(stderr, "baseline: error, \'%s\' does not exist.\n", path); + return EXIT_FAILURE; + } + if (dircache->init(&dc_ctx, db_ctx, objdb) == EXIT_FAILURE) { + return EXIT_FAILURE; + } + if (dircache->insert(dc_ctx, path) == EXIT_FAILURE) { + fprintf(stderr, "baseline: error, failed to add \'%s\'.\n", path); + return EXIT_FAILURE; + } + if (objdb->close != NULL) + if (objdb->close(db_ctx) == EXIT_FAILURE) + return EXIT_FAILURE; + return EXIT_SUCCESS; +} + +/* + * checks if first is subdir of second + */ +int +is_subdir_of(const char *first, const char *second) +{ + char first_abspath[PATH_MAX]; + if (first == NULL || second == NULL) + return 0; + if (realpath(first, first_abspath) == NULL) + return 0; +#ifdef DEBUG + printf("DEBUG: realpath: %s\n", first_abspath); +#endif + if (strstr(first_abspath, second) == first_abspath) + return 1; + return 0; +} + diff --git a/cmd-branch.c b/cmd-branch.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> /* EXIT_FAILURE */ +#include <string.h> /* strstr(3) */ +#include <limits.h> /* PATH_MAX */ +#include <fcntl.h> /* open(2) */ +#include <unistd.h> /* getcwd(3) */ +#include <err.h> /* errx(3) */ + +#include "defaults.h" +#include "config.h" +#include "objdb.h" +#include "dircache.h" +#include "cmd.h" + +extern int objdb_baseline_get_ops(struct objdb_ops **); +extern int dircache_simple_get_ops(struct dircache_ops **); + +int cmd_branch(int, char **); + +/* + * creates a new branch. + */ +static int +create(const char *branch_name) { + char *baseline_path; + int retval; + struct objdb_ops *objdb; + struct objdb_ctx *db_ctx; + objdb_baseline_get_ops(&objdb); + /* FIXME: get repository path correctly */ + asprintf(&baseline_path, "%s", BASELINE_DIR); + /* FIXME: check if repository initialized */ + if (objdb->open != NULL) + objdb->open(&db_ctx, "db", baseline_path); + if (objdb->init != NULL) /* TO BE REMOVED */ + objdb->init(db_ctx); + retval = objdb->branch_create(db_ctx, branch_name); + if (objdb->close != NULL) + objdb->close(db_ctx); + free(baseline_path); + free(objdb); + return retval; +} + +static int +update(const char *branch_name, const char *head_objid) +{ + + return EXIT_SUCCESS; +} + +static int +ls() +{ + char *baseline_path; + int retval; + struct objdb_ops *objdb; + struct objdb_ctx *db_ctx; + objdb_baseline_get_ops(&objdb); + /* FIXME: get repository path correctly */ + asprintf(&baseline_path, "%s", BASELINE_DIR); + /* FIXME: check if repository initialized */ + if (objdb->open != NULL) + objdb->open(&db_ctx, "db", baseline_path); + if (objdb->init != NULL) /* TO BE REMOVED */ + objdb->init(db_ctx); + retval = objdb->branch_ls(db_ctx); + if (objdb->close != NULL) + objdb->close(db_ctx); + free(baseline_path); + free(objdb); + return retval; +} + +int +cmd_branch(int argc, char **argv) +{ + /* + * branch command can be used to: + * 1. list all branches + * 2. create a new branch + */ + + if (argc == 1) { /* no args */ + /* list all branches */ + ls(); + } + else if (argc == 2) { /* 1 arg */ + /* create a new branch */ + if (create(argv[1]) == EXIT_FAILURE) + errx(EXIT_FAILURE, "failed to create branch \'%s\'", argv[1]); + } + else{ + errx(EXIT_FAILURE, "wrong number of parameters for command \'branch\'"); + } + return EXIT_SUCCESS; +} diff --git a/cmd-checkout.c b/cmd-checkout.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> /* EXIT_FAILURE */ +#include <string.h> /* strstr(3) */ +#include <limits.h> /* PATH_MAX */ +#include <fcntl.h> /* open(2) */ +#include <unistd.h> /* getcwd(3) */ +#include <err.h> /* errx(3) */ + +#include "defaults.h" +#include "config.h" +#include "objdb.h" +#include "dircache.h" +#include "cmd.h" + +extern int objdb_baseline_get_ops(struct objdb_ops **); +extern int dircache_simple_get_ops(struct dircache_ops **); + +int cmd_branch(int, char **); + +/* + * creates a new branch. + */ +static int +checkout(const char *branch_name) { + char *baseline_path; + int exist, retval = EXIT_SUCCESS; + struct objdb_ops *objdb; + struct objdb_ctx *db_ctx; + struct dircache_ops *dircache; + struct dircache_ctx *dc_ctx; + objdb_baseline_get_ops(&objdb); + dircache_simple_get_ops(&dircache); + /* FIXME: get repository path correctly */ + asprintf(&baseline_path, "%s", BASELINE_DIR); + /* FIXME: check if repository initialized */ + if (objdb->open != NULL) + objdb->open(&db_ctx, "db", baseline_path); + if (objdb->init != NULL) /* TO BE REMOVED */ + objdb->init(db_ctx); + dircache->init(&dc_ctx, db_ctx, objdb); + if (objdb->branch_if_exists(db_ctx, branch_name, &exist) == EXIT_FAILURE) { + return EXIT_FAILURE; + } + if (exist) + dircache->checkout(dc_ctx, branch_name); + else + errx(EXIT_FAILURE, "branch \'%s\' does not exist.", branch_name); + + if (objdb->close != NULL) + objdb->close(db_ctx); + free(baseline_path); + free(objdb); + return retval; +} + +int +cmd_checkout(int argc, char **argv) +{ + if (argc == 2) { /* 1 arg */ + if (checkout(argv[1]) == EXIT_FAILURE) + errx(EXIT_FAILURE, "failed to checkout branch \'%s\'", argv[1]); + } + else{ + errx(EXIT_FAILURE, "wrong number of parameters for command \'checkout\'"); + } + return EXIT_SUCCESS; +} diff --git a/cmd-commit.c b/cmd-commit.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> /* EXIT_FAILURE */ +#include <string.h> /* strstr(3) */ +#include <limits.h> /* PATH_MAX */ +#include <unistd.h> /* getcwd(3) */ +#include <err.h> /* err(3) */ + +#include "defaults.h" +#include "config.h" +#include "objdb.h" +#include "dircache.h" +#include "cmd.h" + +extern int objdb_baseline_get_ops(struct objdb_ops **); +extern int dircache_simple_get_ops(struct dircache_ops **); + +static const char msg_head[] = { + "\n" + "# Please enter your commit message.\n" + "# Lines starting with \'#\' will be ignored.\n" +}; + +int +gen_temp_msgfile_from_msg(char **filename, const char *msg) +{ + char *msg_file = NULL; + int msgfd; + + if (msg == NULL) + return EXIT_FAILURE; + /* FIXME: get baseline dir */ + asprintf(&msg_file, "%s/tmp.XXXXXX", BASELINE_DIR); + if ((msgfd = mkstemp(msg_file)) == -1) + return EXIT_FAILURE; + if (write(msgfd, msg, strlen(msg)) == -1) + return EXIT_FAILURE; + if (write(msgfd, "\n", strlen("\n")) == -1) + return EXIT_FAILURE; + close(msgfd); + *filename = msg_file; + return EXIT_SUCCESS; +} + +int +gen_temp_msgfile(char **filename) +{ + char *msg_file = NULL; + int msgfd; + /* FIXME: get baseline dir */ + asprintf(&msg_file, "%s/tmp.XXXXXX", BASELINE_DIR); + if ((msgfd = mkstemp(msg_file)) == -1) + return EXIT_FAILURE; + if (write(msgfd, msg_head, sizeof(msg_head) - 1) == -1) + return EXIT_FAILURE; + close(msgfd); + *filename = msg_file; + return EXIT_SUCCESS; +} +int +process_msgfile(char **filename) +{ + char *proc_file = NULL; + char *line = NULL; + int pfd; + size_t size = 0; + FILE *fp; + /* FIXME: get baseline dir */ + asprintf(&proc_file, "%s/tmp.XXXXXX", BASELINE_DIR); + if ((pfd = mkstemp(proc_file)) == -1) + return EXIT_FAILURE; + if((fp = fopen(*filename, "r")) == NULL) { + unlink(proc_file); + goto failure; + } + while (getline(&line, &size, fp) != -1) { + if(line[0] == '#') + continue; + if (write(pfd, line, strlen(line)) == -1) + goto failure; + } + close(pfd); + fclose(fp); + unlink(*filename); + free(*filename); + *filename = proc_file; + return EXIT_SUCCESS; +failure: + free(proc_file); + *filename = NULL; + return EXIT_FAILURE; +} + +int +cmd_commit(int argc, char **argv) +{ + char *commit_msg, *msg_file; + char cwd[PATH_MAX]; + char *exec; + const char *editor; + int ch; + int flag_msg = 0, flag_error = 0; + struct dircache_ops *dircache; + struct dircache_ctx *dc_ctx; + struct objdb_ops *objdb; + struct objdb_ctx *db_ctx; + + /* load configurations */ + if (baseline_config_load(BASELINE_DIR "/config") == EXIT_FAILURE) + errx(EXIT_FAILURE, "failed to load configurations"); + /* parse command line options */ + while ((ch = getopt(argc, argv, "m:")) != -1) { + switch (ch) { + case 'm': + flag_msg = 1; + asprintf(&commit_msg, "%s", optarg); + break; + default: + flag_error = 1; + break; + } + } + argc -= optind; + argv += optind; + + if (flag_error) { + return EXIT_FAILURE; + } + if (flag_msg) { + /* TODO: should be per-stage file */ + if (gen_temp_msgfile_from_msg(&msg_file, commit_msg) == EXIT_FAILURE) + errx(EXIT_FAILURE, "failed to generate commit message"); + free(commit_msg); + } + else { + if (gen_temp_msgfile(&msg_file) == EXIT_FAILURE) + errx(EXIT_FAILURE, "failed to generate commit message"); + if((editor = baseline_config_get_val("editor")) != NULL && strcmp(editor, "")) { + asprintf(&exec, "%s %s", editor, msg_file); + system(exec); + free(exec); + } + else if ((editor = getenv("EDITOR")) != NULL) { + asprintf(&exec, "%s %s", editor, msg_file); + system(exec); + free(exec); + } + else { + errx(EXIT_FAILURE, "error, commit message not specified."); + } + } + if (process_msgfile(&msg_file) == EXIT_FAILURE) + errx(EXIT_FAILURE, "failed to process commit message"); + + objdb_baseline_get_ops(&objdb); + dircache_simple_get_ops(&dircache); + if (objdb->open != NULL) + objdb->open(&db_ctx, "db", ".baseline"); + if (objdb->init != NULL) /* TO BE REMOVED */ + objdb->init(db_ctx); + if (getcwd(cwd, PATH_MAX) == NULL) { + fprintf(stderr, "baseline: error, failed to get the current working directory.\n"); + return EXIT_FAILURE; + } + dircache->init(&dc_ctx, db_ctx, objdb); + if (dircache->commit(dc_ctx, msg_file) == EXIT_FAILURE) + fprintf(stderr, "baseline: error, failed to commit your changes.\n"); + if (objdb->close != NULL) objdb->close(db_ctx); + unlink(msg_file); + free(msg_file); + return EXIT_SUCCESS; +} + diff --git a/cmd-help.c b/cmd-help.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> /* printf(3) */ +#include <stdlib.h> /* EXIT_SUCCESS */ + +#include "cmd.h" + +int +cmd_help(int argc, char **argv) +{ + printf("help\n"); + return EXIT_SUCCESS; +} + diff --git a/cmd-init.c b/cmd-init.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> /* EXIT_FAILURE */ +#include <string.h> /* strstr(3) */ +#include <limits.h> /* PATH_MAX */ +#include <fcntl.h> /* open(2) */ +#include <unistd.h> /* getcwd(3) */ +#include <err.h> /* errx(3) */ + +#include "defaults.h" +#include "config.h" +#include "objdb.h" +#include "dircache.h" +#include "common.h" +#include "cmd.h" + +extern int objdb_baseline_get_ops(struct objdb_ops **); +extern int dircache_simple_get_ops(struct dircache_ops **); + +int cmd_init(int, char **); + +/* + * creates a new repository in the given path. + */ +static int +init(const char *path) { + char *baseline_path, *config_path; + /* int cfd; */ + struct dircache_ops *dircache; + struct dircache_ctx *dc_ctx; + struct objdb_ops *objdb; + struct objdb_ctx *db_ctx; + + objdb_baseline_get_ops(&objdb); + dircache_simple_get_ops(&dircache); + asprintf(&baseline_path, "%s/%s", path, BASELINE_DIR); + /* create '.baseline' dir */ + if (mkdir(baseline_path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) { + return EXIT_FAILURE; + } + /* create '.baseline/config file */ + asprintf(&config_path, "%s/config", baseline_path); +/* + if ((cfd = open(config_path, O_CREAT | O_TRUNC | O_WRONLY)) == -1) { + return EXIT_FAILURE; + } + close(cfd); +*/ + if (baseline_config_create(config_path) == EXIT_FAILURE) + return EXIT_FAILURE; + /* FIXME: error checks */ + /* let objdb initialize it's structures too */ + if (objdb->open != NULL) + objdb->open(&db_ctx, "db", baseline_path); + if (objdb->init != NULL) /* TO BE REMOVED */ + objdb->init(db_ctx); + /* as well as dircache */ + if (dircache->init != NULL) + dircache->init(&dc_ctx, db_ctx, objdb); + if (objdb->close != NULL) + objdb->close(db_ctx); + free(config_path); + free(baseline_path); + free(objdb); + free(dircache); + return EXIT_SUCCESS; +} + +int +cmd_init(int argc, char **argv) +{ + char cwd[PATH_MAX+1]; + char *baseline_dir = NULL; + const char *repo; + + repo = baseline_repo_get_rootdir(); + if (repo != NULL) + errx(EXIT_SUCCESS, "repository already initialized."); + if (getcwd(cwd, sizeof(cwd)) == NULL) + errx(EXIT_FAILURE, "error, can not read current directory."); + asprintf(&baseline_dir, "%s/%s", cwd, BASELINE_DIR); + if (exists(baseline_dir)) + errx(EXIT_FAILURE, "error, corrupted repository already exists."); + free(baseline_dir); + if (init(cwd) == EXIT_FAILURE) + errx(EXIT_FAILURE, "error, could not initialize repository."); + printf("baseline: repository successfully initialized at \'%s\'.\n", cwd); + return EXIT_SUCCESS; +} + diff --git a/cmd-log.c b/cmd-log.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> /* printf(3) */ +#include <stdlib.h> /* EXIT_SUCCESS */ + +#include "cmd.h" +#include "common.h" + +int +cmd_log(int argc, char **argv) +{ + const char *ptr = baseline_repo_get_rootdir(); + const char *ptr2 = baseline_repo_get_rootdir(); + if (ptr != NULL) + printf("%s\n", ptr); + if (ptr2 != NULL) + printf("%s\n", ptr2); + return EXIT_SUCCESS; +} + diff --git a/cmd-version.c b/cmd-version.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> /* printf(3) */ +#include <stdlib.h> /* EXIT_SUCCESS */ + +#include "cmd.h" + +int +cmd_version(int argc, char **argv) +{ + printf("baseline version 0.0.1\n"); + return EXIT_SUCCESS; +} + diff --git a/cmd.h b/cmd.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CMD_H_ +#define _CMD_H_ + +int cmd_add(int, char **); +int cmd_branch(int, char **); +int cmd_checkout(int, char **); +int cmd_commit(int, char **); +int cmd_help(int, char **); +int cmd_init(int, char **); +int cmd_log(int, char **); +int cmd_version(int, char **); + +#endif diff --git a/common.c b/common.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <string.h> +#include <limits.h> +#include <unistd.h> + +#include <sys/stat.h> + +#include "defaults.h" + +/* cache the repository root dir for future calls of baseline_repo_get_rootdir() */ +static char *repo_rootdir = NULL; + +int +exists(const char *f) +{ + struct stat s; + + if(stat(f, &s) == -1) + return 0; + return 1; +} + +int +dir_exists(const char *dir) +{ + struct stat s; + + if (stat(dir, &s) == -1) + return 0; + if (!S_ISDIR(s.st_mode)) + return 0; + return 1; +} + +static int +baseline_validate_repo(const char *path) +{ + char *ptr = NULL; + int valid; + + asprintf(&ptr, "%s/%s", path, BASELINE_DIR); + if (ptr == NULL) + return 0; + valid = dir_exists(ptr); + /* TODO: use access & check for modes as well */ + /* TODO: ask objdb, and may be dircache too */ + free(ptr); + return valid; +} + +/* + * may not be so safe + * do not free the returned pointer, it will be cached for later calls + * try not to use chdir() as possible + */ +const char * +baseline_repo_get_rootdir() +{ + char cwd[PATH_MAX+1]; + int i; + size_t len; + + /* check if the repository root dir already cached */ + if (repo_rootdir != NULL) + return repo_rootdir; + if (getcwd(cwd, sizeof(cwd)) == NULL) + return NULL; + len = strlen(cwd); + for (i=len-1 ; i>=0 ; i--) { + if (cwd[i] == '/') { + if (baseline_validate_repo(cwd)) + break; + cwd[i] = '\0'; + } + } + if (i == -1) { + if (baseline_validate_repo(cwd)) + asprintf(&repo_rootdir, "/"); + else + return NULL; + } + else { + asprintf(&repo_rootdir, "%s", cwd); + } +#ifdef DEBUG + printf("repository root dir: %s\n", repo_rootdir); +#endif + return repo_rootdir; +} + diff --git a/common.h b/common.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _COMMON_H_ +#define _COMMON_H_ + +int exists(const char *); +int dir_exists(const char *); +const char* baseline_repo_get_rootdir(); + +#endif diff --git a/config.c b/config.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> /* printf(3) */ +#include <stdlib.h> /* EXIT_SUCCESS */ +#include <string.h> /* strchr(3), strcmp(3) */ + +#include "config.h" + +#define N_OPTIONS 3 + +static const char config_sample[] = +"#\n" +"# baseline configuration file\n" +"# edit as you wish, but be cautious!\n" +"#\n" +"\n" +"username = Anonymous\n" +"useremail = anon@localhost.localdomain\n" +"\n"; + +struct option { + char *key; + char val[32]; +}; + +static struct option options[] = { + {.key = "username", .val = ""}, + {.key = "useremail", .val = ""}, + {.key = "editor", .val = ""} +}; + +int +baseline_config_create(const char *path) +{ + FILE *fp; + if (path == NULL) + return EXIT_FAILURE; + if ((fp = fopen(path, "w")) == NULL) + return EXIT_FAILURE; + fwrite(config_sample, sizeof(char), sizeof(config_sample) - 1, fp); + fclose(fp); + return EXIT_SUCCESS; +} + +int +baseline_config_load(const char *path) +{ + char *line = NULL; /* must be initialized to NULL or getline()'s realloc() might complain */ + char *ptr, *tmp; + char key[32], val[32]; + int i, idx = 0; + size_t size = 0; + ssize_t len; + FILE *fp; + if (path == NULL) + return EXIT_FAILURE; + if ((fp = fopen(path, "r")) == NULL) + return EXIT_FAILURE; + while ((len = getline(&line, &size, fp)) != -1) { + /* skip blank lines */ + if (line[0] == '\n') + continue; + i = 0; + /* skip white spaces */ + while(line[i] == ' ' || line[i] == '\t') + i++; + ptr = &line[i]; + /* skip comments */ + if (ptr[0] == '#' || ptr[0] == ';') + continue; + if ((tmp = strchr(ptr, '=')) == NULL) + return EXIT_FAILURE; + *tmp = ' '; + sscanf(ptr, "%s%s", key, val); + if (!strcmp(options[idx].key, key)) { + strlcpy(options[idx].val, val, sizeof(options[idx].val)); + idx++; + if (idx > N_OPTIONS) + break; + } + } + fclose(fp); + return EXIT_SUCCESS; +} + +const char * +baseline_config_get_val(const char *key) { + int i; + for (i=0 ; i<N_OPTIONS ; i++) + if(!strcmp(options[i].key, key)) + return options[i].val; + return NULL; +} + diff --git a/config.h b/config.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CONFIG_H_ +#define CONFIG_H_ + +int baseline_config_create(const char *); +int baseline_config_load(const char *); +const char* baseline_config_get_val(const char *); + +#endif diff --git a/defaults.h b/defaults.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DEFAULTS_H_ +#define DEFAULTS_H_ + +#define BASELINE_DIR ".baseline" +#define BASELINE_DB "db" +#define DEFAULT_BRANCH "master" + +#endif diff --git a/dircache-simple.c b/dircache-simple.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/stat.h> /* stat(3) */ + +#include <stdio.h> +#include <stdlib.h> /* malloc(2) */ +#include <string.h> /* str*(2), mem*(2) */ +#include <unistd.h> /* close(2), rmdir(2), unlink(2) */ + +#include <fts.h> /* fts_*(3) */ + +#include "defaults.h" +#include "config.h" +#include "objdb.h" +#include "dircache.h" + +int dircache_simple_get_ops(struct dircache_ops **); +static int simple_init(struct dircache_ctx **, struct objdb_ctx *, struct objdb_ops *); +static int simple_insert(struct dircache_ctx *, const char *); +static int simple_checkout(struct dircache_ctx *, const char *); +static int simple_commit(struct dircache_ctx *, const char *); + +static struct dircache_ops simple_ops = { + .name = "simple", + .version = "1.0", + .init = simple_init, + .insert = simple_insert, + .remove = NULL, + .commit = simple_commit, + .checkout = simple_checkout, + .fsck = NULL, + .compress = NULL, + .dedup = NULL +}; + +int +dircache_simple_get_ops(struct dircache_ops **ops) +{ + if (ops == NULL) + return EXIT_FAILURE; + *ops = (struct dircache_ops *)malloc(sizeof(struct dircache_ops)); + memcpy(*ops, &simple_ops, sizeof(struct dircache_ops)); + return EXIT_SUCCESS; +} + +static int +simple_init(struct dircache_ctx **dc_ctx, struct objdb_ctx *db_ctx, struct objdb_ops *db_ops) +{ + char *branch_name = NULL, *branch_path; + char *dc_path; + int exist, retval; + size_t size = 0; + struct stat s; + FILE *branch_fp; + if (dc_ctx == NULL || db_ctx == NULL) + return EXIT_FAILURE; + /* FIXME: use get baseline path 'common' function */ + asprintf(&dc_path, "%s/%s", ".baseline", "dircache"); + asprintf(&branch_path, "%s/branch", ".baseline"); + /* check if '.baseline/dircache' dir exists */ + if (stat(dc_path, &s) == -1) { + /* create '.baseline/dircache' dir */ + if (mkdir(dc_path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) { + return EXIT_FAILURE; + } + } + /* check if '.baseline/branch' file exists */ + if(stat(branch_path, &s) == 0) { + if ((branch_fp = fopen(branch_path, "r")) == NULL) { + retval = EXIT_FAILURE; + goto ret; + } + if (getline(&branch_name, &size, branch_fp) == -1) { + retval = EXIT_FAILURE; + free(branch_name); + goto ret; + } + fclose(branch_fp); + } + else { + asprintf(&branch_name, "%s", DEFAULT_BRANCH); + if (db_ops->branch_if_exists(db_ctx, branch_name, &exist) == EXIT_FAILURE) { + retval = EXIT_FAILURE; + free(branch_name); + goto ret; + } + if (!exist) + db_ops->branch_create(db_ctx, branch_name); + /* create '.baseline/branch' file */ + if ((branch_fp = fopen(branch_path, "w")) == NULL) { + retval = EXIT_FAILURE; + goto ret; + } + fprintf(branch_fp, branch_name); + fclose(branch_fp); + } + *dc_ctx = (struct dircache_ctx *)malloc(sizeof(struct dircache_ctx)); + (*dc_ctx)->db_ctx = db_ctx; + (*dc_ctx)->db_ops = db_ops; +ret: + free(dc_path); + free(branch_path); + free(branch_name); + return EXIT_SUCCESS; +} + +static int +simple_insert(struct dircache_ctx *dc_ctx, const char *path) +{ + char *objid, *paths[2], *cache_path; + struct stat s, fs; + FILE *fp; + FTS *dir; + FTSENT *entry; + if (stat(path, &s) == -1) + return EXIT_FAILURE; + if (S_ISDIR(s.st_mode)) { + /* dirs are cached for now, and should be inserted at commit time */ + paths[0] = (char *)path; + paths[1] = NULL; + if ((dir = fts_open(paths, FTS_NOCHDIR, 0)) == NULL) + return EXIT_FAILURE; + while ((entry = fts_read(dir)) != NULL) { + /* skip directories starting with '.', other than our top-level directory */ + if (entry->fts_name[0] == '.' && entry->fts_level != FTS_ROOTLEVEL) { + fts_set(dir, entry, FTS_SKIP); + continue; + } + if (entry->fts_info & FTS_D) { +#ifdef DEBUG + printf("DS: %s\n", entry->fts_path); +#endif + /* FIXME: path check is required */ + asprintf(&cache_path, ".baseline/dircache/%s", entry->fts_path); + if (mkdir(cache_path, S_IRUSR | S_IWUSR | S_IXUSR) != 0) { + free(cache_path); + return EXIT_FAILURE; + } +#ifdef DEBUG + printf("[DEBUG] dircache: created dir %s\n", cache_path); +#endif + free(cache_path); + } + if(entry->fts_info & FTS_F) { +#ifdef DEBUG + printf("F: %s\n", entry->fts_path); +#endif + if (stat(entry->fts_path, &fs) == -1) + return EXIT_FAILURE; + if (dc_ctx->db_ops->insert_from_file(dc_ctx->db_ctx, T_FILE, entry->fts_path, &objid) == EXIT_FAILURE) + return EXIT_FAILURE; +#ifdef DEBUG + printf("\t ID = %s\n", objid); +#endif + /* FIXME: path check is required */ + asprintf(&cache_path, ".baseline/dircache/%s", entry->fts_path); + if ((fp = fopen(cache_path, "w")) == NULL) { + free(cache_path); + return EXIT_FAILURE; + } +#ifdef DEBUG + printf("[DEBUG] dircache: created file %s\n", cache_path); +#endif + fprintf(fp, "F %s %o\n", objid, fs.st_mode); + fclose(fp); + free(cache_path); + } + } + fts_close(dir); + } + else { + /* it's a file, why the hell would we wait, insert it immediately */ + return dc_ctx->db_ops->insert_from_file(dc_ctx->db_ctx, T_FILE, path, &objid); + } + return EXIT_SUCCESS; +} + +static int +simple_gen_dindex(char **didx) +{ + char *dircache_path, *didx_path = NULL; + char *paths[2]; + int didx_fd; + struct stat s; + FILE *didx_fp; + FTS *ftsp; + FTSENT *entry; + /* FIXME: root dir */ + asprintf(&dircache_path, ".baseline/dircache"); + if (stat(dircache_path, &s) == -1) + return EXIT_FAILURE; + if (!S_ISDIR(s.st_mode)) + return EXIT_FAILURE; + /* create dir-only index */ + asprintf(&didx_path, ".baseline/dindex.XXXXXXX"); + if ((didx_fd = mkstemp(didx_path)) == -1) { + return EXIT_FAILURE; + } + if ((didx_fp = fdopen(didx_fd, "w")) == NULL) { + unlink(didx_path); + close(didx_fd); + return EXIT_FAILURE; + } + paths[0] = (char *)dircache_path; + paths[1] = NULL; + if ((ftsp = fts_open(paths, FTS_NOCHDIR, 0)) == NULL) + return EXIT_FAILURE; + while ((entry = fts_read(ftsp)) != NULL) { + /* skip directories starting with '.', other than our top-level directory */ + if (entry->fts_name[0] == '.' && entry->fts_level != FTS_ROOTLEVEL) { + fts_set(ftsp, entry, FTS_SKIP); + continue; + } + if (entry->fts_info & FTS_DP) { + /* FIXME: spaces! */ + fprintf(didx_fp, "%s\n", entry->fts_path); + } + } + fts_close(ftsp); + fclose(didx_fp); + free(dircache_path); + *didx = didx_path; + return EXIT_SUCCESS; +} + +static int +simple_gen_commit(const char *dir_id, const char *msg_file, char **commit_file) +{ + char *dc_path = ".baseline/dircache"; /* FIXME */ + char *cmt_path; + char *msg = NULL; + const char *user_name, *user_email; + int cfd; + size_t size = 0; + struct stat s; + FILE *cfp, *mfp; + + user_name = baseline_config_get_val("username"); + user_email = baseline_config_get_val("useremail"); + if (user_name == NULL || user_email == NULL) + return EXIT_FAILURE; + if (msg_file == NULL) + return EXIT_FAILURE; + if (stat(dc_path, &s) == -1) + return EXIT_FAILURE; + if (!S_ISDIR(s.st_mode)) + return EXIT_FAILURE; + /* commit message */ + if ((mfp = fopen(msg_file, "r")) == NULL) + return EXIT_FAILURE; + /* create commit */ + asprintf(&cmt_path, "%s/commit.XXXXXXX", dc_path); + if ((cfd = mkstemp(cmt_path)) == -1) { + return EXIT_FAILURE; + } + if ((cfp = fdopen(cfd, "w")) == NULL) { + unlink(cmt_path); + close(cfd); + return EXIT_FAILURE; + } + fprintf(cfp, "dir %s\n", dir_id); + fprintf(cfp, "author %s <%s>\n", user_name, user_email); + fprintf(cfp, "committer %s <%s>\n", user_name, user_email); + while (getline(&msg, &size, mfp) != -1) { + fprintf(cfp, "%s", msg); + } + fclose(cfp); + fclose(mfp); + *commit_file = cmt_path; + return EXIT_SUCCESS; +} + +static int +simple_get_current_branch(char **branch_name) +{ + size_t size = 0; + FILE *branch_fp; + /* FIXME: */ + *branch_name = NULL; + if ((branch_fp = fopen(".baseline/branch", "r")) == NULL) { + return EXIT_FAILURE; + } + if (getline(branch_name, &size, branch_fp) == -1) { + free(*branch_name); + return EXIT_FAILURE; + } + fclose(branch_fp); + return EXIT_SUCCESS; +} + +static int +simple_commit(struct dircache_ctx *dc_ctx, const char *msgfile) +{ + char *branch; + char *objid, *path, *paths[2], tmp_objid[1024]; + char *dircache_path, *didx_path; + struct objdb_dirhandle *handle; + struct stat s; + FILE *didx_fp, *fp; + FTS *dir; + FTSENT *entry; + unsigned int mode; + size_t size; + ssize_t len; + char type; + + /* FIXME: root dir */ + asprintf(&dircache_path, ".baseline/dircache"); + if (stat(dircache_path, &s) == -1) + return EXIT_FAILURE; + if (!S_ISDIR(s.st_mode)) + return EXIT_FAILURE; + /* FIXME: empty dirache directory */ + simple_gen_dindex(&didx_path); + if ((didx_fp = fopen(didx_path, "r")) == NULL) + return EXIT_FAILURE; + size = 0; + path = NULL; /* if not set to NULL, realloc() will get pissed. And, it can waste your day! */ + while ((len = getline(&path, &size, didx_fp)) != -1) { + if (path[len-1] == '\n') { + path[len-1] = 0; + --len; + } + printf("DIR: %s\n", path); + dc_ctx->db_ops->dir_begin(dc_ctx->db_ctx, &handle); + paths[0] = (char *)path; + paths[1] = NULL; + if ((dir = fts_open(paths, FTS_NOCHDIR, 0)) == NULL) + return EXIT_FAILURE; + while ((entry = fts_read(dir)) != NULL) { + if (entry->fts_name[0] == '.' && entry->fts_level != FTS_ROOTLEVEL) { + fts_set(dir, entry, FTS_SKIP); + continue; + } + if (entry->fts_info & FTS_F) { + printf("\t FILE: %s\n", entry->fts_path); + if ((fp = fopen(entry->fts_path, "r")) == NULL) + return EXIT_FAILURE; + fscanf(fp, "%c %s %o", &type, tmp_objid, &mode); + if (type == 'F') + dc_ctx->db_ops->dir_insert_object(dc_ctx->db_ctx, handle, tmp_objid, entry->fts_name, T_FILE, mode); + else if (type == 'D') + dc_ctx->db_ops->dir_insert_object(dc_ctx->db_ctx, handle, tmp_objid, entry->fts_name, T_DIR, mode); + fclose(fp); + unlink(entry->fts_path); + } + else { + if (entry->fts_level >= 1) { + fts_set(dir, entry, FTS_SKIP); + } + } + } + fts_close(dir); + dc_ctx->db_ops->dir_end(dc_ctx->db_ctx, handle, &objid); +#ifdef DEBUG + printf("dir \'%s\' commited with ID = %s\n", path, objid); +#endif + if (stat(path, &s) == -1) + return EXIT_FAILURE; + /* FIXME: do not remove '.baseline/dircache' dir */ + if (rmdir(path) == -1) + return EXIT_FAILURE; + if ((fp = fopen(path, "w")) == NULL) + return EXIT_FAILURE; + fprintf(fp, "%c %s %o", 'D', objid, s.st_mode); + fclose(fp); + } + free(path); + fclose(didx_fp); + unlink(didx_path); + /* temp */ + unlink(".baseline/dircache"); + if (mkdir(".baseline/dircache", S_IRUSR | S_IWUSR | S_IXUSR) == -1) { + return EXIT_FAILURE; + } + /* create commit */ + if (simple_gen_commit(objid, msgfile, &path) == EXIT_FAILURE) + return EXIT_FAILURE; + if (dc_ctx->db_ops->insert_from_file(dc_ctx->db_ctx, T_COMMIT, path, &objid) == EXIT_FAILURE) + return EXIT_FAILURE; + printf("COMMIT ID: %s\n", objid); + unlink(path); + simple_get_current_branch(&branch); + dc_ctx->db_ops->branch_set_head(dc_ctx->db_ctx, branch, objid); + free(path); + free(objid); + return EXIT_SUCCESS; +} + +static int +simple_checkout(struct dircache_ctx *dc_ctx, const char *branch_name) +{ + FILE *branch_fp; + /* FIXME: */ + if ((branch_fp = fopen(".baseline/branch", "w")) == NULL) { + return EXIT_FAILURE; + } + fprintf(branch_fp, branch_name); + fclose(branch_fp); + /* TODO: copy head files into working dir */ + return EXIT_SUCCESS; +} + diff --git a/dircache.h b/dircache.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DIRCACHE_H_ +#define _DIRCAHCE_H_ + +#include <sys/types.h> +#include "objdb.h" + +struct dircache_ctx { + struct objdb_ctx *db_ctx; + struct objdb_ops *db_ops; +}; + +struct dircache_ops { + char *name; + char *version; + int (*init)(struct dircache_ctx **, struct objdb_ctx *, struct objdb_ops *); + int (*insert)(struct dircache_ctx *, const char *); + int (*remove)(struct dircache_ctx *, const char *); + int (*commit)(struct dircache_ctx *, const char *); + int (*checkout)(struct dircache_ctx *, const char *); + int (*fsck)(struct dircache_ctx *); + int (*compress)(struct dircache_ctx *); + int (*dedup)(struct dircache_ctx *); +}; + +#endif diff --git a/objdb-fs.c b/objdb-fs.c @@ -0,0 +1,481 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/stat.h> /* stat(3) */ + +#include <stdio.h> /* rename(2) */ +#include <stdlib.h> /* malloc(2) */ +#include <string.h> /* str*(2) , mem*(2) */ +#include <unistd.h> /* access(2) */ + +#include <sha2.h> /* SHA256*() */ + +#include <fts.h> /* fts_*(3) */ + +#include "defaults.h" +#include "objdb.h" + +int objdb_baseline_get_ops(struct objdb_ops **); +static char * get_objdb_dir(struct objdb_ctx *); +static int objdb_bl_open(struct objdb_ctx **, const char *, const char *); +static int objdb_bl_close(struct objdb_ctx *); +static int objdb_bl_init(struct objdb_ctx *); +static int objdb_bl_insert(struct objdb_ctx *, enum objdb_type, const u_int8_t *, char **); +static int objdb_bl_insert_from_file(struct objdb_ctx *, enum objdb_type, const char *, char **); +static int objdb_bl_remove(struct objdb_ctx *, const char *, const char *); +static int objdb_bl_dir_begin(struct objdb_ctx *, struct objdb_dirhandle **); +static int objdb_bl_dir_insert_object(struct objdb_ctx *, struct objdb_dirhandle *, const char *, const char *, enum objdb_type, mode_t); +static int objdb_bl_dir_end(struct objdb_ctx *, struct objdb_dirhandle *, char **); +static int objdb_bl_branch_create(struct objdb_ctx *, const char *); +static int objdb_bl_branch_if_exists(struct objdb_ctx *, const char *, int *); +static int objdb_bl_branch_set_head(struct objdb_ctx *, const char *, const char *); +static int objdb_bl_branch_get_head(struct objdb_ctx *, const char *, char **); +static int objdb_bl_branch_ls(struct objdb_ctx *); + + +static const struct objdb_ops baseline_objdb_ops = { + .name = "baseline", + .version = "1.0", + .init = objdb_bl_init, + .open = objdb_bl_open, + .close = objdb_bl_close, + .insert = objdb_bl_insert, + .insert_from_file = objdb_bl_insert_from_file, + .remove = objdb_bl_remove, + .dir_begin = objdb_bl_dir_begin, + .dir_insert_object = objdb_bl_dir_insert_object, + .dir_end = objdb_bl_dir_end, + .branch_create = objdb_bl_branch_create, + .branch_if_exists = objdb_bl_branch_if_exists, + .branch_set_head = objdb_bl_branch_set_head, + .branch_get_head = objdb_bl_branch_get_head, + .branch_ls = objdb_bl_branch_ls, + .fsck = NULL, + .compress = NULL, + .dedup = NULL +}; + +#define N_MAINDIRS 5 +static const char *main_dirs[] = { + BASELINE_DIR "/" BASELINE_DB "/files", + BASELINE_DIR "/" BASELINE_DB "/dirs", + BASELINE_DIR "/" BASELINE_DB "/commits", + BASELINE_DIR "/" BASELINE_DB "/branches", + BASELINE_DIR "/" BASELINE_DB "/tags" +}; + +int +objdb_baseline_get_ops(struct objdb_ops **ops) +{ + if (ops == NULL) + return EXIT_FAILURE; + *ops = (struct objdb_ops *)malloc(sizeof(struct objdb_ops)); + memcpy(*ops, &baseline_objdb_ops, sizeof(struct objdb_ops)); + return EXIT_SUCCESS; +} + +/* FIXME */ +static char * +get_objdb_dir(struct objdb_ctx *ctx) +{ + char *db_dir_name = NULL; + int len; + if (ctx == NULL) + return NULL; + /* malloc() + strlcpy() + strlcat() = asprintf() */ + len = asprintf(&db_dir_name, "%s/%s", ctx->db_path, ctx->db_name); + return db_dir_name; +} + +static int +mkdir_ifn_exists(const char *path) +{ + struct stat s; + /* TODO: check if path is writable */ + if (stat(path, &s) == 0) { + if (S_ISDIR(s.st_mode)) + return EXIT_SUCCESS; + return EXIT_FAILURE; + } + if (mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR) == 0) + return EXIT_SUCCESS; + return EXIT_FAILURE; +} + +static int +objdb_bl_open(struct objdb_ctx **ctx, const char *db_name, const char *db_path) +{ + if (ctx == NULL) + return EXIT_FAILURE; + *ctx = (struct objdb_ctx *)malloc(sizeof(struct objdb_ctx)); + asprintf(&((*ctx)->db_name), "%s", db_name); + asprintf(&((*ctx)->db_path), "%s", db_path); + asprintf(&((*ctx)->db_version), "1.0"); + return EXIT_SUCCESS; +} + +static int +objdb_bl_close(struct objdb_ctx *ctx) +{ + if (ctx == NULL) + return EXIT_FAILURE; + free(ctx->db_name); + free(ctx->db_path); + free(ctx->db_version); + free(ctx); + return EXIT_SUCCESS; +} + +static int +objdb_bl_init(struct objdb_ctx *ctx) +{ + char *db_dir_name; + int i, retval; + struct stat s; + retval = EXIT_FAILURE; + db_dir_name = NULL; + /* check if path exists */ + if (stat(ctx->db_path, &s) == -1) + goto ret; + db_dir_name = get_objdb_dir(ctx); + if (mkdir_ifn_exists(db_dir_name) == EXIT_FAILURE) + goto ret; + /* try to create main sub-dirs as well */ + for (i=0 ; i<N_MAINDIRS ; i++) { + if (mkdir_ifn_exists(main_dirs[i]) == EXIT_FAILURE) + goto ret; + } +ret: + if(db_dir_name != NULL) + free(db_dir_name); + return retval; +} + +static int +objdb_bl_insert(struct objdb_ctx *ctx, enum objdb_type type, const u_int8_t *data, char **obj_id) +{ + return EXIT_SUCCESS; +} + +static int +objdb_bl_insert_from_file(struct objdb_ctx *ctx, enum objdb_type type, const char *file_name, char **obj_id) +{ + char *db_dir_name, *full_path, *obj_file_name, *obj_hash, *ptr, *tmp_file_name; + int count, i, len, retval, tmpfd; + FILE *fp, *tmpfp; + SHA2_CTX hash_ctx; + struct stat s; + u_int8_t buf[4096], digest[SHA256_DIGEST_LENGTH]; + + if (file_name == NULL) { + retval = EXIT_FAILURE; + goto ret; + } + if ((fp = fopen(file_name, "r")) == NULL) { + retval = EXIT_FAILURE; + goto ret; + } + db_dir_name = get_objdb_dir(ctx); + if (db_dir_name == NULL) { + retval = EXIT_FAILURE; + goto ret; + } + /* check object's type */ + if (type == T_FILE) { + /* malloc() + strlcpy() + strlcat() = asprintf() */ + len = asprintf(&full_path, "%s/%s", db_dir_name, "files"); + } + else if (type == T_DIR) { + len = asprintf(&full_path, "%s/%s", db_dir_name, "dirs"); + } + else if (type == T_COMMIT) { + len = asprintf(&full_path, "%s/%s", db_dir_name, "commits"); + } + else { + len = asprintf(&full_path, "%s", db_dir_name); + } + +#ifdef DEBUG + printf("[DEBUG] DB: checking for path = %s\n", full_path); +#endif + /* check if path exists */ + if (stat(full_path, &s) != 0) { +#ifdef DEBUG + printf("\t[DEBUG] DB: creating new dir %s\n", full_path); +#endif + if (mkdir(full_path, S_IRUSR | S_IWUSR | S_IXUSR) != 0) { + retval = EXIT_FAILURE; + goto ret; + } + } + else { + if (!S_ISDIR(s.st_mode)) { + retval = EXIT_FAILURE; + goto ret; + } + } + /* create a temp file */ + /* malloc() + strlcpy() + strlcat() = asprintf() */ + len = asprintf(&tmp_file_name, "%s/tmp.XXXXXX", full_path); + if ((tmpfd = mkstemp(tmp_file_name)) == -1) { + retval = EXIT_FAILURE; + goto ret; + } + if ((tmpfp = fdopen(tmpfd, "w")) == NULL) { + unlink(tmp_file_name); + close(tmpfd); + retval = EXIT_FAILURE; + goto ret; + } + SHA256Init(&hash_ctx); + do { + count = fread(buf, sizeof(u_int8_t), sizeof(buf), fp); + fwrite(buf, sizeof(u_int8_t), count, tmpfp); /* faster for new a file, but slower if the file already exist! */ + SHA256Update(&hash_ctx, buf, count); + } while (!feof(fp)); + SHA256Final(digest, &hash_ctx); + fclose(fp); + fclose(tmpfp); + /* TODO: think of something safer */ + obj_hash = (char *)malloc(SHA256_DIGEST_LENGTH * 2 + 1); + for (i=0, ptr=obj_hash ; i<SHA256_DIGEST_LENGTH ; i++, ptr+=2) { + snprintf(ptr, 3, "%02x", digest[i]); + } + /* malloc() + strlcpy() + strlcat() = asprintf() */ + asprintf(&obj_file_name, "%s/%s", full_path, obj_hash); +#ifdef DEBUG + printf("[DEBUG] DB: object file name = %s\n", obj_file_name); +#endif + /* check if file is already there */ + if (access(obj_file_name, F_OK) != -1) { + unlink(tmp_file_name); + goto success; + } + if (rename(tmp_file_name, obj_file_name)) { + retval = EXIT_FAILURE; + goto ret; + } + +success: + *obj_id = obj_hash; + retval = EXIT_SUCCESS; +ret: + /* assuming free(NULL) is safe */ + free(db_dir_name); + free(full_path); + free(obj_file_name); + free(tmp_file_name); + return retval; +} + +static int +objdb_bl_remove(struct objdb_ctx *ctx, const char *group_name, const char *obj_name) +{ + return EXIT_SUCCESS; +} + +static int +objdb_bl_dir_begin(struct objdb_ctx *ctx, struct objdb_dirhandle **handle) +{ + char *db_dir_name; + char *tmp_file_name; + int len, tmpfd; + FILE *tmpfp; + db_dir_name = get_objdb_dir(ctx); + /* malloc() + strlcpy() + strlcat() = asprintf() */ + len = asprintf(&tmp_file_name, "%s/tmp.XXXXXX", db_dir_name); + if ((tmpfd = mkstemp(tmp_file_name)) == -1) { + return EXIT_FAILURE; + } + if ((tmpfp = fdopen(tmpfd, "w")) == NULL) { + unlink(tmp_file_name); + close(tmpfd); + return EXIT_FAILURE; + } + *handle = (struct objdb_dirhandle *)malloc(sizeof(struct objdb_dirhandle)); + (*handle)->id = tmpfd; + asprintf(&((*handle)->name), "%s", tmp_file_name); + free(db_dir_name); + free(tmp_file_name); + return EXIT_SUCCESS; +} + +static int +objdb_bl_dir_insert_object(struct objdb_ctx *ctx, struct objdb_dirhandle *handle, const char *objid, const char *name, enum objdb_type type, mode_t mode) +{ + char *entry; + int len; + /* TODO: check if obj exists! */ + if (type == T_FILE) { + /* FIXME: non-portable code */ + len = asprintf(&entry, "%c %s %o %s\n", 'F', objid, mode, name); + write(handle->id, entry, len); + } + else if (type == T_DIR) { + /* FIXME: non-portable code */ + len = asprintf(&entry, "%c %s %o %s\n", 'D', objid, mode, name); + write(handle->id, entry, len); + } + return EXIT_SUCCESS; +} + +static int +objdb_bl_dir_end(struct objdb_ctx *ctx, struct objdb_dirhandle *handle, char **objid) +{ + enum objdb_type type = T_DIR; + int retval; + close(handle->id); + retval = objdb_bl_insert_from_file(ctx, type, handle->name, objid); + if (retval == EXIT_SUCCESS) + unlink(handle->name); + free(handle->name); + free(handle); + return retval; +} + +static int +objdb_bl_branch_create(struct objdb_ctx *ctx, const char *branch_name) +{ + char *db_dir_name = NULL; + char *branch_path = NULL; + int retval = EXIT_SUCCESS; + + if (branch_name == NULL) + return EXIT_FAILURE; + /* FIXME: validate branch name & check if already exists */ + db_dir_name = get_objdb_dir(ctx); + asprintf(&branch_path, "%s/branches/%s", db_dir_name, branch_name); +#ifdef DEBUG + printf("creating branch %s\n", branch_path); +#endif + if (mkdir(branch_path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) + retval = EXIT_FAILURE; + free(db_dir_name); + free(branch_path); + return retval; +} + +static int +objdb_bl_branch_if_exists(struct objdb_ctx *ctx, const char *branch_name, int *exist) +{ + char *db_dir_name = NULL; + char *branch_path = NULL; + int retval; + struct stat s; + + if (branch_name == NULL) + return EXIT_FAILURE; + *exist = 0; + db_dir_name = get_objdb_dir(ctx); + asprintf(&branch_path, "%s/branches/%s", db_dir_name, branch_name); + /* TODO: check if path is writable */ + if (stat(branch_path, &s) == 0) { + if (S_ISDIR(s.st_mode)) { + *exist = 1; + retval = EXIT_SUCCESS; + } + else { + *exist = 0; + retval = EXIT_FAILURE; + } + } + else { + *exist = 0; + retval = EXIT_SUCCESS; + } + free(db_dir_name); + free(branch_path); + return retval; +} + +static int +objdb_bl_branch_set_head(struct objdb_ctx *ctx, const char *branch_name, const char *head_objid) +{ + char *db_dir_name = NULL; + char *branch_head = NULL; + int retval = EXIT_SUCCESS; + FILE *head_fp; + db_dir_name = get_objdb_dir(ctx); + asprintf(&branch_head, "%s/branches/%s/head", db_dir_name, branch_name); + if ((head_fp = fopen(branch_head, "w")) == NULL) { + retval = EXIT_FAILURE; + goto ret; + } + fprintf(head_fp, "%s", head_objid); + fclose(head_fp); +ret: + free(db_dir_name); + free(branch_head); + return retval; +} + +static int +objdb_bl_branch_get_head(struct objdb_ctx *ctx, const char *branch_name, char **head_objid) +{ + char *db_dir_name = NULL; + char *branch_head = NULL; + int retval = EXIT_SUCCESS; + size_t size = 0; + FILE *head_fp; + db_dir_name = get_objdb_dir(ctx); + asprintf(&branch_head, "%s/branches/%s/head", db_dir_name, branch_name); + if ((head_fp = fopen(branch_head, "r")) == NULL) { + retval = EXIT_FAILURE; + goto ret; + } + if (getline(head_objid, &size, head_fp) == -1) { + retval = EXIT_FAILURE; + goto ret; + } + fclose(head_fp); +ret: + free(db_dir_name); + free(branch_head); + return retval; +} + +static int +objdb_bl_branch_ls(struct objdb_ctx *ctx) +{ + char *branches_path, *db_dir_name; + char *paths[2]; + FTS *dir; + FTSENT *entry; + + db_dir_name = get_objdb_dir(ctx); + asprintf(&branches_path, "%s/branches", db_dir_name); + + paths[0] = (char *)branches_path; + paths[1] = NULL; + if ((dir = fts_open(paths, FTS_NOCHDIR, 0)) == NULL) + return EXIT_FAILURE; + while ((entry = fts_read(dir)) != NULL) { + if (entry->fts_level == FTS_ROOTLEVEL) + continue; + if (entry->fts_info & FTS_D) { + printf("%s\n", entry->fts_name); + } + else { + if (entry->fts_level >= 1) + fts_set(dir, entry, FTS_SKIP); + } + } + fts_close(dir); + free(db_dir_name); + free(branches_path); + return EXIT_SUCCESS; +} + diff --git a/objdb.h b/objdb.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014 Mohamed Aslan <maslan@sce.carleton.ca> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _OBJDB_H_ +#define _OBJDB_H_ + +#include <sys/types.h> + +enum objdb_type { + T_FILE, + T_DIR, + T_COMMIT, +}; + +struct objdb_dirhandle { + int id; + /* optional */ + char *name; +}; + +struct objdb_ctx { + char *db_name; + char *db_path; + char *db_version; +}; + +struct objdb_ops { + char *name; + char *version; + /**/ + int (*open)(struct objdb_ctx **, const char *, const char *); + int (*close)(struct objdb_ctx *); + int (*init)(struct objdb_ctx *); + /* file ops*/ + int (*insert)(struct objdb_ctx *, enum objdb_type, const u_int8_t *, char **); + int (*insert_from_file)(struct objdb_ctx *, enum objdb_type, const char *, char **); + /* dir ops */ + int (*dir_begin)(struct objdb_ctx *, struct objdb_dirhandle **); + int (*dir_insert_object)(struct objdb_ctx *, struct objdb_dirhandle *, const char *, const char *, enum objdb_type, mode_t); + int (*dir_end)(struct objdb_ctx *, struct objdb_dirhandle *, char **); + /* branch ops */ + int (*branch_create)(struct objdb_ctx *, const char *); + int (*branch_if_exists)(struct objdb_ctx *, const char *, int *); + int (*branch_set_head)(struct objdb_ctx *, const char *, const char *); + int (*branch_get_head)(struct objdb_ctx *, const char *, char **); + int (*branch_ls)(struct objdb_ctx *); + /**/ + int (*remove)(struct objdb_ctx *, const char *, const char *); + int (*fsck)(struct objdb_ctx *); + int (*compress)(struct objdb_ctx *); + int (*dedup)(struct objdb_ctx *); +}; + +#endif