Skip to content

Prune directory links #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ project.ext.moduleName = 'com.blackduck.integration.hub-imageinspector-lib'
project.ext.javaUseAutoModuleName = 'true'
project.ext.junitShowStandardStreams = 'true'

version = '15.0.3-SNAPSHOT'
version = '15.0.3-IDETECT-4714-prune-dir-links-SNAPSHOT'
description = 'A library for creating Black Duck Input Output (BDIO) representing the packages installed in a Linux Docker image'

apply plugin: "io.spring.dependency-management"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,37 +191,27 @@ public boolean createNewFile(final File newFile) throws IOException {
}

public void pruneProblematicSymLinksRecursively(final File dir) throws IOException {
logger.trace(String.format("pruneDanglingSymLinksRecursively: %s", dir.getAbsolutePath()));
for (File dirEntry : dir.listFiles()) {
if (mustPrune(dir, dirEntry)) {
if (mustPrune(dirEntry)) {
final boolean deleteSucceeded = Files.deleteIfExists(dirEntry.toPath());
if (!deleteSucceeded) {
logger.warn(String.format("Delete of dangling or circular symlink %s failed", dirEntry.getAbsolutePath()));
logger.warn("Delete of problematic symlink {} failed", dirEntry.getAbsolutePath());
}
} else if (dirEntry.isDirectory()) {
pruneProblematicSymLinksRecursively(dirEntry);
}
}
}

private boolean mustPrune(File dir, File dirEntry) throws IOException {
Path dirEntryAsPath = dirEntry.toPath();
private boolean mustPrune(File entry) {
Path dirEntryAsPath = entry.toPath();
if (!Files.isSymbolicLink(dirEntryAsPath)) {
return false;
}
final Path symLinkTargetPath = Files.readSymbolicLink(dirEntryAsPath);
final File symLinkTargetFile = new File(dir, symLinkTargetPath.toString());
Path symLinkTargetPathAdjusted = symLinkTargetFile.toPath();
logger.trace(String.format("Found symlink %s -> %s [link value: %s]", dirEntry.getAbsolutePath(), symLinkTargetFile.getAbsolutePath(), symLinkTargetPath));
logger.trace(String.format("Checking to see if %s starts with %s", dirEntryAsPath.normalize().toFile().getAbsolutePath(), symLinkTargetPathAdjusted.normalize().toFile().getAbsolutePath()));
if (dirEntryAsPath.normalize().startsWith(symLinkTargetPathAdjusted.normalize())) {
logger.debug(String.format("symlink %s lives under its target %s; this is a circular symlink that will/must be deleted", dirEntry.getAbsolutePath(), symLinkTargetFile.getAbsolutePath()));
return true;
}
if (!symLinkTargetFile.exists()) {
logger.debug(String.format("Symlink target %s does not exist; %s is a dangling symlink that will/must be deleted", symLinkTargetFile.getAbsolutePath(), dirEntry.getAbsolutePath()));

if (entry.isDirectory()) {
return true;
}
return false;
return !entry.exists();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,39 @@

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;

public class FileOperationsTest {
private static final File TEST_DIR = new File("test");

@BeforeEach
public void setUp() throws IOException {
deleteDirectory(TEST_DIR);
TEST_DIR.mkdirs();
}

private void deleteDirectory(File directory) {
if (directory.exists()) {
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
deleteDirectory(file);
} else {
file.delete();
}
}
}
directory.delete();
}
}
private static FileOperations fileOperations;

@BeforeAll
Expand All @@ -26,9 +52,9 @@ public static void tearDownAfterAll() {

@Test
public void testMoveFile() throws IOException {
final File fileToMove = new File("test/fileToMove.txt");
final File fileToMove = new File(TEST_DIR, "fileToMove.txt");
fileToMove.createNewFile();
File imageDir = new File("test/output");
File imageDir = new File(TEST_DIR, "output");
if (!imageDir.exists()) {
imageDir.mkdirs();
}
Expand All @@ -41,7 +67,7 @@ public void testMoveFile() throws IOException {

@Test
public void testDeleteFilesOnly() throws IOException {
final File fileToDelete = new File("test/dirWithFileToDelete/fileToDelete.txt");
final File fileToDelete = new File(TEST_DIR, "dirWithFileToDelete/fileToDelete.txt");
fileToDelete.getParentFile().mkdir();
fileToDelete.createNewFile();

Expand All @@ -54,7 +80,7 @@ public void testDeleteFilesOnly() throws IOException {

@Test
public void testGetFileOwnerGroupPermsMsgs() throws IOException {
final File fileToLog = new File("test/dirWithFileToDelete/fileToDelete.txt");
final File fileToLog = new File(TEST_DIR, "dirWithFileToDelete/fileToDelete.txt");
fileToLog.getParentFile().mkdir();
fileToLog.createNewFile();

Expand All @@ -69,7 +95,7 @@ public void testGetFileOwnerGroupPermsMsgs() throws IOException {

@Test
public void testDeleteDirPersistently() throws IOException, InterruptedException {
final File fileToDelete = new File("test/dirWithFileToDelete/fileToDelete.txt");
final File fileToDelete = new File(TEST_DIR, "dirWithFileToDelete/fileToDelete.txt");
fileToDelete.getParentFile().mkdir();
fileToDelete.createNewFile();

Expand All @@ -87,4 +113,98 @@ public void testCreateTempDirectory() throws IOException {
assertEquals(0, tempDir.listFiles().length);
tempDir.deleteOnExit();
}

@Test
public void testPruneProblematicSymLinksRecursively_danglingLink() throws IOException {
File testDir = new File(TEST_DIR, "symlinks");
testDir.mkdirs();
File danglingLink = new File(testDir, "danglingLink");
Files.createSymbolicLink(danglingLink.toPath(), new File("nonexistent").toPath());

fileOperations.pruneProblematicSymLinksRecursively(testDir);

assertFalse(danglingLink.exists());
}

@Test
public void testPruneProblematicSymLinksRecursively_circularLink() throws IOException {
File testDir = new File(TEST_DIR, "symlinks");
testDir.mkdirs();
File circularLink = new File(testDir, "circularLink");
Files.createSymbolicLink(circularLink.toPath(), Path.of(testDir.getAbsolutePath()));

fileOperations.pruneProblematicSymLinksRecursively(testDir);

assertFalse(circularLink.exists());
}

@Test
public void testPruneProblematicSymLinksRecursively_validLink() throws IOException {
File testDir = new File(TEST_DIR, "symlinks");
testDir.mkdirs();
File targetFile = new File(testDir, "targetFile");
targetFile.createNewFile();
File validLink = new File(testDir, "validLink");
Files.createSymbolicLink(validLink.toPath(), Path.of("targetFile"));

assertTrue(targetFile.exists());
assertTrue(validLink.exists());
}

@Test
public void testPruneProblematicSymLinksRecursively_nestedDirectories() throws IOException {
File testDir = new File(TEST_DIR, "symlinks/nested");
testDir.mkdirs();

File normalFile = new File(testDir, "normalFile");
normalFile.createNewFile();

File danglingLink = new File(testDir, "danglingLink");
Files.createSymbolicLink(danglingLink.toPath(), new File("nonexistent").toPath());

fileOperations.pruneProblematicSymLinksRecursively(new File(TEST_DIR, "symlinks"));

assertFalse(danglingLink.exists());
assertTrue(normalFile.exists());
}

@Test
public void testPruneProblematicSymLinksRecursively_symlinkToDir_absolutePath() throws IOException {
File testDir = new File(TEST_DIR, "symlinks/nested");
testDir.mkdirs();

File normalFile = new File(testDir, "normalFile");
normalFile.createNewFile();

File linkToDir = new File(testDir.getParent(), "linkToDir");
Files.createSymbolicLink(linkToDir.toPath(), Path.of(testDir.getAbsolutePath()));

assertTrue(linkToDir.exists());

fileOperations.pruneProblematicSymLinksRecursively(new File(TEST_DIR, "symlinks"));

assertFalse(linkToDir.exists());
assertTrue(testDir.exists());
assertTrue(normalFile.exists());
}

@Test
public void testPruneProblematicSymLinksRecursively_symlinkToDir_relativePath() throws IOException {
File testDir = new File(TEST_DIR, "symlinks/nested");
testDir.mkdirs();

File normalFile = new File(testDir, "normalFile");
normalFile.createNewFile();

File linkToDir = new File(testDir.getParent(), "linkToDir");
Files.createSymbolicLink(linkToDir.toPath(), Path.of("nested"));

assertTrue(linkToDir.exists());

fileOperations.pruneProblematicSymLinksRecursively(new File(TEST_DIR, "symlinks"));

assertFalse(linkToDir.exists());
assertTrue(testDir.exists());
assertTrue(normalFile.exists());
}
}