Bug 506109 - Add support to copy files from container to host

- add ContainerFileProxy class to model container file system
- add ContainerFileSelectionDialog for selecting files from
  running Container
- add DynamicCheckboxViewer that allows dynamic reading of the
  Container file system directories
- add checks to DockerConnection copyContainer method to fail
  if API >= 1.24
- add DockerConnection readContainerDirectory method
- save connection info for usage in DockerConnection methods
- copy over CheckboxTreeAndListGroup from platform
- add ContainerFileSystemProvider class which traverses the
  Container file system
- copy MinimizedFileSystemElement from platform
- copy PopulateRootFilesOperation from platform
- copy SelectFilesOperation from platform
- create PopulateContainerFilesPperation
- copy TarEntry, TarInputStream, and TarException classes from platform
- add CopyFromContainerCommandHandler
- add ContainerCopyFrom wizard and ContainerCopyFromPage
- add copy from container menu item to Docker Containers View

Change-Id: I5b928d6581e0ad4f8983866ad9d4bf12df7fc79b
Reviewed-on: https://git.eclipse.org/r/83396
Reviewed-by: Roland Grunberg <rgrunber@redhat.com>
Tested-by: Hudson CI
diff --git a/containers/org.eclipse.linuxtools.docker.core/META-INF/MANIFEST.MF b/containers/org.eclipse.linuxtools.docker.core/META-INF/MANIFEST.MF
index 6dfea14..c811a4c 100644
--- a/containers/org.eclipse.linuxtools.docker.core/META-INF/MANIFEST.MF
+++ b/containers/org.eclipse.linuxtools.docker.core/META-INF/MANIFEST.MF
@@ -24,6 +24,5 @@
 Bundle-ActivationPolicy: lazy
 Export-Package: org.eclipse.linuxtools.docker.core,
  org.eclipse.linuxtools.internal.docker.core;x-friends:="org.eclipse.linuxtools.docker.ui,org.eclipse.linuxtools.docker.ui.tests"
-Import-Package: com.fasterxml.jackson.annotation;version="2.5.0",
- org.eclipse.jface.preference
+Import-Package: com.fasterxml.jackson.annotation;version="2.5.0"
 Bundle-ClassPath: .
