summaryrefslogtreecommitdiff
path: root/tools/hook-scripts/control-chars.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/hook-scripts/control-chars.py')
-rwxr-xr-xtools/hook-scripts/control-chars.py130
1 files changed, 130 insertions, 0 deletions
diff --git a/tools/hook-scripts/control-chars.py b/tools/hook-scripts/control-chars.py
new file mode 100755
index 0000000..17223fe
--- /dev/null
+++ b/tools/hook-scripts/control-chars.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+#
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+
+'''control-chars.py: Subversion repository hook script that rejects filenames
+which contain control characters. Expects to be called like a pre-commit hook:
+ control-chars.py <REPOS-PATH> <TXN-NAME>
+
+Latest version should be available at
+http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/
+
+See validate-files.py for more generic validations.'''
+
+import sys
+import re
+import posixpath
+
+import svn
+import svn.fs
+import svn.repos
+import svn.core
+
+# Can't hurt to disallow chr(0), though the C API will never pass one anyway.
+control_chars = set( [chr(i) for i in range(32)] )
+control_chars.add(chr(127))
+
+def check_node(node, path):
+ "check NODE for control characters. PATH is used for error messages"
+ if node.action == 'A':
+ if any((c in control_chars) for c in node.name):
+ sys.stderr.write("'%s' contains a control character" % path)
+ return 3
+
+def walk_tree(node, path, callback):
+ "Walk NODE"
+ if not node:
+ return 0
+
+ ret_val = callback(node, path)
+ if ret_val > 0:
+ return ret_val
+
+ node = node.child
+ if not node:
+ return 0
+
+ while node:
+ full_path = posixpath.join(path, node.name)
+ ret_val = walk_tree(node, full_path, callback)
+ # If we ran into an error just return up the stack all the way
+ if ret_val > 0:
+ return ret_val
+ node = node.sibling
+
+ return 0
+
+def usage():
+ sys.stderr.write("Invalid arguments, expects to be called like a pre-commit hook.")
+
+def main(ignored_pool, argv):
+ if len(argv) < 3:
+ usage()
+ return 2
+
+ repos_path = svn.core.svn_path_canonicalize(argv[1])
+ txn_name = argv[2]
+
+ if not repos_path or not txn_name:
+ usage()
+ return 2
+
+ repos = svn.repos.svn_repos_open(repos_path)
+ fs = svn.repos.svn_repos_fs(repos)
+ txn = svn.fs.svn_fs_open_txn(fs, txn_name)
+ txn_root = svn.fs.svn_fs_txn_root(txn)
+ base_rev = svn.fs.svn_fs_txn_base_revision(txn)
+ if base_rev is None or base_rev <= svn.core.SVN_INVALID_REVNUM:
+ sys.stderr.write("Transaction '%s' is not based on a revision" % txn_name)
+ return 2
+ base_root = svn.fs.svn_fs_revision_root(fs, base_rev)
+ editor, editor_baton = svn.repos.svn_repos_node_editor(repos, base_root,
+ txn_root)
+ try:
+ svn.repos.svn_repos_replay2(txn_root, "", svn.core.SVN_INVALID_REVNUM,
+ False, editor, editor_baton, None, None)
+ except svn.core.SubversionException as e:
+ # If we get a file not found error then some file has a newline in it and
+ # fsfs's own transaction is now corrupted.
+ if e.apr_err == svn.core.SVN_ERR_FS_NOT_FOUND:
+ match = re.search("path '(.*?)'", e.message)
+ if not match:
+ sys.stderr.write(repr(e))
+ return 2
+ path = match.group(1)
+ sys.stderr.write("Path name that contains '%s' has a newline." % path)
+ return 3
+ # fs corrupt error probably means that there is probably both
+ # file and file\n in the transaction. However, we can't really determine
+ # which files since the transaction is broken. Even if we didn't reject
+ # this it would not be able to be committed. This just gives a better
+ # error message.
+ elif e.apr_err == svn.core.SVN_ERR_FS_CORRUPT:
+ sys.stderr.write("Some path contains a newline causing: %s" % repr(e))
+ return 3
+ else:
+ sys.stderr.write(repr(e))
+ return 2
+ tree = svn.repos.svn_repos_node_from_baton(editor_baton)
+ return walk_tree(tree, "/", check_node)
+
+if __name__ == '__main__':
+ sys.exit(svn.core.run_app(main, sys.argv))