diff --git a/lib/Providers/SystemStore/Provider.php b/lib/Providers/SystemStore/Provider.php index 32f2d8e..ea3b547 100644 --- a/lib/Providers/SystemStore/Provider.php +++ b/lib/Providers/SystemStore/Provider.php @@ -71,6 +71,6 @@ final class Provider implements ProviderBaseInterface private function service(string $tenantId): Service { - return new Service($this->rootDir . '/storage/system-store/' . hash('sha256', $tenantId) . '/default'); + return new Service($this->rootDir . '/storage/' . $tenantId . '/system'); } } diff --git a/lib/Providers/SystemStore/Service.php b/lib/Providers/SystemStore/Service.php index cd1abe9..986d444 100644 --- a/lib/Providers/SystemStore/Service.php +++ b/lib/Providers/SystemStore/Service.php @@ -16,6 +16,10 @@ use KTXF\SystemStore\WriteConflictException; final class Service implements SystemStoreServiceInterface { + private const BLOB_EXTENSION = '.blob'; + private const META_EXTENSION = '.meta'; + private const LOCK_EXTENSION = '.lock'; + public function __construct(private readonly string $root) { } @@ -64,7 +68,7 @@ final class Service implements SystemStoreServiceInterface if ($info === null) { return null; } - $handle = fopen($this->dataPath($key), 'rb'); + $handle = fopen($this->blobPath($key), 'rb'); if ($handle === false) { throw new SystemStoreException("Unable to open local blob '{$key}' for reading"); } @@ -105,10 +109,9 @@ final class Service implements SystemStoreServiceInterface $metaTemp = null; try { $this->assertCondition($this->statUnlocked($key), $condition); - $dataPath = $this->dataPath($key); + $dataPath = $this->blobPath($key); $metaPath = $this->metaPath($key); - $this->ensureParent($dataPath, $this->dataRoot()); - $this->ensureParent($metaPath, $this->metaRoot()); + $this->ensureParent($dataPath); $dataTemp = tempnam(dirname($dataPath), '.write-'); if ($dataTemp === false || ($handle = fopen($dataTemp, 'wb')) === false) { @@ -187,7 +190,7 @@ final class Service implements SystemStoreServiceInterface if ($current === null) { return false; } - if (!unlink($this->dataPath($key))) { + if (!unlink($this->blobPath($key))) { throw new SystemStoreException("Unable to delete local blob '{$key}'"); } $metaPath = $this->metaPath($key); @@ -205,7 +208,7 @@ final class Service implements SystemStoreServiceInterface if ($prefix !== '') { $prefix = $this->validateKey($prefix, true); } - $root = $this->dataRoot(); + $root = $this->root; if (!is_dir($root)) { return; } @@ -214,7 +217,12 @@ final class Service implements SystemStoreServiceInterface if (!$file->isFile() || $file->isLink()) { continue; } - $key = str_replace(DIRECTORY_SEPARATOR, '/', substr($file->getPathname(), strlen($root) + 1)); + $pathname = $file->getPathname(); + if (!str_ends_with($pathname, self::BLOB_EXTENSION)) { + continue; + } + $relative = substr($pathname, strlen($root) + 1, -strlen(self::BLOB_EXTENSION)); + $key = str_replace(DIRECTORY_SEPARATOR, '/', $relative); if ($prefix !== '' && !str_starts_with($key, $prefix)) { continue; } @@ -227,11 +235,11 @@ final class Service implements SystemStoreServiceInterface private function statUnlocked(string $key): ?BlobInfo { - $path = $this->dataPath($key); + $path = $this->blobPath($key); if (!is_file($path) || is_link($path)) { return null; } - $this->assertExistingPathInside($path, $this->dataRoot()); + $this->assertExistingPathInside($path, $this->root); $size = filesize($path); $modified = filemtime($path); $hash = hash_file('sha256', $path); @@ -283,10 +291,10 @@ final class Service implements SystemStoreServiceInterface /** @return resource */ private function lock(string $key, int $operation) { - $root = $this->root . '/locks'; - $this->ensureDirectory($root); - $this->assertExistingPathInside($root, $this->root); - $handle = fopen($root . '/' . hash('sha256', $key) . '.lock', 'c'); + $path = $this->lockPath($key); + $this->ensureDirectory(dirname($path)); + $this->assertExistingPathInside(dirname($path), $this->root); + $handle = fopen($path, 'c'); if ($handle === false || !flock($handle, $operation)) { if (is_resource($handle)) { fclose($handle); } throw new SystemStoreException("Unable to lock local blob '{$key}'"); @@ -325,15 +333,14 @@ final class Service implements SystemStoreServiceInterface return $key; } - private function dataRoot(): string { return $this->root . '/data'; } - private function metaRoot(): string { return $this->root . '/meta'; } - private function dataPath(string $key): string { return $this->dataRoot() . '/' . str_replace('/', DIRECTORY_SEPARATOR, $key); } - private function metaPath(string $key): string { return $this->metaRoot() . '/' . str_replace('/', DIRECTORY_SEPARATOR, $key) . '.json'; } + private function blobPath(string $key): string { return $this->root . '/' . str_replace('/', DIRECTORY_SEPARATOR, $key) . self::BLOB_EXTENSION; } + private function metaPath(string $key): string { return $this->root . '/' . str_replace('/', DIRECTORY_SEPARATOR, $key) . self::META_EXTENSION; } + private function lockPath(string $key): string { return $this->root . '/' . str_replace('/', DIRECTORY_SEPARATOR, $key) . self::LOCK_EXTENSION; } - private function ensureParent(string $path, string $root): void + private function ensureParent(string $path): void { $this->ensureDirectory(dirname($path)); - $this->assertExistingPathInside(dirname($path), $root); + $this->assertExistingPathInside(dirname($path), $this->root); } private function ensureDirectory(string $path): void