Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 4 | |
michael@0 | 5 | # This is a partial python port of nsinstall. |
michael@0 | 6 | # It's intended to be used when there's no natively compile nsinstall |
michael@0 | 7 | # available, and doesn't intend to be fully equivalent. |
michael@0 | 8 | # Its major use is for l10n repackaging on systems that don't have |
michael@0 | 9 | # a full build environment set up. |
michael@0 | 10 | # The basic limitation is, it doesn't even try to link and ignores |
michael@0 | 11 | # all related options. |
michael@0 | 12 | from __future__ import print_function |
michael@0 | 13 | from optparse import OptionParser |
michael@0 | 14 | import os |
michael@0 | 15 | import os.path |
michael@0 | 16 | import sys |
michael@0 | 17 | import shutil |
michael@0 | 18 | import stat |
michael@0 | 19 | |
michael@0 | 20 | def _nsinstall_internal(argv): |
michael@0 | 21 | usage = "usage: %prog [options] arg1 [arg2 ...] target-directory" |
michael@0 | 22 | p = OptionParser(usage=usage) |
michael@0 | 23 | |
michael@0 | 24 | p.add_option('-D', action="store_true", |
michael@0 | 25 | help="Create a single directory only") |
michael@0 | 26 | p.add_option('-t', action="store_true", |
michael@0 | 27 | help="Preserve time stamp") |
michael@0 | 28 | p.add_option('-m', action="store", |
michael@0 | 29 | help="Set mode", metavar="mode") |
michael@0 | 30 | p.add_option('-d', action="store_true", |
michael@0 | 31 | help="Create directories in target") |
michael@0 | 32 | p.add_option('-R', action="store_true", |
michael@0 | 33 | help="Use relative symbolic links (ignored)") |
michael@0 | 34 | p.add_option('-L', action="store", metavar="linkprefix", |
michael@0 | 35 | help="Link prefix (ignored)") |
michael@0 | 36 | p.add_option('-X', action="append", metavar="file", |
michael@0 | 37 | help="Ignore a file when installing a directory recursively.") |
michael@0 | 38 | |
michael@0 | 39 | # The remaining arguments are not used in our tree, thus they're not |
michael@0 | 40 | # implented. |
michael@0 | 41 | def BadArg(option, opt, value, parser): |
michael@0 | 42 | parser.error('option not supported: {0}'.format(opt)) |
michael@0 | 43 | |
michael@0 | 44 | p.add_option('-C', action="callback", metavar="CWD", |
michael@0 | 45 | callback=BadArg, |
michael@0 | 46 | help="NOT SUPPORTED") |
michael@0 | 47 | p.add_option('-o', action="callback", callback=BadArg, |
michael@0 | 48 | help="Set owner (NOT SUPPORTED)", metavar="owner") |
michael@0 | 49 | p.add_option('-g', action="callback", callback=BadArg, |
michael@0 | 50 | help="Set group (NOT SUPPORTED)", metavar="group") |
michael@0 | 51 | |
michael@0 | 52 | (options, args) = p.parse_args(argv) |
michael@0 | 53 | |
michael@0 | 54 | if options.m: |
michael@0 | 55 | # mode is specified |
michael@0 | 56 | try: |
michael@0 | 57 | options.m = int(options.m, 8) |
michael@0 | 58 | except: |
michael@0 | 59 | sys.stderr.write('nsinstall: {0} is not a valid mode\n' |
michael@0 | 60 | .format(options.m)) |
michael@0 | 61 | return 1 |
michael@0 | 62 | |
michael@0 | 63 | # just create one directory? |
michael@0 | 64 | def maybe_create_dir(dir, mode, try_again): |
michael@0 | 65 | dir = os.path.abspath(dir) |
michael@0 | 66 | if os.path.exists(dir): |
michael@0 | 67 | if not os.path.isdir(dir): |
michael@0 | 68 | print('nsinstall: {0} is not a directory'.format(dir), file=sys.stderr) |
michael@0 | 69 | return 1 |
michael@0 | 70 | if mode: |
michael@0 | 71 | os.chmod(dir, mode) |
michael@0 | 72 | return 0 |
michael@0 | 73 | |
michael@0 | 74 | try: |
michael@0 | 75 | if mode: |
michael@0 | 76 | os.makedirs(dir, mode) |
michael@0 | 77 | else: |
michael@0 | 78 | os.makedirs(dir) |
michael@0 | 79 | except Exception as e: |
michael@0 | 80 | # We might have hit EEXIST due to a race condition (see bug 463411) -- try again once |
michael@0 | 81 | if try_again: |
michael@0 | 82 | return maybe_create_dir(dir, mode, False) |
michael@0 | 83 | print("nsinstall: failed to create directory {0}: {1}".format(dir, e)) |
michael@0 | 84 | return 1 |
michael@0 | 85 | else: |
michael@0 | 86 | return 0 |
michael@0 | 87 | |
michael@0 | 88 | if options.X: |
michael@0 | 89 | options.X = [os.path.abspath(p) for p in options.X] |
michael@0 | 90 | |
michael@0 | 91 | if options.D: |
michael@0 | 92 | return maybe_create_dir(args[0], options.m, True) |
michael@0 | 93 | |
michael@0 | 94 | # nsinstall arg1 [...] directory |
michael@0 | 95 | if len(args) < 2: |
michael@0 | 96 | p.error('not enough arguments') |
michael@0 | 97 | |
michael@0 | 98 | def copy_all_entries(entries, target): |
michael@0 | 99 | for e in entries: |
michael@0 | 100 | e = os.path.abspath(e) |
michael@0 | 101 | if options.X and e in options.X: |
michael@0 | 102 | continue |
michael@0 | 103 | |
michael@0 | 104 | dest = os.path.join(target, os.path.basename(e)) |
michael@0 | 105 | dest = os.path.abspath(dest) |
michael@0 | 106 | handleTarget(e, dest) |
michael@0 | 107 | if options.m: |
michael@0 | 108 | os.chmod(dest, options.m) |
michael@0 | 109 | |
michael@0 | 110 | # set up handler |
michael@0 | 111 | if options.d: |
michael@0 | 112 | # we're supposed to create directories |
michael@0 | 113 | def handleTarget(srcpath, targetpath): |
michael@0 | 114 | # target directory was already created, just use mkdir |
michael@0 | 115 | os.mkdir(targetpath) |
michael@0 | 116 | else: |
michael@0 | 117 | # we're supposed to copy files |
michael@0 | 118 | def handleTarget(srcpath, targetpath): |
michael@0 | 119 | if os.path.isdir(srcpath): |
michael@0 | 120 | if not os.path.exists(targetpath): |
michael@0 | 121 | os.mkdir(targetpath) |
michael@0 | 122 | entries = [os.path.join(srcpath, e) for e in os.listdir(srcpath)] |
michael@0 | 123 | copy_all_entries(entries, targetpath) |
michael@0 | 124 | # options.t is not relevant for directories |
michael@0 | 125 | if options.m: |
michael@0 | 126 | os.chmod(targetpath, options.m) |
michael@0 | 127 | else: |
michael@0 | 128 | if os.path.exists(targetpath): |
michael@0 | 129 | # On Windows, read-only files can't be deleted |
michael@0 | 130 | os.chmod(targetpath, stat.S_IWUSR) |
michael@0 | 131 | os.remove(targetpath) |
michael@0 | 132 | if options.t: |
michael@0 | 133 | shutil.copy2(srcpath, targetpath) |
michael@0 | 134 | else: |
michael@0 | 135 | shutil.copy(srcpath, targetpath) |
michael@0 | 136 | |
michael@0 | 137 | # the last argument is the target directory |
michael@0 | 138 | target = args.pop() |
michael@0 | 139 | # ensure target directory (importantly, we do not apply a mode to the directory |
michael@0 | 140 | # because we want to copy files into it and the mode might be read-only) |
michael@0 | 141 | rv = maybe_create_dir(target, None, True) |
michael@0 | 142 | if rv != 0: |
michael@0 | 143 | return rv |
michael@0 | 144 | |
michael@0 | 145 | copy_all_entries(args, target) |
michael@0 | 146 | return 0 |
michael@0 | 147 | |
michael@0 | 148 | # nsinstall as a native command is always UTF-8 |
michael@0 | 149 | def nsinstall(argv): |
michael@0 | 150 | return _nsinstall_internal([unicode(arg, "utf-8") for arg in argv]) |
michael@0 | 151 | |
michael@0 | 152 | if __name__ == '__main__': |
michael@0 | 153 | # sys.argv corrupts characters outside the system code page on Windows |
michael@0 | 154 | # <http://bugs.python.org/issue2128>. Use ctypes instead. This is also |
michael@0 | 155 | # useful because switching to Unicode strings makes python use the wide |
michael@0 | 156 | # Windows APIs, which is what we want here since the wide APIs normally do a |
michael@0 | 157 | # better job at handling long paths and such. |
michael@0 | 158 | if sys.platform == "win32": |
michael@0 | 159 | import ctypes |
michael@0 | 160 | from ctypes import wintypes |
michael@0 | 161 | GetCommandLine = ctypes.windll.kernel32.GetCommandLineW |
michael@0 | 162 | GetCommandLine.argtypes = [] |
michael@0 | 163 | GetCommandLine.restype = wintypes.LPWSTR |
michael@0 | 164 | |
michael@0 | 165 | CommandLineToArgv = ctypes.windll.shell32.CommandLineToArgvW |
michael@0 | 166 | CommandLineToArgv.argtypes = [wintypes.LPWSTR, ctypes.POINTER(ctypes.c_int)] |
michael@0 | 167 | CommandLineToArgv.restype = ctypes.POINTER(wintypes.LPWSTR) |
michael@0 | 168 | |
michael@0 | 169 | argc = ctypes.c_int(0) |
michael@0 | 170 | argv_arr = CommandLineToArgv(GetCommandLine(), ctypes.byref(argc)) |
michael@0 | 171 | # The first argv will be "python", the second will be the .py file |
michael@0 | 172 | argv = argv_arr[1:argc.value] |
michael@0 | 173 | else: |
michael@0 | 174 | # For consistency, do it on Unix as well |
michael@0 | 175 | if sys.stdin.encoding is not None: |
michael@0 | 176 | argv = [unicode(arg, sys.stdin.encoding) for arg in sys.argv] |
michael@0 | 177 | else: |
michael@0 | 178 | argv = [unicode(arg) for arg in sys.argv] |
michael@0 | 179 | |
michael@0 | 180 | sys.exit(_nsinstall_internal(argv[1:])) |