• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2
3#
4# Copyright (C) 2019 The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#      http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19from typing import Any, Callable, Dict, Generic, Iterable, List, NamedTuple, TextIO, Tuple, TypeVar, Optional, Union, TextIO
20
21import re
22
23class Inode2Filename:
24  """
25  Parses a text file of the format
26     "uint(dev_t) uint(ino_t) int(file_size) string(filepath)\\n"*
27
28  Lines not matching this format are ignored.
29  """
30
31  def __init__(self, inode_data_file: TextIO):
32    """
33    Create an Inode2Filename that reads cached inode from a file saved earlier
34    (e.g. with pagecache.py -d or with inode2filename --format=textcache)
35
36    :param inode_data_file: a file object (e.g. created with open or StringIO).
37
38    Lifetime: inode_data_file is only used during the construction of the object.
39    """
40    self._inode_table = Inode2Filename.build_inode_lookup_table(inode_data_file)
41
42  @classmethod
43  def new_from_filename(cls, textcache_filename: str) -> 'Inode2Filename':
44    """
45    Create an Inode2Filename that reads cached inode from a file saved earlier
46    (e.g. with pagecache.py -d or with inode2filename --format=textcache)
47
48    :param textcache_filename: path to textcache
49    """
50    with open(textcache_filename) as inode_data_file:
51      return cls(inode_data_file)
52
53  @staticmethod
54  def build_inode_lookup_table(inode_data_file: TextIO) -> Dict[Tuple[int, int], Tuple[str, str]]:
55    """
56    :return: map { (device_int, inode_int) -> (filename_str, size_str) }
57    """
58    inode2filename = {}
59    for line in inode_data_file:
60      # stat -c "%d %i %s %n
61      # device number, inode number, total size in bytes, file name
62      result = re.match('([0-9]+)d? ([0-9]+) -?([0-9]+) (.*)', line)
63      if result:
64        inode2filename[(int(result.group(1)), int(result.group(2)))] = \
65            (result.group(4), result.group(3))
66
67    return inode2filename
68
69  def resolve(self, dev_t: int, ino_t: int) -> Optional[str]:
70    """
71    Return a filename (str) from a (dev_t, ino_t) inode pair.
72
73    Returns None if the lookup fails.
74    """
75    maybe_result = self._inode_table.get((dev_t, ino_t))
76
77    if not maybe_result:
78      return None
79
80    return maybe_result[0] # filename str
81
82  def __len__(self) -> int:
83    """
84    :return: the number of inode entries parsed from the file.
85    """
86    return len(self._inode_table)
87
88  def __repr__(self) -> str:
89    """
90    :return: string representation for debugging/test failures.
91    """
92    return "Inode2Filename%s" %(repr(self._inode_table))
93
94  # end of class.
95