From 4f41d1118c531e009ba1b468949c694e86d2a5f0 Mon Sep 17 00:00:00 2001 From: Filippo Tessarotto Date: Sun, 22 Apr 2018 08:49:07 +0200 Subject: [PATCH] FileCache: rebuild cache file before touch when different file owner (#16120) * Test Travis envs * The fix * posix_geteuid only exists on Linux * Added PR to changelog --- .travis.yml | 4 +++ framework/CHANGELOG.md | 1 + framework/caching/FileCache.php | 6 ++++ tests/framework/caching/FileCacheTest.php | 35 +++++++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/.travis.yml b/.travis.yml index f5f27c03ca..ac5cb295bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,7 @@ env: - TASK_TESTS_PHP=1 - TASK_TESTS_JS=0 - TASK_TESTS_COVERAGE=0 + - TRAVIS_SECOND_USER=travis_two services: @@ -147,6 +148,9 @@ install: travis_retry npm install fi + # Needed for FileCacheTest + - sudo useradd $TRAVIS_SECOND_USER --gid $(id -g) -M + before_script: # # Disable: diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 972049ba9f..2233263001 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -10,6 +10,7 @@ Yii Framework 2 Change Log - Bug #15988: Fixed bash completion (alekciy) - Bug #15117: Fixed `yii\db\Schema::getTableMetadata` cache refreshing (boboldehampsink) - Bug #16073: Fixed regression in Oracle `IN` condition builder for more than 1000 items (cebe) +- Bug #16120: FileCache: rebuild cache file before touch when different file owner (Slamdunk) 2.0.15.1 March 21, 2018 diff --git a/framework/caching/FileCache.php b/framework/caching/FileCache.php index d884f8097d..08f292084e 100644 --- a/framework/caching/FileCache.php +++ b/framework/caching/FileCache.php @@ -141,6 +141,12 @@ class FileCache extends Cache if ($this->directoryLevel > 0) { @FileHelper::createDirectory(dirname($cacheFile), $this->dirMode, true); } + // If ownership differs the touch call will fail, so we try to + // rebuild the file from scratch by deleting it first + // https://github.com/yiisoft/yii2/pull/16120 + if (is_file($cacheFile) && function_exists('posix_geteuid') && fileowner($cacheFile) !== posix_geteuid()) { + @unlink($cacheFile); + } if (@file_put_contents($cacheFile, $value, LOCK_EX) !== false) { if ($this->fileMode !== null) { @chmod($cacheFile, $this->fileMode); diff --git a/tests/framework/caching/FileCacheTest.php b/tests/framework/caching/FileCacheTest.php index 5c6dbd130e..3f19ee6aca 100644 --- a/tests/framework/caching/FileCacheTest.php +++ b/tests/framework/caching/FileCacheTest.php @@ -52,4 +52,39 @@ class FileCacheTest extends CacheTestCase static::$time++; $this->assertFalse($cache->get('expire_testa')); } + + public function testCacheRenewalOnDifferentOwnership() + { + $TRAVIS_SECOND_USER = getenv('TRAVIS_SECOND_USER'); + if (empty($TRAVIS_SECOND_USER)) { + $this->markTestSkipped('Travis second user not found'); + } + + $cache = $this->getCacheInstance(); + + $cacheValue = uniqid('value_'); + $cachePublicKey = uniqid('key_'); + $cacheInternalKey = $cache->buildKey($cachePublicKey); + + static::$time = \time(); + $this->assertTrue($cache->set($cachePublicKey, $cacheValue, 2)); + $this->assertSame($cacheValue, $cache->get($cachePublicKey)); + + $refClass = new \ReflectionClass($cache); + $refMethodGetCacheFile = $refClass->getMethod('getCacheFile'); + $refMethodGetCacheFile->setAccessible(true); + $cacheFile = $refMethodGetCacheFile->invoke($cache, $cacheInternalKey); + $refMethodGetCacheFile->setAccessible(false); + + $output = array(); + $returnVar = null; + exec(sprintf('sudo chown %s %s', + escapeshellarg($TRAVIS_SECOND_USER), + escapeshellarg($cacheFile) + ), $output, $returnVar); + + $this->assertSame(0, $returnVar, 'Cannot change ownership of cache file to test cache renewal'); + + $this->assertTrue($cache->set($cachePublicKey, uniqid('value_2_'), 2), 'Cannot rebuild cache on different file ownership'); + } }