Search in sources :

Example 11 with ForbiddenSymlinkException

use of org.opengrok.indexer.util.ForbiddenSymlinkException in project OpenGrok by OpenGrok.

the class IndexDatabase method acceptSymlink.

/**
 * Check if I should accept the path containing a symlink.
 *
 * @param absolute the path with a symlink to check
 * @param canonical the canonical file object
 * @param ret defined instance whose {@code localRelPath} property will be
 * non-null afterward if and only if {@code absolute} is a symlink that
 * targets either a {@link Repository}-local filesystem object or the same
 * object ({@code canonical}) as a previously-detected and allowed symlink.
 * N.b. method will return {@code false} if {@code ret.localRelPath} is set
 * non-null.
 * @return a value indicating if {@code file} should be included in index
 */
private boolean acceptSymlink(Path absolute, File canonical, AcceptSymlinkRet ret) {
    ret.localRelPath = null;
    String absolute1 = absolute.toString();
    String canonical1 = canonical.getPath();
    boolean isCanonicalDir = canonical.isDirectory();
    RuntimeEnvironment env = RuntimeEnvironment.getInstance();
    IndexedSymlink indexed1;
    String absolute0;
    if (isLocal(canonical1)) {
        if (!isCanonicalDir) {
            if (LOGGER.isLoggable(Level.FINEST)) {
                LOGGER.log(Level.FINEST, "Local {0} has symlink from {1}", new Object[] { canonical1, absolute1 });
            }
            /*
                 * Always index symlinks to local files, but do not add to
                 * indexedSymlinks for a non-directory.
                 */
            return true;
        }
        /*
             * Do not index symlinks to local directories, because the
             * canonical target will be indexed on its own -- but relativize()
             * a path to be returned in ret so that a symlink can be replicated
             * in xref/.
             */
        ret.localRelPath = absolute.getParent().relativize(canonical.toPath()).toString();
        // Try to put the prime absolute path into indexedSymlinks.
        try {
            String primeRelative = env.getPathRelativeToSourceRoot(canonical);
            absolute0 = env.getSourceRootPath() + primeRelative;
        } catch (ForbiddenSymlinkException | IOException e) {
            /*
                 * This is not expected, as indexDown() would have operated on
                 * the file already -- but we are forced to handle.
                 */
            LOGGER.log(Level.WARNING, String.format("Unexpected error getting relative for %s", canonical), e);
            absolute0 = absolute1;
        }
        indexed1 = new IndexedSymlink(absolute0, canonical1, true);
        indexedSymlinks.put(canonical1, indexed1);
        return false;
    }
    IndexedSymlink indexed0;
    if ((indexed0 = indexedSymlinks.get(canonical1)) != null) {
        if (absolute1.equals(indexed0.getAbsolute())) {
            return true;
        }
        /*
             * Do not index symlinks to external directories already indexed
             * as linked elsewhere, because the canonical target will be
             * indexed already -- but relativize() a path to be returned in ret
             * so that this second symlink can be redone as a local
             * (non-external) symlink in xref/.
             */
        ret.localRelPath = absolute.getParent().relativize(Paths.get(indexed0.getAbsolute())).toString();
        if (LOGGER.isLoggable(Level.FINEST)) {
            LOGGER.log(Level.FINEST, "External dir {0} has symlink from {1} after first {2}", new Object[] { canonical1, absolute1, indexed0.getAbsolute() });
        }
        return false;
    }
    /*
         * Iterate through indexedSymlinks, which is sorted so that shorter
         * canonical entries come first, to see if the new link is a child
         * canonically.
         */
    for (IndexedSymlink a0 : indexedSymlinks.values()) {
        indexed0 = a0;
        if (!indexed0.isLocal() && canonical1.startsWith(indexed0.getCanonicalSeparated())) {
            absolute0 = indexed0.getAbsolute();
            if (!isCanonicalDir) {
                if (LOGGER.isLoggable(Level.FINEST)) {
                    LOGGER.log(Level.FINEST, "External file {0} has symlink from {1} under previous {2}", new Object[] { canonical1, absolute1, absolute0 });
                }
                // Do not add to indexedSymlinks for a non-directory.
                return true;
            }
            /*
                 * See above about redoing a sourceRoot symlink as a local
                 * (non-external) symlink in xref/.
                 */
            Path abs0 = Paths.get(absolute0, canonical1.substring(indexed0.getCanonicalSeparated().length()));
            ret.localRelPath = absolute.getParent().relativize(abs0).toString();
            if (LOGGER.isLoggable(Level.FINEST)) {
                LOGGER.log(Level.FINEST, "External dir {0} has symlink from {1} under previous {2}", new Object[] { canonical1, absolute1, absolute0 });
            }
            return false;
        }
    }
    Set<String> canonicalRoots = env.getCanonicalRoots();
    for (String canonicalRoot : canonicalRoots) {
        if (canonical1.startsWith(canonicalRoot)) {
            if (LOGGER.isLoggable(Level.FINEST)) {
                LOGGER.log(Level.FINEST, "Allowed symlink {0} per canonical root {1}", new Object[] { absolute1, canonical1 });
            }
            if (isCanonicalDir) {
                indexed1 = new IndexedSymlink(absolute1, canonical1, false);
                indexedSymlinks.put(canonical1, indexed1);
            }
            return true;
        }
    }
    Set<String> allowedSymlinks = env.getAllowedSymlinks();
    for (String allowedSymlink : allowedSymlinks) {
        String allowedTarget;
        try {
            allowedTarget = new File(allowedSymlink).getCanonicalPath();
        } catch (IOException e) {
            LOGGER.log(Level.FINE, "unresolvable symlink: {0}", allowedSymlink);
            continue;
        }
        /*
             * The following canonical check is sufficient because indexDown()
             * traverses top-down, and any intermediate symlinks would have
             * also been checked here for an allowed canonical match. This
             * technically means that if there is a set of redundant symlinks
             * with the same canonical target, then allowing one of the set
             * will allow all others in the set.
             */
        if (canonical1.equals(allowedTarget)) {
            if (isCanonicalDir) {
                indexed1 = new IndexedSymlink(absolute1, canonical1, false);
                indexedSymlinks.put(canonical1, indexed1);
            }
            return true;
        }
    }
    return false;
}
Also used : Path(java.nio.file.Path) TandemPath(org.opengrok.indexer.util.TandemPath) RuntimeEnvironment(org.opengrok.indexer.configuration.RuntimeEnvironment) ForbiddenSymlinkException(org.opengrok.indexer.util.ForbiddenSymlinkException) IOException(java.io.IOException) File(java.io.File)

Example 12 with ForbiddenSymlinkException

use of org.opengrok.indexer.util.ForbiddenSymlinkException in project OpenGrok by OpenGrok.

the class IndexDatabase method getDocument.

/**
 * @param file File object of a file under source root
 * @return Document object for the file or {@code null}
 * @throws IOException on I/O error
 * @throws ParseException on problem with building Query
 */
public static Document getDocument(File file) throws IOException, ParseException {
    RuntimeEnvironment env = RuntimeEnvironment.getInstance();
    String path;
    try {
        path = env.getPathRelativeToSourceRoot(file);
    } catch (ForbiddenSymlinkException e) {
        LOGGER.log(Level.FINER, e.getMessage());
        return null;
    }
    // Sanitize Windows path delimiters in order not to conflict with Lucene escape character.
    path = path.replace("\\", "/");
    try (IndexReader ireader = getIndexReader(path)) {
        if (ireader == null) {
            // No index, no document..
            return null;
        }
        Document doc;
        Query q = new QueryBuilder().setPath(path).build();
        IndexSearcher searcher = new IndexSearcher(ireader);
        Statistics stat = new Statistics();
        TopDocs top = searcher.search(q, 1);
        stat.report(LOGGER, Level.FINEST, "search via getDocument done", "search.latency", new String[] { "category", "getdocument", "outcome", top.totalHits.value == 0 ? "empty" : "success" });
        if (top.totalHits.value == 0) {
            // No hits, no document...
            return null;
        }
        doc = searcher.doc(top.scoreDocs[0].doc);
        String foundPath = doc.get(QueryBuilder.PATH);
        // Only use the document if we found an exact match.
        if (!path.equals(foundPath)) {
            return null;
        }
        return doc;
    }
}
Also used : IndexSearcher(org.apache.lucene.search.IndexSearcher) TopDocs(org.apache.lucene.search.TopDocs) RuntimeEnvironment(org.opengrok.indexer.configuration.RuntimeEnvironment) ForbiddenSymlinkException(org.opengrok.indexer.util.ForbiddenSymlinkException) Query(org.apache.lucene.search.Query) IndexReader(org.apache.lucene.index.IndexReader) QueryBuilder(org.opengrok.indexer.search.QueryBuilder) Document(org.apache.lucene.document.Document) Statistics(org.opengrok.indexer.util.Statistics)

