tmf: Fix hang in TarInputStream.skipToEntry when importing large tar.gz

When importing a large tar.gz archive, it can hang in
TarInputStream.skipToEntry. Depending on the size of the entries in the
Tar and the order in which they are read, it is possible that bytesread
will overflow and become negative. Because of that, the code that skips
bytes fails to skip properly and enters in an infinite loop.

This patch fixes the issue by making the bytesread a 'long'. Because
this class is in the platform, several files had to be copied over in
order to get the fix right away. Those files will be removed once the
fix is in the platform and that the fix is released.

Bug: 501379
Change-Id: Ia1613da08715968f23a803519a2e78aa7c51482f
Signed-off-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Reviewed-on: https://git.eclipse.org/r/81053
Reviewed-by: Hudson CI
Reviewed-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
Tested-by: Bernd Hufmann <bernd.hufmann@ericsson.com>
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ArchiveUtil.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ArchiveUtil.java
index a466205..2a247a6 100644
--- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ArchiveUtil.java
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ArchiveUtil.java
@@ -20,9 +20,6 @@
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.tracecompass.tmf.core.util.Pair;
 import org.eclipse.ui.internal.wizards.datatransfer.ArchiveFileManipulations;
-import org.eclipse.ui.internal.wizards.datatransfer.TarException;
-import org.eclipse.ui.internal.wizards.datatransfer.TarFile;
-import org.eclipse.ui.internal.wizards.datatransfer.TarLeveledStructureProvider;
 import org.eclipse.ui.internal.wizards.datatransfer.ZipLeveledStructureProvider;
 import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider;
 
@@ -113,18 +110,28 @@
         return ArchiveFileManipulations.closeZipFile(specifiedFile, shell);
     }
 
