summaryrefslogtreecommitdiffstats
path: root/tools/checkperms
diff options
context:
space:
mode:
authorthestig@chromium.org <thestig@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-11 23:58:47 +0000
committerthestig@chromium.org <thestig@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-11 23:58:47 +0000
commit2bcf72c01df234cd8e0388af3ca7daa22884df7d (patch)
tree7cfc705e73d3950c1021194f5e20bbed2897bdee /tools/checkperms
parent24e7253b4bdae5e56f32eab1f3de22e3f81527bc (diff)
downloadchromium_src-2bcf72c01df234cd8e0388af3ca7daa22884df7d.zip
chromium_src-2bcf72c01df234cd8e0388af3ca7daa22884df7d.tar.gz
chromium_src-2bcf72c01df234cd8e0388af3ca7daa22884df7d.tar.bz2
Add a tool to check for svn:executable file permissions. People with badly configured SCM clients accidentally flips this bit on our files all the time. This will help catch the culprits.
BUG=none TEST=none Review URL: http://codereview.chromium.org/1929001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@49616 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/checkperms')
-rwxr-xr-xtools/checkperms/checkperms.py334
1 files changed, 334 insertions, 0 deletions
diff --git a/tools/checkperms/checkperms.py b/tools/checkperms/checkperms.py
new file mode 100755
index 0000000..2dde576
--- /dev/null
+++ b/tools/checkperms/checkperms.py
@@ -0,0 +1,334 @@
+#!/usr/bin/python
+# Copyright (c) 2010 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Makes sure files have the right permissions.
+
+Some developers have broken SCM configurations that flip the svn:executable
+permission on for no good reason. Unix developers who run ls --color will then
+see .cc files in green and get confused.
+
+To ignore a particular file, add it to WHITELIST_FILES.
+To ignore a particular extension, add it to WHITELIST_EXTENSIONS.
+To ignore whatever regexps your heart desires, add it WHITELIST_REGEX.
+
+Note that all directory separators must be slashes (Unix-style) and not
+backslashes. All directories should be relative to the source root and all
+file paths should be only lowercase.
+"""
+
+import optparse
+import os
+import pipes
+import re
+import stat
+import sys
+
+#### USER EDITABLE SECTION STARTS HERE ####
+
+# Files with these extensions are allowed to have executable permissions.
+WHITELIST_EXTENSIONS = [
+ 'bash',
+ 'bat',
+ 'dylib',
+ 'pl',
+ 'py',
+ 'rb',
+ 'sh',
+]
+
+# Files that end the following paths are whitelisted too.
+WHITELIST_FILES = [
+ '/build/gyp_chromium',
+ '/build/linux/dump_app_syms',
+ '/build/linux/pkg-config-wrapper',
+ '/build/mac/strip_from_xcode',
+ '/build/mac/strip_save_dsym',
+ '/chrome/tools/build/linux/chrome-wrapper',
+ '/chrome/tools/build/mac/build_app_dmg',
+ '/chrome/tools/build/mac/clean_up_old_versions',
+ '/chrome/tools/build/mac/copy_framework_unversioned',
+ '/chrome/tools/build/mac/dump_product_syms',
+ '/chrome/tools/build/mac/generate_localizer',
+ '/chrome/tools/build/mac/make_sign_sh',
+ '/chrome/tools/build/mac/pkg-dmg',
+ '/chrome/tools/build/mac/tweak_info_plist',
+ '/chrome/tools/build/mac/verify_order',
+ '/o3d/build/gyp_o3d',
+ '/o3d/gypbuild',
+ '/o3d/installer/linux/debian.in/rules',
+ '/third_party/icu/source/configure',
+ '/third_party/icu/source/install-sh',
+ '/third_party/icu/source/runconfigureicu',
+ '/third_party/lcov/bin/gendesc',
+ '/third_party/lcov/bin/genhtml',
+ '/third_party/lcov/bin/geninfo',
+ '/third_party/lcov/bin/genpng',
+ '/third_party/lcov/bin/lcov',
+ '/third_party/lcov/bin/mcov',
+ '/third_party/libevent/configure',
+ '/third_party/libxml/linux/xml2-config',
+ '/third_party/lzma_sdk/executable/7za.exe',
+ '/third_party/swig/linux/swig',
+ '/third_party/tcmalloc/chromium/src/pprof',
+ '/tools/git/post-checkout',
+ '/tools/git/post-merge',
+ '/tools/git/post-merge',
+]
+
+# File paths that contain these regexps will be whitelisted as well.
+WHITELIST_REGEX = [
+ re.compile('/third_party/sqlite/'),
+ re.compile('/third_party/xdg-utils/'),
+ re.compile('/third_party/yasm/source/patched-yasm/config'),
+]
+
+#### USER EDITABLE SECTION ENDS HERE ####
+
+WHITELIST_EXTENSIONS_REGEX = re.compile(r'\.(%s)' %
+ '|'.join(WHITELIST_EXTENSIONS))
+
+WHITELIST_FILES_REGEX = re.compile(r'(%s)$' % '|'.join(WHITELIST_FILES))
+
+# Set to true for more output. This is set by the command line options.
+VERBOSE = False
+
+# In lowercase, using forward slashes as directory separators, ending in a
+# forward slash. Set by the command line options.
+BASE_DIRECTORY = ''
+
+# The default if BASE_DIRECTORY is not set on the command line.
+DEFAULT_BASE_DIRECTORY = '../../..'
+
+# The directories which contain the sources managed by git.
+GIT_SOURCE_DIRECTORY = set()
+
+# The SVN repository url.
+SVN_REPO_URL = ''
+
+# Whether we are using SVN or GIT.
+IS_SVN = True
+
+# Executable permission mask
+EXECUTABLE_PERMISSION = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
+
+
+def IsWhiteListedExtension(file_path):
+ """Returns True if file_path has an extension we want to ignore."""
+ return WHITELIST_EXTENSIONS_REGEX.match(os.path.splitext(file_path)[1])
+
+
+def IsWhiteListedPath(file_path):
+ """Returns True if file_path ends with a path we want to ignore."""
+ return WHITELIST_FILES_REGEX.search(file_path)
+
+
+def IsWhiteListedRegex(file_path):
+ """Returns True if file_path match any of the whitelist regexps."""
+ for regex in WHITELIST_REGEX:
+ if regex.search(file_path):
+ return True
+ return False
+
+
+def CheckFile(file_path):
+ """Checks file_path's permissions.
+
+ Args:
+ file_path: The file path to check.
+
+ Returns:
+ Either a string describing the error if there was one, or None if the file
+ checked out OK.
+ """
+ if VERBOSE:
+ print 'Checking file: ' + file_path
+
+ file_path_lower = file_path.lower()
+ # Check to see if it's whitelisted.
+ if IsWhiteListedExtension(file_path_lower):
+ return None
+ if IsWhiteListedPath(file_path_lower):
+ return None
+ if IsWhiteListedRegex(file_path_lower):
+ return None
+
+ # Not whitelisted, stat the file and check permissions.
+ try:
+ st_mode = os.stat(file_path).st_mode
+ except IOError, e:
+ return 'Failed to stat file: %s' % e
+ except OSError, e:
+ return 'Failed to stat file: %s' % e
+
+ if EXECUTABLE_PERMISSION & st_mode:
+ error = 'Contains executable permission'
+ if VERBOSE:
+ return '%s: %06o' % (error, st_mode)
+ return error
+ return None
+
+
+def ShouldCheckDirectory(dir_path):
+ """Determine if we should check the content of dir_path."""
+ if not IS_SVN:
+ return dir_path in GIT_SOURCE_DIRECTORY
+ repo_url = GetSvnRepositoryRoot(dir_path)
+ if not repo_url:
+ return False
+ return repo_url == SVN_REPO_URL
+
+
+def CheckDirectory(dir_path):
+ """Check the files in dir_path; recursively check its subdirectories."""
+ # Collect a list of all files and directories to check.
+ files_to_check = []
+ dirs_to_check = []
+ success = True
+ contents = os.listdir(dir_path)
+ for cur in contents:
+ full_path = os.path.join(dir_path, cur)
+ if os.path.isdir(full_path) and ShouldCheckDirectory(full_path):
+ dirs_to_check.append(full_path)
+ elif os.path.isfile(full_path):
+ files_to_check.append(full_path)
+
+ # First check all files in this directory.
+ for cur_file in files_to_check:
+ file_status = CheckFile(cur_file)
+ if file_status is not None:
+ print 'ERROR in %s\n%s' % (cur_file, file_status)
+ success = False
+
+ # Next recurse into the subdirectories.
+ for cur_dir in dirs_to_check:
+ if not CheckDirectory(cur_dir):
+ success = False
+ return success
+
+
+def GetGitSourceDirectory(root):
+ """Returns a set of the directories to be checked.
+
+ Args:
+ root: The repository root where a .git directory must exist.
+
+ Returns:
+ A set of directories which contain sources managed by git.
+ """
+ git_source_directory = set()
+ popen_out = os.popen('cd %s && git ls-files --full-name .' %
+ pipes.quote(root))
+ for line in popen_out:
+ dir_path = os.path.join(root, os.path.dirname(line))
+ git_source_directory.add(dir_path)
+ git_source_directory.add(root)
+ return git_source_directory
+
+
+def GetSvnRepositoryRoot(dir_path):
+ """Returns the repository root for a directory.
+
+ Args:
+ dir_path: A directory where a .svn subdirectory should exist.
+
+ Returns:
+ The svn repository that contains dir_path or None.
+ """
+ svn_dir = os.path.join(dir_path, '.svn')
+ if not os.path.isdir(svn_dir):
+ return None
+ popen_out = os.popen('cd %s && svn info' % pipes.quote(dir_path))
+ for line in popen_out:
+ if line.startswith('Repository Root: '):
+ return line[len('Repository Root: '):].rstrip()
+ return None
+
+
+def main(argv):
+ usage = """Usage: python %prog [--root <root>] [tocheck]
+ tocheck Specifies the directory, relative to root, to check. This defaults
+ to "." so it checks everything.
+
+Examples:
+ python checkperms.py
+ python checkperms.py --root /path/to/source chrome"""
+
+ option_parser = optparse.OptionParser(usage=usage)
+ option_parser.add_option('--root', dest='base_directory',
+ default=DEFAULT_BASE_DIRECTORY,
+ help='Specifies the repository root. This defaults '
+ 'to %default relative to the script file, which '
+ 'will normally be the repository root.')
+ option_parser.add_option('-v', '--verbose', action='store_true',
+ help='Print debug logging')
+ options, args = option_parser.parse_args()
+
+ global VERBOSE
+ if options.verbose:
+ VERBOSE = True
+
+ # Optional base directory of the repository.
+ global BASE_DIRECTORY
+ if (not options.base_directory or
+ options.base_directory == DEFAULT_BASE_DIRECTORY):
+ BASE_DIRECTORY = os.path.abspath(
+ os.path.join(os.path.abspath(argv[0]), DEFAULT_BASE_DIRECTORY))
+ else:
+ BASE_DIRECTORY = os.path.abspath(argv[2])
+
+ # Figure out which directory we have to check.
+ if not args:
+ # No directory to check specified, use the repository root.
+ start_dir = BASE_DIRECTORY
+ elif len(args) == 1:
+ # Directory specified. Start here. It's supposed to be relative to the
+ # base directory.
+ start_dir = os.path.abspath(os.path.join(BASE_DIRECTORY, args[0]))
+ else:
+ # More than one argument, we don't handle this.
+ option_parser.print_help()
+ return 1
+
+ print 'Using base directory:', BASE_DIRECTORY
+ print 'Checking directory:', start_dir
+
+ # The base directory should be lower case from here on since it will be used
+ # for substring matching against whitelists that all assume lowercase, and we
+ # compile on case-insensitive systems. Plus, we always use slashes here since
+ # the include parsing code will also normalize to slashes.
+ BASE_DIRECTORY = BASE_DIRECTORY.lower()
+ BASE_DIRECTORY = BASE_DIRECTORY.replace('\\', '/')
+ start_dir = start_dir.replace('\\', '/')
+
+ success = True
+ if os.path.exists(os.path.join(BASE_DIRECTORY, '.svn')):
+ global SVN_REPO_URL
+ SVN_REPO_URL = GetSvnRepositoryRoot(BASE_DIRECTORY)
+ if not SVN_REPO_URL:
+ print 'Cannot determine the SVN repo URL'
+ success = False
+ elif os.path.exists(os.path.join(BASE_DIRECTORY, '.git')):
+ global IS_SVN
+ IS_SVN = False
+ global GIT_SOURCE_DIRECTORY
+ GIT_SOURCE_DIRECTORY = GetGitSourceDirectory(BASE_DIRECTORY)
+ if not GIT_SOURCE_DIRECTORY:
+ print 'Cannot determine the list of GIT directories'
+ success = False
+ else:
+ print 'Cannot determine the SCM used in %s' % BASE_DIRECTORY
+ success = False
+
+ if success:
+ success = CheckDirectory(start_dir)
+ if not success:
+ print '\nFAILED\n'
+ return 1
+ print '\nSUCCESS\n'
+ return 0
+
+
+if '__main__' == __name__:
+ sys.exit(main(sys.argv))