94
94
import org .opengrok .indexer .configuration .RuntimeEnvironment ;
95
95
import org .opengrok .indexer .history .HistoryException ;
96
96
import org .opengrok .indexer .history .HistoryGuru ;
97
+ import org .opengrok .indexer .history .Repository ;
97
98
import org .opengrok .indexer .logger .LoggerFactory ;
98
99
import org .opengrok .indexer .search .QueryBuilder ;
99
100
import org .opengrok .indexer .util .ForbiddenSymlinkException ;
@@ -816,16 +817,15 @@ private static void cleanupResources(Document doc) {
816
817
* Check if I should accept this file into the index database.
817
818
*
818
819
* @param file the file to check
819
- * @param outLocalRelPath optional output array whose 0-th element is set
820
- * to a relative path if and only if the {@code file} is a symlink targeting
821
- * a local directory (N.b. method will return {@code false})
822
- * @return true if the file should be included, false otherwise
820
+ * @param ret defined instance whose {@code localRelPath} property will be
821
+ * non-null afterward if and only if {@code file} is a symlink that targets
822
+ * either a {@link Repository}-local filesystem object or the same object
823
+ * as a previously-detected and allowed symlink. N.b. method will return
824
+ * {@code false} if {@code ret.localRelPath} is set non-null.
825
+ * @return a value indicating if {@code file} should be included in index
823
826
*/
824
- private boolean accept (File file , String [] outLocalRelPath ) {
825
- if (outLocalRelPath != null ) {
826
- outLocalRelPath [0 ] = null ;
827
- }
828
-
827
+ private boolean accept (File file , AcceptSymlinkRet ret ) {
828
+ ret .localRelPath = null ;
829
829
String absolutePath = file .getAbsolutePath ();
830
830
831
831
if (!includedNames .isEmpty ()
@@ -849,9 +849,8 @@ private boolean accept(File file, String[] outLocalRelPath) {
849
849
Path absolute = Paths .get (absolutePath );
850
850
if (Files .isSymbolicLink (absolute )) {
851
851
File canonical = file .getCanonicalFile ();
852
- if (!absolutePath .equals (canonical .getPath ())
853
- && !acceptSymlink (absolute , canonical ,
854
- outLocalRelPath )) {
852
+ if (!absolutePath .equals (canonical .getPath ()) &&
853
+ !acceptSymlink (absolute , canonical , ret )) {
855
854
LOGGER .log (Level .FINE , "Skipped symlink ''{0}'' -> ''{1}''" ,
856
855
new Object []{absolutePath , canonical });
857
856
return false ;
@@ -893,15 +892,15 @@ private boolean accept(File file, String[] outLocalRelPath) {
893
892
* Determines if {@code file} should be accepted into the index database.
894
893
* @param parent parent of {@code file}
895
894
* @param file directory object under consideration
896
- * @param outLocalRelPath optional output array whose 0-th element is set
897
- * to a relative path if and only if the {@code file} is a symlink targeting
898
- * a local directory (N.b. method will return {@code false})
899
- * @return {@code true} if the file should be included; else {@code false}
895
+ * @param ret defined instance whose {@code localRelPath} property will be
896
+ * non-null afterward if and only if {@code file} is a symlink that targets
897
+ * either a {@link Repository}-local filesystem object or the same object
898
+ * as a previously-detected and allowed symlink. N.b. method will return
899
+ * {@code false} if {@code ret.localRelPath} is set non-null.
900
+ * @return a value indicating if {@code file} should be included in index
900
901
*/
901
- private boolean accept (File parent , File file , String [] outLocalRelPath ) {
902
- if (outLocalRelPath != null ) {
903
- outLocalRelPath [0 ] = null ;
904
- }
902
+ private boolean accept (File parent , File file , AcceptSymlinkRet ret ) {
903
+ ret .localRelPath = null ;
905
904
906
905
try {
907
906
File f1 = parent .getCanonicalFile ();
@@ -922,7 +921,7 @@ private boolean accept(File parent, File file, String[] outLocalRelPath) {
922
921
}
923
922
}
924
923
925
- return accept (file , outLocalRelPath );
924
+ return accept (file , ret );
926
925
} catch (IOException ex ) {
927
926
LOGGER .log (Level .WARNING , "Failed to resolve name: {0} {1}" ,
928
927
new Object []{parent .getAbsolutePath (), file .getAbsolutePath ()});
@@ -935,53 +934,91 @@ private boolean accept(File parent, File file, String[] outLocalRelPath) {
935
934
*
936
935
* @param absolute the path with a symlink to check
937
936
* @param canonical the canonical file object
938
- * @param outLocalRelPath optional output array whose 0-th element is set
939
- * to a relative path if and only if {@code absolute} is a symlink targeting
940
- * a local directory, {@code canonical} (N.b. method will return
941
- * {@code false})
942
- * @return {@code true} if the file should be accepted; else {@code false}
937
+ * @param ret defined instance whose {@code localRelPath} property will be
938
+ * non-null afterward if and only if {@code absolute} is a symlink that
939
+ * targets either a {@link Repository}-local filesystem object or the same
940
+ * object ({@code canonical}) as a previously-detected and allowed symlink.
941
+ * N.b. method will return {@code false} if {@code ret.localRelPath} is set
942
+ * non-null.
943
+ * @return a value indicating if {@code file} should be included in index
943
944
*/
944
- private boolean acceptSymlink (Path absolute , File canonical ,
945
- String [] outLocalRelPath ) throws IOException {
946
- if (outLocalRelPath != null ) {
947
- outLocalRelPath [0 ] = null ;
948
- }
949
-
950
- if (isLocal (canonical .getPath ())) {
951
- if (!canonical .isDirectory ()) {
952
- // Always accept symlinks to local non-directories.
945
+ private boolean acceptSymlink (Path absolute , File canonical , AcceptSymlinkRet ret ) {
946
+ ret .localRelPath = null ;
947
+
948
+ String absolute1 = absolute .toString ();
949
+ String canonical1 = canonical .getPath ();
950
+ boolean isCanonicalDir = canonical .isDirectory ();
951
+
952
+ if (isLocal (canonical1 )) {
953
+ if (!isCanonicalDir ) {
954
+ // Always accept symlinks to local files.
955
+ if (LOGGER .isLoggable (Level .FINEST )) {
956
+ LOGGER .log (Level .FINEST , "Local {0} has symlink from {1}" ,
957
+ new Object [] {canonical1 , absolute1 });
958
+ }
953
959
return true ;
954
960
} else {
955
- /**
961
+ /*
956
962
* Do not accept symlinks to local directories, because the
957
963
* canonical target will be indexed on its own -- but
958
- * relativize() a path to be returned in outLocalRelPath so that
964
+ * relativize() a path to be returned in ret so that
959
965
* a symlink can be replicated in xref/.
960
- **/
961
- if (outLocalRelPath != null ) {
962
- outLocalRelPath [0 ] = absolute .getParent ().relativize (
963
- canonical .toPath ()).toString ();
964
- }
966
+ */
967
+ ret .localRelPath = absolute .getParent ().relativize (
968
+ canonical .toPath ()).toString ();
965
969
return false ;
966
970
}
967
971
}
968
972
973
+ String absolute0 ;
969
974
// No need to synchronize, as indexDown() runs on one thread.
970
- if (acceptedNonlocalSymlinks .containsKey (canonical .getPath ())) {
971
- return false ;
972
- }
975
+ if ((absolute0 = acceptedNonlocalSymlinks .get (canonical1 )) != null ) {
976
+ if (absolute1 .equals (absolute0 )) {
977
+ return true ;
978
+ } else if (!isCanonicalDir ) {
979
+ if (LOGGER .isLoggable (Level .FINE )) {
980
+ LOGGER .log (Level .FINE , "External file {0} has symlink from {1} after first {2}" ,
981
+ new Object [] {canonical1 , absolute1 , absolute0 });
982
+ }
983
+ return true ;
984
+ } else {
985
+ /*
986
+ * Do not accept symlinks to external directories already
987
+ * accepted as linked elsewhere, because the canonical target
988
+ * will be indexed already -- but relativize() a path to be
989
+ * returned in ret so that this second symlink can be redone as
990
+ * a local (non-external) symlink in xref/.
991
+ */
992
+ ret .localRelPath = absolute .getParent ().relativize (
993
+ Paths .get (absolute0 )).toString ();
973
994
974
- String absolstr = absolute .toString ();
975
- for (String allowedSymlink : RuntimeEnvironment .getInstance ().getAllowedSymlinks ()) {
976
- if (absolstr .startsWith (allowedSymlink )) {
977
- String allowedTarget = new File (allowedSymlink ).getCanonicalPath ();
978
- String canonstr = canonical .getPath ();
979
- if (canonstr .startsWith (allowedTarget )
980
- && absolstr .substring (allowedSymlink .length ()).equals (
981
- canonstr .substring (allowedTarget .length ()))) {
982
- acceptedNonlocalSymlinks .put (canonstr , absolstr );
983
- return true ;
995
+ if (LOGGER .isLoggable (Level .FINE )) {
996
+ LOGGER .log (Level .FINE , "External dir {0} has symlink from {1} after first {2}" ,
997
+ new Object [] {canonical1 , absolute1 , absolute0 });
984
998
}
999
+ return false ;
1000
+ }
1001
+ }
1002
+
1003
+ Set <String > allowedSymlinks = RuntimeEnvironment .getInstance ().getAllowedSymlinks ();
1004
+ for (String allowedSymlink : allowedSymlinks ) {
1005
+ String allowedTarget ;
1006
+ try {
1007
+ allowedTarget = new File (allowedSymlink ).getCanonicalPath ();
1008
+ } catch (IOException e ) {
1009
+ LOGGER .log (Level .FINE , "unresolvable symlink: {0}" , allowedSymlink );
1010
+ continue ;
1011
+ }
1012
+ /*
1013
+ * The following canonical check is sufficient because indexDown()
1014
+ * traverses top-down, and any intermediate symlinks would have
1015
+ * also been checked here for an allowed canonical match. This
1016
+ * technically means that if there is a set of redundant symlinks
1017
+ * with the same canonical target, then allowing one of the set
1018
+ * will allow all others in the set.
1019
+ */
1020
+ if (canonical1 .equals (allowedTarget )) {
1021
+ return true ;
985
1022
}
986
1023
}
987
1024
return false ;
@@ -1035,18 +1072,18 @@ private void indexDown(File dir, String parent, IndexDownArgs args)
1035
1072
return ;
1036
1073
}
1037
1074
1038
- String [] outLocalRelPath = new String [ 1 ] ;
1039
- if (!accept (dir , outLocalRelPath )) {
1040
- /**
1041
- * If outLocalRelPath[0] is defined, then a local symlink was
1042
- * detected but not "accepted" to avoid redundancy with its
1043
- * canonical target. Set up for a deferred recreation of the symlink
1044
- * for xref/.
1075
+ AcceptSymlinkRet ret = new AcceptSymlinkRet () ;
1076
+ if (!accept (dir , ret )) {
1077
+ /*
1078
+ * If ret.localRelPath is defined, then a symlink was detected but
1079
+ * not "accepted" to avoid redundancy with an already-accepted
1080
+ * canonical target. Set up for a deferred creation of a symlink
1081
+ * within xref/.
1045
1082
*/
1046
- if (outLocalRelPath [ 0 ] != null ) {
1083
+ if (ret . localRelPath != null ) {
1047
1084
File xrefPath = new File (xrefDir , parent );
1048
1085
PendingSymlinkage psym = new PendingSymlinkage (
1049
- xrefPath .getAbsolutePath (), outLocalRelPath [ 0 ] );
1086
+ xrefPath .getAbsolutePath (), ret . localRelPath );
1050
1087
completer .add (psym );
1051
1088
}
1052
1089
return ;
@@ -1062,11 +1099,12 @@ private void indexDown(File dir, String parent, IndexDownArgs args)
1062
1099
1063
1100
for (File file : files ) {
1064
1101
String path = parent + File .separator + file .getName ();
1065
- if (!accept (dir , file , outLocalRelPath )) {
1066
- if (outLocalRelPath [0 ] != null ) {
1102
+ if (!accept (dir , file , ret )) {
1103
+ if (ret .localRelPath != null ) {
1104
+ // See note above about ret.localRelPath.
1067
1105
File xrefPath = new File (xrefDir , path );
1068
1106
PendingSymlinkage psym = new PendingSymlinkage (
1069
- xrefPath .getAbsolutePath (), outLocalRelPath [ 0 ] );
1107
+ xrefPath .getAbsolutePath (), ret . localRelPath );
1070
1108
completer .add (psym );
1071
1109
}
1072
1110
} else {
@@ -1767,4 +1805,8 @@ private static class IndexFileWork {
1767
1805
this .path = path ;
1768
1806
}
1769
1807
}
1808
+
1809
+ private static class AcceptSymlinkRet {
1810
+ String localRelPath ;
1811
+ }
1770
1812
}
0 commit comments