]> source.dussan.org Git - nextcloud-server.git/commitdiff
re-acquired expired shared locks on large file uploads 17944/head
authorRobin Appelman <robin@icewind.nl>
Wed, 13 Nov 2019 13:54:22 +0000 (14:54 +0100)
committerBackportbot <backportbot-noreply@rullzer.com>
Thu, 14 Nov 2019 18:35:08 +0000 (18:35 +0000)
during large file uploads, the shared lock that we get at the begining can expire
leading to locked errors later on, instead of erroring, try to re-get the lock

Signed-off-by: Robin Appelman <robin@icewind.nl>
apps/dav/lib/Connector/Sabre/File.php
apps/dav/tests/unit/Connector/Sabre/FileTest.php

index 0767fc9550e84e055b2a3816aad028595b06a8b0..e98ecf5b0a4ea1194589b22dfedfbf4fb28bf5b9 100644 (file)
@@ -252,10 +252,22 @@ class File extends Node implements IFile {
                                try {
                                        $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
                                } catch (LockedException $e) {
-                                       if ($needsPartFile) {
-                                               $partStorage->unlink($internalPartPath);
+                                       // during very large uploads, the shared lock we got at the start might have been expired
+                                       // meaning that the above lock can fail not just only because somebody else got a shared lock
+                                       // or because there is no existing shared lock to make exclusive
+                                       //
+                                       // Thus we try to get a new exclusive lock, if the original lock failed because of a different shared
+                                       // lock this will still fail, if our original shared lock expired the new lock will be successful and
+                                       // the entire operation will be safe
+
+                                       try {
+                                               $this->acquireLock(ILockingProvider::LOCK_EXCLUSIVE);
+                                       } catch (LockedException $e) {
+                                               if ($needsPartFile) {
+                                                       $partStorage->unlink($internalPartPath);
+                                               }
+                                               throw new FileLocked($e->getMessage(), $e->getCode(), $e);
                                        }
-                                       throw new FileLocked($e->getMessage(), $e->getCode(), $e);
                                }
 
                                // rename to correct path
index 3e3e80f752510bcaecb397142237e1c0824cdd6a..c579a904260bed711d55b99b3dc87a9b809aa189 100644 (file)
@@ -1201,4 +1201,25 @@ class FileTest extends TestCase {
 
                $this->assertEquals('new content', $view->file_get_contents('root/file.txt'));
        }
+
+       public function testPutLockExpired() {
+               $view = new \OC\Files\View('/' . $this->user . '/files/');
+
+               $path = 'test-locking.txt';
+               $info = new \OC\Files\FileInfo(
+                       '/' . $this->user . '/files/' . $path,
+                       $this->getMockStorage(),
+                       null,
+                       ['permissions' => \OCP\Constants::PERMISSION_ALL],
+                       null
+               );
+
+               $file = new \OCA\DAV\Connector\Sabre\File($view, $info);
+
+               // don't lock before the PUT to simulate an expired shared lock
+               $this->assertNotEmpty($file->put($this->getStream('test data')));
+
+               // afterMethod unlocks
+               $view->unlockFile($path, ILockingProvider::LOCK_SHARED);
+       }
 }