Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 129 additions & 4 deletions src/Oro/Bundle/InstallerBundle/Composer/PermissionsHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,115 @@ class PermissionsHandler
*/
public function setPermissions($directory)
{
// Check ACL support before attempting to use it
if (!$this->isAclSupported()) {
return $this->setPermissionsTraditional($directory);
}

try {
$this->setPermissionsSetfacl($directory);
$this->setPermissionsChmod($directory);

return true;
} catch (ProcessFailedException $exception) {
// Fall back to traditional permissions if ACL fails
return $this->setPermissionsTraditional($directory);
}
}

/**
* Check if ACL is supported on the filesystem
*
* @return bool
*/
protected function isAclSupported(): bool
{
static $aclSupported = null;

if ($aclSupported !== null) {
return $aclSupported;
}

$fs = new Filesystem();
$testDir = 'var/cache';
if (!$fs->exists($testDir)) {
$fs->mkdir($testDir, 0755);
}

$testFile = $testDir . '/.acl_test_' . uniqid();

try {
$fs->touch($testFile);
$user = trim(shell_exec('whoami') ?: 'www-data');

$testCmd = sprintf('setfacl -m "u:%s:rw" %s 2>&1', escapeshellarg($user), escapeshellarg($testFile));
$process = $this->getProcess($testCmd);
$process->setTimeout(2);
$process->run();

if ($process->isSuccessful()) {
$aclSupported = true;
} else {
$output = $process->getErrorOutput() . $process->getOutput();
$aclSupported = stripos($output, 'Operation not supported') === false
&& stripos($output, 'not supported') === false;
}

if ($fs->exists($testFile)) {
$fs->remove($testFile);
}
} catch (\Exception $e) {
$aclSupported = false;
if ($fs->exists($testFile)) {
try {
$fs->remove($testFile);
} catch (\Exception $e2) {
// Ignore cleanup errors
}
}
}

return $aclSupported;
}

/**
* Set permissions using traditional Unix chmod/chown when ACL is not supported
*
* @param string $directory
* @return bool
*/
protected function setPermissionsTraditional($directory): bool
{
$fs = new Filesystem();
if (!$fs->exists($directory)) {
$fs->mkdir($directory);
}

return false;
try {
$user = trim(shell_exec('whoami') ?: 'www-data');
$group = $user;

$webServerUser = $this->runProcessQuiet(self::PS_AUX);
if ($webServerUser) {
$group = $webServerUser;
}

$chownCmd = sprintf('chown -R %s:%s %s', escapeshellarg($user), escapeshellarg($group), escapeshellarg($directory));
$process = $this->getProcess($chownCmd);
$process->run();

$chmodCmd = sprintf('find %s -type d -exec chmod 775 {} +', escapeshellarg($directory));
$process = $this->getProcess($chmodCmd);
$process->run();

$chmodCmd = sprintf('find %s -type f -exec chmod 664 {} +', escapeshellarg($directory));
$process = $this->getProcess($chmodCmd);
$process->run();

return true;
} catch (\Exception $e) {
return false;
}
}

/**
Expand All @@ -44,6 +144,10 @@ public function setPermissions($directory)
*/
public function setPermissionsSetfacl($path)
{
if (!$this->isAclSupported()) {
throw new ProcessFailedException(new Process(['setfacl'], null, null, null, 0));
}

$fs = new Filesystem();
if (!$fs->exists($path)) {
$fs->mkdir($path);
Expand All @@ -68,9 +172,14 @@ public function setPermissionsChmod($path)
}

foreach ($this->getUsers() as $user) {
$this->runProcess(
str_replace([self::VAR_USER, self::VAR_PATH], [$user, $path], self::CHMOD)
);
try {
$this->runProcess(
str_replace([self::VAR_USER, self::VAR_PATH], [$user, $path], self::CHMOD)
);
} catch (ProcessFailedException $e) {
// chmod +a might not be supported, skip it
continue;
}
}
}

Expand Down Expand Up @@ -104,6 +213,21 @@ protected function runProcess($commandline)
return trim($process->getOutput());
}

/**
* Run process without throwing exception on failure
*
* @param string $commandline
* @return string|null
*/
protected function runProcessQuiet($commandline)
{
try {
return $this->runProcess($commandline);
} catch (ProcessFailedException $e) {
return null;
}
}

protected function getProcess(string $commandline): Process
{
if (method_exists(Process::class, 'fromShellCommandline')) {
Expand All @@ -113,3 +237,4 @@ protected function getProcess(string $commandline): Process
}
}
}