def GetRawDevice(path): """Resolve the raw device that contains the path.""" device_map = GetMountpoints() path = utils.SmartUnicode(path) mount_point = path = utils.NormalizePath(path, "/") result = rdf_paths.PathSpec(pathtype=rdf_paths.PathSpec.PathType.OS) # Assign the most specific mount point to the result while mount_point: try: result.path, fs_type = device_map[mount_point] if fs_type in SUPPORTED_FILESYSTEMS: # These are read filesystems result.pathtype = rdf_paths.PathSpec.PathType.OS else: logging.error( "Filesystem %s is not supported. Supported filesystems " "are %s", fs_type, SUPPORTED_FILESYSTEMS) result.pathtype = rdf_paths.PathSpec.PathType.UNSET # Drop the mount point path = utils.NormalizePath(path[len(mount_point):]) result.mount_point = mount_point return result, path except KeyError: mount_point = os.path.dirname(mount_point)
def LinGetRawDevice(path): """Resolve the raw device that contains the path.""" device_map = GetMountpoints() path = utils.SmartUnicode(path) mount_point = path = utils.NormalizePath(path, "/") result = rdf_paths.PathSpec(pathtype=rdf_paths.PathSpec.PathType.OS) # Assign the most specific mount point to the result while mount_point: try: result.path, fs_type = device_map[mount_point] if fs_type in ["ext2", "ext3", "ext4", "vfat", "ntfs"]: # These are read filesystems result.pathtype = rdf_paths.PathSpec.PathType.OS else: result.pathtype = rdf_paths.PathSpec.PathType.UNSET # Drop the mount point path = utils.NormalizePath(path[len(mount_point):]) result.mount_point = mount_point return result, path except KeyError: mount_point = os.path.dirname(mount_point)
def GenerateDirectory(self, input_dir=None, output_dir=None, replacements=None): input_dir = utils.NormalizePath(input_dir) output_dir = utils.NormalizePath(output_dir) replacements = replacements or [] for (root, _, files) in os.walk(input_dir): for filename in files: in_file = utils.JoinPath(root, filename) out_file = in_file.replace(input_dir, output_dir) for (s, replacement) in replacements: out_file = out_file.replace(s, replacement) self.EnsureDirExists(os.path.dirname(out_file)) self.GenerateFile(in_file, out_file)
def PopulateCache(self): """Parse the paths from the fixture.""" if self.paths: return # The cache is attached to the class so it can be shared by all instance. self.paths = self.__class__.cache[self.prefix] = {} for path, (vfs_type, attributes) in client_fixture.VFS: if not path.startswith(self.prefix): continue path = utils.NormalizePath(path[len(self.prefix):]) if path == "/": continue stat = rdf_client.StatEntry() args = {"client_id": "C.1234"} attrs = attributes.get("aff4:stat") if attrs: attrs %= args # Remove any %% and interpolate client_id. stat = rdf_client.StatEntry.FromTextFormat( utils.SmartStr(attrs)) stat.pathspec = rdf_paths.PathSpec( pathtype=self.supported_pathtype, path=path) # TODO(user): Once we add tests around not crossing device boundaries, # we need to be smarter here, especially for the root entry. stat.st_dev = 1 path = self._NormalizeCaseForPath(path, vfs_type) self.paths[path] = (vfs_type, stat) self.BuildIntermediateDirectories()
def ParseFromString(self, initializer=None): """Create RDFRUN from string. Args: initializer: url string """ # TODO(user): This is another ugly hack but we need to support legacy # clients that still send plain strings in their responses. if self.bare_string_re.match(initializer): initializer = "aff4:/" + initializer self._urn = urlparse.urlparse(initializer, scheme="aff4") # TODO(user): Another hack. Urlparse behaves differently in different # Python versions. We have to make sure the URL is not split at '?' chars. # At this point I think we should just give up on urlparse and roll our # own parsing... if self._urn.query: scheme = self._urn.scheme url = "%s?%s" % (self._urn.path, self._urn.query) netloc, params, query, fragment = "", "", "", "" self._urn = urlparse.ParseResult(scheme, netloc, url, params, query, fragment) # Normalize the URN path component # namedtuple _replace() is not really private. # pylint: disable=protected-access self._urn = self._urn._replace( path=utils.NormalizePath(self._urn.path)) if not self._urn.scheme: self._urn = self._urn._replace(scheme="aff4") self._string_urn = self._urn.geturl()
def testTSKFileCasing(self): """Test our ability to read the correct casing from image.""" path = os.path.join(self.base_path, "test_img.dd") path2 = os.path.join("test directory", "NuMbErS.TxT") ps2 = rdfvalue.PathSpec( path=path2, pathtype=rdfvalue.PathSpec.PathType.TSK) ps = rdfvalue.PathSpec(path=path, pathtype=rdfvalue.PathSpec.PathType.OS) ps.Append(ps2) fd = vfs.VFSOpen(ps) # This fixes Windows paths. path = path.replace("\\", "/") # The pathspec should have 2 components. self.assertEqual(fd.pathspec.first.path, utils.NormalizePath(path)) self.assertEqual(fd.pathspec.first.pathtype, rdfvalue.PathSpec.PathType.OS) nested = fd.pathspec.last self.assertEqual(nested.path, u"/Test Directory/numbers.txt") self.assertEqual(nested.pathtype, rdfvalue.PathSpec.PathType.TSK)
def Run(self, args): """Delete all the GRR temp files in path. If path is a directory, look in the top level for filenames beginning with Client.tempfile_prefix, and delete them. If path is a regular file and starts with Client.tempfile_prefix delete it. Args: args: pathspec pointing to directory containing temp files to be deleted, or a single file to be deleted. Returns: deleted: array of filename strings that were deleted Raises: ErrorBadPath: if path doesn't exist or is not a regular file or directory """ # Normalize the path, so DeleteGRRTempFile can correctly check if # it is within Client.tempdir. if args.path: path = client_utils.CanonicalPathToLocalPath( utils.NormalizePath(args.path)) else: path = config_lib.CONFIG["Client.tempdir"] deleted = [] errors = [] if os.path.isdir(path): for filename in os.listdir(path): abs_filename = os.path.join(path, filename) try: DeleteGRRTempFile(abs_filename) deleted.append(abs_filename) except Exception as e: # pylint: disable=broad-except # The error we are most likely to get is ErrorNotTempFile but # especially on Windows there might be locking issues that raise # various WindowsErrors so we just catch them all and continue # deleting all other temp files in this directory. errors.append(e) elif os.path.isfile(path): DeleteGRRTempFile(path) deleted = [path] elif not os.path.exists(path): raise ErrorBadPath("File %s does not exist" % path) else: raise ErrorBadPath("Not a regular file or directory: %s" % path) reply = "" if deleted: reply = "Deleted: %s." % deleted else: reply = "Nothing deleted." if errors: reply += "\n%s" % errors self.SendReply(rdf_client.LogMessage(data=reply))
def Interpolate(self, client=None): for pattern in self.InterpolateClientAttributes(client=client): # Normalize the component path (this allows us to resolve ../ # sequences). pattern = utils.NormalizePath(pattern.replace("\\", "/")) for pattern in self.InterpolateGrouping(pattern): yield pattern
def ParseFromString(self, initializer): """Create RDFRUN from string. Args: initializer: url string """ # Strip off the aff4: prefix if necessary. if initializer.startswith("aff4:/"): initializer = initializer[5:] self._string_urn = utils.NormalizePath(initializer)
def Interpolate(self, client=None): kb = client.Get(client.Schema.KNOWLEDGE_BASE) patterns = artifact_utils.InterpolateKbAttributes(self._value, kb) for pattern in patterns: # Normalize the component path (this allows us to resolve ../ # sequences). pattern = utils.NormalizePath(pattern.replace("\\", "/")) for pattern in self.InterpolateGrouping(pattern): yield pattern
def testNormpath(self): """Test our Normpath.""" data = [ ("foo/../../../../", "/"), ("/foo/../../../../bar", "/bar"), ("/foo/bar/../3sdfdfsa/.", "/foo/3sdfdfsa"), ("../foo/bar", "/foo/bar"), ("./foo/bar", "/foo/bar"), ("/", "/"), ] for test, expected in data: self.assertEqual(expected, utils.NormalizePath(test))
def Run(self, args): """Delete all the GRR temp files in path. If path is a directory, look in the top level for filenames beginning with Client.tempfile_prefix, and delete them. If path is a regular file and starts with Client.tempfile_prefix delete it. Args: args: pathspec pointing to directory containing temp files to be deleted, or a single file to be deleted. Returns: deleted: array of filename strings that were deleted Raises: ErrorBadPath: if path doesn't exist or is not a regular file or directory """ # Normalize the path, so DeleteGRRTempFile can correctly check if # it is within Client.tempdir. if args.path: path = client_utils.CanonicalPathToLocalPath( utils.NormalizePath(args.path)) else: path = config_lib.CONFIG["Client.tempdir"] deleted = [] if os.path.isdir(path): for filename in os.listdir(path): abs_filename = os.path.join(path, filename) try: DeleteGRRTempFile(abs_filename) deleted.append(abs_filename) except ErrorNotTempFile: pass elif os.path.isfile(path): DeleteGRRTempFile(path) deleted = [path] elif not os.path.exists(path): raise ErrorBadPath("File %s does not exist" % path) else: raise ErrorBadPath("Not a regular file or directory: %s" % path) out_rdfvalue = rdfvalue.LogMessage(data="Deleted: %s" % deleted) self.SendReply(out_rdfvalue)
def GetRawDevice(path): """Resolves the raw device that contains the path. Args: path: A path to examine. Returns: A pathspec to read the raw device as well as the modified path to read within the raw device. This is usually the path without the mount point. Raises: IOError: if the path does not exist or some unexpected behaviour occurs. """ path = CanonicalPathToLocalPath(path) # Try to expand the shortened paths try: path = win32file.GetLongPathName(path) except pywintypes.error: pass try: mount_point = win32file.GetVolumePathName(path) except pywintypes.error as details: logging.info("path not found. %s", details) raise IOError("No mountpoint for path: %s", path) if not path.startswith(mount_point): stripped_mp = mount_point.rstrip("\\") if not path.startswith(stripped_mp): raise IOError("path %s is not mounted under %s" % (path, mount_point)) corrected_path = LocalPathToCanonicalPath(path[len(mount_point):]) corrected_path = utils.NormalizePath(corrected_path) volume = win32file.GetVolumeNameForVolumeMountPoint(mount_point).rstrip( "\\") volume = LocalPathToCanonicalPath(volume) # The pathspec for the raw volume result = rdf_paths.PathSpec(path=volume, pathtype=rdf_paths.PathSpec.PathType.OS, mount_point=mount_point.rstrip("\\")) return result, corrected_path
def CanonicalPathToLocalPath(path): """Linux uses a normal path. We always want to encode as UTF-8 here. If the environment for the client is broken, Python might assume an ASCII based filesystem (those should be rare nowadays) and things will go wrong if we let Python decide what to do. If the filesystem actually is ASCII, encoding and decoding will not change anything so things will still work as expected. Args: path: the canonical path as an Unicode string Returns: a unicode string or an encoded (narrow) string dependent on system settings """ return utils.SmartStr(utils.NormalizePath(path))
def testTSKFileInode(self): """Test opening a file through an indirect pathspec.""" pathspec = rdf_paths.PathSpec(path=os.path.join( self.base_path, "test_img.dd"), pathtype=rdf_paths.PathSpec.PathType.OS) pathspec.Append(pathtype=rdf_paths.PathSpec.PathType.TSK, inode=12, path="/Test Directory") pathspec.Append(pathtype=rdf_paths.PathSpec.PathType.TSK, path="numbers.txt") fd = vfs.VFSOpen(pathspec) # Check that the new pathspec is correctly reduced to two components. self.assertEqual( fd.pathspec.first.path, utils.NormalizePath(os.path.join(self.base_path, "test_img.dd"))) self.assertEqual(fd.pathspec[1].path, "/Test Directory/numbers.txt") # And the correct inode is placed in the final branch. self.assertEqual(fd.Stat().pathspec.nested_path.inode, 15) self.TestFileHandling(fd)
def CanonicalPathToLocalPath(path): """Linux uses a normal path. If sys.getfilesystemencoding() returns None normally any call to a system function will try to encode the string to ASCII. A modern version of Linux will use UTF-8 as (narrow) string encoding. locale.getpreferredencoding() seems to return ASCII at this point. So for older versions of Linux we'll need to rely on locale.getdefaultlocale()[1]. If everything fails we fallback to UTF-8. Args: path: the canonical path as an Unicode string Returns: a unicode string or an encoded (narrow) string dependent on system settings """ canonical_path = utils.NormalizePath(path) if sys.getfilesystemencoding(): return canonical_path encoding = locale.getdefaultlocale()[1] or "UTF-8" return canonical_path.encode(encoding)
def Interpolate(self, client=None): try: kb = client.Get(client.Schema.KNOWLEDGE_BASE) if not kb: raise artifact_lib.KnowledgeBaseInterpolationError( "Client has no knowledge base") patterns = artifact_lib.InterpolateKbAttributes(self._value, kb) except artifact_lib.KnowledgeBaseInterpolationError: # TODO(user): Deprecate InterpolateClientAttributes() support and # make KnowledgeBase the default and only option as soon as we're # confident that it's fully populated. logging.debug( "Can't interpolate glob %s with knowledge base attributes, " "reverting to client attributes.", utils.SmartUnicode(self)) patterns = self.InterpolateClientAttributes(client=client) for pattern in patterns: # Normalize the component path (this allows us to resolve ../ # sequences). pattern = utils.NormalizePath(pattern.replace("\\", "/")) for pattern in self.InterpolateGrouping(pattern): yield pattern
def LocalPathToCanonicalPath(path): """Linux uses a normal path.""" return utils.NormalizePath(path)