9494import org .opengrok .indexer .configuration .RuntimeEnvironment ;
9595import org .opengrok .indexer .history .HistoryException ;
9696import org .opengrok .indexer .history .HistoryGuru ;
97+ import org .opengrok .indexer .history .Repository ;
9798import org .opengrok .indexer .logger .LoggerFactory ;
9899import org .opengrok .indexer .search .QueryBuilder ;
99100import org .opengrok .indexer .util .ForbiddenSymlinkException ;
@@ -816,16 +817,15 @@ private static void cleanupResources(Document doc) {
816817 * Check if I should accept this file into the index database.
817818 *
818819 * @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
823826 */
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 ;
829829 String absolutePath = file .getAbsolutePath ();
830830
831831 if (!includedNames .isEmpty ()
@@ -849,9 +849,8 @@ private boolean accept(File file, String[] outLocalRelPath) {
849849 Path absolute = Paths .get (absolutePath );
850850 if (Files .isSymbolicLink (absolute )) {
851851 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 )) {
855854 LOGGER .log (Level .FINE , "Skipped symlink ''{0}'' -> ''{1}''" ,
856855 new Object []{absolutePath , canonical });
857856 return false ;
@@ -893,15 +892,15 @@ private boolean accept(File file, String[] outLocalRelPath) {
893892 * Determines if {@code file} should be accepted into the index database.
894893 * @param parent parent of {@code file}
895894 * @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
900901 */
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 ;
905904
906905 try {
907906 File f1 = parent .getCanonicalFile ();
@@ -922,7 +921,7 @@ private boolean accept(File parent, File file, String[] outLocalRelPath) {
922921 }
923922 }
924923
925- return accept (file , outLocalRelPath );
924+ return accept (file , ret );
926925 } catch (IOException ex ) {
927926 LOGGER .log (Level .WARNING , "Failed to resolve name: {0} {1}" ,
928927 new Object []{parent .getAbsolutePath (), file .getAbsolutePath ()});
@@ -935,53 +934,91 @@ private boolean accept(File parent, File file, String[] outLocalRelPath) {
935934 *
936935 * @param absolute the path with a symlink to check
937936 * @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
943944 */
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+ }
953959 return true ;
954960 } else {
955- /**
961+ /*
956962 * Do not accept symlinks to local directories, because the
957963 * 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
959965 * 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 ();
965969 return false ;
966970 }
967971 }
968972
973+ String absolute0 ;
969974 // 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 ();
973994
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 });
984998 }
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 ;
9851022 }
9861023 }
9871024 return false ;
@@ -1035,18 +1072,18 @@ private void indexDown(File dir, String parent, IndexDownArgs args)
10351072 return ;
10361073 }
10371074
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/.
10451082 */
1046- if (outLocalRelPath [ 0 ] != null ) {
1083+ if (ret . localRelPath != null ) {
10471084 File xrefPath = new File (xrefDir , parent );
10481085 PendingSymlinkage psym = new PendingSymlinkage (
1049- xrefPath .getAbsolutePath (), outLocalRelPath [ 0 ] );
1086+ xrefPath .getAbsolutePath (), ret . localRelPath );
10501087 completer .add (psym );
10511088 }
10521089 return ;
@@ -1062,11 +1099,12 @@ private void indexDown(File dir, String parent, IndexDownArgs args)
10621099
10631100 for (File file : files ) {
10641101 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.
10671105 File xrefPath = new File (xrefDir , path );
10681106 PendingSymlinkage psym = new PendingSymlinkage (
1069- xrefPath .getAbsolutePath (), outLocalRelPath [ 0 ] );
1107+ xrefPath .getAbsolutePath (), ret . localRelPath );
10701108 completer .add (psym );
10711109 }
10721110 } else {
@@ -1767,4 +1805,8 @@ private static class IndexFileWork {
17671805 this .path = path ;
17681806 }
17691807 }
1808+
1809+ private static class AcceptSymlinkRet {
1810+ String localRelPath ;
1811+ }
17701812}
0 commit comments