Commit 6d9d28c32eec189b3f26c36f5510c7c4513c8ffd

Stefan Sperling 2018-03-11T02:03:45

implement worktree open and close operations

diff --git a/include/got_error.h b/include/got_error.h
index cd99056..282259e 100644
--- a/include/got_error.h
+++ b/include/got_error.h
@@ -41,6 +41,8 @@
 #define GOT_ERR_BAD_OBJ_ID_STR	23
 #define GOT_ERR_WORKTREE_EXISTS	26
 #define GOT_ERR_WORKTREE_META	27
+#define GOT_ERR_WORKTREE_VERS	28
+#define GOT_ERR_WORKTREE_BUSY	29
 
 static const struct got_error {
 	int code;
@@ -72,6 +74,8 @@ static const struct got_error {
 	{ GOT_ERR_BAD_OBJ_ID_STR,"bad object id string" },
 	{ GOT_ERR_WORKTREE_EXISTS,"worktree already exists" },
 	{ GOT_ERR_WORKTREE_META,"bad worktree meta data" },
+	{ GOT_ERR_WORKTREE_VERS,"unsupported worktree format version" },
+	{ GOT_ERR_WORKTREE_BUSY,"worktree already locked" },
 };
 
 const struct got_error * got_error(int code);
diff --git a/lib/got_worktree_priv.h b/lib/got_worktree_priv.h
index 39417fc..bf96dcd 100644
--- a/lib/got_worktree_priv.h
+++ b/lib/got_worktree_priv.h
@@ -18,6 +18,12 @@ struct got_worktree {
 	char *path_worktree_root;
 	char *path_repo;
 	char *path_prefix;
+
+	/*
+	 * This file descriptor exclusively locks GOT_WORKTREE_FILE_INDEX.
+	 * This ensures that only one process opens the work tree at a time.
+	 */
+	int fd_fileindex;
 };
 
 #define GOT_WORKTREE_GOT_DIR		".got"
diff --git a/lib/worktree.c b/lib/worktree.c
index 26980ac..5b44fb5 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -15,6 +15,7 @@
  */
 
 #include <sys/stat.h>
+#include <sys/limits.h>
 
 #include <string.h>
 #include <stdio.h>
@@ -207,12 +208,95 @@ done:
 const struct got_error *
 got_worktree_open(struct got_worktree **worktree, const char *path)
 {
-	return NULL;
+	const struct got_error *err = NULL;
+	char *gotpath;
+	char *refstr = NULL;
+	char *path_repos = NULL;
+	char *formatstr = NULL;
+	char *path_fileindex = NULL;
+	int version, fd = -1;
+	const char *errstr;
+
+	*worktree = NULL;
+
+	if (asprintf(&gotpath, "%s/%s", path, GOT_WORKTREE_GOT_DIR) == -1) {
+		err = got_error(GOT_ERR_NO_MEM);
+		gotpath = NULL;
+		goto done;
+	}
+
+	if (asprintf(&path_fileindex, "%s/%s", gotpath,
+	    GOT_WORKTREE_FILE_INDEX) == -1) {
+		err = got_error(GOT_ERR_NO_MEM);
+		path_fileindex = NULL;
+		goto done;
+	}
+
+	fd = open(path_fileindex, O_RDWR | O_EXCL | O_EXLOCK | O_NOFOLLOW,
+	    GOT_DEFAULT_FILE_MODE);
+	if (fd == -1) {
+		err = got_error(GOT_ERR_WORKTREE_BUSY);
+		goto done;
+	}
+
+	err = read_meta_file(&formatstr, gotpath, GOT_WORKTREE_FORMAT);
+	if (err)
+		goto done;
+
+	version = strtonum(formatstr, 1, INT_MAX, &errstr);
+	if (errstr) {
+		err = got_error(GOT_ERR_WORKTREE_META);
+		goto done;
+	}
+	if (version != GOT_WORKTREE_FORMAT_VERSION) {
+		err = got_error(GOT_ERR_WORKTREE_VERS);
+		goto done;
+	}
+
+	*worktree = calloc(1, sizeof(**worktree));
+	if (*worktree == NULL) {
+		err = got_error(GOT_ERR_NO_MEM);
+		goto done;
+	}
+	(*worktree)->fd_fileindex = -1;
+
+	(*worktree)->path_worktree_root = strdup(path);
+	if ((*worktree)->path_worktree_root == NULL) {
+		err = got_error(GOT_ERR_NO_MEM);
+		goto done;
+	}
+	err = read_meta_file(&(*worktree)->path_repo, gotpath,
+	    GOT_WORKTREE_REPOSITORY);
+	if (err)
+		goto done;
+	err = read_meta_file(&(*worktree)->path_prefix, gotpath,
+	    GOT_WORKTREE_PATH_PREFIX);
+		goto done;
+
+done:
+	free(gotpath);
+	free(path_fileindex);
+	if (err) {
+		if (fd != -1)
+			close(fd);
+		if (*worktree != NULL)
+			got_worktree_close(*worktree);
+		*worktree = NULL;
+	} else
+		(*worktree)->fd_fileindex = fd;
+
+	return err;
 }
 
 void
 got_worktree_close(struct got_worktree *worktree)
 {
+	free(worktree->path_worktree_root);
+	free(worktree->path_repo);
+	free(worktree->path_prefix);
+	if (worktree->fd_fileindex != -1)
+		close(worktree->fd_fileindex);
+	free(worktree);
 }
 
 char *