Skip to content

Commit e253612

Browse files
committed
Fix #1477, fix #2080 : Handle symlink loop. Xref symlink dir.
1 parent 8626e94 commit e253612

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;
@@ -110,6 +114,9 @@ public class IndexDatabase {
110114

111115
private final Object INSTANCE_LOCK = new Object();
112116

117+
/** Key is canonical path; Value is the first accepted, absolute path. */
118+
private final Map<String, String> acceptedNonlocalSymlinks = new HashMap<>();
119+
113120
private Project project;
114121
private FSDirectory indexDirectory;
115122
private IndexReader reader;
@@ -390,6 +397,7 @@ public void update(IndexerParallelizer parallelizer)
390397
writer = null;
391398
settings = null;
392399
uidIter = null;
400+
acceptedNonlocalSymlinks.clear();
393401

394402
IOException finishingException = null;
395403
try {
@@ -775,9 +783,15 @@ private static void cleanupResources(Document doc) {
775783
* Check if I should accept this file into the index database
776784
*
777785
* @param file the file to check
786+
* @param outLocalRelPath optional output array whose 0-th element is set
787+
* to a relative path if and only if the {@code file} is a symlink targeting
788+
* a local directory (N.b. method will return {@code false})
778789
* @return true if the file should be included, false otherwise
779790
*/
780-
private boolean accept(File file) {
791+
private boolean accept(File file, String[] outLocalRelPath) {
792+
if (outLocalRelPath != null) {
793+
outLocalRelPath[0] = null;
794+
}
781795

782796
String absolutePath = file.getAbsolutePath();
783797

@@ -799,13 +813,16 @@ private boolean accept(File file) {
799813
}
800814

801815
try {
802-
String canonicalPath = file.getCanonicalPath();
803-
if (!absolutePath.equals(canonicalPath)
804-
&& !acceptSymlink(absolutePath, canonicalPath)) {
805-
806-
LOGGER.log(Level.FINE, "Skipped symlink ''{0}'' -> ''{1}''",
807-
new Object[]{absolutePath, canonicalPath});
808-
return false;
816+
Path absolute = Paths.get(absolutePath);
817+
if (Files.isSymbolicLink(absolute)) {
818+
File canonical = file.getCanonicalFile();
819+
if (!absolutePath.equals(canonical.getPath())
820+
&& !acceptSymlink(absolute, canonical,
821+
outLocalRelPath)) {
822+
LOGGER.log(Level.FINE, "Skipped symlink ''{0}'' -> ''{1}''",
823+
new Object[]{absolutePath, canonical});
824+
return false;
825+
}
809826
}
810827
//below will only let go files and directories, anything else is considered special and is not added
811828
if (!file.isFile() && !file.isDirectory()) {
@@ -839,7 +856,20 @@ private boolean accept(File file) {
839856
return res;
840857
}
841858

842-
private boolean accept(File parent, File file) {
859+
/**
860+
* Determines if {@code file} should be accepted into the index database.
861+
* @param parent parent of {@code file}
862+
* @param file directory object under consideration
863+
* @param outLocalRelPath optional output array whose 0-th element is set
864+
* to a relative path if and only if the {@code file} is a symlink targeting
865+
* a local directory (N.b. method will return {@code false})
866+
* @return {@code true} if the file should be included; else {@code false}
867+
*/
868+
private boolean accept(File parent, File file, String[] outLocalRelPath) {
869+
if (outLocalRelPath != null) {
870+
outLocalRelPath[0] = null;
871+
}
872+
843873
try {
844874
File f1 = parent.getCanonicalFile();
845875
File f2 = file.getCanonicalFile();
@@ -859,7 +889,7 @@ private boolean accept(File parent, File file) {
859889
}
860890
}
861891

862-
return accept(file);
892+
return accept(file, outLocalRelPath);
863893
} catch (IOException ex) {
864894
LOGGER.log(Level.WARNING, "Failed to resolve name: {0} {1}",
865895
new Object[]{parent.getAbsolutePath(), file.getAbsolutePath()});
@@ -870,21 +900,53 @@ private boolean accept(File parent, File file) {
870900
/**
871901
* Check if I should accept the path containing a symlink
872902
*
873-
* @param absolutePath the path with a symlink to check
874-
* @param canonicalPath the canonical path to the file
875-
* @return true if the file should be accepted, false otherwise
903+
* @param absolute the path with a symlink to check
904+
* @param canonical the canonical file object
905+
* @param outLocalRelPath optional output array whose 0-th element is set
906+
* to a relative path if and only if {@code absolute} is a symlink targeting
907+
* a local directory, {@code canonical} (N.b. method will return
908+
* {@code false})
909+
* @return {@code true} if the file should be accepted; else {@code false}
876910
*/
877-
private boolean acceptSymlink(String absolutePath, String canonicalPath) throws IOException {
878-
// Always accept local symlinks
879-
if (isLocal(canonicalPath)) {
880-
return true;
911+
private boolean acceptSymlink(Path absolute, File canonical,
912+
String[] outLocalRelPath) throws IOException {
913+
if (outLocalRelPath != null) {
914+
outLocalRelPath[0] = null;
915+
}
916+
917+
if (isLocal(canonical.getPath())) {
918+
if (!canonical.isDirectory()) {
919+
// Always accept symlinks to local non-directories.
920+
return true;
921+
} else {
922+
/**
923+
* Do not accept symlinks to local directories, because the
924+
* canonical target will be indexed on its own -- but
925+
* relativize() a path to be returned in outLocalRelPath so that
926+
* a symlink can be replicated in xref/.
927+
**/
928+
if (outLocalRelPath != null) {
929+
outLocalRelPath[0] = absolute.getParent().relativize(
930+
canonical.toPath()).toString();
931+
}
932+
return false;
933+
}
934+
}
935+
936+
// No need to synchronize, as indexDown() runs on one thread.
937+
if (acceptedNonlocalSymlinks.containsKey(canonical.getPath())) {
938+
return false;
881939
}
882940

941+
String absolstr = absolute.toString();
883942
for (String allowedSymlink : RuntimeEnvironment.getInstance().getAllowedSymlinks()) {
884-
if (absolutePath.startsWith(allowedSymlink)) {
943+
if (absolstr.startsWith(allowedSymlink)) {
885944
String allowedTarget = new File(allowedSymlink).getCanonicalPath();
886-
if (canonicalPath.startsWith(allowedTarget)
887-
&& absolutePath.substring(allowedSymlink.length()).equals(canonicalPath.substring(allowedTarget.length()))) {
945+
String canonstr = canonical.getPath();
946+
if (canonstr.startsWith(allowedTarget)
947+
&& absolstr.substring(allowedSymlink.length()).equals(
948+
canonstr.substring(allowedTarget.length()))) {
949+
acceptedNonlocalSymlinks.put(canonstr, absolstr);
888950
return true;
889951
}
890952
}
@@ -949,7 +1011,20 @@ private void indexDown(File dir, String parent, IndexDownArgs args)
9491011
return;
9501012
}
9511013

952-
if (!accept(dir)) {
1014+
String[] outLocalRelPath = new String[1];
1015+
if (!accept(dir, outLocalRelPath)) {
1016+
/**
1017+
* If outLocalRelPath[0] is defined, then a local symlink was
1018+
* detected but not "accepted" to avoid redundancy with its
1019+
* canonical target. Set up for a deferred recreation of the symlink
1020+
* for xref/.
1021+
*/
1022+
if (outLocalRelPath[0] != null) {
1023+
File xrefPath = new File(xrefDir, parent);
1024+
PendingSymlinkage psym = new PendingSymlinkage(
1025+
xrefPath.getAbsolutePath(), outLocalRelPath[0]);
1026+
completer.add(psym);
1027+
}
9531028
return;
9541029
}
9551030

@@ -962,9 +1037,15 @@ private void indexDown(File dir, String parent, IndexDownArgs args)
9621037
Arrays.sort(files, FILENAME_COMPARATOR);
9631038

9641039
for (File file : files) {
965-
if (accept(dir, file)) {
966-
String path = parent + '/' + file.getName();
967-
1040+
String path = parent + '/' + file.getName();
1041+
if (!accept(dir, file, outLocalRelPath)) {
1042+
if (outLocalRelPath[0] != null) {
1043+
File xrefPath = new File(xrefDir, path);
1044+
PendingSymlinkage psym = new PendingSymlinkage(
1045+
xrefPath.getAbsolutePath(), outLocalRelPath[0]);
1046+
completer.add(psym);
1047+
}
1048+
} else {
9681049
if (file.isDirectory()) {
9691050
indexDown(file, path, args);
9701051
} else {
@@ -1510,7 +1591,7 @@ private void finishWriting() throws IOException {
15101591
hasPendingCommit = true;
15111592

15121593
int n = completer.complete();
1513-
LOGGER.log(Level.FINE, "completed {0} file(s)", n);
1594+
LOGGER.log(Level.FINE, "completed {0} object(s)", n);
15141595

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

0 commit comments

Comments
 (0)