Skip to content

Commit

Permalink
[GAL-364] Handle copying files into a folders that are symlinks or which
Browse files Browse the repository at this point in the history
parents are symlinks
  • Loading branch information
msfm authored and spyrkob committed Sep 4, 2024
1 parent e80cb19 commit c61489f
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 4 deletions.
27 changes: 23 additions & 4 deletions common-api/src/main/java/org/jboss/galleon/util/IoUtils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 Red Hat, Inc. and/or its affiliates
* Copyright 2016-2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -151,11 +151,30 @@ public static void copy(Path source, Path target) throws IOException {
copy(source, target, false);
}

private static Path replaceSymLinkParent(Path originalPath) throws IOException {
Path path = originalPath;
while (path != null && !Files.exists(path)) {
path = path.getParent();
}

if (path == null || !Files.isSymbolicLink(path)) {
// either we couldn't find an existing parent, assuming it's a real path
// or it's not a symbolic link and we can use the original path
return originalPath;
} else {
// we need to rebuild the path on top of the existing path
Path relative = path.relativize(originalPath);
return path.toRealPath().resolve(relative);
}
}

public static void copy(Path source, Path target, boolean skipExistingFiles) throws IOException {
if(Files.isDirectory(source)) {
Files.createDirectories(target);
// Files.createDirectories throws FileAlreadyExistsException if the folder being created (or it's parent) is
// a symlink to a directory. To avoid that, replace the symlink with a real path
if (Files.isDirectory(source)) {
Files.createDirectories(replaceSymLinkParent(target));
} else {
Files.createDirectories(target.getParent());
Files.createDirectories(replaceSymLinkParent(target.getParent()));
}
Files.walkFileTree(source, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
new SimpleFileVisitor<Path>() {
Expand Down
76 changes: 76 additions & 0 deletions common-api/src/test/java/org/jboss/galleon/util/IoUtilsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2016-2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.galleon.util;

import java.nio.file.Files;
import java.nio.file.Path;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

public class IoUtilsTest {

@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Test
public void copySymlinkDirectory() throws Exception {
final Path source = temporaryFolder.newFolder("source").toPath();
final Path targetParent = temporaryFolder.newFolder("target-parent").toPath();

final Path sourceFile = source.resolve("test.txt");
final Path target = targetParent.resolve("target");
Files.createDirectory(target);
final Path link = Files.createSymbolicLink(targetParent.resolve("link"), target.toAbsolutePath());

Files.writeString(sourceFile, "test text");

IoUtils.copy(source, link);
}

@Test
public void copyFileIntoSymlinkDirectory() throws Exception {
final Path source = temporaryFolder.newFolder("source").toPath();
final Path targetParent = temporaryFolder.newFolder("target-parent").toPath();

final Path sourceFile = source.resolve("test.txt");
final Path target = targetParent.resolve("target");
Files.createDirectory(target);
final Path link = Files.createSymbolicLink(targetParent.resolve("link"), target.toAbsolutePath());

Files.writeString(sourceFile, "test text");

IoUtils.copy(sourceFile, link.resolve("test.txt"));
}

@Test
public void copySubFolderIntoSymlinkDirectory() throws Exception {
final Path source = temporaryFolder.newFolder("source").toPath();
final Path targetParent = temporaryFolder.newFolder("target-parent").toPath();

final Path sourceFile = source.resolve("sub").resolve("test.txt");
final Path target = targetParent.resolve("target");
Files.createDirectory(target);
final Path link = Files.createSymbolicLink(targetParent.resolve("link"), target.toAbsolutePath());

Files.createDirectory(source.resolve("sub"));
Files.writeString(sourceFile, "test text");

IoUtils.copy(sourceFile, link.resolve("sub").resolve("test.txt"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2016-2024 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.galleon.test;

import java.nio.file.Files;

import org.jboss.galleon.ProvisioningDescriptionException;
import org.jboss.galleon.ProvisioningException;
import org.jboss.galleon.ProvisioningManager;
import org.jboss.galleon.config.FeaturePackConfig;
import org.jboss.galleon.config.ProvisioningConfig;
import org.jboss.galleon.creator.FeaturePackCreator;
import org.jboss.galleon.state.ProvisionedFeaturePack;
import org.jboss.galleon.state.ProvisionedPackage;
import org.jboss.galleon.state.ProvisionedState;
import org.jboss.galleon.test.util.fs.state.DirState;
import org.jboss.galleon.universe.FeaturePackLocation;
import org.jboss.galleon.universe.galleon1.LegacyGalleon1Universe;
import org.junit.Before;

public class InstallIntoSymLinkTestCase extends PmTestBase {

private static final FeaturePackLocation.FPID FP1_100_GAV = LegacyGalleon1Universe.newFPID("org.jboss.pm.test:fp1", "1", "1.0.0.Final");

@Before
public void before() throws Exception {
super.before();
// change the installHome to be a symbolic link
installHome = Files.createSymbolicLink(workDir.resolve("link"), installHome);
}

@Override
protected void createFeaturePacks(FeaturePackCreator creator) throws ProvisioningException {
creator.newFeaturePack(FP1_100_GAV)
.newPackage("p1", true)
.writeContent("fp1/p1.txt", "fp1 1.0.0.Final p1");

}

@Override
protected void testPm(ProvisioningManager pm) throws ProvisioningException {
pm.install(FP1_100_GAV.getLocation());
}

@Override
protected ProvisioningConfig provisionedConfig() throws ProvisioningException {
return ProvisioningConfig.builder()
.addFeaturePackDep(FeaturePackConfig.builder(FP1_100_GAV.getLocation())
.build())
.build();
}

@Override
protected ProvisionedState provisionedState() throws ProvisioningDescriptionException {
return ProvisionedState.builder()
.addFeaturePack(ProvisionedFeaturePack.builder(FP1_100_GAV)
.addPackage(ProvisionedPackage.newInstance("p1"))
.build())
.build();
}

@Override
protected DirState provisionedHomeDir() {
return newDirBuilder()
.addFile("fp1/p1.txt", "fp1 1.0.0.Final p1")
.build();
}
}

0 comments on commit c61489f

Please sign in to comment.