From d1992a5b9de51622612f3229201f52185e635777 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Wed, 8 Aug 2018 11:18:26 -0700 Subject: [PATCH] Added support for marking resources as executable. --- .../threerings/getdown/data/Application.java | 39 ++++++++++--------- .../com/threerings/getdown/data/Resource.java | 38 +++++++++++++----- .../threerings/getdown/launcher/Getdown.java | 23 ++--------- .../getdown/launcher/GetdownApp.java | 2 +- .../com/threerings/getdown/util/FileUtil.java | 15 +++++++ 5 files changed, 67 insertions(+), 50 deletions(-) diff --git a/src/main/java/com/threerings/getdown/data/Application.java b/src/main/java/com/threerings/getdown/data/Application.java index 2259410..6732677 100644 --- a/src/main/java/com/threerings/getdown/data/Application.java +++ b/src/main/java/com/threerings/getdown/data/Application.java @@ -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 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 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 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 cdata, String name, boolean unpack, - List list) + protected void parseResources (Map cdata, String name, + EnumSet attrs, List 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); } diff --git a/src/main/java/com/threerings/getdown/data/Resource.java b/src/main/java/com/threerings/getdown/data/Resource.java index aada5ee..262dd09 100644 --- a/src/main/java/com/threerings/getdown/data/Resource.java +++ b/src/main/java/com/threerings/getdown/data/Resource.java @@ -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 { + /** 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 NORMAL = EnumSet.noneOf(Attr.class); + public static EnumSet UNPACK = EnumSet.of(Attr.UNPACK); + public static EnumSet 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 /** * 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 attrs) { _path = path; _remote = remote; @@ -124,12 +137,13 @@ public class Resource implements Comparable 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 */ public boolean shouldUnpack () { - return _unpack && !SysProps.noUnpack(); + return _attrs.contains(Attr.UNPACK) && !SysProps.noUnpack(); } /** @@ -254,8 +268,7 @@ public class Resource implements Comparable 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 } /** - * 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 protected String _path; protected URL _remote; protected File _local, _localNew, _marker, _unpacked; - protected boolean _unpack, _isJar, _isPacked200Jar; + protected EnumSet _attrs; + protected boolean _isJar, _isPacked200Jar; /** Used to sort the entries in a jar file. */ protected static final Comparator ENTRY_COMP = new Comparator() { diff --git a/src/main/java/com/threerings/getdown/launcher/Getdown.java b/src/main/java/com/threerings/getdown/launcher/Getdown.java index 03743b7..f20846e 100644 --- a/src/main/java/com/threerings/getdown/launcher/Getdown.java +++ b/src/main/java/com/threerings/getdown/launcher/Getdown.java @@ -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. */ diff --git a/src/main/java/com/threerings/getdown/launcher/GetdownApp.java b/src/main/java/com/threerings/getdown/launcher/GetdownApp.java index f94bb7e..2e5a554 100644 --- a/src/main/java/com/threerings/getdown/launcher/GetdownApp.java +++ b/src/main/java/com/threerings/getdown/launcher/GetdownApp.java @@ -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); } } diff --git a/src/main/java/com/threerings/getdown/util/FileUtil.java b/src/main/java/com/threerings/getdown/util/FileUtil.java index 0b26946..9fdcc93 100644 --- a/src/main/java/com/threerings/getdown/util/FileUtil.java +++ b/src/main/java/com/threerings/getdown/util/FileUtil.java @@ -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}. */