summaryrefslogtreecommitdiff
path: root/startop/scripts/iorap/lib/inode2filename.py
blob: 2e713936319aab7895f5f768f0ec1b90ccc4ca93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#!/usr/bin/env python3

#
# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from typing import Any, Callable, Dict, Generic, Iterable, List, NamedTuple, TextIO, Tuple, TypeVar, Optional, Union, TextIO

import re

class Inode2Filename:
  """
  Parses a text file of the format
     "uint(dev_t) uint(ino_t) int(file_size) string(filepath)\\n"*

  Lines not matching this format are ignored.
  """

  def __init__(self, inode_data_file: TextIO):
    """
    Create an Inode2Filename that reads cached inode from a file saved earlier
    (e.g. with pagecache.py -d or with inode2filename --format=textcache)

    :param inode_data_file: a file object (e.g. created with open or StringIO).

    Lifetime: inode_data_file is only used during the construction of the object.
    """
    self._inode_table = Inode2Filename.build_inode_lookup_table(inode_data_file)

  @classmethod
  def new_from_filename(cls, textcache_filename: str) -> 'Inode2Filename':
    """
    Create an Inode2Filename that reads cached inode from a file saved earlier
    (e.g. with pagecache.py -d or with inode2filename --format=textcache)

    :param textcache_filename: path to textcache
    """
    with open(textcache_filename) as inode_data_file:
      return cls(inode_data_file)

  @staticmethod
  def build_inode_lookup_table(inode_data_file: TextIO) -> Dict[Tuple[int, int], Tuple[str, str]]:
    """
    :return: map { (device_int, inode_int) -> (filename_str, size_str) }
    """
    inode2filename = {}
    for line in inode_data_file:
      # stat -c "%d %i %s %n
      # device number, inode number, total size in bytes, file name
      result = re.match('([0-9]+)d? ([0-9]+) -?([0-9]+) (.*)', line)
      if result:
        inode2filename[(int(result.group(1)), int(result.group(2)))] = \
            (result.group(4), result.group(3))

    return inode2filename

  def resolve(self, dev_t: int, ino_t: int) -> Optional[str]:
    """
    Return a filename (str) from a (dev_t, ino_t) inode pair.

    Returns None if the lookup fails.
    """
    maybe_result = self._inode_table.get((dev_t, ino_t))

    if not maybe_result:
      return None

    return maybe_result[0] # filename str

  def __len__(self) -> int:
    """
    :return: the number of inode entries parsed from the file.
    """
    return len(self._inode_table)

  def __repr__(self) -> str:
    """
    :return: string representation for debugging/test failures.
    """
    return "Inode2Filename%s" %(repr(self._inode_table))

  # end of class.