'Search the file for the pattern.
This implements the grep algorithm used to scan files. It reads
the data in chunks of BUFF_SIZE (10 MB currently) and can use
different functions to search for matching patterns. In every
step, a buffer that is a bit bigger than the block size is used in
order to return all the requested results. Specifically, a
preamble is used in order to not miss any patterns that start in
one block of data and end in the next and also a postscript buffer
is kept such that the algorithm can return bytes trailing the
pattern even if the pattern is at the end of one block.
One block:
| Pre | Data         | Post |
Searching the pattern is done here:
The following block is constructed like this:
| Pre | Data         | Post |
| Pre | Data         | Post |
The preamble is filled from Data so every hit that happens to fall
entirely into the preamble has to be discarded since it has
already been discovered in the step before.
Grepping for memory
If this action is used to grep the memory of a client machine
using one of the GRR memory acquisition drivers, we have to be
very careful not to have any hits in the GRR process memory space
itself. Therefore, if the input is a literal, it is XOR encoded
and only visible in memory when the pattern is matched. This is
done using bytearrays which guarantees in place updates and no
leaking patterns. Also the returned data is encoded using a
different XOR \'key\'.
This should guarantee that there are no hits when the pattern is
not present in memory. However, since the data will be copied to
the preamble and the postscript, a single pattern might in some
cases produce multiple hits.
Args:
args: A protobuf describing the grep request.
Raises:
RuntimeError: No search pattern has been given in the request.'

def Run(self, args):

    fd = vfs.VFSOpen(args.target, progress_callback=self.Progress)
    fd.Seek(args.start_offset)
    base_offset = args.start_offset
    self.xor_in_key = args.xor_in_key
    self.xor_out_key = args.xor_out_key
    if args.regex:
        find_func = functools.partial(self.FindRegex, args.regex)
    elif args.literal:
        find_func = functools.partial(self.FindLiteral, bytearray(utils.SmartStr(args.literal)))
    else:
        raise RuntimeError('Grep    needs    a    regex    or    a    literal.')
    preamble_size = 0
    postscript_size = 0
    hits = 0
    data = ''
    while (fd.Tell() < (args.start_offset + args.length)):
        to_read = min(args.length, self.BUFF_SIZE, ((args.start_offset + args.length) - fd.Tell()))
        to_read += (self.ENVELOPE_SIZE - postscript_size)
        read_data = fd.Read(to_read)
        data = (data[((- postscript_size) - self.ENVELOPE_SIZE):] + read_data)
        postscript_size = max(0, (self.ENVELOPE_SIZE - (to_read - len(read_data))))
        data_size = ((len(data) - preamble_size) - postscript_size)
        if ((data_size == 0) and (postscript_size == 0)):
            break
        for (start, end) in find_func(data):
            if (end <= preamble_size):
                continue
            if (end > (preamble_size + data_size)):
                continue
            if (((end + base_offset) - preamble_size) > (args.start_offset + args.length)):
                break
            out_data = ''
            for i in xrange(max(0, (start - args.bytes_before)), min(len(data), (end + args.bytes_after))):
                out_data += chr((ord(data[i]) ^ self.xor_out_key))
            hits += 1
            self.SendReply(rdf_client.BufferReference(offset=((base_offset + start) - preamble_size), data=out_data, length=len(out_data), pathspec=fd.pathspec))
            if (args.mode == rdf_client.GrepSpec.Mode.FIRST_HIT):
                return
            if (hits >= self.HIT_LIMIT):
                msg = utils.Xor(('This    Grep    has    reached    the    maximum    number    of    hits    (%d).' % self.HIT_LIMIT), self.xor_out_key)
                self.SendReply(rdf_client.BufferReference(offset=0, data=msg, length=len(msg)))
                return
        self.Progress()
        base_offset += data_size
        preamble_size = min(len(data), self.ENVELOPE_SIZE)
