Skip to content

Commit efa2cda

Browse files
authored
Merge pull request #51491 from nextcloud/fix/path-length
fix(dav): allow uploading of files with long filenames
2 parents 25bc18c + b4255a9 commit efa2cda

File tree

2 files changed

+63
-34
lines changed

2 files changed

+63
-34
lines changed

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

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,9 @@ public function put($data) {
129129
$view = Filesystem::getView();
130130

131131
if ($needsPartFile) {
132+
$transferId = \rand();
132133
// mark file as partial while uploading (ignored by the scanner)
133-
$partFilePath = $this->getPartFileBasePath($this->path) . '.ocTransferId' . rand() . '.part';
134+
$partFilePath = $this->getPartFileBasePath($this->path) . '.ocTransferId' . $transferId . '.part';
134135

135136
if (!$view->isCreatable($partFilePath) && $view->isUpdatable($this->path)) {
136137
$needsPartFile = false;
@@ -232,42 +233,36 @@ public function put($data) {
232233
fclose($target);
233234
}
234235

235-
if ($result === false) {
236-
$expected = -1;
237-
$lengthHeader = $this->request->getHeader('content-length');
238-
if ($lengthHeader) {
239-
$expected = (int)$lengthHeader;
240-
}
241-
if ($expected !== 0) {
242-
throw new Exception(
243-
$this->l10n->t(
244-
'Error while copying file to target location (copied: %1$s, expected filesize: %2$s)',
245-
[
246-
$this->l10n->n('%n byte', '%n bytes', $count),
247-
$this->l10n->n('%n byte', '%n bytes', $expected),
248-
],
249-
)
250-
);
251-
}
236+
$lengthHeader = $this->request->getHeader('content-length');
237+
$expected = $lengthHeader !== '' ? (int)$lengthHeader : -1;
238+
if ($result === false && $expected >= 0) {
239+
throw new Exception(
240+
$this->l10n->t(
241+
'Error while copying file to target location (copied: %1$s, expected filesize: %2$s)',
242+
[
243+
$this->l10n->n('%n byte', '%n bytes', $count),
244+
$this->l10n->n('%n byte', '%n bytes', $expected),
245+
],
246+
)
247+
);
252248
}
253249

254250
// if content length is sent by client:
255251
// double check if the file was fully received
256252
// compare expected and actual size
257-
$lengthHeader = $this->request->getHeader('content-length');
258-
if ($lengthHeader && $this->request->getMethod() === 'PUT') {
259-
$expected = (int)$lengthHeader;
260-
if ($count !== $expected) {
261-
throw new BadRequest(
262-
$this->l10n->t(
263-
'Expected filesize of %1$s but read (from Nextcloud client) and wrote (to Nextcloud storage) %2$s. Could either be a network problem on the sending side or a problem writing to the storage on the server side.',
264-
[
265-
$this->l10n->n('%n byte', '%n bytes', $expected),
266-
$this->l10n->n('%n byte', '%n bytes', $count),
267-
],
268-
)
269-
);
270-
}
253+
if ($expected >= 0
254+
&& $expected !== $count
255+
&& $this->request->getMethod() === 'PUT'
256+
) {
257+
throw new BadRequest(
258+
$this->l10n->t(
259+
'Expected filesize of %1$s but read (from Nextcloud client) and wrote (to Nextcloud storage) %2$s. Could either be a network problem on the sending side or a problem writing to the storage on the server side.',
260+
[
261+
$this->l10n->n('%n byte', '%n bytes', $expected),
262+
$this->l10n->n('%n byte', '%n bytes', $count),
263+
],
264+
)
265+
);
271266
}
272267
} catch (\Exception $e) {
273268
if ($e instanceof LockedException) {
@@ -383,9 +378,14 @@ public function put($data) {
383378
private function getPartFileBasePath($path) {
384379
$partFileInStorage = Server::get(IConfig::class)->getSystemValue('part_file_in_storage', true);
385380
if ($partFileInStorage) {
386-
return $path;
381+
$filename = basename($path);
382+
// hash does not need to be secure but fast and semi unique
383+
$hashedFilename = hash('xxh128', $filename);
384+
return substr($path, 0, strlen($path) - strlen($filename)) . $hashedFilename;
387385
} else {
388-
return md5($path); // will place it in the root of the view with a unique name
386+
// will place the .part file in the users root directory
387+
// therefor we need to make the name (semi) unique - hash does not need to be secure but fast.
388+
return hash('xxh128', $path);
389389
}
390390
}
391391

build/integration/dav_features/dav-v2.feature

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,24 @@ Feature: dav-v2
108108
When User "user0" uploads file "data/textfile.txt" to "/testquota/asdf.txt"
109109
Then the HTTP status code should be "201"
110110

111+
Scenario: Uploading a file with very long filename
112+
Given using new dav path
113+
And As an "admin"
114+
And user "user0" exists
115+
And user "user0" has a quota of "10 MB"
116+
And As an "user0"
117+
When User "user0" uploads file "data/textfile.txt" to "/long-filename-with-250-characters-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.txt"
118+
Then the HTTP status code should be "201"
119+
120+
Scenario: Uploading a file with a too long filename
121+
Given using new dav path
122+
And As an "admin"
123+
And user "user0" exists
124+
And user "user0" has a quota of "10 MB"
125+
And As an "user0"
126+
When User "user0" uploads file "data/textfile.txt" to "/long-filename-with-251-characters-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.txt"
127+
Then the HTTP status code should be "400"
128+
111129
Scenario: Create a search query on image
112130
Given using new dav path
113131
And As an "admin"
@@ -132,3 +150,14 @@ Feature: dav-v2
132150
Then Favorite search should work
133151
And the single response should contain a property "{http://owncloud.org/ns}favorite" with value "1"
134152

153+
Scenario: Create a search query on favorite
154+
Given using new dav path
155+
And As an "admin"
156+
And user "user0" exists
157+
And As an "user0"
158+
When User "user0" uploads file "data/green-square-256.png" to "/fav_image.png"
159+
Then Favorite search should work
160+
And the response should be empty
161+
When user "user0" favorites element "/fav_image.png"
162+
Then Favorite search should work
163+
And the single response should contain a property "{http://owncloud.org/ns}favorite" with value "1"

0 commit comments

Comments
 (0)