@@ -523,19 +523,12 @@ private boolean isRecursiveLink(IFileStore parentStore, IFileInfo localInfo) {
523523 if (disable_advanced_recursive_link_checks ) {
524524 // Multiple ../ backwards links can go outside the project tree
525525 if (linkTarget != null && PatternHolder .REPEATING_BACKWARDS_PATTERN .matcher (linkTarget ).matches ()) {
526- Path targetPath = parent .resolve (linkTarget ).normalize ();
527-
528- // Recursive if literal target points to the literal parent of this tree
529- if (parent .normalize ().startsWith (targetPath )) {
526+ if (isRecursiveBackwardsLink (parent , linkTarget , false )) {
530527 return true ;
531528 }
532-
533- // Recursive if resolved target points to the resolved parent of this tree
534- Path realTargetPath = targetPath .toRealPath ();
535- if (realParentPath .startsWith (realTargetPath )) {
529+ if (isRecursiveBackwardsLink (realParentPath , linkTarget , true )) {
536530 return true ;
537531 }
538-
539532 // If link is outside the project tree, consider as non recursive
540533 // The link still can create recursion in the tree, but we can't detect it here.
541534 }
@@ -573,6 +566,29 @@ private boolean isRecursiveLink(IFileStore parentStore, IFileInfo localInfo) {
573566 return false ;
574567 }
575568
569+ private static boolean isRecursiveBackwardsLink (Path parent , String linkTarget , boolean resolveRealTarget )
570+ throws IOException {
571+ // Cheap tests first
572+ // Recursive if literal target points to the literal parent of this tree
573+ Path notNormalizedLink = parent .resolve (linkTarget );
574+ if (parent .startsWith (notNormalizedLink )) {
575+ return true ;
576+ }
577+ // Same as above but using normalized variants
578+ Path normalizedLink = notNormalizedLink .normalize ();
579+ if (parent .normalize ().startsWith (normalizedLink )) {
580+ return true ;
581+ }
582+ // Next check costs more time because it does real IO when resolving paths
583+ if (resolveRealTarget ) {
584+ Path realTarget = normalizedLink .toRealPath ();
585+ if (parent .startsWith (realTarget )) {
586+ return true ;
587+ }
588+ }
589+ return false ;
590+ }
591+
576592 protected boolean isValidLevel (int currentLevel , int depth ) {
577593 return switch (depth ) {
578594 case IResource .DEPTH_INFINITE -> true ;
0 commit comments