-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Preserve symbolic links from sync clients #41321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
taminob
wants to merge
51
commits into
nextcloud:master
Choose a base branch
from
taminob:feature/allow-symlink-synchronization
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+541
−3
Open
Changes from all commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
7b1ee27
feature(storage): Allow unlink operation on symlinks
taminob 1c75b70
feature(storage): Add symlink operation to local storage
taminob cf75521
feature(dav/connector): Add createSymlink to Directory node
taminob b9ab528
feature(dav/upload): Add symlink single-file upload to ChunkingV2Plugin
taminob af70873
feature(dav/bulk): Add symlink upload to BulkUploadPlugin
taminob 4ee5062
feature(files): Add file type TYPE_SYMLINK to FileInfo
taminob 97d3dc5
feature(storage): Add symlink, is_link and readlink to IStorage, Wrap…
taminob 5cc9108
feature(dav/file): Add readlink method
taminob 23ee19a
feature(storage): Allow deletion of symlinks with remove method in Co…
taminob da98a9f
feature(storage): Add default implementation for readlink and symlink…
taminob 1b31d59
feature(fileinfo): Add symlink mimetype
taminob 0ef014a
Add couple of TODOs, mostly related to symlink mimetype
taminob 3274c8e
feature(dav/connector): Draft for handling GET request for symlinks
taminob 3b02d61
Revert commits for storing actual symlinks on server
taminob 88f510e
First draft for metadata-only symlinks
taminob b537727
Initial draft for PROPFIND for symlinks
taminob 7f18076
Revert "Initial draft for PROPFIND for symlinks"
taminob 4f7ae29
Draft for symlink mimetype on regular file for symlinks
taminob 7348605
SymlinkManager: Initial implementation for symlink database management
taminob 15f6ac6
SymlinkManager: Introduce symlink table via database migration
taminob d182a3a
SymlinkManager: Accept FileInfo type and escape path for purgeSymlink
taminob 4e8748f
BulkUpload: Use SymlinkManager to create symlinks
taminob edf4f95
FilesPlugin: Use SymlinkManager to check for symlinks
taminob 8127b70
FileInfo: Remove unused TYPE_SYMLINK
taminob 8b21c07
SymlinkPlugin: Initialize SymlinkPlugin with basic implementation
taminob c6e2e8c
Server: Register SymlinkPlugin
taminob 47ee7ba
ChunkingV2Plugin: Remove symlink handling
taminob 641eac7
SymlinkPlugin: Pass correct object to symlinkManager for httpDelete
taminob 61faf44
migrations: Rename symlink migration to correct format
taminob 8d7dcf6
SymlinkManager: Various small but important fixes
taminob 6dc1aa6
Revert now unnecessary changes
taminob e0d5164
SymlinkPlugin/FilesPlugin: Fix httpGet and httpDelete for symlinks
taminob eac2b0a
migrations: Remove last_updated column from symlinks table
taminob 2eb1252
SymlinkManager: Remove last_updated from operations
taminob ed77937
FilesPlugin: Send Last-Modified and ETag header for GET response
taminob 7bf32f3
SymlinkPlugin: Handle move operation for symlinks
taminob b480d40
SymlinkPlugin: Fix PUT for regular file at previous symlink location
taminob a09a196
BulkUploadPlugin: Fix POST for regular file at previous symlink location
taminob 90a75b5
migrations: Remove default for storage column
taminob b8051ce
SymlinkManager: Use numeric_id for storage in database
taminob 24cea5d
SymlinkManager: Reduce code duplication for numeric_id and use intern…
taminob 8a9a48c
SymlinkPlugin: Fix update of symlinks for move operation
taminob c44b5af
BulkUploadPlugin: Cleanup for "feature(dav/bulk): Add symlink upload …
taminob 0e1fbed
SymlinkManager: Move to correct namespace OC\Files and update all Plu…
taminob 22324eb
composer: Update namespace of SymlinkPlugin in autoload files
taminob b5c1828
Connector\Sabre\File: Add string as possible parameter type to put
taminob 6f60e7e
FilesPlugin: Fix resourcetype of symlinks for PROPFIND
taminob 47d3eae
FilesPlugin/SymlinkPlugin/SymlinkManager: Fix some style guide violat…
taminob 65b5796
PreviewController: Indicate symlinks via preview icon in web ui
taminob 4fac7d8
SymlinkPlugin: Fix copying of symlinks
taminob 6666955
FilesPlugin: Fix linter issue
taminob File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
| /* | ||
| * @copyright Copyright (c) 2023 Tamino Bauknecht <[email protected]> | ||
| * | ||
| * @author Tamino Bauknecht <[email protected]> | ||
| * | ||
| * @license GNU AGPL version 3 or any later version | ||
| * | ||
| * This program is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU Affero General Public License as | ||
| * published by the Free Software Foundation, either version 3 of the | ||
| * License, or (at your option) any later version. | ||
| * | ||
| * This program is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU Affero General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU Affero General Public License | ||
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| */ | ||
|
|
||
| namespace OCA\DAV\Upload; | ||
|
|
||
| use OC\Files\SymlinkManager; | ||
| use Psr\Log\LoggerInterface; | ||
| use Sabre\DAV\Server; | ||
| use Sabre\DAV\ServerPlugin; | ||
| use Sabre\HTTP\RequestInterface; | ||
| use Sabre\HTTP\ResponseInterface; | ||
|
|
||
| class SymlinkPlugin extends ServerPlugin { | ||
| /** | ||
| * @var Server | ||
| * | ||
| * @psalm-suppress PropertyNotSetInConstructor | ||
| */ | ||
| private $server; | ||
| /** @var SymlinkManager */ | ||
| private $symlinkManager; | ||
| /** @var LoggerInterface */ | ||
| private $logger; | ||
|
|
||
| public function __construct(LoggerInterface $logger) { | ||
| $this->symlinkManager = new SymlinkManager(); | ||
| $this->logger = $logger; | ||
| } | ||
|
|
||
| /** | ||
| * @inheritdoc | ||
| */ | ||
| public function initialize(Server $server): void { | ||
| $server->on('method:PUT', [$this, 'httpPut']); | ||
| $server->on('method:DELETE', [$this, 'httpDelete']); | ||
| $server->on('afterMove', [$this, 'afterMove']); | ||
| $server->on('afterCopy', [$this, 'afterCopy']); | ||
|
|
||
| $this->server = $server; | ||
| } | ||
|
|
||
| public function httpPut(RequestInterface $request, ResponseInterface $response): bool { | ||
| if ($request->hasHeader('OC-File-Type') && $request->getHeader('OC-File-Type') == 1) { | ||
| $symlinkPath = $request->getPath(); | ||
| $symlinkName = basename($symlinkPath); | ||
| $symlinkTarget = $request->getBodyAsString(); | ||
| $parentPath = dirname($symlinkPath); | ||
| $parentNode = $this->server->tree->getNodeForPath($parentPath); | ||
| if (!$parentNode instanceof \Sabre\DAV\ICollection) { | ||
| throw new \Sabre\DAV\Exception\Forbidden("Directory does not allow creation of files - failed to upload '$symlinkName'"); | ||
| } | ||
| $etag = $parentNode->createFile($symlinkName); | ||
| $symlinkNode = $parentNode->getChild($symlinkName); | ||
| if (!$symlinkNode instanceof \OCA\DAV\Connector\Sabre\File) { | ||
| throw new \Sabre\DAV\Exception\NotFound("Failed to get newly created file '$symlinkName'"); | ||
| } | ||
| $symlinkNode->put($symlinkTarget); | ||
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Show fixed
Hide fixed
|
||
| $this->symlinkManager->storeSymlink($symlinkNode->getFileInfo()); | ||
|
|
||
| if ($etag) { | ||
| $response->setHeader('OC-ETag', $etag); | ||
| } | ||
| $response->setStatus(201); | ||
| return false; // this request was handled already | ||
| } elseif ($this->server->tree->nodeExists($request->getPath())) { | ||
| $node = $this->server->tree->getNodeForPath($request->getPath()); | ||
| if (!$node instanceof \OCA\DAV\Connector\Sabre\File) { | ||
| // cannot check if file was symlink before - let's hope it's not | ||
| $this->logger->warning('Unable to check if there was a symlink | ||
| before at the same location'); | ||
| return true; | ||
| } | ||
| // if the newly uploaded file is not a symlink, | ||
| // but there was a symlink at the same path before | ||
| if ($this->symlinkManager->isSymlink($node->getFileInfo())) { | ||
| $this->symlinkManager->deleteSymlink($node->getFileInfo()); | ||
| } | ||
| } | ||
| return true; // continue handling this request | ||
| } | ||
|
|
||
| public function httpDelete(RequestInterface $request, ResponseInterface $response): bool { | ||
| $path = $request->getPath(); | ||
| $node = $this->server->tree->getNodeForPath($path); | ||
| if (!$node instanceof \OCA\DAV\Connector\Sabre\Node) { | ||
| return true; | ||
| } | ||
| $info = $node->getFileInfo(); | ||
| if ($this->symlinkManager->isSymlink($info)) { | ||
| if (!$this->symlinkManager->deleteSymlink($info)) { | ||
| $symlinkName = $info->getName(); | ||
| throw new \Sabre\DAV\Exception\NotFound("Unable to delete symlink '$symlinkName'!"); | ||
| } | ||
| } | ||
| // always propagate to trigger deletion of regular file representing symlink in filesystem | ||
| return true; | ||
| } | ||
|
|
||
| public function afterMove(string $source, string $destination): void { | ||
| // source node does not exist anymore, thus use still existing parent | ||
| $sourceParentNode = dirname($source); | ||
| $sourceParentNode = $this->server->tree->getNodeForPath($sourceParentNode); | ||
| if (!$sourceParentNode instanceof \OCA\DAV\Connector\Sabre\Node) { | ||
| throw new \Sabre\DAV\Exception\NotImplemented('Unable to check if moved file is a symlink!'); | ||
| } | ||
| $destinationNode = $this->server->tree->getNodeForPath($destination); | ||
| if (!$destinationNode instanceof \OCA\DAV\Connector\Sabre\Node) { | ||
| throw new \Sabre\DAV\Exception\NotImplemented('Unable to set symlink information on move destination!'); | ||
| } | ||
|
|
||
| $sourceInfo = new \OC\Files\FileInfo( | ||
| $source, | ||
| $sourceParentNode->getFileInfo()->getStorage(), | ||
| $sourceParentNode->getInternalPath() . '/' . basename($source), | ||
| [], | ||
| $sourceParentNode->getFileInfo()->getMountPoint()); | ||
| $destinationInfo = $destinationNode->getFileInfo(); | ||
|
|
||
| if ($this->symlinkManager->isSymlink($sourceInfo)) { | ||
| $this->symlinkManager->deleteSymlink($sourceInfo); | ||
| $this->symlinkManager->storeSymlink($destinationInfo); | ||
| } elseif ($this->symlinkManager->isSymlink($destinationInfo)) { | ||
| // source was not a symlink, but destination was a symlink before | ||
| $this->symlinkManager->deleteSymlink($destinationInfo); | ||
| } | ||
| } | ||
|
|
||
| public function afterCopy(string $source, string $destination): void { | ||
| $sourceNode = $this->server->tree->getNodeForPath($source); | ||
| if (!$sourceNode instanceof \OCA\DAV\Connector\Sabre\Node) { | ||
| throw new \Sabre\DAV\Exception\NotImplemented( | ||
| 'Unable to check if copied file is a symlink!'); | ||
| } | ||
| $destinationNode = $this->server->tree->getNodeForPath($destination); | ||
| if (!$destinationNode instanceof \OCA\DAV\Connector\Sabre\Node) { | ||
| throw new \Sabre\DAV\Exception\NotImplemented( | ||
| 'Unable to set symlink information on copy destination!'); | ||
| } | ||
|
|
||
| $sourceInfo = $sourceNode->getFileInfo(); | ||
| $destinationInfo = $destinationNode->getFileInfo(); | ||
|
|
||
| if ($this->symlinkManager->isSymlink($sourceInfo)) { | ||
| $this->symlinkManager->storeSymlink($destinationInfo); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.