1"""
2@copyright: 2009 Bastian Blank <waldi@debian.org>
3@license: GNU GPL-3
4"""
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License version 3 as
7# published by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17import struct
18
19from .general import DescriptorTag, ExtentAD
20
21
22class AnchorVolumeDescriptorPointer(object):
23    _struct = struct.Struct('<16x20s20s')
24
25    def __init__(self, tag, buf):
26        self.tag = tag
27        if tag.identifier != 2:
28            raise RuntimeError('Anchor Volume Descriptor Pointer illegal')
29
30        data = self._struct.unpack(buf[:56])
31        self.main_descriptor_extent = ExtentAD(data[0])
32        self.reserve_descriptor_extent = ExtentAD(data[1])
33
34
35def _DescriptorSequence(media, location, length):
36    buf = media.read_sector(location, length)
37
38    cur = 0
39    while cur < length:
40        tag = DescriptorTag(buf[cur:cur + 16])
41        if tag.identifier == 8:
42            break
43
44        yield tag, buf[cur:cur + 2048]
45
46        cur += 2048
47
48
49class Volume(object):
50    _lazy = 'partitions',
51
52    def __init__(self, media):
53        self._media = media
54
55        buf = media.read_sector(256)
56        tag = DescriptorTag(buf)
57        self.anchor = AnchorVolumeDescriptorPointer(tag, buf)
58
59    def __getattr__(self, key):
60        if key in self._lazy:
61            self._populate()
62        return super().__getattribute__(key)
63
64    def _read_descriptors(self, extent):
65        seq = _DescriptorSequence(self._media, extent.location, extent.length)
66
67        partitions = {}
68
69        for tag, buf in seq:
70            if tag.identifier == 5:
71                p = Partition(self._media, self, tag, buf)
72                partitions[p.number] = p
73            elif tag.identifier == 6:
74                # TODO
75                pass
76
77        if partitions:
78            return partitions
79        raise RuntimeError
80
81    def _populate(self):
82        try:
83            data = self._read_descriptors(self.anchor.main_descriptor_extent)
84        except RuntimeError:
85            data = self._read_descriptors(self.anchor.reserve_descriptor_extent)
86        self.partitions = data
87
88
89class Partition(object):
90    _lazy = 'fileset'
91    _struct = struct.Struct('<16x4xHH164xII316x')
92
93    def __init__(self, media, volume, tag, buf):
94        self._media = media
95        self._volume = volume
96
97        self.tag = tag
98        if tag.identifier != 5:
99            raise RuntimeError('Partition Descriptor illegal')
100
101        data = self._struct.unpack(buf[:512])
102        self.flags, self.number, self.location, self.length = data
103
104    def __getattr__(self, key):
105        if key in self._lazy:
106            self._populate()
107        return super().__getattribute__(key)
108
109    def _populate(self):
110        from .filesystem import FileSet
111
112        seq = _DescriptorSequence(self._media, self.location, self.length)
113
114        for tag, buf in seq:
115            if tag.identifier == 256:
116                self.fileset = FileSet(self._media, self._volume, self.number, tag, buf)
117