"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
Re: change got_worktree_init, open_worktree to use fds
To:
Yang Zhong <yzhong@freebsdfoundation.org>
Cc:
gameoftrees@openbsd.org
Date:
Wed, 9 Dec 2020 18:28:10 +0100

Download raw body.

Thread
On Wed, Dec 09, 2020 at 09:00:00AM -0800, Yang Zhong wrote:
> Here's the updated diff. With regard to committing, am I OK to
> commit the initial patch (the one that adds the fd field to got_worktree)
> at this point?

I didn't realize the new patch you posted wasn't self-contained.

You mean the initial patch quoted below?

My point of view on this is that you should not actually need most of
these changes if you call cap_enter() after got_worktree_open() and
got_repo_open() are done.
Adding a root_fd to struct got_worktree is fine, of course. I would suggest
to roll a new patch that is self-contained and doesn't assume that your
sandbox is active during got_worktree_init/open() and got_repo_open().

diff --git a/got/got.c b/got/got.c
index 0357368c..e8cfb93a 100644
--- a/got/got.c
+++ b/got/got.c
@@ -2602,11 +2602,13 @@ cmd_checkout(int argc, char *argv[])
 	struct got_worktree *worktree = NULL;
 	char *repo_path = NULL;
 	char *worktree_path = NULL;
+	char *worktree_abspath = NULL;
 	const char *path_prefix = "";
 	const char *branch_name = GOT_REF_HEAD;
 	char *commit_id_str = NULL;
 	char *cwd = NULL;
 	int ch, same_path_prefix, allow_nonempty = 0;
+	int worktree_fd = -1;
 	struct got_pathlist_head paths;
 	struct got_checkout_progress_arg cpa;
 
@@ -2711,6 +2713,20 @@ cmd_checkout(int argc, char *argv[])
 			goto done;
 		}
 	}
+	worktree_fd = open(worktree_path, O_DIRECTORY);
+	if (worktree_fd == -1) {
+		error = got_error_from_errno2("open", worktree_path);
+		goto done;
+	}
+
+	/* Make worktree path absolute */
+	worktree_abspath = realpath(worktree_path, NULL);
+	if (worktree_abspath == NULL) {
+		error = got_error_from_errno2("realpath", worktree_abspath);
+		goto done;
+	}
+	free(worktree_path);
+	worktree_path = worktree_abspath;
 
 	error = apply_unveil(got_repo_get_path(repo), 0, worktree_path);
 	if (error)
@@ -2720,7 +2736,7 @@ cmd_checkout(int argc, char *argv[])
 	if (error != NULL)
 		goto done;
 
-	error = got_worktree_init(worktree_path, head_ref, path_prefix, repo);
+	error = got_worktree_init(worktree_fd, worktree_path, head_ref, path_prefix, repo);
 	if (error != NULL && !(error->code == GOT_ERR_ERRNO && errno == EEXIST))
 		goto done;
 
diff --git a/include/got_path.h b/include/got_path.h
index 0219d3d3..04558384 100644
--- a/include/got_path.h
+++ b/include/got_path.h
@@ -132,3 +132,4 @@ const struct got_error *got_path_find_prog(char **, const char *);
 
 /* Create a new file at a specified path, with optional content. */
 const struct got_error *got_path_create_file(const char *, const char *);
+const struct got_error *got_path_create_fileat(int, const char *, const char *);
diff --git a/include/got_worktree.h b/include/got_worktree.h
index 24fddd52..13b13521 100644
--- a/include/got_worktree.h
+++ b/include/got_worktree.h
@@ -42,12 +42,11 @@ struct got_fileindex;
 /*
  * Attempt to initialize a new work tree on disk.
  * The first argument is the path to a directory where the work tree
- * will be created. The path itself must not yet exist, but the dirname(3)
- * of the path must already exist.
+ * will be created. The dirname(3) of the path must already exist.
  * The reference provided will be used to determine the new worktree's
  * base commit. The third argument speficies the work tree's path prefix.
  */
