Skip to content

Commit

Permalink
Merge pull request #579 from nextcloud/fix/lock-config
Browse files Browse the repository at this point in the history
fix: lock config file when reading and writing
  • Loading branch information
come-nc authored Feb 10, 2025
2 parents f7980c0 + 0e2c455 commit 75e20a0
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 53 deletions.
63 changes: 37 additions & 26 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,7 @@ public function __construct(
) {
$this->nextcloudDir = realpath(dirname($baseDir));

if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
$configFileName = realpath($dir . '/config.php');
} else {
$configFileName = $this->nextcloudDir . '/config/config.php';
}
if (!file_exists($configFileName)) {
throw new \Exception('Could not find config.php. Is this file in the "updater" subfolder of Nextcloud?');
}

/** @var array $CONFIG */
require_once $configFileName;
$this->configValues = $CONFIG;
[$this->configValues] = $this->readConfigFile();

if (php_sapi_name() !== 'cli' && ($this->configValues['upgrade.disable-web'] ?? false)) {
// updater disabled
Expand Down Expand Up @@ -100,6 +89,38 @@ public function __construct(
$this->buildTime = $buildTime;
}

/**
* @return array{array, string}
*/
private function readConfigFile(): array {
if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
$configFileName = realpath($dir . '/config.php');
} else {
$configFileName = $this->nextcloudDir . '/config/config.php';
}
if (!file_exists($configFileName)) {
throw new \Exception('Could not find config.php (' . $configFileName . '). Is this file in the "updater" subfolder of Nextcloud?');
}
$filePointer = @fopen($configFileName, 'r');
if ($filePointer === false) {
throw new \Exception('Could not open config.php (' . $configFileName . ').');
}
if (!flock($filePointer, LOCK_SH)) {
throw new \Exception('Could not acquire a shared lock on the config file (' . $configFileName . ')');
}

try {
require $configFileName;
} finally {
// Close the file pointer and release the lock
flock($filePointer, LOCK_UN);
fclose($filePointer);
}

/** @var array $CONFIG */
return [$CONFIG,$configFileName];
}

/**
* Returns whether the web updater is disabled
*/
Expand Down Expand Up @@ -385,27 +406,17 @@ public function checkWritePermissions(): void {
public function setMaintenanceMode(bool $state): void {
$this->silentLog('[info] setMaintenanceMode("' . ($state ? 'true' : 'false') . '")');

if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
$configFileName = rtrim($dir, '/') . '/config.php';
} else {
$configFileName = $this->nextcloudDir . '/config/config.php';
}
[$CONFIG, $configFileName] = $this->readConfigFile();
$this->silentLog('[info] configFileName ' . $configFileName);

// usually is already tested in the constructor but just to be on the safe side
if (!file_exists($configFileName)) {
throw new \Exception('Could not find config.php.');
}
/** @var array $CONFIG */
require $configFileName;
$CONFIG['maintenance'] = $state;
$content = "<?php\n";
$content .= '$CONFIG = ';
$content .= var_export($CONFIG, true);
$content .= ";\n";
$state = file_put_contents($configFileName, $content);
if ($state === false) {
throw new \Exception('Could not write to config.php');
$writeSuccess = file_put_contents($configFileName, $content, LOCK_EX);
if ($writeSuccess === false) {
throw new \Exception('Could not write to config.php (' . $configFileName . ')');
}
$this->silentLog('[info] end of setMaintenanceMode()');
}
Expand Down
63 changes: 37 additions & 26 deletions lib/Updater.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,7 @@ public function __construct(
) {
$this->nextcloudDir = realpath(dirname($baseDir));

if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
$configFileName = realpath($dir . '/config.php');
} else {
$configFileName = $this->nextcloudDir . '/config/config.php';
}
if (!file_exists($configFileName)) {
throw new \Exception('Could not find config.php. Is this file in the "updater" subfolder of Nextcloud?');
}

/** @var array $CONFIG */
require_once $configFileName;
$this->configValues = $CONFIG;
[$this->configValues] = $this->readConfigFile();

if (php_sapi_name() !== 'cli' && ($this->configValues['upgrade.disable-web'] ?? false)) {
// updater disabled
Expand Down Expand Up @@ -82,6 +71,38 @@ public function __construct(
$this->buildTime = $buildTime;
}

/**
* @return array{array, string}
*/
private function readConfigFile(): array {
if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
$configFileName = realpath($dir . '/config.php');
} else {
$configFileName = $this->nextcloudDir . '/config/config.php';
}
if (!file_exists($configFileName)) {
throw new \Exception('Could not find config.php (' . $configFileName . '). Is this file in the "updater" subfolder of Nextcloud?');
}
$filePointer = @fopen($configFileName, 'r');
if ($filePointer === false) {
throw new \Exception('Could not open config.php (' . $configFileName . ').');
}
if (!flock($filePointer, LOCK_SH)) {
throw new \Exception('Could not acquire a shared lock on the config file (' . $configFileName . ')');
}

try {
require $configFileName;
} finally {
// Close the file pointer and release the lock
flock($filePointer, LOCK_UN);
fclose($filePointer);
}

/** @var array $CONFIG */
return [$CONFIG,$configFileName];
}

/**
* Returns whether the web updater is disabled
*/
Expand Down Expand Up @@ -367,27 +388,17 @@ public function checkWritePermissions(): void {
public function setMaintenanceMode(bool $state): void {
$this->silentLog('[info] setMaintenanceMode("' . ($state ? 'true' : 'false') . '")');

if ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) {
$configFileName = rtrim($dir, '/') . '/config.php';
} else {
$configFileName = $this->nextcloudDir . '/config/config.php';
}
[$CONFIG, $configFileName] = $this->readConfigFile();
$this->silentLog('[info] configFileName ' . $configFileName);

// usually is already tested in the constructor but just to be on the safe side
if (!file_exists($configFileName)) {
throw new \Exception('Could not find config.php.');
}
/** @var array $CONFIG */
require $configFileName;
$CONFIG['maintenance'] = $state;
$content = "<?php\n";
$content .= '$CONFIG = ';
$content .= var_export($CONFIG, true);
$content .= ";\n";
$state = file_put_contents($configFileName, $content);
if ($state === false) {
throw new \Exception('Could not write to config.php');
$writeSuccess = file_put_contents($configFileName, $content, LOCK_EX);
if ($writeSuccess === false) {
throw new \Exception('Could not write to config.php (' . $configFileName . ')');
}
$this->silentLog('[info] end of setMaintenanceMode()');
}
Expand Down
2 changes: 1 addition & 1 deletion tests/features/cli.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Feature: CLI updater
Scenario: No update is available - 25.0.0
Given the current version is 25.0.0
When the CLI updater is run
Then the output should contain "Could not find config.php. Is this file in the "updater" subfolder of Nextcloud?"
Then the output should contain "Could not find config.php"

Scenario: No update is available - 25.0.0
Given the current installed version is 25.0.0
Expand Down
Binary file modified updater.phar
Binary file not shown.

0 comments on commit 75e20a0

Please sign in to comment.