1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-02 18:08:26 +01:00

Parse "shallow" frames in the Git "upload-pack" wire protocol parser

Summary:
Fixes T13309. If you void the warranty on a repository on disk and turn it into a shallow clone, Phabricator currently can't serve it.

We don't support hosting shallow working copies, but we should still parse and proxy the protocol rather than breaking in a mysterious way.

Test Plan:
  - Created a shallow working copy with `mv X X.full; git clone --depth Y file://.../X.full X` in the storage directory on disk.
  - Cloned it with `git clone <uri>`.
  - Deleted all the refs inside it so the wire only has "shallow" frames; cloned it.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13309

Differential Revision: https://secure.phabricator.com/D20577
This commit is contained in:
epriestley 2019-06-13 16:28:30 -07:00
parent 7538286499
commit dda5c13ef5
2 changed files with 80 additions and 39 deletions

View file

@ -187,61 +187,76 @@ final class DiffusionGitUploadPackWireProtocol
// <hash> <ref-name>\0<capabilities>
// <hash> <ref-name>
// ...
//
// See T13309. The end of this list (which may be empty if a repository
// does not have any refs) has a list of zero or more of these:
//
// shallow <hash>
//
// These entries are present if the repository is a shallow clone
// which was made with the "--depth" flag.
//
// Note that "shallow" frames do not advertise capabilities, and if
// a repository has only "shallow" frames, capabilities are never
// advertised.
$bytes = $frame['bytes'];
$matches = array();
if ($is_first) {
$ok = preg_match(
'('.
'^'.
'(?P<hash>[0-9a-f]{40})'.
' '.
'(?P<name>[^\0\n]+)'.
'\0'.
'(?P<capabilities>[^\n]+)'.
'\n'.
'\z'.
')',
$bytes,
$matches);
if (!$ok) {
$capabilities_pattern = '\0(?P<capabilities>[^\n]+)';
} else {
$capabilities_pattern = '';
}
$ok = preg_match(
'('.
'^'.
'(?:'.
'(?P<hash>[0-9a-f]{40}) (?P<name>[^\0\n]+)'.$capabilities_pattern.
'|'.
'shallow (?P<shallow>[0-9a-f]{40})'.
')'.
'\n'.
'\z'.
')',
$bytes,
$matches);
if (!$ok) {
if ($is_first) {
throw new Exception(
pht(
'Unexpected "git upload-pack" initial protocol frame: expected '.
'"<hash> <name>\0<capabilities>\n", got "%s".',
'"<hash> <name>\0<capabilities>\n", or '.
'"shallow <hash>\n", got "%s".',
$bytes));
}
} else {
$ok = preg_match(
'('.
'^'.
'(?P<hash>[0-9a-f]{40})'.
' '.
'(?P<name>[^\0\n]+)'.
'\n'.
'\z'.
')',
$bytes,
$matches);
if (!$ok) {
} else {
throw new Exception(
pht(
'Unexpected "git upload-pack" protocol frame: expected '.
'"<hash> <name>\n", got "%s".',
'"<hash> <name>\n", or "shallow <hash>\n", got "%s".',
$bytes));
}
}
$hash = $matches['hash'];
$name = $matches['name'];
if (isset($matches['shallow'])) {
$name = null;
$hash = $matches['shallow'];
$is_shallow = true;
} else {
$name = $matches['name'];
$hash = $matches['hash'];
$is_shallow = false;
}
if ($is_first) {
if (isset($matches['capabilities'])) {
$capabilities = $matches['capabilities'];
}
$refs[] = array(
'hash' => $hash,
'name' => $name,
'shallow' => $is_shallow,
);
}
@ -252,10 +267,16 @@ final class DiffusionGitUploadPackWireProtocol
->setCapabilities($capabilities);
foreach ($refs as $ref) {
$ref_list->addRef(
id(new DiffusionGitWireProtocolRef())
->setName($ref['name'])
->setHash($ref['hash']));
$wire_ref = id(new DiffusionGitWireProtocolRef())
->setHash($ref['hash']);
if ($ref['shallow']) {
$wire_ref->setIsShallow(true);
} else {
$wire_ref->setName($ref['name']);
}
$ref_list->addRef($wire_ref);
}
// TODO: Here, we have a structured list of refs. In a future change,
@ -275,10 +296,19 @@ final class DiffusionGitUploadPackWireProtocol
// a little surprising, but is consistent with the native behavior of the
// protocol.
// Likewise, we don't send back any capabilities if we're sending only
// "shallow" frames.
$output = array();
$is_first = true;
foreach ($refs as $ref) {
if ($is_first) {
$is_shallow = $ref->getIsShallow();
if ($is_shallow) {
$result = sprintf(
"shallow %s\n",
$ref->getHash());
} else if ($is_first) {
$result = sprintf(
"%s %s\0%s\n",
$ref->getHash(),

View file

@ -5,6 +5,7 @@ final class DiffusionGitWireProtocolRef
private $name;
private $hash;
private $isShallow;
public function setName($name) {
$this->name = $name;
@ -24,9 +25,19 @@ final class DiffusionGitWireProtocolRef
return $this->hash;
}
public function setIsShallow($is_shallow) {
$this->isShallow = $is_shallow;
return $this;
}
public function getIsShallow() {
return $this->isShallow;
}
public function newSortVector() {
return id(new PhutilSortVector())
->addString($this->getName());
->addInt((int)$this->getIsShallow())
->addString((string)$this->getName());
}
}