Commit 18f08264 by Russell Belfer

Make mode handling during init more like git

When creating files, instead of actually using GIT_FILEMODE_BLOB
and the other various constants that happen to correspond to
mode values, apparently I should be just using 0666 and 0777, and
relying on the umask to clear bits and make the value sane.

This fixes the rules for copying a template directory and fixes
the checks to match that new behavior.  (Further changes to the
checkout logic to follow separately.)
parent 0d1b094b
...@@ -693,7 +693,7 @@ static int _cp_r_mkdir(cp_r_info *info, git_buf *from) ...@@ -693,7 +693,7 @@ static int _cp_r_mkdir(cp_r_info *info, git_buf *from)
if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) { if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) {
error = git_futils_mkdir( error = git_futils_mkdir(
info->to_root, NULL, info->dirmode, info->to_root, NULL, info->dirmode,
((info->flags & GIT_CPDIR_CHMOD) != 0) ? GIT_MKDIR_CHMOD : 0); (info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0);
info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT; info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT;
} }
...@@ -738,7 +738,7 @@ static int _cp_r_callback(void *ref, git_buf *from) ...@@ -738,7 +738,7 @@ static int _cp_r_callback(void *ref, git_buf *from)
mode_t oldmode = info->dirmode; mode_t oldmode = info->dirmode;
/* if we are not chmod'ing, then overwrite 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; info->dirmode = from_st.st_mode;
/* make directory now if CREATE_EMPTY_DIRS is requested and needed */ /* make directory now if CREATE_EMPTY_DIRS is requested and needed */
...@@ -783,17 +783,10 @@ static int _cp_r_callback(void *ref, git_buf *from) ...@@ -783,17 +783,10 @@ static int _cp_r_callback(void *ref, git_buf *from)
else { else {
mode_t usemode = from_st.st_mode; mode_t usemode = from_st.st_mode;
/* if chmod'ing, then blend dirmode & from_st bits */ if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
if ((info->flags & GIT_CPDIR_CHMOD) != 0) usemode = (usemode & 0111) ? 0777 : 0666;
usemode = (info->dirmode & 0666) | (usemode & ~0666);
error = git_futils_cp(from->ptr, info->to.ptr, usemode); error = git_futils_cp(from->ptr, info->to.ptr, usemode);
if (!error &&
(info->flags & GIT_CPDIR_CHMOD) != 0 &&
(error = p_chmod(info->to.ptr, usemode)) < 0)
giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
info->to.ptr);
} }
return error; return error;
...@@ -824,12 +817,12 @@ int git_futils_cp_r( ...@@ -824,12 +817,12 @@ int git_futils_cp_r(
* demand right before files are copied. * demand right before files are copied.
*/ */
info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST; 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; info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH;
} else { } else {
/* otherwise, we will do simple mkdir as directories are encountered */ /* otherwise, we will do simple mkdir as directories are encountered */
info.mkdir_flags = 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); error = _cp_r_callback(&info, &path);
......
...@@ -162,13 +162,26 @@ extern int git_futils_cp( ...@@ -162,13 +162,26 @@ extern int git_futils_cp(
/** /**
* Flags that can be passed to `git_futils_cp_r`. * 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 { typedef enum {
GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0), GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0),
GIT_CPDIR_COPY_SYMLINKS = (1u << 1), GIT_CPDIR_COPY_SYMLINKS = (1u << 1),
GIT_CPDIR_COPY_DOTFILES = (1u << 2), GIT_CPDIR_COPY_DOTFILES = (1u << 2),
GIT_CPDIR_OVERWRITE = (1u << 3), GIT_CPDIR_OVERWRITE = (1u << 3),
GIT_CPDIR_CHMOD = (1u << 4), GIT_CPDIR_CHMOD_DIRS = (1u << 4),
GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5),
} git_futils_cpdir_flags; } git_futils_cpdir_flags;
/** /**
......
...@@ -11,10 +11,10 @@ ...@@ -11,10 +11,10 @@
#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/" #define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
#define GIT_HOOKS_DIR "hooks/" #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_FILE GIT_HOOKS_DIR "README.sample"
#define GIT_HOOKS_README_MODE 0755 #define GIT_HOOKS_README_MODE 0777
#define GIT_HOOKS_README_CONTENT \ #define GIT_HOOKS_README_CONTENT \
"#!/bin/sh\n"\ "#!/bin/sh\n"\
"#\n"\ "#\n"\
...@@ -23,16 +23,16 @@ ...@@ -23,16 +23,16 @@
"# more information.\n" "# more information.\n"
#define GIT_INFO_DIR "info/" #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_FILE GIT_INFO_DIR "exclude"
#define GIT_INFO_EXCLUDE_MODE 0644 #define GIT_INFO_EXCLUDE_MODE 0666
#define GIT_INFO_EXCLUDE_CONTENT \ #define GIT_INFO_EXCLUDE_CONTENT \
"# File patterns to ignore; see `git help ignore` for more information.\n"\ "# File patterns to ignore; see `git help ignore` for more information.\n"\
"# Lines that start with '#' are comments.\n" "# Lines that start with '#' are comments.\n"
#define GIT_DESC_FILE "description" #define GIT_DESC_FILE "description"
#define GIT_DESC_MODE 0644 #define GIT_DESC_MODE 0666
#define GIT_DESC_CONTENT \ #define GIT_DESC_CONTENT \
"Unnamed repository; edit this file 'description' to name the repository.\n" "Unnamed repository; edit this file 'description' to name the repository.\n"
......
...@@ -934,7 +934,7 @@ static int repo_write_gitlink( ...@@ -934,7 +934,7 @@ static int repo_write_gitlink(
error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo); error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo);
if (!error) 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: cleanup:
git_buf_free(&buf); git_buf_free(&buf);
...@@ -944,7 +944,7 @@ cleanup: ...@@ -944,7 +944,7 @@ cleanup:
static mode_t pick_dir_mode(git_repository_init_options *opts) static mode_t pick_dir_mode(git_repository_init_options *opts)
{ {
if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK) if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
return 0755; return 0777;
if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP)
return (0775 | S_ISGID); return (0775 | S_ISGID);
if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL)
...@@ -1006,7 +1006,8 @@ static int repo_init_structure( ...@@ -1006,7 +1006,8 @@ static int repo_init_structure(
} }
error = git_futils_cp_r(tdir, repo_dir, 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 (error < 0) {
if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0) if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0)
...@@ -1168,10 +1169,8 @@ static int repo_init_directories( ...@@ -1168,10 +1169,8 @@ static int repo_init_directories(
has_dotgit) has_dotgit)
{ {
/* create path #1 */ /* create path #1 */
if ((error = git_futils_mkdir( error = git_futils_mkdir(repo_path->ptr, NULL, dirmode,
repo_path->ptr, NULL, dirmode, GIT_MKDIR_VERIFY_DIR | ((dirmode & S_ISGID) ? GIT_MKDIR_CHMOD : 0));
GIT_MKDIR_VERIFY_DIR | GIT_MKDIR_CHMOD)) < 0)
return error;
} }
/* prettify both directories now that they are created */ /* prettify both directories now that they are created */
......
...@@ -363,24 +363,11 @@ void test_repo_init__extended_1(void) ...@@ -363,24 +363,11 @@ void test_repo_init__extended_1(void)
cl_fixture_cleanup("root"); cl_fixture_cleanup("root");
} }
static uint32_t normalize_filemode(uint32_t mode, bool core_filemode)
{
/* if no filemode support, strip SETGID, exec, and low-order bits */
/* cannot use constants because on platform without SETGID, that
* will have been defined to zero - must use hardcoded value to
* clear it effectively from the expected value
*/
return core_filemode ? mode : (mode & ~02177);
}
static void assert_hooks_match( static void assert_hooks_match(
const char *template_dir, const char *template_dir,
const char *repo_dir, const char *repo_dir,
const char *hook_path, const char *hook_path,
bool core_filemode, bool core_filemode)
uint32_t expected_mode)
{ {
git_buf expected = GIT_BUF_INIT; git_buf expected = GIT_BUF_INIT;
git_buf actual = GIT_BUF_INIT; git_buf actual = GIT_BUF_INIT;
...@@ -394,19 +381,20 @@ static void assert_hooks_match( ...@@ -394,19 +381,20 @@ static void assert_hooks_match(
cl_assert(expected_st.st_size == st.st_size); cl_assert(expected_st.st_size == st.st_size);
if (!expected_mode) if (!core_filemode) {
expected_mode = (uint32_t)expected_st.st_mode; expected_st.st_mode = expected_st.st_mode & ~0111;
st.st_mode = st.st_mode & ~0111;
expected_mode = normalize_filemode(expected_mode, core_filemode); }
cl_assert_equal_i((int)expected_mode, (int)st.st_mode); cl_assert_equal_i((int)expected_st.st_mode, (int)st.st_mode);
git_buf_free(&expected); git_buf_free(&expected);
git_buf_free(&actual); git_buf_free(&actual);
} }
static void assert_has_mode( static void assert_mode_seems_okay(
const char *base, const char *path, bool core_filemode, uint32_t expected) const char *base, const char *path,
git_filemode_t expect_mode, bool expect_setgid, bool core_filemode)
{ {
git_buf full = GIT_BUF_INIT; git_buf full = GIT_BUF_INIT;
struct stat st; struct stat st;
...@@ -415,9 +403,25 @@ static void assert_has_mode( ...@@ -415,9 +403,25 @@ static void assert_has_mode(
cl_git_pass(git_path_lstat(full.ptr, &st)); cl_git_pass(git_path_lstat(full.ptr, &st));
git_buf_free(&full); git_buf_free(&full);
expected = normalize_filemode(expected, core_filemode); if (!core_filemode) {
expect_mode = expect_mode & ~0111;
st.st_mode = st.st_mode & ~0111;
expect_setgid = false;
}
if (S_ISGID != 0) {
if (expect_setgid)
cl_assert((st.st_mode & S_ISGID) != 0);
else
cl_assert((st.st_mode & S_ISGID) == 0);
}
if ((expect_mode & 0111) != 0)
cl_assert((st.st_mode & 0111) != 0);
else
cl_assert((st.st_mode & 0111) == 0);
cl_assert_equal_i((int)expected, (int)st.st_mode); cl_assert((expect_mode & 0170000) == (st.st_mode & 0170000));
} }
void test_repo_init__extended_with_template(void) void test_repo_init__extended_with_template(void)
...@@ -450,11 +454,11 @@ void test_repo_init__extended_with_template(void) ...@@ -450,11 +454,11 @@ void test_repo_init__extended_with_template(void)
assert_hooks_match( assert_hooks_match(
cl_fixture("template"), git_repository_path(_repo), cl_fixture("template"), git_repository_path(_repo),
"hooks/update.sample", true, 0); "hooks/update.sample", true);
assert_hooks_match( assert_hooks_match(
cl_fixture("template"), git_repository_path(_repo), cl_fixture("template"), git_repository_path(_repo),
"hooks/link.sample", true, 0); "hooks/link.sample", true);
} }
void test_repo_init__extended_with_template_and_shared_mode(void) void test_repo_init__extended_with_template_and_shared_mode(void)
...@@ -464,6 +468,7 @@ void test_repo_init__extended_with_template_and_shared_mode(void) ...@@ -464,6 +468,7 @@ void test_repo_init__extended_with_template_and_shared_mode(void)
git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
git_config *config; git_config *config;
int filemode = true; int filemode = true;
const char *repo_path = NULL;
cl_set_cleanup(&cleanup_repository, "init_shared_from_tpl"); cl_set_cleanup(&cleanup_repository, "init_shared_from_tpl");
...@@ -491,25 +496,25 @@ void test_repo_init__extended_with_template_and_shared_mode(void) ...@@ -491,25 +496,25 @@ void test_repo_init__extended_with_template_and_shared_mode(void)
git_buf_free(&expected); git_buf_free(&expected);
git_buf_free(&actual); git_buf_free(&actual);
assert_has_mode(git_repository_path(_repo), "hooks", filemode, repo_path = git_repository_path(_repo);
GIT_REPOSITORY_INIT_SHARED_GROUP | S_IFDIR); assert_mode_seems_okay(repo_path, "hooks",
assert_has_mode(git_repository_path(_repo), "info", filemode, GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
GIT_REPOSITORY_INIT_SHARED_GROUP | S_IFDIR); assert_mode_seems_okay(repo_path, "info",
assert_has_mode(git_repository_path(_repo), "description", filemode, GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
(GIT_REPOSITORY_INIT_SHARED_GROUP | S_IFREG) & ~(S_ISGID | 0111)); assert_mode_seems_okay(repo_path, "description",
GIT_FILEMODE_BLOB, false, filemode);
/* for a non-symlinked hook, it should have shared permissions now */ /* for a non-symlinked hook, it should have shared permissions now */
assert_hooks_match( assert_hooks_match(
cl_fixture("template"), git_repository_path(_repo), cl_fixture("template"), git_repository_path(_repo),
"hooks/update.sample", filemode, "hooks/update.sample", filemode);
(GIT_REPOSITORY_INIT_SHARED_GROUP | S_IFREG) & ~S_ISGID);
/* for a symlinked hook, the permissions still should match the /* for a symlinked hook, the permissions still should match the
* source link, not the GIT_REPOSITORY_INIT_SHARED_GROUP value * source link, not the GIT_REPOSITORY_INIT_SHARED_GROUP value
*/ */
assert_hooks_match( assert_hooks_match(
cl_fixture("template"), git_repository_path(_repo), cl_fixture("template"), git_repository_path(_repo),
"hooks/link.sample", filemode, 0); "hooks/link.sample", filemode);
} }
void test_repo_init__can_reinit_an_initialized_repository(void) void test_repo_init__can_reinit_an_initialized_repository(void)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment