Skip to content

Commit

Permalink
VerifyChecksum Command #30951
Browse files Browse the repository at this point in the history
  • Loading branch information
IljaN committed Apr 5, 2018
1 parent 03f0462 commit 21893d6
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 0 deletions.
1 change: 1 addition & 0 deletions apps/files/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<command>OCA\Files\Command\Scan</command>
<command>OCA\Files\Command\DeleteOrphanedFiles</command>
<command>OCA\Files\Command\TransferOwnership</command>
<command>OCA\Files\Command\VerifyChecksums</command>
</commands>

<navigation>
Expand Down
142 changes: 142 additions & 0 deletions apps/files/lib/Command/VerifyChecksums.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php
/**
* @author Ilja Neumann <[email protected]>
*
* @copyright Copyright (c) 2018, ownCloud GmbH
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

namespace OCA\Files\Command;


use OC\DB\Connection;
use OCP\Files\Storage\IStorage;
use OCP\IDBConnection;
use OCP\IUser;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;


class VerifyChecksums extends Command {


private $fieIdsWithBrokenChecksums = [];

protected function configure() {
$this
->setName('files:checksums:verify')
->setDescription('Get all checksums in filecache and compares them by recalculating the checksum of the file. (Might take very long)')
->addArgument('repair', InputArgument::OPTIONAL, "Reset cacheentry with missmatched checksums. They will be recalculated on first download.")
->addOption('path', 'p', InputOption::VALUE_REQUIRED, "Specific path to check");
}


private static function calculateActualChecksums($path, IStorage $storage) {
return sprintf(
'SHA1:%s MD5:%s ADLER32:%s',
$storage->hash('sha1', $path),
$storage->hash('md5', $path),
$storage->hash('adler32', $path)
);
}

public function execute(InputInterface $input, OutputInterface $output) {
\OC::$server->getUserManager()->callForAllUsers(function(IUser $user) use ($output) {
$scanner = new \OC\Files\Utils\Scanner(
$user->getUID(),
$this->reconnectToDatabase($output) ,
\OC::$server->getLogger()
);

$rootFolder = \OC::$server->getRootFolder();
$scanner->listen("\OC\Files\Utils\Scanner", 'scanFile', function ($path) use ($output, $rootFolder, $user) {
try {
$file = $rootFolder->get($path);
$currentChecksums = $file->getChecksum();
} catch (\BadMethodCallException $ex) {

}

// Files without calculated checksum can`t cause checksum errors
if (empty($currentChecksums)) {
return;
}

// StorageWrappers use getSourcePath() internally which already prepends the username to the path
// so we need to remove it here or else we will get paths like admin//admin/thumbnails/4/32-32.png
$pathWithoutUid = ltrim($path, "/{$user->getUID()}");
$actualChecksums = self::calculateActualChecksums($pathWithoutUid, $file->getStorage());

if ($actualChecksums !== $currentChecksums) {
$this->fieIdsWithBrokenChecksums[] = $file;
$output->writeln("Missmatch for $path:\n Filecache:\t$currentChecksums\n Actual:\t$actualChecksums");
}
});

$scanner->scan();

});

/** @var QuestionHelper $questionHelper */
$questionHelper = $this->getHelper('question');
$repairQuestion = new ConfirmationQuestion("Do you want to reset (repair) broken checksums (y/N)? ", false);

if (!empty($this->fieIdsWithBrokenChecksums)) {
if ($input->getArgument('repair') || $questionHelper->ask($input, $output, $repairQuestion)) {
self::resetChecksumsForFileIds(array_unique($this->fieIdsWithBrokenChecksums));
return;
}
}
}

/**
* @param $files
*/
private static function resetChecksumsForFileIds(array $files) {
foreach ($files as $file) {
$storage = $file->getStorage();
$cache = $storage->getCache();
$cache->update($file->getId(), ['checksum' => '']);
}
}

/**
* @return \OCP\IDBConnection
*/
protected function reconnectToDatabase(OutputInterface $output) {
/** @var Connection | IDBConnection $connection*/
$connection = \OC::$server->getDatabaseConnection();
try {
$connection->close();
} catch (\Exception $ex) {
$output->writeln("<info>Error while disconnecting from database: {$ex->getMessage()}</info>");
}
while (!$connection->isConnected()) {
try {
$connection->connect();
} catch (\Exception $ex) {
$output->writeln("<info>Error while re-connecting to database: {$ex->getMessage()}</info>");
sleep(60);
}
}
return $connection;
}
}

0 comments on commit 21893d6

Please sign in to comment.