comparison mercurial/util.py @ 13916:98ee3dd5bab4

path_auditor: check filenames for basic platform validity (issue2755) Example (on Windows): $ hg parents $ hg manifest tip con.xml $ hg update abort: filename contains 'con', which is reserved on Windows: con.xml Before this patch, update produced (as explained in issue2755): $ hg update abort: No usable temporary filename found I've added the new function checkwinfilename to util.py and not to windows.py, so that we can later call it when running on posix platforms too, for when we decide to implement a (configurable) warning message on 'hg add'. As per this patch, checkwinfilename is currently only used when running on Windwows. path_auditor calls checkosfilename, which is a NOP on posix and an alias for checkwinfilename on Windows.
author Adrian Buehlmann <adrian@cadifra.com>
date Wed, 06 Apr 2011 18:09:43 +0200
parents 93452579df9e
children 61ba09d8d118
comparison
equal deleted inserted replaced
13915:8f81d6f4047f 13916:98ee3dd5bab4
490 else: 490 else:
491 shutil.copy(src, dst) 491 shutil.copy(src, dst)
492 num += 1 492 num += 1
493 493
494 return hardlink, num 494 return hardlink, num
495
496 _windows_reserved_filenames = '''con prn aux nul
497 com1 com2 com3 com4 com5 com6 com7 com8 com9
498 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
499 _windows_reserved_chars = ':*?"<>|'
500 def checkwinfilename(path):
501 '''Check that the base-relative path is a valid filename on Windows.
502 Returns None if the path is ok, or a UI string describing the problem.
503
504 >>> checkwinfilename("just/a/normal/path")
505 >>> checkwinfilename("foo/bar/con.xml")
506 "filename contains 'con', which is reserved on Windows"
507 >>> checkwinfilename("foo/con.xml/bar")
508 "filename contains 'con', which is reserved on Windows"
509 >>> checkwinfilename("foo/bar/xml.con")
510 >>> checkwinfilename("foo/bar/AUX/bla.txt")
511 "filename contains 'AUX', which is reserved on Windows"
512 >>> checkwinfilename("foo/bar/bla:.txt")
513 "filename contains ':', which is reserved on Windows"
514 >>> checkwinfilename("foo/bar/b\07la.txt")
515 "filename contains '\\x07', which is invalid on Windows"
516 >>> checkwinfilename("foo/bar/bla ")
517 "filename ends with ' ', which is not allowed on Windows"
518 '''
519 for n in path.replace('\\', '/').split('/'):
520 if not n:
521 continue
522 for c in n:
523 if c in _windows_reserved_chars:
524 return _("filename contains '%s', which is reserved "
525 "on Windows") % c
526 if ord(c) <= 31:
527 return _("filename contains '%s', which is invalid "
528 "on Windows") % c
529 base = n.split('.')[0]
530 if base and base.lower() in _windows_reserved_filenames:
531 return _("filename contains '%s', which is reserved "
532 "on Windows") % base
533 t = n[-1]
534 if t in '. ':
535 return _("filename ends with '%s', which is not allowed "
536 "on Windows") % t
495 537
496 class path_auditor(object): 538 class path_auditor(object):
497 '''ensure that a filesystem path contains no banned components. 539 '''ensure that a filesystem path contains no banned components.
498 the following properties of a path are checked: 540 the following properties of a path are checked:
499 541
558 break 600 break
559 check(prefix) 601 check(prefix)
560 prefixes.append(prefix) 602 prefixes.append(prefix)
561 parts.pop() 603 parts.pop()
562 604
605 r = checkosfilename(path)
606 if r:
607 raise Abort("%s: %s" % (r, path))
563 self.audited.add(path) 608 self.audited.add(path)
564 # only add prefixes to the cache after checking everything: we don't 609 # only add prefixes to the cache after checking everything: we don't
565 # want to add "foo/bar/baz" before checking if there's a "foo/.hg" 610 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
566 self.auditeddir.update(prefixes) 611 self.auditeddir.update(prefixes)
567 612
575 child process under Windows, unneeded on other systems. 620 child process under Windows, unneeded on other systems.
576 """ 621 """
577 pass 622 pass
578 623
579 if os.name == 'nt': 624 if os.name == 'nt':
625 checkosfilename = checkwinfilename
580 from windows import * 626 from windows import *
581 else: 627 else:
582 from posix import * 628 from posix import *
583 629
584 def makelock(info, pathname): 630 def makelock(info, pathname):