2424use OCP \Files \InvalidCharacterInPathException ;
2525use OCP \Files \InvalidDirectoryException ;
2626use OCP \Files \InvalidPathException ;
27+ use OCP \Files \Mount \IMountManager ;
2728use OCP \Files \Mount \IMountPoint ;
2829use OCP \Files \NotFoundException ;
2930use OCP \Files \ReservedWordException ;
31+ use OCP \IL10N ;
3032use OCP \IUser ;
3133use OCP \IUserManager ;
34+ use OCP \L10N \IFactory ;
3235use OCP \Lock \ILockingProvider ;
3336use OCP \Lock \LockedException ;
3437use OCP \Server ;
@@ -59,6 +62,7 @@ class View {
5962 private bool $ updaterEnabled = true ;
6063 private UserManager $ userManager ;
6164 private LoggerInterface $ logger ;
65+ private IL10N $ l10n ;
6266
6367 /**
6468 * @throws \Exception If $root contains an invalid path
@@ -73,6 +77,7 @@ public function __construct(string $root = '') {
7377 $ this ->lockingEnabled = !($ this ->lockingProvider instanceof \OC \Lock \NoopLockingProvider);
7478 $ this ->userManager = \OC ::$ server ->getUserManager ();
7579 $ this ->logger = \OC ::$ server ->get (LoggerInterface::class);
80+ $ this ->l10n = \OC ::$ server ->get (IFactory::class)->get ('files ' );
7681 }
7782
7883 /**
@@ -695,18 +700,24 @@ public function deleteAll($directory) {
695700 *
696701 * @param string $source source path
697702 * @param string $target target path
703+ * @param array $options
698704 *
699705 * @return bool|mixed
700706 * @throws LockedException
701707 */
702- public function rename ($ source , $ target ) {
708+ public function rename ($ source , $ target , array $ options = []) {
709+ $ checkSubMounts = $ options ['checkSubMounts ' ] ?? true ;
710+
703711 $ absolutePath1 = Filesystem::normalizePath ($ this ->getAbsolutePath ($ source ));
704712 $ absolutePath2 = Filesystem::normalizePath ($ this ->getAbsolutePath ($ target ));
705713
706714 if (str_starts_with ($ absolutePath2 , $ absolutePath1 . '/ ' )) {
707715 throw new ForbiddenException ('Moving a folder into a child folder is forbidden ' , false );
708716 }
709717
718+ /** @var IMountManager $mountManager */
719+ $ mountManager = \OC ::$ server ->get (IMountManager::class);
720+
710721 $ targetParts = explode ('/ ' , $ absolutePath2 );
711722 $ targetUser = $ targetParts [1 ] ?? null ;
712723 $ result = false ;
@@ -764,31 +775,38 @@ public function rename($source, $target) {
764775 try {
765776 $ this ->changeLock ($ target , ILockingProvider::LOCK_EXCLUSIVE , true );
766777
778+ if ($ checkSubMounts ) {
779+ $ movedMounts = $ mountManager ->findIn ($ this ->getAbsolutePath ($ source ));
780+ } else {
781+ $ movedMounts = [];
782+ }
783+
767784 if ($ internalPath1 === '' ) {
768- if ($ mount1 instanceof MoveableMount) {
769- $ sourceParentMount = $ this ->getMount (dirname ($ source ));
770- if ($ sourceParentMount === $ mount2 && $ this ->targetIsNotShared ($ targetUser , $ absolutePath2 )) {
771- /**
772- * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
773- */
774- $ sourceMountPoint = $ mount1 ->getMountPoint ();
775- $ result = $ mount1 ->moveMount ($ absolutePath2 );
776- $ manager ->moveMount ($ sourceMountPoint , $ mount1 ->getMountPoint ());
777- } else {
778- $ result = false ;
779- }
780- } else {
781- $ result = false ;
782- }
785+ $ sourceParentMount = $ this ->getMount (dirname ($ source ));
786+ $ movedMounts [] = $ mount1 ;
787+ $ this ->validateMountMove ($ movedMounts , $ sourceParentMount , $ mount2 , !$ this ->targetIsNotShared ($ targetUser , $ absolutePath2 ));
788+ /**
789+ * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1
790+ */
791+ $ sourceMountPoint = $ mount1 ->getMountPoint ();
792+ $ result = $ mount1 ->moveMount ($ absolutePath2 );
793+ $ manager ->moveMount ($ sourceMountPoint , $ mount1 ->getMountPoint ());
794+
783795 // moving a file/folder within the same mount point
784796 } elseif ($ storage1 === $ storage2 ) {
797+ if (count ($ movedMounts ) > 0 ) {
798+ $ this ->validateMountMove ($ movedMounts , $ mount1 , $ mount2 , !$ this ->targetIsNotShared ($ targetUser , $ absolutePath2 ));
799+ }
785800 if ($ storage1 ) {
786801 $ result = $ storage1 ->rename ($ internalPath1 , $ internalPath2 );
787802 } else {
788803 $ result = false ;
789804 }
790805 // moving a file/folder between storages (from $storage1 to $storage2)
791806 } else {
807+ if (count ($ movedMounts ) > 0 ) {
808+ $ this ->validateMountMove ($ movedMounts , $ mount1 , $ mount2 , !$ this ->targetIsNotShared ($ targetUser , $ absolutePath2 ));
809+ }
792810 $ result = $ storage2 ->moveFromStorage ($ storage1 , $ internalPath1 , $ internalPath2 );
793811 }
794812
@@ -838,6 +856,55 @@ public function rename($source, $target) {
838856 return $ result ;
839857 }
840858
859+ /**
860+ * @throws ForbiddenException
861+ */
862+ private function validateMountMove (array $ mounts , IMountPoint $ sourceMount , IMountPoint $ targetMount , bool $ targetIsShared ): void {
863+ $ targetPath = $ this ->getRelativePath ($ targetMount ->getMountPoint ());
864+ if ($ targetPath ) {
865+ $ targetPath = trim ($ targetPath , '/ ' );
866+ } else {
867+ $ targetPath = $ targetMount ->getMountPoint ();
868+ }
869+
870+ foreach ($ mounts as $ mount ) {
871+ $ sourcePath = $ this ->getRelativePath ($ mount ->getMountPoint ());
872+ if ($ sourcePath ) {
873+ $ sourcePath = trim ($ sourcePath , '/ ' );
874+ } else {
875+ $ sourcePath = $ mount ->getMountPoint ();
876+ }
877+
878+ if (!$ mount instanceof MoveableMount) {
879+ throw new ForbiddenException ($ this ->l10n ->t ('Storage %s cannot be moved ' , [$ sourcePath ]), false );
880+ }
881+
882+ if ($ targetIsShared ) {
883+ if ($ sourceMount instanceof SharedMount) {
884+ throw new ForbiddenException ($ this ->l10n ->t ('Moving a share (%s) into a shared folder is not allowed ' , [$ sourcePath ]), false );
885+ } else {
886+ throw new ForbiddenException ($ this ->l10n ->t ('Moving a storage (%s) into a shared folder is not allowed ' , [$ sourcePath ]), false );
887+ }
888+ }
889+
890+ if ($ sourceMount !== $ targetMount ) {
891+ if ($ sourceMount instanceof SharedMount) {
892+ if ($ targetMount instanceof SharedMount) {
893+ throw new ForbiddenException ($ this ->l10n ->t ('Moving a share (%s) into another share (%s) is not allowed ' , [$ sourcePath , $ targetPath ]), false );
894+ } else {
895+ throw new ForbiddenException ($ this ->l10n ->t ('Moving a share (%s) into another storage (%s) is not allowed ' , [$ sourcePath , $ targetPath ]), false );
896+ }
897+ } else {
898+ if ($ targetMount instanceof SharedMount) {
899+ throw new ForbiddenException ($ this ->l10n ->t ('Moving a storage (%s) into a share (%s) is not allowed ' , [$ sourcePath , $ targetPath ]), false );
900+ } else {
901+ throw new ForbiddenException ($ this ->l10n ->t ('Moving a storage (%s) into another storage (%s) is not allowed ' , [$ sourcePath , $ targetPath ]), false );
902+ }
903+ }
904+ }
905+ }
906+ }
907+
841908 /**
842909 * Copy a file/folder from the source path to target path
843910 *
0 commit comments