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"""Provides class CompositeMounter. 15 16CompositeMounter implements the abstract class BaseMounter. It can add multiple 17mounters inside as sub-mounters, and operate these sub-mounters with the 18BaseMounter interface. Uses CompositeMounter.add_sub_mounter() to add 19sub-mounter. 20 21Usually, using CompositeMounter.add_by_mount_target() to add mounters is easier, 22the method uses class _MounterFactory to create a mounter and then adds it. 23 24class _MounterFactory provides a method to create a mounter by 'mounter_target'. 25'mounter_target' is a name which identify what is the file source to be 26mounted. See _MounterFactory.create_by_mount_target() for the detail. 27""" 28 29import logging 30import os 31 32from gsi_util.mounters import adb_mounter 33from gsi_util.mounters import base_mounter 34from gsi_util.mounters import folder_mounter 35from gsi_util.mounters import image_mounter 36 37SUPPORTED_PARTITIONS = ['system', 'vendor', 'odm'] 38 39 40class _MounterFactory(object): 41 42 @classmethod 43 def create_by_mount_target(cls, mount_target, partition): 44 """Create a proper Mounter instance by a string of mount target. 45 46 Args: 47 partition: the partition to be mounted as 48 mount_target: 'adb', a folder name or an image file name to mount. 49 see Returns for the detail. 50 51 Returns: 52 Returns an AdbMounter if mount_target is 'adb[:SERIAL_NUM]' 53 Returns a FolderMounter if mount_target is a folder name 54 Returns an ImageMounter if mount_target is an image file name 55 56 Raises: 57 ValueError: partiton is not support or mount_target is not exist. 58 """ 59 if partition not in SUPPORTED_PARTITIONS: 60 raise ValueError('Wrong partition name "{}"'.format(partition)) 61 62 if mount_target == 'adb' or mount_target.startswith('adb:'): 63 (_, _, serial_num) = mount_target.partition(':') 64 return adb_mounter.AdbMounter(serial_num) 65 66 path_prefix = '/{}/'.format(partition) 67 68 if os.path.isdir(mount_target): 69 return folder_mounter.FolderMounter(mount_target, path_prefix) 70 71 if os.path.isfile(mount_target): 72 if partition == 'system': 73 path_prefix = image_mounter.ImageMounter.DETECT_SYSTEM_AS_ROOT 74 return image_mounter.ImageMounter(mount_target, path_prefix) 75 76 raise ValueError('Unknown target "{}"'.format(mount_target)) 77 78 79class _CompositeFileAccessor(base_mounter.BaseFileAccessor): 80 81 def __init__(self, file_accessors): 82 super(_CompositeFileAccessor, self).__init__() 83 self._file_accessors = file_accessors 84 85 # override 86 def _handle_prepare_file(self, filename_in_storage): 87 logging.debug('_CompositeFileAccessor._handle_prepare_file(%s)', 88 filename_in_storage) 89 90 pathfile_to_prepare = '/' + filename_in_storage 91 for (prefix_path, file_accessor) in self._file_accessors: 92 if pathfile_to_prepare.startswith(prefix_path): 93 return file_accessor.prepare_file(pathfile_to_prepare) 94 95 logging.debug(' Not found') 96 return None 97 98 99class CompositeMounter(base_mounter.BaseMounter): 100 """Implements a BaseMounter which can add multiple sub-mounters.""" 101 102 def __init__(self): 103 super(CompositeMounter, self).__init__() 104 self._mounters = [] 105 106 def is_empty(self): 107 return not self._mounters 108 109 # override 110 def _handle_mount(self): 111 file_accessors = [(path_prefix, mounter.mount()) 112 for (path_prefix, mounter) in self._mounters] 113 return _CompositeFileAccessor(file_accessors) 114 115 # override 116 def _handle_unmount(self): 117 for (_, mounter) in reversed(self._mounters): 118 mounter.unmount() 119 120 def add_sub_mounter(self, mount_point, mounter): 121 self._mounters.append((mount_point, mounter)) 122 123 def add_by_mount_target(self, partition, mount_target): 124 logging.debug('CompositeMounter.add_by_mount_target(%s, %s)', 125 partition, mount_target) 126 mount_point = '/{}/'.format(partition) 127 mounter = _MounterFactory.create_by_mount_target(mount_target, partition) 128 self.add_sub_mounter(mount_point, mounter) 129