Commit 3c45a30aebce27db2fb0f5b453695260456bacc9

joshua stein 2019-05-12T14:56:24

checkout: if worktree path exists and is empty, suppress mkdir failure

diff --git a/got/got.c b/got/got.c
index bc14b93..3d3ea43 100644
--- a/got/got.c
+++ b/got/got.c
@@ -19,6 +19,7 @@
 #include <sys/limits.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/param.h>
 
 #include <err.h>
 #include <errno.h>
@@ -197,10 +198,25 @@ apply_unveil(const char *repo_path, int repo_read_only,
     const char *worktree_path, int create_worktree)
 {
 	const struct got_error *error;
+	static char err_msg[MAXPATHLEN + 36];
 
 	if (create_worktree) {
 		/* Pre-create work tree path to avoid unveiling its parents. */
 		error = got_path_mkdir(worktree_path);
+
+		if (errno == EEXIST) {
+			if (got_dir_is_empty(worktree_path)) {
+				errno = 0;
+				error = NULL;
+			} else {
+				snprintf(err_msg, sizeof(err_msg),
+				    "%s: directory exists but is not empty",
+				    worktree_path);
+				error = got_error_msg(GOT_ERR_BAD_PATH,
+				    err_msg);
+			}
+		}
+
 		if (error && (error->code != GOT_ERR_ERRNO || errno != EISDIR))
 			return error;
 	}
diff --git a/include/got_path.h b/include/got_path.h
index e4dc262..b80b464 100644
--- a/include/got_path.h
+++ b/include/got_path.h
@@ -90,6 +90,9 @@ void got_pathlist_free(struct got_pathlist_head *);
 /* Attempt to create a directory at a given path. */
 const struct got_error *got_path_mkdir(const char *);
 
+/* Determine whether a directory has no files or directories in it. */
+int got_dir_is_empty(const char *);
+
 /* dirname(3) with error handling and dynamically allocated result. */
 const struct got_error *got_path_dirname(char **, const char *);
 
diff --git a/lib/path.c b/lib/path.c
index 3cbf1e5..abd5d7f 100644
--- a/lib/path.c
+++ b/lib/path.c
@@ -25,6 +25,7 @@
 #include <unistd.h>
 #include <stdio.h>
 #include <string.h>
+#include <dirent.h>
 
 #include "got_error.h"
 #include "got_path.h"
@@ -325,6 +326,29 @@ done:
 	return err;
 }
 
+int
+got_dir_is_empty(const char *dir)
+{
+	DIR *d;
+	struct dirent *dent;
+	int empty = 1;
+
+	d = opendir(dir);
+	if (d == NULL)
+		return 1;
+
+	while ((dent = readdir(d)) != NULL) {
+		if (strcmp(dent->d_name, ".") == 0 ||
+		    strcmp(dent->d_name, "..") == 0)
+			continue;
+
+		empty = 0;
+		break;
+	}
+
+	return empty;
+}
+
 const struct got_error *
 got_path_dirname(char **parent, const char *path)
 {