[PATCH] shellpath.c: Fix creation of HOME directory symbolic links
Rob Walker
bob.mt.wya at gmail.com
Wed Jun 6 02:46:49 CDT 2018
Fixes: https://bugs.winehq.org/show_bug.cgi?id=41668 (1)
https://bugs.winehq.org/show_bug.cgi?id=28216 (2)
(1) Introduces a "WINESYMLINK" env variable that allows a user to
control what shell folder links are created to the HOME folder, when
a WINEPREFIX is initially created.
The Wineprefix Shell Folders, that Wine symlinks to the user's HOME directory,
are only (re-)created on each Wine boot if they: do not pre-exist or are broken
symbolic links.
(2) The XDG Directory specification automatically falls back to the user HOME
directory for any XDG_*_DIR that does not exist (xdg-user-dirs-update).
The implemented solution is to block Wine from symlinking directly
to HOME directory.
Test on Gentoo GNU/Linux.
Signed-off-by: Rob Walker <bob.mt.wya at gmail.com>
---
dlls/shell32/shellpath.c | 554 ++++++++++++++++++++++++---------------
1 file changed, 343 insertions(+), 211 deletions(-)
diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c
index a551e93aa8..b8c74273c6 100644
--- a/dlls/shell32/shellpath.c
+++ b/dlls/shell32/shellpath.c
@@ -4326,252 +4326,259 @@ static HRESULT _SHRegisterCommonShellFolders(void)
}
/******************************************************************************
- * _SHAppendToUnixPath [Internal]
+ * append_to_unix_path [Internal]
*
- * Helper function for _SHCreateSymbolicLinks. Appends pwszSubPath (or the
- * corresponding resource, if IS_INTRESOURCE) to the unix base path 'szBasePath'
- * and replaces backslashes with slashes.
+ * Helper function for create_home_subdir_symbolic_link.
+ * Appends 'subpath', or the corresponding resource (if it is a
+ * resource identifier), to the Unix base path 'base_dir'.
+ * Replace backslashes with forward slashes.
*
* PARAMS
- * szBasePath [IO] The unix base path, which will be appended to (CP_UNXICP).
- * pwszSubPath [I] Sub-path or resource id (use MAKEINTRESOURCEW).
+ * base_dir [IO] The Unix base path, which will be appended to (CP_UNXICP).
+ * subpath [I] Sub-path or resource id (use MAKEINTRESOURCEW, pre-call).
*
* RETURNS
- * Success: TRUE,
+ * Success: TRUE
* Failure: FALSE
*/
-static inline BOOL _SHAppendToUnixPath(char *szBasePath, LPCWSTR pwszSubPath) {
- WCHAR wszSubPath[MAX_PATH];
- int cLen = strlen(szBasePath);
- char *pBackslash;
-
- if (IS_INTRESOURCE(pwszSubPath)) {
- if (!LoadStringW(shell32_hInstance, LOWORD(pwszSubPath), wszSubPath, MAX_PATH)) {
- /* Fall back to hard coded defaults. */
- switch (LOWORD(pwszSubPath)) {
- case IDS_PERSONAL:
- lstrcpyW(wszSubPath, DocumentsW);
- break;
- case IDS_MYMUSIC:
- lstrcpyW(wszSubPath, My_MusicW);
- break;
- case IDS_MYPICTURES:
- lstrcpyW(wszSubPath, My_PicturesW);
- break;
- case IDS_MYVIDEOS:
- lstrcpyW(wszSubPath, My_VideosW);
- break;
- default:
- ERR("LoadString(%d) failed!\n", LOWORD(pwszSubPath));
- return FALSE;
- }
+static inline BOOL append_to_unix_path(char *base_dir, LPCWSTR subpath)
+{
+ WCHAR ws_subpath[MAX_PATH];
+ int cLen = strlen(base_dir);
+ char *back_slash;
+
+ if (!IS_INTRESOURCE(subpath))
+ {
+ lstrcpyW(ws_subpath, subpath);
+ }
+ else if (!LoadStringW(shell32_hInstance, LOWORD(subpath), ws_subpath, MAX_PATH))
+ {
+ /* Fall back to hard coded defaults. */
+ switch (LOWORD(subpath)) {
+ case IDS_PERSONAL:
+ lstrcpyW(ws_subpath, DocumentsW);
+ break;
+ case IDS_MYMUSIC:
+ lstrcpyW(ws_subpath, My_MusicW);
+ break;
+ case IDS_MYPICTURES:
+ lstrcpyW(ws_subpath, My_PicturesW);
+ break;
+ case IDS_MYVIDEOS:
+ lstrcpyW(ws_subpath, My_VideosW);
+ break;
+ case IDS_DESKTOP:
+ lstrcpyW(ws_subpath, DesktopW);
+ break;
+ default:
+ ERR("LoadString(%d) failed!\n", LOWORD(subpath));
+ return FALSE;
}
- } else {
- lstrcpyW(wszSubPath, pwszSubPath);
}
-
- if (szBasePath[cLen-1] != '/') szBasePath[cLen++] = '/';
-
- if (!WideCharToMultiByte(CP_UNIXCP, 0, wszSubPath, -1, szBasePath + cLen,
+
+ if (!cLen || (base_dir[cLen-1] != '/')) base_dir[cLen++] = '/';
+
+ if (!WideCharToMultiByte(CP_UNIXCP, 0, ws_subpath, -1, base_dir + cLen,
FILENAME_MAX - cLen, NULL, NULL))
{
return FALSE;
}
-
- pBackslash = szBasePath + cLen;
- while ((pBackslash = strchr(pBackslash, '\\'))) *pBackslash = '/';
-
+
+ back_slash = base_dir + cLen;
+ while ((back_slash = strchr(back_slash, '\\'))) *back_slash = '/';
+
return TRUE;
}
/******************************************************************************
- * _SHCreateSymbolicLinks [Internal]
+ * compare_pathft_to_bootft [Internal]
*
- * Sets up symbol links for various shell folders to point into the users home
- * directory. We do an educated guess about what the user would probably want:
- * - If there is a 'My Documents' directory in $HOME, the user probably wants
- * wine's 'My Documents' to point there. Furthermore, we imply that the user
- * is a Windows lover and has no problem with wine creating 'My Pictures',
- * 'My Music' and 'My Videos' subfolders under '$HOME/My Documents', if those
- * do not already exits. We put appropriate symbolic links in place for those,
- * too.
- * - If there is no 'My Documents' directory in $HOME, we let 'My Documents'
- * point directly to $HOME. We assume the user to be a unix hacker who does not
- * want wine to create anything anywhere besides the .wine directory. So, if
- * there already is a 'My Music' directory in $HOME, we symlink the 'My Music'
- * shell folder to it. But if not, then we check XDG_MUSIC_DIR - "well known"
- * directory, and try to link to that. If that fails, then we symlink to
- * $HOME directly. The same holds fo 'My Pictures' and 'My Videos'.
- * - The Desktop shell folder is symlinked to XDG_DESKTOP_DIR. If that does not
- * exist, then we try '$HOME/Desktop'. If that does not exist, then we leave
- * it alone.
- * ('My Music',... above in fact means LoadString(IDS_MYMUSIC))
+ * Function to compare the FILETIME of ws_path (a Windows path) to the (estimated)
+ * Wine System boot time.
+ *
+ * PARAMS
+ * ws_path [I] The Windows path, for which provides the FS write time.
+ * time_difference [O] -1 Windows path last written before time of Wine boot.
+ * 0 Windows path last written at time of Wine boot.
+ * +1 Windows path last written after time of Wine boot.
+ * RETURNS
+ * Success: S_OK
+ * Failure: E_FAIL
+ *
+ * NOTES
+ * Pre-allocate storage for time_difference pointer variable externally.
*/
-static void _SHCreateSymbolicLinks(void)
-{
- UINT aidsMyStuff[] = { IDS_MYPICTURES, IDS_MYVIDEOS, IDS_MYMUSIC }, i;
- const WCHAR* MyOSXStuffW[] = { PicturesW, MoviesW, MusicW };
- int acsidlMyStuff[] = { CSIDL_MYPICTURES, CSIDL_MYVIDEO, CSIDL_MYMUSIC };
- static const char * const xdg_dirs[] = { "PICTURES", "VIDEOS", "MUSIC", "DOCUMENTS", "DESKTOP" };
- static const unsigned int num = ARRAY_SIZE(xdg_dirs);
- WCHAR wszTempPath[MAX_PATH];
- char szPersonalTarget[FILENAME_MAX], *pszPersonal;
- char szMyStuffTarget[FILENAME_MAX], *pszMyStuff;
- char szDesktopTarget[FILENAME_MAX], *pszDesktop;
- struct stat statFolder;
- const char *pszHome;
- HRESULT hr;
- char ** xdg_results;
- char * xdg_desktop_dir;
-
- /* Create all necessary profile sub-dirs up to 'My Documents' and get the unix path. */
- hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE, NULL,
- SHGFP_TYPE_DEFAULT, wszTempPath);
- if (FAILED(hr)) return;
- pszPersonal = wine_get_unix_file_name(wszTempPath);
- if (!pszPersonal) return;
+static HRESULT compare_pathft_to_bootft(const WCHAR * ws_path, int * time_difference)
+{
+ static ULONG64 time_system_start = 0;
+ SYSTEMTIME system_time_st;
+ FILETIME dummy_ft, system_time_ft, last_write_ft;
+ ULONG64 tick_count, time_last_write;
+ HANDLE fhandle;
+ DWORD ft_errorc = 0;
- hr = XDG_UserDirLookup(xdg_dirs, num, &xdg_results);
- if (FAILED(hr)) xdg_results = NULL;
+ if (!time_difference) return E_FAIL;
- pszHome = getenv("HOME");
- if (pszHome && !stat(pszHome, &statFolder) && S_ISDIR(statFolder.st_mode))
+ if (!time_system_start)
{
- while (1)
+ tick_count = (ULONG64) GetTickCount64();
+ GetSystemTime(&system_time_st);
+ if (!SystemTimeToFileTime( &system_time_st, &system_time_ft))
{
- /* Check if there's already a Wine-specific 'My Documents' folder */
- strcpy(szPersonalTarget, pszHome);
- if (_SHAppendToUnixPath(szPersonalTarget, MAKEINTRESOURCEW(IDS_PERSONAL)) &&
- !stat(szPersonalTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
- {
- /* '$HOME/My Documents' exists. Create 'My Pictures',
- * 'My Videos' and 'My Music' subfolders or fail silently if
- * they already exist.
- */
- for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++)
- {
- strcpy(szMyStuffTarget, szPersonalTarget);
- if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
- mkdir(szMyStuffTarget, 0777);
- }
- break;
- }
-
- /* Try to point to the XDG Documents folder */
- if (xdg_results && xdg_results[num-2] &&
- !stat(xdg_results[num-2], &statFolder) &&
- S_ISDIR(statFolder.st_mode))
- {
- strcpy(szPersonalTarget, xdg_results[num-2]);
- break;
- }
-
- /* Or the hardcoded / OS X Documents folder */
- strcpy(szPersonalTarget, pszHome);
- if (_SHAppendToUnixPath(szPersonalTarget, DocumentsW) &&
- !stat(szPersonalTarget, &statFolder) &&
- S_ISDIR(statFolder.st_mode))
- break;
-
- /* As a last resort point to $HOME. */
- strcpy(szPersonalTarget, pszHome);
- break;
+ ERR("SystemTimeToFileTime call failed (%d)\n", GetLastError());
+ return E_FAIL;
}
-
- /* Replace 'My Documents' directory with a symlink or fail silently if not empty. */
- remove(pszPersonal);
- symlink(szPersonalTarget, pszPersonal);
+ time_system_start = (((ULONG64)system_time_ft.dwHighDateTime) << 32)
+ + system_time_ft.dwLowDateTime
+ - (tick_count * 10000);
}
- else
+ fhandle = CreateFileW( ws_path, 0, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (fhandle == INVALID_HANDLE_VALUE)
{
- /* '$HOME' doesn't exist. Create 'My Pictures', 'My Videos' and 'My Music' subdirs
- * in '%USERPROFILE%\\My Documents' or fail silently if they already exist. */
- pszHome = NULL;
- strcpy(szPersonalTarget, pszPersonal);
- for (i = 0; i < ARRAY_SIZE(aidsMyStuff); i++) {
- strcpy(szMyStuffTarget, szPersonalTarget);
- if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])))
- mkdir(szMyStuffTarget, 0777);
- }
+ ERR("Open file handle failed for path: %s (%d)\n",
+ debugstr_w(ws_path), GetLastError());
+ return E_FAIL;
}
-
- /* Create symbolic links for 'My Pictures', 'My Videos' and 'My Music'. */
- for (i=0; i < ARRAY_SIZE(aidsMyStuff); i++)
+ if (!GetFileTime(fhandle, &dummy_ft, &dummy_ft, &last_write_ft))
+ ft_errorc = GetLastError();
+ if (!CloseHandle(fhandle))
{
- /* Create the current 'My Whatever' folder and get its unix path. */
- hr = SHGetFolderPathW(NULL, acsidlMyStuff[i]|CSIDL_FLAG_CREATE, NULL,
- SHGFP_TYPE_DEFAULT, wszTempPath);
- if (FAILED(hr)) continue;
-
- pszMyStuff = wine_get_unix_file_name(wszTempPath);
- if (!pszMyStuff) continue;
-
- while (1)
- {
- /* Check for the Wine-specific '$HOME/My Documents' subfolder */
- strcpy(szMyStuffTarget, szPersonalTarget);
- if (_SHAppendToUnixPath(szMyStuffTarget, MAKEINTRESOURCEW(aidsMyStuff[i])) &&
- !stat(szMyStuffTarget, &statFolder) && S_ISDIR(statFolder.st_mode))
- break;
+ ERR("Close file handle failed: %p (%d)\n",
+ fhandle, GetLastError());
+ return E_FAIL;
+ }
+ if (ft_errorc)
+ {
+ ERR("Get file time failed: %p (%d)\n",
+ fhandle, ft_errorc);
+ return E_FAIL;
+ }
+ time_last_write = (((ULONG64)last_write_ft.dwHighDateTime) << 32)
+ + last_write_ft.dwLowDateTime;
+ *time_difference = ((time_last_write > time_system_start) ? 1 :
+ ((time_last_write < time_system_start) ? -1 :
+ 0));
- /* Try the XDG_XXX_DIR folder */
- if (xdg_results && xdg_results[i])
- {
- strcpy(szMyStuffTarget, xdg_results[i]);
- break;
- }
+ return S_OK;
+}
- /* Or the OS X folder (these are never localized) */
- if (pszHome)
- {
- strcpy(szMyStuffTarget, pszHome);
- if (_SHAppendToUnixPath(szMyStuffTarget, MyOSXStuffW[i]) &&
- !stat(szMyStuffTarget, &statFolder) &&
- S_ISDIR(statFolder.st_mode))
- break;
- }
+/******************************************************************************
+ * create_homedir_symbolic_link [Internal]
+ *
+ * Creates a symbolic link from the current Wineprefix to an appropriate
+ * HOME subdirectory (if one is found).
+ *
+ * Creates 'XXXX' directory in Wineprefix.
+ * Then create a 'My XXXX' symbolic link in Wineprefix:
+ * 1) If '$HOME/XXXX' (IDS directory) exists then target this.
+ * 2) If '$HOME/XXXX' (XDG_XXXX_DIR) exists then target this.
+ * 3) If '$HOME/XXXX' (MacOS XXXX media directory) exists then target this.
+ *
+ * PARAMS
+ * env_enabled [I]
+ * TRUE The user has enabled this Shell Folder to be symlinked (default).
+ * FALSE The user has chosen to disable symlinking this Shell Folder.
+ * The Shell Folder will be created, in the current Wineprefix (if not pre-existing).
+ * ids_dir [I] Windows Resource Identifier code for current Shell Folder.
+ * csidl_dir [I] Constant Special Item ID List identifier for current Shell Folder.
+ * xdg_dir [I] Full path of external Unix XDG directory corresponding to current Shell Folder.
+ * ws_osx_dir [I] Fallback directory name to use, corresponding to current Shell Folder (OSX specific).
+ *
+ */
+void create_homedir_symbolic_link(BOOL env_enabled,
+ UINT ids_dir,
+ int csidl_dir,
+ const char * xdg_dir,
+ const WCHAR * ws_osx_dir)
+{
+ static const char * env_homedir = NULL;
+ WCHAR ws_temp_path[MAX_PATH];
+ char home_target[FILENAME_MAX], * prefix_dir;
+ struct stat stat_folder, stat_home_folder;
+ HRESULT hr;
+ BOOL target_ok;
+ int time_difference;
- /* As a last resort point to the same location as 'My Documents' */
- strcpy(szMyStuffTarget, szPersonalTarget);
- break;
+ hr = SHGetFolderPathW(NULL, csidl_dir, NULL,
+ SHGFP_TYPE_DEFAULT, ws_temp_path);
+ if (SUCCEEDED(hr))
+ {
+ if (ids_dir != IDS_DESKTOPDIRECTORY) return;
+
+ if ((compare_pathft_to_bootft(ws_temp_path, &time_difference) == S_OK)
+ && (time_difference < 0))
+ {
+ TRACE("%s directory created before wineboot\n",
+ debugstr_w(ws_temp_path));
+ return;
}
- remove(pszMyStuff);
- symlink(szMyStuffTarget, pszMyStuff);
- heap_free(pszMyStuff);
}
-
- /* Last but not least, the Desktop folder */
- if (pszHome)
- strcpy(szDesktopTarget, pszHome);
+ else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
+ {
+ hr = SHGetFolderPathW(NULL, csidl_dir|CSIDL_FLAG_CREATE, NULL,
+ SHGFP_TYPE_DEFAULT, ws_temp_path);
+ if (FAILED(hr)) return;
+ }
else
- strcpy(szDesktopTarget, pszPersonal);
- heap_free(pszPersonal);
+ {
+ ERR("Failed to get Wineprefix path corresponding to %d CSIDL\n",
+ csidl_dir);
+ return;
+ }
+ if (!env_enabled) return;
+ prefix_dir = wine_get_unix_file_name(ws_temp_path);
+ if (!prefix_dir)
+ {
+ ERR("Failed to get Unix Wineprefix directory for: %s\n",
+ debugstr_w(ws_temp_path));
+ return;
+ }
+ if (!env_homedir) env_homedir = getenv("HOME");
+ if (!(env_homedir
+ && (stat(env_homedir, &stat_home_folder) != 1)
+ && S_ISDIR(stat_home_folder.st_mode)))
+ {
+ if (prefix_dir) heap_free(prefix_dir);
+ return;
+ }
- xdg_desktop_dir = xdg_results ? xdg_results[num - 1] : NULL;
- if (xdg_desktop_dir ||
- (_SHAppendToUnixPath(szDesktopTarget, DesktopW) &&
- !stat(szDesktopTarget, &statFolder) && S_ISDIR(statFolder.st_mode)))
+ /* Check for:
+ * '$HOME/XXXX' (IDS directory)
+ * or '$HOME/XXXX' (XDG directory)
+ * or '$HOME/XXXX' (MacOS directory)
+ */
+ target_ok = FALSE;
+ strcpy(home_target, env_homedir);
+ if (append_to_unix_path(home_target, MAKEINTRESOURCEW(ids_dir))
+ && (stat(home_target, &stat_folder) != -1)
+ && S_ISDIR(stat_folder.st_mode))
{
- hr = SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY|CSIDL_FLAG_CREATE, NULL,
- SHGFP_TYPE_DEFAULT, wszTempPath);
- if (SUCCEEDED(hr) && (pszDesktop = wine_get_unix_file_name(wszTempPath)))
- {
- remove(pszDesktop);
- if (xdg_desktop_dir)
- symlink(xdg_desktop_dir, pszDesktop);
- else
- symlink(szDesktopTarget, pszDesktop);
- heap_free(pszDesktop);
- }
+ target_ok = TRUE;
+ }
+ else if (xdg_dir && stat(xdg_dir, &stat_folder))
+ {
+ strcpy(home_target, xdg_dir);
+ /* Only link to the XDG directory, if it does not point directly
+ * to the user's HOME directory (XDG specification fallback path). */
+ target_ok = (stat_folder.st_ino != stat_home_folder.st_ino);
+ }
+ else
+ {
+ strcpy(home_target, env_homedir);
+ target_ok = append_to_unix_path(home_target, ws_osx_dir)
+ && (stat(home_target, &stat_folder) != -1)
+ && S_ISDIR(stat_folder.st_mode);
}
- /* Free resources allocated by XDG_UserDirLookup() */
- if (xdg_results)
+ if (target_ok)
{
- for (i = 0; i < num; i++)
- heap_free(xdg_results[i]);
- heap_free(xdg_results);
+ remove(prefix_dir);
+ symlink(home_target, prefix_dir);
}
+
+
+ if (prefix_dir) heap_free(prefix_dir);
}
/******************************************************************************
@@ -6126,15 +6133,139 @@ static void register_system_knownfolders(void)
}
}
+/******************************************************************************
+ * parse_symlinks_env_variable [Internal]
+ *
+ * PARAMS
+ * xdg_dir [I] Unix XDG directory name (without "XDG_" prefix and "_DIR" suffix).
+ *
+ * RETURNS
+ * TRUE "WINESYMLINK" contains the specified XDG directory string.
+ * FALSE "WINESYMLINK" does not contain the specified XDG directory string.
+ * Any error condition.
+ *
+ * Note: "WINESYMLINK", defaults to "ALL", when unset. This enables all symlinks.
+ * "WINESYMLINK" can also be manually set to "ALL", enabling all symlinks.
+ *
+ */
+static BOOL parse_symlinks_env_variable(const char * xdg_dir)
+{
+ static const char * matchall_env_var = "ALL";
+ static char env_var[MAX_PATH+1];
+ static char * env_winesymlink = NULL;
+ char * sstr, * sstr_stop, * tstr, * word_start = NULL;
+ char seperator_ch = '\0';
+ BOOL end_string, in_word = FALSE, matched = FALSE;
+ size_t slength = 0, matchall_length, xdg_dir_slength;
+
+ if (!xdg_dir) return matched;
+
+ if (!env_winesymlink)
+ {
+ env_winesymlink = getenv("WINESYMLINK");
+ if (!env_winesymlink) env_winesymlink = (char*) matchall_env_var;
+ tstr = env_var;
+ sstr_stop = env_winesymlink + MAX_PATH;
+ for (sstr = env_winesymlink; *sstr && (sstr != sstr_stop); ++sstr)
+ {
+ if (!seperator_ch && (ispunct(*sstr) || isspace(*sstr)))
+ seperator_ch = *sstr;
+ if (!isalpha(*sstr)) continue;
+
+ if (seperator_ch && in_word)
+ {
+ *tstr++ = seperator_ch;
+ ++slength;
+ }
+ in_word = TRUE;
+ seperator_ch = '\0';
+ *tstr++ = toupper(*sstr);
+ ++slength;
+ }
+ env_var[slength] = '\0';
+ }
+
+ TRACE("processed env variable: %s\n", debugstr_a(&env_var[0]));
+ matchall_length = strlen(matchall_env_var);
+ xdg_dir_slength = strlen(xdg_dir);
+ end_string = !(env_var[0]);
+ for (sstr = &env_var[0]; !end_string; ++sstr)
+ {
+ end_string = !(*(sstr+1));
+ in_word = isalpha(*sstr);
+ if (in_word && !word_start) word_start = sstr;
+ in_word = in_word && !end_string;
+ if (in_word || !word_start) continue;
+
+ slength = ((size_t) (sstr-word_start))+(end_string ? 1 : 0);
+ matched = ( (slength == xdg_dir_slength) && (strncmp(word_start, xdg_dir, slength) == 0) )
+ || ( (slength == matchall_length) && (strncmp(word_start, "ALL", slength) == 0) );
+ if (matched) break;
+
+ word_start = NULL;
+ }
+ TRACE("%s symlinking for %s\n", matched ? "Enabled" : "Disabled", debugstr_a(xdg_dir));
+
+ return matched;
+}
+
+/******************************************************************************
+ * create_homedir_symbolic_links [Internal]
+ *
+ * Parse WINESYMLINK env variable, for each XDG directory argument. To test if symlinking is
+ * enabled for that XDG directory / Wine Profile Folder.
+ * Then calls the function create_homedir_symbolic_link to potentially symlink from a Shell Folder,
+ * in the current Wineprefix, to a subdirectory of the current user's HOME directory.
+ *
+ * PARAMS
+ * xdg_dirnames [I] Pointer to an array of Unix XDG directory names
+ * (without "XDG_" prefix and "_DIR" suffix).
+ * xdg_dir_count [I] Item count of array (above).
+ *
+ */
+static void create_homedir_symbolic_links(const char * const xdg_dirnames[], const UINT xdg_dir_count)
+{
+ char ** xdg_dirs_array;
+ char * xdg_dir;
+ HRESULT hr;
+ UINT i;
+ BOOL env_enabled;
+
+ if (!xdg_dirnames) return;
+
+ hr = XDG_UserDirLookup(xdg_dirnames, xdg_dir_count, &xdg_dirs_array);
+ if (FAILED(hr)) xdg_dirs_array = NULL;
+
+ for (i = 0; i < xdg_dir_count; ++i)
+ {
+ env_enabled = parse_symlinks_env_variable(xdg_dirnames[i]);
+ xdg_dir = xdg_dirs_array ? xdg_dirs_array[i] : NULL;
+ if (!strcmp(xdg_dirnames[i],"DOCUMENTS"))
+ create_homedir_symbolic_link(env_enabled, IDS_PERSONAL, CSIDL_PERSONAL, xdg_dir, DocumentsW);
+ else if (!strcmp(xdg_dirnames[i],"PICTURES"))
+ create_homedir_symbolic_link(env_enabled, IDS_MYPICTURES, CSIDL_MYPICTURES, xdg_dir, PicturesW);
+ else if (!strcmp(xdg_dirnames[i],"VIDEOS"))
+ create_homedir_symbolic_link(env_enabled, IDS_MYVIDEOS, CSIDL_MYVIDEO, xdg_dir, MoviesW);
+ else if (!strcmp(xdg_dirnames[i],"MUSIC"))
+ create_homedir_symbolic_link(env_enabled, IDS_MYMUSIC, CSIDL_MYMUSIC, xdg_dir, MusicW);
+ else if (!strcmp(xdg_dirnames[i],"DESKTOP"))
+ create_homedir_symbolic_link(env_enabled, IDS_DESKTOPDIRECTORY, CSIDL_DESKTOPDIRECTORY, xdg_dir, DesktopW);
+ else
+ ERR("XDG directory name specifier invalid: %s\n", debugstr_a(xdg_dirnames[i]));
+ if (xdg_dir) heap_free(xdg_dirs_array[i]);
+ }
+ if (xdg_dirs_array) heap_free(xdg_dirs_array);
+}
+
HRESULT SHELL_RegisterShellFolders(void)
{
+ static const char * const xdg_dirnames[] = { "DOCUMENTS", "PICTURES", "VIDEOS", "MUSIC", "DESKTOP" };
+ const UINT xdg_dir_count = 5;
HRESULT hr;
- /* Set up '$HOME' targeted symlinks for 'My Documents', 'My Pictures',
- * 'My Videos', 'My Music' and 'Desktop' in advance, so that the
- * _SHRegister*ShellFolders() functions will find everything nice and clean
- * and thus will not attempt to create them in the profile directory. */
- _SHCreateSymbolicLinks();
+ /* Early setup of symlinks from specific User Shell Folders, in
+ * current Wineprefix, to subdirecties of the user's HOME directory. */
+ create_homedir_symbolic_links(xdg_dirnames, xdg_dir_count);
hr = _SHRegisterUserShellFolders(TRUE);
if (SUCCEEDED(hr))
@@ -6147,5 +6278,6 @@ HRESULT SHELL_RegisterShellFolders(void)
hr = set_folder_attributes();
if (SUCCEEDED(hr))
register_system_knownfolders();
+
return hr;
}
--
2.17.1
More information about the wine-devel
mailing list