Page MenuHomePhabricator

D19817.diff
No OneTemporary

D19817.diff

diff --git a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php
--- a/src/applications/diffusion/engine/DiffusionCommitHookEngine.php
+++ b/src/applications/diffusion/engine/DiffusionCommitHookEngine.php
@@ -164,6 +164,16 @@
$this->applyHeraldRefRules($ref_updates);
}
+ try {
+ if (!$is_initial_import) {
+ $this->rejectOversizedFiles($content_updates);
+ }
+ } catch (DiffusionCommitHookRejectException $ex) {
+ // If we're rejecting oversized files, flag everything.
+ $this->rejectCode = PhabricatorRepositoryPushLog::REJECT_OVERSIZED;
+ throw $ex;
+ }
+
try {
if (!$is_initial_import) {
$this->rejectEnormousChanges($content_updates);
@@ -1255,6 +1265,139 @@
return $changesets;
}
+ private function rejectOversizedFiles(array $content_updates) {
+ $repository = $this->getRepository();
+
+ // TODO: Allow repositories to be configured for a maximum filesize.
+ $limit = 0;
+
+ if (!$limit) {
+ return;
+ }
+
+ foreach ($content_updates as $update) {
+ $identifier = $update->getRefNew();
+
+ $sizes = $this->loadFileSizesForCommit($identifier);
+ foreach ($sizes as $path => $size) {
+ if ($size <= $limit) {
+ continue;
+ }
+
+ $message = pht(
+ 'OVERSIZED FILE'.
+ "\n".
+ 'This repository ("%s") is configured with a maximum individual '.
+ 'file size limit, but you are pushing a change ("%s") which causes '.
+ 'the size of a file ("%s") to exceed the limit. The commit makes '.
+ 'the file %s bytes long, but the limit for this repository is '.
+ '%s bytes.',
+ $repository->getDisplayName(),
+ $identifier,
+ $path,
+ new PhutilNumber($size),
+ new PhutilNumber($limit));
+
+ throw new DiffusionCommitHookRejectException($message);
+ }
+ }
+ }
+
+ public function loadFileSizesForCommit($identifier) {
+ $repository = $this->getRepository();
+ $vcs = $repository->getVersionControlSystem();
+
+ $path_sizes = array();
+
+ switch ($vcs) {
+ case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
+ list($paths_raw) = $repository->execxLocalCommand(
+ 'diff-tree -z -r --no-commit-id %s --',
+ $identifier);
+
+ // With "-z" we get "<fields>\0<filename>\0" for each line. Group the
+ // delimited text into "<fields>, <filename>" pairs.
+ $paths_raw = trim($paths_raw, "\0");
+ $paths_raw = explode("\0", $paths_raw);
+ if (count($paths_raw) % 2) {
+ throw new Exception(
+ pht(
+ 'Unexpected number of output lines from "git diff-tree" when '.
+ 'processing commit ("%s"): got %s lines, expected an even '.
+ 'number.',
+ $identifier,
+ phutil_count($paths_raw)));
+ }
+ $paths_raw = array_chunk($paths_raw, 2);
+
+ $paths = array();
+ foreach ($paths_raw as $path_raw) {
+ list($fields, $pathname) = $path_raw;
+ $fields = explode(' ', $fields);
+
+ // Fields are:
+ //
+ // :100644 100644 aaaa bbbb M
+ //
+ // [0] Old file mode.
+ // [1] New file mode.
+ // [2] Old object hash.
+ // [3] New object hash.
+ // [4] Change mode.
+
+ $paths[] = array(
+ 'path' => $pathname,
+ 'newHash' => $fields[3],
+ );
+ }
+
+ if ($paths) {
+ $check_paths = array();
+ foreach ($paths as $path) {
+ if ($path['newHash'] === self::EMPTY_HASH) {
+ $path_sizes[$path['path']] = 0;
+ continue;
+ }
+ $check_paths[$path['newHash']][] = $path['path'];
+ }
+
+ if ($check_paths) {
+ $future = $repository->getLocalCommandFuture(
+ 'cat-file --batch-check=%s',
+ '%(objectsize)')
+ ->write(implode("\n", array_keys($check_paths)));
+
+ list($sizes) = $future->resolvex();
+ $sizes = trim($sizes);
+ $sizes = phutil_split_lines($sizes, false);
+ if (count($sizes) !== count($check_paths)) {
+ throw new Exception(
+ pht(
+ 'Unexpected number of output lines from "git cat-file" when '.
+ 'processing commit ("%s"): got %s lines, expected %s.',
+ $identifier,
+ phutil_count($sizes),
+ phutil_count($check_paths)));
+ }
+
+ foreach ($check_paths as $object_hash => $path_names) {
+ $object_size = (int)array_shift($sizes);
+ foreach ($path_names as $path_name) {
+ $path_sizes[$path_name] = $object_size;
+ }
+ }
+ }
+ }
+ break;
+ default:
+ throw new Exception(
+ pht(
+ 'File size limits are not supported for this VCS.'));
+ }
+
+ return $path_sizes;
+ }
+
public function loadCommitRefForCommit($identifier) {
$repository = $this->getRepository();
$vcs = $repository->getVersionControlSystem();
diff --git a/src/applications/repository/storage/PhabricatorRepositoryPushLog.php b/src/applications/repository/storage/PhabricatorRepositoryPushLog.php
--- a/src/applications/repository/storage/PhabricatorRepositoryPushLog.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryPushLog.php
@@ -24,6 +24,7 @@
const CHANGEFLAG_REWRITE = 8;
const CHANGEFLAG_DANGEROUS = 16;
const CHANGEFLAG_ENORMOUS = 32;
+ const CHANGEFLAG_OVERSIZED = 64;
const REJECT_ACCEPT = 0;
const REJECT_DANGEROUS = 1;
@@ -31,6 +32,7 @@
const REJECT_EXTERNAL = 3;
const REJECT_BROKEN = 4;
const REJECT_ENORMOUS = 5;
+ const REJECT_OVERSIZED = 6;
protected $repositoryPHID;
protected $epoch;
@@ -63,6 +65,7 @@
self::CHANGEFLAG_REWRITE => pht('Rewrite'),
self::CHANGEFLAG_DANGEROUS => pht('Dangerous'),
self::CHANGEFLAG_ENORMOUS => pht('Enormous'),
+ self::CHANGEFLAG_OVERSIZED => pht('Oversized'),
);
}
@@ -74,6 +77,7 @@
self::REJECT_EXTERNAL => pht('Rejected: External Hook'),
self::REJECT_BROKEN => pht('Rejected: Broken'),
self::REJECT_ENORMOUS => pht('Rejected: Enormous'),
+ self::REJECT_OVERSIZED => pht('Rejected: Oversized File'),
);
}

File Metadata

Mime Type
text/plain
Expires
Sun, Mar 23, 9:42 AM (21 h, 13 m ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7389544
Default Alt Text
D19817.diff (6 KB)

Event Timeline