# HG changeset patch # User Adrian Buehlmann # Date 1302106183 -7200 # Node ID 98ee3dd5bab4c5cb06eb9c6d202cf25936ed5470 # Parent 8f81d6f4047fedc325dc0ff7ba507188d7b9d67d 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. diff -r 8f81d6f4047f -r 98ee3dd5bab4 mercurial/posix.py --- a/mercurial/posix.py Fri Apr 08 17:47:58 2011 +0300 +++ b/mercurial/posix.py Wed Apr 06 18:09:43 2011 +0200 @@ -147,6 +147,11 @@ except (OSError, AttributeError): return False +def checkosfilename(path): + '''Check that the base-relative path is a valid filename on this platform. + Returns None if the path is ok, or a UI string describing the problem.''' + pass # on posix platforms, every path is ok + def set_binary(fd): pass diff -r 8f81d6f4047f -r 98ee3dd5bab4 mercurial/util.py --- a/mercurial/util.py Fri Apr 08 17:47:58 2011 +0300 +++ b/mercurial/util.py Wed Apr 06 18:09:43 2011 +0200 @@ -493,6 +493,48 @@ return hardlink, num +_windows_reserved_filenames = '''con prn aux nul + com1 com2 com3 com4 com5 com6 com7 com8 com9 + lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split() +_windows_reserved_chars = ':*?"<>|' +def checkwinfilename(path): + '''Check that the base-relative path is a valid filename on Windows. + Returns None if the path is ok, or a UI string describing the problem. + + >>> checkwinfilename("just/a/normal/path") + >>> checkwinfilename("foo/bar/con.xml") + "filename contains 'con', which is reserved on Windows" + >>> checkwinfilename("foo/con.xml/bar") + "filename contains 'con', which is reserved on Windows" + >>> checkwinfilename("foo/bar/xml.con") + >>> checkwinfilename("foo/bar/AUX/bla.txt") + "filename contains 'AUX', which is reserved on Windows" + >>> checkwinfilename("foo/bar/bla:.txt") + "filename contains ':', which is reserved on Windows" + >>> checkwinfilename("foo/bar/b\07la.txt") + "filename contains '\\x07', which is invalid on Windows" + >>> checkwinfilename("foo/bar/bla ") + "filename ends with ' ', which is not allowed on Windows" + ''' + for n in path.replace('\\', '/').split('/'): + if not n: + continue + for c in n: + if c in _windows_reserved_chars: + return _("filename contains '%s', which is reserved " + "on Windows") % c + if ord(c) <= 31: + return _("filename contains '%s', which is invalid " + "on Windows") % c + base = n.split('.')[0] + if base and base.lower() in _windows_reserved_filenames: + return _("filename contains '%s', which is reserved " + "on Windows") % base + t = n[-1] + if t in '. ': + return _("filename ends with '%s', which is not allowed " + "on Windows") % t + class path_auditor(object): '''ensure that a filesystem path contains no banned components. the following properties of a path are checked: @@ -560,6 +602,9 @@ prefixes.append(prefix) parts.pop() + r = checkosfilename(path) + if r: + raise Abort("%s: %s" % (r, path)) self.audited.add(path) # only add prefixes to the cache after checking everything: we don't # want to add "foo/bar/baz" before checking if there's a "foo/.hg" @@ -577,6 +622,7 @@ pass if os.name == 'nt': + checkosfilename = checkwinfilename from windows import * else: from posix import *