Skip to content

Commit

Permalink
archive: add lock to folder creation
Browse files Browse the repository at this point in the history
Close #122
Close #681

Signed-off-by: Varun Patil <radialapps@gmail.com>
  • Loading branch information
pulsejet committed Oct 30, 2023
1 parent 782b2c9 commit 5e4b963
Showing 1 changed file with 63 additions and 13 deletions.
76 changes: 63 additions & 13 deletions lib/Controller/ArchiveController.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\Files\Folder;
use OCP\Lock\ILockingProvider;

class ArchiveController extends GenericApiController
{
Expand Down Expand Up @@ -152,19 +153,7 @@ public function archive(string $id): Http\Response
// Create folder tree
$folder = $parent;
foreach ($destinationFolders as $folderName) {
try {
$existingFolder = $folder->get($folderName.'/');
if (!$existingFolder instanceof Folder) {
throw Exceptions::NotFound('Not a folder: '.$existingFolder->getPath());
}
$folder = $existingFolder;
} catch (\OCP\Files\NotFoundException $e) {
try {
$folder = $folder->newFolder($folderName);
} catch (\OCP\Files\NotPermittedException $e) {
throw Exceptions::ForbiddenFileUpdate($folder->getPath().' [create]');
}
}
$folder = $this->getOrCreateFolder($folder, $folderName);
}

// Move file to archive folder
Expand All @@ -173,4 +162,65 @@ public function archive(string $id): Http\Response
return new JSONResponse([], Http::STATUS_OK);
});
}

/**
* Get or create a folder in the given parent folder.
*
* @param Folder $parent Parent folder
* @param string $name Folder name
* @param int $tries Number of tries to create the folder
*
* @throws \OCA\Memories\HttpResponseException
*/
private function getOrCreateFolder(Folder $parent, string $name, int $tries = 3): Folder
{
// Path of the folder we want to create (for error messages)
$finalPath = $parent->getPath().'/'.$name;

// Attempt to create the folder
if (!$parent->nodeExists($name)) {
$lockingProvider = \OC::$server->get(ILockingProvider::class);
$lockKey = "memories/create/{$finalPath}";
$lockType = ILockingProvider::LOCK_EXCLUSIVE;
$locked = false;

try {
// Attempt to acquire exclusive lock
$lockingProvider->acquireLock($lockKey, $lockType);
$locked = true;
} catch (\OCP\Lock\LockedException) {
// Someone else is creating, wait and try to get the folder
usleep(1000000);
}

if ($locked) {
// Green light to create the folder
try {
return $parent->newFolder($name);
} catch (\OCP\Files\NotPermittedException $e) {
// Cannot create folder, throw error
throw Exceptions::ForbiddenFileUpdate("{$finalPath} [create]");
} catch (\OCP\Lock\LockedException $e) {
// This is the Files lock ... well
throw Exceptions::ForbiddenFileUpdate("{$finalPath} [locked]");
} finally {
// Release our lock
$lockingProvider->releaseLock($lockKey, $lockType);
}
}
}

// Second check if the folder exists
if (!$parent->nodeExists($name)) {
throw Exceptions::NotFound("Folder not found: {$finalPath}");
}

// Attempt to get the folder that should already exist
$existing = $parent->get($name);
if (!$existing instanceof Folder) {
throw Exceptions::NotFound("Not a folder: {$existing->getPath()}");
}

return $existing;
}
}

0 comments on commit 5e4b963

Please sign in to comment.