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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
# Copyright (C) 2018-2019 Chris Lalancette <clalancette@gmail.com>
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License.
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
'''
PyCdlib Inode class.
'''
from __future__ import absolute_import
from pycdlib import pycdlibexception
# For mypy annotations
if False: # pylint: disable=using-constant-test
from typing import BinaryIO, List, Optional, Tuple, Union # NOQA pylint: disable=unused-import
# NOTE: this import has to be here to avoid circular deps
from pycdlib import dr # NOQA pylint: disable=unused-import
from pycdlib import eltorito # NOQA pylint: disable=unused-import
from pycdlib import udf # NOQA pylint: disable=unused-import
class Inode(object):
'''
A class that represents an inode, the pointer to a piece of data
(not metadata) on an ISO.
'''
__slots__ = ('_initialized', 'new_extent_loc', 'orig_extent_loc',
'linked_records', 'data_length', 'manage_fp', 'data_fp',
'original_data_location', 'fp_offset', 'boot_info_table',
'num_udf')
DATA_ON_ORIGINAL_ISO = 1
DATA_IN_EXTERNAL_FP = 2
def __init__(self):
# type: () -> None
self.linked_records = [] # type: List[Tuple[Union[eltorito.EltoritoEntry, udf.UDFFileEntry, dr.DirectoryRecord], bool]]
self._initialized = False
self.data_length = 0
self.num_udf = 0
self.boot_info_table = None # type: Optional[eltorito.EltoritoBootInfoTable]
self.new_extent_loc = -1
def new(self, length, fp, manage_fp, offset):
# type: (int, Union[BinaryIO, str], bool, int) -> None
'''
Initialize a new Inode.
Parameters:
None.
Returns:
Nothing.
'''
if self._initialized:
raise pycdlibexception.PyCdlibInternalError('Inode is already initialized')
self.data_length = length
self.data_fp = fp
self.manage_fp = manage_fp
self.fp_offset = offset
self.original_data_location = self.DATA_IN_EXTERNAL_FP
self._initialized = True
def parse(self, extent, length, fp, log_block_size):
# type: (int, int, BinaryIO, int) -> None
'''
Parse an existing Inode. This just saves off the extent for later use.
Parameters:
extent - The original extent that the data lives at.
Returns:
Nothing.
'''
if self._initialized:
raise pycdlibexception.PyCdlibInternalError('Inode is already initialized')
self.orig_extent_loc = extent
self.data_length = length
self.data_fp = fp
self.manage_fp = False
self.fp_offset = extent * log_block_size
self.original_data_location = self.DATA_ON_ORIGINAL_ISO
self._initialized = True
def extent_location(self):
# type: () -> int
'''
Get the current location of this Inode on the ISO.
Parameters:
None.
Returns:
The extent location of this Inode on the ISO.
'''
if not self._initialized:
raise pycdlibexception.PyCdlibInternalError('Inode is not initialized')
if self.new_extent_loc < 0:
return self.orig_extent_loc
return self.new_extent_loc
def set_extent_location(self, extent):
# type: (int) -> None
'''
Set the current location of this Inode on the ISO.
Parameters:
extent - The new extent location for this Inode.
Returns:
Nothing.
'''
if not self._initialized:
raise pycdlibexception.PyCdlibInternalError('Inode is not initialized')
self.new_extent_loc = extent
def get_data_length(self):
# type: () -> int
'''
Get the length of the data pointed to by this Inode.
Parameters:
None.
Returns:
The length of the data pointed to by this Inode.
'''
if not self._initialized:
raise pycdlibexception.PyCdlibInternalError('Inode is not initialized')
return self.data_length
def add_boot_info_table(self, boot_info_table):
# type: (eltorito.EltoritoBootInfoTable) -> None
'''
Add a boot info table to this Inode.
Parameters:
boot_info_table - The Boot Info Table object to add to this Inode.
Returns:
Nothing.
'''
if not self._initialized:
raise pycdlibexception.PyCdlibInternalError('Inode is not initialized')
self.boot_info_table = boot_info_table
def update_fp(self, fp, length):
# type: (BinaryIO, int) -> None
'''
Update the Inode to use a different file object and length.
Parameters:
fp - A file object that contains the data for this Inode.
length - The length of the data.
Returns:
Nothing.
'''
if not self._initialized:
raise pycdlibexception.PyCdlibInternalError('Inode is not initialized')
self.original_data_location = self.DATA_IN_EXTERNAL_FP
self.data_fp = fp
self.data_length = length
self.fp_offset = 0
class InodeOpenData(object):
'''
A class to be a contextmanager for opening data on a DirectoryRecord object.
'''
__slots__ = ('ino', 'logical_block_size', 'data_fp')
def __init__(self, ino, logical_block_size):
# type: (Inode, int) -> None
self.ino = ino
self.logical_block_size = logical_block_size
def __enter__(self):
if self.ino.manage_fp:
# In the case that we are managing the FP, the data_fp member
# actually contains the filename, not the fp. Use that to
# our advantage here.
self.data_fp = open(self.ino.data_fp, 'rb')
else:
self.data_fp = self.ino.data_fp
if self.ino.original_data_location == self.ino.DATA_ON_ORIGINAL_ISO:
self.data_fp.seek(self.ino.orig_extent_loc * self.logical_block_size)
else:
self.data_fp.seek(self.ino.fp_offset)
return self.data_fp, self.ino.data_length
def __exit__(self, *args):
if self.ino.manage_fp:
self.data_fp.close()
|