#!/bin/sh global test_description := ''test fetching over git protocol'' source ./test-lib.sh source "$TEST_DIRECTORY"/lib-git-daemon.sh start_git_daemon proc check_verbose_connect { grep -F "Looking up 127.0.0.1 ..." stderr && grep -F "Connecting to 127.0.0.1 (port " stderr && grep -F "done." stderr } test_expect_success 'setup repository' ' git config push.default matching && echo content >file && git add file && git commit -m one ' test_expect_success 'create git-accessible bare repository' ' mkdir "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" && (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" && git --bare init && : >git-daemon-export-ok ) && git remote add public "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" && git push public master:master ' test_expect_success 'clone git repository' ' git clone -v "$GIT_DAEMON_URL/repo.git" clone 2>stderr && check_verbose_connect && test_cmp file clone/file ' test_expect_success 'fetch changes via git protocol' ' echo content >>file && git commit -a -m two && git push public && (cd clone && git pull -v) 2>stderr && check_verbose_connect && test_cmp file clone/file ' test_expect_success 'no-op fetch -v stderr is as expected' ' (cd clone && git fetch -v) 2>stderr && check_verbose_connect ' test_expect_success 'no-op fetch without "-v" is quiet' ' (cd clone && git fetch) 2>stderr && ! test -s stderr ' test_expect_success 'remote detects correct HEAD' ' git push public master:other && (cd clone && git remote set-head -d origin && git remote set-head -a origin && git symbolic-ref refs/remotes/origin/HEAD > output && echo refs/remotes/origin/master > expect && test_cmp expect output ) ' test_expect_success 'prepare pack objects' ' cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git && (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git && git --bare repack -a -d ) ' test_expect_success 'fetch notices corrupt pack' ' cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad1.git && (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad1.git && p=$(ls objects/pack/pack-*.pack) && chmod u+w $p && printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc ) && mkdir repo_bad1.git && (cd repo_bad1.git && git --bare init && test_must_fail git --bare fetch "$GIT_DAEMON_URL/repo_bad1.git" && test 0 = $(ls objects/pack/pack-*.pack | wc -l) ) ' test_expect_success 'fetch notices corrupt idx' ' cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad2.git && (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad2.git && p=$(ls objects/pack/pack-*.idx) && chmod u+w $p && printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc ) && mkdir repo_bad2.git && (cd repo_bad2.git && git --bare init && test_must_fail git --bare fetch "$GIT_DAEMON_URL/repo_bad2.git" && test 0 = $(ls objects/pack | wc -l) ) ' proc test_remote_error { global do_export := 'YesPlease' while test $# -gt 0 { matchstr $1 { -x { shift chmod -x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" } -n { shift global do_export := '' } * { break } } } global msg := $1 shift global cmd := $1 shift global repo := $1 shift || error "invalid number of arguments" if test -x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo" { if test -n $do_export { : >"$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo/git-daemon-export-ok" } else { rm -f "$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo/git-daemon-export-ok" } } test_must_fail git $cmd "$GIT_DAEMON_URL/$repo" @Argv !2 >output && test_i18ngrep "fatal: remote error: $msg: /$repo" output && global ret := $Status chmod +x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" shell {exit $ret} } global msg := '"access denied or repository not exported'" test_expect_success 'clone non-existent' "test_remote_error '$msg' clone nowhere.git " test_expect_success 'push disabled' "test_remote_error '$msg' push repo.git master" test_expect_success 'read access denied' "test_remote_error -x '$msg' fetch repo.git " test_expect_success 'not exported' "test_remote_error -n '$msg' fetch repo.git " stop_git_daemon start_git_daemon --informative-errors test_expect_success 'clone non-existent' "test_remote_error 'no such repository' clone nowhere.git " test_expect_success 'push disabled' "test_remote_error 'service not enabled' push repo.git master" test_expect_success 'read access denied' "test_remote_error -x 'no such repository' fetch repo.git " test_expect_success 'not exported' "test_remote_error -n 'repository not exported' fetch repo.git " stop_git_daemon start_git_daemon --interpolated-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH/%H%D" test_expect_success 'access repo via interpolated hostname' ' repo="$GIT_DAEMON_DOCUMENT_ROOT_PATH/localhost/interp.git" && git init --bare "$repo" && git push "$repo" HEAD && >"$repo"/git-daemon-export-ok && rm -rf tmp.git && GIT_OVERRIDE_VIRTUAL_HOST=localhost \ git clone --bare "$GIT_DAEMON_URL/interp.git" tmp.git && rm -rf tmp.git && GIT_OVERRIDE_VIRTUAL_HOST=LOCALHOST \ git clone --bare "$GIT_DAEMON_URL/interp.git" tmp.git ' test_expect_success 'hostname cannot break out of directory' ' rm -rf tmp.git && repo="$GIT_DAEMON_DOCUMENT_ROOT_PATH/../escape.git" && git init --bare "$repo" && git push "$repo" HEAD && >"$repo"/git-daemon-export-ok && test_must_fail \ env GIT_OVERRIDE_VIRTUAL_HOST=.. \ git clone --bare "$GIT_DAEMON_URL/escape.git" tmp.git ' stop_git_daemon test_done (CommandList children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:test_description) op: Equal rhs: {(SQ <"test fetching over git protocol">)} spids: [4] ) ] spids: [4] ) (C {(.)} {(./test-lib.sh)}) (C {(.)} {(DQ ($ VSub_Name "$TEST_DIRECTORY")) (/lib-git-daemon.sh)}) (C {(start_git_daemon)}) (FuncDef name: check_verbose_connect body: (BraceGroup children: [ (AndOr children: [ (C {(grep)} {(-F)} {(DQ ("Looking up 127.0.0.1 ..."))} {(stderr)}) (AndOr children: [ (C {(grep)} {(-F)} {(DQ ("Connecting to 127.0.0.1 (port "))} {(stderr)}) (C {(grep)} {(-F)} {(DQ (done.))} {(stderr)}) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) ] spids: [29] ) spids: [24 28] ) (C {(test_expect_success)} {(SQ <"setup repository">)} { (SQ <"\n"> <"\tgit config push.default matching &&\n"> <"\techo content >file &&\n"> <"\tgit add file &&\n"> <"\tgit commit -m one\n"> ) } ) (C {(test_expect_success)} {(SQ <"create git-accessible bare repository">)} { (SQ <"\n"> <"\tmkdir \"$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git\" &&\n"> <"\t(cd \"$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git\" &&\n"> <"\t git --bare init &&\n"> <"\t : >git-daemon-export-ok\n"> <"\t) &&\n"> <"\tgit remote add public \"$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git\" &&\n"> <"\tgit push public master:master\n"> ) } ) (C {(test_expect_success)} {(SQ <"clone git repository">)} { (SQ <"\n"> <"\tgit clone -v \"$GIT_DAEMON_URL/repo.git\" clone 2>stderr &&\n"> <"\tcheck_verbose_connect &&\n"> <"\ttest_cmp file clone/file\n"> ) } ) (C {(test_expect_success)} {(SQ <"fetch changes via git protocol">)} { (SQ <"\n"> <"\techo content >>file &&\n"> <"\tgit commit -a -m two &&\n"> <"\tgit push public &&\n"> <"\t(cd clone && git pull -v) 2>stderr &&\n"> <"\tcheck_verbose_connect &&\n"> <"\ttest_cmp file clone/file\n"> ) } ) (C {(test_expect_success)} {(SQ <"no-op fetch -v stderr is as expected">)} {(SQ <"\n"> <"\t(cd clone && git fetch -v) 2>stderr &&\n"> <"\tcheck_verbose_connect\n">)} ) (C {(test_expect_success)} {(SQ <"no-op fetch without \"-v\" is quiet">)} {(SQ <"\n"> <"\t(cd clone && git fetch) 2>stderr &&\n"> <"\t! test -s stderr\n">)} ) (C {(test_expect_success)} {(SQ <"remote detects correct HEAD">)} { (SQ <"\n"> <"\tgit push public master:other &&\n"> <"\t(cd clone &&\n"> <"\t git remote set-head -d origin &&\n"> <"\t git remote set-head -a origin &&\n"> <"\t git symbolic-ref refs/remotes/origin/HEAD > output &&\n"> <"\t echo refs/remotes/origin/master > expect &&\n"> <"\t test_cmp expect output\n"> <"\t)\n"> ) } ) (C {(test_expect_success)} {(SQ <"prepare pack objects">)} { (SQ <"\n"> < "\tcp -R \"$GIT_DAEMON_DOCUMENT_ROOT_PATH\"/repo.git \"$GIT_DAEMON_DOCUMENT_ROOT_PATH\"/repo_pack.git &&\n" > <"\t(cd \"$GIT_DAEMON_DOCUMENT_ROOT_PATH\"/repo_pack.git &&\n"> <"\t git --bare repack -a -d\n"> <"\t)\n"> ) } ) (C {(test_expect_success)} {(SQ <"fetch notices corrupt pack">)} { (SQ <"\n"> < "\tcp -R \"$GIT_DAEMON_DOCUMENT_ROOT_PATH\"/repo_pack.git \"$GIT_DAEMON_DOCUMENT_ROOT_PATH\"/repo_bad1.git &&\n" > <"\t(cd \"$GIT_DAEMON_DOCUMENT_ROOT_PATH\"/repo_bad1.git &&\n"> <"\t p=$(ls objects/pack/pack-*.pack) &&\n"> <"\t chmod u+w $p &&\n"> <"\t printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc\n"> <"\t) &&\n"> <"\tmkdir repo_bad1.git &&\n"> <"\t(cd repo_bad1.git &&\n"> <"\t git --bare init &&\n"> <"\t test_must_fail git --bare fetch \"$GIT_DAEMON_URL/repo_bad1.git\" &&\n"> <"\t test 0 = $(ls objects/pack/pack-*.pack | wc -l)\n"> <"\t)\n"> ) } ) (C {(test_expect_success)} {(SQ <"fetch notices corrupt idx">)} { (SQ <"\n"> < "\tcp -R \"$GIT_DAEMON_DOCUMENT_ROOT_PATH\"/repo_pack.git \"$GIT_DAEMON_DOCUMENT_ROOT_PATH\"/repo_bad2.git &&\n" > <"\t(cd \"$GIT_DAEMON_DOCUMENT_ROOT_PATH\"/repo_bad2.git &&\n"> <"\t p=$(ls objects/pack/pack-*.idx) &&\n"> <"\t chmod u+w $p &&\n"> <"\t printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc\n"> <"\t) &&\n"> <"\tmkdir repo_bad2.git &&\n"> <"\t(cd repo_bad2.git &&\n"> <"\t git --bare init &&\n"> <"\t test_must_fail git --bare fetch \"$GIT_DAEMON_URL/repo_bad2.git\" &&\n"> <"\t test 0 = $(ls objects/pack | wc -l)\n"> <"\t)\n"> ) } ) (FuncDef name: test_remote_error body: (BraceGroup children: [ (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:do_export) op: Equal rhs: {(YesPlease)} spids: [248] ) ] spids: [248] ) (While cond: [(C {(test)} {($ VSub_Pound "$#")} {(-gt)} {(0)})] body: (DoGroup children: [ (Case to_match: {($ VSub_Number "$1")} arms: [ (case_arm pat_list: [{(-x)}] action: [ (C {(shift)}) (C {(chmod)} {(-x)} {(DQ ($ VSub_Name "$GIT_DAEMON_DOCUMENT_ROOT_PATH") (/repo.git))} ) ] spids: [273 274 290 -1] ) (case_arm pat_list: [{(-n)}] action: [ (C {(shift)}) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:do_export) op: Equal rhs: {(SQ )} spids: [300] ) ] spids: [300] ) ] spids: [293 294 303 -1] ) (case_arm pat_list: [{(Lit_Other "*")}] action: [(ControlFlow token:)] spids: [306 307 -1 313] ) ] spids: [266 270 313] ) ] spids: [263 316] ) ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:msg) op: Equal rhs: {($ VSub_Number "$1")} spids: [320] ) ] spids: [320] ) (C {(shift)}) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:cmd) op: Equal rhs: {($ VSub_Number "$1")} spids: [327] ) ] spids: [327] ) (C {(shift)}) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:repo) op: Equal rhs: {($ VSub_Number "$1")} spids: [334] ) ] spids: [334] ) (AndOr children: [(C {(shift)}) (C {(error)} {(DQ ("invalid number of arguments"))})] op_id: Op_DPipe ) (If arms: [ (if_arm cond: [ (C {(test)} {(-x)} {(DQ ($ VSub_Name "$GIT_DAEMON_DOCUMENT_ROOT_PATH") (/) ($ VSub_Name "$repo"))} ) ] action: [ (If arms: [ (if_arm cond: [(C {(test)} {(-n)} {(DQ ($ VSub_Name "$do_export"))})] action: [ (SimpleCommand words: [{(Lit_Other ":")}] redirects: [ (Redir op_id: Redir_Great fd: -1 arg_word: { (DQ ($ VSub_Name "$GIT_DAEMON_DOCUMENT_ROOT_PATH") (/) ($ VSub_Name "$repo") (/git-daemon-export-ok) ) } spids: [382] ) ] ) ] spids: [-1 377] ) ] else_action: [ (C {(rm)} {(-f)} { (DQ ($ VSub_Name "$GIT_DAEMON_DOCUMENT_ROOT_PATH") (/) ($ VSub_Name "$repo") (/git-daemon-export-ok) ) } ) ] spids: [391 406] ) ] spids: [-1 363] ) ] spids: [-1 409] ) (AndOr children: [ (SimpleCommand words: [ {(test_must_fail)} {(git)} {(DQ ($ VSub_Name "$cmd"))} {(DQ ($ VSub_Name "$GIT_DAEMON_URL") (/) ($ VSub_Name "$repo"))} {(DQ ($ VSub_At "$@"))} ] redirects: [(Redir op_id:Redir_Great fd:2 arg_word:{(output)} spids:[431])] ) (AndOr children: [ (C {(test_i18ngrep)} { (DQ ("fatal: remote error: ") ($ VSub_Name "$msg") (": /") ($ VSub_Name "$repo")) } {(output)} ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:ret) op: Equal rhs: {($ VSub_QMark "$?")} spids: [451] ) ] spids: [451] ) ] op_id: Op_DAmp ) ] op_id: Op_DAmp ) (C {(chmod)} {(Lit_Other "+") (x)} {(DQ ($ VSub_Name "$GIT_DAEMON_DOCUMENT_ROOT_PATH") (/repo.git))} ) (Subshell child:(C {(exit)} {($ VSub_Name "$ret")}) spids:[466470]) ] spids: [245] ) spids: [241 244] ) (Assignment keyword: Assign_None pairs: [ (assign_pair lhs: (LhsName name:msg) op: Equal rhs: {(DQ ("access denied or repository not exported"))} spids: [475] ) ] spids: [475] ) (C {(test_expect_success)} {(SQ <"clone non-existent">)} {(DQ ("test_remote_error '") ($ VSub_Name "$msg") ("' clone nowhere.git "))} ) (C {(test_expect_success)} {(SQ <"push disabled">)} {(DQ ("test_remote_error '") ($ VSub_Name "$msg") ("' push repo.git master"))} ) (C {(test_expect_success)} {(SQ <"read access denied">)} {(DQ ("test_remote_error -x '") ($ VSub_Name "$msg") ("' fetch repo.git "))} ) (C {(test_expect_success)} {(SQ <"not exported">)} {(DQ ("test_remote_error -n '") ($ VSub_Name "$msg") ("' fetch repo.git "))} ) (C {(stop_git_daemon)}) (C {(start_git_daemon)} {(--informative-errors)}) (C {(test_expect_success)} {(SQ <"clone non-existent">)} {(DQ ("test_remote_error 'no such repository' clone nowhere.git "))} ) (C {(test_expect_success)} {(SQ <"push disabled">)} {(DQ ("test_remote_error 'service not enabled' push repo.git master"))} ) (C {(test_expect_success)} {(SQ <"read access denied">)} {(DQ ("test_remote_error -x 'no such repository' fetch repo.git "))} ) (C {(test_expect_success)} {(SQ <"not exported">)} {(DQ ("test_remote_error -n 'repository not exported' fetch repo.git "))} ) (C {(stop_git_daemon)}) (C {(start_git_daemon)} {(--interpolated-path) (Lit_Other "=") (DQ ($ VSub_Name "$GIT_DAEMON_DOCUMENT_ROOT_PATH") ("/%H%D")) } ) (C {(test_expect_success)} {(SQ <"access repo via interpolated hostname">)} { (SQ <"\n"> <"\trepo=\"$GIT_DAEMON_DOCUMENT_ROOT_PATH/localhost/interp.git\" &&\n"> <"\tgit init --bare \"$repo\" &&\n"> <"\tgit push \"$repo\" HEAD &&\n"> <"\t>\"$repo\"/git-daemon-export-ok &&\n"> <"\trm -rf tmp.git &&\n"> <"\tGIT_OVERRIDE_VIRTUAL_HOST=localhost \\\n"> <"\t\tgit clone --bare \"$GIT_DAEMON_URL/interp.git\" tmp.git &&\n"> <"\trm -rf tmp.git &&\n"> <"\tGIT_OVERRIDE_VIRTUAL_HOST=LOCALHOST \\\n"> <"\t\tgit clone --bare \"$GIT_DAEMON_URL/interp.git\" tmp.git\n"> ) } ) (C {(test_expect_success)} {(SQ <"hostname cannot break out of directory">)} { (SQ <"\n"> <"\trm -rf tmp.git &&\n"> <"\trepo=\"$GIT_DAEMON_DOCUMENT_ROOT_PATH/../escape.git\" &&\n"> <"\tgit init --bare \"$repo\" &&\n"> <"\tgit push \"$repo\" HEAD &&\n"> <"\t>\"$repo\"/git-daemon-export-ok &&\n"> <"\ttest_must_fail \\\n"> <"\t\tenv GIT_OVERRIDE_VIRTUAL_HOST=.. \\\n"> <"\t\tgit clone --bare \"$GIT_DAEMON_URL/escape.git\" tmp.git\n"> ) } ) (C {(stop_git_daemon)}) (C {(test_done)}) ] )