simlu / voxelshop

This is the official repositiory for VoxelShop
https://blackflux.com/node/11
Apache License 2.0
350 stars 65 forks source link

[WIP] Voxelshop creates `.profile` *directory* which breaks `rustup`. #305

Open follower opened 3 years ago

follower commented 3 years ago

[WIP report, hope to add some more details but this is better than nothing. :) ]

Problem

When Voxelshop first runs (and before .voxelshop is created AFAICT), it creates a directory named .profile.

On a Mac this is located at:

This issue was also mentioned in passing in https://github.com/simlu/voxelshop/issues/295#issuecomment-478766197.

(I encountered this issue nearly a year after I first tried out Voxelshop and was only able to match the creation of the ~/.profile directory to Voxelshop because I noticed that the creation time was around the same time as~/.voxelshop.)

Apparent cause

After a lot of searching, I think I've identified why this is happening.

AFAICT the code that is creating the directory is in the (closed) portions of the com.jidesoftapplication framework--specifically the code related to layout persistence (primarily docking related?).

In JIDE_Docking_Framework_Developer_Guide.pdf there is a section that talks about layout persisting which mentions:

JIDE Docking Framework offers the ability to save windows information and settings between sessions, using the java.util.prefs package. This means that under Windows, the information will be stored in the registry, while under UNIX, it will be stored in a file in your home directory.

All layout data are organized under one key called the ‘profile key’. This can be any string, but usually it’s your company name (we use “jidesoft” in our sample application). You should call setProfileKey(String key) to set this key when your application starts up.

[...] The default set of preferences lies under the key “default”, and is used whenever loadLayoutData() and saveLayoutData() are called to persist the window state.

And later on:

Another option you have is to let the JIDE Docking Framework use its default file location. By default it uses java.util.prefs to store layout information. However if you prefer disk storage, but want JIDE to manage the location, you can call setUsePref(false) to disable using java.util.prefs. Your layout data will be stored at {user.home}/.{profileName}, where profileName is either “default” or your profile name as specified above. If you want to specify where to store the layout data, you can call setLayoutDirectory(String dirName). Please note, the directory will be used only when setUsePref is false. You also need to make sure you call set those values (i.e. setProfileKey(), setUsePref(), setLayoutDirectory()) before you call any loadLayout() or saveLayout() methods.

Note: My supposition is that the documentation is incorrect when it says the default profileName value is "default"; or, when the order of calls is incorrect the profileName is different (e.g. "profile" leading to .profile as the filename).

It's not entirely clear to me if the correct order is used in the relevant code:

https://github.com/simlu/voxelshop/blob/4094dd1ef36eee0d406b69235156956a866769c3/src/main/java/com/vitco/app/layout/WindowManager.java#L533-L541

Update: See comment below for a de-compilation of the relevant code, that explains what is happening behind the scenes.

Potential fix

It seems that a call to setProfileKey() (on the manager, or all classes that use layout persistence) needs to occur before any call to a method that directly or indirectly calls getLayoutDirectory().

Example setProfileKey() use:

Additional details

If ~/.profile exists as a file an error appears in the console:

10:08:00 PM Failed to create directory: /Users/<username>/.profile

Related links

follower commented 3 years ago

Turns out I couldn't resist the temptation to decompile the non-open source code and track down the issue for sure... :D

So, the issue is occurring due to the profileKey not being set at the correct time; and, as a result the (undocumented) default _profileKey value "profile" is being used in getLayoutDirectory() to create the directory via mkdirs if it doesn't exist! (Which is behaviour that also doesn't appear to be documented.)

// From: `com.jidesoft.swing.AbstractLayoutPersistence` in `jide-common-3.4.8.jar`
//
// (via jadx-gui 0.9.0)

public abstract class AbstractLayoutPersistence implements LayoutPersistence {
    protected static final String DEFAULT_PROFILE_NAME = "default";
// [...]
    public static final String PROPERTY_PROFILE_KEY = "profileKey";
    public static final String PROPERTY_USE_PREF = "usePref";
    public static final String PROPERTY_VERSION = "version";
// [...]
    protected final String LAYOUT_POSTFIX = ".layout";
    protected String _layoutDirectory = null;
    protected boolean _loadingLayoutData = false;
    protected String _profileKey = "profile";   // <---------------- [HERE!]
// [...]
    protected boolean _usePref = true;

// [...]

    public String getLayoutDirectory() {
        boolean z = f28i;
        String str = this._layoutDirectory;
        if (z) {
            return str;
        }
        if (str != null) {
            return this._layoutDirectory;
        }
        str = SecurityUtils.getProperty("user.home", "") + File.separator + "." + this._profileKey;
        File file = new File(str);
        boolean isDirectory = file.isDirectory();
        if (!z) {
            if (isDirectory) {
                return str;
            }
            isDirectory = file.mkdirs();    // <---------------- [And HERE!]
        }
        if (isDirectory) {
            return str;
        }
        System.err.println("Failed to create directory: " + str);
        return str;
    }

Other occurrences of this pattern

A quick code search for getLayoutDirectory & mkdirs (need to be logged in) reveals that some other projects use a pattern of getLayoutDirectory() calling mkdirs() if the directory doesn't exist--which seems unwise in general but probably only a significant issue in a situation like this where .profile is the directory created in the user home directory.

It also occurs in jedit: https://sourceforge.net/p/jedit/svn/23219/tree//jEdit/trunk/org/gjt/sp/jedit/gui/DockableWindowManager.java#l751 (Which seems to date back to this commit.)