Added support for marking resources as executable.

This commit is contained in:
Michael Bayne
2018-08-08 11:18:26 -07:00
parent 4c7fa5c87b
commit d1992a5b9d
5 changed files with 67 additions and 50 deletions
@@ -269,7 +269,7 @@ public class Application
public Resource getConfigResource ()
{
try {
return createResource(CONFIG_FILE, false);
return createResource(CONFIG_FILE, Resource.NORMAL);
} catch (Exception e) {
throw new RuntimeException("Invalid appbase '" + _vappbase + "'.", e);
}
@@ -395,7 +395,7 @@ public class Application
String pfile = "patch" + infix + _version + ".dat";
try {
URL remote = new URL(createVAppBase(_targetVersion), encodePath(pfile));
return new Resource(pfile, remote, getLocalPath(pfile), false);
return new Resource(pfile, remote, getLocalPath(pfile), Resource.NORMAL);
} catch (Exception e) {
log.warning("Failed to create patch resource path",
"pfile", pfile, "appbase", _appbase, "tvers", _targetVersion, "error", e);
@@ -417,7 +417,8 @@ public class Application
String vmfile = LaunchUtil.LOCAL_JAVA_DIR + ".jar";
try {
URL remote = new URL(createVAppBase(_targetVersion), encodePath(_javaLocation));
return new Resource(vmfile, remote, getLocalPath(vmfile), true);
return new Resource(vmfile, remote, getLocalPath(vmfile),
EnumSet.of(Resource.Attr.UNPACK));
} catch (Exception e) {
log.warning("Failed to create VM resource", "vmfile", vmfile, "appbase", _appbase,
"tvers", _targetVersion, "javaloc", _javaLocation, "error", e);
@@ -434,7 +435,7 @@ public class Application
String file = "full";
try {
URL remote = new URL(createVAppBase(_targetVersion), encodePath(file));
return new Resource(file, remote, getLocalPath(file), false);
return new Resource(file, remote, getLocalPath(file), Resource.NORMAL);
} catch (Exception e) {
log.warning("Failed to create full resource path",
"file", file, "appbase", _appbase, "tvers", _targetVersion, "error", e);
@@ -656,21 +657,22 @@ public class Application
ConfigUtil.getMultiValue(cdata, "ucode") == null) {
throw new IOException("m.missing_code");
}
parseResources(cdata, "code", false, _codes);
parseResources(cdata, "ucode", true, _codes);
parseResources(cdata, "code", Resource.NORMAL, _codes);
parseResources(cdata, "ucode", Resource.UNPACK, _codes);
// parse our non-code resources
parseResources(cdata, "resource", false, _resources);
parseResources(cdata, "uresource", true, _resources);
parseResources(cdata, "resource", Resource.NORMAL, _resources);
parseResources(cdata, "uresource", Resource.UNPACK, _resources);
parseResources(cdata, "xresource", Resource.EXEC, _resources);
// parse our auxiliary resource groups
for (String auxgroup : parseList(cdata, "auxgroups")) {
ArrayList<Resource> codes = new ArrayList<>();
parseResources(cdata, auxgroup + ".code", false, codes);
parseResources(cdata, auxgroup + ".ucode", true, codes);
parseResources(cdata, auxgroup + ".code", Resource.NORMAL, codes);
parseResources(cdata, auxgroup + ".ucode", Resource.UNPACK, codes);
ArrayList<Resource> rsrcs = new ArrayList<>();
parseResources(cdata, auxgroup + ".resource", false, rsrcs);
parseResources(cdata, auxgroup + ".uresource", true, rsrcs);
parseResources(cdata, auxgroup + ".resource", Resource.NORMAL, rsrcs);
parseResources(cdata, auxgroup + ".uresource", Resource.UNPACK, rsrcs);
_auxgroups.put(auxgroup, new AuxGroup(auxgroup, codes, rsrcs));
}
@@ -1375,8 +1377,7 @@ public class Application
toInstall.add(rsrc);
return;
}
// unpack this resource if appropriate
rsrc.unpackIfNeeded();
rsrc.applyAttrs();
unpacked.add(rsrc);
rsrc.markAsValid();
return;
@@ -1672,15 +1673,15 @@ public class Application
}
/** Helper function for creating {@link Resource} instances. */
protected Resource createResource (String path, boolean unpack)
protected Resource createResource (String path, EnumSet<Resource.Attr> attrs)
throws MalformedURLException
{
return new Resource(path, getRemoteURL(path), getLocalPath(path), unpack);
return new Resource(path, getRemoteURL(path), getLocalPath(path), attrs);
}
/** Used to parse resources with the specified name. */
protected void parseResources (Map<String,Object> cdata, String name, boolean unpack,
List<Resource> list)
protected void parseResources (Map<String,Object> cdata, String name,
EnumSet<Resource.Attr> attrs, List<Resource> list)
{
String[] rsrcs = ConfigUtil.getMultiValue(cdata, name);
if (rsrcs == null) {
@@ -1688,7 +1689,7 @@ public class Application
}
for (String rsrc : rsrcs) {
try {
list.add(createResource(rsrc, unpack));
list.add(createResource(rsrc, attrs));
} catch (Exception e) {
log.warning("Invalid resource '" + rsrc + "'. " + e);
}
@@ -10,6 +10,7 @@ import java.net.URL;
import java.security.MessageDigest;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -27,6 +28,18 @@ import static com.threerings.getdown.Log.log;
*/
public class Resource implements Comparable<Resource>
{
/** Defines special attributes for resources. */
public static enum Attr {
/** Indicates that the resource should be unpacked. */
UNPACK,
/** Indicates that the resource should be marked executable. */
EXEC
};
public static EnumSet<Attr> NORMAL = EnumSet.noneOf(Attr.class);
public static EnumSet<Attr> UNPACK = EnumSet.of(Attr.UNPACK);
public static EnumSet<Attr> EXEC = EnumSet.of(Attr.EXEC);
/**
* Computes the MD5 hash of the supplied file.
* @param version the version of the digest protocol to use.
@@ -115,7 +128,7 @@ public class Resource implements Comparable<Resource>
/**
* Creates a resource with the supplied remote URL and local path.
*/
public Resource (String path, URL remote, File local, boolean unpack)
public Resource (String path, URL remote, File local, EnumSet<Attr> attrs)
{
_path = path;
_remote = remote;
@@ -124,12 +137,13 @@ public class Resource implements Comparable<Resource>
String lpath = _local.getPath();
_marker = new File(lpath + "v");
_unpack = unpack;
_attrs = attrs;
_isJar = isJar(lpath);
_isPacked200Jar = isPacked200Jar(lpath);
if (_unpack && _isJar) {
boolean unpack = attrs.contains(Attr.UNPACK);
if (unpack && _isJar) {
_unpacked = _local.getParentFile();
} else if(_unpack && _isPacked200Jar) {
} else if(unpack && _isPacked200Jar) {
String dotJar = ".jar", lname = _local.getName();
String uname = lname.substring(0, lname.lastIndexOf(dotJar) + dotJar.length());
_unpacked = new File(_local.getParent(), uname);
@@ -189,7 +203,7 @@ public class Resource implements Comparable<Resource>
*/
public boolean shouldUnpack ()
{
return _unpack && !SysProps.noUnpack();
return _attrs.contains(Attr.UNPACK) && !SysProps.noUnpack();
}
/**
@@ -254,8 +268,7 @@ public class Resource implements Comparable<Resource>
if (!FileUtil.renameTo(source, dest)) {
throw new IOException("Failed to rename " + source + " to " + dest);
}
// unpack the resource, now that it's installed, and mark it as valid
unpackIfNeeded();
applyAttrs();
markAsValid();
}
@@ -276,12 +289,16 @@ public class Resource implements Comparable<Resource>
}
/**
* Unpacks this resource if needed.
* Applies this resources special attributes: unpacks this resource if needed, marks it as
* executable if needed.
*/
public void unpackIfNeeded () throws IOException {
public void applyAttrs () throws IOException {
if (shouldUnpack()) {
unpack();
}
if (_attrs.contains(Attr.EXEC)) {
FileUtil.makeExecutable(_local);
}
}
/**
@@ -341,7 +358,8 @@ public class Resource implements Comparable<Resource>
protected String _path;
protected URL _remote;
protected File _local, _localNew, _marker, _unpacked;
protected boolean _unpack, _isJar, _isPacked200Jar;
protected EnumSet<Attr> _attrs;
protected boolean _isJar, _isPacked200Jar;
/** Used to sort the entries in a jar file. */
protected static final Comparator<JarEntry> ENTRY_COMP = new Comparator<JarEntry>() {
@@ -633,9 +633,9 @@ public abstract class Getdown extends Thread
// these only run on non-Windows platforms, so we use Unix file separators
String localJavaDir = LaunchUtil.LOCAL_JAVA_DIR + "/";
makeExecutable(localJavaDir + "bin/java");
makeExecutable(localJavaDir + "lib/jspawnhelper");
makeExecutable(localJavaDir + "lib/amd64/jspawnhelper");
FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "bin/java"));
FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "lib/jspawnhelper"));
FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "lib/amd64/jspawnhelper"));
// lastly regenerate the .jsa dump file that helps Java to start up faster
String vmpath = LaunchUtil.getJVMPath(_app.getLocalPath(""));
@@ -649,23 +649,6 @@ public abstract class Getdown extends Thread
reportTrackingEvent("jvm_complete", -1);
}
protected void makeExecutable (String path) {
// Java doesn't know anything about file permissions (and by extension then,
// neither does Jar), so on Unix we have to hackily do so via chmod
if (!RunAnywhere.isWindows()) {
File target = _app.getLocalPath(path);
String cmd = "chmod a+rx " + target;
try {
if (target.exists()) {
log.info("Running: " + cmd);
Runtime.getRuntime().exec(cmd);
}
} catch (Exception e) {
log.warning("Failed to mark VM binary as executable", "cmd", cmd, "error", e);
}
}
}
/**
* Called if the application is determined to be of an old version.
*/
@@ -169,7 +169,7 @@ public class GetdownApp
if (icons.isEmpty()) {
log.warning("Failed to load any icons", "iconImages", _ifc.iconImages);
} else {
SwingUtil.setFrameIcons(_frame, icons);
_frame.setIconImages(icons);
}
}
@@ -206,6 +206,21 @@ public class FileUtil
}
}
/**
* Marks {@code file} as executable, if it exists. Catches and logs any errors that occur.
*/
public static void makeExecutable (File file) {
try {
if (file.exists()) {
if (!file.setExecutable(true, false)) {
log.warning("Failed to mark as executable", "file", file);
}
}
} catch (Exception e) {
log.warning("Failed to mark as executable", "file", file, "error", e);
}
}
/**
* Used by {@link #walkTree}.
*/