From ed5424699407cda9a53d4b6c5ae61efb4d773512 Mon Sep 17 00:00:00 2001 From: Michael Donat Date: Thu, 7 Nov 2013 20:54:29 +0000 Subject: [PATCH 1/4] added tmp to ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e423313..6e7bae2 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea vendor build +tmp \ No newline at end of file From a65ba4a91792e1bb9a8ed28b6b5b2750936c7cee Mon Sep 17 00:00:00 2001 From: Michael Donat Date: Thu, 7 Nov 2013 20:55:08 +0000 Subject: [PATCH 2/4] renamed isWriteable/isReadable to more meaningful --- src/VirtualFileSystem/Wrapper.php | 4 ++-- src/VirtualFileSystem/Wrapper/FileHandler.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/VirtualFileSystem/Wrapper.php b/src/VirtualFileSystem/Wrapper.php index 120168e..0499e96 100644 --- a/src/VirtualFileSystem/Wrapper.php +++ b/src/VirtualFileSystem/Wrapper.php @@ -193,7 +193,7 @@ public function stream_open($path, $mode, $options, &$opened_path) */ public function stream_write($data) { - if (!$this->currently_opened->isWritable()) { + if (!$this->currently_opened->isOpenedForWriting()) { return false; } //file access time changes so stat cache needs to be cleared @@ -253,7 +253,7 @@ public function url_stat($path, $flags) */ public function stream_read($bytes) { - if (!$this->currently_opened->isReadable()) { + if (!$this->currently_opened->isOpenedForReading()) { return null; } $data = $this->currently_opened->read($bytes); diff --git a/src/VirtualFileSystem/Wrapper/FileHandler.php b/src/VirtualFileSystem/Wrapper/FileHandler.php index 66f88a8..225877f 100644 --- a/src/VirtualFileSystem/Wrapper/FileHandler.php +++ b/src/VirtualFileSystem/Wrapper/FileHandler.php @@ -173,7 +173,7 @@ public function setWriteOnlyMode() * * @return bool */ - public function isWritable() + public function isOpenedForWriting() { return (bool)($this->mode & self::WRITE_MODE); } @@ -183,7 +183,7 @@ public function isWritable() * * @return bool */ - public function isReadable() + public function isOpenedForReading() { return (bool)($this->mode & self::READ_MODE); } From 4bed9e958f025eeb077165dc7faf0bd3250a821d Mon Sep 17 00:00:00 2001 From: Michael Donat Date: Fri, 8 Nov 2013 09:38:34 +0000 Subject: [PATCH 3/4] added PermissionHelper to provide isWriteable/isReadable methods --- .../Wrapper/PermissionHelper.php | 136 +++++++++++++ .../Wrapper/PermissionHelperTest.php | 185 ++++++++++++++++++ 2 files changed, 321 insertions(+) create mode 100644 src/VirtualFileSystem/Wrapper/PermissionHelper.php create mode 100644 tests/VirtualFileSystem/Wrapper/PermissionHelperTest.php diff --git a/src/VirtualFileSystem/Wrapper/PermissionHelper.php b/src/VirtualFileSystem/Wrapper/PermissionHelper.php new file mode 100644 index 0000000..1f9d038 --- /dev/null +++ b/src/VirtualFileSystem/Wrapper/PermissionHelper.php @@ -0,0 +1,136 @@ +node = $node; + $this->userid = posix_getuid(); + $this->groupid = posix_getgid(); + } + + /** + * Checks whether user_id on file is the same as executing user + * + * @return bool + */ + public function userIsOwner() + { + return (bool)($this->userid == $this->node->user()); + } + + /** + * Checks whether file is readable for user + * + * @return bool + */ + public function userCanRead() + { + return $this->userIsOwner() && ($this->node->mode() & self::MODE_USER_READ); + } + + /** + * Checks whether file is writable for user + * + * @return bool + */ + public function userCanWrite() + { + return $this->userIsOwner() && ($this->node->mode() & self::MODE_USER_WRITE); + } + + /** + * Checks whether group_id on file is the same as executing user + * + * @return bool + */ + public function groupIsOwner() + { + return (bool)($this->groupid == $this->node->group()); + } + + /** + * Checks whether file is readable for group + * + * @return bool + */ + public function groupCanRead() + { + return $this->groupIsOwner() && ($this->node->mode() & self::MODE_GROUP_READ); + } + + /** + * Checks whether file is writable for group + * + * @return bool + */ + public function groupCanWrite() + { + return $this->groupIsOwner() && ($this->node->mode() & self::MODE_GROUP_WRITE); + } + + /** + * Checks whether file is readable for world + * + * @return bool + */ + public function worldCanRead() + { + return (bool)($this->node->mode() & self::MODE_WORLD_READ); + } + + /** + * Checks whether file is writable for world + * + * @return bool + */ + public function worldCanWrite() + { + return (bool)($this->node->mode() & self::MODE_WORLD_WRITE); + } + + /** + * Checks whether file is readable (by user, group or world) + * + * @return bool + */ + public function isReadable() + { + return $this->userCanRead() || $this->groupCanRead() || $this->worldCanRead(); + } + + /** + * Checks whether file is writable (by user, group or world) + * + * @return bool + */ + public function isWritable() + { + return $this->userCanWrite() || $this->groupCanWrite() || $this->worldCanWrite(); + } +} diff --git a/tests/VirtualFileSystem/Wrapper/PermissionHelperTest.php b/tests/VirtualFileSystem/Wrapper/PermissionHelperTest.php new file mode 100644 index 0000000..73f1aeb --- /dev/null +++ b/tests/VirtualFileSystem/Wrapper/PermissionHelperTest.php @@ -0,0 +1,185 @@ +chown(posix_getuid()); + + $ph = new PermissionHelper($file); + + $file->chmod(0000); + $this->assertFalse($ph->userCanRead(), 'User can\'t read with 0000'); + $this->assertFalse($ph->userCanWrite(), 'User can\'t write with 0000'); + + $file->chmod(0100); + $this->assertFalse($ph->userCanRead(), 'User can\'t read with 0100'); + $this->assertFalse($ph->userCanWrite(), 'User can\'t write with 0100'); + + $file->chmod(0200); + $this->assertFalse($ph->userCanRead(), 'User can\'t read with 0200'); + $this->assertTrue($ph->userCanWrite(), 'User can write with 0200'); + + $file->chmod(0400); + $this->assertTrue($ph->userCanRead(), 'User can read with 0400'); + $this->assertFalse($ph->userCanWrite(), 'User can\'t write with 0400'); + + $file->chmod(0500); + $this->assertTrue($ph->userCanRead(), 'User can read with 0500'); + $this->assertFalse($ph->userCanWrite(), 'User can\'t write with 0500'); + + $file->chmod(0600); + $this->assertTrue($ph->userCanRead(), 'User can read with 0600'); + $this->assertTrue($ph->userCanWrite(), 'User can read with 0600'); + + $file->chown(1); + $file->chmod(0666); + + $this->assertFalse($ph->userCanRead(), 'User can\'t read without ownership'); + $this->assertFalse($ph->userCanWrite(), 'User can\'t without ownership'); + } + + public function testGroupPermissionsAreCalculatedCorrectly() + { + $file = new File('file'); + $file->chgrp(posix_getgid()); + + $ph = new PermissionHelper($file); + + $file->chmod(0000); + $this->assertFalse($ph->groupCanRead(), 'group can\'t read with 0000'); + $this->assertFalse($ph->groupCanWrite(), 'group can\'t write with 0000'); + + $file->chmod(0010); + $this->assertFalse($ph->groupCanRead(), 'group can\'t read with 0010'); + $this->assertFalse($ph->groupCanWrite(), 'group can\'t write with 0010'); + + $file->chmod(0020); + $this->assertFalse($ph->groupCanRead(), 'group can\'t read with 0020'); + $this->assertTrue($ph->groupCanWrite(), 'group can write with 0020'); + + $file->chmod(0040); + $this->assertTrue($ph->groupCanRead(), 'group can read with 0040'); + $this->assertFalse($ph->groupCanWrite(), 'group can\'t write with 0040'); + + $file->chmod(0050); + $this->assertTrue($ph->groupCanRead(), 'group can read with 0050'); + $this->assertFalse($ph->groupCanWrite(), 'group can\'t write with 0050'); + + $file->chmod(0060); + $this->assertTrue($ph->groupCanRead(), 'group can read with 0060'); + $this->assertTrue($ph->groupCanWrite(), 'group can read with 0060'); + + $file->chgrp(0); + $file->chmod(0666); + + $this->assertFalse($ph->groupCanRead(), 'group can\'t read without ownership'); + $this->assertFalse($ph->groupCanWrite(), 'group can\'t without ownership'); + } + + public function testWorldPermissionsAreCalculatedCorrectly() + { + $file = new File('file'); + + $ph = new PermissionHelper($file); + + $file->chmod(0000); + $this->assertFalse($ph->worldCanRead(), 'world can\'t read with 0000'); + $this->assertFalse($ph->worldCanWrite(), 'world can\'t write with 0000'); + + $file->chmod(0001); + $this->assertFalse($ph->worldCanRead(), 'world can\'t read with 0001'); + $this->assertFalse($ph->worldCanWrite(), 'world can\'t write with 0001'); + + $file->chmod(0002); + $this->assertFalse($ph->worldCanRead(), 'world can\'t read with 0002'); + $this->assertTrue($ph->worldCanWrite(), 'world can write with 0002'); + + $file->chmod(0004); + $this->assertTrue($ph->worldCanRead(), 'world can read with 0004'); + $this->assertFalse($ph->worldCanWrite(), 'world can\'t write with 0004'); + + $file->chmod(0005); + $this->assertTrue($ph->worldCanRead(), 'world can read with 0005'); + $this->assertFalse($ph->worldCanWrite(), 'world can\'t write with 0005'); + + $file->chmod(0006); + $this->assertTrue($ph->worldCanRead(), 'world can read with 0006'); + $this->assertTrue($ph->worldCanWrite(), 'world can read with 0006'); + + } + + public function testIsReadable() + { + $file = new File('file'); + + $ph = new PermissionHelper($file); + + $file->chmod(0000); + $file->chown(0); + $file->chgrp(0); + $this->assertFalse($ph->isReadable(), 'File is not readable root:root 0000'); + + $file->chmod(0400); + $file->chown(0); + $file->chgrp(0); + $this->assertFalse($ph->isReadable(), 'File is not readable root:root 0400'); + + $file->chmod(0040); + $file->chown(0); + $file->chgrp(0); + $this->assertFalse($ph->isReadable(), 'File is not readable root:root 0040'); + + $file->chmod(0004); + $file->chown(0); + $file->chgrp(0); + $this->assertTrue($ph->isReadable(), 'File is readable root:root 0004'); + + $file->chmod(0000); + $file->chown(posix_getuid()); + $file->chgrp(0); + $this->assertFalse($ph->isReadable(), 'File is not readable user:root 0000'); + + $file->chmod(0400); + $file->chown(posix_getuid()); + $file->chgrp(0); + $this->assertTrue($ph->isReadable(), 'File is readable user:root 0400'); + + $file->chmod(0040); + $file->chown(posix_getuid()); + $file->chgrp(0); + $this->assertFalse($ph->isReadable(), 'File is not readable user:root 0040'); + + $file->chmod(0004); + $file->chown(posix_getuid()); + $file->chgrp(0); + $this->assertTrue($ph->isReadable(), 'File is readable user:root 0004'); + + $file->chmod(0000); + $file->chown(0); + $file->chgrp(posix_getgid()); + $this->assertFalse($ph->isReadable(), 'File is not readable root:user 0000'); + + $file->chmod(0040); + $file->chown(0); + $file->chgrp(posix_getgid()); + $this->assertTrue($ph->isReadable(), 'File is readable root:user 0040'); + + $file->chmod(0400); + $file->chown(0); + $file->chgrp(posix_getgid()); + $this->assertFalse($ph->isReadable(), 'File is not readable root:user 0400'); + + $file->chmod(0004); + $file->chown(0); + $file->chgrp(posix_getgid()); + $this->assertTrue($ph->isReadable(), 'File is readable root:user 0004'); + } +} From ddde6442969739f61bacebcabf0df3ed676ee6fc Mon Sep 17 00:00:00 2001 From: Michael Donat Date: Fri, 8 Nov 2013 10:46:28 +0000 Subject: [PATCH 4/4] added v1 support for file permissions --- src/VirtualFileSystem/Structure/Node.php | 2 +- src/VirtualFileSystem/Wrapper.php | 41 +++- .../VirtualFileSystem/Structure/NodeTest.php | 2 +- tests/VirtualFileSystem/WrapperTest.php | 206 ++++++++++++++++++ 4 files changed, 248 insertions(+), 3 deletions(-) diff --git a/src/VirtualFileSystem/Structure/Node.php b/src/VirtualFileSystem/Structure/Node.php index 0cafce1..a070395 100644 --- a/src/VirtualFileSystem/Structure/Node.php +++ b/src/VirtualFileSystem/Structure/Node.php @@ -19,7 +19,7 @@ abstract class Node { const S_IFMT = 0160000; - const DEF_MODE = 0000; + const DEF_MODE = 0755; protected $basename; protected $parent; diff --git a/src/VirtualFileSystem/Wrapper.php b/src/VirtualFileSystem/Wrapper.php index 0499e96..43628c1 100644 --- a/src/VirtualFileSystem/Wrapper.php +++ b/src/VirtualFileSystem/Wrapper.php @@ -14,6 +14,7 @@ use VirtualFileSystem\Structure\File; use VirtualFileSystem\Wrapper\FileHandler; use VirtualFileSystem\Wrapper\DirectoryHandler; +use VirtualFileSystem\Wrapper\PermissionHelper; /** * Stream wrapper class. This is the class that PHP uses as the stream operations handler. @@ -145,12 +146,19 @@ public function stream_open($path, $mode, $options, &$opened_path) return false; } $parent = $container->fileAt(dirname($path)); + $ph = new PermissionHelper($parent); + if (!$ph->isWritable()) { + if ($options & STREAM_REPORT_ERRORS) { + trigger_error(sprintf('%s: failed to open stream: Permission denied', $path), E_USER_WARNING); + } + return false; + } $parent->addFile($container->factory()->getFile(basename($path))); } $file = $container->fileAt($path); - if (($extended || $writeMode) && $file instanceof Directory) { + if (($extended || $writeMode || $appendMode) && $file instanceof Directory) { if ($options & STREAM_REPORT_ERRORS) { trigger_error(sprintf('fopen(%s): failed to open stream: Is a directory', $path), E_USER_WARNING); } @@ -158,16 +166,40 @@ public function stream_open($path, $mode, $options, &$opened_path) } if ($file instanceof Directory) { + $dir = $file; $file = $container->factory()->getFile('tmp'); + $file->chmod($dir->mode()); + $file->chown($dir->user()); + $file->chgrp($dir->group()); } + $permissionHelper = new PermissionHelper($file); + $this->currently_opened = new FileHandler(); $this->currently_opened->setFile($file); if ($extended) { + if (!$permissionHelper->isReadable() or !$permissionHelper->isWritable()) { + if ($options & STREAM_REPORT_ERRORS) { + trigger_error(sprintf('fopen(%s): failed to open stream: Permission denied', $path), E_USER_WARNING); + } + return false; + } $this->currently_opened->setReadWriteMode(); } elseif ($readMode) { + if (!$permissionHelper->isReadable()) { + if ($options & STREAM_REPORT_ERRORS) { + trigger_error(sprintf('fopen(%s): failed to open stream: Permission denied', $path), E_USER_WARNING); + } + return false; + } $this->currently_opened->setReadOnlyMode(); } else { // a or w are for write only + if (!$permissionHelper->isWritable()) { + if ($options & STREAM_REPORT_ERRORS) { + trigger_error(sprintf('fopen(%s): failed to open stream: Permission denied', $path), E_USER_WARNING); + } + return false; + } $this->currently_opened->setWriteOnlyMode(); } @@ -542,6 +574,13 @@ public function dir_opendir($path, $options) return false; } + $permissionHelper = new PermissionHelper($dir); + + if (!$permissionHelper->isReadable()) { + trigger_error(sprintf('opendir(%s): failed to open dir: Permission denied', $path), E_USER_WARNING); + return false; + } + $this->currently_opened = new DirectoryHandler(); $this->currently_opened->setDirectory($dir); diff --git a/tests/VirtualFileSystem/Structure/NodeTest.php b/tests/VirtualFileSystem/Structure/NodeTest.php index eb56129..3f96374 100755 --- a/tests/VirtualFileSystem/Structure/NodeTest.php +++ b/tests/VirtualFileSystem/Structure/NodeTest.php @@ -14,7 +14,7 @@ public function testChmod() { $file = new File('file'); - $this->assertEquals(0000 | File::S_IFTYPE, $file->mode()); + $this->assertEquals(Node::DEF_MODE | File::S_IFTYPE, $file->mode()); $file->chmod(0200); $this->assertEquals(0200 | File::S_IFTYPE, $file->mode()); diff --git a/tests/VirtualFileSystem/WrapperTest.php b/tests/VirtualFileSystem/WrapperTest.php index 64885df..f3ecf60 100644 --- a/tests/VirtualFileSystem/WrapperTest.php +++ b/tests/VirtualFileSystem/WrapperTest.php @@ -760,6 +760,8 @@ public function testStreamOpenDoesNotOpenDirectoriesForWriting() $this->assertFalse(@fopen($fs->path('/dir'), 'w')); $this->assertFalse(@fopen($fs->path('/dir'), 'r+')); $this->assertFalse(@fopen($fs->path('/dir'), 'w+')); + $this->assertFalse(@fopen($fs->path('/dir'), 'a')); + $this->assertFalse(@fopen($fs->path('/dir'), 'a+')); $opened_path = null; @@ -786,4 +788,208 @@ public function testStreamOpenAllowsForDirectoryOpeningForReadingAndReturnsEmpty $this->assertEmpty(fread($handle, 1)); } + + public function testPermissionsAreCheckedWhenOpeningFiles() + { + $fs = new FileSystem(); + $file = $fs->container()->createFile('/file'); + + $wr = new Wrapper(); + + $file->chmod(0000); + $file->chown(0); + $file->chgrp(0); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'r', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'r+', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'w', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'w+', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'a', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'a+', 0, $path)); + + $file->chmod(0400); + $file->chown(posix_getuid()); + $file->chgrp(0); + $this->assertTrue($wr->stream_open($fs->path('/file'), 'r', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'r+', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'w', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'w+', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'a', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'a+', 0, $path)); + + $file->chmod(0200); + $file->chown(posix_getuid()); + $file->chgrp(0); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'r', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'r+', 0, $path)); + $this->assertTrue($wr->stream_open($fs->path('/file'), 'w', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'w+', 0, $path)); + $this->assertTrue($wr->stream_open($fs->path('/file'), 'a', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/file'), 'a+', 0, $path)); + + $file->chmod(0600); + $file->chown(posix_getuid()); + $file->chgrp(0); + $this->assertTrue($wr->stream_open($fs->path('/file'), 'r', 0, $path)); + $this->assertTrue($wr->stream_open($fs->path('/file'), 'r+', 0, $path)); + $this->assertTrue($wr->stream_open($fs->path('/file'), 'w', 0, $path)); + $this->assertTrue($wr->stream_open($fs->path('/file'), 'w+', 0, $path)); + $this->assertTrue($wr->stream_open($fs->path('/file'), 'a', 0, $path)); + $this->assertTrue($wr->stream_open($fs->path('/file'), 'a+', 0, $path)); + + } + + public function testTemporaryFileCreatedToReadDirectoriesWithStreamOpenInheritsPermissions() + { + $fs = new FileSystem(); + $file = $fs->container()->createDir('/dir'); + + $wr = new Wrapper(); + + $file->chmod(0000); + $file->chown(0); + $file->chgrp(0); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'r', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'r+', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'w', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'w+', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'a', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'a+', 0, $path)); + + $file->chmod(0400); + $file->chown(posix_getuid()); + $file->chgrp(0); + $this->assertTrue($wr->stream_open($fs->path('/dir'), 'r', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'r+', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'w', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'w+', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'a', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'a+', 0, $path)); + + $file->chmod(0200); + $file->chown(posix_getuid()); + $file->chgrp(0); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'r', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'r+', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'w', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'w+', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'a', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'a+', 0, $path)); + + $file->chmod(0600); + $file->chown(posix_getuid()); + $file->chgrp(0); + $this->assertTrue($wr->stream_open($fs->path('/dir'), 'r', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'r+', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'w', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'w+', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'a', 0, $path)); + $this->assertFalse($wr->stream_open($fs->path('/dir'), 'a+', 0, $path)); + } + + public function testPermissionsAreCheckedWhenOpeningDirectories() + { + $fs = new FileSystem(); + $file = $fs->container()->createDir('/dir'); + + $wr = new Wrapper(); + + $file->chmod(0000); + $file->chown(0); + $file->chgrp(0); + $this->assertFalse(@$wr->dir_opendir($fs->path('/dir'), 0)); + + $file->chmod(0200); + $file->chown(posix_getuid()); + $file->chgrp(0); + $this->assertFalse(@$wr->dir_opendir($fs->path('/dir'), 0)); + + $file->chmod(0400); + $file->chown(posix_getuid()); + $file->chgrp(0); + $this->assertTrue(@$wr->stream_open($fs->path('/dir'), 'r', 0, $path)); + + $file->chmod(0040); + $file->chown(0); + $file->chgrp(posix_getgid()); + $this->assertTrue(@$wr->stream_open($fs->path('/dir'), 'r', 0, $path)); + } + + public function testPermissionsAreCheckedWhenCreatingFilesWithinDirectories() + { + $fs = new FileSystem(); + $dir = $fs->createDirectory('/dir'); + + $dir->chmod(0000); + $this->assertFalse(@file_put_contents($fs->path('/dir/file'), 'data')); + + $dir->chmod(0400); + $this->assertFalse(@file_put_contents($fs->path('/dir/file'), 'data')); + + $dir->chmod(0200); + $this->assertGreaterThan(0, @file_put_contents($fs->path('/dir/file'), 'data')); + } + + public function testStreamOpenReportsErrorsOnPermissionDenied() + { + $fs = new FileSystem(); + $dir = $fs->createDirectory('/dir'); + $file = $fs->createFile('/file'); + $dir->chmod(0000); + + $wr = new Wrapper(); + + @$wr->stream_open($fs->path('/dir/file'), 'w', STREAM_REPORT_ERRORS, $path); + + $error = error_get_last(); + + $this->assertStringMatchesFormat( + '%s: failed to open stream: Permission denied', + $error['message'] + ); + + @$na['n/a']; //putting error in known state + $file->chmod(0000); + @$wr->stream_open($fs->path('/file'), 'r', STREAM_REPORT_ERRORS, $path); + + $error = error_get_last(); + + $this->assertStringMatchesFormat( + '%s: failed to open stream: Permission denied', + $error['message'] + ); + + @$na['n/a']; //putting error in known state + $file->chmod(0000); + @$wr->stream_open($fs->path('/file'), 'w', STREAM_REPORT_ERRORS, $path); + + $error = error_get_last(); + + $this->assertStringMatchesFormat( + '%s: failed to open stream: Permission denied', + $error['message'] + ); + + @$na['n/a']; //putting error in known state + $file->chmod(0000); + @$wr->stream_open($fs->path('/file'), 'a', STREAM_REPORT_ERRORS, $path); + + $error = error_get_last(); + + $this->assertStringMatchesFormat( + '%s: failed to open stream: Permission denied', + $error['message'] + ); + + @$na['n/a']; //putting error in known state + $file->chmod(0000); + @$wr->stream_open($fs->path('/file'), 'w+', STREAM_REPORT_ERRORS, $path); + + $error = error_get_last(); + + $this->assertStringMatchesFormat( + '%s: failed to open stream: Permission denied', + $error['message'] + ); + + } }