1# Copyright 2017 - The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Base classes to implement Mounter classes."""
15
16import abc
17import logging
18
19
20class MounterFile(object):
21
22  def __init__(self, filename, cleanup_func=None):
23    self._filename = filename
24    self._clean_up_func = cleanup_func
25
26  def _handle_get_filename(self):
27    return self._filename
28
29  def _handle_clean_up(self):
30    if self._clean_up_func:
31      self._clean_up_func()
32
33  def __enter__(self):
34    return self._handle_get_filename()
35
36  def __exit__(self, exc_type, exc_val, exc_tb):
37    self._handle_clean_up()
38
39  def get_filename(self):
40    return self._handle_get_filename()
41
42  def clean_up(self):
43    self._handle_clean_up()
44
45
46class MounterFileList(object):
47
48  def __init__(self, file_list):
49    self._file_list = file_list
50
51  def _handle_get_filenames(self):
52    return [x.get_filename() for x in self._file_list]
53
54  def _handle_clean_up(self):
55    for x in reversed(self._file_list):
56      x.clean_up()
57
58  def __enter__(self):
59    return self._handle_get_filenames()
60
61  def __exit__(self, exc_type, exc_val, exc_tb):
62    self._handle_clean_up()
63
64  def get_filenames(self):
65    return self._handle_get_filenames()
66
67  def clean_up(self):
68    self._handle_clean_up()
69
70
71class BaseFileAccessor(object):
72  """An abstract class to implement the file accessors.
73
74  A mounter returns a file accessor when it is mounted. A file accessor must
75  override the method  _handle_prepare_file() to return the file name of
76  the requested file in the storage. However, files in some mounter storages
77  couldn't be access directly, e.g. the file accessor of AdbMounter, which
78  accesses the file in a device by adb. In this case, file accessor could
79  return a temp file which contains the content. A file accessor could give the
80  cleanup_func when creating MounterFile to cleanup the temp file.
81  """
82
83  __metaclass__ = abc.ABCMeta
84
85  def __init__(self, path_prefix='/'):
86    logging.debug('BaseFileAccessor(path_prefix=%s)', path_prefix)
87    self._path_prefix = path_prefix
88
89  def _get_pathfile_to_access(self, file_to_map):
90    path_prefix = self._path_prefix
91
92    if not file_to_map.startswith(path_prefix):
93      raise RuntimeError('"%s" does not start with "%s"', file_to_map,
94                         path_prefix)
95
96    return file_to_map[len(path_prefix):]
97
98  @abc.abstractmethod
99  def _handle_prepare_file(self, filename_in_storage):
100    """Override this method to prepare the given file in the storage.
101
102    Args:
103      filename_in_storage: the file in the storage to be prepared
104
105    Returns:
106      Return an MounterFile instance. Return None if the request file is not
107      in the mount.
108    """
109
110  def prepare_file(self, filename_in_mount):
111    """Return the accessable file name in the storage.
112
113    The function prepares a accessable file which contains the content of the
114    filename_in_mount.
115
116    See BaseFileAccessor for the detail.
117
118    Args:
119      filename_in_mount: the file to map.
120        filename_in_mount should be a full path file as the path in a real
121        device, and must start with a '/'. For example: '/system/build.prop',
122        '/vendor/default.prop', '/init.rc', etc.
123
124    Returns:
125      A MounterFile instance. Return None if the file is not exit in the
126      storage.
127    """
128    filename_in_storage = self._get_pathfile_to_access(filename_in_mount)
129    ret = self._handle_prepare_file(filename_in_storage)
130    return ret if ret else MounterFile(None)
131
132  def prepare_multi_files(self, filenames_in_mount):
133    file_list = [self.prepare_file(x) for x in filenames_in_mount]
134    return MounterFileList(file_list)
135
136
137class BaseMounter(object):
138
139  __metaclass__ = abc.ABCMeta
140
141  @abc.abstractmethod
142  def _handle_mount(self):
143    """Override this method to handle mounting and return a file accessor.
144
145    File accessor must inherit from  BaseFileAccessor.
146    """
147
148  def _handle_unmount(self):
149    """Override this method to handle cleanup this mounting."""
150    # default is do nothing
151    return
152
153  def _process_mount(self):
154    if self._mounted:
155      raise RuntimeError('The mounter had been mounted.')
156
157    file_accessor = self._handle_mount()
158    self._mounted = True
159
160    return file_accessor
161
162  def _process_unmount(self):
163    if self._mounted:
164      self._handle_unmount()
165      self._mounted = False
166
167  def __init__(self):
168    self._mounted = False
169
170  def __enter__(self):
171    return self._process_mount()
172
173  def __exit__(self, exc_type, exc_val, exc_tb):
174    self._process_unmount()
175
176  def mount(self):
177    return self._process_mount()
178
179  def unmount(self):
180    self._process_unmount()
181