-    static boolean ensureTarSourceIsValid(String archivePath, Shell shell) {
+    static boolean ensureTarSourceIsValid(String archivePath) {
         TarFile specifiedFile = getSpecifiedTarSourceFile(archivePath);
         if (specifiedFile == null) {
             return false;
         }
-        return ArchiveFileManipulations.closeTarFile(specifiedFile, shell);
+        return closeTarFile(specifiedFile);
     }
 
     static boolean ensureGzipSourceIsValid(String archivePath) {
         return isGzipFile(archivePath);
     }
 
+    static boolean closeTarFile(TarFile file) {
+        try {
+            file.close();
+        } catch (IOException e) {
+            return false;
+        }
+
+        return true;
+    }
+
     /**
      * Get the root file system object and it's associated import provider for
      * the specified source file. A shell is used to display messages in case of
@@ -154,7 +161,7 @@
             FileSystemObjectLeveledImportStructureProvider leveledImportStructureProvider = null;
             String archivePath = sourceFile.getAbsolutePath();
             if (isTarFile(archivePath)) {
-                if (ensureTarSourceIsValid(archivePath, shell)) {
+                if (ensureTarSourceIsValid(archivePath)) {
                     // We close the file when we dispose the import provider,
                     // see disposeSelectionGroupRoot
                     TarFile tarFile = getSpecifiedTarSourceFile(archivePath);
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/FileSystemObjectImportStructureProvider.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/FileSystemObjectImportStructureProvider.java
index a14d75f..02ddad6 100644
--- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/FileSystemObjectImportStructureProvider.java
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/FileSystemObjectImportStructureProvider.java
@@ -19,14 +19,12 @@
 import java.util.List;
 import java.util.zip.ZipEntry;
 
-import org.eclipse.ui.internal.wizards.datatransfer.TarEntry;
 import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
 
 /**
  * An import provider that makes use of the IFileSystemObject abstraction
  * instead of using plain file system objects (File, TarEntry, ZipEntry, etc)
  */
-@SuppressWarnings("restriction")
 public class FileSystemObjectImportStructureProvider implements IImportStructureProvider {
 
     private IImportStructureProvider fImportProvider;
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ImportTraceWizardPage.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ImportTraceWizardPage.java
index 44bd425..9ee72e6 100644
--- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ImportTraceWizardPage.java
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/ImportTraceWizardPage.java
@@ -943,7 +943,7 @@
             return false;
         }
 
-        if (!isImportFromDirectory() && !ArchiveUtil.ensureTarSourceIsValid(source.getAbsolutePath(), getContainer().getShell()) && !ArchiveUtil.ensureZipSourceIsValid(source.getAbsolutePath(), getContainer().getShell())
+        if (!isImportFromDirectory() && !ArchiveUtil.ensureTarSourceIsValid(source.getAbsolutePath()) && !ArchiveUtil.ensureZipSourceIsValid(source.getAbsolutePath(), getContainer().getShell())
                 && !ArchiveUtil.ensureGzipSourceIsValid(source.getAbsolutePath())) {
             setMessage(null);
             setErrorMessage(Messages.ImportTraceWizard_BadArchiveFormat);
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarEntry.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarEntry.java
new file mode 100644
index 0000000..7fb5a58
--- /dev/null
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarEntry.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2015 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Marc-Andre Laperle <marc-andre.laperle@ericsson.com> - Copied to Trace Compass to work around bug 501379
+ *******************************************************************************/
+package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace;
+
+/**
+ * Representation of a file in a tar archive.
+ */
+public class TarEntry implements Cloneable
+{
+	private String name;
+	private long mode, time, size;
+	private int type;
+	int filepos;
+
+	/**
+	 * Entry type for normal files.
+	 */
+	public static final int FILE = '0';
+
+	/**
+	 * Entry type for directories.
+	 */
+	public static final int DIRECTORY = '5';
+
+	/**
+	 * Create a new TarEntry for a file of the given name at the
+	 * given position in the file.
+	 *
+	 * @param name filename
+	 * @param pos position in the file in bytes
+	 */
+	TarEntry(String name, int pos) {
+		this.name = name;
+		mode = 0644;
+		type = FILE;
+		filepos = pos;
+		time = System.currentTimeMillis() / 1000;
+	}
+
+	/**
+	 * Create a new TarEntry for a file of the given name.
+	 *
+	 * @param name filename
+	 */
+	public TarEntry(String name) {
+		this(name, -1);
+	}
+
+	/**
+	 * Returns the type of this file, one of FILE, LINK, SYM_LINK,
+	 * CHAR_DEVICE, BLOCK_DEVICE, DIRECTORY or FIFO.
+	 *
+	 * @return file type
+	 */
+	public int getFileType() {
+		return type;
+	}
+
+	/**
+	 * Returns the mode of the file in UNIX permissions format.
+	 *
+	 * @return file mode
+	 */
+	public long getMode() {
+		return mode;
+	}
+
+	/**
+	 * Returns the name of the file.
+	 *
+	 * @return filename
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * Returns the size of the file in bytes.
+	 *
+	 * @return filesize
+	 */
+	public long getSize() {
+		return size;
+	}
+
+	/**
+	 * Returns the modification time of the file in seconds since January
+	 * 1st 1970.
+	 *
+	 * @return time
+	 */
+	public long getTime() {
+		return time;
+	}
+
+	/**
+	 * Sets the type of the file, one of FILE, LINK, SYMLINK, CHAR_DEVICE,
+	 * BLOCK_DEVICE, or DIRECTORY.
+	 *
+	 * @param type the file type
+	 */
+	public void setFileType(int type) {
+		this.type = type;
+	}
+
+	/**
+	 * Sets the mode of the file in UNIX permissions format.
+	 *
+	 * @param mode the mode
+	 */
+	public void setMode(long mode) {
+		this.mode = mode;
+	}
+
+	/**
+	 * Sets the size of the file in bytes.
+	 *
+	 * @param size the file size
+	 */
+	public void setSize(long size) {
+		this.size = size;
+	}
+
+	/**
+	 * Sets the modification time of the file in seconds since January
+	 * 1st 1970.
+	 *
+	 * @param time the modification time
+	 */
+	public void setTime(long time) {
+		this.time = time;
+	}
+}
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarException.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarException.java
new file mode 100644
index 0000000..f67d409
--- /dev/null
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarException.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2015 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Marc-Andre Laperle <marc-andre.laperle@ericsson.com> - Copied to Trace Compass to work around bug 501379
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace;
+
+/**
+ * Exception generated upon encountering corrupted tar files.
+ */
+public class TarException extends Exception {
+	/**
+	 * Generated serial version UID for this class.
+	 */
+	private static final long serialVersionUID = 2886671254518853528L;
+
+    /**
+     * Constructs a TarException without a detail string.
+     */
+    public TarException() {
+    	super();
+    }
+
+	/**
+     * Constructs a TarException with the specified detail string.
+     *
+     * @param s the detail string
+     */
+    public TarException(String s) {
+    	super(s);
+    }
+
+    /**
+     * Constructs a TarException with the specified detail string.
+     *
+     * @param s the detail string
+     * @param cause the cause
+     */
+    public TarException(String s, Throwable cause) {
+    	super(s, cause);
+    }
+}
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFile.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFile.java
new file mode 100644
index 0000000..028473a
--- /dev/null
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFile.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2015 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Remy Chi Jian Suen <remy.suen@gmail.com> - Bug 243347 TarFile should not throw NPE in finalize()
+ * Marc-Andre Laperle <marc-andre.laperle@ericsson.com> - Bug 463633
+ * Marc-Andre Laperle <marc-andre.laperle@ericsson.com> - Copied to Trace Compass to work around bug 501379
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.zip.GZIPInputStream;
+
+
+/**
+ * Reads a .tar or .tar.gz archive file, providing an index enumeration
+ * and allows for accessing an InputStream for arbitrary files in the
+ * archive.
+ */
+public class TarFile {
+	private File file;
+	private TarInputStream entryEnumerationStream;
+	private TarEntry curEntry;
+	private TarInputStream entryStream;
+
+	private InputStream internalEntryStream;
+    // This field is just to prevent try with resources error and keep the code
+    // similar to the original
+    private InputStream fInputStream;
+
+	/**
+	 * Create a new TarFile for the given file.
+	 *
+	 * @param file the file
+	 * @throws TarException on Tar error (bad format, etc)
+	 * @throws IOException on i/o error
+	 */
+	public TarFile(File file) throws TarException, IOException {
+		this.file = file;
+
+		fInputStream = new FileInputStream(file);
+		// First, check if it's a GZIPInputStream.
+		try {
+			fInputStream = new GZIPInputStream(fInputStream);
+		} catch(IOException e) {
+			//If it is not compressed we close
+			//the old one and recreate
+			fInputStream.close();
+			fInputStream = new FileInputStream(file);
+		}
+		try {
+			entryEnumerationStream = new TarInputStream(fInputStream);
+		} catch (TarException | IOException ex) {
+			fInputStream.close();
+			throw ex;
+		}
+		curEntry = entryEnumerationStream.getNextEntry();
+	}
+
+	/**
+	 * Close the tar file input stream.
+	 *
+	 * @throws IOException if the file cannot be successfully closed
+	 */
+	public void close() throws IOException {
+		if (entryEnumerationStream != null) {
+            entryEnumerationStream.close();
+        }
+		if (internalEntryStream != null) {
+            internalEntryStream.close();
+        }
+	}
+
+	/**
+	 * Create a new TarFile for the given path name.
+	 *
+	 * @param filename the file name to create the TarFile from
+     * @throws TarException on Tar error (bad format, etc)
+     * @throws IOException on i/o error
+	 */
+	public TarFile(String filename) throws TarException, IOException {
+		this(new File(filename));
+	}
+
+	/**
+	 * Returns an enumeration cataloguing the tar archive.
+	 *
+	 * @return enumeration of all files in the archive
+	 */
+	public Enumeration<TarEntry> entries() {
+		return new Enumeration<TarEntry>() {
+			@Override
+			public boolean hasMoreElements() {
+				return (curEntry != null);
+			}
+
+			@Override
+			public TarEntry nextElement() {
+				TarEntry oldEntry = curEntry;
+				try {
+					curEntry = entryEnumerationStream.getNextEntry();
+				} catch(TarException e) {
+					curEntry = null;
+				} catch(IOException e) {
+					curEntry = null;
+				}
+				return oldEntry;
+			}
+		};
+	}
+
+	/**
+	 * Returns a new InputStream for the given file in the tar archive.
+	 *
+	 * @param entry the entry to get the InputStream from
+	 * @return an input stream for the given file
+     * @throws TarException on Tar error (bad format, etc)
+     * @throws IOException on i/o error
+	 */
+	public InputStream getInputStream(TarEntry entry) throws TarException, IOException {
+		if(entryStream == null || !entryStream.skipToEntry(entry)) {
+			if (internalEntryStream != null) {
+				internalEntryStream.close();
+			}
+			internalEntryStream = new FileInputStream(file);
+			// First, check if it's a GZIPInputStream.
+			try {
+				internalEntryStream = new GZIPInputStream(internalEntryStream);
+			} catch(IOException e) {
+				//If it is not compressed we close
+				//the old one and recreate
+				internalEntryStream.close();
+				internalEntryStream = new FileInputStream(file);
+			}
+			entryStream = new TarInputStream(internalEntryStream, entry) {
+				@Override
+				public void close() {
+					// Ignore close() since we want to reuse the stream.
+				}
+			};
+		}
+		return entryStream;
+	}
+
+	/**
+	 * Returns the path name of the file this archive represents.
+	 *
+	 * @return path
+	 */
+	public String getName() {
+		return file.getPath();
+	}
+
+	@Override
+	protected void finalize() throws Throwable {
+		close();
+	}
+}
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFileSystemObject.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFileSystemObject.java
index 6717708..93db412 100644
--- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFileSystemObject.java
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarFileSystemObject.java
@@ -19,13 +19,11 @@
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.URIUtil;
-import org.eclipse.ui.internal.wizards.datatransfer.TarEntry;
 
 /**
  * The "Tar" implementation of an IFileSystemObject, entries can also be Gzipped
  * and are uncompressed transparently.
  */
-@SuppressWarnings("restriction")
 class TarFileSystemObject implements IFileSystemObject {
 
     private TarEntry fFileSystemObject;
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarInputStream.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarInputStream.java
new file mode 100644
index 0000000..06b6347
--- /dev/null
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarInputStream.java
@@ -0,0 +1,343 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2015 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Marc-Andre Laperle <marc-andre.laperle@ericsson.com> - Copied to Trace Compass to work around bug 501379
+ *******************************************************************************/
+package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.ui.internal.wizards.datatransfer.DataTransferMessages;
+
+/**
+ * Input stream for reading files in ustar format (tar) compatible
+ * with the specification in IEEE Std 1003.1-2001.  Also supports
+ * long filenames encoded using the GNU @LongLink extension.
+ */
+@SuppressWarnings("restriction")
+public class TarInputStream extends FilterInputStream
+{
+	private int nextEntry = 0;
+	private int nextEOF = 0;
+	private int filepos = 0;
+	private long bytesread = 0;
+	private TarEntry firstEntry = null;
+	private String longLinkName = null;
+
+	/**
+	 * Creates a new tar input stream on the given input stream.
+	 *
+	 * @param in input stream
+     * @throws TarException on Tar error (bad format, etc)
+     * @throws IOException on i/o error
+	 */
+	public TarInputStream(InputStream in) throws TarException, IOException {
+		super(in);
+
+		// Read in the first TarEntry to make sure
+		// the input is a valid tar file stream.
+		firstEntry = getNextEntry();
+	}
+
+	/**
+	 * Create a new tar input stream, skipping ahead to the given entry
+	 * in the file.
+	 *
+	 * @param in input stream
+	 * @param entry skips to this entry in the file
+	 * @throws TarException
+	 * @throws IOException
+	 */
+	TarInputStream(InputStream in, TarEntry entry) throws TarException, IOException {
+		super(in);
+		skipToEntry(entry);
+	}
+
+	/**
+	 *  The checksum of a tar file header is simply the sum of the bytes in
+	 *  the header.
+	 *
+	 * @param header
+	 * @return checksum
+	 */
+	private static long headerChecksum(byte[] header) {
+		long sum = 0;
+		for(int i = 0; i < 512; i++) {
+			sum += header[i] & 0xff;
+		}
+		return sum;
+	}
+
+	/**
+	 * Skips ahead to the position of the given entry in the file.
+	 *
+	 * @param entry
+	 * @returns false if the entry has already been passed
+	 * @throws TarException
+	 * @throws IOException
+	 */
+	boolean skipToEntry(TarEntry entry) throws TarException, IOException {
+		long bytestoskip = entry.filepos - bytesread;
+		if(bytestoskip < 0) {
+			return false;
+		}
+		while(bytestoskip > 0) {
+			long ret = in.skip(bytestoskip);
+			if(ret < 0) {
+				throw new IOException("early end of stream"); //$NON-NLS-1$
+			}
+			bytestoskip -= ret;
+			bytesread += ret;
+		}
+		filepos = entry.filepos;
+		nextEntry = 0;
+		nextEOF = 0;
+		// Read next header to seek to file data.
+		getNextEntry();
+		return true;
+	}
+
+	/**
+	 * Returns true if the header checksum is correct.
+	 *
+	 * @param header
+	 * @return true if this header has a valid checksum
+	 */
+	private static boolean isValidTarHeader(byte[] header) {
+		long fileChecksum, calculatedChecksum;
+		int pos, i;
+
+		pos = 148;
+		StringBuffer checksumString = new StringBuffer();
+		for(i = 0; i < 8; i++) {
+			if(header[pos + i] == ' ') {
+				continue;
+			}
+			if(header[pos + i] == 0 || !Character.isDigit((char) header[pos + i])) {
+				break;
+			}
+			checksumString.append((char) header[pos + i]);
+		}
+		if(checksumString.length() == 0) {
+			return false;
+		}
+		if(checksumString.charAt(0) != '0') {
+			checksumString.insert(0, '0');
+		}
+		try {
+			fileChecksum = Long.decode(checksumString.toString()).longValue();
+		} catch(NumberFormatException exception) {
+			//This is not valid if it cannot be parsed
+			return false;
+		}
+
+		// Blank out the checksum.
+		for(i = 0; i < 8; i++) {
+			header[pos + i] = ' ';
+		}
+		calculatedChecksum = headerChecksum(header);
+
+		return (fileChecksum == calculatedChecksum);
+	}
+
+	/**
+	 * Returns the next entry in the tar file.  Does not handle
+	 * GNU @LongLink extensions.
+	 *
+	 * @return the next entry in the tar file
+	 * @throws TarException
+	 * @throws IOException
+	 */
+	TarEntry getNextEntryInternal() throws TarException, IOException {
+		byte[] header = new byte[512];
+		int pos = 0;
+		int i;
+
+		if(firstEntry != null) {
+			TarEntry entryReturn = firstEntry;
+			firstEntry = null;
+			return entryReturn;
+		}
+
+		while(nextEntry > 0) {
+			long ret = in.skip(nextEntry);
+			if(ret < 0) {
+				throw new IOException("early end of stream"); //$NON-NLS-1$
+			}
+			nextEntry -= ret;
+			bytesread += ret;
+		}
+
+		int bytestoread = 512;
+		while(bytestoread > 0) {
+			int ret = super.read(header, 512 - bytestoread, bytestoread);
+			if( ret < 0 ) {
+				throw new IOException("early end of stream"); //$NON-NLS-1$
+			}
+			bytestoread -= ret;
+			bytesread += ret;
+		}
+
+		// If we have a header of all zeros, this marks the end of the file.
+		if(headerChecksum(header) == 0) {
+			// We are at the end of the file.
+			if(filepos > 0) {
+				return null;
+			}
+
+			// Invalid stream.
+			throw new TarException("not in tar format"); //$NON-NLS-1$
+		}
+
+		// Validate checksum.
+		if(!isValidTarHeader(header)) {
+			throw new TarException("not in tar format"); //$NON-NLS-1$
+		}
+
+		while (pos < 100 && header[pos] != 0) {
+			pos++;
+		}
+		String name = new String(header, 0, pos, "UTF8"); //$NON-NLS-1$
+		// Prepend the prefix here.
+		pos = 345;
+		if(header[pos] != 0) {
+			while (pos < 500 && header[pos] != 0) {
+				pos++;
+			}
+			String prefix = new String(header, 345, pos - 345, "UTF8"); //$NON-NLS-1$
+			name = prefix + "/" + name; //$NON-NLS-1$
+		}
+
+		TarEntry entry;
+		if(longLinkName != null) {
+			entry = new TarEntry(longLinkName, filepos);
+			longLinkName = null;
+		} else {
+			entry = new TarEntry(name, filepos);
+		}
+		if(header[156] != 0) {
+			entry.setFileType(header[156]);
+		}
+
+		pos = 100;
+		StringBuffer mode = new StringBuffer();
+		for(i = 0; i < 8; i++) {
+			if(header[pos + i] == 0) {
+				break;
+			}
+			if(header[pos + i] == ' ') {
+				continue;
+			}
+			mode.append((char) header[pos + i]);
+		}
+		if(mode.length() > 0 && mode.charAt(0) != '0') {
+			mode.insert(0, '0');
+		}
+		try {
+			long fileMode = Long.decode(mode.toString()).longValue();
+			entry.setMode(fileMode);
+		} catch(NumberFormatException nfe) {
+			throw new TarException(DataTransferMessages.TarImport_invalid_tar_format, nfe);
+		}
+
+		pos = 100 + 24;
+		StringBuffer size = new StringBuffer();
+		for(i = 0; i < 12; i++) {
+			if(header[pos + i] == 0) {
+				break;
+			}
+			if(header[pos + i] == ' ') {
+				continue;
+			}
+			size.append((char) header[pos + i]);
+		}
+		if(size.charAt(0) != '0') {
+			size.insert(0, '0');
+		}
+		int fileSize;
+		try {
+			fileSize = Integer.decode(size.toString()).intValue();
+		} catch(NumberFormatException nfe) {
+			throw new TarException(DataTransferMessages.TarImport_invalid_tar_format, nfe);
+		}
+
+		entry.setSize(fileSize);
+		nextEOF = fileSize;
+		if(fileSize % 512 > 0) {
+			nextEntry = fileSize + (512 - (fileSize % 512));
+		} else {
+			nextEntry = fileSize;
+		}
+		filepos += (nextEntry + 512);
+		return entry;
+	}
+
+	/**
+	 * Moves ahead to the next file in the tar archive and returns
+	 * a TarEntry object describing it.
+	 *
+	 * @return the next entry in the tar file
+     * @throws TarException on Tar error (bad format, etc)
+     * @throws IOException on i/o errors
+	 */
+	public TarEntry getNextEntry() throws TarException, IOException {
+		TarEntry entry = getNextEntryInternal();
+
+		if(entry != null && entry.getName().equals("././@LongLink")) { //$NON-NLS-1$
+			// This is a GNU extension for doing long filenames.
+			// We get a file called ././@LongLink which just contains
+			// the real pathname.
+			byte[] longNameData = new byte[(int) entry.getSize()];
+			int nbytesread = 0;
+			while (nbytesread < longNameData.length) {
+				int cur = read(longNameData, nbytesread, longNameData.length - nbytesread);
+				if (cur < 0) {
+					throw new IOException("early end of stream"); //$NON-NLS-1$
+				}
+				nbytesread += cur;
+			}
+
+			int pos = 0;
+			while (pos < longNameData.length && longNameData[pos] != 0) {
+				pos++;
+			}
+			longLinkName = new String(longNameData, 0, pos, "UTF8"); //$NON-NLS-1$
+			return getNextEntryInternal();
+		}
+		return entry;
+	}
+
+	@Override
+	public int read(byte[] b, int off, int len) throws IOException {
+	    int lenToRead = len;
+		if(nextEOF == 0) {
+			return -1;
+		}
+		if(lenToRead > nextEOF) {
+		    lenToRead = nextEOF;
+		}
+		int size = super.read(b, off, lenToRead);
+		nextEntry -= size;
+		nextEOF -= size;
+		bytesread += size;
+		return size;
+	}
+
+	@Override
+	public int read() throws IOException {
+		byte[] data = new byte[1];
+		int size = read(data, 0, 1);
+		if (size < 0) {
+			return size;
+		}
+		return data[0];
+	}
+}
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarLeveledStructureProvider.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarLeveledStructureProvider.java
new file mode 100644
index 0000000..137f1aa
--- /dev/null
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/importtrace/TarLeveledStructureProvider.java
@@ -0,0 +1,258 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Red Hat, Inc - Was TarFileStructureProvider, performed changes from
+ *     IImportStructureProvider to ILeveledImportStructureProvider
+ *     Mickael Istria (Red Hat Inc.) - Bug 486901
+ *     Marc-Andre Laperle <marc-andre.laperle@ericsson.com> - Copied to Trace Compass to work around bug 501379
+ *******************************************************************************/
+package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.resources.ResourceAttributes;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.tracecompass.common.core.NonNullUtils;
+import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
+import org.eclipse.ui.internal.wizards.datatransfer.DataTransferMessages;
+import org.eclipse.ui.internal.wizards.datatransfer.ILeveledImportStructureProvider;
+
+/**
+ * This class provides information regarding the context structure and content
+ * of specified tar file entry objects.
+ */
+@SuppressWarnings("restriction")
+public class TarLeveledStructureProvider implements
+		ILeveledImportStructureProvider {
+	private TarFile tarFile;
+
+	private TarEntry root = new TarEntry("/");//$NON-NLS-1$
+
+	private Map<TarEntry, List<TarEntry>> children;
+
+	private Map<IPath, TarEntry> directoryEntryCache = new HashMap<>();
+
+	private int stripLevel;
+
+	/**
+	 * Creates a <code>TarFileStructureProvider</code>, which will operate on
+	 * the passed tar file.
+	 *
+	 * @param sourceFile
+	 *            the source TarFile
+	 */
+	public TarLeveledStructureProvider(TarFile sourceFile) {
+		super();
+		tarFile = sourceFile;
+		root.setFileType(TarEntry.DIRECTORY);
+	}
+
+	/**
+	 * Creates a new container tar entry with the specified name, iff it has
+	 * not already been created. If the parent of the given element does not
+	 * already exist it will be recursively created as well.
+	 * @param pathname The path representing the container
+	 * @return The element represented by this pathname (it may have already existed)
+	 */
+	protected TarEntry createContainer(IPath pathname) {
+		TarEntry existingEntry = directoryEntryCache.get(pathname);
+		if (existingEntry != null) {
+			return existingEntry;
+		}
+
+		TarEntry parent;
+		if (pathname.segmentCount() == 1) {
+			parent = root;
+		} else {
+			parent = createContainer(pathname.removeLastSegments(1));
+		}
+		TarEntry newEntry = new TarEntry(pathname.toString());
+		newEntry.setFileType(TarEntry.DIRECTORY);
+		directoryEntryCache.put(pathname, newEntry);
+		List<TarEntry> childList = new ArrayList<>();
+		children.put(newEntry, childList);
+
+		List<TarEntry> parentChildList = children.get(parent);
+		NonNullUtils.checkNotNull(parentChildList).add(newEntry);
+		return newEntry;
+	}
+
+	/**
+	 * Creates a new tar file entry with the specified name.
+	 * @param entry the entry to create the file for
+	 */
+	protected void createFile(TarEntry entry) {
+		IPath pathname = new Path(entry.getName());
+		TarEntry parent;
+		if (pathname.segmentCount() == 1) {
+			parent = root;
+		} else {
+			parent = directoryEntryCache.get(pathname
+					.removeLastSegments(1));
+		}
+
+		List<TarEntry> childList = children.get(parent);
+		NonNullUtils.checkNotNull(childList).add(entry);
+	}
+
+	@Override
+	public List getChildren(Object element) {
+		if (children == null) {
+			initialize();
+		}
+
+		return (children.get(element));
+	}
+
+    @Override
+	public InputStream getContents(Object element) {
+		try {
+			return tarFile.getInputStream((TarEntry) element);
+		} catch (TarException e) {
+			IDEWorkbenchPlugin.log(e.getLocalizedMessage(), e);
+			return null;
+		} catch (IOException e) {
+			IDEWorkbenchPlugin.log(e.getLocalizedMessage(), e);
+			return null;
+		}
+	}
+
+	/**
+	 * Returns the resource attributes for this file.
+	 *
+	 * @param element the element to get the attributes from
+	 * @return the attributes of the file
+	 */
+	public ResourceAttributes getResourceAttributes(Object element) {
+		ResourceAttributes attributes = new ResourceAttributes();
+		TarEntry entry = (TarEntry) element;
+		attributes.setExecutable((entry.getMode() & 0100) != 0);
+		attributes.setReadOnly((entry.getMode() & 0200) == 0);
+		return attributes;
+	}
+
+	@Override
+	public String getFullPath(Object element) {
+		return stripPath(((TarEntry) element).getName());
+	}
+
+	@Override
+	public String getLabel(Object element) {
+		if (element.equals(root)) {
+			return ((TarEntry) element).getName();
+		}
+
+		return stripPath(new Path(((TarEntry) element).getName()).lastSegment());
+	}
+
+	/**
+	 * Returns the entry that this importer uses as the root sentinel.
+	 *
+	 * @return TarEntry entry
+	 */
+	@Override
+	public Object getRoot() {
+		return root;
+	}
+
+	/**
+	 * Returns the tar file that this provider provides structure for.
+	 *
+	 * @return TarFile file
+	 */
+	public TarFile getTarFile() {
+		return tarFile;
+	}
+
+	@Override
+	public boolean closeArchive(){
+		try {
+			getTarFile().close();
+		} catch (IOException e) {
+			IDEWorkbenchPlugin.log(DataTransferMessages.ZipImport_couldNotClose
+					+ getTarFile().getName(), e);
+			return false;
+		}
+		return true;
+	}
+
+	/**
+	 * Initializes this object's children table based on the contents of the
+	 * specified source file.
+	 */
+	protected void initialize() {
+		children = new HashMap<>(1000);
+
+		children.put(root, new ArrayList<>());
+		Enumeration<TarEntry> entries = tarFile.entries();
+		while (entries.hasMoreElements()) {
+			TarEntry entry = entries.nextElement();
+			IPath path = new Path(entry.getName()).addTrailingSeparator();
+
+			if (entry.getFileType() == TarEntry.DIRECTORY) {
+				createContainer(path);
+			} else
+			{
+				// Ensure the container structure for all levels above this is initialized
+				// Once we hit a higher-level container that's already added we need go no further
+				int pathSegmentCount = path.segmentCount();
+				if (pathSegmentCount > 1) {
+					createContainer(path.uptoSegment(pathSegmentCount - 1));
+				}
+				createFile(entry);
+			}
+		}
+	}
+
+	@Override
+	public boolean isFolder(Object element) {
+		return (((TarEntry) element).getFileType() == TarEntry.DIRECTORY);
+	}
+
+	/*
+	 * Strip the leading directories from the path
+	 */
+	private String stripPath(String path) {
+	    String strippedPath = path;
+		String pathOrig = strippedPath;
+		for (int i = 0; i < stripLevel; i++) {
+			int firstSep = strippedPath.indexOf('/');
+			// If the first character was a seperator we must strip to the next
+			// seperator as well
+			if (firstSep == 0) {
+			    strippedPath = strippedPath.substring(1);
+				firstSep = strippedPath.indexOf('/');
+			}
+			// No seperator wasw present so we're in a higher directory right
+			// now
+			if (firstSep == -1) {
+				return pathOrig;
+			}
+			strippedPath = strippedPath.substring(firstSep);
+		}
+		return strippedPath;
+	}
+
+	@Override
+	public void setStrip(int level) {
+		stripLevel = level;
+	}
+
+	@Override
+	public int getStrip() {
+		return stripLevel;
+	}
+}
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/AbstractTracePackageOperation.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/AbstractTracePackageOperation.java
index e5470df..baace2b 100644
--- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/AbstractTracePackageOperation.java
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/AbstractTracePackageOperation.java
@@ -22,9 +22,9 @@
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jdt.annotation.NonNull;
-import org.eclipse.ui.internal.wizards.datatransfer.TarEntry;
-import org.eclipse.ui.internal.wizards.datatransfer.TarException;
-import org.eclipse.ui.internal.wizards.datatransfer.TarFile;
+import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.TarEntry;
+import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.TarException;
+import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.TarFile;
 
 /**
  * An abstract operation containing common code useful for other trace package
@@ -32,7 +32,6 @@
  *
  * @author Marc-Andre Laperle
  */
-@SuppressWarnings("restriction")
 public abstract class AbstractTracePackageOperation {
     private IStatus fStatus;
     // Result of this operation, if any
diff --git a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/importexport/TracePackageImportOperation.java b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/importexport/TracePackageImportOperation.java
index a4ae99b..8f5cad1 100644
--- a/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/importexport/TracePackageImportOperation.java
+++ b/tmf/org.eclipse.tracecompass.tmf.ui/src/org/eclipse/tracecompass/internal/tmf/ui/project/wizards/tracepkg/importexport/TracePackageImportOperation.java
@@ -42,6 +42,7 @@
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jface.operation.ModalContext;
 import org.eclipse.tracecompass.internal.tmf.ui.Activator;
+import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.TarException;
 import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.AbstractTracePackageOperation;
 import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageBookmarkElement;
 import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageElement;
@@ -60,7 +61,6 @@
 import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceTypeUIUtils;
 import org.eclipse.ui.dialogs.IOverwriteQuery;
 import org.eclipse.ui.ide.IDE;
-import org.eclipse.ui.internal.wizards.datatransfer.TarException;
 import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
 import org.eclipse.ui.wizards.datatransfer.ImportOperation;
 
@@ -69,7 +69,6 @@
  *
  * @author Marc-Andre Laperle
  */
-@SuppressWarnings("restriction")
 public class TracePackageImportOperation extends AbstractTracePackageOperation implements IOverwriteQuery {
 
     private final TracePackageElement[] fImportTraceElements;