diff --git a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/ContainerFileProxy.java b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/ContainerFileProxy.java
new file mode 100644
index 0000000..5038434
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/ContainerFileProxy.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat.
+ * 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:
+ *     Red Hat - Initial Contribution
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.docker.core;
+
+public class ContainerFileProxy {
+
+	private final String path;
+	private final String name;
+	private final String link;
+	private final boolean isFolder;
+	private final boolean isLink;
+
+	public ContainerFileProxy(String directory, String name,
+			boolean isFolder) {
+		this.path = directory + (directory.equals("/") ? "" : "/") + name; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		this.name = name;
+		this.isFolder = isFolder;
+		this.isLink = false;
+		this.link = this.path;
+	}
+
+	public ContainerFileProxy(String directory, String name, boolean isFolder,
+			boolean isLink, String link) {
+		this.path = directory + (directory.equals("/") ? "" : "/") + name; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		this.name = name;
+		this.isFolder = isFolder;
+		this.isLink = isLink;
+		this.link = (link == null ? this.path : link);
+	}
+
+	public String getFullPath() {
+		return path;
+	}
+
+	public String getLabel() {
+		return name + (isFolder() ? "/" : "");
+	}
+
+	public boolean isFolder() {
+		return isFolder;
+	}
+
+	public boolean isLink() {
+		return isLink;
+	}
+
+	public String getLink() {
+		return link;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	@Override
+	public String toString() {
+		return getFullPath();
+	}
+
+}
diff --git a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerConnection.java b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerConnection.java
index 4eacc3a..f650bdf 100644
--- a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerConnection.java
+++ b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerConnection.java
@@ -37,6 +37,7 @@
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
@@ -165,6 +166,7 @@
 
 	private String name;
 	private IDockerConnectionSettings connectionSettings;
+	private IDockerConnectionInfo connectionInfo;
 	private final String username;
 	private final Object imageLock = new Object();
 	private final Object containerLock = new Object();
@@ -281,6 +283,12 @@
 
 	public void setClient(final DockerClient client) {
 		this.client = client;
+		try {
+			this.connectionInfo = getInfo();
+		} catch (Exception e) {
+			// ignore for now as this seems to occur too often and we always
+			// check the value of connectioninfo before using
+		}
 	}
 	/**
 	 * Change the default {@link DockerClientFactory}
@@ -1831,7 +1839,28 @@
 			throws DockerException, InterruptedException {
 		InputStream stream;
 		try {
-			stream = client.copyContainer(id, path);
+			if (this.connectionInfo != null) {
+				String apiversion = connectionInfo.getApiVersion();
+				if (apiversion != null) {
+					String[] tokens = apiversion.split("\\."); //$NON-NLS-1$
+					if (tokens.length > 1) {
+						try {
+							int major = Integer.valueOf(tokens[0]);
+							int minor = Integer.valueOf(tokens[1]);
+							if (major > 1 || minor >= 24) {
+								throw new DockerException(
+										DockerMessages.getFormattedString(
+												"DockerClientVersionTooLow.error", //$NON-NLS-1$
+												"copyContainer", "1.24")); //$NON-NLS-1$ //$NON-NLS-2$
+							}
+						} catch (NumberFormatException e) {
+							// ignore for now and let things occur
+						}
+					}
+				}
+			}
+			DockerClient copy = getClientCopy();
+			stream = copy.copyContainer(id, path);
 		} catch (com.spotify.docker.client.DockerException e) {
 			throw new DockerException(e.getMessage(), e.getCause());
 		}
@@ -1998,6 +2027,68 @@
 		}
 	}
 
+	@SuppressWarnings("unused")
+	public List<ContainerFileProxy> readContainerDirectory(final String id,
+			final String path) throws DockerException {
+		List<ContainerFileProxy> childList = new ArrayList<>();
+		try {
+			DockerClient copyClient = getClientCopy();
+			final String execId = copyClient.execCreate(id,
+					new String[] { "/bin/sh", "-c", "ls -l -F -L -Q " + path }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+					ExecCreateParam.attachStdout(),
+					ExecCreateParam.attachStderr());
+			final LogStream pty_stream = copyClient.execStart(execId);
+			try {
+				while (pty_stream.hasNext()) {
+					ByteBuffer b = pty_stream.next().content();
+					byte[] buffer = new byte[b.remaining()];
+					b.get(buffer);
+					String s = new String(buffer);
+					String[] lines = s.split("\\r?\\n"); //$NON-NLS-1$
+					for (String line : lines) {
+						if (line.trim().startsWith("total")) //$NON-NLS-1$
+							continue; // ignore the total line
+						String[] token = line.split("\\s+"); //$NON-NLS-1$
+						boolean isDirectory = token[0].startsWith("d"); //$NON-NLS-1$
+						boolean isLink = token[0].startsWith("l"); //$NON-NLS-1$
+						if (token.length > 8) {
+							// last token depends on whether we have a link or not
+							String link = null;
+							if (isLink) {
+								String linkname = token[token.length - 1];
+								if (linkname.endsWith("/")) { //$NON-NLS-1$
+									linkname = linkname.substring(0, linkname.length() - 1);
+									isDirectory = true;
+								}
+								IPath linkPath = new Path(path);
+								linkPath = linkPath.append(linkname);
+								link = linkPath.toString();
+								String name = token[token.length - 3];
+								childList.add(new ContainerFileProxy(path, name,
+										isDirectory, isLink, link));
+							} else {
+								String name = token[token.length - 1];
+								// remove quotes and any indicator char
+								name = name.substring(1, name.length()
+										- (name.endsWith("\"") ? 1 : 2));
+								childList.add(new ContainerFileProxy(path, name,
+										isDirectory));
+							}
+						}
+					}
+				}
+			} finally {
+				if (pty_stream != null)
+					pty_stream.close();
+				if (copyClient != null)
+					copyClient.close();
+			}
+		} catch (Exception e) {
+			// e.printStackTrace();
+		}
+		return childList;
+	}
+
 	public void execShell(final String id) throws DockerException {
 		try {
 			final String execId = client.execCreate(id,
diff --git a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerMessages.properties b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerMessages.properties
index 56ed190..e408840 100644
--- a/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerMessages.properties
+++ b/containers/org.eclipse.linuxtools.docker.core/src/org/eclipse/linuxtools/internal/docker/core/DockerMessages.properties
@@ -27,3 +27,5 @@
 ImageBuildStep.msg=Step 
 ImageBuildingJobName.msg=Building Docker Image - {0}
 ImageBuilding.msg=Building image
+
+DockerClientVersionTooLow.error=The version of docker client cannot support {0} using a daemon with API version: {1} and higher.
diff --git a/containers/org.eclipse.linuxtools.docker.ui/.settings/.api_filters b/containers/org.eclipse.linuxtools.docker.ui/.settings/.api_filters
new file mode 100644
index 0000000..639132e
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/.settings/.api_filters
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<component id="org.eclipse.linuxtools.docker.ui" version="2">
+    <resource path="src/org/eclipse/linuxtools/internal/docker/ui/DynamicCheckboxTreeViewer.java" type="org.eclipse.linuxtools.internal.docker.ui.DynamicCheckboxTreeViewer">
+        <filter id="571473929">
+            <message_arguments>
+                <message_argument value="CheckboxTreeViewer"/>
+                <message_argument value="DynamicCheckboxTreeViewer"/>
+            </message_arguments>
+        </filter>
+    </resource>
+</component>
diff --git a/containers/org.eclipse.linuxtools.docker.ui/plugin.properties b/containers/org.eclipse.linuxtools.docker.ui/plugin.properties
index 21a9461..a237362 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/plugin.properties
+++ b/containers/org.eclipse.linuxtools.docker.ui/plugin.properties
@@ -72,6 +72,9 @@
 command.commitcontainer.name=Commit
 command.commitcontainer.description=Commit the selected container into a new image
 
+command.copyfromcontainer.name=Copy from Container
+command.copyfromcontainer.description=Copy files from running Container to a local directory
+
 command.displaycontainerlog.name=Display Log
 command.displaycontainerlog.description=Display the log for the selected container in the Console
 
diff --git a/containers/org.eclipse.linuxtools.docker.ui/plugin.xml b/containers/org.eclipse.linuxtools.docker.ui/plugin.xml
index a0a066b..584bb34 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/plugin.xml
+++ b/containers/org.eclipse.linuxtools.docker.ui/plugin.xml
@@ -265,6 +265,11 @@
 	        name="%command.commitcontainer.name">
         </command>
 		<command
+	        description="%command.copyfromcontainer.description"
+	        id="org.eclipse.linuxtools.docker.ui.commands.copyfromcontainer"
+	        name="%command.copyfromcontainer.name">
+        </command>
+		<command
 	        description="%command.displaycontainerlog.description"
 	        id="org.eclipse.linuxtools.docker.ui.commands.displayContainerLog"
 	        name="%command.displaycontainerlog.name">
@@ -657,6 +662,38 @@
 	            </with>
      		</enabledWhen>
      	</handler>
+     	<handler
+        	class="org.eclipse.linuxtools.internal.docker.ui.commands.CopyFromContainerCommandHandler"
+        	commandId="org.eclipse.linuxtools.docker.ui.commands.copyfromcontainer">
+     		<enabledWhen>
+        		<with
+              		variable="selection">
+           			<count
+                 		value="1">
+           			</count>
+           			<iterate
+                 		ifEmpty="false">
+              			<and>
+                 			<instanceof
+                       			value="org.eclipse.linuxtools.docker.core.IDockerContainer">
+                 			</instanceof>
+                 			<or>
+                    			<test
+                          			forcePluginActivation="true"
+                          			property="org.eclipse.linuxtools.docker.propertytesters.container.isStopped"
+                          			value="false">
+                    			</test>
+                    			<test
+                          			forcePluginActivation="true"
+                          			property="org.eclipse.linuxtools.docker.propertytesters.container.isUnknown"
+                          			value="true">
+                    			</test>
+                 			</or>
+              			</and>
+           			</iterate>
+        		</with>
+     		</enabledWhen>
+  		</handler>
 		<handler
               commandId="org.eclipse.linuxtools.docker.ui.commands.displayContainerLog"
               class="org.eclipse.linuxtools.internal.docker.ui.commands.DisplayContainerLogCommandHandler">
@@ -1646,6 +1683,15 @@
 	           style="push">
 		      </command>
 		</menuContribution>
+		<!-- containers view context menu: copy from containers -->
+		<menuContribution
+			locationURI="popup:org.eclipse.linuxtools.docker.ui.dockerContainersView">
+	  		<command
+	           commandId="org.eclipse.linuxtools.docker.ui.commands.copyfromcontainer"
+	           id="org.eclipse.linuxtools.docker.ui.commands.copyfromcontainer"
+	           style="push">
+		      </command>
+		</menuContribution>
 		
 		<!-- containers view toolbar: refresh view -->
 		<menuContribution
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/CheckboxTreeAndListGroup.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/CheckboxTreeAndListGroup.java
new file mode 100644
index 0000000..2c1991c
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/CheckboxTreeAndListGroup.java
@@ -0,0 +1,864 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.docker.ui;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.eclipse.core.commands.common.EventManager;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.jface.util.SafeRunnable;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.ITreeViewerListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreeExpansionEvent;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Tree;
+
+/**
+ * Workbench-level composite that combines a CheckboxTreeViewer and
+ * CheckboxListViewer. All viewer selection-driven interactions are handled
+ * within this object
+ */
+@SuppressWarnings("rawtypes")
+public class CheckboxTreeAndListGroup extends EventManager implements
+		ICheckStateListener, ISelectionChangedListener, ITreeViewerListener {
+	private Object root;
+
+	private Object currentTreeSelection;
+
+	private List expandedTreeNodes = new ArrayList();
+
+	private Map checkedStateStore = new HashMap(9);
+
+	private List whiteCheckedTreeItems = new ArrayList();
+
+	private ITreeContentProvider treeContentProvider;
+
+	private ITreeContentProvider dynamicTreeContentProvider;
+
+	private IStructuredContentProvider listContentProvider;
+
+	private ILabelProvider treeLabelProvider;
+
+	private ILabelProvider listLabelProvider;
+
+	// widgets
+	private CheckboxTreeViewer treeViewer;
+
+	private CheckboxTableViewer listViewer;
+
+	/**
+	 * Create an instance of this class. Use this constructor if you wish to
+	 * specify the width and/or height of the combined widget (to only hardcode
+	 * one of the sizing dimensions, specify the other dimension's value as -1)
+	 * 
+	 * @param parent
+	 * @param rootObject
+	 * @param treeContentProvider
+	 * @param treeLabelProvider
+	 * @param listContentProvider
+	 * @param listLabelProvider
+	 * @param style
+	 * @param width
+	 * @param height
+	 */
+	public CheckboxTreeAndListGroup(Composite parent, Object rootObject,
+			ITreeContentProvider treeContentProvider,
+			ITreeContentProvider dynamicTreeContentProvider,
+			ILabelProvider treeLabelProvider,
+			IStructuredContentProvider listContentProvider,
+			ILabelProvider listLabelProvider, int style, int width,
+			int height) {
+
+		root = rootObject;
+		this.treeContentProvider = treeContentProvider;
+		this.dynamicTreeContentProvider = dynamicTreeContentProvider;
+		this.listContentProvider = listContentProvider;
+		this.treeLabelProvider = treeLabelProvider;
+		this.listLabelProvider = listLabelProvider;
+		createContents(parent, width, height, style);
+	}
+
+	/**
+	 * This method must be called just before this window becomes visible.
+	 */
+	public void aboutToOpen() {
+		determineWhiteCheckedDescendents(root);
+		checkNewTreeElements(treeContentProvider.getElements(root));
+		currentTreeSelection = null;
+
+		// select the first element in the list
+		Object[] elements = treeContentProvider.getElements(root);
+		Object primary = elements.length > 0 ? elements[0] : null;
+		if (primary != null) {
+			treeViewer.setSelection(new StructuredSelection(primary));
+		}
+		treeViewer.getControl().setFocus();
+	}
+
+	/**
+	 * Add the passed listener to self's collection of clients that listen for
+	 * changes to element checked states
+	 *
+	 * @param listener
+	 *            ICheckStateListener
+	 */
+	public void addCheckStateListener(ICheckStateListener listener) {
+		addListenerObject(listener);
+	}
+
+	/**
+	 * Add the receiver and all of it's ancestors to the checkedStateStore if
+	 * they are not already there.
+	 */
+	@SuppressWarnings("unchecked")
+	private void addToHierarchyToCheckedStore(Object treeElement) {
+
+		// if this tree element is already gray then its ancestors all are as
+		// well
+		if (!checkedStateStore.containsKey(treeElement)) {
+			checkedStateStore.put(treeElement, new ArrayList());
+		}
+
+		Object parent = treeContentProvider.getParent(treeElement);
+		if (parent != null) {
+			addToHierarchyToCheckedStore(parent);
+		}
+	}
+
+	/**
+	 * Return a boolean indicating whether all children of the passed tree
+	 * element are currently white-checked
+	 *
+	 * @return boolean
+	 * @param treeElement
+	 *            java.lang.Object
+	 */
+	protected boolean areAllChildrenWhiteChecked(Object treeElement) {
+		Object[] children = treeContentProvider.getChildren(treeElement);
+		for (int i = 0; i < children.length; ++i) {
+			if (!whiteCheckedTreeItems.contains(children[i])) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * Return a boolean indicating whether all list elements associated with the
+	 * passed tree element are currently checked
+	 *
+	 * @return boolean
+	 * @param treeElement
+	 *            java.lang.Object
+	 */
+	protected boolean areAllElementsChecked(Object treeElement) {
+		List checkedElements = (List) checkedStateStore.get(treeElement);
+		if (checkedElements == null) {
+			return false;
+		}
+
+		return getListItemsSize(treeElement) == checkedElements.size();
+	}
+
+	/**
+	 * Iterate through the passed elements which are being realized for the
+	 * first time and check each one in the tree viewer as appropriate
+	 */
+	protected void checkNewTreeElements(Object[] elements) {
+		for (int i = 0; i < elements.length; ++i) {
+			Object currentElement = elements[i];
+			boolean checked = checkedStateStore.containsKey(currentElement);
+			treeViewer.setChecked(currentElement, checked);
+			treeViewer.setGrayed(currentElement,
+					checked && !whiteCheckedTreeItems.contains(currentElement));
+		}
+	}
+
+	/**
+	 * An item was checked in one of self's two views. Determine which view this
+	 * occurred in and delegate appropriately
+	 *
+	 * @param event
+	 *            CheckStateChangedEvent
+	 */
+	@Override
+	public void checkStateChanged(final CheckStateChangedEvent event) {
+
+		// Potentially long operation - show a busy cursor
+		BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), () -> {
+			if (event.getCheckable().equals(treeViewer)) {
+				treeItemChecked(event.getElement(), event.getChecked());
+			} else {
+				listItemChecked(event.getElement(), event.getChecked(), true);
+			}
+
+			notifyCheckStateChangeListeners(event);
+		});
+	}
+
+	/**
+	 * Lay out and initialize self's visual components.
+	 *
+	 * @param parent
+	 *            org.eclipse.swt.widgets.Composite
+	 * @param width
+	 *            int
+	 * @param height
+	 *            int
+	 */
+	protected void createContents(Composite parent, int width, int height,
+			int style) {
+		// group pane
+		Composite composite = new Composite(parent, style);
+		GridLayout layout = new GridLayout();
+		layout.numColumns = 2;
+		layout.makeColumnsEqualWidth = true;
+		layout.marginHeight = 0;
+		layout.marginWidth = 0;
+		composite.setLayout(layout);
+		composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+		composite.setFont(parent.getFont());
+
+		createTreeViewer(composite, width / 2, height);
+		createListViewer(composite, width / 2, height);
+
+		initialize();
+	}
+
+	/**
+	 * Create this group's list viewer.
+	 */
+	protected void createListViewer(Composite parent, int width, int height) {
+		listViewer = CheckboxTableViewer.newCheckList(parent, SWT.BORDER);
+		GridData data = new GridData(GridData.FILL_BOTH);
+		data.widthHint = width;
+		data.heightHint = height;
+		listViewer.getTable().setLayoutData(data);
+		listViewer.getTable().setFont(parent.getFont());
+		listViewer.setContentProvider(listContentProvider);
+		listViewer.setLabelProvider(listLabelProvider);
+		listViewer.addCheckStateListener(this);
+	}
+
+	/**
+	 * Create this group's tree viewer.
+	 */
+	protected void createTreeViewer(Composite parent, int width, int height) {
+		Tree tree = new Tree(parent, SWT.CHECK | SWT.BORDER);
+		GridData data = new GridData(GridData.FILL_BOTH);
+		data.widthHint = width;
+		data.heightHint = height;
+		tree.setLayoutData(data);
+		tree.setFont(parent.getFont());
+
+		treeViewer = new DynamicCheckboxTreeViewer(tree,
+				dynamicTreeContentProvider);
+		treeViewer.setContentProvider(treeContentProvider);
+		treeViewer.setLabelProvider(treeLabelProvider);
+		treeViewer.addTreeListener(this);
+		treeViewer.addCheckStateListener(this);
+		treeViewer.addSelectionChangedListener(this);
+	}
+
+	/**
+	 * Returns a boolean indicating whether the passed tree element should be at
+	 * LEAST gray-checked. Note that this method does not consider whether it
+	 * should be white-checked, so a specified tree item which should be
+	 * white-checked will result in a <code>true</code> answer from this method.
+	 * To determine whether a tree item should be white-checked use method
+	 * #determineShouldBeWhiteChecked(Object).
+	 *
+	 * @param treeElement
+	 *            java.lang.Object
+	 * @return boolean
+	 * @see #determineShouldBeWhiteChecked(java.lang.Object)
+	 */
+	protected boolean determineShouldBeAtLeastGrayChecked(Object treeElement) {
+		// if any list items associated with treeElement are checked then it
+		// retains its gray-checked status regardless of its children
+		List checked = (List) checkedStateStore.get(treeElement);
+		if (checked != null && (!checked.isEmpty())) {
+			return true;
+		}
+
+		// if any children of treeElement are still gray-checked then
+		// treeElement
+		// must remain gray-checked as well
+		Object[] children = treeContentProvider.getChildren(treeElement);
+		for (int i = 0; i < children.length; ++i) {
+			if (checkedStateStore.containsKey(children[i])) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Returns a boolean indicating whether the passed tree item should be
+	 * white-checked.
+	 *
+	 * @return boolean
+	 * @param treeElement
+	 *            java.lang.Object
+	 */
+	protected boolean determineShouldBeWhiteChecked(Object treeElement) {
+		return areAllChildrenWhiteChecked(treeElement)
+				&& areAllElementsChecked(treeElement);
+	}
+
+	/**
+	 * Recursively add appropriate tree elements to the collection of known
+	 * white-checked tree elements.
+	 *
+	 * @param treeElement
+	 *            java.lang.Object
+	 */
+	protected void determineWhiteCheckedDescendents(Object treeElement) {
+		// always go through all children first since their white-checked
+		// statuses will be needed to determine the white-checked status for
+		// this tree element
+		Object[] children = treeContentProvider.getElements(treeElement);
+		for (int i = 0; i < children.length; ++i) {
+			determineWhiteCheckedDescendents(children[i]);
+		}
+
+		// now determine the white-checked status for this tree element
+		if (determineShouldBeWhiteChecked(treeElement)) {
+			setWhiteChecked(treeElement, true);
+		}
+	}
+
+	/**
+	 * Cause the tree viewer to expand all its items
+	 */
+	public void expandAll() {
+		treeViewer.expandAll();
+	}
+
+	/**
+	 * Answer a flat collection of all of the checked elements in the list
+	 * portion of self
+	 *
+	 * @return java.util.Vector
+	 */
+	@SuppressWarnings("unchecked")
+	public Iterator getAllCheckedListItems() {
+		List result = new ArrayList();
+		Iterator listCollectionsEnum = checkedStateStore.values().iterator();
+
+		while (listCollectionsEnum.hasNext()) {
+			Iterator currentCollection = ((List) listCollectionsEnum.next())
+					.iterator();
+			while (currentCollection.hasNext()) {
+				result.add(currentCollection.next());
+			}
+		}
+
+		return result.iterator();
+	}
+
+	/**
+	 * Answer a collection of all of the checked elements in the tree portion of
+	 * self
+	 *
+	 * @return java.util.Vector
+	 */
+	public Set getAllCheckedTreeItems() {
+		return checkedStateStore.keySet();
+	}
+
+	/**
+	 * Answer the number of elements that have been checked by the user.
+	 *
+	 * @return int
+	 */
+	public int getCheckedElementCount() {
+		return checkedStateStore.size();
+	}
+
+	/**
+	 * Return a count of the number of list items associated with a given tree
+	 * item.
+	 *
+	 * @return int
+	 * @param treeElement
+	 *            java.lang.Object
+	 */
+	protected int getListItemsSize(Object treeElement) {
+		Object[] elements = listContentProvider.getElements(treeElement);
+		return elements.length;
+	}
+
+	/**
+	 * Get the table the list viewer uses.
+	 * 
+	 * @return org.eclipse.swt.widgets.Table
+	 */
+	public Table getListTable() {
+		return this.listViewer.getTable();
+	}
+
+	/**
+	 * Logically gray-check all ancestors of treeItem by ensuring that they
+	 * appear in the checked table
+	 */
+	@SuppressWarnings("unchecked")
+	protected void grayCheckHierarchy(Object treeElement) {
+
+		// if this tree element is already gray then its ancestors all are as
+		// well
+		if (checkedStateStore.containsKey(treeElement)) {
+			return; // no need to proceed upwards from here
+		}
+
+		checkedStateStore.put(treeElement, new ArrayList());
+		if (determineShouldBeWhiteChecked(treeElement)) {
+			setWhiteChecked(treeElement, true);
+		}
+		Object parent = treeContentProvider.getParent(treeElement);
+		if (parent != null) {
+			grayCheckHierarchy(parent);
+		}
+	}
+
+	/**
+	 * Set the initial checked state of the passed list element to true.
+	 *
+	 * @param element
+	 *            the element in the list to select
+	 */
+	public void initialCheckListItem(Object element) {
+		Object parent = treeContentProvider.getParent(element);
+		currentTreeSelection = parent;
+		// As this is not done from the UI then set the box for updating from
+		// the selection to false
+		listItemChecked(element, true, false);
+		updateHierarchy(parent);
+	}
+
+	/**
+	 * Set the initial checked state of the passed element to true, as well as
+	 * to all of its children and associated list elements
+	 *
+	 * @param element
+	 *            the element in the tree to select
+	 */
+	public void initialCheckTreeItem(Object element) {
+		treeItemChecked(element, true);
+	}
+
+	/**
+	 * Initialize this group's viewers after they have been laid out.
+	 */
+	protected void initialize() {
+		treeViewer.setInput(root);
+	}
+
+	/**
+	 * Callback that's invoked when the checked status of an item in the list is
+	 * changed by the user. Do not try and update the hierarchy if we are
+	 * building the initial list.
+	 */
+	@SuppressWarnings("unchecked")
+	protected void listItemChecked(Object listElement, boolean state,
+			boolean updatingFromSelection) {
+		List checkedListItems = (List) checkedStateStore
+				.get(currentTreeSelection);
+
+		if (state) {
+			if (checkedListItems == null) {
+				// since the associated tree item has gone from 0 -> 1 checked
+				// list items, tree checking may need to be updated
+				grayCheckHierarchy(currentTreeSelection);
+				checkedListItems = (List) checkedStateStore
+						.get(currentTreeSelection);
+			}
+			checkedListItems.add(listElement);
+		} else {
+			checkedListItems.remove(listElement);
+			if (checkedListItems.isEmpty()) {
+				// since the associated tree item has gone from 1 -> 0 checked
+				// list items, tree checking may need to be updated
+				ungrayCheckHierarchy(currentTreeSelection);
+			}
+		}
+
+		if (updatingFromSelection) {
+			updateHierarchy(currentTreeSelection);
+		}
+	}
+
+	/**
+	 * Notify all checked state listeners that the passed element has had its
+	 * checked state changed to the passed state
+	 */
+	protected void notifyCheckStateChangeListeners(
+			final CheckStateChangedEvent event) {
+		Object[] array = getListeners();
+		for (int i = 0; i < array.length; i++) {
+			final ICheckStateListener l = (ICheckStateListener) array[i];
+			SafeRunner.run(new SafeRunnable() {
+				@Override
+				public void run() {
+					l.checkStateChanged(event);
+				}
+			});
+		}
+	}
+
+	/**
+	 * Set the contents of the list viewer based upon the specified selected
+	 * tree element. This also includes checking the appropriate list items.
+	 *
+	 * @param treeElement
+	 *            java.lang.Object
+	 */
+	protected void populateListViewer(final Object treeElement) {
+		listViewer.setInput(treeElement);
+		List listItemsToCheck = (List) checkedStateStore.get(treeElement);
+
+		if (listItemsToCheck != null) {
+			Iterator listItemsEnum = listItemsToCheck.iterator();
+			while (listItemsEnum.hasNext()) {
+				listViewer.setChecked(listItemsEnum.next(), true);
+			}
+		}
+	}
+
+	/**
+	 * Remove the passed listener from self's collection of clients that listen
+	 * for changes to element checked states
+	 *
+	 * @param listener
+	 *            ICheckStateListener
+	 */
+	public void removeCheckStateListener(ICheckStateListener listener) {
+		removeListenerObject(listener);
+	}
+
+	/**
+	 * Handle the selection of an item in the tree viewer
+	 *
+	 * @param event
+	 *            SelectionChangedEvent
+	 */
+	@Override
+	public void selectionChanged(SelectionChangedEvent event) {
+		IStructuredSelection selection = (IStructuredSelection) event
+				.getSelection();
+		Object selectedElement = selection.getFirstElement();
+		if (selectedElement == null) {
+			currentTreeSelection = null;
+			listViewer.setInput(currentTreeSelection);
+			return;
+		}
+
+		// ie.- if not an item deselection
+		if (selectedElement != currentTreeSelection) {
+			populateListViewer(selectedElement);
+		}
+
+		currentTreeSelection = selectedElement;
+	}
+
+	/**
+	 * Select or deselect all of the elements in the tree depending on the value
+	 * of the selection boolean. Be sure to update the displayed files as well.
+	 *
+	 * @param selection
+	 *            boolean indicating whether or not to select all elements
+	 */
+	public void setAllSelections(final boolean selection) {
+
+		// Potentially long operation - show a busy cursor
+		BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), () -> {
+			setTreeChecked(root, selection);
+			listViewer.setAllChecked(selection);
+		});
+	}
+
+	/**
+	 * Set the list viewer's providers to those passed
+	 *
+	 * @param contentProvider
+	 *            ITreeContentProvider
+	 * @param labelProvider
+	 *            ILabelProvider
+	 */
+	public void setListProviders(IStructuredContentProvider contentProvider,
+			ILabelProvider labelProvider) {
+		listViewer.setContentProvider(contentProvider);
+		listViewer.setLabelProvider(labelProvider);
+	}
+
+	/**
+	 * Set the comparator that is to be applied to self's list viewer
+	 *
+	 * @param comparator
+	 *            the comparator for the list viewer
+	 */
+	public void setListComparator(ViewerComparator comparator) {
+		listViewer.setComparator(comparator);
+	}
+
+	/**
+	 * Set the root of the widget to be new Root. Regenerate all of the tables
+	 * and lists from this value.
+	 * 
+	 * @param newRoot
+	 */
+	public void setRoot(Object newRoot) {
+		this.root = newRoot;
+		initialize();
+	}
+
+	/**
+	 * Set the checked state of the passed tree element appropriately, and do so
+	 * recursively to all of its child tree elements as well
+	 */
+	@SuppressWarnings("unchecked")
+	protected void setTreeChecked(Object treeElement, boolean state) {
+
+		if (treeElement.equals(currentTreeSelection)) {
+			listViewer.setAllChecked(state);
+		}
+
+		if (state) {
+			Object[] listItems = listContentProvider.getElements(treeElement);
+			List listItemsChecked = new ArrayList();
+			for (int i = 0; i < listItems.length; ++i) {
+				listItemsChecked.add(listItems[i]);
+			}
+
+			checkedStateStore.put(treeElement, listItemsChecked);
+		} else {
+			checkedStateStore.remove(treeElement);
+		}
+
+		setWhiteChecked(treeElement, state);
+		treeViewer.setChecked(treeElement, state);
+		treeViewer.setGrayed(treeElement, false);
+
+		// now logically check/uncheck all children as well
+		Object[] children = treeContentProvider.getChildren(treeElement);
+		for (int i = 0; i < children.length; ++i) {
+			setTreeChecked(children[i], state);
+		}
+	}
+
+	/**
+	 * Set the tree viewer's providers to those passed
+	 *
+	 * @param contentProvider
+	 *            ITreeContentProvider
+	 * @param labelProvider
+	 *            ILabelProvider
+	 */
+	public void setTreeProviders(ITreeContentProvider contentProvider,
+			ILabelProvider labelProvider) {
+		treeViewer.setContentProvider(contentProvider);
+		treeViewer.setLabelProvider(labelProvider);
+	}
+
+	/**
+	 * Set the comparator that is to be applied to self's tree viewer
+	 *
+	 * @param comparator
+	 *            the comparator for the tree
+	 */
+	public void setTreeComparator(ViewerComparator comparator) {
+		treeViewer.setComparator(comparator);
+	}
+
+	/**
+	 * Adjust the collection of references to white-checked tree elements
+	 * appropriately.
+	 *
+	 * @param treeElement
+	 *            java.lang.Object
+	 * @param isWhiteChecked
+	 *            boolean
+	 */
+	@SuppressWarnings("unchecked")
+	protected void setWhiteChecked(Object treeElement, boolean isWhiteChecked) {
+		if (isWhiteChecked) {
+			if (!whiteCheckedTreeItems.contains(treeElement)) {
+				whiteCheckedTreeItems.add(treeElement);
+			}
+		} else {
+			whiteCheckedTreeItems.remove(treeElement);
+		}
+	}
+
+	/**
+	 * Handle the collapsing of an element in a tree viewer
+	 */
+	@Override
+	public void treeCollapsed(TreeExpansionEvent event) {
+		// We don't need to do anything with this
+	}
+
+	/**
+	 * Handle the expansionsion of an element in a tree viewer
+	 */
+	@SuppressWarnings("unchecked")
+	@Override
+	public void treeExpanded(TreeExpansionEvent event) {
+
+		Object item = event.getElement();
+
+		// First see if the children need to be given their checked state at
+		// all. If they've
+		// already been realized then this won't be necessary
+		if (!expandedTreeNodes.contains(item)) {
+			expandedTreeNodes.add(item);
+			checkNewTreeElements(dynamicTreeContentProvider.getChildren(item));
+			Object[] children = treeContentProvider.getElements(item);
+			for (int i = 0; i < children.length; ++i) {
+				dynamicTreeContentProvider.getElements(children[i]);
+			}
+		}
+	}
+
+	/**
+	 * Callback that's invoked when the checked status of an item in the tree is
+	 * changed by the user.
+	 */
+	protected void treeItemChecked(Object treeElement, boolean state) {
+
+		// recursively adjust all child tree elements appropriately
+		setTreeChecked(treeElement, state);
+
+		Object parent = treeContentProvider.getParent(treeElement);
+		if (parent == null) {
+			return;
+		}
+
+		// now update upwards in the tree hierarchy
+		if (state) {
+			grayCheckHierarchy(parent);
+		} else {
+			ungrayCheckHierarchy(parent);
+		}
+
+		updateHierarchy(treeElement);
+	}
+
+	/**
+	 * Logically un-gray-check all ancestors of treeItem iff appropriate.
+	 */
+	protected void ungrayCheckHierarchy(Object treeElement) {
+		if (!determineShouldBeAtLeastGrayChecked(treeElement)) {
+			checkedStateStore.remove(treeElement);
+		}
+
+		Object parent = treeContentProvider.getParent(treeElement);
+		if (parent != null) {
+			ungrayCheckHierarchy(parent);
+		}
+	}
+
+	/**
+	 * Set the checked state of self and all ancestors appropriately
+	 */
+	protected void updateHierarchy(Object treeElement) {
+
+		boolean whiteChecked = determineShouldBeWhiteChecked(treeElement);
+		boolean shouldBeAtLeastGray = determineShouldBeAtLeastGrayChecked(
+				treeElement);
+
+		treeViewer.setChecked(treeElement, shouldBeAtLeastGray);
+		setWhiteChecked(treeElement, whiteChecked);
+		if (!whiteChecked) {
+			treeViewer.setGrayed(treeElement, shouldBeAtLeastGray);
+		}
+
+		// proceed up the tree element hierarchy
+		Object parent = treeContentProvider.getParent(treeElement);
+		if (parent != null) {
+			updateHierarchy(parent);
+		}
+	}
+
+	/**
+	 * Update the selections of the tree elements in items to reflect the new
+	 * selections provided.
+	 * 
+	 * @param items
+	 *            Map with keys of Object (the tree element) and values of List
+	 *            (the selected list elements).
+	 */
+	@SuppressWarnings("unchecked")
+	public void updateSelections(final Map items) {
+
+		// Potentially long operation - show a busy cursor
+		BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), () -> {
+			// Update the store before the hierarchy to prevent updating parents
+			// before all of the children are done
+			for (Entry<?, List> entry : ((Map<Object, List>) items)
+					.entrySet()) {
+				Object key1 = entry.getKey();
+				// Replace the items in the checked state store with those from
+				// the supplied items
+				List selections = entry.getValue();
+				if (selections.size() == 0) {
+					// If it is empty remove it from the list
+					checkedStateStore.remove(key1);
+				} else {
+					checkedStateStore.put(key1, selections);
+					// proceed up the tree element hierarchy
+					Object parent = treeContentProvider.getParent(key1);
+					if (parent != null) {
+						addToHierarchyToCheckedStore(parent);
+					}
+				}
+			}
+
+			// Now update hierarchies
+			for (Entry<Object, List> entry : ((Map<Object, List>) items)
+					.entrySet()) {
+				Object key2 = entry.getKey();
+				updateHierarchy(key2);
+				if (currentTreeSelection != null
+						&& currentTreeSelection.equals(key2)) {
+					listViewer.setAllChecked(false);
+					listViewer.setCheckedElements(entry.getValue().toArray());
+				}
+			}
+		});
+
+	}
+}
+
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/ContainerFileSelectionDialog.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/ContainerFileSelectionDialog.java
new file mode 100644
index 0000000..6d2a014
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/ContainerFileSelectionDialog.java
@@ -0,0 +1,307 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat Inc. 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:
+ *     Red Hat - Initial Contribution
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.docker.ui;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.FileSystemElement;
+import org.eclipse.ui.dialogs.SelectionDialog;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.eclipse.ui.model.WorkbenchViewerComparator;
+import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
+
+/**
+ * A standard file selection dialog which solicits a list of files from the user.
+ * The <code>getResult</code> method returns the selected files.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ * <p>
+ * Example:
+ * <pre>
+ *	FileSelectionDialog dialog =
+ *		new FileSelectionDialog(getShell(), rootElement, msg);
+ *	dialog.setInitialSelections(selectedResources);
+ *	dialog.open();
+ *	return dialog.getResult();
+ * </pre>
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ContainerFileSelectionDialog extends SelectionDialog {
+    // the root file representative to populate the viewer with
+    private FileSystemElement root;
+
+	private IImportStructureProvider structureProvider;
+	static final String FILE_SELECTION_DIALOG = "org.eclipse.ui.ide.file_selection_dialog_context"; //$NON-NLS-1$
+
+    // the visual selection widget group
+    CheckboxTreeAndListGroup selectionGroup;
+
+    // expand all items in the tree view on dialog open
+    private boolean expandAllOnOpen = false;
+
+    // sizing constants
+    private static final int SIZING_SELECTION_WIDGET_WIDTH = 500;
+
+    private static final int SIZING_SELECTION_WIDGET_HEIGHT = 250;
+
+	static final String SELECT_ALL_TITLE = "SelectionDialog_selectLabel"; //$NON-NLS-1$
+	static final String DESELECT_ALL_TITLE = "SelectionDialog_deselectLabel"; //$NON-NLS-1$
+
+    /**
+     * Creates a file selection dialog rooted at the given file system element.
+     *
+     * @param parentShell the parent shell
+     * @param fileSystemElement the root element to populate this dialog with
+     * @param message the message to be displayed at the top of this dialog, or
+     *    <code>null</code> to display a default message
+     */
+    public ContainerFileSelectionDialog(Shell parentShell,
+			FileSystemElement fileSystemElement,
+			IImportStructureProvider structureProvider, String message) {
+        super(parentShell);
+		setTitle(Messages.getString("FileSelectionDialog_title")); //$NON-NLS-1$
+        root = fileSystemElement;
+		this.structureProvider = structureProvider;
+        if (message != null) {
+			setMessage(message);
+		} else {
+			setMessage(Messages.getString("FileSelectionDialog_message")); //$NON-NLS-1$
+		}
+    }
+
+    /**
+     * Add the selection and deselection buttons to the dialog.
+     * @param composite org.eclipse.swt.widgets.Composite
+     */
+    private void addSelectionButtons(Composite composite) {
+
+        Composite buttonComposite = new Composite(composite, SWT.RIGHT);
+        GridLayout layout = new GridLayout();
+        layout.numColumns = 2;
+        buttonComposite.setLayout(layout);
+        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END);
+        composite.setData(data);
+
+        Button selectButton = new Button(buttonComposite, SWT.PUSH);
+		selectButton.setText(Messages.getString(SELECT_ALL_TITLE));
+        SelectionListener listener = new SelectionAdapter() {
+            @Override
+			public void widgetSelected(SelectionEvent e) {
+                selectionGroup.setAllSelections(true);
+            }
+        };
+        selectButton.addSelectionListener(listener);
+
+        Button deselectButton = new Button(buttonComposite, SWT.PUSH);
+		deselectButton.setText(Messages.getString(DESELECT_ALL_TITLE));
+        listener = new SelectionAdapter() {
+            @Override
+			public void widgetSelected(SelectionEvent e) {
+                selectionGroup.setAllSelections(false);
+
+            }
+        };
+        deselectButton.addSelectionListener(listener);
+
+    }
+
+    /**
+     * Visually checks the previously-specified elements in the container (left)
+     * portion of this dialog's file selection viewer.
+     */
+    private void checkInitialSelections() {
+		@SuppressWarnings("rawtypes")
+		Iterator itemsToCheck = getInitialElementSelections().iterator();
+
+        while (itemsToCheck.hasNext()) {
+            FileSystemElement currentElement = (FileSystemElement) itemsToCheck
+                    .next();
+
+            if (currentElement.isDirectory()) {
+				selectionGroup.initialCheckTreeItem(currentElement);
+			} else {
+				selectionGroup.initialCheckListItem(currentElement);
+			}
+        }
+    }
+
+    @Override
+	protected void configureShell(Shell shell) {
+        super.configureShell(shell);
+        PlatformUI.getWorkbench().getHelpSystem().setHelp(shell,
+				FILE_SELECTION_DIALOG);
+    }
+
+    @Override
+	public void create() {
+        super.create();
+        initializeDialog();
+    }
+
+    @Override
+	protected Control createDialogArea(Composite parent) {
+        // page group
+        Composite composite = (Composite) super.createDialogArea(parent);
+
+        createMessageArea(composite);
+
+        // Create a fake parent of the root to be the dialog input element.
+        // Use an empty label so that display of the element's full name
+        // doesn't include a confusing label
+        FileSystemElement input = new FileSystemElement("", null, true);//$NON-NLS-1$
+        input.addChild(root);
+        root.setParent(input);
+
+        selectionGroup = new CheckboxTreeAndListGroup(composite, input,
+				getFolderProvider(), getDynamicFolderProvider(),
+				new WorkbenchLabelProvider(),
+                getFileProvider(), new WorkbenchLabelProvider(), SWT.NONE,
+                SIZING_SELECTION_WIDGET_WIDTH, // since this page has no other significantly-sized
+                SIZING_SELECTION_WIDGET_HEIGHT); // widgets we need to hardcode the combined widget's
+        // size, otherwise it will open too small
+
+        ICheckStateListener listener = event -> getOkButton().setEnabled(
+		        selectionGroup.getCheckedElementCount() > 0);
+
+        WorkbenchViewerComparator comparator = new WorkbenchViewerComparator();
+        selectionGroup.setTreeComparator(comparator);
+        selectionGroup.setListComparator(comparator);
+        selectionGroup.addCheckStateListener(listener);
+
+        addSelectionButtons(composite);
+
+        return composite;
+    }
+
+    /**
+     * Returns whether the tree view of the file system element
+     * will be fully expanded when the dialog is opened.
+     *
+     * @return true to expand all on dialog open, false otherwise.
+     */
+    public boolean getExpandAllOnOpen() {
+        return expandAllOnOpen;
+    }
+
+    /**
+     * Returns a content provider for <code>FileSystemElement</code>s that returns
+     * only files as children.
+     */
+    private ITreeContentProvider getFileProvider() {
+        return new WorkbenchContentProvider() {
+            @Override
+			public Object[] getChildren(Object o) {
+				if (o instanceof FileSystemElement) {
+					return ((FileSystemElement) o).getFiles().getChildren(o);
+                }
+
+                return new Object[0];
+            }
+        };
+    }
+
+    /**
+     * Returns a content provider for <code>FileSystemElement</code>s that returns
+     * only folders as children.
+     */
+    private ITreeContentProvider getFolderProvider() {
+        return new WorkbenchContentProvider() {
+            @Override
+			public Object[] getChildren(Object o) {
+				if (o instanceof FileSystemElement) {
+					return ((FileSystemElement) o).getFolders().getChildren(o);
+                }
+
+                return new Object[0];
+			}
+        };
+    }
+
+    /**
+	 * Returns a content provider for <code>FileSystemElement</code>s that
+	 * returns only folders as children.
+	 */
+	private ITreeContentProvider getDynamicFolderProvider() {
+		return new WorkbenchContentProvider() {
+			@Override
+			public Object[] getChildren(Object o) {
+				if (o instanceof MinimizedFileSystemElement) {
+					return ((MinimizedFileSystemElement) o)
+							.getFolders(structureProvider)
+							.getChildren(o);
+				} else if (o instanceof FileSystemElement) {
+					return ((FileSystemElement) o).getFolders().getChildren(o);
+				}
+
+				return new Object[0];
+			}
+		};
+	}
+
+	/**
+	 * Initializes this dialog's controls.
+	 */
+    private void initializeDialog() {
+        // initialize page
+        if (getInitialElementSelections().isEmpty()) {
+			getOkButton().setEnabled(false);
+		} else {
+			checkInitialSelections();
+		}
+        selectionGroup.aboutToOpen();
+        if (expandAllOnOpen) {
+			selectionGroup.expandAll();
+		}
+    }
+
+    /**
+     * The <code>FileSelectionDialog</code> implementation of this
+     * <code>Dialog</code> method builds a list of the selected files for later
+     * retrieval by the client and closes this dialog.
+     */
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	@Override
+	protected void okPressed() {
+        Iterator resultEnum = selectionGroup.getAllCheckedListItems();
+        ArrayList list = new ArrayList();
+        while (resultEnum.hasNext()) {
+			list.add(resultEnum.next());
+		}
+        setResult(list);
+        super.okPressed();
+    }
+
+    /**
+     * Set whether the tree view of the file system element
+     * will be fully expanded when the dialog is opened.
+     *
+     * @param expandAll true to expand all on dialog open, false otherwise.
+     */
+    public void setExpandAllOnOpen(boolean expandAll) {
+        expandAllOnOpen = expandAll;
+    }
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/ContainerFileSystemProvider.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/ContainerFileSystemProvider.java
new file mode 100644
index 0000000..7f49d33
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/ContainerFileSystemProvider.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat Inc. 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:
+ *     Red Hat - Initial Contribution
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.docker.ui;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.linuxtools.docker.core.DockerException;
+import org.eclipse.linuxtools.docker.core.IDockerConnection;
+import org.eclipse.linuxtools.internal.docker.core.ContainerFileProxy;
+import org.eclipse.linuxtools.internal.docker.core.DockerConnection;
+import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
+
+public class ContainerFileSystemProvider implements IImportStructureProvider {
+
+	private final IDockerConnection connection;
+	private final String containerId;
+
+	public ContainerFileSystemProvider(IDockerConnection connection,
+			String containerId) {
+		this.connection = connection;
+		this.containerId = containerId;
+	}
+
+	@SuppressWarnings("rawtypes")
+	@Override
+	public List getChildren(Object element) {
+		try {
+			ContainerFileProxy proxy = (ContainerFileProxy) element;
+			if (proxy.isFolder()) {
+				return ((DockerConnection) connection).readContainerDirectory(
+						containerId,
+						proxy.getFullPath());
+			}
+		} catch (DockerException e) {
+			// do nothing for now
+		}
+		return new ArrayList<ContainerFileProxy>();
+	}
+
+	@Override
+	public InputStream getContents(Object element) {
+		return null; // we do not have the contents of container file
+	}
+
+	@Override
+	public String getFullPath(Object element) {
+		return ((ContainerFileProxy) element).getFullPath();
+	}
+
+	@Override
+	public String getLabel(Object element) {
+		return ((ContainerFileProxy) element).getLabel();
+	}
+
+	@Override
+	public boolean isFolder(Object element) {
+		return ((ContainerFileProxy) element).isFolder();
+	}
+
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/DynamicCheckboxTreeViewer.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/DynamicCheckboxTreeViewer.java
new file mode 100644
index 0000000..e52081c
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/DynamicCheckboxTreeViewer.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat Inc. 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:
+ *     Red Hat - Initial Contribution
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.docker.ui;
+
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.swt.events.TreeEvent;
+import org.eclipse.swt.widgets.Tree;
+
+public class DynamicCheckboxTreeViewer extends CheckboxTreeViewer {
+
+	private final ITreeContentProvider dynamicProvider;
+	private boolean useDynamic;
+
+	public DynamicCheckboxTreeViewer(Tree tree,
+			ITreeContentProvider dynamicProvider) {
+		super(tree);
+		this.dynamicProvider = dynamicProvider;
+	}
+
+	public void useDynamic(boolean value) {
+		this.useDynamic = value;
+	}
+
+	@Override
+	public IContentProvider getContentProvider() {
+		if (useDynamic) {
+			return dynamicProvider;
+		}
+		return super.getContentProvider();
+	}
+
+	@Override
+	protected void handleTreeExpand(TreeEvent event) {
+		useDynamic(true);
+		super.handleTreeExpand(event);
+		useDynamic(false);
+	}
+
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/Messages.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/Messages.java
new file mode 100644
index 0000000..345dde3
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/Messages.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat.
+ * 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:
+ *     Red Hat - Initial Contribution
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.docker.ui;
+
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+public class Messages {
+
+	private static final String BUNDLE_NAME = Messages.class.getName();
+
+	public static String getString(String key) {
+		try {
+			return ResourceBundle.getBundle(BUNDLE_NAME).getString(key);
+		} catch (MissingResourceException e) {
+			return '!' + key + '!';
+		} catch (NullPointerException e) {
+			return '#' + key + '#';
+		}
+	}
+
+	public static String getFormattedString(String key, String arg) {
+		return MessageFormat.format(getString(key), new Object[] { arg });
+	}
+
+	public static String getFormattedString(String key, String[] args) {
+		return MessageFormat.format(getString(key), (Object[]) args);
+	}
+
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/Messages.properties b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/Messages.properties
new file mode 100644
index 0000000..9cf863a
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/Messages.properties
@@ -0,0 +1,6 @@
+
+TarImport_invalid_tar_format = Not a valid tar format
+FileSelectionDialog_title = File Selection
+FileSelectionDialog_message = Select the files:
+SelectionDialog_selectLabel = &Select All
+SelectionDialog_deselectLabel = &Deselect All
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/MinimizedFileSystemElement.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/MinimizedFileSystemElement.java
new file mode 100644
index 0000000..1eb503d
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/MinimizedFileSystemElement.java
@@ -0,0 +1,109 @@
+package org.eclipse.linuxtools.internal.docker.ui;
+
+/*******************************************************************************
+ * Copyright (c) 2000, 2009, 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. - modified to use with Docker Tooling
+ *******************************************************************************/
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.ui.dialogs.FileSystemElement;
+import org.eclipse.ui.model.AdaptableList;
+import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
+
+/**
+ * The <code>MinimizedFileSystemElement</code> is a
+ * <code>FileSystemElement</code> that knows if it has been populated or not.
+ */
+public class MinimizedFileSystemElement extends FileSystemElement {
+	private boolean populated = false;
+
+	/**
+	 * Create a <code>MinimizedFileSystemElement</code> with the supplied name
+	 * and parent.
+	 * 
+	 * @param name
+	 *            the name of the file element this represents
+	 * @param parent
+	 *            the containing parent
+	 * @param isDirectory
+	 *            indicated if this could have children or not
+	 */
+	public MinimizedFileSystemElement(String name, FileSystemElement parent,
+			boolean isDirectory) {
+		super(name, parent, isDirectory);
+	}
+
+	/**
+	 * Returns a list of the files that are immediate children. Use the supplied
+	 * provider if it needs to be populated. of this folder.
+	 */
+	public AdaptableList getFiles(IImportStructureProvider provider) {
+		if (!populated) {
+			populate(provider);
+		}
+		return super.getFiles();
+	}
+
+	/**
+	 * Returns a list of the folders that are immediate children. Use the
+	 * supplied provider if it needs to be populated. of this folder.
+	 */
+	public AdaptableList getFolders(IImportStructureProvider provider) {
+		if (!populated) {
+			populate(provider);
+		}
+		return super.getFolders();
+	}
+
+	/**
+	 * Return whether or not population has happened for the receiver.
+	 */
+	boolean isPopulated() {
+		return this.populated;
+	}
+
+	/**
+	 * Populate the files and folders of the receiver using the supplied
+	 * structure provider.
+	 * 
+	 * @param provider
+	 *            org.eclipse.ui.wizards.datatransfer.IImportStructureProvider
+	 */
+	private void populate(IImportStructureProvider provider) {
+
+		Object fileSystemObject = getFileSystemObject();
+
+		@SuppressWarnings("rawtypes")
+		List children = provider.getChildren(fileSystemObject);
+		if (children != null) {
+			@SuppressWarnings("rawtypes")
+			Iterator childrenEnum = children.iterator();
+			while (childrenEnum.hasNext()) {
+				Object child = childrenEnum.next();
+
+				String elementLabel = provider.getLabel(child);
+				// Create one level below
+				MinimizedFileSystemElement result = new MinimizedFileSystemElement(
+						elementLabel, this, provider.isFolder(child));
+				result.setFileSystemObject(child);
+			}
+		}
+		setPopulated();
+	}
+
+	/**
+	 * Set whether or not population has happened for the receiver to true.
+	 */
+	public void setPopulated() {
+		this.populated = true;
+	}
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/PopulateContainerFilesOperation.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/PopulateContainerFilesOperation.java
new file mode 100644
index 0000000..5e688e4
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/PopulateContainerFilesOperation.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat Inc. 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:
+ *     Red Hat - Initial Contribution
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.docker.ui;
+
+import org.eclipse.ui.dialogs.FileSystemElement;
+import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
+
+public class PopulateContainerFilesOperation extends PopulateRootOperation {
+
+	private final FileSystemElement rootParent;
+
+	public PopulateContainerFilesOperation(Object rootObject,
+			FileSystemElement rootParent,
+			IImportStructureProvider structureProvider) {
+		super(rootObject, structureProvider);
+		this.rootParent = rootParent;
+	}
+
+	@Override
+	protected FileSystemElement createElement(FileSystemElement parent,
+			Object fileSystemObject) throws InterruptedException {
+		FileSystemElement element = (parent == null ? this.rootParent : parent);
+		return super.createElement(element, fileSystemObject);
+	}
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/PopulateRootOperation.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/PopulateRootOperation.java
new file mode 100644
index 0000000..7f79328
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/PopulateRootOperation.java
@@ -0,0 +1,90 @@
+package org.eclipse.linuxtools.internal.docker.ui;
+/*******************************************************************************
+ * Copyright (c) 2000, 2014, 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. - modified to use with Docker Tooling
+ *******************************************************************************/
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.operation.ModalContext;
+import org.eclipse.ui.dialogs.FileSystemElement;
+import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
+
+/**
+ * The PopulateFilesOperation is an operation used to populate a
+ * FileSystemElement one level deep rather than the whole way.
+ */
+public class PopulateRootOperation extends SelectFilesOperation {
+	/**
+	 * Create a new <code>PopulateFilesOperation</code>.
+	 * 
+	 * @param rootObject
+	 *            the object to be populated
+	 * @param structureProvider
+	 *            the object that defines how we are to populate it.
+	 */
+	public PopulateRootOperation(Object rootObject,
+			IImportStructureProvider structureProvider) {
+		super(rootObject, structureProvider);
+	}
+
+	/**
+	 * Creates and returns a <code>FileSystemElement</code> if the specified
+	 * file system object merits one. The criteria for this are: - if the file
+	 * system object is a container then it must have either a child container
+	 * or an associated file - if the file system object is a file then it must
+	 * have an extension suitable for selection
+	 */
+	@Override
+	protected FileSystemElement createElement(FileSystemElement parent,
+			Object fileSystemObject) throws InterruptedException {
+
+		// Iterate on level deep
+		return createElement(parent, fileSystemObject, 2);
+
+	}
+
+	/**
+	 * Creates and returns a <code>FileSystemElement</code> if the specified
+	 * file system object merits one. The criteria for this are: - if the file
+	 * system object is a container then it must have either a child container
+	 * or an associated file - if the file system object is a file then it must
+	 * have an extension suitable for selection recurse down for depth to
+	 * populate children
+	 */
+	@SuppressWarnings("rawtypes")
+	protected FileSystemElement createElement(FileSystemElement parent,
+			Object fileSystemObject, int depth) throws InterruptedException {
+		ModalContext.checkCanceled(monitor);
+		boolean isContainer = provider.isFolder(fileSystemObject);
+		String elementLabel = parent == null
+				? provider.getFullPath(fileSystemObject)
+				: provider.getLabel(fileSystemObject);
+
+		MinimizedFileSystemElement result = new MinimizedFileSystemElement(
+				elementLabel, parent, isContainer);
+		result.setFileSystemObject(fileSystemObject);
+
+		if (isContainer && depth > 0) {
+			List children = provider.getChildren(fileSystemObject);
+			if (children != null) {
+				Iterator childrenEnum = children.iterator();
+				while (childrenEnum.hasNext()) {
+					createElement(result, childrenEnum.next(), depth - 1);
+				}
+			}
+			result.setPopulated();
+		}
+
+
+		return result;
+	}
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/SelectFilesOperation.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/SelectFilesOperation.java
new file mode 100644
index 0000000..9f4a099
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/SelectFilesOperation.java
@@ -0,0 +1,171 @@
+package org.eclipse.linuxtools.internal.docker.ui;
+/*******************************************************************************
+ * Copyright (c) 2000, 2014, 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. - copied to Docker Tooling
+ *******************************************************************************/
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.operation.ModalContext;
+import org.eclipse.linuxtools.internal.docker.ui.jobs.JobMessages;
+import org.eclipse.ui.dialogs.FileSystemElement;
+import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
+
+/**
+ * Operation responsible for traversing a specified file system position
+ * recursively and building - a tree that represents the container structure - a
+ * collection containing all files meeting a specified extension criteria
+ *
+ * This is implemented as an Operation in order to provide an escape to the user
+ * (the Cancel button) if the operation drags on for too long
+ */
+public class SelectFilesOperation implements IRunnableWithProgress {
+	IProgressMonitor monitor;
+
+	Object root;
+
+	IImportStructureProvider provider;
+
+	String desiredExtensions[];
+
+	FileSystemElement result;
+
+	private static String POPULATING_CONTAINER_FILES = "PopulateContainerFiles.msg"; //$NON-NLS-1$
+
+	/**
+	 * Creates a new <code>SelectFilesOperation</code>.
+	 */
+	public SelectFilesOperation(Object rootObject,
+			IImportStructureProvider structureProvider) {
+		super();
+		root = rootObject;
+		provider = structureProvider;
+	}
+
+	/**
+	 * Creates and returns a <code>FileSystemElement</code> if the specified
+	 * file system object merits one. The criteria for this are: - if the file
+	 * system object is a container then it must have either a child container
+	 * or an associated file - if the file system object is a file then it must
+	 * have an extension suitable for selection
+	 */
+	@SuppressWarnings("rawtypes")
+	protected FileSystemElement createElement(FileSystemElement parent,
+			Object fileSystemObject) throws InterruptedException {
+		ModalContext.checkCanceled(monitor);
+		boolean isContainer = provider.isFolder(fileSystemObject);
+		String elementLabel = parent == null
+				? provider.getFullPath(fileSystemObject)
+				: provider.getLabel(fileSystemObject);
+
+		if (!isContainer && !hasDesiredExtension(elementLabel)) {
+			return null;
+		}
+
+		FileSystemElement result = new FileSystemElement(elementLabel, parent,
+				isContainer);
+		result.setFileSystemObject(fileSystemObject);
+
+		if (isContainer) {
+			boolean haveChildOrFile = false;
+			List children = provider.getChildren(fileSystemObject);
+			if (children == null) {
+				children = new ArrayList(1);
+			}
+			Iterator childrenEnum = children.iterator();
+			while (childrenEnum.hasNext()) {
+				if (createElement(result, childrenEnum.next()) != null) {
+					haveChildOrFile = true;
+				}
+			}
+
+			if (!haveChildOrFile && parent != null) {
+				parent.removeFolder(result);
+				result = null;
+			}
+		}
+
+		return result;
+	}
+
+	/**
+	 * Returns the extension portion of the passed filename string.
+	 */
+	protected String getExtensionFor(String filename) {
+		int nIndex = filename.lastIndexOf('.');
+
+		if (nIndex >= 0) {
+			return filename.substring(nIndex + 1);
+		}
+
+		return "";//$NON-NLS-1$
+
+	}
+
+	/**
+	 * Returns the resulting root file system element.
+	 */
+	public FileSystemElement getResult() {
+		return result;
+	}
+
+	/**
+	 * Returns a boolean indicating whether the extension of the passed filename
+	 * is one of the extensions specified as desired by the filter.
+	 */
+	protected boolean hasDesiredExtension(String filename) {
+		if (desiredExtensions == null) {
+			return true;
+		}
+
+		int extensionsSize = desiredExtensions.length;
+		for (int i = 0; i < extensionsSize; i++) {
+			if (getExtensionFor(filename)
+					.equalsIgnoreCase(desiredExtensions[i])) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Runs the operation.
+	 */
+	@Override
+	public void run(IProgressMonitor monitor) throws InterruptedException {
+		try {
+			this.monitor = monitor;
+			monitor.beginTask(
+					JobMessages.getString(POPULATING_CONTAINER_FILES),
+					IProgressMonitor.UNKNOWN);
+			result = createElement(null, root);
+			if (result == null) {
+				result = new FileSystemElement(provider.getLabel(root), null,
+						provider.isFolder(root));
+				result.setFileSystemObject(root);
+			}
+		} finally {
+			monitor.done();
+		}
+	}
+
+	/**
+	 * Sets the file extensions which are desired. A value of <code>null</code>
+	 * indicates that all files should be kept regardless of extension.
+	 */
+	public void setDesiredExtensions(String[] extensions) {
+		desiredExtensions = extensions;
+	}
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/TarEntry.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/TarEntry.java
new file mode 100644
index 0000000..ef4d54d
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/TarEntry.java
@@ -0,0 +1,145 @@
+package org.eclipse.linuxtools.internal.docker.ui;
+
+/*******************************************************************************
+ * Copyright (c) 2004, 2015, 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. - copied over to Docker Tooling
+ *******************************************************************************/
+
+/**
+ * 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
+	 */
+	public void setFileType(int type) {
+		this.type = type;
+	}
+
+	/**
+	 * Sets the mode of the file in UNIX permissions format.
+	 *
+	 * @param mode
+	 */
+	public void setMode(long mode) {
+		this.mode = mode;
+	}
+
+	/**
+	 * Sets the size of the file in bytes.
+	 *
+	 * @param size
+	 */
+	public void setSize(long size) {
+		this.size = size;
+	}
+
+	/**
+	 * Sets the modification time of the file in seconds since January 1st 1970.
+	 *
+	 * @param time
+	 */
+	public void setTime(long time) {
+		this.time = time;
+	}
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/TarException.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/TarException.java
new file mode 100644
index 0000000..ae1bc8b
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/TarException.java
@@ -0,0 +1,44 @@
+package org.eclipse.linuxtools.internal.docker.ui;
+/*******************************************************************************
+ * Copyright (c) 2004, 2015, 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. - used in Docker Tooling
+ *******************************************************************************/
+
+/**
+ * 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 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/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/TarInputStream.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/TarInputStream.java
new file mode 100644
index 0000000..4da621e
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/TarInputStream.java
@@ -0,0 +1,347 @@
+package org.eclipse.linuxtools.internal.docker.ui;
+/*******************************************************************************
+ * Copyright (c) 2004, 2015, 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. - modification to use in Docker UI plug-in
+ *******************************************************************************/
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * 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.
+ */
+public class TarInputStream extends FilterInputStream {
+	private final static String TarImport_invalid_tar_format = "TarImport_invalid_tar_format"; //$NON-NLS-1$
+	private int nextEntry = 0;
+	private int nextEOF = 0;
+	private int filepos = 0;
+	private int 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
+	 * @throws IOException
+	 */
+	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 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 {
+		int 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 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(
+					Messages.getString(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(
+					Messages.getString(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
+	 * @throws IOException
+	 */
+	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 bytesread = 0;
+			while (bytesread < longNameData.length) {
+				int cur = read(longNameData, bytesread,
+						longNameData.length - bytesread);
+				if (cur < 0) {
+					throw new IOException("early end of stream"); //$NON-NLS-1$
+				}
+				bytesread += 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 {
+		if (nextEOF == 0) {
+			return -1;
+		}
+		if (len > nextEOF) {
+			len = nextEOF;
+		}
+		int size = super.read(b, off, len);
+		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/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/CommandMessages.properties b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/CommandMessages.properties
index 981ba58..f5c96a0 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/CommandMessages.properties
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/CommandMessages.properties
@@ -10,7 +10,13 @@
 command.showIn.systemExplorer.failure.execute=Failed to execute {0}. Return code was: {1}
 
 missing_connection=Missing connection
-command.pullImage.failure.no_connection=Unable to pull an image: no connection was not found in the selection.
+command.pullImage.failure.no_connection=Unable to pull an image: no connection was found in the selection.
+
+command.copyfromcontainer.failure.no_connection=Unable to copy from image: no conection was found.
+command.copyfromcontainer.job.title=Copying files from {0}
+command.copyfromcontainer.job.task=Copying files
+command.copyfromcontainer.job.subtask=Copying {0}
+command.copyfromcontainer.error.msg=Error copying [{0}] from <{1}>
 
 command.enableconnection=Opening connection to {0} 
 command.enableconnection.failure=Failed to connect to {0}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/CopyFromContainerCommandHandler.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/CopyFromContainerCommandHandler.java
new file mode 100644
index 0000000..0161625
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/commands/CopyFromContainerCommandHandler.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat Inc. 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:
+ *     Red Hat - Initial Contribution
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.docker.ui.commands;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.linuxtools.docker.core.DockerException;
+import org.eclipse.linuxtools.docker.core.IDockerConnection;
+import org.eclipse.linuxtools.docker.core.IDockerContainer;
+import org.eclipse.linuxtools.docker.ui.Activator;
+import org.eclipse.linuxtools.docker.ui.wizards.ImageSearch;
+import org.eclipse.linuxtools.internal.docker.core.ContainerFileProxy;
+import org.eclipse.linuxtools.internal.docker.core.DockerConnection;
+import org.eclipse.linuxtools.internal.docker.ui.TarEntry;
+import org.eclipse.linuxtools.internal.docker.ui.TarException;
+import org.eclipse.linuxtools.internal.docker.ui.TarInputStream;
+import org.eclipse.linuxtools.internal.docker.ui.wizards.ContainerCopyFrom;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * Command handler that opens the {@link ImageSearch} wizard and pulls the
+ * selected image in background on completion.
+ *
+ */
+public class CopyFromContainerCommandHandler extends AbstractHandler {
+
+	private static final String ERROR_COPYING_FROM_CONTAINER_NO_CONNECTION = "command.copyfromcontainer.failure.no_connection"; //$NON-NLS-1$
+	private static final String MISSING_CONNECTION = "missing_connection"; //$NON-NLS-1$
+	private static final String ERROR_COPYING_FROM_CONTAINER = "command.copyfromcontainer.error.msg"; //$NON-NLS-1$
+	private static final String COPY_FROM_CONTAINER_JOB_TASK = "command.copyfromcontainer.job.task"; //$NON-NLS-1$
+	private static final String COPY_FROM_CONTAINER_JOB_TITLE = "command.copyfromcontainer.job.title"; //$NON-NLS-1$
+	private static final String COPY_FROM_CONTAINER_JOB_SUBTASK = "command.copyfromcontainer.job.subtask"; //$NON-NLS-1$
+
+	@Override
+	public Object execute(final ExecutionEvent event) {
+		final IWorkbenchPart activePart = HandlerUtil.getActivePart(event);
+		final IDockerConnection connection = CommandUtils
+				.getCurrentConnection(activePart);
+		final List<IDockerContainer> selectedContainers = CommandUtils
+				.getSelectedContainers(activePart);
+		if (selectedContainers.size() != 1) {
+			return null;
+		}
+		final IDockerContainer container = selectedContainers.get(0);
+		if (connection == null) {
+			MessageDialog.openError(
+					PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+							.getShell(),
+					CommandMessages.getString(MISSING_CONNECTION),
+					CommandMessages
+							.getString(
+									ERROR_COPYING_FROM_CONTAINER_NO_CONNECTION));
+		} else {
+			final ContainerCopyFrom wizard = new ContainerCopyFrom(connection,
+					container);
+			final boolean copyFromContainer = CommandUtils.openWizard(wizard,
+					HandlerUtil.getActiveShell(event));
+			if (copyFromContainer) {
+				performCopyFromContainer(connection, container,
+						wizard.getTarget(), wizard.getSources());
+			}
+		}
+		return null;
+	}
+
+	private void performCopyFromContainer(final IDockerConnection connection,
+			final IDockerContainer container, final String target,
+			final List<ContainerFileProxy> files) {
+		final Job copyFromContainerJob = new Job(
+				CommandMessages.getFormattedString(
+				COPY_FROM_CONTAINER_JOB_TITLE, container.name())) {
+
+			@Override
+			protected IStatus run(final IProgressMonitor monitor) {
+				monitor.beginTask(
+						CommandMessages.getString(COPY_FROM_CONTAINER_JOB_TASK),
+						files.size());
+				try {
+					for (ContainerFileProxy proxy : files) {
+						if (monitor.isCanceled()) {
+							monitor.done();
+							return Status.CANCEL_STATUS;
+						}
+						try {
+							monitor.setTaskName(
+									CommandMessages.getFormattedString(
+											COPY_FROM_CONTAINER_JOB_SUBTASK,
+											proxy.getFullPath()));
+							monitor.worked(1);
+							TarInputStream k = new TarInputStream(
+									((DockerConnection) connection)
+											.copyContainer(container.id(),
+													proxy.getLink()));
+							TarEntry te = k.getNextEntry();
+							long size = te.getSize();
+							IPath path = new Path(target);
+							path = path.append(proxy.getName());
+							File f = new File(path.toOSString());
+							f.createNewFile();
+							FileOutputStream os = new FileOutputStream(f);
+							if (size > 4096)
+								size = 4096;
+							byte[] barray = new byte[(int) size];
+							while (k.read(barray) > 0) {
+								if (monitor.isCanceled()) {
+									monitor.done();
+									k.close();
+									os.close();
+									return Status.CANCEL_STATUS;
+								}
+								os.write(barray);
+							}
+							k.close();
+							os.close();
+						} catch (final DockerException e) {
+							Display.getDefault()
+									.syncExec(() -> MessageDialog.openError(
+											PlatformUI.getWorkbench()
+													.getActiveWorkbenchWindow()
+													.getShell(),
+											CommandMessages.getFormattedString(
+													ERROR_COPYING_FROM_CONTAINER,
+													proxy.getLink(),
+													container.name()),
+											e.getMessage()));
+							// for now
+						}
+					}
+				} catch (InterruptedException e) {
+					// do nothing
+				} catch (TarException | IOException e) {
+					Activator.log(e);
+				} finally {
+					monitor.done();
+				}
+				return Status.OK_STATUS;
+			}
+
+		};
+
+		copyFromContainerJob.schedule();
+
+	}
+
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/jobs/JobMessages.properties b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/jobs/JobMessages.properties
index 0aee2c1..1d8e660 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/jobs/JobMessages.properties
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/jobs/JobMessages.properties
@@ -20,3 +20,4 @@
 DockerComposeStop.title=Stopping Docker Compose...
 DockerComposeStop.error=Docker Compose failed to stop
 RetrieveImageHierarchyJob.error=The selected element is not an Image nor a Container
+PopulateContainerFiles.msg=Populating container files...
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ContainerCopyFrom.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ContainerCopyFrom.java
new file mode 100644
index 0000000..b52d1d1
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ContainerCopyFrom.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat.
+ * 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:
+ *     Red Hat - Initial Contribution
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.docker.ui.wizards;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.linuxtools.docker.core.IDockerConnection;
+import org.eclipse.linuxtools.docker.core.IDockerContainer;
+import org.eclipse.linuxtools.docker.ui.Activator;
+import org.eclipse.linuxtools.internal.docker.core.ContainerFileProxy;
+import org.eclipse.linuxtools.internal.docker.core.DockerConnection;
+import org.eclipse.linuxtools.internal.docker.ui.ContainerFileSystemProvider;
+import org.eclipse.linuxtools.internal.docker.ui.MinimizedFileSystemElement;
+import org.eclipse.linuxtools.internal.docker.ui.PopulateContainerFilesOperation;
+
+public class ContainerCopyFrom extends Wizard {
+
+	private DockerConnection connection;
+	private IDockerContainer container;
+	private String target;
+	private List<ContainerFileProxy> sources;
+	private ContainerCopyFromPage mainPage;
+
+	public ContainerCopyFrom(IDockerConnection connection,
+			IDockerContainer container) {
+		this.connection = (DockerConnection) connection;
+		this.container = container;
+	}
+
+	public String getTarget() {
+		return target;
+	}
+
+	public List<ContainerFileProxy> getSources() {
+		return sources;
+	}
+
+	@Override
+	public void addPages() {
+		ProgressMonitorDialog pd = new ProgressMonitorDialog(
+				Activator.getActiveWorkbenchShell());
+		ContainerFileSystemProvider provider = new ContainerFileSystemProvider(
+				connection, container.id());
+		PopulateContainerFilesOperation sfo = new PopulateContainerFilesOperation(
+				new ContainerFileProxy("", "", true), //$NON-NLS-1$ //$NON-NLS-2$
+				null, provider);
+		try {
+			pd.run(true, true, sfo);
+		} catch (InvocationTargetException | InterruptedException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+
+		mainPage = new ContainerCopyFromPage(sfo.getResult(), provider,
+				container.name());
+		addPage(mainPage);
+	}
+
+	@Override
+	public boolean canFinish() {
+		return mainPage.isPageComplete();
+	}
+
+	@Override
+	public boolean performFinish() {
+		target = mainPage.getTarget();
+		@SuppressWarnings("rawtypes")
+		Iterator iterator = mainPage.getValueIterator();
+		ArrayList<ContainerFileProxy> copyList = new ArrayList<>();
+		while (iterator.hasNext()) {
+			MinimizedFileSystemElement e = (MinimizedFileSystemElement) iterator
+					.next();
+			ContainerFileProxy p = (ContainerFileProxy) e.getFileSystemObject();
+			copyList.add(p);
+		}
+		sources = copyList;
+		return true;
+	}
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ContainerCopyFromPage.java b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ContainerCopyFromPage.java
new file mode 100644
index 0000000..27b4a64
--- /dev/null
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/ContainerCopyFromPage.java
@@ -0,0 +1,292 @@
+/*******************************************************************************
+ * Copyright (c) 2016 Red Hat.
+ * 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:
+ *     Red Hat - Initial Contribution
+ *******************************************************************************/
+package org.eclipse.linuxtools.internal.docker.ui.wizards;
+
+import java.io.File;
+import java.util.Iterator;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.linuxtools.docker.ui.Activator;
+import org.eclipse.linuxtools.internal.docker.ui.CheckboxTreeAndListGroup;
+import org.eclipse.linuxtools.internal.docker.ui.MinimizedFileSystemElement;
+import org.eclipse.linuxtools.internal.docker.ui.SWTImagesFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.dialogs.FileSystemElement;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.eclipse.ui.model.WorkbenchViewerComparator;
+import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider;
+
+/**
+ * A standard file selection dialog which solicits a list of files from the user.
+ * The <code>getResult</code> method returns the selected files.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ * <p>
+ * Example:
+ * <pre>
+ *	FileSelectionDialog dialog =
+ *		new FileSelectionDialog(getShell(), rootElement, msg);
+ *	dialog.setInitialSelections(selectedResources);
+ *	dialog.open();
+ *	return dialog.getResult();
+ * </pre>
+ * </p>
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ContainerCopyFromPage extends WizardPage {
+
+	private final static String NAME = "ContainerCopyFrom.name"; //$NON-NLS-1$
+	private final static String TITLE = "ContainerCopyFrom.title"; //$NON-NLS-1$
+	private final static String DESC = "ContainerCopyFrom.desc"; //$NON-NLS-1$
+	private static final String TARGET_LABEL = "ContainerCopyFrom.target.label"; //$NON-NLS-1$
+	private static final String TARGET_TOOLTIP = "ContainerCopyFrom.target.tooltip"; //$NON-NLS-1$
+	private static final String NO_TARGET_SPECIFIED = "ContainerCopyFrom.notarget.error"; //$NON-NLS-1$
+	private static final String BROWSE_LABEL = "ContainerCopyFrom.browse.label"; //$NON-NLS-1$
+
+	// the root file representative to populate the viewer with
+    private FileSystemElement root;
+
+	private IImportStructureProvider structureProvider;
+
+    // the visual selection widget group
+    CheckboxTreeAndListGroup selectionGroup;
+
+	private Text targetText;
+	private Button browseButton;
+
+	private String target;
+
+    // sizing constants
+    private static final int SIZING_SELECTION_WIDGET_WIDTH = 500;
+
+    private static final int SIZING_SELECTION_WIDGET_HEIGHT = 250;
+	private static final int COLUMNS = 3;
+
+    /**
+	 * Wizard page for copying files from container
+	 * 
+	 * @param fileSystemElement
+	 *            - FileSystemElement of root
+	 * @param structureProvider
+	 *            - IImportStructureProvider to get file system structure from
+	 *            container
+	 * @param containerName
+	 *            - name of container
+	 */
+	public ContainerCopyFromPage(FileSystemElement fileSystemElement,
+			IImportStructureProvider structureProvider, String containerName) {
+		super(WizardMessages.getString(NAME));
+		setDescription(WizardMessages.getFormattedString(DESC,
+				containerName));
+		setTitle(WizardMessages.getString(TITLE));
+		setImageDescriptor(SWTImagesFactory.DESC_WIZARD);
+        root = fileSystemElement;
+		this.structureProvider = structureProvider;
+    }
+
+
+	/*
+	 * Return the host directory for the copy operation
+	 */
+	public String getTarget() {
+		return target;
+	}
+
+	/*
+	 * Get an iterator for the selected items to copy
+	 */
+	@SuppressWarnings("rawtypes")
+	public Iterator getValueIterator() {
+		// TODO: look at also returning all checked table items and
+		// remove files from that directory to shorten the
+		// time taken to copy (i.e. copy the whole directory
+		// at once).
+		return selectionGroup.getAllCheckedListItems();
+	}
+
+	private ModifyListener Listener = e -> validate();
+	private ICheckStateListener CheckListener = e -> validate();
+
+	private void validate() {
+		boolean complete = true;
+		boolean error = false;
+
+		if (targetText.getText().length() == 0) {
+			error = true;
+			setErrorMessage(WizardMessages.getString(NO_TARGET_SPECIFIED));
+		} else {
+			File f = new File(targetText.getText());
+			if (!f.exists()) {
+				error = true;
+				setErrorMessage(WizardMessages.getString(NO_TARGET_SPECIFIED));
+			}
+		}
+
+		if (selectionGroup.getCheckedElementCount() == 0)
+			complete = false;
+
+		if (!error) {
+			target = targetText.getText();
+			setErrorMessage(null);
+		}
+		setPageComplete(complete && !error);
+	}
+
+    @Override
+	public void createControl(Composite parent) {
+        // page group
+		Composite composite = new Composite(parent, SWT.NULL);
+		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(1, 1)
+				.grab(true, false).applyTo(composite);
+		GridLayoutFactory.fillDefaults().numColumns(COLUMNS).margins(6, 6)
+				.applyTo(composite);
+
+		Label targetLabel = new Label(composite, SWT.NULL);
+		targetLabel.setText(WizardMessages.getString(TARGET_LABEL));
+
+		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(1, 1)
+				.grab(false, false).applyTo(targetLabel);
+
+		targetText = new Text(composite, SWT.BORDER | SWT.SINGLE);
+		targetText.addModifyListener(Listener);
+		targetText.setToolTipText(WizardMessages.getString(TARGET_TOOLTIP));
+
+		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(1, 1)
+				.grab(true, false).applyTo(targetText);
+
+		browseButton = new Button(composite, SWT.NONE);
+		browseButton.setText(WizardMessages.getString(BROWSE_LABEL));
+		browseButton.addSelectionListener(onBrowseSelect());
+
+		GridDataFactory.fillDefaults().align(SWT.END, SWT.FILL).span(1, 1)
+				.grab(false, false).applyTo(browseButton);
+
+		// Create a fake parent of the root to be the dialog input element.
+        // Use an empty label so that display of the element's full name
+        // doesn't include a confusing label
+        FileSystemElement input = new FileSystemElement("", null, true);//$NON-NLS-1$
+        input.addChild(root);
+        root.setParent(input);
+
+		Composite selectionComposite = new Composite(composite, SWT.NULL);
+		GridLayout selectionLayout = new GridLayout();
+		selectionComposite.setLayout(selectionLayout);
+
+		selectionGroup = new CheckboxTreeAndListGroup(selectionComposite, input,
+				getFolderProvider(), getDynamicFolderProvider(),
+				new WorkbenchLabelProvider(),
+                getFileProvider(), new WorkbenchLabelProvider(), SWT.NONE,
+                SIZING_SELECTION_WIDGET_WIDTH, // since this page has no other significantly-sized
+                SIZING_SELECTION_WIDGET_HEIGHT); // widgets we need to hardcode the combined widget's
+        // size, otherwise it will open too small
+
+		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).span(3, 1)
+				.grab(true, false).applyTo(selectionComposite);
+
+        WorkbenchViewerComparator comparator = new WorkbenchViewerComparator();
+        selectionGroup.setTreeComparator(comparator);
+        selectionGroup.setListComparator(comparator);
+		selectionGroup.addCheckStateListener(CheckListener);
+
+		setControl(composite);
+		validate();
+		setPageComplete(false);
+
+		selectionGroup.aboutToOpen();
+    }
+
+	private SelectionListener onBrowseSelect() {
+		final ContainerCopyFromPage page = this;
+		return new SelectionAdapter() {
+			@Override
+			public void widgetSelected(final SelectionEvent e) {
+				DirectoryDialog d = new DirectoryDialog(
+						Activator.getActiveWorkbenchShell());
+				String x = d.open();
+				if (x != null) {
+					page.targetText.setText(x);
+				}
+			}
+		};
+
+	}
+    /**
+     * Returns a content provider for <code>FileSystemElement</code>s that returns
+     * only files as children.
+     */
+    private ITreeContentProvider getFileProvider() {
+        return new WorkbenchContentProvider() {
+            @Override
+			public Object[] getChildren(Object o) {
+				if (o instanceof FileSystemElement) {
+					return ((FileSystemElement) o).getFiles().getChildren(o);
+                }
+
+                return new Object[0];
+            }
+        };
+    }
+
+    /**
+     * Returns a content provider for <code>FileSystemElement</code>s that returns
+     * only folders as children.
+     */
+    private ITreeContentProvider getFolderProvider() {
+        return new WorkbenchContentProvider() {
+            @Override
+			public Object[] getChildren(Object o) {
+				if (o instanceof FileSystemElement) {
+					return ((FileSystemElement) o).getFolders().getChildren(o);
+                }
+
+                return new Object[0];
+			}
+        };
+    }
+
+    /**
+	 * Returns a content provider for <code>FileSystemElement</code>s that
+	 * returns only folders as children.
+	 */
+	private ITreeContentProvider getDynamicFolderProvider() {
+		return new WorkbenchContentProvider() {
+			@Override
+			public Object[] getChildren(Object o) {
+				if (o instanceof MinimizedFileSystemElement) {
+					return ((MinimizedFileSystemElement) o)
+							.getFolders(structureProvider)
+							.getChildren(o);
+				} else if (o instanceof FileSystemElement) {
+					return ((FileSystemElement) o).getFolders().getChildren(o);
+				}
+
+				return new Object[0];
+			}
+		};
+	}
+
+}
diff --git a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/WizardMessages.properties b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/WizardMessages.properties
index 45f0b51..eda9108 100644
--- a/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/WizardMessages.properties
+++ b/containers/org.eclipse.linuxtools.docker.ui/src/org/eclipse/linuxtools/internal/docker/ui/wizards/WizardMessages.properties
@@ -164,6 +164,14 @@
 ContainerCommit.title=Commit Container
 ContainerCommit.name=Commit Container
 
+ContainerCopyFrom.desc=Copy files from container <{0}> to host directory
+ContainerCopyFrom.title=Copy From Container
+ContainerCopyFrom.name=Copy From Container
+ContainerCopyFrom.target.label=Host Directory
+ContainerCopyFrom.target.tooltip=Specify a directory on host to copy files to
+ContainerCopyFrom.notarget.error=A valid host directory must be specified
+ContainerCopyFrom.browse.label=Browse...
+ 
 ImageName.toolTip=Enter name for image (either REPOSITORY or REPOSITORY:TAG)
 
 ErrorInvalidRepo.msg=Image name is invalid