diff --git a/Documentation/config.txt b/Documentation/config.txt index fe43b12572fcb1a0b4b91972d2d504b2b594a4da..7a24f6e819ea0e5431748cea64b46369dfd6a114 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -261,7 +261,12 @@ core.sharedRepository:: group-writable). When 'all' (or 'world' or 'everybody'), the repository will be readable by all users, additionally to being group-shareable. When 'umask' (or 'false'), git will use permissions - reported by umask(2). See linkgit:git-init[1]. False by default. + reported by umask(2). When '0xxx', where '0xxx' is an octal number, + files in the repository will have this mode value. '0xxx' will override + user's umask value, and thus, users with a safe umask (0077) can use + this option. Examples: '0660' is equivalent to 'group'. '0640' is a + repository that is group-readable but not group-writable. + See linkgit:git-init[1]. False by default. core.warnAmbiguousRefs:: If true, git will warn you if the ref name you passed it is ambiguous diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt index 62914da97b5b8335a2c2597606f046dfb2295e03..b17ae8485cf2d3eb4fc21acba769942a57d33e67 100644 --- a/Documentation/git-init.txt +++ b/Documentation/git-init.txt @@ -31,7 +31,7 @@ structure, some suggested "exclude patterns", and copies of non-executing "hook" files. The suggested patterns and hook files are all modifiable and extensible. ---shared[={false|true|umask|group|all|world|everybody}]:: +--shared[={false|true|umask|group|all|world|everybody|0xxx}]:: Specify that the git repository is to be shared amongst several users. This allows users belonging to the same group to push into that @@ -52,6 +52,12 @@ is given: - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository readable by all users. + - '0xxx': '0xxx' is an octal number and each file will have mode '0xxx' + Any option except 'umask' can be set using this option. '0xxx' will + override users umask(2) value, and thus, users with a safe umask (0077) + can use this option. '0640' will create a repository which is group-readable + but not writable. '0660' is equivalent to 'group'. + By default, the configuration flag receive.denyNonFastForwards is enabled in shared repositories, so that you cannot force a non fast-forwarding push into it. diff --git a/builtin-init-db.c b/builtin-init-db.c index 2854868b4e4185d33024b9fe5d00ad631c18bb36..a76f5d3474f9d1e16bfc988ad6fd1d971876fe91 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -400,9 +400,16 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) char buf[10]; /* We do not spell "group" and such, so that * the configuration can be read by older version - * of git. + * of git. Note, we use octal numbers for new share modes, + * and compatibility values for PERM_GROUP and + * PERM_EVERYBODY. */ - sprintf(buf, "%d", shared_repository); + if (shared_repository == PERM_GROUP) + sprintf(buf, "%d", OLD_PERM_GROUP); + else if (shared_repository == PERM_EVERYBODY) + sprintf(buf, "%d", OLD_PERM_EVERYBODY); + else + sprintf(buf, "0%o", shared_repository); git_config_set("core.sharedrepository", buf); git_config_set("receive.denyNonFastforwards", "true"); } diff --git a/cache.h b/cache.h index 50b28fad0126d14d012769d5e8b95eb9ab6853d3..3fcc2830029feb82223f2c82c424c7ea09fdd0b4 100644 --- a/cache.h +++ b/cache.h @@ -474,10 +474,20 @@ static inline void hashclr(unsigned char *hash) int git_mkstemp(char *path, size_t n, const char *template); +/* + * NOTE NOTE NOTE!! + * + * PERM_UMASK, OLD_PERM_GROUP and OLD_PERM_EVERYBODY enumerations must + * not be changed. Old repositories have core.sharedrepository written in + * numeric format, and therefore these values are preserved for compatibility + * reasons. + */ enum sharedrepo { - PERM_UMASK = 0, - PERM_GROUP, - PERM_EVERYBODY + PERM_UMASK = 0, + OLD_PERM_GROUP = 1, + OLD_PERM_EVERYBODY = 2, + PERM_GROUP = 0660, + PERM_EVERYBODY = 0664, }; int git_config_perm(const char *var, const char *value); int adjust_shared_perm(const char *path); diff --git a/path.c b/path.c index f4ed979997b6a968ba1987a199beae39035d5da2..2ae7cd997555883ecbe73b12fb53c1e6c650b62c 100644 --- a/path.c +++ b/path.c @@ -266,24 +266,25 @@ int adjust_shared_perm(const char *path) if (lstat(path, &st) < 0) return -1; mode = st.st_mode; - if (mode & S_IRUSR) - mode |= (shared_repository == PERM_GROUP - ? S_IRGRP - : (shared_repository == PERM_EVERYBODY - ? (S_IRGRP|S_IROTH) - : 0)); - - if (mode & S_IWUSR) - mode |= S_IWGRP; - - if (mode & S_IXUSR) - mode |= (shared_repository == PERM_GROUP - ? S_IXGRP - : (shared_repository == PERM_EVERYBODY - ? (S_IXGRP|S_IXOTH) - : 0)); - if (S_ISDIR(mode)) + + if (shared_repository) { + int tweak = shared_repository; + if (!(mode & S_IWUSR)) + tweak &= ~0222; + mode = (mode & ~0777) | tweak; + } else { + /* Preserve old PERM_UMASK behaviour */ + if (mode & S_IWUSR) + mode |= S_IWGRP; + } + + if (S_ISDIR(mode)) { mode |= FORCE_DIR_SET_GID; + + /* Copy read bits to execute bits */ + mode |= (shared_repository & 0444) >> 2; + } + if ((mode & st.st_mode) != mode && chmod(path, mode) < 0) return -2; return 0; diff --git a/setup.c b/setup.c index 3d2d9580f3283b4e4bc741d98863777510bd5173..1b4fa6a8c4289bedf10f57d9aa19db4c7f186b01 100644 --- a/setup.c +++ b/setup.c @@ -428,21 +428,53 @@ const char *setup_git_directory_gently(int *nongit_ok) int git_config_perm(const char *var, const char *value) { - if (value) { - int i; - if (!strcmp(value, "umask")) - return PERM_UMASK; - if (!strcmp(value, "group")) - return PERM_GROUP; - if (!strcmp(value, "all") || - !strcmp(value, "world") || - !strcmp(value, "everybody")) - return PERM_EVERYBODY; - i = atoi(value); - if (i > 1) - return i; + int i; + char *endptr; + + if (value == NULL) + return PERM_GROUP; + + if (!strcmp(value, "umask")) + return PERM_UMASK; + if (!strcmp(value, "group")) + return PERM_GROUP; + if (!strcmp(value, "all") || + !strcmp(value, "world") || + !strcmp(value, "everybody")) + return PERM_EVERYBODY; + + /* Parse octal numbers */ + i = strtol(value, &endptr, 8); + + /* If not an octal number, maybe true/false? */ + if (*endptr != 0) + return git_config_bool(var, value) ? PERM_GROUP : PERM_UMASK; + + /* + * Treat values 0, 1 and 2 as compatibility cases, otherwise it is + * a chmod value. + */ + switch (i) { + case PERM_UMASK: /* 0 */ + return PERM_UMASK; + case OLD_PERM_GROUP: /* 1 */ + return PERM_GROUP; + case OLD_PERM_EVERYBODY: /* 2 */ + return PERM_EVERYBODY; } - return git_config_bool(var, value); + + /* A filemode value was given: 0xxx */ + + if ((i & 0600) != 0600) + die("Problem with core.sharedRepository filemode value " + "(0%.3o).\nThe owner of files must always have " + "read and write permissions.", i); + + /* + * Mask filemode value. Others can not get write permission. + * x flags for directories are handled separately. + */ + return i & 0666; } int check_repository_format_version(const char *var, const char *value) diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh index 6bfe19a4e5e8a22bfd286636e62c0c2d5cd56846..5e4252a320d5f967eacb9bd68bb0a510fe748e80 100755 --- a/t/t1301-shared-repo.sh +++ b/t/t1301-shared-repo.sh @@ -7,6 +7,16 @@ test_description='Test shared repository initialization' . ./test-lib.sh +# User must have read permissions to the repo -> failure on --shared=0400 +test_expect_success 'shared = 0400 (faulty permission u-w)' ' + mkdir sub && ( + cd sub && git init --shared=0400 + ) + ret="$?" + rm -rf sub + test $ret != "0" +' + test_expect_success 'shared=all' ' mkdir sub && cd sub && @@ -33,4 +43,44 @@ test_expect_success 'update-server-info honors core.sharedRepository' ' esac ' +for u in 0660:rw-rw---- \ + 0640:rw-r----- \ + 0600:rw------- \ + 0666:rw-rw-rw- \ + 0664:rw-rw-r-- +do + x=$(expr "$u" : ".*:\([rw-]*\)") && + y=$(echo "$x" | sed -e "s/w/-/g") && + u=$(expr "$u" : "\([0-7]*\)") && + git config core.sharedrepository "$u" && + umask 0277 && + + test_expect_success "shared = $u ($y) ro" ' + + rm -f .git/info/refs && + git update-server-info && + actual="$(ls -l .git/info/refs)" && + actual=${actual%% *} && + test "x$actual" = "x-$y" || { + ls -lt .git/info + false + } + ' + + umask 077 && + test_expect_success "shared = $u ($x) rw" ' + + rm -f .git/info/refs && + git update-server-info && + actual="$(ls -l .git/info/refs)" && + actual=${actual%% *} && + test "x$actual" = "x-$x" || { + ls -lt .git/info + false + } + + ' + +done + test_done