1 /*******************************************************************************
2  * Copyright (c) 2009, 2017 Cloudsmith Inc. and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     Cloudsmith Inc. - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.equinox.internal.p2.touchpoint.natives.actions;
15 
16 import java.io.*;
17 import java.util.ArrayList;
18 import java.util.Map;
19 import org.eclipse.core.runtime.IStatus;
20 import org.eclipse.core.runtime.Status;
21 import org.eclipse.equinox.internal.p2.engine.Profile;
22 import org.eclipse.equinox.internal.p2.touchpoint.natives.*;
23 import org.eclipse.equinox.p2.engine.spi.ProvisioningAction;
24 import org.eclipse.equinox.p2.metadata.IArtifactKey;
25 import org.eclipse.equinox.p2.metadata.IInstallableUnit;
26 import org.eclipse.osgi.util.NLS;
27 
28 /**
29  * Copies from PARM_COPY_SOURCE to PARAM_COPY_TARGET
30  * The optional parameter PARAM_COPY_OVERWRITE overwrites and existing file if set to true, else
31  * and existing file with the same name is an error. The default is false.
32  * If the source is a directory, a merge copy to the target is performed.
33  * Copy will copy files and directories (recursively).
34  *
35  */
36 public class CopyAction extends ProvisioningAction {
37 	public static final String ID = "cp"; //$NON-NLS-1$
38 
39 	@Override
execute(Map<String, Object> parameters)40 	public IStatus execute(Map<String, Object> parameters) {
41 		return copy(parameters, true);
42 	}
43 
44 	/** Perform the copy.
45 	 *
46 	 * @param parameters action parameters
47 	 * @param restoreable  flag indicating if the operation should be backed up
48 	 * @return status
49 	 */
copy(Map<String, Object> parameters, boolean restoreable)50 	public static IStatus copy(Map<String, Object> parameters, boolean restoreable) {
51 		String target = (String) parameters.get(ActionConstants.PARM_COPY_TARGET);
52 		IBackupStore backupStore = restoreable ? (IBackupStore) parameters.get(NativeTouchpoint.PARM_BACKUP) : null;
53 
54 		if (target == null)
55 			return new Status(IStatus.ERROR, Activator.ID, IStatus.OK, NLS.bind(Messages.param_not_set, ActionConstants.PARM_COPY_TARGET, ID), null);
56 
57 		String source = (String) parameters.get(ActionConstants.PARM_COPY_SOURCE);
58 		if (source == null)
59 			return new Status(IStatus.ERROR, Activator.ID, IStatus.OK, NLS.bind(Messages.param_not_set, ActionConstants.PARM_COPY_SOURCE, ID), null);
60 
61 		String overwrite = (String) parameters.get(ActionConstants.PARM_COPY_OVERWRITE);
62 		Profile profile = (Profile) parameters.get(ActionConstants.PARM_PROFILE);
63 		IInstallableUnit iu = (IInstallableUnit) parameters.get(ActionConstants.PARM_IU);
64 
65 		String originalSource = source;
66 		if (source.equals(ActionConstants.PARM_AT_ARTIFACT)) {
67 			String artifactLocation = (String) parameters.get(NativeTouchpoint.PARM_ARTIFACT_LOCATION);
68 			if (artifactLocation == null) {
69 				IArtifactKey artifactKey = (IArtifactKey) parameters.get(NativeTouchpoint.PARM_ARTIFACT);
70 				return Util.createError(NLS.bind(Messages.artifact_not_available, artifactKey));
71 			}
72 			source = artifactLocation;
73 		}
74 
75 		File sourceFile = new File(source);
76 		File targetFile = new File(target);
77 		File[] copiedFiles = null;
78 		try {
79 			copiedFiles = mergeCopy(sourceFile, targetFile, Boolean.parseBoolean(overwrite), backupStore);
80 		} catch (IOException e) {
81 			return new Status(IStatus.ERROR, Activator.ID, IStatus.OK, NLS.bind(Messages.copy_failed, sourceFile.getPath()), e);
82 		}
83 		// keep copied file in the profile as memento for CleanupCopy
84 		StringBuffer copiedFileNameBuffer = new StringBuffer();
85 		for (File copiedFile : copiedFiles) {
86 			copiedFileNameBuffer.append(copiedFile.getAbsolutePath()).append(ActionConstants.PIPE);
87 		}
88 
89 		profile.setInstallableUnitProperty(iu, "copied" + ActionConstants.PIPE + originalSource + ActionConstants.PIPE + target, copiedFileNameBuffer.toString()); //$NON-NLS-1$
90 
91 		return Status.OK_STATUS;
92 	}
93 
94 	@Override
undo(Map<String, Object> parameters)95 	public IStatus undo(Map<String, Object> parameters) {
96 		return CleanupcopyAction.cleanupcopy(parameters, false);
97 	}
98 
99 	/**
100 	 * Merge-copy file or directory.
101 	 * @param source
102 	 * @param target
103 	 * @param overwrite
104 	 * @throws IOException
105 	 */
mergeCopy(File source, File target, boolean overwrite, IBackupStore backupStore)106 	private static File[] mergeCopy(File source, File target, boolean overwrite, IBackupStore backupStore) throws IOException {
107 		ArrayList<File> copiedFiles = new ArrayList<>();
108 		xcopy(copiedFiles, source, target, overwrite, backupStore);
109 		return copiedFiles.toArray(new File[copiedFiles.size()]);
110 	}
111 
112 	/**
113 	 * Merge-copy file or directory.
114 	 * @param copiedFiles - ArrayList where copied files are collected
115 	 * @param source
116 	 * @param target
117 	 * @param overwrite
118 	 * @throws IOException
119 	 */
xcopy(ArrayList<File> copiedFiles, File source, File target, boolean overwrite, IBackupStore backupStore)120 	private static void xcopy(ArrayList<File> copiedFiles, File source, File target, boolean overwrite, IBackupStore backupStore) throws IOException {
121 		if (!source.exists())
122 			throw new IOException("Source: " + source + "does not exists"); //$NON-NLS-1$//$NON-NLS-2$
123 
124 		if (source.isDirectory()) {
125 			if (target.exists() && target.isFile()) {
126 				if (!overwrite)
127 					throw new IOException("Target: " + target + " already exists"); //$NON-NLS-1$//$NON-NLS-2$
128 				if (backupStore != null)
129 					backupStore.backup(target);
130 				else
131 					target.delete();
132 			}
133 			if (!target.exists())
134 				target.mkdirs();
135 			copiedFiles.add(target);
136 			File[] children = source.listFiles();
137 			if (children == null)
138 				throw new IOException("Error while retrieving children of directory: " + source); //$NON-NLS-1$
139 			for (File child : children) {
140 				xcopy(copiedFiles, child, new File(target, child.getName()), overwrite, backupStore);
141 			}
142 			return;
143 		}
144 		if (target.exists() && !overwrite)
145 			throw new IOException("Target: " + target + " already exists"); //$NON-NLS-1$//$NON-NLS-2$
146 
147 		if (!target.getParentFile().exists() && !target.getParentFile().mkdirs())
148 			throw new IOException("Target: Path " + target.getParent() + " could not be created"); //$NON-NLS-1$//$NON-NLS-2$
149 
150 		try {
151 			Util.copyStream(new FileInputStream(source), true, new FileOutputStream(target), true);
152 		} catch (IOException e) {
153 			// get the original IOException to the log
154 			e.printStackTrace();
155 			throw new IOException("Error while copying:" + source.getAbsolutePath()); //$NON-NLS-1$
156 		}
157 		copiedFiles.add(target);
158 	}
159 }
160