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:
René Pfeuffer
2021-02-18 16:30:35 +01:00
committed by GitHub
parent d8427ed4ed
commit d0df8977ef
14 changed files with 316 additions and 135 deletions

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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());