Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.stream.Stream;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
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 org.apache.commons.compress.archivers.tar.TarConstants;
import org.apache.commons.io.IOUtils;
import org.apache.hadoop.hdds.HddsUtils;
import org.apache.hadoop.ozone.OzoneConsts;
Expand Down Expand Up @@ -79,12 +81,42 @@ public static byte[] readEntry(InputStream input, final long size)
return output.toByteArray();
}

private static TarArchiveEntry createBasicTarArchiveEntry(File file, String entryName)
throws IOException {
final Path path = file.toPath();

final TarArchiveEntry entry;
if (Files.isDirectory(path)) {
final int nameLength = entryName.length();
final String dirName = nameLength == 0 || entryName.charAt(nameLength - 1) != '/'
? entryName + "/"
: entryName;
entry = new TarArchiveEntry(dirName, TarConstants.LF_DIR);
entry.setMode(TarArchiveEntry.DEFAULT_DIR_MODE);
} else {
entry = new TarArchiveEntry(entryName);
entry.setMode(TarArchiveEntry.DEFAULT_FILE_MODE);
entry.setSize(Files.size(path));
}

try {
BasicFileAttributes attrs = Files.readAttributes(
file.toPath(), BasicFileAttributes.class);
entry.setLastModifiedTime(attrs.lastModifiedTime());
entry.setLastAccessTime(attrs.lastAccessTime());
entry.setCreationTime(attrs.creationTime());
} catch (IOException e) {
entry.setModTime(file.lastModified()); // fallback
}
return entry;
}

public static void includePath(Path dir, String subdir,
ArchiveOutputStream<TarArchiveEntry> archiveOutput) throws IOException {

// Add a directory entry before adding files, in case the directory is
// empty.
TarArchiveEntry entry = archiveOutput.createArchiveEntry(dir.toFile(), subdir);
TarArchiveEntry entry = createBasicTarArchiveEntry(dir.toFile(), subdir);
archiveOutput.putArchiveEntry(entry);
archiveOutput.closeArchiveEntry();

Expand All @@ -105,7 +137,7 @@ public static void includePath(Path dir, String subdir,
public static long includeFile(File file, String entryName,
ArchiveOutputStream<TarArchiveEntry> archiveOutput) throws IOException {
final long bytes;
TarArchiveEntry entry = archiveOutput.createArchiveEntry(file, entryName);
TarArchiveEntry entry = createBasicTarArchiveEntry(file, entryName);
archiveOutput.putArchiveEntry(entry);
try (InputStream input = Files.newInputStream(file.toPath())) {
bytes = IOUtils.copy(input, archiveOutput, getBufferSize(file.length()));
Expand Down Expand Up @@ -138,7 +170,7 @@ public static long linkAndIncludeFile(File file, String entryName,
long bytes = 0;
try {
Files.createLink(link.toPath(), file.toPath());
TarArchiveEntry entry = archiveOutput.createArchiveEntry(link, entryName);
TarArchiveEntry entry = createBasicTarArchiveEntry(link, entryName);
archiveOutput.putArchiveEntry(entry);
try (InputStream input = Files.newInputStream(link.toPath())) {
bytes = IOUtils.copyLarge(input, archiveOutput);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import org.mockito.MockedStatic;

/** Test {@link Archiver}. */
Expand Down Expand Up @@ -76,22 +77,19 @@ void testLinkAndIncludeFileSuccessfulHardLink() throws IOException {
Files.write(tempFile.toPath(), "Test Content".getBytes(StandardCharsets.UTF_8));

TarArchiveOutputStream mockArchiveOutput = mock(TarArchiveOutputStream.class);
TarArchiveEntry mockEntry = new TarArchiveEntry(entryName);
AtomicBoolean isHardLinkCreated = new AtomicBoolean(false);
when(mockArchiveOutput.createArchiveEntry(any(File.class), eq(entryName)))
.thenAnswer(invocation -> {
File linkFile = invocation.getArgument(0);
isHardLinkCreated.set(Files.isSameFile(tempFile.toPath(), linkFile.toPath()));
return mockEntry;
});

// Call method under test
long bytesCopied = Archiver.linkAndIncludeFile(tempFile, entryName, mockArchiveOutput, tmpDir);
assertEquals(Files.size(tempFile.toPath()), bytesCopied);
// Verify archive interactions
verify(mockArchiveOutput, times(1)).putArchiveEntry(mockEntry);
ArgumentCaptor<TarArchiveEntry> entryCaptor = ArgumentCaptor.forClass(TarArchiveEntry.class);
verify(mockArchiveOutput, times(1)).putArchiveEntry(entryCaptor.capture());
verify(mockArchiveOutput, times(1)).closeArchiveEntry();
assertTrue(isHardLinkCreated.get());

TarArchiveEntry capturedEntry = entryCaptor.getValue();
assertEquals(entryName, capturedEntry.getName());
assertEquals(Files.size(tempFile.toPath()), capturedEntry.getSize());

assertFalse(Files.exists(tmpDir.resolve(entryName)));
// Cleanup
assertTrue(tempFile.delete());
Expand Down