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

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

Issue 318863006: Add support for JSON output to checkperms.py (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fixes Created 6 years, 6 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 | no next file » | 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 - For file extensions that must be executable, add it to EXECUTABLE_EXTENSIONS. 12 - For file extensions that must be executable, add it to EXECUTABLE_EXTENSIONS.
13 - For file extensions that must not be executable, add it to 13 - For file extensions that must not be executable, add it to
14 NOT_EXECUTABLE_EXTENSIONS. 14 NOT_EXECUTABLE_EXTENSIONS.
15 - To ignore all the files inside a directory, add it to IGNORED_PATHS. 15 - To ignore all the files inside a directory, add it to IGNORED_PATHS.
16 - For file base name with ambiguous state and that should not be checked for 16 - For file base name with ambiguous state and that should not be checked for
17 shebang, add it to IGNORED_FILENAMES. 17 shebang, add it to IGNORED_FILENAMES.
18 18
19 Any file not matching the above will be opened and looked if it has a shebang 19 Any file not matching the above will be opened and looked if it has a shebang
20 or an ELF header. If this does not match the executable bit on the file, the 20 or an ELF header. If this does not match the executable bit on the file, the
21 file will be flagged. 21 file will be flagged.
22 22
23 Note that all directory separators must be slashes (Unix-style) and not 23 Note that all directory separators must be slashes (Unix-style) and not
24 backslashes. All directories should be relative to the source root and all 24 backslashes. All directories should be relative to the source root and all
25 file paths should be only lowercase. 25 file paths should be only lowercase.
26 """ 26 """
27 27
28 import json
28 import logging 29 import logging
29 import optparse 30 import optparse
30 import os 31 import os
31 import stat 32 import stat
32 import string 33 import string
33 import subprocess 34 import subprocess
34 import sys 35 import sys
35 36
36 #### USER EDITABLE SECTION STARTS HERE #### 37 #### USER EDITABLE SECTION STARTS HERE ####
37 38
(...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after
291 def has_shebang_or_is_elf(full_path): 292 def has_shebang_or_is_elf(full_path):
292 """Returns if the file starts with #!/ or is an ELF binary. 293 """Returns if the file starts with #!/ or is an ELF binary.
293 294
294 full_path is the absolute path to the file. 295 full_path is the absolute path to the file.
295 """ 296 """
296 with open(full_path, 'rb') as f: 297 with open(full_path, 'rb') as f:
297 data = f.read(4) 298 data = f.read(4)
298 return (data[:3] == '#!/', data == '\x7fELF') 299 return (data[:3] == '#!/', data == '\x7fELF')
299 300
300 301
301 def check_file(root_path, rel_path, bare_output): 302 def check_file(root_path, rel_path):
302 """Checks the permissions of the file whose path is root_path + rel_path and 303 """Checks the permissions of the file whose path is root_path + rel_path and
303 returns an error if it is inconsistent. 304 returns an error if it is inconsistent. Returns None on success.
304 305
305 It is assumed that the file is not ignored by is_ignored(). 306 It is assumed that the file is not ignored by is_ignored().
306 307
307 If the file name is matched with must_be_executable() or 308 If the file name is matched with must_be_executable() or
308 must_not_be_executable(), only its executable bit is checked. 309 must_not_be_executable(), only its executable bit is checked.
309 Otherwise, the first few bytes of the file are read to verify if it has a 310 Otherwise, the first few bytes of the file are read to verify if it has a
310 shebang or ELF header and compares this with the executable bit on the file. 311 shebang or ELF header and compares this with the executable bit on the file.
311 """ 312 """
312 full_path = os.path.join(root_path, rel_path) 313 full_path = os.path.join(root_path, rel_path)
314 def result_dict(error):
315 return {
316 'error': error,
317 'full_path': full_path,
318 'rel_path': rel_path,
319 }
313 try: 320 try:
314 bit = has_executable_bit(full_path) 321 bit = has_executable_bit(full_path)
315 except OSError: 322 except OSError:
316 # It's faster to catch exception than call os.path.islink(). Chromium 323 # It's faster to catch exception than call os.path.islink(). Chromium
317 # tree happens to have invalid symlinks under 324 # tree happens to have invalid symlinks under
318 # third_party/openssl/openssl/test/. 325 # third_party/openssl/openssl/test/.
319 return None 326 return None
320 327
321 if must_be_executable(rel_path): 328 if must_be_executable(rel_path):
322 if not bit: 329 if not bit:
323 if bare_output: 330 return result_dict('Must have executable bit set')
324 return full_path
325 return '%s: Must have executable bit set' % full_path
326 return 331 return
327 if must_not_be_executable(rel_path): 332 if must_not_be_executable(rel_path):
328 if bit: 333 if bit:
329 if bare_output: 334 return result_dict('Must not have executable bit set')
330 return full_path
331 return '%s: Must not have executable bit set' % full_path
332 return 335 return
333 336
334 # For the others, it depends on the file header. 337 # For the others, it depends on the file header.
335 (shebang, elf) = has_shebang_or_is_elf(full_path) 338 (shebang, elf) = has_shebang_or_is_elf(full_path)
336 if bit != (shebang or elf): 339 if bit != (shebang or elf):
337 if bare_output:
338 return full_path
339 if bit: 340 if bit:
340 return '%s: Has executable bit but not shebang or ELF header' % full_path 341 return result_dict('Has executable bit but not shebang or ELF header')
341 if shebang: 342 if shebang:
342 return '%s: Has shebang but not executable bit' % full_path 343 return result_dict('Has shebang but not executable bit')
343 return '%s: Has ELF header but not executable bit' % full_path 344 return result_dict('Has ELF header but not executable bit')
344 345
345 346
346 def check_files(root, files, bare_output): 347 def check_files(root, files):
347 errors = [] 348 gen = (check_file(root, f) for f in files if not is_ignored(f))
348 for rel_path in files: 349 return filter(None, gen)
349 if is_ignored(rel_path):
350 continue
351
352 error = check_file(root, rel_path, bare_output)
353 if error:
354 errors.append(error)
355
356 return errors
357 350
358 351
359 class ApiBase(object): 352 class ApiBase(object):
360 def __init__(self, root_dir, bare_output): 353 def __init__(self, root_dir, bare_output):
361 self.root_dir = root_dir 354 self.root_dir = root_dir
362 self.bare_output = bare_output 355 self.bare_output = bare_output
363 self.count = 0 356 self.count = 0
364 self.count_read_header = 0 357 self.count_read_header = 0
365 358
366 def check_file(self, rel_path): 359 def check_file(self, rel_path):
367 logging.debug('check_file(%s)' % rel_path) 360 logging.debug('check_file(%s)' % rel_path)
368 self.count += 1 361 self.count += 1
369 362
370 if (not must_be_executable(rel_path) and 363 if (not must_be_executable(rel_path) and
371 not must_not_be_executable(rel_path)): 364 not must_not_be_executable(rel_path)):
372 self.count_read_header += 1 365 self.count_read_header += 1
373 366
374 return check_file(self.root_dir, rel_path, self.bare_output) 367 return check_file(self.root_dir, rel_path)
375 368
376 def check_dir(self, rel_path): 369 def check_dir(self, rel_path):
377 return self.check(rel_path) 370 return self.check(rel_path)
378 371
379 def check(self, start_dir): 372 def check(self, start_dir):
380 """Check the files in start_dir, recursively check its subdirectories.""" 373 """Check the files in start_dir, recursively check its subdirectories."""
381 errors = [] 374 errors = []
382 items = self.list_dir(start_dir) 375 items = self.list_dir(start_dir)
383 logging.info('check(%s) -> %d' % (start_dir, len(items))) 376 logging.info('check(%s) -> %d' % (start_dir, len(items)))
384 for item in items: 377 for item in items:
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
495 '-v', '--verbose', action='count', default=0, help='Print debug logging') 488 '-v', '--verbose', action='count', default=0, help='Print debug logging')
496 parser.add_option( 489 parser.add_option(
497 '--bare', 490 '--bare',
498 action='store_true', 491 action='store_true',
499 default=False, 492 default=False,
500 help='Prints the bare filename triggering the checks') 493 help='Prints the bare filename triggering the checks')
501 parser.add_option( 494 parser.add_option(
502 '--file', action='append', dest='files', 495 '--file', action='append', dest='files',
503 help='Specifics a list of files to check the permissions of. Only these ' 496 help='Specifics a list of files to check the permissions of. Only these '
504 'files will be checked') 497 'files will be checked')
498 parser.add_option('--json', help='Path to JSON output file')
505 options, args = parser.parse_args() 499 options, args = parser.parse_args()
506 500
507 levels = [logging.ERROR, logging.INFO, logging.DEBUG] 501 levels = [logging.ERROR, logging.INFO, logging.DEBUG]
508 logging.basicConfig(level=levels[min(len(levels) - 1, options.verbose)]) 502 logging.basicConfig(level=levels[min(len(levels) - 1, options.verbose)])
509 503
510 if len(args) > 1: 504 if len(args) > 1:
511 parser.error('Too many arguments used') 505 parser.error('Too many arguments used')
512 506
513 if options.root: 507 if options.root:
514 options.root = os.path.abspath(options.root) 508 options.root = os.path.abspath(options.root)
515 509
516 if options.files: 510 if options.files:
517 errors = check_files(options.root, options.files, options.bare) 511 # --file implies --bare (for PRESUBMIT.py).
518 print '\n'.join(errors) 512 options.bare = True
519 return bool(errors)
520 513
521 api = get_scm(options.root, options.bare) 514 errors = check_files(options.root, options.files)
522 if args:
523 start_dir = args[0]
524 else: 515 else:
525 start_dir = api.root_dir 516 api = get_scm(options.root, options.bare)
517 start_dir = args[0] if args else api.root_dir
518 errors = api.check(start_dir)
526 519
527 errors = api.check(start_dir) 520 if not options.bare:
521 print('Processed %s files, %d files where tested for shebang/ELF '
522 'header' % (api.count, api.count_read_header))
528 523
529 if not options.bare: 524 if options.json:
530 print 'Processed %s files, %d files where tested for shebang/ELF header' % ( 525 with open(options.json, 'w') as f:
531 api.count, api.count_read_header) 526 json.dump(errors, f)
532 527
533 if errors: 528 if errors:
534 if not options.bare: 529 if options.bare:
530 print '\n'.join(e['full_path'] for e in errors)
531 else:
535 print '\nFAILED\n' 532 print '\nFAILED\n'
536 print '\n'.join(errors) 533 print '\n'.join('%s: %s' % (e['full_path'], e['error']) for e in errors)
537 return 1 534 return 1
538 if not options.bare: 535 if not options.bare:
539 print '\nSUCCESS\n' 536 print '\nSUCCESS\n'
540 return 0 537 return 0
541 538
542 539
543 if '__main__' == __name__: 540 if '__main__' == __name__:
544 sys.exit(main()) 541 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698