sj-i /
php-fuse
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * This file is part of the sj-i/php-fuse package. |
||
| 5 | * |
||
| 6 | * (c) sji <[email protected]> |
||
| 7 | * |
||
| 8 | * For the full copyright and license information, please view the LICENSE |
||
| 9 | * file that was distributed with this source code. |
||
| 10 | */ |
||
| 11 | |||
| 12 | declare(strict_types=1); |
||
| 13 | |||
| 14 | use Fuse\FilesystemDefaultImplementationTrait; |
||
| 15 | use Fuse\FilesystemInterface; |
||
| 16 | use Fuse\Libc\Errno\Errno; |
||
| 17 | use Fuse\Libc\Fuse\FuseFileInfo; |
||
| 18 | use Fuse\Libc\Fuse\FuseFillDir; |
||
| 19 | use Fuse\Libc\Fuse\FuseReadDirBuffer; |
||
| 20 | use Fuse\Libc\String\CBytesBuffer; |
||
| 21 | use Fuse\Libc\Sys\Stat\Stat; |
||
| 22 | use Fuse\Mounter; |
||
| 23 | |||
| 24 | require 'vendor/autoload.php'; |
||
| 25 | |||
| 26 | /** |
||
| 27 | * @psalm-type NodeType=scalar|array|null |
||
| 28 | */ |
||
| 29 | class ArrayFs implements FilesystemInterface |
||
| 30 | { |
||
| 31 | use FilesystemDefaultImplementationTrait; |
||
| 32 | |||
| 33 | /** @var NodeType[] */ |
||
| 34 | private array $array; |
||
| 35 | |||
| 36 | /** |
||
| 37 | * ArrayFs constructor. |
||
| 38 | * @param NodeType[] $array |
||
| 39 | */ |
||
| 40 | public function __construct(array $array) |
||
| 41 | { |
||
| 42 | $this->array = $array; |
||
| 43 | } |
||
| 44 | |||
| 45 | public function getArray(): array |
||
| 46 | { |
||
| 47 | return $this->array; |
||
| 48 | } |
||
| 49 | |||
| 50 | public function getattr(string $path, Stat $stat): int |
||
| 51 | { |
||
| 52 | echo "attr read {$path}" . PHP_EOL; |
||
| 53 | |||
| 54 | if ($path === '/') { |
||
| 55 | $stat->st_mode = Stat::S_IFDIR | 0777; |
||
| 56 | $stat->st_nlink = 2; |
||
| 57 | $stat->st_uid = getmyuid(); |
||
| 58 | $stat->st_gid = getmygid(); |
||
| 59 | return 0; |
||
| 60 | } |
||
| 61 | |||
| 62 | $element = $this->getEntry($path); |
||
| 63 | if (is_null($element)) { |
||
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
| 64 | return -Errno::ENOENT; |
||
| 65 | } |
||
| 66 | if (is_array($element)) { |
||
|
0 ignored issues
–
show
|
|||
| 67 | $stat->st_mode = Stat::S_IFDIR | 0777; |
||
| 68 | $stat->st_nlink = 2; |
||
| 69 | $stat->st_uid = getmyuid(); |
||
| 70 | $stat->st_gid = getmygid(); |
||
| 71 | return 0; |
||
| 72 | } |
||
| 73 | $stat->st_mode = Stat::S_IFREG | 0777; |
||
| 74 | $stat->st_nlink = 1; |
||
| 75 | $stat->st_size = strlen((string)$element); |
||
| 76 | $stat->st_uid = getmyuid(); |
||
| 77 | $stat->st_gid = getmygid(); |
||
| 78 | return 0; |
||
| 79 | } |
||
| 80 | |||
| 81 | /** |
||
| 82 | * @param NodeType[] $array |
||
| 83 | * @param list<string> $offsets |
||
|
0 ignored issues
–
show
The type
list was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths Loading history...
|
|||
| 84 | * @param callable(array, string):NodeType $operation |
||
| 85 | * @return NodeType |
||
|
0 ignored issues
–
show
The type
NodeType was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths Loading history...
|
|||
| 86 | */ |
||
| 87 | private function &getRecursive(&$array, array $offsets, ?callable $operation = null) |
||
| 88 | { |
||
| 89 | $null = null; |
||
| 90 | |||
| 91 | $count = count($offsets); |
||
| 92 | |||
| 93 | if ($count === 0) { |
||
| 94 | return $null; |
||
| 95 | } |
||
| 96 | if ($count === 1) { |
||
| 97 | if (isset($array[$offsets[0]])) { |
||
| 98 | if (!is_null($operation)) { |
||
| 99 | return $operation($array, $offsets[0]); |
||
| 100 | } else { |
||
| 101 | /** @var NodeType */ |
||
| 102 | return $array[$offsets[0]]; |
||
| 103 | } |
||
| 104 | } else { |
||
| 105 | return $null; |
||
| 106 | } |
||
| 107 | } |
||
| 108 | |||
| 109 | $offset = array_shift($offsets); |
||
| 110 | if (is_array($array[$offset])) { |
||
|
0 ignored issues
–
show
|
|||
| 111 | /** @var NodeType[] $next_array */ |
||
| 112 | $next_array =& $array[$offset]; |
||
| 113 | return $this->getRecursive($next_array, $offsets); |
||
| 114 | } else { |
||
| 115 | return $null; |
||
| 116 | } |
||
| 117 | } |
||
| 118 | |||
| 119 | /** |
||
| 120 | * @param string $path |
||
| 121 | * @return scalar|array|null |
||
| 122 | */ |
||
| 123 | private function &getEntry(string $path) |
||
| 124 | { |
||
| 125 | if ($path === '/') { |
||
| 126 | return $this->array; |
||
| 127 | } |
||
| 128 | $splitted = explode('/', $path); |
||
| 129 | array_shift($splitted); |
||
| 130 | return $this->getRecursive($this->array, $splitted); |
||
| 131 | } |
||
| 132 | |||
| 133 | /** |
||
| 134 | * @param string $path |
||
| 135 | * @return NodeType |
||
| 136 | */ |
||
| 137 | private function &getParentEntry(string $path) |
||
| 138 | { |
||
| 139 | $splitted = explode('/', $path); |
||
| 140 | array_shift($splitted); |
||
| 141 | array_pop($splitted); |
||
| 142 | if (count($splitted) === 0) { |
||
| 143 | return $this->array; |
||
|
0 ignored issues
–
show
|
|||
| 144 | } |
||
| 145 | return $this->getRecursive($this->array, $splitted); |
||
| 146 | } |
||
| 147 | |||
| 148 | /** |
||
| 149 | * @param string $path |
||
| 150 | */ |
||
| 151 | private function unsetEntry(string $path): void |
||
| 152 | { |
||
| 153 | $splitted = explode('/', $path); |
||
| 154 | array_shift($splitted); |
||
| 155 | $this->getRecursive( |
||
| 156 | $this->array, |
||
| 157 | $splitted, |
||
| 158 | function &(array &$array, string $index) { |
||
| 159 | $null = null; |
||
| 160 | unset($array[$index]); |
||
| 161 | return $null; |
||
| 162 | } |
||
| 163 | ); |
||
| 164 | } |
||
| 165 | |||
| 166 | public function readdir( |
||
| 167 | string $path, |
||
| 168 | FuseReadDirBuffer $buf, |
||
| 169 | FuseFillDir $filler, |
||
| 170 | int $offset, |
||
| 171 | FuseFileInfo $fuse_file_info |
||
| 172 | ): int { |
||
| 173 | $filler($buf, '.', null, 0); |
||
| 174 | $filler($buf, '..', null, 0); |
||
| 175 | $entry = $this->getEntry($path); |
||
| 176 | if (!is_array($entry)) { |
||
|
0 ignored issues
–
show
|
|||
| 177 | return -Errno::ENOTDIR; |
||
| 178 | } |
||
| 179 | foreach ($entry as $key => $_) { |
||
| 180 | $filler($buf, (string)$key, null, 0); |
||
| 181 | } |
||
| 182 | |||
| 183 | return 0; |
||
| 184 | } |
||
| 185 | |||
| 186 | public function open(string $path, FuseFileInfo $fuse_file_info): int |
||
| 187 | { |
||
| 188 | $entry = $this->getEntry($path); |
||
| 189 | if (!is_scalar($entry)) { |
||
|
0 ignored issues
–
show
|
|||
| 190 | return Errno::ENOENT; |
||
| 191 | } |
||
| 192 | |||
| 193 | echo "open {$path}" . PHP_EOL; |
||
| 194 | return 0; |
||
| 195 | } |
||
| 196 | |||
| 197 | public function read(string $path, CBytesBuffer $buffer, int $size, int $offset, FuseFileInfo $fuse_file_info): int |
||
| 198 | { |
||
| 199 | $entry = $this->getEntry($path); |
||
| 200 | |||
| 201 | echo "read {$path}" . PHP_EOL; |
||
| 202 | |||
| 203 | assert(!is_array($entry)); |
||
| 204 | $len = strlen((string)$entry); |
||
| 205 | |||
| 206 | if ($offset + $size > $len) { |
||
| 207 | $size = ($len - $offset); |
||
| 208 | } |
||
| 209 | |||
| 210 | $content = substr((string)$entry, $offset, $size); |
||
| 211 | $buffer->write($content, $size); |
||
| 212 | |||
| 213 | return $size; |
||
| 214 | } |
||
| 215 | |||
| 216 | public function write(string $path, string $buffer, int $size, int $offset, FuseFileInfo $fuse_file_info): int |
||
| 217 | { |
||
| 218 | $entry = &$this->getEntry($path); |
||
| 219 | assert(!is_array($entry)); |
||
| 220 | $entry = substr_replace((string)$entry, $buffer, $offset, $size); |
||
| 221 | |||
| 222 | return $size; |
||
| 223 | } |
||
| 224 | |||
| 225 | public function create(string $path, int $mode, FuseFileInfo $fuse_file_info): int |
||
| 226 | { |
||
| 227 | $entry = &$this->getParentEntry($path); |
||
| 228 | if (is_array($entry)) { |
||
|
0 ignored issues
–
show
|
|||
| 229 | $segments = explode('/', $path); |
||
| 230 | $filename = array_pop($segments); |
||
| 231 | $entry[$filename] = ''; |
||
| 232 | return 0; |
||
| 233 | } else { |
||
| 234 | return Errno::ENOENT; |
||
| 235 | } |
||
| 236 | } |
||
| 237 | |||
| 238 | public function unlink(string $path): int |
||
| 239 | { |
||
| 240 | $this->unsetEntry($path); |
||
| 241 | return 0; |
||
| 242 | } |
||
| 243 | |||
| 244 | public function rename(string $from, string $to): int |
||
| 245 | { |
||
| 246 | $fromValue = $this->getEntry($from); |
||
| 247 | $parent_entry = &$this->getParentEntry($to); |
||
| 248 | if (is_array($parent_entry)) { |
||
|
0 ignored issues
–
show
|
|||
| 249 | $segments = explode('/', $to); |
||
| 250 | $filename = array_pop($segments); |
||
| 251 | $parent_entry[$filename] = $fromValue; |
||
| 252 | $this->unsetEntry($from); |
||
| 253 | return 0; |
||
| 254 | } else { |
||
| 255 | return -Errno::ENOENT; |
||
| 256 | } |
||
| 257 | } |
||
| 258 | } |
||
| 259 | |||
| 260 | $e = new \DateTimeImmutable(); |
||
| 261 | |||
| 262 | $mounter = new Mounter(); |
||
| 263 | /** @psalm-suppress MixedArgumentTypeCoercion */ |
||
| 264 | $array_fs = new ArrayFs([ |
||
| 265 | 1, |
||
| 266 | 2, |
||
| 267 | 'foo' => 'bar', |
||
| 268 | 'e' => json_decode(json_encode($e), true) |
||
| 269 | ]); |
||
| 270 | $result = $mounter->mount('/tmp/example/', $array_fs); |
||
| 271 | /** @psalm-suppress ForbiddenCode */ |
||
| 272 | var_dump($array_fs->getArray()); |
||
| 273 | return $result; |