##// END OF EJS Templates
posix: ignore execution bit in cygwin (issue3301)
A. S. Budden -
r16240:95e45abe default
parent child Browse files
Show More
@@ -1,453 +1,460
1 1 # posix.py - Posix utility function implementations for Mercurial
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 from i18n import _
9 9 import os, sys, errno, stat, getpass, pwd, grp, tempfile, unicodedata
10 10
11 11 posixfile = open
12 12 nulldev = '/dev/null'
13 13 normpath = os.path.normpath
14 14 samestat = os.path.samestat
15 15 oslink = os.link
16 16 unlink = os.unlink
17 17 rename = os.rename
18 18 expandglobs = False
19 19
20 20 umask = os.umask(0)
21 21 os.umask(umask)
22 22
23 23 def openhardlinks():
24 24 '''return true if it is safe to hold open file handles to hardlinks'''
25 25 return True
26 26
27 27 def nlinks(name):
28 28 '''return number of hardlinks for the given file'''
29 29 return os.lstat(name).st_nlink
30 30
31 31 def parsepatchoutput(output_line):
32 32 """parses the output produced by patch and returns the filename"""
33 33 pf = output_line[14:]
34 34 if os.sys.platform == 'OpenVMS':
35 35 if pf[0] == '`':
36 36 pf = pf[1:-1] # Remove the quotes
37 37 else:
38 38 if pf.startswith("'") and pf.endswith("'") and " " in pf:
39 39 pf = pf[1:-1] # Remove the quotes
40 40 return pf
41 41
42 42 def sshargs(sshcmd, host, user, port):
43 43 '''Build argument list for ssh'''
44 44 args = user and ("%s@%s" % (user, host)) or host
45 45 return port and ("%s -p %s" % (args, port)) or args
46 46
47 47 def isexec(f):
48 48 """check whether a file is executable"""
49 49 return (os.lstat(f).st_mode & 0100 != 0)
50 50
51 51 def setflags(f, l, x):
52 52 s = os.lstat(f).st_mode
53 53 if l:
54 54 if not stat.S_ISLNK(s):
55 55 # switch file to link
56 56 fp = open(f)
57 57 data = fp.read()
58 58 fp.close()
59 59 os.unlink(f)
60 60 try:
61 61 os.symlink(data, f)
62 62 except OSError:
63 63 # failed to make a link, rewrite file
64 64 fp = open(f, "w")
65 65 fp.write(data)
66 66 fp.close()
67 67 # no chmod needed at this point
68 68 return
69 69 if stat.S_ISLNK(s):
70 70 # switch link to file
71 71 data = os.readlink(f)
72 72 os.unlink(f)
73 73 fp = open(f, "w")
74 74 fp.write(data)
75 75 fp.close()
76 76 s = 0666 & ~umask # avoid restatting for chmod
77 77
78 78 sx = s & 0100
79 79 if x and not sx:
80 80 # Turn on +x for every +r bit when making a file executable
81 81 # and obey umask.
82 82 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
83 83 elif not x and sx:
84 84 # Turn off all +x bits
85 85 os.chmod(f, s & 0666)
86 86
87 87 def copymode(src, dst, mode=None):
88 88 '''Copy the file mode from the file at path src to dst.
89 89 If src doesn't exist, we're using mode instead. If mode is None, we're
90 90 using umask.'''
91 91 try:
92 92 st_mode = os.lstat(src).st_mode & 0777
93 93 except OSError, inst:
94 94 if inst.errno != errno.ENOENT:
95 95 raise
96 96 st_mode = mode
97 97 if st_mode is None:
98 98 st_mode = ~umask
99 99 st_mode &= 0666
100 100 os.chmod(dst, st_mode)
101 101
102 102 def checkexec(path):
103 103 """
104 104 Check whether the given path is on a filesystem with UNIX-like exec flags
105 105
106 106 Requires a directory (like /foo/.hg)
107 107 """
108 108
109 109 # VFAT on some Linux versions can flip mode but it doesn't persist
110 110 # a FS remount. Frequently we can detect it if files are created
111 111 # with exec bit on.
112 112
113 113 try:
114 114 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
115 115 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
116 116 try:
117 117 os.close(fh)
118 118 m = os.stat(fn).st_mode & 0777
119 119 new_file_has_exec = m & EXECFLAGS
120 120 os.chmod(fn, m ^ EXECFLAGS)
121 121 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
122 122 finally:
123 123 os.unlink(fn)
124 124 except (IOError, OSError):
125 125 # we don't care, the user probably won't be able to commit anyway
126 126 return False
127 127 return not (new_file_has_exec or exec_flags_cannot_flip)
128 128
129 129 def checklink(path):
130 130 """check whether the given path is on a symlink-capable filesystem"""
131 131 # mktemp is not racy because symlink creation will fail if the
132 132 # file already exists
133 133 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
134 134 try:
135 135 os.symlink(".", name)
136 136 os.unlink(name)
137 137 return True
138 138 except (OSError, AttributeError):
139 139 return False
140 140
141 141 def checkosfilename(path):
142 142 '''Check that the base-relative path is a valid filename on this platform.
143 143 Returns None if the path is ok, or a UI string describing the problem.'''
144 144 pass # on posix platforms, every path is ok
145 145
146 146 def setbinary(fd):
147 147 pass
148 148
149 149 def pconvert(path):
150 150 return path
151 151
152 152 def localpath(path):
153 153 return path
154 154
155 155 def samefile(fpath1, fpath2):
156 156 """Returns whether path1 and path2 refer to the same file. This is only
157 157 guaranteed to work for files, not directories."""
158 158 return os.path.samefile(fpath1, fpath2)
159 159
160 160 def samedevice(fpath1, fpath2):
161 161 """Returns whether fpath1 and fpath2 are on the same device. This is only
162 162 guaranteed to work for files, not directories."""
163 163 st1 = os.lstat(fpath1)
164 164 st2 = os.lstat(fpath2)
165 165 return st1.st_dev == st2.st_dev
166 166
167 167 encodinglower = None
168 168 encodingupper = None
169 169
170 170 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
171 171 def normcase(path):
172 172 return path.lower()
173 173
174 174 if sys.platform == 'darwin':
175 175 import fcntl # only needed on darwin, missing on jython
176 176
177 177 def normcase(path):
178 178 try:
179 179 u = path.decode('utf-8')
180 180 except UnicodeDecodeError:
181 181 # percent-encode any characters that don't round-trip
182 182 p2 = path.decode('utf-8', 'ignore').encode('utf-8')
183 183 s = ""
184 184 pos = 0
185 185 for c in path:
186 186 if p2[pos:pos + 1] == c:
187 187 s += c
188 188 pos += 1
189 189 else:
190 190 s += "%%%02X" % ord(c)
191 191 u = s.decode('utf-8')
192 192
193 193 # Decompose then lowercase (HFS+ technote specifies lower)
194 194 return unicodedata.normalize('NFD', u).lower().encode('utf-8')
195 195
196 196 def realpath(path):
197 197 '''
198 198 Returns the true, canonical file system path equivalent to the given
199 199 path.
200 200
201 201 Equivalent means, in this case, resulting in the same, unique
202 202 file system link to the path. Every file system entry, whether a file,
203 203 directory, hard link or symbolic link or special, will have a single
204 204 path preferred by the system, but may allow multiple, differing path
205 205 lookups to point to it.
206 206
207 207 Most regular UNIX file systems only allow a file system entry to be
208 208 looked up by its distinct path. Obviously, this does not apply to case
209 209 insensitive file systems, whether case preserving or not. The most
210 210 complex issue to deal with is file systems transparently reencoding the
211 211 path, such as the non-standard Unicode normalisation required for HFS+
212 212 and HFSX.
213 213 '''
214 214 # Constants copied from /usr/include/sys/fcntl.h
215 215 F_GETPATH = 50
216 216 O_SYMLINK = 0x200000
217 217
218 218 try:
219 219 fd = os.open(path, O_SYMLINK)
220 220 except OSError, err:
221 221 if err.errno == errno.ENOENT:
222 222 return path
223 223 raise
224 224
225 225 try:
226 226 return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
227 227 finally:
228 228 os.close(fd)
229 229 elif sys.version_info < (2, 4, 2, 'final'):
230 230 # Workaround for http://bugs.python.org/issue1213894 (os.path.realpath
231 231 # didn't resolve symlinks that were the first component of the path.)
232 232 def realpath(path):
233 233 if os.path.isabs(path):
234 234 return os.path.realpath(path)
235 235 else:
236 236 return os.path.realpath('./' + path)
237 237 else:
238 238 # Fallback to the likely inadequate Python builtin function.
239 239 realpath = os.path.realpath
240 240
241 241 if sys.platform == 'cygwin':
242 242 # workaround for cygwin, in which mount point part of path is
243 243 # treated as case sensitive, even though underlying NTFS is case
244 244 # insensitive.
245 245
246 246 # default mount points
247 247 cygwinmountpoints = sorted([
248 248 "/usr/bin",
249 249 "/usr/lib",
250 250 "/cygdrive",
251 251 ], reverse=True)
252 252
253 253 # use upper-ing as normcase as same as NTFS workaround
254 254 def normcase(path):
255 255 pathlen = len(path)
256 256 if (pathlen == 0) or (path[0] != os.sep):
257 257 # treat as relative
258 258 return encodingupper(path)
259 259
260 260 # to preserve case of mountpoint part
261 261 for mp in cygwinmountpoints:
262 262 if not path.startswith(mp):
263 263 continue
264 264
265 265 mplen = len(mp)
266 266 if mplen == pathlen: # mount point itself
267 267 return mp
268 268 if path[mplen] == os.sep:
269 269 return mp + encodingupper(path[mplen:])
270 270
271 271 return encodingupper(path)
272 272
273 # Cygwin translates native ACLs to POSIX permissions,
274 # but these translations are not supported by native
275 # tools, so the exec bit tends to be set erroneously.
276 # Therefore, disable executable bit access on Cygwin.
277 def checkexec(path):
278 return False
279
273 280 def shellquote(s):
274 281 if os.sys.platform == 'OpenVMS':
275 282 return '"%s"' % s
276 283 else:
277 284 return "'%s'" % s.replace("'", "'\\''")
278 285
279 286 def quotecommand(cmd):
280 287 return cmd
281 288
282 289 def popen(command, mode='r'):
283 290 return os.popen(command, mode)
284 291
285 292 def testpid(pid):
286 293 '''return False if pid dead, True if running or not sure'''
287 294 if os.sys.platform == 'OpenVMS':
288 295 return True
289 296 try:
290 297 os.kill(pid, 0)
291 298 return True
292 299 except OSError, inst:
293 300 return inst.errno != errno.ESRCH
294 301
295 302 def explainexit(code):
296 303 """return a 2-tuple (desc, code) describing a subprocess status
297 304 (codes from kill are negative - not os.system/wait encoding)"""
298 305 if code >= 0:
299 306 return _("exited with status %d") % code, code
300 307 return _("killed by signal %d") % -code, -code
301 308
302 309 def isowner(st):
303 310 """Return True if the stat object st is from the current user."""
304 311 return st.st_uid == os.getuid()
305 312
306 313 def findexe(command):
307 314 '''Find executable for command searching like which does.
308 315 If command is a basename then PATH is searched for command.
309 316 PATH isn't searched if command is an absolute or relative path.
310 317 If command isn't found None is returned.'''
311 318 if sys.platform == 'OpenVMS':
312 319 return command
313 320
314 321 def findexisting(executable):
315 322 'Will return executable if existing file'
316 323 if os.path.isfile(executable) and os.access(executable, os.X_OK):
317 324 return executable
318 325 return None
319 326
320 327 if os.sep in command:
321 328 return findexisting(command)
322 329
323 330 for path in os.environ.get('PATH', '').split(os.pathsep):
324 331 executable = findexisting(os.path.join(path, command))
325 332 if executable is not None:
326 333 return executable
327 334 return None
328 335
329 336 def setsignalhandler():
330 337 pass
331 338
332 339 def statfiles(files):
333 340 'Stat each file in files and yield stat or None if file does not exist.'
334 341 lstat = os.lstat
335 342 for nf in files:
336 343 try:
337 344 st = lstat(nf)
338 345 except OSError, err:
339 346 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
340 347 raise
341 348 st = None
342 349 yield st
343 350
344 351 def getuser():
345 352 '''return name of current user'''
346 353 return getpass.getuser()
347 354
348 355 def username(uid=None):
349 356 """Return the name of the user with the given uid.
350 357
351 358 If uid is None, return the name of the current user."""
352 359
353 360 if uid is None:
354 361 uid = os.getuid()
355 362 try:
356 363 return pwd.getpwuid(uid)[0]
357 364 except KeyError:
358 365 return str(uid)
359 366
360 367 def groupname(gid=None):
361 368 """Return the name of the group with the given gid.
362 369
363 370 If gid is None, return the name of the current group."""
364 371
365 372 if gid is None:
366 373 gid = os.getgid()
367 374 try:
368 375 return grp.getgrgid(gid)[0]
369 376 except KeyError:
370 377 return str(gid)
371 378
372 379 def groupmembers(name):
373 380 """Return the list of members of the group with the given
374 381 name, KeyError if the group does not exist.
375 382 """
376 383 return list(grp.getgrnam(name).gr_mem)
377 384
378 385 def spawndetached(args):
379 386 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
380 387 args[0], args)
381 388
382 389 def gethgcmd():
383 390 return sys.argv[:1]
384 391
385 392 def termwidth():
386 393 try:
387 394 import termios, array, fcntl
388 395 for dev in (sys.stderr, sys.stdout, sys.stdin):
389 396 try:
390 397 try:
391 398 fd = dev.fileno()
392 399 except AttributeError:
393 400 continue
394 401 if not os.isatty(fd):
395 402 continue
396 403 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
397 404 width = array.array('h', arri)[1]
398 405 if width > 0:
399 406 return width
400 407 except ValueError:
401 408 pass
402 409 except IOError, e:
403 410 if e[0] == errno.EINVAL:
404 411 pass
405 412 else:
406 413 raise
407 414 except ImportError:
408 415 pass
409 416 return 80
410 417
411 418 def makedir(path, notindexed):
412 419 os.mkdir(path)
413 420
414 421 def unlinkpath(f):
415 422 """unlink and remove the directory if it is empty"""
416 423 os.unlink(f)
417 424 # try removing directories that might now be empty
418 425 try:
419 426 os.removedirs(os.path.dirname(f))
420 427 except OSError:
421 428 pass
422 429
423 430 def lookupreg(key, name=None, scope=None):
424 431 return None
425 432
426 433 def hidewindow():
427 434 """Hide current shell window.
428 435
429 436 Used to hide the window opened when starting asynchronous
430 437 child process under Windows, unneeded on other systems.
431 438 """
432 439 pass
433 440
434 441 class cachestat(object):
435 442 def __init__(self, path):
436 443 self.stat = os.stat(path)
437 444
438 445 def cacheable(self):
439 446 return bool(self.stat.st_ino)
440 447
441 448 __hash__ = object.__hash__
442 449
443 450 def __eq__(self, other):
444 451 try:
445 452 return self.stat == other.stat
446 453 except AttributeError:
447 454 return False
448 455
449 456 def __ne__(self, other):
450 457 return not self == other
451 458
452 459 def executablepath():
453 460 return None # available on Windows only
General Comments 0
You need to be logged in to leave comments. Login now