summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fileops.c122
-rw-r--r--src/fileops.h27
-rw-r--r--src/refs.c11
-rw-r--r--src/repo_template.h10
-rw-r--r--src/repository.c102
5 files changed, 184 insertions, 88 deletions
diff --git a/src/fileops.c b/src/fileops.c
index 6429e55b4..c1824e812 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -272,6 +272,10 @@ int git_futils_mkdir(
}
/* if we are not supposed to made the last element, truncate it */
+ if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) {
+ git_buf_rtruncate_at_char(&make_path, '/');
+ flags |= GIT_MKDIR_SKIP_LAST;
+ }
if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
git_buf_rtruncate_at_char(&make_path, '/');
@@ -303,34 +307,34 @@ int git_futils_mkdir(
int already_exists = 0;
switch (errno) {
- case EEXIST:
- if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
- !git_path_isdir(make_path.ptr)) {
- giterr_set(
- GITERR_OS, "Existing path is not a directory '%s'",
- make_path.ptr);
- error = GIT_ENOTFOUND;
- goto fail;
- }
-
- already_exists = 1;
- break;
- case ENOSYS:
- /* Solaris can generate this error if you try to mkdir
- * a path which is already a mount point. In that case,
- * the path does already exist; but it's not implied by
- * the definition of the error, so let's recheck */
- if (git_path_isdir(make_path.ptr)) {
- already_exists = 1;
- break;
- }
-
- /* Fall through */
- errno = ENOSYS;
- default:
- giterr_set(GITERR_OS, "Failed to make directory '%s'",
+ case EEXIST:
+ if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
+ !git_path_isdir(make_path.ptr)) {
+ giterr_set(
+ GITERR_OS, "Existing path is not a directory '%s'",
make_path.ptr);
+ error = GIT_ENOTFOUND;
goto fail;
+ }
+
+ already_exists = 1;
+ break;
+ case ENOSYS:
+ /* Solaris can generate this error if you try to mkdir
+ * a path which is already a mount point. In that case,
+ * the path does already exist; but it's not implied by
+ * the definition of the error, so let's recheck */
+ if (git_path_isdir(make_path.ptr)) {
+ already_exists = 1;
+ break;
+ }
+
+ /* Fall through */
+ errno = ENOSYS;
+ default:
+ giterr_set(GITERR_OS, "Failed to make directory '%s'",
+ make_path.ptr);
+ goto fail;
}
if (already_exists && (flags & GIT_MKDIR_EXCL) != 0) {
@@ -714,8 +718,33 @@ typedef struct {
mode_t dirmode;
} cp_r_info;
+#define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10)
+
+static int _cp_r_mkdir(cp_r_info *info, git_buf *from)
+{
+ int error = 0;
+
+ /* create root directory the first time we need to create a directory */
+ if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) {
+ error = git_futils_mkdir(
+ info->to_root, NULL, info->dirmode,
+ (info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0);
+
+ info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT;
+ }
+
+ /* create directory with root as base to prevent excess chmods */
+ if (!error)
+ error = git_futils_mkdir(
+ from->ptr + info->from_prefix, info->to_root,
+ info->dirmode, info->mkdir_flags);
+
+ return error;
+}
+
static int _cp_r_callback(void *ref, git_buf *from)
{
+ int error = 0;
cp_r_info *info = ref;
struct stat from_st, to_st;
bool exists = false;
@@ -737,24 +766,22 @@ static int _cp_r_callback(void *ref, git_buf *from)
} else
exists = true;
- if (git_path_lstat(from->ptr, &from_st) < 0)
- return -1;
+ if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
+ return error;
if (S_ISDIR(from_st.st_mode)) {
- int error = 0;
mode_t oldmode = info->dirmode;
/* if we are not chmod'ing, then overwrite dirmode */
- if ((info->flags & GIT_CPDIR_CHMOD) == 0)
+ if ((info->flags & GIT_CPDIR_CHMOD_DIRS) == 0)
info->dirmode = from_st.st_mode;
/* make directory now if CREATE_EMPTY_DIRS is requested and needed */
if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0)
- error = git_futils_mkdir(
- info->to.ptr, NULL, info->dirmode, info->mkdir_flags);
+ error = _cp_r_mkdir(info, from);
/* recurse onto target directory */
- if (!exists || S_ISDIR(to_st.st_mode))
+ if (!error && (!exists || S_ISDIR(to_st.st_mode)))
error = git_path_direach(from, _cp_r_callback, info);
if (oldmode != 0)
@@ -782,15 +809,22 @@ static int _cp_r_callback(void *ref, git_buf *from)
/* Make container directory on demand if needed */
if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
- git_futils_mkdir(
- info->to.ptr, NULL, info->dirmode, info->mkdir_flags) < 0)
- return -1;
+ (error = _cp_r_mkdir(info, from)) < 0)
+ return error;
/* make symlink or regular file */
if (S_ISLNK(from_st.st_mode))
- return cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
- else
- return git_futils_cp(from->ptr, info->to.ptr, from_st.st_mode);
+ error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
+ else {
+ mode_t usemode = from_st.st_mode;
+
+ if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
+ usemode = (usemode & 0111) ? 0777 : 0666;
+
+ error = git_futils_cp(from->ptr, info->to.ptr, usemode);
+ }
+
+ return error;
}
int git_futils_cp_r(
@@ -803,7 +837,7 @@ int git_futils_cp_r(
git_buf path = GIT_BUF_INIT;
cp_r_info info;
- if (git_buf_sets(&path, from) < 0)
+ if (git_buf_joinpath(&path, from, "") < 0) /* ensure trailing slash */
return -1;
info.to_root = to;
@@ -814,12 +848,16 @@ int git_futils_cp_r(
/* precalculate mkdir flags */
if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0) {
+ /* if not creating empty dirs, then use mkdir to create the path on
+ * demand right before files are copied.
+ */
info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST;
- if ((flags & GIT_CPDIR_CHMOD) != 0)
+ if ((flags & GIT_CPDIR_CHMOD_DIRS) != 0)
info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH;
} else {
+ /* otherwise, we will do simple mkdir as directories are encountered */
info.mkdir_flags =
- ((flags & GIT_CPDIR_CHMOD) != 0) ? GIT_MKDIR_CHMOD : 0;
+ ((flags & GIT_CPDIR_CHMOD_DIRS) != 0) ? GIT_MKDIR_CHMOD : 0;
}
error = _cp_r_callback(&info, &path);
diff --git a/src/fileops.h b/src/fileops.h
index f01f22706..7ba99d3d9 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -65,6 +65,7 @@ extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t m
* * GIT_MKDIR_CHMOD says to chmod the final directory entry after creation
* * GIT_MKDIR_CHMOD_PATH says to chmod each directory component in the path
* * GIT_MKDIR_SKIP_LAST says to leave off the last element of the path
+ * * GIT_MKDIR_SKIP_LAST2 says to leave off the last 2 elements of the path
* * GIT_MKDIR_VERIFY_DIR says confirm final item is a dir, not just EEXIST
*
* Note that the chmod options will be executed even if the directory already
@@ -76,7 +77,8 @@ typedef enum {
GIT_MKDIR_CHMOD = 4,
GIT_MKDIR_CHMOD_PATH = 8,
GIT_MKDIR_SKIP_LAST = 16,
- GIT_MKDIR_VERIFY_DIR = 32,
+ GIT_MKDIR_SKIP_LAST2 = 32,
+ GIT_MKDIR_VERIFY_DIR = 64,
} git_futils_mkdir_flags;
/**
@@ -168,13 +170,26 @@ extern int git_futils_cp(
/**
* Flags that can be passed to `git_futils_cp_r`.
+ *
+ * - GIT_CPDIR_CREATE_EMPTY_DIRS: create directories even if there are no
+ * files under them (otherwise directories will only be created lazily
+ * when a file inside them is copied).
+ * - GIT_CPDIR_COPY_SYMLINKS: copy symlinks, otherwise they are ignored.
+ * - GIT_CPDIR_COPY_DOTFILES: copy files with leading '.', otherwise ignored.
+ * - GIT_CPDIR_OVERWRITE: overwrite pre-existing files with source content,
+ * otherwise they are silently skipped.
+ * - GIT_CPDIR_CHMOD_DIRS: explicitly chmod directories to `dirmode`
+ * - GIT_CPDIR_SIMPLE_TO_MODE: default tries to replicate the mode of the
+ * source file to the target; with this flag, always use 0666 (or 0777 if
+ * source has exec bits set) for target.
*/
typedef enum {
- GIT_CPDIR_CREATE_EMPTY_DIRS = 1,
- GIT_CPDIR_COPY_SYMLINKS = 2,
- GIT_CPDIR_COPY_DOTFILES = 4,
- GIT_CPDIR_OVERWRITE = 8,
- GIT_CPDIR_CHMOD = 16
+ GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0),
+ GIT_CPDIR_COPY_SYMLINKS = (1u << 1),
+ GIT_CPDIR_COPY_DOTFILES = (1u << 2),
+ GIT_CPDIR_OVERWRITE = (1u << 3),
+ GIT_CPDIR_CHMOD_DIRS = (1u << 4),
+ GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5),
} git_futils_cpdir_flags;
/**
diff --git a/src/refs.c b/src/refs.c
index cca3f3ec8..113cadad5 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -1599,7 +1599,8 @@ static int ensure_segment_validity(const char *name)
{
const char *current = name;
char prev = '\0';
- int lock_len = strlen(GIT_FILELOCK_EXTENSION);
+ const int lock_len = (int)strlen(GIT_FILELOCK_EXTENSION);
+ int segment_len;
if (*current == '.')
return -1; /* Refname starts with "." */
@@ -1620,12 +1621,14 @@ static int ensure_segment_validity(const char *name)
prev = *current;
}
+ segment_len = (int)(current - name);
+
/* A refname component can not end with ".lock" */
- if (current - name >= lock_len &&
+ if (segment_len >= lock_len &&
!memcmp(current - lock_len, GIT_FILELOCK_EXTENSION, lock_len))
return -1;
- return (int)(current - name);
+ return segment_len;
}
static bool is_all_caps_and_underscore(const char *name, size_t len)
@@ -1700,7 +1703,7 @@ int git_reference__normalize_name(
/* No empty segment is allowed when not normalizing */
if (segment_len == 0 && !normalize)
goto cleanup;
-
+
if (current[segment_len] == '\0')
break;
diff --git a/src/repo_template.h b/src/repo_template.h
index 90ffe851b..099279aa7 100644
--- a/src/repo_template.h
+++ b/src/repo_template.h
@@ -11,10 +11,10 @@
#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
#define GIT_HOOKS_DIR "hooks/"
-#define GIT_HOOKS_DIR_MODE 0755
+#define GIT_HOOKS_DIR_MODE 0777
#define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"
-#define GIT_HOOKS_README_MODE 0755
+#define GIT_HOOKS_README_MODE 0777
#define GIT_HOOKS_README_CONTENT \
"#!/bin/sh\n"\
"#\n"\
@@ -23,16 +23,16 @@
"# more information.\n"
#define GIT_INFO_DIR "info/"
-#define GIT_INFO_DIR_MODE 0755
+#define GIT_INFO_DIR_MODE 0777
#define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"
-#define GIT_INFO_EXCLUDE_MODE 0644
+#define GIT_INFO_EXCLUDE_MODE 0666
#define GIT_INFO_EXCLUDE_CONTENT \
"# File patterns to ignore; see `git help ignore` for more information.\n"\
"# Lines that start with '#' are comments.\n"
#define GIT_DESC_FILE "description"
-#define GIT_DESC_MODE 0644
+#define GIT_DESC_MODE 0666
#define GIT_DESC_CONTENT \
"Unnamed repository; edit this file 'description' to name the repository.\n"
diff --git a/src/repository.c b/src/repository.c
index c28a0882d..278abfaf2 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -935,7 +935,7 @@ static int repo_write_gitlink(
error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo);
if (!error)
- error = repo_write_template(in_dir, true, DOT_GIT, 0644, true, buf.ptr);
+ error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr);
cleanup:
git_buf_free(&buf);
@@ -945,7 +945,7 @@ cleanup:
static mode_t pick_dir_mode(git_repository_init_options *opts)
{
if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
- return 0755;
+ return 0777;
if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP)
return (0775 | S_ISGID);
if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL)
@@ -1006,17 +1006,9 @@ static int repo_init_structure(
tdir = GIT_TEMPLATE_DIR;
}
- /* FIXME: GIT_CPDIR_CHMOD cannot applied here as an attempt
- * would be made to chmod() all directories up to the last
- * component of repo_dir, e.g., also /home etc. Recall that
- * repo_dir is prettified at this point.
- *
- * Best probably would be to have the same logic as in
- * git_futils_mkdir(), i.e., to separate the base from
- * the path.
- */
error = git_futils_cp_r(tdir, repo_dir,
- GIT_CPDIR_COPY_SYMLINKS /*| GIT_CPDIR_CHMOD*/, dmode);
+ GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
+ GIT_CPDIR_SIMPLE_TO_MODE, dmode);
if (error < 0) {
if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0)
@@ -1051,6 +1043,17 @@ static int repo_init_structure(
return error;
}
+static int mkdir_parent(git_buf *buf, uint32_t mode, bool skip2)
+{
+ /* When making parent directories during repository initialization
+ * don't try to set gid or grant world write access
+ */
+ return git_futils_mkdir(
+ buf->ptr, NULL, mode & ~(S_ISGID | 0002),
+ GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR |
+ (skip2 ? GIT_MKDIR_SKIP_LAST2 : GIT_MKDIR_SKIP_LAST));
+}
+
static int repo_init_directories(
git_buf *repo_path,
git_buf *wd_path,
@@ -1058,14 +1061,36 @@ static int repo_init_directories(
git_repository_init_options *opts)
{
int error = 0;
- bool add_dotgit, has_dotgit, natural_wd;
+ bool is_bare, add_dotgit, has_dotgit, natural_wd;
mode_t dirmode;
+ /* There are three possible rules for what we are allowed to create:
+ * - MKPATH means anything we need
+ * - MKDIR means just the .git directory and its parent and the workdir
+ * - Neither means only the .git directory can be created
+ *
+ * There are 5 "segments" of path that we might need to deal with:
+ * 1. The .git directory
+ * 2. The parent of the .git directory
+ * 3. Everything above the parent of the .git directory
+ * 4. The working directory (often the same as #2)
+ * 5. Everything above the working directory (often the same as #3)
+ *
+ * For all directories created, we start with the init_mode value for
+ * permissions and then strip off bits in some cases:
+ *
+ * For MKPATH, we create #3 (and #5) paths without S_ISGID or S_IWOTH
+ * For MKPATH and MKDIR, we create #2 (and #4) without S_ISGID
+ * For all rules, we create #1 using the untouched init_mode
+ */
+
/* set up repo path */
+ is_bare = ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
+
add_dotgit =
(opts->flags & GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) == 0 &&
- (opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
+ !is_bare &&
git__suffixcmp(given_repo, "/" DOT_GIT) != 0 &&
git__suffixcmp(given_repo, "/" GIT_DIR) != 0;
@@ -1078,7 +1103,7 @@ static int repo_init_directories(
/* set up workdir path */
- if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0) {
+ if (!is_bare) {
if (opts->workdir_path) {
if (git_path_join_unrooted(
wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0)
@@ -1110,30 +1135,45 @@ static int repo_init_directories(
dirmode = pick_dir_mode(opts);
- if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 && has_dotgit) {
- git_buf p = GIT_BUF_INIT;
- if ((error = git_path_dirname_r(&p, repo_path->ptr)) >= 0)
- error = git_futils_mkdir(p.ptr, NULL, dirmode, 0);
- git_buf_free(&p);
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0) {
+ /* create path #5 */
+ if (wd_path->size > 0 &&
+ (error = mkdir_parent(wd_path, dirmode, false)) < 0)
+ return error;
+
+ /* create path #3 (if not the same as #5) */
+ if (!natural_wd &&
+ (error = mkdir_parent(repo_path, dirmode, has_dotgit)) < 0)
+ return error;
+ }
+
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
+ (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0)
+ {
+ /* create path #4 */
+ if (wd_path->size > 0 &&
+ (error = git_futils_mkdir(
+ wd_path->ptr, NULL, dirmode & ~S_ISGID,
+ GIT_MKDIR_VERIFY_DIR)) < 0)
+ return error;
+
+ /* create path #2 (if not the same as #4) */
+ if (!natural_wd &&
+ (error = git_futils_mkdir(
+ repo_path->ptr, NULL, dirmode & ~S_ISGID,
+ GIT_MKDIR_VERIFY_DIR | GIT_MKDIR_SKIP_LAST)) < 0)
+ return error;
}
if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
(opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 ||
has_dotgit)
{
- uint32_t mkflag = GIT_MKDIR_CHMOD;
- if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0)
- mkflag |= GIT_MKDIR_PATH;
- error = git_futils_mkdir(repo_path->ptr, NULL, dirmode, mkflag);
+ /* create path #1 */
+ error = git_futils_mkdir(repo_path->ptr, NULL, dirmode,
+ GIT_MKDIR_VERIFY_DIR | ((dirmode & S_ISGID) ? GIT_MKDIR_CHMOD : 0));
}
- if (wd_path->size > 0 &&
- !natural_wd &&
- ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
- (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0))
- error = git_futils_mkdir(wd_path->ptr, NULL, dirmode & ~S_ISGID,
- (opts->flags & GIT_REPOSITORY_INIT_MKPATH) ? GIT_MKDIR_PATH : 0);
-
/* prettify both directories now that they are created */
if (!error) {