diff --git a/archive.c b/archive.c index e6de0397cc..251d69e153 100644 --- a/archive.c +++ b/archive.c @@ -196,7 +196,8 @@ static void parse_pathspec_arg(const char **pathspec, } static void parse_treeish_arg(const char **argv, - struct archiver_args *ar_args, const char *prefix) + struct archiver_args *ar_args, const char *prefix, + int remote) { const char *name = argv[0]; const unsigned char *commit_sha1; @@ -205,8 +206,17 @@ static void parse_treeish_arg(const char **argv, const struct commit *commit; unsigned char sha1[20]; - if (get_sha1(name, sha1)) - die("Not a valid object name"); + /* Remotes are only allowed to fetch actual refs */ + if (remote) { + char *ref = NULL; + if (!dwim_ref(name, strlen(name), sha1, &ref)) + die("no such ref: %s", name); + free(ref); + } + else { + if (get_sha1(name, sha1)) + die("Not a valid object name"); + } commit = lookup_commit_reference_gently(sha1, 1); if (commit) { @@ -324,7 +334,7 @@ static int parse_archive_args(int argc, const char **argv, } int write_archive(int argc, const char **argv, const char *prefix, - int setup_prefix) + int setup_prefix, int remote) { const struct archiver *ar = NULL; struct archiver_args args; @@ -333,7 +343,7 @@ int write_archive(int argc, const char **argv, const char *prefix, if (setup_prefix && prefix == NULL) prefix = setup_git_directory(); - parse_treeish_arg(argv, &args, prefix); + parse_treeish_arg(argv, &args, prefix, remote); parse_pathspec_arg(argv + 1, &args); git_config(git_default_config, NULL); diff --git a/archive.h b/archive.h index 0b15b35143..9375057d72 100644 --- a/archive.h +++ b/archive.h @@ -24,6 +24,7 @@ extern int write_tar_archive(struct archiver_args *); extern int write_zip_archive(struct archiver_args *); extern int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry); -extern int write_archive(int argc, const char **argv, const char *prefix, int setup_prefix); +extern int write_archive(int argc, const char **argv, const char *prefix, + int setup_prefix, int remote); #endif /* ARCHIVE_H */ diff --git a/builtin-archive.c b/builtin-archive.c index 5ceec433fd..a081a6bf17 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -117,5 +117,5 @@ int cmd_archive(int argc, const char **argv, const char *prefix) setvbuf(stderr, NULL, _IOLBF, BUFSIZ); - return write_archive(argc, argv, prefix, 1); + return write_archive(argc, argv, prefix, 1, 0); } diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c index a9b02fa32f..47efadc465 100644 --- a/builtin-upload-archive.c +++ b/builtin-upload-archive.c @@ -64,7 +64,7 @@ static int run_upload_archive(int argc, const char **argv, const char *prefix) sent_argv[sent_argc] = NULL; /* parse all options sent by the client */ - return write_archive(sent_argc, sent_argv, prefix, 0); + return write_archive(sent_argc, sent_argv, prefix, 0, 1); } static void error_clnt(const char *fmt, ...) diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index c942c8be85..1a2ee105a4 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -213,4 +213,12 @@ test_expect_success \ 'git archive --list outside of a git repo' \ 'GIT_DIR=some/non-existing/directory git archive --list' +test_expect_success 'clients cannot access unreachable commits' ' + test_commit unreachable && + sha1=`git rev-parse HEAD` && + git reset --hard HEAD^ && + git archive $sha1 >remote.tar && + test_must_fail git archive --remote=. $sha1 >remote.tar +' + test_done