OLD | NEW |
(Empty) | |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 |
| 5 """Creates a tree of hardlinks.""" |
| 6 |
| 7 import logging |
| 8 import os |
| 9 import shutil |
| 10 import sys |
| 11 |
| 12 import find_depot_tools # pylint: disable=W0611 |
| 13 import gclient_utils |
| 14 |
| 15 |
| 16 # Types of action accepted by recreate_tree(). |
| 17 DRY_RUN, HARDLINK, SYMLINK, COPY = range(5)[1:] |
| 18 |
| 19 |
| 20 def CreateHardLink(src, dst): |
| 21 """Add support for os.link() on Windows.""" |
| 22 import ctypes |
| 23 if not ctypes.windll.kernel32.CreateHardLinkW(unicode(dst), unicode(src), 0): |
| 24 raise OSError() |
| 25 |
| 26 |
| 27 class Foo(OSError): |
| 28 """Failed to recreate the tree.""" |
| 29 pass |
| 30 |
| 31 |
| 32 def recreate_tree(outdir, indir, infiles, action, blacklist): |
| 33 """Creates a new tree with only the input files in it.""" |
| 34 assert action in (DRY_RUN, HARDLINK, SYMLINK, COPY) |
| 35 indir = os.path.normpath(indir) |
| 36 outdir = os.path.normpath(outdir) |
| 37 logging.info('%s -> %s' % (indir, outdir)) |
| 38 if not os.path.isdir(indir): |
| 39 raise Foo('%s is not a directory' % indir) |
| 40 # Do not call abspath until it was verified the directory exists. |
| 41 indir = os.path.abspath(indir) |
| 42 if os.path.isdir(outdir): |
| 43 logging.warn('Deleting %s' % outdir) |
| 44 if action != DRY_RUN: |
| 45 gclient_utils.RemoveDirectory(outdir) |
| 46 if action != DRY_RUN: |
| 47 logging.info ('Creating %s' % outdir) |
| 48 os.makedirs(outdir) |
| 49 # Do not call abspath until the directory exists. |
| 50 outdir = os.path.abspath(outdir) |
| 51 logging.info ('Created %s' % outdir) |
| 52 |
| 53 def process(inf, outf): |
| 54 inf = os.path.normpath(inf) |
| 55 outf = os.path.normpath(outf) |
| 56 if action == DRY_RUN: |
| 57 print outf |
| 58 else: |
| 59 logging.debug('%s -> %s' % (inf, outf)) |
| 60 if not os.path.isfile(inf): |
| 61 raise Foo('%s doesn\'t exist' % inf) |
| 62 if os.path.isfile(outf): |
| 63 raise Foo('%s already exist' % outf) |
| 64 if action == COPY: |
| 65 shutil.copy(inf, outf) |
| 66 elif action == SYMLINK: |
| 67 os.symlink(inf, outf) |
| 68 try: |
| 69 if action == HARDLINK: |
| 70 if sys.platform == 'win32': |
| 71 CreateHardLink(inf, outf) |
| 72 else: |
| 73 os.link(inf, outf) |
| 74 except OSError: |
| 75 # Probably a different file system. |
| 76 os.symlink(inf, outf) |
| 77 |
| 78 # Make the file read-only. |
| 79 # TODO(maruel): Implement DACL modification on Windows. |
| 80 mode = os.stat(inf).st_mode |
| 81 mode = mode & 0500 |
| 82 if hasattr(os, 'lchmod'): |
| 83 # Module 'os' has no 'lchmod' member |
| 84 # pylint: disable=E1101 |
| 85 os.lchmod(outf, mode) |
| 86 else: |
| 87 os.chmod(outf, mode) |
| 88 |
| 89 for relfile in infiles: |
| 90 infile = os.path.normpath(os.path.join(indir, relfile)) |
| 91 logging.debug(infile) |
| 92 if not infile.startswith(indir): |
| 93 raise Foo('Can\'t map file %s outside %s' % (infile, indir)) |
| 94 if os.path.isdir(infile): |
| 95 # Process recursively. |
| 96 for dirpath, dirnames, filenames in os.walk(infile): |
| 97 insubdir = os.path.normpath(os.path.join(infile, dirpath)) |
| 98 outsubdir = os.path.normpath( |
| 99 os.path.join(outdir, insubdir[len(indir) + 1:])) |
| 100 for filename in filenames: |
| 101 insubfile = os.path.normpath(os.path.join(infile, dirpath, filename)) |
| 102 outsubfile = os.path.normpath(os.path.join(outsubdir, filename)) |
| 103 if blacklist and blacklist(outsubfile): |
| 104 continue |
| 105 if not os.path.isdir(outsubdir): |
| 106 os.makedirs(outsubdir) |
| 107 process(insubfile, outsubfile) |
| 108 for index, dirname in enumerate(dirnames): |
| 109 if blacklist(os.path.normpath(os.path.join(outdir, dirname))): |
| 110 del dirnames[index] |
| 111 else: |
| 112 # TODO(maruel): Is that secure? |
| 113 outsubfile = os.path.normpath(os.path.join(outdir, relfile)) |
| 114 if blacklist and blacklist(outsubfile): |
| 115 continue |
| 116 outsubdir = os.path.dirname(outsubfile) |
| 117 if not os.path.isdir(outsubdir): |
| 118 os.makedirs(outsubdir) |
| 119 process(infile, outsubfile) |
| 120 |
| 121 # Then mark all the directories as read-only to inhibit file creation. |
| 122 for dirpath, _, _ in os.walk(outdir): |
| 123 # TODO(maruel): Implement DACL modification on Windows. |
| 124 os.chmod(os.path.join(outdir, dirpath), 0500) |
OLD | NEW |