Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XDG Basedir directories for Windows

I have made a Racket library for convenience in accessing the XDG Basedir directories. As I want the library to be useable on Windows as well (for cross-platform programs), I have it use standard Windows directories as the defaults when the XDG environment variables are unset.

I am currently using the following:

  • $XDG_DATA_HOME = %LOCALAPPDATA%
  • $XDG_DATA_DIRS = %APPDATA%
  • $XDG_CONFIG_HOME = %LOCALAPPDATA%
  • $XDG_CONFIG_DIRS = %APPDATA%
  • $XDG_CACHE_HOME = %TEMP%
  • $XDG_RUNTIME_DIR = %TEMP%

My question is whether there are better defaults than those. I know that %TEMP% as the $XDG_RUNTIME_DIR is wrong, since it really should be on a ramfs like /tmp, but I don't know of any directory on Windows that is like that. In Windows it seems that there is no good option to have data and configuration directories separate, so I am using the same directories for them. My gut feeling is that %LOCALAPPDATA% is a better choice for the writable $XDG_*_HOME variables and having the "roaming" configuration in the $XDG_*_DIRS lists to be read and generally not overwritten. But would corporate Windows users that have a roaming configuration find that strange and disagree?

like image 299
William Hatch Avatar asked May 08 '17 17:05

William Hatch


1 Answers

I have implemented such functionality in libraries for the JVM and Rust. Here is what I learned:

Deal with application names, because your users can't or won't.

Provide APIs that compute the full path (including the application name!) to configuration, cache, etc. directories. Not doing this will result in code that is guaranteed to be wrong on at least 2 of 3 major platforms, as the conventions differ dramatically.

Consider an application written by company MegaCorp (web address MegaCorp.co.uk) named Foo App. Under Linux, the path segment naming the application should be fooapp/ (lower-cased, no spaces), on Windows it should be MegaCorp\Foo App\ (note the two folders), and on macOS it should be uk.co.MegaCorp.Foo-App (invalid characters replaced with -).

Have a clear definition of the purpose of each directory.

For instance, my library does not offer a runtimeDir on macOS or Windows, because XDG_RUNTIME_DIR is very different from e. g. %TEMP% on Windows.

This is a potential source of security issues, as the runtime dir on Linux guarantees that it can only be accessed by the owner, is deleted when the user logs off, etc.

Also, I only offer fontDir on Linux and macOS. Windows does have a font directory, but unlike on Linux and macOS, it is not user-writable.

On the other hand, I offer both dataDir (%APPDATA%) and dataLocalDir (%LOCALAPPDATA%) across all three platforms. On macOS and Linux those directories return the same path – this is an explicit design decision, considering how users would write code if one of those directories would not be available: Users would either forget to handle it, or just fallback to the other directory. With the chosen design this just works out of the box, without users needing to think about it.

Avoid issues before the user encounters them.

This is why the general cache, config etc. directory paths return %LOCALAPPDATA% and %APPDATA%, but application-specific cache and config directory paths return %LOCALAPPDATA%\Company\Application\cache and %APPDATA%\Company\Application\config.

Note the sub-directories! This is to guarantee a clean separation of an application's cache, config and data directory, regardless of what weird Windows settings a user might have.

Split use cases into separate modules.

There are three distinct modules in my library, with clearly defined, separated use cases:

BaseDirs, which queries the paths of user-invisible standard directories (cache, config, data, executable, runtime directories) and strongly suggests using ProjectDirs instead.

ProjectDirs, which computes the location of cache, config or data directories for your own application or project, which are derived from the standard directories.

UserDirs, which queries the paths of user-facing standard directories (Audio, Documents, Downloads, etc.).

While BaseDirs and UserDirs have fairly uninteresting constructors (new()), ProjectDirs provides this factory method:

ProjectDirs::from(qualifier: &str, organization: &str, application: &str)

This method ensures that users end up with correct, standards-compliant paths to their applications' cache, config, etc. directories – without them needing to be aware of all the intricacies of each individual platform.


One last suggestion: I would keep a library named "XDG Basedir Library" focused on Linux, and publish a library with a more general name, like "Standard Directory Library" that deals with Linux, Windows, etc. to avoid confusion.

Hope this was helpful!

like image 101
soc Avatar answered Oct 02 '22 05:10

soc