Example 13 with ForbiddenSymlinkException

use of org.opengrok.indexer.util.ForbiddenSymlinkException in project OpenGrok by OpenGrok.

the class HistoryGuru method getLastHistoryEntry.

/**
 * @param file file to get the history entry for
 * @param ui is the request coming from the UI
 * @return last (newest) history entry for given file or null
 * @throws HistoryException if history retrieval failed
 */
@Nullable
public HistoryEntry getLastHistoryEntry(File file, boolean ui) throws HistoryException {
    final File dir = file.isDirectory() ? file : file.getParentFile();
    final Repository repository = getRepository(dir);
    if (!isRepoHistoryEligible(repository, file, ui)) {
        return null;
    }
    History history;
    try {
        history = getHistoryFromCache(file, repository, false, false);
        if (history != null) {
            HistoryEntry lastHistoryEntry = history.getLastHistoryEntry();
            if (lastHistoryEntry != null) {
                LOGGER.log(Level.FINEST, "got latest history entry {0} for ''{1}'' from history cache", new Object[] { lastHistoryEntry, file });
                return lastHistoryEntry;
            }
        }
    } catch (ForbiddenSymlinkException e) {
        LOGGER.log(Level.FINER, e.getMessage());
        return null;
    }
    return repository.getLastHistoryEntry(file, ui);
}
Also used : ForbiddenSymlinkException(org.opengrok.indexer.util.ForbiddenSymlinkException) File(java.io.File) Nullable(org.jetbrains.annotations.Nullable)

Example 14 with ForbiddenSymlinkException

use of org.opengrok.indexer.util.ForbiddenSymlinkException in project OpenGrok by OpenGrok.

the class HistoryGuru method addRepositories.

/**
 * recursively search for repositories with a depth limit, add those found
 * to the internally used map.
 *
 * @param files list of files to check if they contain a repository
 * @param allowedNesting number of levels of nested repos to allow
 * @param depth current depth - using global scanningDepth - one can limit
 * this to improve scanning performance
 * @param isNested a value indicating if a parent {@link Repository} was
 * already found above the {@code files}
 * @return collection of added repositories
 */
private Collection<RepositoryInfo> addRepositories(File[] files, int allowedNesting, int depth, boolean isNested) {
    List<RepositoryInfo> repoList = new ArrayList<>();
    PathAccepter pathAccepter = env.getPathAccepter();
    for (File file : files) {
        if (!file.isDirectory()) {
            continue;
        }
        String path;
        try {
            path = file.getCanonicalPath();
            Repository repository = null;
            try {
                repository = RepositoryFactory.getRepository(file, CommandTimeoutType.INDEXER, isNested);
            } catch (InstantiationException | NoSuchMethodException | InvocationTargetException e) {
                LOGGER.log(Level.WARNING, "Could not create repository for '" + file + "', could not instantiate the repository.", e);
            } catch (IllegalAccessException iae) {
                LOGGER.log(Level.WARNING, "Could not create repository for '" + file + "', missing access rights.", iae);
                continue;
            } catch (ForbiddenSymlinkException e) {
                LOGGER.log(Level.WARNING, "Could not create repository for ''{0}'': {1}", new Object[] { file, e.getMessage() });
                continue;
            }
            if (repository == null) {
                if (depth > env.getScanningDepth()) {
                    // we reached our search max depth, skip looking through the children
                    continue;
                }
                // Not a repository, search its sub-dirs.
                if (pathAccepter.accept(file)) {
                    File[] subFiles = file.listFiles();
                    if (subFiles == null) {
                        LOGGER.log(Level.WARNING, "Failed to get sub directories for ''{0}'', " + "check access permissions.", file.getAbsolutePath());
                    } else {
                        // Recursive call to scan next depth
                        repoList.addAll(addRepositories(subFiles, allowedNesting, depth + 1, isNested));
                    }
                }
            } else {
                LOGGER.log(Level.CONFIG, "Adding <{0}> repository: <{1}>", new Object[] { repository.getClass().getName(), path });
                repoList.add(new RepositoryInfo(repository));
                putRepository(repository);
                if (allowedNesting > 0 && repository.supportsSubRepositories()) {
                    File[] subFiles = file.listFiles();
                    if (subFiles == null) {
                        LOGGER.log(Level.WARNING, "Failed to get sub directories for ''{0}'', check access permissions.", file.getAbsolutePath());
                    } else if (depth <= env.getScanningDepth()) {
                        // Search down to a limit -- if not: too much
                        // stat'ing for huge Mercurial repositories
                        repoList.addAll(addRepositories(subFiles, allowedNesting - 1, depth + 1, true));
                    }
                }
            }
        } catch (IOException exp) {
            LOGGER.log(Level.WARNING, "Failed to get canonical path for {0}: {1}", new Object[] { file.getAbsolutePath(), exp.getMessage() });
            LOGGER.log(Level.WARNING, "Repository will be ignored...", exp);
        }
    }
    return repoList;
}
Also used : ForbiddenSymlinkException(org.opengrok.indexer.util.ForbiddenSymlinkException) ArrayList(java.util.ArrayList) IOException(java.io.IOException) InvocationTargetException(java.lang.reflect.InvocationTargetException) PathAccepter(org.opengrok.indexer.configuration.PathAccepter) File(java.io.File)

