##// END OF EJS Templates
win32: correctly break hardlinks on network drives (issue761)...
Patrick Mezard -
r11991:50523b44 stable
parent child Browse files
Show More
@@ -1,204 +1,205 b''
1 1 # win32.py - utility functions that use win32 API
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """Utility functions that use win32 API.
9 9
10 10 Mark Hammond's win32all package allows better functionality on
11 11 Windows. This module overrides definitions in util.py. If not
12 12 available, import of this module will fail, and generic code will be
13 13 used.
14 14 """
15 15
16 16 import win32api
17 17
18 18 import errno, os, sys, pywintypes, win32con, win32file, win32process
19 19 import winerror, win32gui, win32console
20 20 import osutil, encoding
21 21 from win32com.shell import shell, shellcon
22 22
23 23 def os_link(src, dst):
24 24 try:
25 25 win32file.CreateHardLink(dst, src)
26 # CreateHardLink sometimes succeeds on mapped drives but
27 # following nlinks() returns 1. Check it now and bail out.
28 if nlinks(src) < 2:
29 try:
30 win32file.DeleteFile(dst)
31 except:
32 pass
33 # Fake hardlinking error
34 raise OSError(errno.EINVAL, 'Hardlinking not supported')
35 26 except pywintypes.error:
36 27 raise OSError(errno.EINVAL, 'target implements hardlinks improperly')
37 28 except NotImplementedError: # Another fake error win Win98
38 29 raise OSError(errno.EINVAL, 'Hardlinking not supported')
39 30
40 31 def _getfileinfo(pathname):
41 32 """Return number of hardlinks for the given file."""
42 33 try:
43 34 fh = win32file.CreateFile(pathname,
44 35 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
45 36 None, win32file.OPEN_EXISTING, 0, None)
46 37 try:
47 38 return win32file.GetFileInformationByHandle(fh)
48 39 finally:
49 40 fh.Close()
50 41 except pywintypes.error:
51 42 return None
52 43
53 44 def nlinks(pathname):
54 45 """Return number of hardlinks for the given file."""
55 46 res = _getfileinfo(pathname)
56 47 if res is not None:
57 return res[7]
48 links = res[7]
58 49 else:
59 return os.lstat(pathname).st_nlink
50 links = os.lstat(pathname).st_nlink
51 if links < 2:
52 # Known to be wrong for most network drives
53 dirname = os.path.dirname(pathname)
54 if not dirname:
55 dirname = '.'
56 dt = win32file.GetDriveType(dirname + '\\')
57 if dt == 4 or dt == 1:
58 # Fake hardlink to force COW for network drives
59 links = 2
60 return links
60 61
61 62 def samefile(fpath1, fpath2):
62 63 """Returns whether fpath1 and fpath2 refer to the same file. This is only
63 64 guaranteed to work for files, not directories."""
64 65 res1 = _getfileinfo(fpath1)
65 66 res2 = _getfileinfo(fpath2)
66 67 if res1 is not None and res2 is not None:
67 68 # Index 4 is the volume serial number, and 8 and 9 contain the file ID
68 69 return res1[4] == res2[4] and res1[8] == res2[8] and res1[9] == res2[9]
69 70 else:
70 71 return False
71 72
72 73 def samedevice(fpath1, fpath2):
73 74 """Returns whether fpath1 and fpath2 are on the same device. This is only
74 75 guaranteed to work for files, not directories."""
75 76 res1 = _getfileinfo(fpath1)
76 77 res2 = _getfileinfo(fpath2)
77 78 if res1 is not None and res2 is not None:
78 79 return res1[4] == res2[4]
79 80 else:
80 81 return False
81 82
82 83 def testpid(pid):
83 84 '''return True if pid is still running or unable to
84 85 determine, False otherwise'''
85 86 try:
86 87 handle = win32api.OpenProcess(
87 88 win32con.PROCESS_QUERY_INFORMATION, False, pid)
88 89 if handle:
89 90 status = win32process.GetExitCodeProcess(handle)
90 91 return status == win32con.STILL_ACTIVE
91 92 except pywintypes.error, details:
92 93 return details[0] != winerror.ERROR_INVALID_PARAMETER
93 94 return True
94 95
95 96 def lookup_reg(key, valname=None, scope=None):
96 97 ''' Look up a key/value name in the Windows registry.
97 98
98 99 valname: value name. If unspecified, the default value for the key
99 100 is used.
100 101 scope: optionally specify scope for registry lookup, this can be
101 102 a sequence of scopes to look up in order. Default (CURRENT_USER,
102 103 LOCAL_MACHINE).
103 104 '''
104 105 try:
105 106 from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \
106 107 QueryValueEx, OpenKey
107 108 except ImportError:
108 109 return None
109 110
110 111 if scope is None:
111 112 scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
112 113 elif not isinstance(scope, (list, tuple)):
113 114 scope = (scope,)
114 115 for s in scope:
115 116 try:
116 117 val = QueryValueEx(OpenKey(s, key), valname)[0]
117 118 # never let a Unicode string escape into the wild
118 119 return encoding.tolocal(val.encode('UTF-8'))
119 120 except EnvironmentError:
120 121 pass
121 122
122 123 def system_rcpath_win32():
123 124 '''return default os-specific hgrc search path'''
124 125 proc = win32api.GetCurrentProcess()
125 126 try:
126 127 # This will fail on windows < NT
127 128 filename = win32process.GetModuleFileNameEx(proc, 0)
128 129 except:
129 130 filename = win32api.GetModuleFileName(0)
130 131 # Use mercurial.ini found in directory with hg.exe
131 132 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
132 133 if os.path.isfile(progrc):
133 134 return [progrc]
134 135 # Use hgrc.d found in directory with hg.exe
135 136 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
136 137 if os.path.isdir(progrcd):
137 138 rcpath = []
138 139 for f, kind in osutil.listdir(progrcd):
139 140 if f.endswith('.rc'):
140 141 rcpath.append(os.path.join(progrcd, f))
141 142 return rcpath
142 143 # else look for a system rcpath in the registry
143 144 try:
144 145 value = win32api.RegQueryValue(
145 146 win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
146 147 rcpath = []
147 148 for p in value.split(os.pathsep):
148 149 if p.lower().endswith('mercurial.ini'):
149 150 rcpath.append(p)
150 151 elif os.path.isdir(p):
151 152 for f, kind in osutil.listdir(p):
152 153 if f.endswith('.rc'):
153 154 rcpath.append(os.path.join(p, f))
154 155 return rcpath
155 156 except pywintypes.error:
156 157 return []
157 158
158 159 def user_rcpath_win32():
159 160 '''return os-specific hgrc search path to the user dir'''
160 161 userdir = os.path.expanduser('~')
161 162 if sys.getwindowsversion()[3] != 2 and userdir == '~':
162 163 # We are on win < nt: fetch the APPDATA directory location and use
163 164 # the parent directory as the user home dir.
164 165 appdir = shell.SHGetPathFromIDList(
165 166 shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
166 167 userdir = os.path.dirname(appdir)
167 168 return [os.path.join(userdir, 'mercurial.ini'),
168 169 os.path.join(userdir, '.hgrc')]
169 170
170 171 def getuser():
171 172 '''return name of current user'''
172 173 return win32api.GetUserName()
173 174
174 175 def set_signal_handler_win32():
175 176 """Register a termination handler for console events including
176 177 CTRL+C. python signal handlers do not work well with socket
177 178 operations.
178 179 """
179 180 def handler(event):
180 181 win32process.ExitProcess(1)
181 182 win32api.SetConsoleCtrlHandler(handler)
182 183
183 184 def hidewindow():
184 185 def callback(*args, **kwargs):
185 186 hwnd, pid = args
186 187 wpid = win32process.GetWindowThreadProcessId(hwnd)[1]
187 188 if pid == wpid:
188 189 win32gui.ShowWindow(hwnd, win32con.SW_HIDE)
189 190
190 191 pid = win32process.GetCurrentProcessId()
191 192 win32gui.EnumWindows(callback, pid)
192 193
193 194 def termwidth_():
194 195 try:
195 196 # Query stderr to avoid problems with redirections
196 197 screenbuf = win32console.GetStdHandle(win32console.STD_ERROR_HANDLE)
197 198 try:
198 199 window = screenbuf.GetConsoleScreenBufferInfo()['Window']
199 200 width = window.Right - window.Left
200 201 return width
201 202 finally:
202 203 screenbuf.Detach()
203 204 except pywintypes.error:
204 205 return 79
General Comments 0
You need to be logged in to leave comments. Login now