-const struct got_error *got_worktree_init(const char *, struct got_reference *,
+const struct got_error *got_worktree_init(int, const char *, struct got_reference *,
     const char *, struct got_repository *);
 
 /*
diff --git a/lib/got_lib_worktree.h b/lib/got_lib_worktree.h
index 067bac22..651bcc8f 100644
--- a/lib/got_lib_worktree.h
+++ b/lib/got_lib_worktree.h
@@ -17,6 +17,7 @@
 struct got_worktree {
 	char *root_path;
 	char *repo_path;
+	int root_fd;
 	char *path_prefix;
 	struct got_object_id *base_commit_id;
 	char *head_ref_name;
diff --git a/lib/path.c b/lib/path.c
index a670334c..655304a0 100644
--- a/lib/path.c
+++ b/lib/path.c
@@ -513,11 +513,17 @@ got_path_find_prog(char **filename, const char *prog)
 
 const struct got_error *
 got_path_create_file(const char *path, const char *content)
+{
+	return got_path_create_fileat(AT_FDCWD, path, content);
+}
+
+const struct got_error *
+got_path_create_fileat(int dir_fd, const char *path, const char *content)
 {
 	const struct got_error *err = NULL;
 	int fd = -1;
 
-	fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW,
+	fd = openat(dir_fd, path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW,
 	    GOT_DEFAULT_FILE_MODE);
 	if (fd == -1) {
 		err = got_error_from_errno2("open", path);
diff --git a/lib/worktree.c b/lib/worktree.c
index 2e265b00..8bf4b7ff 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -67,15 +67,15 @@
 #define GOT_MERGE_LABEL_BASE	"3-way merge base"
 
 static const struct got_error *
-create_meta_file(const char *path_got, const char *name, const char *content)
+create_meta_file(int worktree_fd, const char *name, const char *content)
 {
 	const struct got_error *err = NULL;
 	char *path;
 
-	if (asprintf(&path, "%s/%s", path_got, name) == -1)
+	if (asprintf(&path, "%s/%s", GOT_WORKTREE_GOT_DIR, name) == -1)
 		return got_error_from_errno("asprintf");
 
-	err = got_path_create_file(path, content);
+	err = got_path_create_fileat(worktree_fd, path, content);
 	free(path);
 	return err;
 }
@@ -120,7 +120,7 @@ done:
 }
 
 static const struct got_error *
-read_meta_file(char **content, const char *path_got, const char *name)
+read_meta_file(char **content, int worktree_fd, const char *path_got, const char *name)
 {
 	const struct got_error *err = NULL;
 	char *path;
@@ -136,7 +136,7 @@ read_meta_file(char **content, const char *path_got, const char *name)
 		goto done;
 	}
 
-	fd = open(path, O_RDONLY | O_NOFOLLOW);
+	fd = openat(worktree_fd, path, O_RDONLY | O_NOFOLLOW);
 	if (fd == -1) {
 		if (errno == ENOENT)
 			err = got_error_path(path, GOT_ERR_WORKTREE_META);
@@ -204,7 +204,7 @@ write_head_ref(const char *path_got, struct got_reference *head_ref)
 }
 
 const struct got_error *
-got_worktree_init(const char *path, struct got_reference *head_ref,
+got_worktree_init(int fd, const char *path, struct got_reference *head_ref,
     const char *prefix, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
@@ -237,29 +237,23 @@ got_worktree_init(const char *path, struct got_reference *head_ref,
 			return got_error_from_errno("asprintf");
 	}
 
-	/* Create top-level directory (may already exist). */
-	if (mkdir(path, GOT_DEFAULT_DIR_MODE) == -1 && errno != EEXIST) {
-		err = got_error_from_errno2("mkdir", path);
-		goto done;
-	}
-
 	/* Create .got directory (may already exist). */
 	if (asprintf(&path_got, "%s/%s", path, GOT_WORKTREE_GOT_DIR) == -1) {
 		err = got_error_from_errno("asprintf");
 		goto done;
 	}
-	if (mkdir(path_got, GOT_DEFAULT_DIR_MODE) == -1 && errno != EEXIST) {
+	if (mkdirat(fd, GOT_WORKTREE_GOT_DIR, GOT_DEFAULT_DIR_MODE) == -1 && errno != EEXIST) {
 		err = got_error_from_errno2("mkdir", path_got);
 		goto done;
 	}
 
 	/* Create an empty lock file. */
-	err = create_meta_file(path_got, GOT_WORKTREE_LOCK, NULL);
+	err = create_meta_file(fd, GOT_WORKTREE_LOCK, NULL);
 	if (err)
 		goto done;
 
 	/* Create an empty file index. */
-	err = create_meta_file(path_got, GOT_WORKTREE_FILE_INDEX, NULL);
+	err = create_meta_file(fd, GOT_WORKTREE_FILE_INDEX, NULL);
 	if (err)
 		goto done;
 
@@ -272,18 +266,18 @@ got_worktree_init(const char *path, struct got_reference *head_ref,
 	err = got_object_id_str(&basestr, commit_id);
 	if (err)
 		goto done;
-	err = create_meta_file(path_got, GOT_WORKTREE_BASE_COMMIT, basestr);
+	err = create_meta_file(fd, GOT_WORKTREE_BASE_COMMIT, basestr);
 	if (err)
 		goto done;
 
 	/* Store path to repository. */
-	err = create_meta_file(path_got, GOT_WORKTREE_REPOSITORY,
+	err = create_meta_file(fd, GOT_WORKTREE_REPOSITORY,
 	    got_repo_get_path(repo));
 	if (err)
 		goto done;
 
 	/* Store in-repository path prefix. */
-	err = create_meta_file(path_got, GOT_WORKTREE_PATH_PREFIX,
+	err = create_meta_file(fd, GOT_WORKTREE_PATH_PREFIX,
 	    absprefix ? absprefix : prefix);
 	if (err)
 		goto done;
@@ -299,7 +293,7 @@ got_worktree_init(const char *path, struct got_reference *head_ref,
 		err = got_error_uuid(uuid_status, "uuid_to_string");
 		goto done;
 	}
-	err = create_meta_file(path_got, GOT_WORKTREE_UUID, uuidstr);
+	err = create_meta_file(fd, GOT_WORKTREE_UUID, uuidstr);
 	if (err)
 		goto done;
 
@@ -308,7 +302,7 @@ got_worktree_init(const char *path, struct got_reference *head_ref,
 		err = got_error_from_errno("asprintf");
 		goto done;
 	}
-	err = create_meta_file(path_got, GOT_WORKTREE_FORMAT, formatstr);
+	err = create_meta_file(fd, GOT_WORKTREE_FORMAT, formatstr);
 	if (err)
 		goto done;
 
@@ -323,10 +317,9 @@ done:
 }
 
 static const struct got_error *
-open_worktree(struct got_worktree **worktree, const char *path)
+open_worktree(struct got_worktree **worktree, int worktree_fd, const char *path)
 {
 	const struct got_error *err = NULL;
-	char *path_got;
 	char *formatstr = NULL;
 	char *uuidstr = NULL;
 	char *path_lock = NULL;
@@ -338,26 +331,20 @@ open_worktree(struct got_worktree **worktree, const char *path)
 
 	*worktree = NULL;
 
-	if (asprintf(&path_got, "%s/%s", path, GOT_WORKTREE_GOT_DIR) == -1) {
-		err = got_error_from_errno("asprintf");
-		path_got = NULL;
-		goto done;
-	}
-
-	if (asprintf(&path_lock, "%s/%s", path_got, GOT_WORKTREE_LOCK) == -1) {
+	if (asprintf(&path_lock, "%s/%s", GOT_WORKTREE_GOT_DIR, GOT_WORKTREE_LOCK) == -1) {
 		err = got_error_from_errno("asprintf");
 		path_lock = NULL;
 		goto done;
 	}
 
-	fd = open(path_lock, O_RDWR | O_EXLOCK | O_NONBLOCK);
+	fd = openat(worktree_fd, path_lock, O_RDWR | O_EXLOCK | O_NONBLOCK);
 	if (fd == -1) {
 		err = (errno == EWOULDBLOCK ? got_error(GOT_ERR_WORKTREE_BUSY)
 		    : got_error_from_errno2("open", path_lock));
 		goto done;
 	}
 
-	err = read_meta_file(&formatstr, path_got, GOT_WORKTREE_FORMAT);
+	err = read_meta_file(&formatstr, worktree_fd, GOT_WORKTREE_GOT_DIR, GOT_WORKTREE_FORMAT);
 	if (err)
 		goto done;
 
@@ -379,27 +366,27 @@ open_worktree(struct got_worktree **worktree, const char *path)
 	}
 	(*worktree)->lockfd = -1;
 
-	(*worktree)->root_path = realpath(path, NULL);
+	(*worktree)->root_path = strdup(path);
 	if ((*worktree)->root_path == NULL) {
-		err = got_error_from_errno2("realpath", path);
+		err = got_error_from_errno2("strdup", path);
 		goto done;
 	}
-	err = read_meta_file(&(*worktree)->repo_path, path_got,
+	err = read_meta_file(&(*worktree)->repo_path, worktree_fd, GOT_WORKTREE_GOT_DIR,
 	    GOT_WORKTREE_REPOSITORY);
 	if (err)
 		goto done;
 
-	err = read_meta_file(&(*worktree)->path_prefix, path_got,
+	err = read_meta_file(&(*worktree)->path_prefix, worktree_fd, GOT_WORKTREE_GOT_DIR,
 	    GOT_WORKTREE_PATH_PREFIX);
 	if (err)
 		goto done;
 
-	err = read_meta_file(&base_commit_id_str, path_got,
+	err = read_meta_file(&base_commit_id_str, worktree_fd, GOT_WORKTREE_GOT_DIR,
 	    GOT_WORKTREE_BASE_COMMIT);
 	if (err)
 		goto done;
 
-	err = read_meta_file(&uuidstr, path_got, GOT_WORKTREE_UUID);
+	err = read_meta_file(&uuidstr, worktree_fd, GOT_WORKTREE_GOT_DIR, GOT_WORKTREE_UUID);
 	if (err)
 		goto done;
 	uuid_from_string(uuidstr, &(*worktree)->uuid, &uuid_status);
@@ -417,7 +404,7 @@ open_worktree(struct got_worktree **worktree, const char *path)
 	if (err)
 		goto done;
 
-	err = read_meta_file(&(*worktree)->head_ref_name, path_got,
+	err = read_meta_file(&(*worktree)->head_ref_name, worktree_fd, GOT_WORKTREE_GOT_DIR,
 	    GOT_WORKTREE_HEAD_REF);
 	if (err)
 		goto done;
@@ -431,10 +418,11 @@ open_worktree(struct got_worktree **worktree, const char *path)
 
 	err = got_gotconfig_read(&(*worktree)->gotconfig,
 	    (*worktree)->gotconfig_path);
+
+	(*worktree)->root_fd = worktree_fd;
 done:
 	if (repo)
 		got_repo_close(repo);
-	free(path_got);
 	free(path_lock);
 	free(base_commit_id_str);
 	free(uuidstr);
@@ -456,15 +444,20 @@ got_worktree_open(struct got_worktree **worktree, const char *path)
 {
 	const struct got_error *err = NULL;
 	char *worktree_path;
+	int worktree_fd;
 
 	worktree_path = strdup(path);
 	if (worktree_path == NULL)
 		return got_error_from_errno("strdup");
 
+	worktree_fd = open(worktree_path, O_DIRECTORY);
+	if (worktree_fd == -1)
+		return got_error_from_errno2("open", worktree_path);
+
 	for (;;) {
 		char *parent_path;
 
-		err = open_worktree(worktree, worktree_path);
+		err = open_worktree(worktree, worktree_fd, worktree_path);
 		if (err && !(err->code == GOT_ERR_ERRNO && errno == ENOENT)) {
 			free(worktree_path);
 			return err;
@@ -484,7 +477,9 @@ got_worktree_open(struct got_worktree **worktree, const char *path)
 			break;
 		}
 		free(worktree_path);
+		close(worktree_fd);
 		worktree_path = parent_path;
+		worktree_fd = open(parent_path, O_DIRECTORY);
 	}
 
 	free(worktree_path);
@@ -507,6 +502,7 @@ got_worktree_close(struct got_worktree *worktree)
 	free(worktree->gotconfig_path);
 	got_gotconfig_free(worktree->gotconfig);
 	free(worktree);
+	close(worktree->root_fd);
 	return err;
 }