Example 15 with ForbiddenSymlinkException

use of org.opengrok.indexer.util.ForbiddenSymlinkException in project OpenGrok by OpenGrok.

the class RuntimeEnvironmentTest method testGetPathRelativeToSourceRoot.

/**
 * Verify that getPathRelativeToSourceRoot() returns path relative to
 * source root for both directories and symbolic links.
 * @throws java.io.IOException I/O exception
 * @throws ForbiddenSymlinkException forbidden symlink exception
 */
@Test
public void testGetPathRelativeToSourceRoot() throws IOException, ForbiddenSymlinkException {
    RuntimeEnvironment env = RuntimeEnvironment.getInstance();
    // Create and set source root.
    File sourceRoot = Files.createTempDirectory("src").toFile();
    assertTrue(sourceRoot.exists());
    assertTrue(sourceRoot.isDirectory());
    env.setSourceRoot(sourceRoot.getPath());
    // Create directory underneath source root and check.
    String filename = "foo";
    File file = new File(env.getSourceRootFile(), filename);
    file.createNewFile();
    assertTrue(file.exists());
    assertEquals(File.separator + filename, env.getPathRelativeToSourceRoot(file));
    // Create symlink underneath source root.
    String symlinkName = "symlink";
    Path realDir = Files.createTempDirectory("realdir");
    File symlink = new File(sourceRoot, symlinkName);
    Files.createSymbolicLink(symlink.toPath(), realDir);
    assertTrue(symlink.exists());
    env.setAllowedSymlinks(new HashSet<>());
    ForbiddenSymlinkException expex = null;
    try {
        env.getPathRelativeToSourceRoot(symlink);
    } catch (ForbiddenSymlinkException e) {
        expex = e;
    }
    assertNotNull(expex, "getPathRelativeToSourceRoot() should have thrown " + "IOexception for symlink that is not allowed");
    // Allow the symlink and retest.
    env.setAllowedSymlinks(new HashSet<>(Arrays.asList(symlink.getPath())));
    assertEquals(File.separator + symlinkName, env.getPathRelativeToSourceRoot(symlink));
    // cleanup
    IOUtils.removeRecursive(sourceRoot.toPath());
    IOUtils.removeRecursive(realDir);
}
Also used : Path(java.nio.file.Path) ForbiddenSymlinkException(org.opengrok.indexer.util.ForbiddenSymlinkException) File(java.io.File) Test(org.junit.jupiter.api.Test)

Aggregations

ForbiddenSymlinkException (org.opengrok.indexer.util.ForbiddenSymlinkException)17 File (java.io.File)14 IOException (java.io.IOException)8 RuntimeEnvironment (org.opengrok.indexer.configuration.RuntimeEnvironment)7 BufferedReader (java.io.BufferedReader)3 InputStreamReader (java.io.InputStreamReader)3 InvalidPathException (java.nio.file.InvalidPathException)3 ParseException (java.text.ParseException)3 ArrayList (java.util.ArrayList)3 Date (java.util.Date)3 FileNotFoundException (java.io.FileNotFoundException)2 Path (java.nio.file.Path)2 TreeSet (java.util.TreeSet)2 RepositoryInfo (org.opengrok.indexer.history.RepositoryInfo)2 Statistics (org.opengrok.indexer.util.Statistics)2 Inject (jakarta.inject.Inject)1 HttpServletRequest (jakarta.servlet.http.HttpServletRequest)1 Consumes (jakarta.ws.rs.Consumes)1 DELETE (jakarta.ws.rs.DELETE)1 GET (jakarta.ws.rs.GET)1