Skip to content

Commit 92e2762

Browse files
authored
Merge pull request #2097 from idodeclare/bugfix/symlink_loop
Fix #1477, fix #2080 : Handle symlink loop. Xref symlink dir.
2 parents 1e66100 + 5b1c43f commit 92e2762

File tree

4 files changed

+341
-34
lines changed

4 files changed

+341
-34
lines changed

src/org/opensolaris/opengrok/index/IndexDatabase.java

Lines changed: 106 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,13 @@
3434
import java.io.OutputStreamWriter;
3535
import java.io.Writer;
3636
import java.net.ConnectException;
37+
import java.nio.file.Files;
38+
import java.nio.file.Path;
39+
import java.nio.file.Paths;
3740
import java.util.ArrayList;
3841
import java.util.Arrays;
3942
import java.util.Comparator;
43+
import java.util.HashMap;
4044
import java.util.HashSet;
4145
import java.util.List;
4246
import java.util.Map;
@@ -114,6 +118,9 @@ public class IndexDatabase {
114118

115119
private final Object INSTANCE_LOCK = new Object();
116120

121+
/** Key is canonical path; Value is the first accepted, absolute path. */
122+
private final Map<String, String> acceptedNonlocalSymlinks = new HashMap<>();
123+
117124
private Project project;
118125
private FSDirectory indexDirectory;
119126
private IndexReader reader;
@@ -401,6 +408,7 @@ public void update(IndexerParallelizer parallelizer)
401408
settings = null;
402409
uidIter = null;
403410
postsIter = null;
411+
acceptedNonlocalSymlinks.clear();
404412

405413
IOException finishingException = null;
406414
try {
@@ -792,9 +800,15 @@ private static void cleanupResources(Document doc) {
792800
* Check if I should accept this file into the index database
793801
*
794802
* @param file the file to check
803+
* @param outLocalRelPath optional output array whose 0-th element is set
804+
* to a relative path if and only if the {@code file} is a symlink targeting
805+
* a local directory (N.b. method will return {@code false})
795806
* @return true if the file should be included, false otherwise
796807
*/
797-
private boolean accept(File file) {
808+
private boolean accept(File file, String[] outLocalRelPath) {
809+
if (outLocalRelPath != null) {
810+
outLocalRelPath[0] = null;
811+
}
798812

799813
String absolutePath = file.getAbsolutePath();
800814

@@ -816,13 +830,16 @@ private boolean accept(File file) {
816830
}
817831

818832
try {
819-
String canonicalPath = file.getCanonicalPath();
820-
if (!absolutePath.equals(canonicalPath)
821-
&& !acceptSymlink(absolutePath, canonicalPath)) {
822-
823-
LOGGER.log(Level.FINE, "Skipped symlink ''{0}'' -> ''{1}''",
824-
new Object[]{absolutePath, canonicalPath});
825-
return false;
833+
Path absolute = Paths.get(absolutePath);
834+
if (Files.isSymbolicLink(absolute)) {
835+
File canonical = file.getCanonicalFile();
836+
if (!absolutePath.equals(canonical.getPath())
837+
&& !acceptSymlink(absolute, canonical,
838+
outLocalRelPath)) {
839+
LOGGER.log(Level.FINE, "Skipped symlink ''{0}'' -> ''{1}''",
840+
new Object[]{absolutePath, canonical});
841+
return false;
842+
}
826843
}
827844
//below will only let go files and directories, anything else is considered special and is not added
828845
if (!file.isFile() && !file.isDirectory()) {
@@ -856,7 +873,20 @@ private boolean accept(File file) {
856873
return res;
857874
}
858875

859-
private boolean accept(File parent, File file) {
876+
/**
877+
* Determines if {@code file} should be accepted into the index database.
878+
* @param parent parent of {@code file}
879+
* @param file directory object under consideration
880+
* @param outLocalRelPath optional output array whose 0-th element is set
881+
* to a relative path if and only if the {@code file} is a symlink targeting
882+
* a local directory (N.b. method will return {@code false})
883+
* @return {@code true} if the file should be included; else {@code false}
884+
*/
885+
private boolean accept(File parent, File file, String[] outLocalRelPath) {
886+
if (outLocalRelPath != null) {
887+
outLocalRelPath[0] = null;
888+
}
889+
860890
try {
861891
File f1 = parent.getCanonicalFile();
862892
File f2 = file.getCanonicalFile();
@@ -876,7 +906,7 @@ private boolean accept(File parent, File file) {
876906
}
877907
}
878908

879-
return accept(file);
909+
return accept(file, outLocalRelPath);
880910
} catch (IOException ex) {
881911
LOGGER.log(Level.WARNING, "Failed to resolve name: {0} {1}",
882912
new Object[]{parent.getAbsolutePath(), file.getAbsolutePath()});
@@ -887,21 +917,53 @@ private boolean accept(File parent, File file) {
887917
/**
888918
* Check if I should accept the path containing a symlink
889919
*
890-
* @param absolutePath the path with a symlink to check
891-
* @param canonicalPath the canonical path to the file
892-
* @return true if the file should be accepted, false otherwise
920+
* @param absolute the path with a symlink to check
921+
* @param canonical the canonical file object
922+
* @param outLocalRelPath optional output array whose 0-th element is set
923+
* to a relative path if and only if {@code absolute} is a symlink targeting
924+
* a local directory, {@code canonical} (N.b. method will return
925+
* {@code false})
926+
* @return {@code true} if the file should be accepted; else {@code false}
893927
*/
894-
private boolean acceptSymlink(String absolutePath, String canonicalPath) throws IOException {
895-
// Always accept local symlinks
896-
if (isLocal(canonicalPath)) {
897-
return true;
928+
private boolean acceptSymlink(Path absolute, File canonical,
929+
String[] outLocalRelPath) throws IOException {
930+
if (outLocalRelPath != null) {
931+
outLocalRelPath[0] = null;
898932
}
899933

934+
if (isLocal(canonical.getPath())) {
935+
if (!canonical.isDirectory()) {
936+
// Always accept symlinks to local non-directories.
937+
return true;
938+
} else {
939+
/**
940+
* Do not accept symlinks to local directories, because the
941+
* canonical target will be indexed on its own -- but
942+
* relativize() a path to be returned in outLocalRelPath so that
943+
* a symlink can be replicated in xref/.
944+
**/
945+
if (outLocalRelPath != null) {
946+
outLocalRelPath[0] = absolute.getParent().relativize(
947+
canonical.toPath()).toString();
948+
}
949+
return false;
950+
}
951+
}
952+
953+
// No need to synchronize, as indexDown() runs on one thread.
954+
if (acceptedNonlocalSymlinks.containsKey(canonical.getPath())) {
955+
return false;
956+
}
957+
958+
String absolstr = absolute.toString();
900959
for (String allowedSymlink : RuntimeEnvironment.getInstance().getAllowedSymlinks()) {
901-
if (absolutePath.startsWith(allowedSymlink)) {
960+
if (absolstr.startsWith(allowedSymlink)) {
902961
String allowedTarget = new File(allowedSymlink).getCanonicalPath();
903-
if (canonicalPath.startsWith(allowedTarget)
904-
&& absolutePath.substring(allowedSymlink.length()).equals(canonicalPath.substring(allowedTarget.length()))) {
962+
String canonstr = canonical.getPath();
963+
if (canonstr.startsWith(allowedTarget)
964+
&& absolstr.substring(allowedSymlink.length()).equals(
965+
canonstr.substring(allowedTarget.length()))) {
966+
acceptedNonlocalSymlinks.put(canonstr, absolstr);
905967
return true;
906968
}
907969
}
@@ -966,7 +1028,20 @@ private void indexDown(File dir, String parent, IndexDownArgs args)
9661028
return;
9671029
}
9681030

969-
if (!accept(dir)) {
1031+
String[] outLocalRelPath = new String[1];
1032+
if (!accept(dir, outLocalRelPath)) {
1033+
/**
1034+
* If outLocalRelPath[0] is defined, then a local symlink was
1035+
* detected but not "accepted" to avoid redundancy with its
1036+
* canonical target. Set up for a deferred recreation of the symlink
1037+
* for xref/.
1038+
*/
1039+
if (outLocalRelPath[0] != null) {
1040+
File xrefPath = new File(xrefDir, parent);
1041+
PendingSymlinkage psym = new PendingSymlinkage(
1042+
xrefPath.getAbsolutePath(), outLocalRelPath[0]);
1043+
completer.add(psym);
1044+
}
9701045
return;
9711046
}
9721047

@@ -979,9 +1054,15 @@ private void indexDown(File dir, String parent, IndexDownArgs args)
9791054
Arrays.sort(files, FILENAME_COMPARATOR);
9801055

9811056
for (File file : files) {
982-
if (accept(dir, file)) {
983-
String path = parent + '/' + file.getName();
984-
1057+
String path = parent + '/' + file.getName();
1058+
if (!accept(dir, file, outLocalRelPath)) {
1059+
if (outLocalRelPath[0] != null) {
1060+
File xrefPath = new File(xrefDir, path);
1061+
PendingSymlinkage psym = new PendingSymlinkage(
1062+
xrefPath.getAbsolutePath(), outLocalRelPath[0]);
1063+
completer.add(psym);
1064+
}
1065+
} else {
9851066
if (file.isDirectory()) {
9861067
indexDown(file, path, args);
9871068
} else {
@@ -1527,7 +1608,7 @@ private void finishWriting() throws IOException {
15271608
hasPendingCommit = true;
15281609

15291610
int n = completer.complete();
1530-
LOGGER.log(Level.FINE, "completed {0} file(s)", n);
1611+
LOGGER.log(Level.FINE, "completed {0} object(s)", n);
15311612

15321613
// Just before commit(), reset the `hasPendingCommit' flag,
15331614
// since after commit() is called, there is no need for

0 commit comments

Comments
 (0)