Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(252)

Side by Side Diff: tools/checkperms/checkperms.py

Issue 10088002: Remove over 100 lines from checkperms.py. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tools/checkperms/pylintrc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Makes sure files have the right permissions. 6 """Makes sure files have the right permissions.
7 7
8 Some developers have broken SCM configurations that flip the svn:executable 8 Some developers have broken SCM configurations that flip the svn:executable
9 permission on for no good reason. Unix developers who run ls --color will then 9 permission on for no good reason. Unix developers who run ls --color will then
10 see .cc files in green and get confused. 10 see .cc files in green and get confused.
11 11
12 To ignore a particular file, add it to WHITELIST_FILES. 12 To ignore a particular file, add it to WHITELIST_FILES.
13 To ignore a particular extension, add it to WHITELIST_EXTENSIONS. 13 To ignore a particular extension, add it to WHITELIST_EXTENSIONS.
14 To ignore whatever regexps your heart desires, add it WHITELIST_REGEX. 14 To ignore a particular path, add it WHITELIST_PATHS.
15 15
16 Note that all directory separators must be slashes (Unix-style) and not 16 Note that all directory separators must be slashes (Unix-style) and not
17 backslashes. All directories should be relative to the source root and all 17 backslashes. All directories should be relative to the source root and all
18 file paths should be only lowercase. 18 file paths should be only lowercase.
19 """ 19 """
20 20
21 import logging
21 import optparse 22 import optparse
22 import os 23 import os
23 import pipes
24 import re
25 import stat 24 import stat
25 import subprocess
26 import sys 26 import sys
27 27
28 #### USER EDITABLE SECTION STARTS HERE #### 28 #### USER EDITABLE SECTION STARTS HERE ####
29 29
30 # Files with these extensions are allowed to have executable permissions. 30 # Files with these extensions are allowed to have executable permissions.
31 WHITELIST_EXTENSIONS = [ 31 WHITELIST_EXTENSIONS = (
32 'bash', 32 'bat',
33 'bat', 33 'dll',
34 'dll', 34 'dylib',
35 'dylib', 35 'exe',
36 'exe', 36 )
37 'pl',
38 'py',
39 'rb',
40 'sed',
41 'sh',
42 ]
43
44 # Files that end the following paths are whitelisted too.
45 WHITELIST_FILES = [
46 '/build/gyp_chromium',
47 '/build/linux/dump_app_syms',
48 '/build/linux/pkg-config-wrapper',
49 '/build/mac/strip_from_xcode',
50 '/build/mac/strip_save_dsym',
51 '/chrome/installer/mac/pkg-dmg',
52 '/chrome/tools/build/linux/chrome-wrapper',
53 '/chrome/tools/build/mac/build_app_dmg',
54 '/chrome/tools/build/mac/clean_up_old_versions',
55 '/chrome/tools/build/mac/dump_product_syms',
56 '/chrome/tools/build/mac/generate_localizer',
57 '/chrome/tools/build/mac/make_sign_sh',
58 '/chrome/tools/build/mac/verify_order',
59 '/o3d/build/gyp_o3d',
60 '/o3d/gypbuild',
61 '/o3d/installer/linux/debian.in/rules',
62 '/third_party/icu/source/runconfigureicu',
63 '/third_party/gold/gold32',
64 '/third_party/gold/gold64',
65 '/third_party/gold/ld',
66 '/third_party/gold/ld.bfd',
67 '/third_party/lcov/bin/gendesc',
68 '/third_party/lcov/bin/genhtml',
69 '/third_party/lcov/bin/geninfo',
70 '/third_party/lcov/bin/genpng',
71 '/third_party/lcov/bin/lcov',
72 '/third_party/lcov/bin/mcov',
73 '/third_party/lcov-1.9/bin/gendesc',
74 '/third_party/lcov-1.9/bin/genhtml',
75 '/third_party/lcov-1.9/bin/geninfo',
76 '/third_party/lcov-1.9/bin/genpng',
77 '/third_party/lcov-1.9/bin/lcov',
78 '/third_party/libxml/linux/xml2-config',
79 '/third_party/lzma_sdk/executable/7za.exe',
80 '/third_party/swig/linux/swig',
81 '/third_party/tcmalloc/chromium/src/pprof',
82 '/tools/deep_memory_profiler/dmprof',
83 '/tools/git/post-checkout',
84 '/tools/git/post-merge',
85 '/tools/ld_bfd/ld',
86 ]
87 37
88 # File names that are always whitelisted. (These are all autoconf spew.) 38 # File names that are always whitelisted. (These are all autoconf spew.)
89 WHITELIST_FILENAMES = set(( 39 WHITELIST_FILENAMES = set((
90 'config.guess', 40 'config.guess',
91 'config.sub', 41 'config.sub',
92 'configure', 42 'configure',
93 'depcomp', 43 'depcomp',
94 'install-sh', 44 'install-sh',
95 'missing', 45 'missing',
96 'mkinstalldirs', 46 'mkinstalldirs',
47 'naclsdk',
97 'scons', 48 'scons',
98 'naclsdk',
99 )) 49 ))
100 50
101 # File paths that contain these regexps will be whitelisted as well. 51 # File paths that contain these regexps will be whitelisted as well.
102 WHITELIST_REGEX = [ 52 WHITELIST_PATHS = (
103 re.compile('/third_party/openssl/'), 53 '/third_party/openssl/',
104 re.compile('/third_party/sqlite/'), 54 '/third_party/sqlite/',
105 re.compile('/third_party/xdg-utils/'), 55 '/third_party/xdg-utils/',
106 re.compile('/third_party/yasm/source/patched-yasm/config'), 56 '/third_party/yasm/source/patched-yasm/config',
107 re.compile('/third_party/ffmpeg/tools'), 57 '/third_party/ffmpeg/tools',
108 ] 58 )
109 59
110 #### USER EDITABLE SECTION ENDS HERE #### 60 #### USER EDITABLE SECTION ENDS HERE ####
111 61
112 WHITELIST_EXTENSIONS_REGEX = re.compile(r'\.(%s)' %
113 '|'.join(WHITELIST_EXTENSIONS))
114 62
115 WHITELIST_FILES_REGEX = re.compile(r'(%s)$' % '|'.join(WHITELIST_FILES)) 63 def is_verbose():
116 64 return logging.getLogger().isEnabledFor(logging.DEBUG)
117 # Set to true for more output. This is set by the command line options.
118 VERBOSE = False
119
120 # Using forward slashes as directory separators, ending in a forward slash.
121 # Set by the command line options.
122 BASE_DIRECTORY = ''
123
124 # The default if BASE_DIRECTORY is not set on the command line.
125 DEFAULT_BASE_DIRECTORY = '../../..'
126
127 # The directories which contain the sources managed by git.
128 GIT_SOURCE_DIRECTORY = set()
129
130 # The SVN repository url.
131 SVN_REPO_URL = ''
132
133 # Whether we are using SVN or GIT.
134 IS_SVN = True
135
136 # Executable permission mask
137 EXECUTABLE_PERMISSION = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
138 65
139 66
140 def IsWhiteListed(file_path): 67 def capture(cmd, cwd):
141 """Returns True if file_path is in our whitelist of files to ignore.""" 68 """Returns the output of a command.
142 if WHITELIST_EXTENSIONS_REGEX.match(os.path.splitext(file_path)[1]): 69
143 return True 70 Ignores the error code or stderr.
144 if WHITELIST_FILES_REGEX.search(file_path): 71 """
145 return True 72 env = os.environ.copy()
146 if os.path.basename(file_path) in WHITELIST_FILENAMES: 73 env['LANG'] = 'en_us.UTF-8'
147 return True 74 p = subprocess.Popen(
148 for regex in WHITELIST_REGEX: 75 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, env=env)
149 if regex.search(file_path): 76 return p.communicate()[0]
150 return True
151 return False
152 77
153 78
154 def CheckFile(file_path): 79 class ApiBase(object):
155 """Checks file_path's permissions. 80 def __init__(self, root_dir):
81 self.root_dir = root_dir
156 82
157 Args: 83 @staticmethod
158 file_path: The file path to check. 84 def is_whitelisted(file_path):
85 """Returns True if file_path is in our whitelist of files to ignore.
159 86
160 Returns: 87 file_path is a relative directory to the checkout root.
161 Either a string describing the error if there was one, or None if the file 88 """
162 checked out OK. 89 file_path = file_path.lower()
163 """ 90 return (
164 if VERBOSE: 91 os.path.splitext(file_path)[1][1:] in WHITELIST_EXTENSIONS or
165 print 'Checking file: ' + file_path 92 os.path.basename(file_path) in WHITELIST_FILENAMES or
93 file_path.startswith(WHITELIST_PATHS))
166 94
167 file_path_lower = file_path.lower() 95 @staticmethod
168 if IsWhiteListed(file_path_lower): 96 def has_executable_bit(file_path):
169 return None 97 """Returns if any executable bit is set.
170 98
171 # Not whitelisted, stat the file and check permissions. 99 file_path is the absolute path to the file.
172 try: 100 """
173 st_mode = os.stat(file_path).st_mode 101 permission = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
174 except IOError, e: 102 return bool(permission & os.stat(file_path).st_mode)
175 return 'Failed to stat file: %s' % e
176 except OSError, e:
177 return 'Failed to stat file: %s' % e
178 103
179 if EXECUTABLE_PERMISSION & st_mode: 104 @staticmethod
180 # Look if the file starts with #!/ 105 def has_shebang(file_path):
106 """Returns if the file starts with #!/.
107
108 file_path is the absolute path to the file.
109 """
181 with open(file_path, 'rb') as f: 110 with open(file_path, 'rb') as f:
182 if f.read(3) == '#!/': 111 return f.read(3) == '#!/'
183 # That's fine. 112
184 return None 113 def check_file(self, file_path):
185 # TODO(maruel): Check that non-executable file do not start with a shebang. 114 """Checks file_path's permissions and returns an error if it is
186 error = 'Contains executable permission' 115 inconsistent.
187 if VERBOSE: 116 """
188 return '%s: %06o' % (error, st_mode) 117 if is_verbose():
189 return error 118 print 'Checking file: %s' % file_path
190 return None 119
120 if self.is_whitelisted(file_path):
121 return None
122
123 full_path = os.path.join(self.root_dir, file_path)
124 bit = self.has_executable_bit(full_path)
125 shebang = self.has_shebang(full_path)
126 if bit != shebang:
127 if bit:
128 return '%s: Has executable bit but not shebang' % file_path
129 else:
130 return '%s: Has shebang but not executable bit' % file_path
131
132 def check(self, start_dir):
133 """Check the files in start_dir, recursively check its subdirectories."""
134 errors = []
135 for root, dirs, files in os.walk(start_dir):
136 for f in files:
137 error = check_file(root_dir, os.path.join(root, f))
138 if error:
139 errors.append(error)
140 return errors
191 141
192 142
193 def ShouldCheckDirectory(dir_path): 143 class ApiSvn(ApiBase):
194 """Determine if we should check the content of dir_path.""" 144 @staticmethod
195 if not IS_SVN: 145 def get_info(dir_path):
196 return dir_path in GIT_SOURCE_DIRECTORY 146 """Returns svn meta-data for a svn checkout."""
197 repo_url = GetSvnRepositoryRoot(dir_path) 147 if not os.path.isdir(dir_path):
198 if not repo_url: 148 return {}
199 return False 149 out = capture(['svn', 'info', '.'], dir_path)
200 return repo_url == SVN_REPO_URL 150 return dict(l.split(': ', 1) for l in out.splitlines() if l)
151
152 @staticmethod
153 def get_root(dir_path):
154 """Returns the svn checkout root or None."""
155 svn_url = get_svn_info(dir_path).get('Repository Root:')
156 if not svn_url:
157 return None
158 while True:
159 parent = os.path.dirname(dir_path)
160 if parent == dir_path:
161 return None
162 if svn_url != get_svn_info(parent).get('Repository Root:'):
163 return dir_path
164 dir_path = parent
165
166 def check(self, start_dir):
167 """Like ApiBase.check() excepts that it skips non-versioned directories."""
168 return super(ApiSvn, self).check(start_dir)
201 169
202 170
203 def CheckDirectory(dir_path): 171 class ApiGit(ApiBase):
204 """Check the files in dir_path; recursively check its subdirectories.""" 172 @staticmethod
205 # Collect a list of all files and directories to check. 173 def get_root(dir_path):
206 files_to_check = [] 174 """Returns the git checkout root or None."""
207 dirs_to_check = [] 175 root = capture(['git', 'rev-parse', '--show-toplevel'], dir_path).strip()
208 success = True 176 if root:
209 contents = os.listdir(dir_path) 177 return root
210 for cur in contents:
211 full_path = os.path.join(dir_path, cur)
212 if os.path.isdir(full_path) and ShouldCheckDirectory(full_path):
213 dirs_to_check.append(full_path)
214 elif os.path.isfile(full_path):
215 files_to_check.append(full_path)
216 178
217 # First check all files in this directory. 179 def check(self, start_dir):
218 for cur_file in files_to_check: 180 """Like ApiBase.check() excepts that it skips non-versioned directories."""
219 file_status = CheckFile(cur_file) 181 return super(ApiSvn, self).check(start_dir)
220 if file_status is not None:
221 print 'ERROR in %s\n%s' % (cur_file, file_status)
222 success = False
223
224 # Next recurse into the subdirectories.
225 for cur_dir in dirs_to_check:
226 if not CheckDirectory(cur_dir):
227 success = False
228 return success
229 182
230 183
231 def GetGitSourceDirectory(root): 184 def get_scm(dir_path):
232 """Returns a set of the directories to be checked. 185 """Returns a properly configured ApiBase instance."""
186 root = ApiSvn.get_root(dir_path)
187 if root:
188 return ApiSvn(root)
189 root = ApiGit.get_root(dir_path)
190 if root:
191 return ApiGit(root)
233 192
234 Args: 193 # Returns a non-scm aware checker.
235 root: The repository root where a .git directory must exist. 194 logging.warn('Failed to determine the SCM for %s' % dir_path)
236 195 return ApiBase(root)
237 Returns:
238 A set of directories which contain sources managed by git.
239 """
240 git_source_directory = set()
241 popen_out = os.popen('cd %s && git ls-files --full-name .' %
242 pipes.quote(root))
243 for line in popen_out:
244 dir_path = os.path.join(root, os.path.dirname(line))
245 git_source_directory.add(dir_path)
246 git_source_directory.add(root)
247 return git_source_directory
248 196
249 197
250 def GetSvnRepositoryRoot(dir_path): 198 def main():
251 """Returns the repository root for a directory.
252
253 Args:
254 dir_path: A directory where a .svn subdirectory should exist.
255
256 Returns:
257 The svn repository that contains dir_path or None.
258 """
259 svn_dir = os.path.join(dir_path, '.svn')
260 if not os.path.isdir(svn_dir):
261 return None
262 popen_out = os.popen('cd %s && svn info' % pipes.quote(dir_path))
263 for line in popen_out:
264 if line.startswith('Repository Root: '):
265 return line[len('Repository Root: '):].rstrip()
266 return None
267
268
269 def main(argv):
270 usage = """Usage: python %prog [--root <root>] [tocheck] 199 usage = """Usage: python %prog [--root <root>] [tocheck]
271 tocheck Specifies the directory, relative to root, to check. This defaults 200 tocheck Specifies the directory, relative to root, to check. This defaults
272 to "." so it checks everything. 201 to "." so it checks everything.
273 202
274 Examples: 203 Examples:
275 python checkperms.py 204 python %prog
276 python checkperms.py --root /path/to/source chrome""" 205 python %prog --root /path/to/source chrome"""
277 206
278 option_parser = optparse.OptionParser(usage=usage) 207 parser = optparse.OptionParser(usage=usage)
279 option_parser.add_option('--root', dest='base_directory', 208 parser.add_option(
280 default=DEFAULT_BASE_DIRECTORY, 209 '--root',
281 help='Specifies the repository root. This defaults ' 210 default=get_scm('.')[0],
282 'to %default relative to the script file, which ' 211 help='Specifies the repository root. This defaults '
283 'will normally be the repository root.') 212 'to %default relative to the script file, which '
284 option_parser.add_option('-v', '--verbose', action='store_true', 213 'will normally be the repository root.')
285 help='Print debug logging') 214 parser.add_option(
286 options, args = option_parser.parse_args() 215 '-v', '--verbose', action='store_true', help='Print debug logging')
216 options, args = parser.parse_args()
287 217
288 global VERBOSE 218 logging.basicConfig(
289 if options.verbose: 219 level=(logging.DEBUG if options.verbose else logging.ERROR))
290 VERBOSE = True
291 220
292 # Optional base directory of the repository. 221 if len(args) > 1:
293 global BASE_DIRECTORY 222 parser.error('Too many arguments used')
294 if (not options.base_directory or 223 if args:
295 options.base_directory == DEFAULT_BASE_DIRECTORY): 224 start_dir = args[0]
296 BASE_DIRECTORY = os.path.abspath(
297 os.path.join(os.path.abspath(argv[0]), DEFAULT_BASE_DIRECTORY))
298 else:
299 BASE_DIRECTORY = os.path.abspath(argv[2])
300 225
301 # Figure out which directory we have to check. 226 if not options.root:
302 if not args: 227 parser.error('Must specify --root')
303 # No directory to check specified, use the repository root. 228 options.root = os.path.abspath(options.root)
304 start_dir = BASE_DIRECTORY
305 elif len(args) == 1:
306 # Directory specified. Start here. It's supposed to be relative to the
307 # base directory.
308 start_dir = os.path.abspath(os.path.join(BASE_DIRECTORY, args[0]))
309 else:
310 # More than one argument, we don't handle this.
311 option_parser.print_help()
312 return 1
313 229
314 print 'Using base directory:', BASE_DIRECTORY 230 # Guess again the SCM used.
315 print 'Checking directory:', start_dir 231 api = get_scm(options.root)
316 232
317 BASE_DIRECTORY = BASE_DIRECTORY.replace('\\', '/') 233 if not api.check(start_dir):
318 start_dir = start_dir.replace('\\', '/')
319
320 success = True
321 if os.path.exists(os.path.join(BASE_DIRECTORY, '.svn')):
322 global SVN_REPO_URL
323 SVN_REPO_URL = GetSvnRepositoryRoot(BASE_DIRECTORY)
324 if not SVN_REPO_URL:
325 print 'Cannot determine the SVN repo URL'
326 success = False
327 elif os.path.exists(os.path.join(BASE_DIRECTORY, '.git')):
328 global IS_SVN
329 IS_SVN = False
330 global GIT_SOURCE_DIRECTORY
331 GIT_SOURCE_DIRECTORY = GetGitSourceDirectory(BASE_DIRECTORY)
332 if not GIT_SOURCE_DIRECTORY:
333 print 'Cannot determine the list of GIT directories'
334 success = False
335 else:
336 print 'Cannot determine the SCM used in %s' % BASE_DIRECTORY
337 success = False
338
339 if success:
340 success = CheckDirectory(start_dir)
341 if not success:
342 print '\nFAILED\n' 234 print '\nFAILED\n'
343 return 1 235 return 1
344 print '\nSUCCESS\n' 236 print '\nSUCCESS\n'
345 return 0 237 return 0
346 238
347 239
348 if '__main__' == __name__: 240 if '__main__' == __name__:
349 sys.exit(main(sys.argv)) 241 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | tools/checkperms/pylintrc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698