Skip to content

Commit 224073e

Browse files
authored
Merge pull request #17926 from nextcloud/dav-upload-shared-lock-expired
re-acquired expired shared locks on large file uploads
2 parents a3036aa + 2e97e8b commit 224073e

File tree

2 files changed

+36
-3
lines changed

2 files changed

+36
-3
lines changed

apps/dav/lib/Connector/Sabre/File.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,22 @@ public function put($data) {
252252
try {
253253
$this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
254254
} catch (LockedException $e) {
255-
if ($needsPartFile) {
256-
$partStorage->unlink($internalPartPath);
255+
// during very large uploads, the shared lock we got at the start might have been expired
256+
// meaning that the above lock can fail not just only because somebody else got a shared lock
257+
// or because there is no existing shared lock to make exclusive
258+
//
259+
// Thus we try to get a new exclusive lock, if the original lock failed because of a different shared
260+
// lock this will still fail, if our original shared lock expired the new lock will be successful and
261+
// the entire operation will be safe
262+
263+
try {
264+
$this->acquireLock(ILockingProvider::LOCK_EXCLUSIVE);
265+
} catch (LockedException $e) {
266+
if ($needsPartFile) {
267+
$partStorage->unlink($internalPartPath);
268+
}
269+
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
257270
}
258-
throw new FileLocked($e->getMessage(), $e->getCode(), $e);
259271
}
260272

261273
// rename to correct path

apps/dav/tests/unit/Connector/Sabre/FileTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,4 +1201,25 @@ public function testSimplePutNoCreatePermissions() {
12011201

12021202
$this->assertEquals('new content', $view->file_get_contents('root/file.txt'));
12031203
}
1204+
1205+
public function testPutLockExpired() {
1206+
$view = new \OC\Files\View('/' . $this->user . '/files/');
1207+
1208+
$path = 'test-locking.txt';
1209+
$info = new \OC\Files\FileInfo(
1210+
'/' . $this->user . '/files/' . $path,
1211+
$this->getMockStorage(),
1212+
null,
1213+
['permissions' => \OCP\Constants::PERMISSION_ALL],
1214+
null
1215+
);
1216+
1217+
$file = new \OCA\DAV\Connector\Sabre\File($view, $info);
1218+
1219+
// don't lock before the PUT to simulate an expired shared lock
1220+
$this->assertNotEmpty($file->put($this->getStream('test data')));
1221+
1222+
// afterMethod unlocks
1223+
$view->unlockFile($path, ILockingProvider::LOCK_SHARED);
1224+
}
12041225
}

0 commit comments

Comments
 (0)