##// END OF EJS Templates
checklink: always close the NamedTemporaryFile...
Augie Fackler -
r22946:77c121da default
parent child Browse files
Show More
@@ -1,593 +1,596 b''
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 encoding
10 10 import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata
11 11 import fcntl
12 12
13 13 posixfile = open
14 14 normpath = os.path.normpath
15 15 samestat = os.path.samestat
16 16 oslink = os.link
17 17 unlink = os.unlink
18 18 rename = os.rename
19 19 expandglobs = False
20 20
21 21 umask = os.umask(0)
22 22 os.umask(umask)
23 23
24 24 def split(p):
25 25 '''Same as posixpath.split, but faster
26 26
27 27 >>> import posixpath
28 28 >>> for f in ['/absolute/path/to/file',
29 29 ... 'relative/path/to/file',
30 30 ... 'file_alone',
31 31 ... 'path/to/directory/',
32 32 ... '/multiple/path//separators',
33 33 ... '/file_at_root',
34 34 ... '///multiple_leading_separators_at_root',
35 35 ... '']:
36 36 ... assert split(f) == posixpath.split(f), f
37 37 '''
38 38 ht = p.rsplit('/', 1)
39 39 if len(ht) == 1:
40 40 return '', p
41 41 nh = ht[0].rstrip('/')
42 42 if nh:
43 43 return nh, ht[1]
44 44 return ht[0] + '/', ht[1]
45 45
46 46 def openhardlinks():
47 47 '''return true if it is safe to hold open file handles to hardlinks'''
48 48 return True
49 49
50 50 def nlinks(name):
51 51 '''return number of hardlinks for the given file'''
52 52 return os.lstat(name).st_nlink
53 53
54 54 def parsepatchoutput(output_line):
55 55 """parses the output produced by patch and returns the filename"""
56 56 pf = output_line[14:]
57 57 if os.sys.platform == 'OpenVMS':
58 58 if pf[0] == '`':
59 59 pf = pf[1:-1] # Remove the quotes
60 60 else:
61 61 if pf.startswith("'") and pf.endswith("'") and " " in pf:
62 62 pf = pf[1:-1] # Remove the quotes
63 63 return pf
64 64
65 65 def sshargs(sshcmd, host, user, port):
66 66 '''Build argument list for ssh'''
67 67 args = user and ("%s@%s" % (user, host)) or host
68 68 return port and ("%s -p %s" % (args, port)) or args
69 69
70 70 def isexec(f):
71 71 """check whether a file is executable"""
72 72 return (os.lstat(f).st_mode & 0100 != 0)
73 73
74 74 def setflags(f, l, x):
75 75 s = os.lstat(f).st_mode
76 76 if l:
77 77 if not stat.S_ISLNK(s):
78 78 # switch file to link
79 79 fp = open(f)
80 80 data = fp.read()
81 81 fp.close()
82 82 os.unlink(f)
83 83 try:
84 84 os.symlink(data, f)
85 85 except OSError:
86 86 # failed to make a link, rewrite file
87 87 fp = open(f, "w")
88 88 fp.write(data)
89 89 fp.close()
90 90 # no chmod needed at this point
91 91 return
92 92 if stat.S_ISLNK(s):
93 93 # switch link to file
94 94 data = os.readlink(f)
95 95 os.unlink(f)
96 96 fp = open(f, "w")
97 97 fp.write(data)
98 98 fp.close()
99 99 s = 0666 & ~umask # avoid restatting for chmod
100 100
101 101 sx = s & 0100
102 102 if x and not sx:
103 103 # Turn on +x for every +r bit when making a file executable
104 104 # and obey umask.
105 105 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
106 106 elif not x and sx:
107 107 # Turn off all +x bits
108 108 os.chmod(f, s & 0666)
109 109
110 110 def copymode(src, dst, mode=None):
111 111 '''Copy the file mode from the file at path src to dst.
112 112 If src doesn't exist, we're using mode instead. If mode is None, we're
113 113 using umask.'''
114 114 try:
115 115 st_mode = os.lstat(src).st_mode & 0777
116 116 except OSError, inst:
117 117 if inst.errno != errno.ENOENT:
118 118 raise
119 119 st_mode = mode
120 120 if st_mode is None:
121 121 st_mode = ~umask
122 122 st_mode &= 0666
123 123 os.chmod(dst, st_mode)
124 124
125 125 def checkexec(path):
126 126 """
127 127 Check whether the given path is on a filesystem with UNIX-like exec flags
128 128
129 129 Requires a directory (like /foo/.hg)
130 130 """
131 131
132 132 # VFAT on some Linux versions can flip mode but it doesn't persist
133 133 # a FS remount. Frequently we can detect it if files are created
134 134 # with exec bit on.
135 135
136 136 try:
137 137 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
138 138 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
139 139 try:
140 140 os.close(fh)
141 141 m = os.stat(fn).st_mode & 0777
142 142 new_file_has_exec = m & EXECFLAGS
143 143 os.chmod(fn, m ^ EXECFLAGS)
144 144 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
145 145 finally:
146 146 os.unlink(fn)
147 147 except (IOError, OSError):
148 148 # we don't care, the user probably won't be able to commit anyway
149 149 return False
150 150 return not (new_file_has_exec or exec_flags_cannot_flip)
151 151
152 152 def checklink(path):
153 153 """check whether the given path is on a symlink-capable filesystem"""
154 154 # mktemp is not racy because symlink creation will fail if the
155 155 # file already exists
156 156 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
157 157 try:
158 158 fd = tempfile.NamedTemporaryFile(dir=path, prefix='hg-checklink-')
159 os.symlink(os.path.basename(fd.name), name)
160 os.unlink(name)
161 return True
159 try:
160 os.symlink(os.path.basename(fd.name), name)
161 os.unlink(name)
162 return True
163 finally:
164 fd.close()
162 165 except AttributeError:
163 166 return False
164 167 except OSError, inst:
165 168 # sshfs might report failure while successfully creating the link
166 169 if inst[0] == errno.EIO and os.path.exists(name):
167 170 os.unlink(name)
168 171 return False
169 172
170 173 def checkosfilename(path):
171 174 '''Check that the base-relative path is a valid filename on this platform.
172 175 Returns None if the path is ok, or a UI string describing the problem.'''
173 176 pass # on posix platforms, every path is ok
174 177
175 178 def setbinary(fd):
176 179 pass
177 180
178 181 def pconvert(path):
179 182 return path
180 183
181 184 def localpath(path):
182 185 return path
183 186
184 187 def samefile(fpath1, fpath2):
185 188 """Returns whether path1 and path2 refer to the same file. This is only
186 189 guaranteed to work for files, not directories."""
187 190 return os.path.samefile(fpath1, fpath2)
188 191
189 192 def samedevice(fpath1, fpath2):
190 193 """Returns whether fpath1 and fpath2 are on the same device. This is only
191 194 guaranteed to work for files, not directories."""
192 195 st1 = os.lstat(fpath1)
193 196 st2 = os.lstat(fpath2)
194 197 return st1.st_dev == st2.st_dev
195 198
196 199 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
197 200 def normcase(path):
198 201 return path.lower()
199 202
200 203 if sys.platform == 'darwin':
201 204
202 205 def normcase(path):
203 206 '''
204 207 Normalize a filename for OS X-compatible comparison:
205 208 - escape-encode invalid characters
206 209 - decompose to NFD
207 210 - lowercase
208 211
209 212 >>> normcase('UPPER')
210 213 'upper'
211 214 >>> normcase('Caf\xc3\xa9')
212 215 'cafe\\xcc\\x81'
213 216 >>> normcase('\xc3\x89')
214 217 'e\\xcc\\x81'
215 218 >>> normcase('\xb8\xca\xc3\xca\xbe\xc8.JPG') # issue3918
216 219 '%b8%ca%c3\\xca\\xbe%c8.jpg'
217 220 '''
218 221
219 222 try:
220 223 return encoding.asciilower(path) # exception for non-ASCII
221 224 except UnicodeDecodeError:
222 225 pass
223 226 try:
224 227 u = path.decode('utf-8')
225 228 except UnicodeDecodeError:
226 229 # OS X percent-encodes any bytes that aren't valid utf-8
227 230 s = ''
228 231 g = ''
229 232 l = 0
230 233 for c in path:
231 234 o = ord(c)
232 235 if l and o < 128 or o >= 192:
233 236 # we want a continuation byte, but didn't get one
234 237 s += ''.join(["%%%02X" % ord(x) for x in g])
235 238 g = ''
236 239 l = 0
237 240 if l == 0 and o < 128:
238 241 # ascii
239 242 s += c
240 243 elif l == 0 and 194 <= o < 245:
241 244 # valid leading bytes
242 245 if o < 224:
243 246 l = 1
244 247 elif o < 240:
245 248 l = 2
246 249 else:
247 250 l = 3
248 251 g = c
249 252 elif l > 0 and 128 <= o < 192:
250 253 # valid continuations
251 254 g += c
252 255 l -= 1
253 256 if not l:
254 257 s += g
255 258 g = ''
256 259 else:
257 260 # invalid
258 261 s += "%%%02X" % o
259 262
260 263 # any remaining partial characters
261 264 s += ''.join(["%%%02X" % ord(x) for x in g])
262 265 u = s.decode('utf-8')
263 266
264 267 # Decompose then lowercase (HFS+ technote specifies lower)
265 268 return unicodedata.normalize('NFD', u).lower().encode('utf-8')
266 269
267 270 if sys.platform == 'cygwin':
268 271 # workaround for cygwin, in which mount point part of path is
269 272 # treated as case sensitive, even though underlying NTFS is case
270 273 # insensitive.
271 274
272 275 # default mount points
273 276 cygwinmountpoints = sorted([
274 277 "/usr/bin",
275 278 "/usr/lib",
276 279 "/cygdrive",
277 280 ], reverse=True)
278 281
279 282 # use upper-ing as normcase as same as NTFS workaround
280 283 def normcase(path):
281 284 pathlen = len(path)
282 285 if (pathlen == 0) or (path[0] != os.sep):
283 286 # treat as relative
284 287 return encoding.upper(path)
285 288
286 289 # to preserve case of mountpoint part
287 290 for mp in cygwinmountpoints:
288 291 if not path.startswith(mp):
289 292 continue
290 293
291 294 mplen = len(mp)
292 295 if mplen == pathlen: # mount point itself
293 296 return mp
294 297 if path[mplen] == os.sep:
295 298 return mp + encoding.upper(path[mplen:])
296 299
297 300 return encoding.upper(path)
298 301
299 302 # Cygwin translates native ACLs to POSIX permissions,
300 303 # but these translations are not supported by native
301 304 # tools, so the exec bit tends to be set erroneously.
302 305 # Therefore, disable executable bit access on Cygwin.
303 306 def checkexec(path):
304 307 return False
305 308
306 309 # Similarly, Cygwin's symlink emulation is likely to create
307 310 # problems when Mercurial is used from both Cygwin and native
308 311 # Windows, with other native tools, or on shared volumes
309 312 def checklink(path):
310 313 return False
311 314
312 315 def shellquote(s):
313 316 if os.sys.platform == 'OpenVMS':
314 317 return '"%s"' % s
315 318 else:
316 319 return "'%s'" % s.replace("'", "'\\''")
317 320
318 321 def quotecommand(cmd):
319 322 return cmd
320 323
321 324 def popen(command, mode='r'):
322 325 return os.popen(command, mode)
323 326
324 327 def testpid(pid):
325 328 '''return False if pid dead, True if running or not sure'''
326 329 if os.sys.platform == 'OpenVMS':
327 330 return True
328 331 try:
329 332 os.kill(pid, 0)
330 333 return True
331 334 except OSError, inst:
332 335 return inst.errno != errno.ESRCH
333 336
334 337 def explainexit(code):
335 338 """return a 2-tuple (desc, code) describing a subprocess status
336 339 (codes from kill are negative - not os.system/wait encoding)"""
337 340 if code >= 0:
338 341 return _("exited with status %d") % code, code
339 342 return _("killed by signal %d") % -code, -code
340 343
341 344 def isowner(st):
342 345 """Return True if the stat object st is from the current user."""
343 346 return st.st_uid == os.getuid()
344 347
345 348 def findexe(command):
346 349 '''Find executable for command searching like which does.
347 350 If command is a basename then PATH is searched for command.
348 351 PATH isn't searched if command is an absolute or relative path.
349 352 If command isn't found None is returned.'''
350 353 if sys.platform == 'OpenVMS':
351 354 return command
352 355
353 356 def findexisting(executable):
354 357 'Will return executable if existing file'
355 358 if os.path.isfile(executable) and os.access(executable, os.X_OK):
356 359 return executable
357 360 return None
358 361
359 362 if os.sep in command:
360 363 return findexisting(command)
361 364
362 365 if sys.platform == 'plan9':
363 366 return findexisting(os.path.join('/bin', command))
364 367
365 368 for path in os.environ.get('PATH', '').split(os.pathsep):
366 369 executable = findexisting(os.path.join(path, command))
367 370 if executable is not None:
368 371 return executable
369 372 return None
370 373
371 374 def setsignalhandler():
372 375 pass
373 376
374 377 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
375 378
376 379 def statfiles(files):
377 380 '''Stat each file in files. Yield each stat, or None if a file does not
378 381 exist or has a type we don't care about.'''
379 382 lstat = os.lstat
380 383 getkind = stat.S_IFMT
381 384 for nf in files:
382 385 try:
383 386 st = lstat(nf)
384 387 if getkind(st.st_mode) not in _wantedkinds:
385 388 st = None
386 389 except OSError, err:
387 390 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
388 391 raise
389 392 st = None
390 393 yield st
391 394
392 395 def getuser():
393 396 '''return name of current user'''
394 397 return getpass.getuser()
395 398
396 399 def username(uid=None):
397 400 """Return the name of the user with the given uid.
398 401
399 402 If uid is None, return the name of the current user."""
400 403
401 404 if uid is None:
402 405 uid = os.getuid()
403 406 try:
404 407 return pwd.getpwuid(uid)[0]
405 408 except KeyError:
406 409 return str(uid)
407 410
408 411 def groupname(gid=None):
409 412 """Return the name of the group with the given gid.
410 413
411 414 If gid is None, return the name of the current group."""
412 415
413 416 if gid is None:
414 417 gid = os.getgid()
415 418 try:
416 419 return grp.getgrgid(gid)[0]
417 420 except KeyError:
418 421 return str(gid)
419 422
420 423 def groupmembers(name):
421 424 """Return the list of members of the group with the given
422 425 name, KeyError if the group does not exist.
423 426 """
424 427 return list(grp.getgrnam(name).gr_mem)
425 428
426 429 def spawndetached(args):
427 430 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
428 431 args[0], args)
429 432
430 433 def gethgcmd():
431 434 return sys.argv[:1]
432 435
433 436 def termwidth():
434 437 try:
435 438 import termios, array
436 439 for dev in (sys.stderr, sys.stdout, sys.stdin):
437 440 try:
438 441 try:
439 442 fd = dev.fileno()
440 443 except AttributeError:
441 444 continue
442 445 if not os.isatty(fd):
443 446 continue
444 447 try:
445 448 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
446 449 width = array.array('h', arri)[1]
447 450 if width > 0:
448 451 return width
449 452 except AttributeError:
450 453 pass
451 454 except ValueError:
452 455 pass
453 456 except IOError, e:
454 457 if e[0] == errno.EINVAL:
455 458 pass
456 459 else:
457 460 raise
458 461 except ImportError:
459 462 pass
460 463 return 80
461 464
462 465 def makedir(path, notindexed):
463 466 os.mkdir(path)
464 467
465 468 def unlinkpath(f, ignoremissing=False):
466 469 """unlink and remove the directory if it is empty"""
467 470 try:
468 471 os.unlink(f)
469 472 except OSError, e:
470 473 if not (ignoremissing and e.errno == errno.ENOENT):
471 474 raise
472 475 # try removing directories that might now be empty
473 476 try:
474 477 os.removedirs(os.path.dirname(f))
475 478 except OSError:
476 479 pass
477 480
478 481 def lookupreg(key, name=None, scope=None):
479 482 return None
480 483
481 484 def hidewindow():
482 485 """Hide current shell window.
483 486
484 487 Used to hide the window opened when starting asynchronous
485 488 child process under Windows, unneeded on other systems.
486 489 """
487 490 pass
488 491
489 492 class cachestat(object):
490 493 def __init__(self, path):
491 494 self.stat = os.stat(path)
492 495
493 496 def cacheable(self):
494 497 return bool(self.stat.st_ino)
495 498
496 499 __hash__ = object.__hash__
497 500
498 501 def __eq__(self, other):
499 502 try:
500 503 # Only dev, ino, size, mtime and atime are likely to change. Out
501 504 # of these, we shouldn't compare atime but should compare the
502 505 # rest. However, one of the other fields changing indicates
503 506 # something fishy going on, so return False if anything but atime
504 507 # changes.
505 508 return (self.stat.st_mode == other.stat.st_mode and
506 509 self.stat.st_ino == other.stat.st_ino and
507 510 self.stat.st_dev == other.stat.st_dev and
508 511 self.stat.st_nlink == other.stat.st_nlink and
509 512 self.stat.st_uid == other.stat.st_uid and
510 513 self.stat.st_gid == other.stat.st_gid and
511 514 self.stat.st_size == other.stat.st_size and
512 515 self.stat.st_mtime == other.stat.st_mtime and
513 516 self.stat.st_ctime == other.stat.st_ctime)
514 517 except AttributeError:
515 518 return False
516 519
517 520 def __ne__(self, other):
518 521 return not self == other
519 522
520 523 def executablepath():
521 524 return None # available on Windows only
522 525
523 526 class unixdomainserver(socket.socket):
524 527 def __init__(self, join, subsystem):
525 528 '''Create a unix domain socket with the given prefix.'''
526 529 super(unixdomainserver, self).__init__(socket.AF_UNIX)
527 530 sockname = subsystem + '.sock'
528 531 self.realpath = self.path = join(sockname)
529 532 if os.path.islink(self.path):
530 533 if os.path.exists(self.path):
531 534 self.realpath = os.readlink(self.path)
532 535 else:
533 536 os.unlink(self.path)
534 537 try:
535 538 self.bind(self.realpath)
536 539 except socket.error, err:
537 540 if err.args[0] == 'AF_UNIX path too long':
538 541 tmpdir = tempfile.mkdtemp(prefix='hg-%s-' % subsystem)
539 542 self.realpath = os.path.join(tmpdir, sockname)
540 543 try:
541 544 self.bind(self.realpath)
542 545 os.symlink(self.realpath, self.path)
543 546 except (OSError, socket.error):
544 547 self.cleanup()
545 548 raise
546 549 else:
547 550 raise
548 551 self.listen(5)
549 552
550 553 def cleanup(self):
551 554 def okayifmissing(f, path):
552 555 try:
553 556 f(path)
554 557 except OSError, err:
555 558 if err.errno != errno.ENOENT:
556 559 raise
557 560
558 561 okayifmissing(os.unlink, self.path)
559 562 if self.realpath != self.path:
560 563 okayifmissing(os.unlink, self.realpath)
561 564 okayifmissing(os.rmdir, os.path.dirname(self.realpath))
562 565
563 566 def statislink(st):
564 567 '''check whether a stat result is a symlink'''
565 568 return st and stat.S_ISLNK(st.st_mode)
566 569
567 570 def statisexec(st):
568 571 '''check whether a stat result is an executable file'''
569 572 return st and (st.st_mode & 0100 != 0)
570 573
571 574 def readpipe(pipe):
572 575 """Read all available data from a pipe."""
573 576 # We can't fstat() a pipe because Linux will always report 0.
574 577 # So, we set the pipe to non-blocking mode and read everything
575 578 # that's available.
576 579 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
577 580 flags |= os.O_NONBLOCK
578 581 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
579 582
580 583 try:
581 584 chunks = []
582 585 while True:
583 586 try:
584 587 s = pipe.read()
585 588 if not s:
586 589 break
587 590 chunks.append(s)
588 591 except IOError:
589 592 break
590 593
591 594 return ''.join(chunks)
592 595 finally:
593 596 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
General Comments 0
You need to be logged in to leave comments. Login now