def Add(self, path, age=None): """Add a relative stem to the current value and return a new RDFURN. If urn is a fully qualified URN, replace the current value with it. Args: path: A string containing a relative path. age: The age of the object. If None set to current time. Returns: A new RDFURN that can be chained. Raises: ValueError: if the path component is not a string. """ if not isinstance(path, basestring): raise ValueError("Only strings should be added to a URN.") result = self.Copy(age) result.Update(path=utils.JoinPath(self._string_urn, path)) return result
def Add(self, path, age=None): """Add a relative stem to the current value and return a new RDFURN. Note that this returns an RDFURN, not a ClientURN since the resulting object would not pass validation. Args: path: A string containing a relative path. age: The age of the object. If None set to current time. Returns: A new RDFURN that can be chained. Raises: ValueError: if the path component is not a string. """ if not isinstance(path, basestring): raise ValueError("Only strings should be added to a URN.") result = rdfvalue.RDFURN(self.Copy(age)) result.Update(path=utils.JoinPath(self._string_urn, path)) return result
def setUp(self): super(DeleteGRRTempFiles, self).setUp() filename = "%s_blah" % config.CONFIG["Client.tempfile_prefix"] self.tempfile = utils.JoinPath(self.temp_dir, "delete_test", filename) self.dirname = os.path.dirname(self.tempfile) os.makedirs(self.dirname) self.tempdir_overrider = test_lib.ConfigOverrider({ "Client.tempdir_roots": [os.path.dirname(self.dirname)], "Client.grr_tempdir": os.path.basename(self.dirname) }) self.tempdir_overrider.Start() self.not_tempfile = os.path.join(self.temp_dir, "notatempfile") open(self.not_tempfile, "wb").write("something") self.temp_fd = tempfiles.CreateGRRTempFile(filename="file1") self.temp_fd2 = tempfiles.CreateGRRTempFile(filename="file2") self.assertTrue(os.path.exists(self.not_tempfile)) self.assertTrue(os.path.exists(self.temp_fd.name)) self.assertTrue(os.path.exists(self.temp_fd2.name)) self.pathspec = rdf_paths.PathSpec( path=self.dirname, pathtype=rdf_paths.PathSpec.PathType.OS)
def _Stat(self, name, value, value_type, mtime=None): response = rdf_client.StatEntry() response_pathspec = self.pathspec.Copy() # No matter how we got here, there is no need to do case folding from now on # since this is the exact filename casing. response_pathspec.path_options = rdf_paths.PathSpec.Options.CASE_LITERAL response_pathspec.last.path = utils.JoinPath(response_pathspec.last.path, name) response.pathspec = response_pathspec if self.IsDirectory(): response.st_mode = stat.S_IFDIR else: response.st_mode = stat.S_IFREG if mtime: response.st_mtime = mtime response.st_size = len(utils.SmartStr(value)) if value_type is not None: response.registry_type = self.registry_map.get(value_type, 0) response.registry_data = rdf_protodict.DataBlob().SetValue(value) return response
def DownloadDir(aff4_path, output_dir, bufsize=8192, preserve_path=True): """Take an aff4 path and download all files in it to output_dir. Args: aff4_path: Any aff4 path as a string output_dir: A local directory to write to, will be created if not there. bufsize: Buffer size to use. preserve_path: If set all paths will be created. Note that this works for collections as well. It will download all files in the collection. This only downloads files that are already in the datastore, it doesn't queue anything on the client. """ if not os.path.isdir(output_dir): os.makedirs(output_dir) fd = aff4.FACTORY.Open(aff4_path) for child in fd.OpenChildren(): if preserve_path: # Get a full path without the aff4: full_dir = utils.JoinPath(output_dir, child.urn.Path()) full_dir = os.path.dirname(full_dir) if not os.path.isdir(full_dir): os.makedirs(full_dir) outfile = os.path.join(full_dir, child.urn.Basename()) else: outfile = os.path.join(output_dir, child.urn.Basename()) logging.info(u"Downloading %s to %s", child.urn, outfile) with open(outfile, "wb") as out_fd: try: buf = child.Read(bufsize) while buf: out_fd.write(buf) buf = child.Read(bufsize) except IOError as e: logging.error("Failed to read %s. Err: %s", child.urn, e)
def __init__(self, base_fd, pathspec=None, progress_callback=None, full_pathspec=None): """Use TSK to read the pathspec. Args: base_fd: The file like object we read this component from. pathspec: An optional pathspec to open directly. progress_callback: A callback to indicate that the open call is still working but needs more time. full_pathspec: The full pathspec we are trying to open. Raises: IOError: If the file can not be opened. """ super(TSKFile, self).__init__(base_fd, pathspec=pathspec, progress_callback=progress_callback, full_pathspec=full_pathspec) if self.base_fd is None: raise IOError("TSK driver must have a file base.") # If our base is another tsk driver - borrow the reference to the raw # device, and replace the last pathspec component with this one after # extending its path. elif isinstance(base_fd, TSKFile) and self.base_fd.IsDirectory(): self.tsk_raw_device = self.base_fd.tsk_raw_device last_path = utils.JoinPath(self.pathspec.last.path, pathspec.path) # Replace the last component with this one. self.pathspec.Pop(-1) self.pathspec.Append(pathspec) self.pathspec.last.path = last_path # Use the base fd as a base to parse the filesystem only if its file like. elif not self.base_fd.IsDirectory(): self.tsk_raw_device = self.base_fd self.pathspec.Append(pathspec) else: # If we get here we have a directory from a non sleuthkit driver - dont # know what to do with it. raise IOError("Unable to parse base using Sleuthkit.") # If we are successful in opening this path below the path casing is # correct. self.pathspec.last.path_options = rdf_paths.PathSpec.Options.CASE_LITERAL fd_hash = self.tsk_raw_device.pathspec.SerializeToString() # Cache the filesystem using the path of the raw device try: self.filesystem = vfs.DEVICE_CACHE.Get(fd_hash) self.fs = self.filesystem.fs except KeyError: self.img = MyImgInfo(fd=self.tsk_raw_device, progress_callback=progress_callback) self.fs = pytsk3.FS_Info(self.img, 0) self.filesystem = CachedFilesystem(self.fs, self.img) vfs.DEVICE_CACHE.Put(fd_hash, self.filesystem) # We prefer to open the file based on the inode because that is more # efficient. if pathspec.HasField("inode"): self.fd = self.fs.open_meta(pathspec.inode) self.tsk_attribute = self.GetAttribute(pathspec.ntfs_type, pathspec.ntfs_id) if self.tsk_attribute: self.size = self.tsk_attribute.info.size else: self.size = self.fd.info.meta.size else: # Does the filename exist in the image? self.fd = self.fs.open(utils.SmartStr(self.pathspec.last.path)) self.size = self.fd.info.meta.size self.pathspec.last.inode = self.fd.info.meta.addr
def MakeStatResponse(self, tsk_file, tsk_attribute=None, append_name=False): """Given a TSK info object make a StatEntry. Note that tsk uses two things to uniquely identify a data stream - the inode object given in tsk_file and the attribute object which may correspond to an ADS of this file for filesystems which support ADS. We store both of these in the stat response. Args: tsk_file: A TSK File object for the specified inode. tsk_attribute: A TSK Attribute object for the ADS. If None we use the main stream. append_name: If specified we append this name to the last element of the pathspec. Returns: A StatEntry which can be used to re-open this exact VFS node. """ info = tsk_file.info response = rdf_client.StatEntry() meta = info.meta if meta: response.st_ino = meta.addr for attribute in [ "mode", "nlink", "uid", "gid", "size", "atime", "mtime", "ctime", "crtime" ]: try: value = int(getattr(meta, attribute)) if value < 0: value &= 0xFFFFFFFF setattr(response, "st_%s" % attribute, value) except AttributeError: pass name = info.name child_pathspec = self.pathspec.Copy() if append_name: # Append the name to the most inner pathspec child_pathspec.last.path = utils.JoinPath( child_pathspec.last.path, utils.SmartUnicode(append_name)) child_pathspec.last.inode = meta.addr if tsk_attribute is not None: child_pathspec.last.ntfs_type = int(tsk_attribute.info.type) child_pathspec.last.ntfs_id = int(tsk_attribute.info.id) child_pathspec.last.stream_name = tsk_attribute.info.name # Update the size with the attribute size. response.st_size = tsk_attribute.info.size default = rdf_paths.PathSpec.tsk_fs_attr_type.TSK_FS_ATTR_TYPE_DEFAULT last = child_pathspec.last if last.ntfs_type != default or last.ntfs_id: # This is an ads and should be treated as a file. # Clear all file type bits. response.st_mode &= ~self.stat_type_mask response.st_mode |= stat.S_IFREG else: child_pathspec.last.ntfs_type = None child_pathspec.last.ntfs_id = None child_pathspec.last.stream_name = None if name: # Encode the type onto the st_mode response response.st_mode |= self.FILE_TYPE_LOOKUP.get(int(name.type), 0) if meta: # What if the types are different? What to do here? response.st_mode |= self.META_TYPE_LOOKUP.get(int(meta.type), 0) # Write the pathspec on the response. response.pathspec = child_pathspec return response
def MakeDestinationKey(directory, filename): """Creates a name that identifies a database file.""" return utils.SmartStr(utils.JoinPath(directory, filename)).lstrip("/")
def __init__(self, base_fd, pathspec=None, progress_callback=None, full_pathspec=None): super(File, self).__init__( base_fd, pathspec=pathspec, full_pathspec=full_pathspec, progress_callback=progress_callback) if base_fd is None: self.pathspec.Append(pathspec) # We can stack on another directory, which means we concatenate their # directory with ours. elif base_fd.IsDirectory(): self.pathspec.last.path = utils.JoinPath(self.pathspec.last.path, pathspec.path) else: raise IOError("File handler can not be stacked on another handler.") self.path = self.pathspec.last.path # We can optionally apply a global offset to the file. if self.pathspec[0].HasField("offset"): self.file_offset = self.pathspec[0].offset self.pathspec.last.path_options = rdf_paths.PathSpec.Options.CASE_LITERAL self.FileHacks() self.filename = client_utils.CanonicalPathToLocalPath(self.path) error = None # Pythonic way - duck typing. Is the handle a directory? try: if not self.files: # Note that the encoding of local path is system specific local_path = client_utils.CanonicalPathToLocalPath(self.path + "/") self.files = [ utils.SmartUnicode(entry) for entry in os.listdir(local_path) ] # Some filesystems do not support unicode properly except UnicodeEncodeError as e: raise IOError(str(e)) except (IOError, OSError) as e: self.files = [] error = e # Ok, it's not. Is it a file then? try: with FileHandleManager(self.filename) as fd: if pathspec.last.HasField("file_size_override"): self.size = pathspec.last.file_size_override - self.file_offset else: # Work out how large the file is. if self.size is None: fd.Seek(0, 2) end = fd.Tell() if end == 0: # This file is not seekable, we just use the default. end = pathspec.last.file_size_override self.size = end - self.file_offset error = None # Some filesystems do not support unicode properly except UnicodeEncodeError as e: raise IOError(str(e)) except IOError as e: if error: error = e if error is not None: raise error # pylint: disable=raising-bad-type
def Open(cls, fd, component, pathspec=None, progress_callback=None, full_pathspec=None): """Try to correct the casing of component. This method is called when we failed to open the component directly. We try to transform the component into something which is likely to work. In this implementation, we correct the case of the component until we can not open the path any more. Args: fd: The base fd we will use. component: The component we should open. pathspec: The rest of the pathspec object. progress_callback: A callback to indicate that the open call is still working but needs more time. full_pathspec: The full pathspec we are trying to open. Returns: A file object. Raises: IOError: If nothing could be opened still. """ # The handler for this component try: handler = VFS_HANDLERS[component.pathtype] except KeyError: raise IOError("VFS handler %d not supported." % component.pathtype) # We will not do any case folding unless requested. if component.path_options == rdf_paths.PathSpec.Options.CASE_LITERAL: return handler(base_fd=fd, pathspec=component) path_components = client_utils.LocalPathToCanonicalPath(component.path) path_components = ["/"] + filter(None, path_components.split("/")) for i, path_component in enumerate(path_components): try: if fd: new_pathspec = fd.MatchBestComponentName(path_component) else: new_pathspec = component new_pathspec.path = path_component # The handler for this component try: handler = VFS_HANDLERS[new_pathspec.pathtype] except KeyError: raise IOError("VFS handler %d not supported." % new_pathspec.pathtype) fd = handler(base_fd=fd, pathspec=new_pathspec, full_pathspec=full_pathspec, progress_callback=progress_callback) except IOError: # Can not open the first component, we must raise here. if i <= 1: raise IOError("File not found") # Insert the remaining path at the front of the pathspec. pathspec.Insert(0, path=utils.JoinPath(*path_components[i:]), pathtype=rdf_paths.PathSpec.PathType.TSK) break return fd
def _Touch(self, relative_path): open(utils.JoinPath(self.temp_dir, relative_path), "wb").close()
def CollapsePath(self): return utils.JoinPath(*[x.path for x in self])