OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # coding=utf-8 | 2 # coding=utf-8 |
3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 """Traces an executable and its child processes and extract the files accessed | 7 """Traces an executable and its child processes and extract the files accessed |
8 by them. | 8 by them. |
9 | 9 |
10 The implementation uses OS-specific API. The native Kernel logger and the ETL | 10 The implementation uses OS-specific API. The native Kernel logger and the ETL |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
187 if not drive or not match.group(2): | 187 if not drive or not match.group(2): |
188 return drive | 188 return drive |
189 return drive + match.group(2) | 189 return drive + match.group(2) |
190 | 190 |
191 | 191 |
192 def isabs(path): | 192 def isabs(path): |
193 """Accepts X: as an absolute path, unlike python's os.path.isabs().""" | 193 """Accepts X: as an absolute path, unlike python's os.path.isabs().""" |
194 return os.path.isabs(path) or len(path) == 2 and path[1] == ':' | 194 return os.path.isabs(path) or len(path) == 2 and path[1] == ':' |
195 | 195 |
196 | 196 |
197 def get_native_path_case(path): | 197 def get_native_path_case(p): |
198 """Returns the native path case for an existing file. | 198 """Returns the native path case for an existing file. |
199 | 199 |
200 On Windows, removes any leading '\\?\'. | 200 On Windows, removes any leading '\\?\'. |
201 """ | 201 """ |
202 if not isabs(path): | 202 if not isabs(p): |
203 raise ValueError( | 203 raise ValueError( |
204 'Can\'t get native path case for a non-absolute path: %s' % path, | 204 'Can\'t get native path case for a non-absolute path: %s' % p, |
205 path) | 205 p) |
206 # Windows used to have an option to turn on case sensitivity on non Win32 | 206 # Windows used to have an option to turn on case sensitivity on non Win32 |
207 # subsystem but that's out of scope here and isn't supported anymore. | 207 # subsystem but that's out of scope here and isn't supported anymore. |
208 # Go figure why GetShortPathName() is needed. | 208 # Go figure why GetShortPathName() is needed. |
209 try: | 209 try: |
210 path = GetLongPathName(GetShortPathName(path)) | 210 out = GetLongPathName(GetShortPathName(p)) |
211 except OSError: | 211 except OSError, e: |
212 # This is wrong to silently eat the exception but there's nothing that can | 212 if e.args[0] in (2, 3): |
213 # be done about it. | 213 # The path does not exist. Try to recurse and reconstruct the path. |
214 logging.info('No access to %s' % path) | 214 base = os.path.dirname(p) |
215 if path.startswith('\\\\?\\'): | 215 rest = os.path.basename(p) |
216 path = path[4:] | 216 return os.path.join(get_native_path_case(base), rest) |
| 217 raise |
| 218 if out.startswith('\\\\?\\'): |
| 219 out = out[4:] |
217 # Always upper case the first letter since GetLongPathName() will return the | 220 # Always upper case the first letter since GetLongPathName() will return the |
218 # drive letter in the case it was given. | 221 # drive letter in the case it was given. |
219 return path[0].upper() + path[1:] | 222 return out[0].upper() + out[1:] |
220 | 223 |
221 | 224 |
222 def CommandLineToArgvW(command_line): | 225 def CommandLineToArgvW(command_line): |
223 """Splits a commandline into argv using CommandLineToArgvW().""" | 226 """Splits a commandline into argv using CommandLineToArgvW().""" |
224 # http://msdn.microsoft.com/library/windows/desktop/bb776391.aspx | 227 # http://msdn.microsoft.com/library/windows/desktop/bb776391.aspx |
225 size = c_int() | 228 size = c_int() |
226 ptr = windll.shell32.CommandLineToArgvW(unicode(command_line), byref(size)) | 229 ptr = windll.shell32.CommandLineToArgvW(unicode(command_line), byref(size)) |
227 try: | 230 try: |
228 return [arg for arg in (c_wchar_p * size.value).from_address(ptr)] | 231 return [arg for arg in (c_wchar_p * size.value).from_address(ptr)] |
229 finally: | 232 finally: |
(...skipping 17 matching lines...) Expand all Loading... |
247 for element in os.listdir(root_path): | 250 for element in os.listdir(root_path): |
248 if element.lower() == item: | 251 if element.lower() == item: |
249 return element | 252 return element |
250 | 253 |
251 | 254 |
252 def _native_case(p): | 255 def _native_case(p): |
253 """Gets the native path case. Warning: this function resolves symlinks.""" | 256 """Gets the native path case. Warning: this function resolves symlinks.""" |
254 logging.debug('native_case(%s)' % p) | 257 logging.debug('native_case(%s)' % p) |
255 try: | 258 try: |
256 rel_ref, _ = Carbon.File.FSPathMakeRef(p) | 259 rel_ref, _ = Carbon.File.FSPathMakeRef(p) |
257 return rel_ref.FSRefMakePath() | 260 out = rel_ref.FSRefMakePath() |
| 261 if p.endswith(os.path.sep) and not out.endswith(os.path.sep): |
| 262 return out + os.path.sep |
| 263 return out |
258 except MacOS.Error, e: | 264 except MacOS.Error, e: |
| 265 if e.args[0] == -43: |
| 266 # The path does not exist. Try to recurse and reconstruct the path. |
| 267 base = os.path.dirname(p) |
| 268 rest = os.path.basename(p) |
| 269 return os.path.join(_native_case(base), rest) |
259 raise OSError( | 270 raise OSError( |
260 e.args[0], 'Failed to get native path for %s' % p, p, e.args[1]) | 271 e.args[0], 'Failed to get native path for %s' % p, p, e.args[1]) |
261 | 272 |
262 | 273 |
263 def _split_at_symlink_native(base_path, rest): | 274 def _split_at_symlink_native(base_path, rest): |
264 """Returns the native path for a symlink.""" | 275 """Returns the native path for a symlink.""" |
265 base, symlink, rest = split_at_symlink(base_path, rest) | 276 base, symlink, rest = split_at_symlink(base_path, rest) |
266 if symlink: | 277 if symlink: |
267 if not base_path: | 278 if not base_path: |
268 base_path = base | 279 base_path = base |
(...skipping 18 matching lines...) Expand all Loading... |
287 return path | 298 return path |
288 | 299 |
289 # Starts assuming there is no symlink along the path. | 300 # Starts assuming there is no symlink along the path. |
290 resolved = _native_case(path) | 301 resolved = _native_case(path) |
291 if resolved.lower() == path.lower(): | 302 if resolved.lower() == path.lower(): |
292 # This code path is incredibly faster. | 303 # This code path is incredibly faster. |
293 return resolved | 304 return resolved |
294 | 305 |
295 # There was a symlink, process it. | 306 # There was a symlink, process it. |
296 base, symlink, rest = _split_at_symlink_native(None, path) | 307 base, symlink, rest = _split_at_symlink_native(None, path) |
297 assert symlink, (path, base, symlink, rest) | 308 assert symlink, (path, base, symlink, rest, resolved) |
298 prev = base | 309 prev = base |
299 base = safe_join(_native_case(base), symlink) | 310 base = safe_join(_native_case(base), symlink) |
300 assert len(base) > len(prev) | 311 assert len(base) > len(prev) |
301 while rest: | 312 while rest: |
302 prev = base | 313 prev = base |
303 relbase, symlink, rest = _split_at_symlink_native(base, rest) | 314 relbase, symlink, rest = _split_at_symlink_native(base, rest) |
304 base = safe_join(base, relbase) | 315 base = safe_join(base, relbase) |
305 assert len(base) > len(prev), (prev, base, symlink) | 316 assert len(base) > len(prev), (prev, base, symlink) |
306 if symlink: | 317 if symlink: |
307 base = safe_join(base, symlink) | 318 base = safe_join(base, symlink) |
(...skipping 19 matching lines...) Expand all Loading... |
327 TODO(maruel): This is not strictly true. Implement if necessary. | 338 TODO(maruel): This is not strictly true. Implement if necessary. |
328 """ | 339 """ |
329 if not isabs(path): | 340 if not isabs(path): |
330 raise ValueError( | 341 raise ValueError( |
331 'Can\'t get native path case for a non-absolute path: %s' % path, | 342 'Can\'t get native path case for a non-absolute path: %s' % path, |
332 path) | 343 path) |
333 # Give up on cygwin, as GetLongPathName() can't be called. | 344 # Give up on cygwin, as GetLongPathName() can't be called. |
334 # Linux traces tends to not be normalized so use this occasion to normalize | 345 # Linux traces tends to not be normalized so use this occasion to normalize |
335 # it. This function implementation already normalizes the path on the other | 346 # it. This function implementation already normalizes the path on the other |
336 # OS so this needs to be done here to be coherent between OSes. | 347 # OS so this needs to be done here to be coherent between OSes. |
337 return os.path.normpath(path) | 348 out = os.path.normpath(path) |
| 349 if path.endswith(os.path.sep) and not out.endswith(os.path.sep): |
| 350 return out + os.path.sep |
| 351 return out |
338 | 352 |
339 | 353 |
340 if sys.platform != 'win32': # All non-Windows OSes. | 354 if sys.platform != 'win32': # All non-Windows OSes. |
341 | 355 |
342 | 356 |
343 def safe_join(*args): | 357 def safe_join(*args): |
344 """Joins path elements like os.path.join() but doesn't abort on absolute | 358 """Joins path elements like os.path.join() but doesn't abort on absolute |
345 path. | 359 path. |
346 | 360 |
347 os.path.join('foo', '/bar') == '/bar' | 361 os.path.join('foo', '/bar') == '/bar' |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
562 self.tainted = tainted | 576 self.tainted = tainted |
563 # These are cache only. | 577 # These are cache only. |
564 self._real_path = None | 578 self._real_path = None |
565 self._size = None | 579 self._size = None |
566 | 580 |
567 # Check internal consistency. | 581 # Check internal consistency. |
568 assert path, path | 582 assert path, path |
569 assert tainted or bool(root) != bool(isabs(path)), (root, path) | 583 assert tainted or bool(root) != bool(isabs(path)), (root, path) |
570 assert tainted or ( | 584 assert tainted or ( |
571 not os.path.exists(self.full_path) or | 585 not os.path.exists(self.full_path) or |
572 (self.full_path.rstrip(os.path.sep) == | 586 (self.full_path == get_native_path_case(self.full_path))), ( |
573 get_native_path_case(self.full_path))), ( | |
574 tainted, self.full_path, get_native_path_case(self.full_path)) | 587 tainted, self.full_path, get_native_path_case(self.full_path)) |
575 | 588 |
576 @property | 589 @property |
577 def existent(self): | 590 def existent(self): |
578 return self.size != -1 | 591 return self.size != -1 |
579 | 592 |
580 @property | 593 @property |
581 def full_path(self): | 594 def full_path(self): |
582 if self.root: | 595 if self.root: |
583 return os.path.join(self.root, self.path) | 596 return os.path.join(self.root, self.path) |
(...skipping 2492 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3076 return command(argv[1:]) | 3089 return command(argv[1:]) |
3077 except TracingFailure, e: | 3090 except TracingFailure, e: |
3078 sys.stderr.write('\nError: ') | 3091 sys.stderr.write('\nError: ') |
3079 sys.stderr.write(str(e)) | 3092 sys.stderr.write(str(e)) |
3080 sys.stderr.write('\n') | 3093 sys.stderr.write('\n') |
3081 return 1 | 3094 return 1 |
3082 | 3095 |
3083 | 3096 |
3084 if __name__ == '__main__': | 3097 if __name__ == '__main__': |
3085 sys.exit(main(sys.argv[1:])) | 3098 sys.exit(main(sys.argv[1:])) |
OLD | NEW |