From: Daira Hopwood Date: Thu, 29 Jan 2015 18:32:05 +0000 (+0000) Subject: Fix user-path-expansion on Windows for non-ASCII home directories. refs #1674 X-Git-Tag: allmydata-tahoe-1.10.1a1~77^2~6 X-Git-Url: https://git.rkrishnan.org/(%5B%5E?a=commitdiff_plain;h=c1d5c4f07acb5299c5e8929513fa4fd3bc742388;p=tahoe-lafs%2Ftahoe-lafs.git Fix user-path-expansion on Windows for non-ASCII home directories. refs #1674 Signed-off-by: Daira Hopwood --- diff --git a/src/allmydata/test/test_util.py b/src/allmydata/test/test_util.py index 16e8383f..d907c118 100644 --- a/src/allmydata/test/test_util.py +++ b/src/allmydata/test/test_util.py @@ -521,6 +521,20 @@ class FileUtil(ReallyEqualMixin, unittest.TestCase): _cleanup() self.failIf(os.path.exists(long_path)) + def test_windows_expanduser(self): + def call_windows_getenv(name): + if name == u"HOMEDRIVE": return u"C:" + if name == u"HOMEPATH": return u"\\Documents and Settings\\\u0100" + self.fail("unexpected argument to call_windows_getenv") + self.patch(fileutil, 'windows_getenv', call_windows_getenv) + + self.failUnlessReallyEqual(fileutil.windows_expanduser(u"~"), os.path.join(u"C:", u"\\Documents and Settings\\\u0100")) + self.failUnlessReallyEqual(fileutil.windows_expanduser(u"~\\foo"), os.path.join(u"C:", u"\\Documents and Settings\\\u0100", u"foo")) + self.failUnlessReallyEqual(fileutil.windows_expanduser(u"~/foo"), os.path.join(u"C:", u"\\Documents and Settings\\\u0100", u"foo")) + self.failUnlessReallyEqual(fileutil.windows_expanduser(u"a"), u"a") + self.failUnlessReallyEqual(fileutil.windows_expanduser(u"a~"), u"a~") + self.failUnlessReallyEqual(fileutil.windows_expanduser(u"a\\~\\foo"), u"a\\~\\foo") + def test_disk_stats(self): avail = fileutil.get_available_space('.', 2**14) if avail == 0: diff --git a/src/allmydata/util/fileutil.py b/src/allmydata/util/fileutil.py index 466361d9..45b74063 100644 --- a/src/allmydata/util/fileutil.py +++ b/src/allmydata/util/fileutil.py @@ -315,7 +315,7 @@ def abspath_expanduser_unicode(path, base=None): if base is not None: precondition_abspath(base) - path = os.path.expanduser(path) + path = expanduser(path) if _getfullpathname: # On Windows, os.path.isabs will return True for paths without a drive letter, @@ -359,10 +359,17 @@ def to_windows_long_path(path): have_GetDiskFreeSpaceExW = False if sys.platform == "win32": - try: - from ctypes import WINFUNCTYPE, windll, POINTER, byref, c_ulonglong - from ctypes.wintypes import BOOL, DWORD, LPCWSTR + from ctypes import WINFUNCTYPE, windll, POINTER, byref, c_ulonglong, create_unicode_buffer + from ctypes.wintypes import BOOL, DWORD, LPCWSTR, LPWSTR + + # + GetLastError = WINFUNCTYPE(DWORD)(("GetLastError", windll.kernel32)) + # + GetEnvironmentVariableW = WINFUNCTYPE(DWORD, LPCWSTR, LPWSTR, DWORD)( + ("GetEnvironmentVariableW", windll.kernel32)) + + try: # PULARGE_INTEGER = POINTER(c_ulonglong) @@ -370,14 +377,51 @@ if sys.platform == "win32": GetDiskFreeSpaceExW = WINFUNCTYPE(BOOL, LPCWSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER)( ("GetDiskFreeSpaceExW", windll.kernel32)) - # - GetLastError = WINFUNCTYPE(DWORD)(("GetLastError", windll.kernel32)) - have_GetDiskFreeSpaceExW = True except Exception: import traceback traceback.print_exc() +def expanduser(path): + # os.path.expanduser is hopelessly broken for Unicode paths on Windows (ticket #1674). + if sys.platform == "win32": + return windows_expanduser(path) + else: + return os.path.expanduser(path) + +def windows_expanduser(path): + if not path.startswith('~'): + return path + home_drive = windows_getenv(u'HOMEDRIVE') + home_path = windows_getenv(u'HOMEPATH') + if path == '~': + return os.path.join(home_drive, home_path) + elif path.startswith('~/') or path.startswith('~\\'): + return os.path.join(home_drive, home_path, path[2 :]) + else: + return path + +def windows_getenv(name): + # Based on , + # with improved error handling. + if not isinstance(name, unicode): + raise AssertionError("name must be Unicode") + + n = GetEnvironmentVariableW(name, None, 0) + if n <= 0: + err = GetLastError() + raise OSError("Windows error %d attempting to read environment variable %r" + % (err, name)) + + buf = create_unicode_buffer(u'\0'*n) + retval = GetEnvironmentVariableW(name, buf, n) + if retval <= 0: + err = GetLastError() + raise OSError("Windows error %d attempting to read environment variable %r" + % (err, name)) + + return buf.value + def get_disk_stats(whichdir, reserved_space=0): """Return disk statistics for the storage disk, in the form of a dict with the following fields.