mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-02 19:45:51 +01:00
Bugfix for long filenames in tar (#1552)
Fixes errors with long file names in tar archives. This may arise with hg repositories with deep directories.
This commit is contained in:
@@ -98,6 +98,9 @@ dependencies {
|
||||
annotationProcessor libraries.sspProcessor
|
||||
testImplementation libraries.shiroUnit
|
||||
|
||||
// compression
|
||||
implementation libraries.commonsCompress
|
||||
|
||||
// tests
|
||||
testImplementation libraries.junitJupiterApi
|
||||
testImplementation libraries.junitJupiterParams
|
||||
|
||||
218
scm-core/src/main/java/sonia/scm/util/Archives.java
Normal file
218
scm-core/src/main/java/sonia/scm/util/Archives.java
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.util;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class Archives {
|
||||
|
||||
private Archives() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tar output stream that is backed by the given output stream.
|
||||
* @param dest The stream the tar should be written to.
|
||||
*/
|
||||
public static TarArchiveOutputStream createTarOutputStream(OutputStream dest) {
|
||||
BufferedOutputStream bos = new BufferedOutputStream(dest);
|
||||
TarArchiveOutputStream tarArchiveOutputStream = new TarArchiveOutputStream(bos);
|
||||
tarArchiveOutputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
|
||||
tarArchiveOutputStream.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
|
||||
return tarArchiveOutputStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tar input stream that takes its bytes from the given input stream.
|
||||
* @param source The stream the tar should be extracted from.
|
||||
*/
|
||||
public static TarArchiveInputStream createTarInputStream(InputStream source) {
|
||||
return new TarArchiveInputStream(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare bundling a complete {@link Path} in a tar. Aside from the path whose files shall be added to the tar,
|
||||
* it is possible to specify a base path for the tar archive ({@link TarWriter#withBasePath(String)}) and to
|
||||
* specify a filter to select what files shall be added ({@link TarWriter#withFilter(Predicate)}). To start
|
||||
* the process, {@link TarWriter#run()} has to be called.
|
||||
* <br>
|
||||
* Example:
|
||||
* <pre>
|
||||
* Archives.addPathToTar(Paths.get("some/dir"), Files.newOutputStream("my.tar"))
|
||||
* .filter(path -> !path.toString().endsWith("~"))
|
||||
* .run();
|
||||
* </pre>
|
||||
* @param path The path containing the files to be added to the tar.
|
||||
* @param outputStream The output stream the tar shall be written to.
|
||||
* @return Instance of {@link TarWriter}.
|
||||
*/
|
||||
public static TarWriter addPathToTar(Path path, OutputStream outputStream) {
|
||||
return new TarWriter(path, outputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts files from a input stream with a tar.
|
||||
* <br>
|
||||
* Example:
|
||||
* <pre>
|
||||
* Archives.extractTar(Files.newOutputStream("my.tar"), Paths.get("target/dir"))
|
||||
* .run();
|
||||
* </pre>
|
||||
* @param inputStream The input stream providing the tar stream.
|
||||
* @param targetPath The target path to write the files to.
|
||||
* @return Instance of {@link TarExtractor}.
|
||||
*/
|
||||
public static TarExtractor extractTar(InputStream inputStream, Path targetPath) {
|
||||
return new TarExtractor(inputStream, targetPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class used in {@link #addPathToTar(Path, OutputStream)}.
|
||||
*/
|
||||
public static class TarWriter {
|
||||
private final Path path;
|
||||
private final OutputStream outputStream;
|
||||
|
||||
private String basePath = "";
|
||||
private Predicate<Path> filter = path -> true;
|
||||
|
||||
TarWriter(Path path, OutputStream outputStream) {
|
||||
this.path = path;
|
||||
this.outputStream = outputStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a base path that will be prepended to every tar entry in the archive.
|
||||
*/
|
||||
public TarWriter withBasePath(String basePath) {
|
||||
this.basePath = basePath;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a filter to select which files shall be added to the archive.
|
||||
*/
|
||||
public TarWriter withFilter(Predicate<Path> filter) {
|
||||
this.filter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the process.
|
||||
*/
|
||||
public void run() throws IOException {
|
||||
try (TarArchiveOutputStream tarArchiveOutputStream = Archives.createTarOutputStream(outputStream)) {
|
||||
createTarEntryForFiles(basePath, path, tarArchiveOutputStream);
|
||||
tarArchiveOutputStream.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void createTarEntryForFiles(String path, Path fileOrDir, TarArchiveOutputStream taos) throws IOException {
|
||||
try (Stream<Path> files = Files.list(fileOrDir)) {
|
||||
if (files != null) {
|
||||
files
|
||||
.filter(filter)
|
||||
.forEach(f -> bundleFileOrDir(path, f, taos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("java:S1075") // We use / here because we build tar entries, not files
|
||||
private void bundleFileOrDir(String path, Path fileOrDir, TarArchiveOutputStream taos) {
|
||||
try {
|
||||
String filePath = path + "/" + fileOrDir.getFileName().toString();
|
||||
if (Files.isDirectory(fileOrDir)) {
|
||||
createTarEntryForFiles(filePath, fileOrDir, taos);
|
||||
} else {
|
||||
createArchiveEntryForFile(filePath, fileOrDir, taos);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ArchiveException(
|
||||
"Could not add file '" + fileOrDir + "' to tar archive as '" + path + "'",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void createArchiveEntryForFile(String filePath, Path path, TarArchiveOutputStream taos) throws IOException {
|
||||
TarArchiveEntry entry = new TarArchiveEntry(filePath);
|
||||
entry.setSize(path.toFile().length());
|
||||
taos.putArchiveEntry(entry);
|
||||
Files.copy(path, taos);
|
||||
taos.closeArchiveEntry();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classed used in {@link #extractTar(InputStream, Path)}.
|
||||
*/
|
||||
public static class TarExtractor {
|
||||
private final InputStream inputStream;
|
||||
private final Path targetPath;
|
||||
|
||||
TarExtractor(InputStream inputStream, Path targetPath) {
|
||||
this.inputStream = inputStream;
|
||||
this.targetPath = targetPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the process.
|
||||
*/
|
||||
public void run() throws IOException {
|
||||
try (TarArchiveInputStream tais = createTarInputStream(inputStream)) {
|
||||
TarArchiveEntry entry;
|
||||
while ((entry = tais.getNextTarEntry()) != null) {
|
||||
Path filePath = targetPath.resolve(entry.getName());
|
||||
createDirectoriesIfNestedFile(filePath);
|
||||
Files.copy(tais, filePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createDirectoriesIfNestedFile(Path filePath) throws IOException {
|
||||
Path directory = filePath.getParent();
|
||||
if (!Files.exists(directory)) {
|
||||
Files.createDirectories(directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ArchiveException extends RuntimeException {
|
||||
public ArchiveException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
scm-core/src/test/java/sonia/scm/util/ArchivesTest.java
Normal file
67
scm-core/src/test/java/sonia/scm/util/ArchivesTest.java
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package sonia.scm.util;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static java.util.Collections.singleton;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static sonia.scm.util.Archives.addPathToTar;
|
||||
import static sonia.scm.util.Archives.extractTar;
|
||||
import static sonia.scm.util.Archives.createTarOutputStream;
|
||||
|
||||
class ArchivesTest {
|
||||
|
||||
@Test
|
||||
void writeAndReadTar(@TempDir Path temp) throws IOException {
|
||||
Path source = temp.resolve("source");
|
||||
Files.createDirectories(source);
|
||||
|
||||
Path file = source.resolve("file");
|
||||
Path fileInDirectory = source.resolve("some").resolve("very").resolve("deep").resolve("nested").resolve("directory").resolve("with").resolve("file");
|
||||
Files.createDirectories(fileInDirectory.getParent());
|
||||
|
||||
Files.write(file, singleton("content"));
|
||||
Files.write(fileInDirectory, singleton("content in directory"));
|
||||
|
||||
Path tarFile = temp.resolve("test.tar");
|
||||
try (OutputStream outputStream = Files.newOutputStream(tarFile)) {
|
||||
addPathToTar(source, outputStream).run();
|
||||
}
|
||||
|
||||
Path target = temp.resolve("target");
|
||||
Files.createDirectories(target);
|
||||
extractTar(Files.newInputStream(tarFile), target).run();
|
||||
|
||||
assertThat(target.resolve("file")).hasContent("content");
|
||||
assertThat(target.resolve("some").resolve("very").resolve("deep").resolve("nested").resolve("directory").resolve("with").resolve("file")).hasContent("content in directory");
|
||||
}
|
||||
}
|
||||
@@ -23,18 +23,16 @@
|
||||
*/
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import sonia.scm.ContextEntry;
|
||||
import sonia.scm.repository.api.BundleResponse;
|
||||
import sonia.scm.repository.api.ExportFailedException;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static sonia.scm.util.Archives.addPathToTar;
|
||||
|
||||
public class GitBundleCommand extends AbstractGitCommand implements BundleCommand {
|
||||
|
||||
@@ -48,12 +46,8 @@ public class GitBundleCommand extends AbstractGitCommand implements BundleComman
|
||||
public BundleResponse bundle(BundleCommandRequest request) throws IOException {
|
||||
Path repoDir = context.getDirectory().toPath();
|
||||
if (Files.exists(repoDir)) {
|
||||
try (OutputStream os = request.getArchive().openStream();
|
||||
BufferedOutputStream bos = new BufferedOutputStream(os);
|
||||
TarArchiveOutputStream taos = new TarArchiveOutputStream(bos)) {
|
||||
|
||||
createTarEntryForFiles("", repoDir, taos);
|
||||
taos.finish();
|
||||
try (OutputStream os = request.getArchive().openStream()) {
|
||||
addPathToTar(repoDir, os).withFilter(this::shouldIncludeFile).run();
|
||||
}
|
||||
} else {
|
||||
throw new ExportFailedException(
|
||||
@@ -69,42 +63,7 @@ public class GitBundleCommand extends AbstractGitCommand implements BundleComman
|
||||
return TAR_ARCHIVE;
|
||||
}
|
||||
|
||||
private void createTarEntryForFiles(String path, Path fileOrDir, TarArchiveOutputStream taos) throws IOException {
|
||||
try (Stream<Path> files = Files.list(fileOrDir)) {
|
||||
if (files != null) {
|
||||
files
|
||||
.filter(this::shouldIncludeFile)
|
||||
.forEach(f -> bundleFileOrDir(path, f, taos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void bundleFileOrDir(String path, Path fileOrDir, TarArchiveOutputStream taos) {
|
||||
try {
|
||||
String filePath = path + "/" + fileOrDir.getFileName().toString();
|
||||
if (Files.isDirectory(fileOrDir)) {
|
||||
createTarEntryForFiles(filePath, fileOrDir, taos);
|
||||
} else {
|
||||
createArchiveEntryForFile(filePath, fileOrDir, taos);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ExportFailedException(
|
||||
ContextEntry.ContextBuilder.noContext(),
|
||||
"Could not export repository. Error on bundling files.",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldIncludeFile(Path filePath) {
|
||||
return !filePath.getFileName().toString().equals("config");
|
||||
}
|
||||
|
||||
private void createArchiveEntryForFile(String filePath, Path path, TarArchiveOutputStream taos) throws IOException {
|
||||
TarArchiveEntry entry = new TarArchiveEntry(filePath);
|
||||
entry.setSize(path.toFile().length());
|
||||
taos.putArchiveEntry(entry);
|
||||
Files.copy(path, taos);
|
||||
taos.closeArchiveEntry();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import com.google.common.io.ByteSource;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.api.UnbundleResponse;
|
||||
@@ -33,10 +31,9 @@ import sonia.scm.repository.api.UnbundleResponse;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static sonia.scm.util.Archives.extractTar;
|
||||
|
||||
public class GitUnbundleCommand extends AbstractGitCommand implements UnbundleCommand {
|
||||
|
||||
@@ -61,20 +58,6 @@ public class GitUnbundleCommand extends AbstractGitCommand implements UnbundleCo
|
||||
}
|
||||
|
||||
private void unbundleRepositoryFromRequest(UnbundleCommandRequest request, Path repositoryDir) throws IOException {
|
||||
try (TarArchiveInputStream tais = new TarArchiveInputStream(request.getArchive().openBufferedStream())) {
|
||||
TarArchiveEntry entry;
|
||||
while ((entry = tais.getNextTarEntry()) != null) {
|
||||
Path filePath = repositoryDir.resolve(entry.getName());
|
||||
createDirectoriesIfNestedFile(filePath);
|
||||
Files.copy(tais, filePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createDirectoriesIfNestedFile(Path filePath) throws IOException {
|
||||
Path directory = filePath.getParent();
|
||||
if (!Files.exists(directory)) {
|
||||
Files.createDirectories(directory);
|
||||
}
|
||||
extractTar(request.getArchive().openBufferedStream(), repositoryDir).run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import java.nio.file.StandardCopyOption;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static sonia.scm.util.Archives.createTarInputStream;
|
||||
|
||||
class GitBundleCommandTest {
|
||||
|
||||
@@ -94,7 +95,7 @@ class GitBundleCommandTest {
|
||||
}
|
||||
|
||||
private void assertStreamContainsContent(ByteArrayOutputStream baos, String content) throws IOException {
|
||||
TarArchiveInputStream tais = new TarArchiveInputStream(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())));
|
||||
TarArchiveInputStream tais = createTarInputStream(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())));
|
||||
tais.getNextEntry();
|
||||
|
||||
byte[] result = IOUtils.toByteArray(tais);
|
||||
|
||||
@@ -30,8 +30,8 @@ import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import sonia.scm.util.Archives;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -82,9 +82,10 @@ class GitUnbundleCommandTest extends AbstractGitCommandTestBase {
|
||||
|
||||
private UnbundleCommandRequest createUnbundleCommandRequestForFile(Path temp, String filePath, String fileContent) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
TarArchiveOutputStream taos = new TarArchiveOutputStream(baos);
|
||||
TarArchiveOutputStream taos = Archives.createTarOutputStream(baos);
|
||||
addEntry(taos, filePath, fileContent);
|
||||
taos.finish();
|
||||
taos.close();
|
||||
|
||||
when(gitContext.getDirectory()).thenReturn(temp.toFile());
|
||||
ByteSource byteSource = ByteSource.wrap(baos.toByteArray());
|
||||
|
||||
@@ -24,18 +24,16 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import sonia.scm.ContextEntry;
|
||||
import sonia.scm.repository.api.BundleResponse;
|
||||
import sonia.scm.repository.api.ExportFailedException;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static sonia.scm.util.Archives.addPathToTar;
|
||||
|
||||
public class HgBundleCommand implements BundleCommand {
|
||||
|
||||
@@ -51,12 +49,8 @@ public class HgBundleCommand implements BundleCommand {
|
||||
public BundleResponse bundle(BundleCommandRequest request) throws IOException {
|
||||
Path repoDir = context.getDirectory().toPath();
|
||||
if (Files.exists(repoDir)) {
|
||||
try (OutputStream os = request.getArchive().openStream();
|
||||
BufferedOutputStream bos = new BufferedOutputStream(os);
|
||||
TarArchiveOutputStream taos = new TarArchiveOutputStream(bos)) {
|
||||
|
||||
createTarEntryForFiles("", repoDir, taos);
|
||||
taos.finish();
|
||||
try (OutputStream os = request.getArchive().openStream()) {
|
||||
addPathToTar(repoDir, os).run();
|
||||
}
|
||||
} else {
|
||||
throw new ExportFailedException(
|
||||
@@ -71,37 +65,4 @@ public class HgBundleCommand implements BundleCommand {
|
||||
public String getFileExtension() {
|
||||
return TAR_ARCHIVE;
|
||||
}
|
||||
|
||||
private void createTarEntryForFiles(String path, Path fileOrDir, TarArchiveOutputStream taos) throws IOException {
|
||||
try (Stream<Path> files = Files.list(fileOrDir)) {
|
||||
if (files != null) {
|
||||
files.forEach(f -> bundleFileOrDir(path, f, taos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void bundleFileOrDir(String path, Path fileOrDir, TarArchiveOutputStream taos) {
|
||||
try {
|
||||
String filePath = path + "/" + fileOrDir.getFileName().toString();
|
||||
if (Files.isDirectory(fileOrDir)) {
|
||||
createTarEntryForFiles(filePath, fileOrDir, taos);
|
||||
} else {
|
||||
createArchiveEntryForFile(filePath, fileOrDir, taos);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ExportFailedException(
|
||||
ContextEntry.ContextBuilder.noContext(),
|
||||
"Could not export repository. Error on bundling files.",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void createArchiveEntryForFile(String filePath, Path path, TarArchiveOutputStream taos) throws IOException {
|
||||
TarArchiveEntry entry = new TarArchiveEntry(filePath);
|
||||
entry.setSize(path.toFile().length());
|
||||
taos.putArchiveEntry(entry);
|
||||
Files.copy(path, taos);
|
||||
taos.closeArchiveEntry();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import com.google.common.io.ByteSource;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.api.UnbundleResponse;
|
||||
@@ -34,9 +32,9 @@ import sonia.scm.repository.api.UnbundleResponse;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static sonia.scm.util.Archives.extractTar;
|
||||
|
||||
|
||||
public class HgUnbundleCommand implements UnbundleCommand {
|
||||
@@ -63,20 +61,6 @@ public class HgUnbundleCommand implements UnbundleCommand {
|
||||
}
|
||||
|
||||
private void unbundleRepositoryFromRequest(UnbundleCommandRequest request, Path repositoryDir) throws IOException {
|
||||
try (TarArchiveInputStream tais = new TarArchiveInputStream(request.getArchive().openBufferedStream())) {
|
||||
TarArchiveEntry entry;
|
||||
while ((entry = tais.getNextTarEntry()) != null) {
|
||||
Path filePath = repositoryDir.resolve(entry.getName());
|
||||
createDirectoriesIfNestedFile(filePath);
|
||||
Files.copy(tais, filePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createDirectoriesIfNestedFile(Path filePath) throws IOException {
|
||||
Path directory = filePath.getParent();
|
||||
if (!Files.exists(directory)) {
|
||||
Files.createDirectories(directory);
|
||||
}
|
||||
extractTar(request.getArchive().openBufferedStream(), repositoryDir).run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import java.nio.file.StandardCopyOption;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static sonia.scm.util.Archives.createTarInputStream;
|
||||
|
||||
class HgBundleCommandTest {
|
||||
|
||||
@@ -95,7 +96,7 @@ class HgBundleCommandTest {
|
||||
}
|
||||
|
||||
private void assertStreamContainsContent(ByteArrayOutputStream baos, String content) throws IOException {
|
||||
TarArchiveInputStream tais = new TarArchiveInputStream(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())));
|
||||
TarArchiveInputStream tais = createTarInputStream(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())));
|
||||
tais.getNextEntry();
|
||||
|
||||
byte[] result = IOUtils.toByteArray(tais);
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import sonia.scm.util.Archives;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
@@ -82,9 +83,10 @@ class HgUnbundleCommandTest {
|
||||
|
||||
private UnbundleCommandRequest createUnbundleCommandRequestForFile(Path temp, String filePath, String fileContent) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
TarArchiveOutputStream taos = new TarArchiveOutputStream(baos);
|
||||
TarArchiveOutputStream taos = Archives.createTarOutputStream(baos);
|
||||
addEntry(taos, filePath, fileContent);
|
||||
taos.finish();
|
||||
taos.close();
|
||||
|
||||
when(hgContext.getDirectory()).thenReturn(temp.toFile());
|
||||
ByteSource byteSource = ByteSource.wrap(baos.toByteArray());
|
||||
|
||||
@@ -34,6 +34,7 @@ import sonia.scm.repository.api.ExportFailedException;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
import sonia.scm.repository.work.WorkdirProvider;
|
||||
import sonia.scm.util.Archives;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -84,7 +85,7 @@ public class FullScmRepositoryExporter {
|
||||
RepositoryService service = serviceFactory.create(repository);
|
||||
BufferedOutputStream bos = new BufferedOutputStream(outputStream);
|
||||
GzipCompressorOutputStream gzos = new GzipCompressorOutputStream(bos);
|
||||
TarArchiveOutputStream taos = new TarArchiveOutputStream(gzos)
|
||||
TarArchiveOutputStream taos = Archives.createTarOutputStream(gzos)
|
||||
) {
|
||||
writeEnvironmentData(taos);
|
||||
writeMetadata(repository, taos);
|
||||
|
||||
@@ -40,6 +40,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static sonia.scm.util.Archives.createTarInputStream;
|
||||
|
||||
public class FullScmRepositoryImporter {
|
||||
|
||||
@@ -65,7 +66,7 @@ public class FullScmRepositoryImporter {
|
||||
try (
|
||||
BufferedInputStream bif = new BufferedInputStream(inputStream);
|
||||
GzipCompressorInputStream gcis = new GzipCompressorInputStream(bif);
|
||||
TarArchiveInputStream tais = new TarArchiveInputStream(gcis)
|
||||
TarArchiveInputStream tais = createTarInputStream(gcis)
|
||||
) {
|
||||
return run(repository, tais);
|
||||
}
|
||||
|
||||
@@ -35,9 +35,9 @@ import sonia.scm.store.ExportableStore;
|
||||
import sonia.scm.store.StoreEntryMetaData;
|
||||
import sonia.scm.store.StoreExporter;
|
||||
import sonia.scm.store.StoreType;
|
||||
import sonia.scm.util.Archives;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@@ -58,8 +58,7 @@ public class TarArchiveRepositoryStoreExporter {
|
||||
|
||||
public void export(Repository repository, OutputStream output) {
|
||||
try (
|
||||
BufferedOutputStream bos = new BufferedOutputStream(output);
|
||||
final TarArchiveOutputStream taos = new TarArchiveOutputStream(bos)
|
||||
final TarArchiveOutputStream taos = Archives.createTarOutputStream(output)
|
||||
) {
|
||||
List<ExportableStore> exportableStores = storeExporter.listExportableStores(repository);
|
||||
for (ExportableStore store : exportableStores) {
|
||||
|
||||
Reference in New Issue
Block a user