xref: /aosp_15_r20/build/make/tools/fat16copy.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Worker#!/usr/bin/env python
2*9e94795aSAndroid Build Coastguard Worker#
3*9e94795aSAndroid Build Coastguard Worker# Copyright 2016 The Android Open Source Project
4*9e94795aSAndroid Build Coastguard Worker#
5*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*9e94795aSAndroid Build Coastguard Worker#
9*9e94795aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*9e94795aSAndroid Build Coastguard Worker#
11*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*9e94795aSAndroid Build Coastguard Worker# limitations under the License.
16*9e94795aSAndroid Build Coastguard Worker
17*9e94795aSAndroid Build Coastguard Workerimport os
18*9e94795aSAndroid Build Coastguard Workerimport sys
19*9e94795aSAndroid Build Coastguard Workerimport struct
20*9e94795aSAndroid Build Coastguard Worker
21*9e94795aSAndroid Build Coastguard WorkerFAT_TABLE_START = 0x200
22*9e94795aSAndroid Build Coastguard WorkerDEL_MARKER = 0xe5
23*9e94795aSAndroid Build Coastguard WorkerESCAPE_DEL_MARKER = 0x05
24*9e94795aSAndroid Build Coastguard Worker
25*9e94795aSAndroid Build Coastguard WorkerATTRIBUTE_READ_ONLY = 0x1
26*9e94795aSAndroid Build Coastguard WorkerATTRIBUTE_HIDDEN = 0x2
27*9e94795aSAndroid Build Coastguard WorkerATTRIBUTE_SYSTEM = 0x4
28*9e94795aSAndroid Build Coastguard WorkerATTRIBUTE_VOLUME_LABEL = 0x8
29*9e94795aSAndroid Build Coastguard WorkerATTRIBUTE_SUBDIRECTORY = 0x10
30*9e94795aSAndroid Build Coastguard WorkerATTRIBUTE_ARCHIVE = 0x20
31*9e94795aSAndroid Build Coastguard WorkerATTRIBUTE_DEVICE = 0x40
32*9e94795aSAndroid Build Coastguard Worker
33*9e94795aSAndroid Build Coastguard WorkerLFN_ATTRIBUTES = \
34*9e94795aSAndroid Build Coastguard Worker    ATTRIBUTE_VOLUME_LABEL | \
35*9e94795aSAndroid Build Coastguard Worker    ATTRIBUTE_SYSTEM | \
36*9e94795aSAndroid Build Coastguard Worker    ATTRIBUTE_HIDDEN | \
37*9e94795aSAndroid Build Coastguard Worker    ATTRIBUTE_READ_ONLY
38*9e94795aSAndroid Build Coastguard WorkerLFN_ATTRIBUTES_BYTE = struct.pack("B", LFN_ATTRIBUTES)
39*9e94795aSAndroid Build Coastguard Worker
40*9e94795aSAndroid Build Coastguard WorkerMAX_CLUSTER_ID = 0x7FFF
41*9e94795aSAndroid Build Coastguard Worker
42*9e94795aSAndroid Build Coastguard Workerdef read_le_short(f):
43*9e94795aSAndroid Build Coastguard Worker  "Read a little-endian 2-byte integer from the given file-like object"
44*9e94795aSAndroid Build Coastguard Worker  return struct.unpack("<H", f.read(2))[0]
45*9e94795aSAndroid Build Coastguard Worker
46*9e94795aSAndroid Build Coastguard Workerdef read_le_long(f):
47*9e94795aSAndroid Build Coastguard Worker  "Read a little-endian 4-byte integer from the given file-like object"
48*9e94795aSAndroid Build Coastguard Worker  return struct.unpack("<L", f.read(4))[0]
49*9e94795aSAndroid Build Coastguard Worker
50*9e94795aSAndroid Build Coastguard Workerdef read_byte(f):
51*9e94795aSAndroid Build Coastguard Worker  "Read a 1-byte integer from the given file-like object"
52*9e94795aSAndroid Build Coastguard Worker  return struct.unpack("B", f.read(1))[0]
53*9e94795aSAndroid Build Coastguard Worker
54*9e94795aSAndroid Build Coastguard Workerdef skip_bytes(f, n):
55*9e94795aSAndroid Build Coastguard Worker  "Fast-forward the given file-like object by n bytes"
56*9e94795aSAndroid Build Coastguard Worker  f.seek(n, os.SEEK_CUR)
57*9e94795aSAndroid Build Coastguard Worker
58*9e94795aSAndroid Build Coastguard Workerdef skip_short(f):
59*9e94795aSAndroid Build Coastguard Worker  "Fast-forward the given file-like object 2 bytes"
60*9e94795aSAndroid Build Coastguard Worker  skip_bytes(f, 2)
61*9e94795aSAndroid Build Coastguard Worker
62*9e94795aSAndroid Build Coastguard Workerdef skip_byte(f):
63*9e94795aSAndroid Build Coastguard Worker  "Fast-forward the given file-like object 1 byte"
64*9e94795aSAndroid Build Coastguard Worker  skip_bytes(f, 1)
65*9e94795aSAndroid Build Coastguard Worker
66*9e94795aSAndroid Build Coastguard Workerdef rewind_bytes(f, n):
67*9e94795aSAndroid Build Coastguard Worker  "Rewind the given file-like object n bytes"
68*9e94795aSAndroid Build Coastguard Worker  skip_bytes(f, -n)
69*9e94795aSAndroid Build Coastguard Worker
70*9e94795aSAndroid Build Coastguard Workerdef rewind_short(f):
71*9e94795aSAndroid Build Coastguard Worker  "Rewind the given file-like object 2 bytes"
72*9e94795aSAndroid Build Coastguard Worker  rewind_bytes(f, 2)
73*9e94795aSAndroid Build Coastguard Worker
74*9e94795aSAndroid Build Coastguard Workerclass fake_file(object):
75*9e94795aSAndroid Build Coastguard Worker  """
76*9e94795aSAndroid Build Coastguard Worker  Interface for python file-like objects that we use to manipulate the image.
77*9e94795aSAndroid Build Coastguard Worker  Inheritors must have an idx member which indicates the file pointer, and a
78*9e94795aSAndroid Build Coastguard Worker  size member which indicates the total file size.
79*9e94795aSAndroid Build Coastguard Worker  """
80*9e94795aSAndroid Build Coastguard Worker
81*9e94795aSAndroid Build Coastguard Worker  def seek(self, amount, direction=0):
82*9e94795aSAndroid Build Coastguard Worker    "Implementation of seek from python's file-like object interface."
83*9e94795aSAndroid Build Coastguard Worker    if direction == os.SEEK_CUR:
84*9e94795aSAndroid Build Coastguard Worker      self.idx += amount
85*9e94795aSAndroid Build Coastguard Worker    elif direction == os.SEEK_END:
86*9e94795aSAndroid Build Coastguard Worker      self.idx = self.size - amount
87*9e94795aSAndroid Build Coastguard Worker    else:
88*9e94795aSAndroid Build Coastguard Worker      self.idx = amount
89*9e94795aSAndroid Build Coastguard Worker
90*9e94795aSAndroid Build Coastguard Worker    if self.idx < 0:
91*9e94795aSAndroid Build Coastguard Worker      self.idx = 0
92*9e94795aSAndroid Build Coastguard Worker    if self.idx > self.size:
93*9e94795aSAndroid Build Coastguard Worker      self.idx = self.size
94*9e94795aSAndroid Build Coastguard Worker
95*9e94795aSAndroid Build Coastguard Workerclass fat_file(fake_file):
96*9e94795aSAndroid Build Coastguard Worker  """
97*9e94795aSAndroid Build Coastguard Worker  A file inside of our fat image. The file may or may not have a dentry, and
98*9e94795aSAndroid Build Coastguard Worker  if it does this object knows nothing about it. All we see is a valid cluster
99*9e94795aSAndroid Build Coastguard Worker  chain.
100*9e94795aSAndroid Build Coastguard Worker  """
101*9e94795aSAndroid Build Coastguard Worker
102*9e94795aSAndroid Build Coastguard Worker  def __init__(self, fs, cluster, size=None):
103*9e94795aSAndroid Build Coastguard Worker    """
104*9e94795aSAndroid Build Coastguard Worker    fs: The fat() object for the image this file resides in.
105*9e94795aSAndroid Build Coastguard Worker    cluster: The first cluster of data for this file.
106*9e94795aSAndroid Build Coastguard Worker    size: The size of this file. If not given, we use the total length of the
107*9e94795aSAndroid Build Coastguard Worker          cluster chain that starts from the cluster argument.
108*9e94795aSAndroid Build Coastguard Worker    """
109*9e94795aSAndroid Build Coastguard Worker    self.fs = fs
110*9e94795aSAndroid Build Coastguard Worker    self.start_cluster = cluster
111*9e94795aSAndroid Build Coastguard Worker    self.size = size
112*9e94795aSAndroid Build Coastguard Worker
113*9e94795aSAndroid Build Coastguard Worker    if self.size is None:
114*9e94795aSAndroid Build Coastguard Worker      self.size = fs.get_chain_size(cluster)
115*9e94795aSAndroid Build Coastguard Worker
116*9e94795aSAndroid Build Coastguard Worker    self.idx = 0
117*9e94795aSAndroid Build Coastguard Worker
118*9e94795aSAndroid Build Coastguard Worker  def read(self, size):
119*9e94795aSAndroid Build Coastguard Worker    "Read method for pythonic file-like interface."
120*9e94795aSAndroid Build Coastguard Worker    if self.idx + size > self.size:
121*9e94795aSAndroid Build Coastguard Worker      size = self.size - self.idx
122*9e94795aSAndroid Build Coastguard Worker    got = self.fs.read_file(self.start_cluster, self.idx, size)
123*9e94795aSAndroid Build Coastguard Worker    self.idx += len(got)
124*9e94795aSAndroid Build Coastguard Worker    return got
125*9e94795aSAndroid Build Coastguard Worker
126*9e94795aSAndroid Build Coastguard Worker  def write(self, data):
127*9e94795aSAndroid Build Coastguard Worker    "Write method for pythonic file-like interface."
128*9e94795aSAndroid Build Coastguard Worker    self.fs.write_file(self.start_cluster, self.idx, data)
129*9e94795aSAndroid Build Coastguard Worker    self.idx += len(data)
130*9e94795aSAndroid Build Coastguard Worker
131*9e94795aSAndroid Build Coastguard Worker    if self.idx > self.size:
132*9e94795aSAndroid Build Coastguard Worker      self.size = self.idx
133*9e94795aSAndroid Build Coastguard Worker
134*9e94795aSAndroid Build Coastguard Workerdef shorten(name, index):
135*9e94795aSAndroid Build Coastguard Worker  """
136*9e94795aSAndroid Build Coastguard Worker  Create a file short name from the given long name (with the extension already
137*9e94795aSAndroid Build Coastguard Worker  removed). The index argument gives a disambiguating integer to work into the
138*9e94795aSAndroid Build Coastguard Worker  name to avoid collisions.
139*9e94795aSAndroid Build Coastguard Worker  """
140*9e94795aSAndroid Build Coastguard Worker  name = "".join(name.split('.')).upper()
141*9e94795aSAndroid Build Coastguard Worker  postfix = "~" + str(index)
142*9e94795aSAndroid Build Coastguard Worker  return name[:8 - len(postfix)] + postfix
143*9e94795aSAndroid Build Coastguard Worker
144*9e94795aSAndroid Build Coastguard Workerclass fat_dir(object):
145*9e94795aSAndroid Build Coastguard Worker  "A directory in our fat filesystem."
146*9e94795aSAndroid Build Coastguard Worker
147*9e94795aSAndroid Build Coastguard Worker  def __init__(self, backing):
148*9e94795aSAndroid Build Coastguard Worker    """
149*9e94795aSAndroid Build Coastguard Worker    backing: A file-like object from which we can read dentry info. Should have
150*9e94795aSAndroid Build Coastguard Worker    an fs member allowing us to get to the underlying image.
151*9e94795aSAndroid Build Coastguard Worker    """
152*9e94795aSAndroid Build Coastguard Worker    self.backing = backing
153*9e94795aSAndroid Build Coastguard Worker    self.dentries = []
154*9e94795aSAndroid Build Coastguard Worker    to_read = self.backing.size / 32
155*9e94795aSAndroid Build Coastguard Worker
156*9e94795aSAndroid Build Coastguard Worker    self.backing.seek(0)
157*9e94795aSAndroid Build Coastguard Worker
158*9e94795aSAndroid Build Coastguard Worker    while to_read > 0:
159*9e94795aSAndroid Build Coastguard Worker      (dent, consumed) = self.backing.fs.read_dentry(self.backing)
160*9e94795aSAndroid Build Coastguard Worker      to_read -= consumed
161*9e94795aSAndroid Build Coastguard Worker
162*9e94795aSAndroid Build Coastguard Worker      if dent:
163*9e94795aSAndroid Build Coastguard Worker        self.dentries.append(dent)
164*9e94795aSAndroid Build Coastguard Worker
165*9e94795aSAndroid Build Coastguard Worker  def __str__(self):
166*9e94795aSAndroid Build Coastguard Worker    return "\n".join([str(x) for x in self.dentries]) + "\n"
167*9e94795aSAndroid Build Coastguard Worker
168*9e94795aSAndroid Build Coastguard Worker  def add_dentry(self, attributes, shortname, ext, longname, first_cluster,
169*9e94795aSAndroid Build Coastguard Worker      size):
170*9e94795aSAndroid Build Coastguard Worker    """
171*9e94795aSAndroid Build Coastguard Worker    Add a new dentry to this directory.
172*9e94795aSAndroid Build Coastguard Worker    attributes: Attribute flags for this dentry. See the ATTRIBUTE_ constants
173*9e94795aSAndroid Build Coastguard Worker                above.
174*9e94795aSAndroid Build Coastguard Worker    shortname: Short name of this file. Up to 8 characters, no dots.
175*9e94795aSAndroid Build Coastguard Worker    ext: Extension for this file. Up to 3 characters, no dots.
176*9e94795aSAndroid Build Coastguard Worker    longname: The long name for this file, with extension. Largely unrestricted.
177*9e94795aSAndroid Build Coastguard Worker    first_cluster: The first cluster in the cluster chain holding the contents
178*9e94795aSAndroid Build Coastguard Worker                   of this file.
179*9e94795aSAndroid Build Coastguard Worker    size: The size of this file. Set to 0 for subdirectories.
180*9e94795aSAndroid Build Coastguard Worker    """
181*9e94795aSAndroid Build Coastguard Worker    new_dentry = dentry(self.backing.fs, attributes, shortname, ext,
182*9e94795aSAndroid Build Coastguard Worker        longname, first_cluster, size)
183*9e94795aSAndroid Build Coastguard Worker    new_dentry.commit(self.backing)
184*9e94795aSAndroid Build Coastguard Worker    self.dentries.append(new_dentry)
185*9e94795aSAndroid Build Coastguard Worker    return new_dentry
186*9e94795aSAndroid Build Coastguard Worker
187*9e94795aSAndroid Build Coastguard Worker  def make_short_name(self, name):
188*9e94795aSAndroid Build Coastguard Worker    """
189*9e94795aSAndroid Build Coastguard Worker    Given a long file name, return an 8.3 short name as a tuple. Name will be
190*9e94795aSAndroid Build Coastguard Worker    engineered not to collide with other such names in this folder.
191*9e94795aSAndroid Build Coastguard Worker    """
192*9e94795aSAndroid Build Coastguard Worker    parts = name.rsplit('.', 1)
193*9e94795aSAndroid Build Coastguard Worker
194*9e94795aSAndroid Build Coastguard Worker    if len(parts) == 1:
195*9e94795aSAndroid Build Coastguard Worker      parts.append('')
196*9e94795aSAndroid Build Coastguard Worker
197*9e94795aSAndroid Build Coastguard Worker    name = parts[0]
198*9e94795aSAndroid Build Coastguard Worker    ext = parts[1].upper()
199*9e94795aSAndroid Build Coastguard Worker
200*9e94795aSAndroid Build Coastguard Worker    index = 1
201*9e94795aSAndroid Build Coastguard Worker    shortened = shorten(name, index)
202*9e94795aSAndroid Build Coastguard Worker
203*9e94795aSAndroid Build Coastguard Worker    for dent in self.dentries:
204*9e94795aSAndroid Build Coastguard Worker      assert dent.longname != name, "File must not exist"
205*9e94795aSAndroid Build Coastguard Worker      if dent.shortname == shortened:
206*9e94795aSAndroid Build Coastguard Worker        index += 1
207*9e94795aSAndroid Build Coastguard Worker        shortened = shorten(name, index)
208*9e94795aSAndroid Build Coastguard Worker
209*9e94795aSAndroid Build Coastguard Worker    if len(name) <= 8 and len(ext) <= 3 and not '.' in name:
210*9e94795aSAndroid Build Coastguard Worker      return (name.upper().ljust(8), ext.ljust(3))
211*9e94795aSAndroid Build Coastguard Worker
212*9e94795aSAndroid Build Coastguard Worker    return (shortened.ljust(8), ext[:3].ljust(3))
213*9e94795aSAndroid Build Coastguard Worker
214*9e94795aSAndroid Build Coastguard Worker  def new_file(self, name, data=None):
215*9e94795aSAndroid Build Coastguard Worker    """
216*9e94795aSAndroid Build Coastguard Worker    Add a new regular file to this directory.
217*9e94795aSAndroid Build Coastguard Worker    name: The name of the new file.
218*9e94795aSAndroid Build Coastguard Worker    data: The contents of the new file. Given as a file-like object.
219*9e94795aSAndroid Build Coastguard Worker    """
220*9e94795aSAndroid Build Coastguard Worker    size = 0
221*9e94795aSAndroid Build Coastguard Worker    if data:
222*9e94795aSAndroid Build Coastguard Worker      data.seek(0, os.SEEK_END)
223*9e94795aSAndroid Build Coastguard Worker      size = data.tell()
224*9e94795aSAndroid Build Coastguard Worker
225*9e94795aSAndroid Build Coastguard Worker    # Empty files shouldn't have any clusters assigned.
226*9e94795aSAndroid Build Coastguard Worker    chunk = self.backing.fs.allocate(size) if size > 0 else 0
227*9e94795aSAndroid Build Coastguard Worker    (shortname, ext) = self.make_short_name(name)
228*9e94795aSAndroid Build Coastguard Worker    self.add_dentry(0, shortname, ext, name, chunk, size)
229*9e94795aSAndroid Build Coastguard Worker
230*9e94795aSAndroid Build Coastguard Worker    if data is None:
231*9e94795aSAndroid Build Coastguard Worker      return
232*9e94795aSAndroid Build Coastguard Worker
233*9e94795aSAndroid Build Coastguard Worker    data_file = fat_file(self.backing.fs, chunk, size)
234*9e94795aSAndroid Build Coastguard Worker    data.seek(0)
235*9e94795aSAndroid Build Coastguard Worker    data_file.write(data.read())
236*9e94795aSAndroid Build Coastguard Worker
237*9e94795aSAndroid Build Coastguard Worker  def open_subdirectory(self, name):
238*9e94795aSAndroid Build Coastguard Worker    """
239*9e94795aSAndroid Build Coastguard Worker    Open a subdirectory of this directory with the given name. If the
240*9e94795aSAndroid Build Coastguard Worker    subdirectory doesn't exist, a new one is created instead.
241*9e94795aSAndroid Build Coastguard Worker    Returns a fat_dir().
242*9e94795aSAndroid Build Coastguard Worker    """
243*9e94795aSAndroid Build Coastguard Worker    for dent in self.dentries:
244*9e94795aSAndroid Build Coastguard Worker      if dent.longname == name:
245*9e94795aSAndroid Build Coastguard Worker        return dent.open_directory()
246*9e94795aSAndroid Build Coastguard Worker
247*9e94795aSAndroid Build Coastguard Worker    chunk = self.backing.fs.allocate(1)
248*9e94795aSAndroid Build Coastguard Worker    (shortname, ext) = self.make_short_name(name)
249*9e94795aSAndroid Build Coastguard Worker    new_dentry = self.add_dentry(ATTRIBUTE_SUBDIRECTORY, shortname,
250*9e94795aSAndroid Build Coastguard Worker            ext, name, chunk, 0)
251*9e94795aSAndroid Build Coastguard Worker    result = new_dentry.open_directory()
252*9e94795aSAndroid Build Coastguard Worker
253*9e94795aSAndroid Build Coastguard Worker    parent_cluster = 0
254*9e94795aSAndroid Build Coastguard Worker
255*9e94795aSAndroid Build Coastguard Worker    if hasattr(self.backing, 'start_cluster'):
256*9e94795aSAndroid Build Coastguard Worker      parent_cluster = self.backing.start_cluster
257*9e94795aSAndroid Build Coastguard Worker
258*9e94795aSAndroid Build Coastguard Worker    result.add_dentry(ATTRIBUTE_SUBDIRECTORY, '.', '', '', chunk, 0)
259*9e94795aSAndroid Build Coastguard Worker    result.add_dentry(ATTRIBUTE_SUBDIRECTORY, '..', '', '', parent_cluster, 0)
260*9e94795aSAndroid Build Coastguard Worker
261*9e94795aSAndroid Build Coastguard Worker    return result
262*9e94795aSAndroid Build Coastguard Worker
263*9e94795aSAndroid Build Coastguard Workerdef lfn_checksum(name_data):
264*9e94795aSAndroid Build Coastguard Worker  """
265*9e94795aSAndroid Build Coastguard Worker  Given the characters of an 8.3 file name (concatenated *without* the dot),
266*9e94795aSAndroid Build Coastguard Worker  Compute a one-byte checksum which needs to appear in corresponding long file
267*9e94795aSAndroid Build Coastguard Worker  name entries.
268*9e94795aSAndroid Build Coastguard Worker  """
269*9e94795aSAndroid Build Coastguard Worker  assert len(name_data) == 11, "Name data should be exactly 11 characters"
270*9e94795aSAndroid Build Coastguard Worker  name_data = struct.unpack("B" * 11, name_data)
271*9e94795aSAndroid Build Coastguard Worker
272*9e94795aSAndroid Build Coastguard Worker  result = 0
273*9e94795aSAndroid Build Coastguard Worker
274*9e94795aSAndroid Build Coastguard Worker  for char in name_data:
275*9e94795aSAndroid Build Coastguard Worker    last_bit = (result & 1) << 7
276*9e94795aSAndroid Build Coastguard Worker    result = (result >> 1) | last_bit
277*9e94795aSAndroid Build Coastguard Worker    result += char
278*9e94795aSAndroid Build Coastguard Worker    result = result & 0xFF
279*9e94795aSAndroid Build Coastguard Worker
280*9e94795aSAndroid Build Coastguard Worker  return struct.pack("B", result)
281*9e94795aSAndroid Build Coastguard Worker
282*9e94795aSAndroid Build Coastguard Workerclass dentry(object):
283*9e94795aSAndroid Build Coastguard Worker  "A directory entry"
284*9e94795aSAndroid Build Coastguard Worker  def __init__(self, fs, attributes, shortname, ext, longname,
285*9e94795aSAndroid Build Coastguard Worker      first_cluster, size):
286*9e94795aSAndroid Build Coastguard Worker    """
287*9e94795aSAndroid Build Coastguard Worker    fs: The fat() object for the image we're stored in.
288*9e94795aSAndroid Build Coastguard Worker    attributes: The attribute flags for this dentry. See the ATTRIBUTE_ flags
289*9e94795aSAndroid Build Coastguard Worker                above.
290*9e94795aSAndroid Build Coastguard Worker    shortname: The short name stored in this dentry. Up to 8 characters, no
291*9e94795aSAndroid Build Coastguard Worker               dots.
292*9e94795aSAndroid Build Coastguard Worker    ext: The file extension stored in this dentry. Up to 3 characters, no
293*9e94795aSAndroid Build Coastguard Worker         dots.
294*9e94795aSAndroid Build Coastguard Worker    longname: The long file name stored in this dentry.
295*9e94795aSAndroid Build Coastguard Worker    first_cluster: The first cluster in the cluster chain backing the file
296*9e94795aSAndroid Build Coastguard Worker                   this dentry points to.
297*9e94795aSAndroid Build Coastguard Worker    size: Size of the file this dentry points to. 0 for subdirectories.
298*9e94795aSAndroid Build Coastguard Worker    """
299*9e94795aSAndroid Build Coastguard Worker    self.fs = fs
300*9e94795aSAndroid Build Coastguard Worker    self.attributes = attributes
301*9e94795aSAndroid Build Coastguard Worker    self.shortname = shortname
302*9e94795aSAndroid Build Coastguard Worker    self.ext = ext
303*9e94795aSAndroid Build Coastguard Worker    self.longname = longname
304*9e94795aSAndroid Build Coastguard Worker    self.first_cluster = first_cluster
305*9e94795aSAndroid Build Coastguard Worker    self.size = size
306*9e94795aSAndroid Build Coastguard Worker
307*9e94795aSAndroid Build Coastguard Worker  def name(self):
308*9e94795aSAndroid Build Coastguard Worker    "A friendly text file name for this dentry."
309*9e94795aSAndroid Build Coastguard Worker    if self.longname:
310*9e94795aSAndroid Build Coastguard Worker      return self.longname
311*9e94795aSAndroid Build Coastguard Worker
312*9e94795aSAndroid Build Coastguard Worker    if not self.ext or len(self.ext) == 0:
313*9e94795aSAndroid Build Coastguard Worker      return self.shortname
314*9e94795aSAndroid Build Coastguard Worker
315*9e94795aSAndroid Build Coastguard Worker    return self.shortname + "." + self.ext
316*9e94795aSAndroid Build Coastguard Worker
317*9e94795aSAndroid Build Coastguard Worker  def __str__(self):
318*9e94795aSAndroid Build Coastguard Worker    return self.name() + " (" + str(self.size) + \
319*9e94795aSAndroid Build Coastguard Worker      " bytes @ " + str(self.first_cluster) + ")"
320*9e94795aSAndroid Build Coastguard Worker
321*9e94795aSAndroid Build Coastguard Worker  def is_directory(self):
322*9e94795aSAndroid Build Coastguard Worker    "Return whether this dentry points to a directory."
323*9e94795aSAndroid Build Coastguard Worker    return (self.attributes & ATTRIBUTE_SUBDIRECTORY) != 0
324*9e94795aSAndroid Build Coastguard Worker
325*9e94795aSAndroid Build Coastguard Worker  def open_file(self):
326*9e94795aSAndroid Build Coastguard Worker    "Open the target of this dentry if it is a regular file."
327*9e94795aSAndroid Build Coastguard Worker    assert not self.is_directory(), "Cannot open directory as file"
328*9e94795aSAndroid Build Coastguard Worker    return fat_file(self.fs, self.first_cluster, self.size)
329*9e94795aSAndroid Build Coastguard Worker
330*9e94795aSAndroid Build Coastguard Worker  def open_directory(self):
331*9e94795aSAndroid Build Coastguard Worker    "Open the target of this dentry if it is a directory."
332*9e94795aSAndroid Build Coastguard Worker    assert self.is_directory(), "Cannot open file as directory"
333*9e94795aSAndroid Build Coastguard Worker    return fat_dir(fat_file(self.fs, self.first_cluster))
334*9e94795aSAndroid Build Coastguard Worker
335*9e94795aSAndroid Build Coastguard Worker  def longname_records(self, checksum):
336*9e94795aSAndroid Build Coastguard Worker    """
337*9e94795aSAndroid Build Coastguard Worker    Get the longname records necessary to store this dentry's long name,
338*9e94795aSAndroid Build Coastguard Worker    packed as a series of 32-byte strings.
339*9e94795aSAndroid Build Coastguard Worker    """
340*9e94795aSAndroid Build Coastguard Worker    if self.longname is None:
341*9e94795aSAndroid Build Coastguard Worker      return []
342*9e94795aSAndroid Build Coastguard Worker    if len(self.longname) == 0:
343*9e94795aSAndroid Build Coastguard Worker      return []
344*9e94795aSAndroid Build Coastguard Worker
345*9e94795aSAndroid Build Coastguard Worker    encoded_long_name = self.longname.encode('utf-16-le')
346*9e94795aSAndroid Build Coastguard Worker    long_name_padding = "\0" * (26 - (len(encoded_long_name) % 26))
347*9e94795aSAndroid Build Coastguard Worker    padded_long_name = encoded_long_name + long_name_padding
348*9e94795aSAndroid Build Coastguard Worker
349*9e94795aSAndroid Build Coastguard Worker    chunks = [padded_long_name[i:i+26] for i in range(0,
350*9e94795aSAndroid Build Coastguard Worker      len(padded_long_name), 26)]
351*9e94795aSAndroid Build Coastguard Worker    records = []
352*9e94795aSAndroid Build Coastguard Worker    sequence_number = 1
353*9e94795aSAndroid Build Coastguard Worker
354*9e94795aSAndroid Build Coastguard Worker    for c in chunks:
355*9e94795aSAndroid Build Coastguard Worker      sequence_byte = struct.pack("B", sequence_number)
356*9e94795aSAndroid Build Coastguard Worker      sequence_number += 1
357*9e94795aSAndroid Build Coastguard Worker      record = sequence_byte + c[:10] + LFN_ATTRIBUTES_BYTE + "\0" + \
358*9e94795aSAndroid Build Coastguard Worker          checksum + c[10:22] + "\0\0" + c[22:]
359*9e94795aSAndroid Build Coastguard Worker      records.append(record)
360*9e94795aSAndroid Build Coastguard Worker
361*9e94795aSAndroid Build Coastguard Worker    last = records.pop()
362*9e94795aSAndroid Build Coastguard Worker    last_seq = struct.unpack("B", last[0])[0]
363*9e94795aSAndroid Build Coastguard Worker    last_seq = last_seq | 0x40
364*9e94795aSAndroid Build Coastguard Worker    last = struct.pack("B", last_seq) + last[1:]
365*9e94795aSAndroid Build Coastguard Worker    records.append(last)
366*9e94795aSAndroid Build Coastguard Worker    records.reverse()
367*9e94795aSAndroid Build Coastguard Worker
368*9e94795aSAndroid Build Coastguard Worker    return records
369*9e94795aSAndroid Build Coastguard Worker
370*9e94795aSAndroid Build Coastguard Worker  def commit(self, f):
371*9e94795aSAndroid Build Coastguard Worker    """
372*9e94795aSAndroid Build Coastguard Worker    Write this dentry into the given file-like object,
373*9e94795aSAndroid Build Coastguard Worker    which is assumed to contain a FAT directory.
374*9e94795aSAndroid Build Coastguard Worker    """
375*9e94795aSAndroid Build Coastguard Worker    f.seek(0)
376*9e94795aSAndroid Build Coastguard Worker    padded_short_name = self.shortname.ljust(8)
377*9e94795aSAndroid Build Coastguard Worker    padded_ext = self.ext.ljust(3)
378*9e94795aSAndroid Build Coastguard Worker    name_data = padded_short_name + padded_ext
379*9e94795aSAndroid Build Coastguard Worker    longname_record_data = self.longname_records(lfn_checksum(name_data))
380*9e94795aSAndroid Build Coastguard Worker    record = struct.pack("<11sBBBHHHHHHHL",
381*9e94795aSAndroid Build Coastguard Worker        name_data,
382*9e94795aSAndroid Build Coastguard Worker        self.attributes,
383*9e94795aSAndroid Build Coastguard Worker        0,
384*9e94795aSAndroid Build Coastguard Worker        0,
385*9e94795aSAndroid Build Coastguard Worker        0,
386*9e94795aSAndroid Build Coastguard Worker        0,
387*9e94795aSAndroid Build Coastguard Worker        0,
388*9e94795aSAndroid Build Coastguard Worker        0,
389*9e94795aSAndroid Build Coastguard Worker        0,
390*9e94795aSAndroid Build Coastguard Worker        0,
391*9e94795aSAndroid Build Coastguard Worker        self.first_cluster,
392*9e94795aSAndroid Build Coastguard Worker        self.size)
393*9e94795aSAndroid Build Coastguard Worker    entry = "".join(longname_record_data + [record])
394*9e94795aSAndroid Build Coastguard Worker
395*9e94795aSAndroid Build Coastguard Worker    record_count = len(longname_record_data) + 1
396*9e94795aSAndroid Build Coastguard Worker
397*9e94795aSAndroid Build Coastguard Worker    found_count = 0
398*9e94795aSAndroid Build Coastguard Worker    while found_count < record_count:
399*9e94795aSAndroid Build Coastguard Worker      record = f.read(32)
400*9e94795aSAndroid Build Coastguard Worker
401*9e94795aSAndroid Build Coastguard Worker      if record is None or len(record) != 32:
402*9e94795aSAndroid Build Coastguard Worker        # We reached the EOF, so we need to extend the file with a new cluster.
403*9e94795aSAndroid Build Coastguard Worker        f.write("\0" * self.fs.bytes_per_cluster)
404*9e94795aSAndroid Build Coastguard Worker        f.seek(-self.fs.bytes_per_cluster, os.SEEK_CUR)
405*9e94795aSAndroid Build Coastguard Worker        record = f.read(32)
406*9e94795aSAndroid Build Coastguard Worker
407*9e94795aSAndroid Build Coastguard Worker      marker = struct.unpack("B", record[0])[0]
408*9e94795aSAndroid Build Coastguard Worker
409*9e94795aSAndroid Build Coastguard Worker      if marker == DEL_MARKER or marker == 0:
410*9e94795aSAndroid Build Coastguard Worker        found_count += 1
411*9e94795aSAndroid Build Coastguard Worker      else:
412*9e94795aSAndroid Build Coastguard Worker        found_count = 0
413*9e94795aSAndroid Build Coastguard Worker
414*9e94795aSAndroid Build Coastguard Worker    f.seek(-(record_count * 32), os.SEEK_CUR)
415*9e94795aSAndroid Build Coastguard Worker    f.write(entry)
416*9e94795aSAndroid Build Coastguard Worker
417*9e94795aSAndroid Build Coastguard Workerclass root_dentry_file(fake_file):
418*9e94795aSAndroid Build Coastguard Worker  """
419*9e94795aSAndroid Build Coastguard Worker  File-like object for the root directory. The root directory isn't stored in a
420*9e94795aSAndroid Build Coastguard Worker  normal file, so we can't use a normal fat_file object to create a view of it.
421*9e94795aSAndroid Build Coastguard Worker  """
422*9e94795aSAndroid Build Coastguard Worker  def __init__(self, fs):
423*9e94795aSAndroid Build Coastguard Worker    self.fs = fs
424*9e94795aSAndroid Build Coastguard Worker    self.idx = 0
425*9e94795aSAndroid Build Coastguard Worker    self.size = fs.root_entries * 32
426*9e94795aSAndroid Build Coastguard Worker
427*9e94795aSAndroid Build Coastguard Worker  def read(self, count):
428*9e94795aSAndroid Build Coastguard Worker    f = self.fs.f
429*9e94795aSAndroid Build Coastguard Worker    f.seek(self.fs.data_start() + self.idx)
430*9e94795aSAndroid Build Coastguard Worker
431*9e94795aSAndroid Build Coastguard Worker    if self.idx + count > self.size:
432*9e94795aSAndroid Build Coastguard Worker      count = self.size - self.idx
433*9e94795aSAndroid Build Coastguard Worker
434*9e94795aSAndroid Build Coastguard Worker    ret = f.read(count)
435*9e94795aSAndroid Build Coastguard Worker    self.idx += len(ret)
436*9e94795aSAndroid Build Coastguard Worker    return ret
437*9e94795aSAndroid Build Coastguard Worker
438*9e94795aSAndroid Build Coastguard Worker  def write(self, data):
439*9e94795aSAndroid Build Coastguard Worker    f = self.fs.f
440*9e94795aSAndroid Build Coastguard Worker    f.seek(self.fs.data_start() + self.idx)
441*9e94795aSAndroid Build Coastguard Worker
442*9e94795aSAndroid Build Coastguard Worker    if self.idx + len(data) > self.size:
443*9e94795aSAndroid Build Coastguard Worker      data = data[:self.size - self.idx]
444*9e94795aSAndroid Build Coastguard Worker
445*9e94795aSAndroid Build Coastguard Worker    f.write(data)
446*9e94795aSAndroid Build Coastguard Worker    self.idx += len(data)
447*9e94795aSAndroid Build Coastguard Worker    if self.idx > self.size:
448*9e94795aSAndroid Build Coastguard Worker      self.size = self.idx
449*9e94795aSAndroid Build Coastguard Worker
450*9e94795aSAndroid Build Coastguard Workerclass fat(object):
451*9e94795aSAndroid Build Coastguard Worker  "A FAT image"
452*9e94795aSAndroid Build Coastguard Worker
453*9e94795aSAndroid Build Coastguard Worker  def __init__(self, path):
454*9e94795aSAndroid Build Coastguard Worker    """
455*9e94795aSAndroid Build Coastguard Worker    path: Path to an image file containing a FAT file system.
456*9e94795aSAndroid Build Coastguard Worker    """
457*9e94795aSAndroid Build Coastguard Worker    f = open(path, "r+b")
458*9e94795aSAndroid Build Coastguard Worker
459*9e94795aSAndroid Build Coastguard Worker    self.f = f
460*9e94795aSAndroid Build Coastguard Worker
461*9e94795aSAndroid Build Coastguard Worker    f.seek(0xb)
462*9e94795aSAndroid Build Coastguard Worker    bytes_per_sector = read_le_short(f)
463*9e94795aSAndroid Build Coastguard Worker    sectors_per_cluster = read_byte(f)
464*9e94795aSAndroid Build Coastguard Worker
465*9e94795aSAndroid Build Coastguard Worker    self.bytes_per_cluster = bytes_per_sector * sectors_per_cluster
466*9e94795aSAndroid Build Coastguard Worker
467*9e94795aSAndroid Build Coastguard Worker    reserved_sectors = read_le_short(f)
468*9e94795aSAndroid Build Coastguard Worker    assert reserved_sectors == 1, \
469*9e94795aSAndroid Build Coastguard Worker        "Can only handle FAT with 1 reserved sector"
470*9e94795aSAndroid Build Coastguard Worker
471*9e94795aSAndroid Build Coastguard Worker    fat_count = read_byte(f)
472*9e94795aSAndroid Build Coastguard Worker    assert fat_count == 2, "Can only handle FAT with 2 tables"
473*9e94795aSAndroid Build Coastguard Worker
474*9e94795aSAndroid Build Coastguard Worker    self.root_entries = read_le_short(f)
475*9e94795aSAndroid Build Coastguard Worker
476*9e94795aSAndroid Build Coastguard Worker    skip_short(f) # Image size. Sort of. Useless field.
477*9e94795aSAndroid Build Coastguard Worker    skip_byte(f) # Media type. We don't care.
478*9e94795aSAndroid Build Coastguard Worker
479*9e94795aSAndroid Build Coastguard Worker    self.fat_size = read_le_short(f) * bytes_per_sector
480*9e94795aSAndroid Build Coastguard Worker    self.root = fat_dir(root_dentry_file(self))
481*9e94795aSAndroid Build Coastguard Worker
482*9e94795aSAndroid Build Coastguard Worker  def data_start(self):
483*9e94795aSAndroid Build Coastguard Worker    """
484*9e94795aSAndroid Build Coastguard Worker    Index of the first byte after the FAT tables.
485*9e94795aSAndroid Build Coastguard Worker    """
486*9e94795aSAndroid Build Coastguard Worker    return FAT_TABLE_START + self.fat_size * 2
487*9e94795aSAndroid Build Coastguard Worker
488*9e94795aSAndroid Build Coastguard Worker  def get_chain_size(self, head_cluster):
489*9e94795aSAndroid Build Coastguard Worker    """
490*9e94795aSAndroid Build Coastguard Worker    Return how many total bytes are in the cluster chain rooted at the given
491*9e94795aSAndroid Build Coastguard Worker    cluster.
492*9e94795aSAndroid Build Coastguard Worker    """
493*9e94795aSAndroid Build Coastguard Worker    if head_cluster == 0:
494*9e94795aSAndroid Build Coastguard Worker      return 0
495*9e94795aSAndroid Build Coastguard Worker
496*9e94795aSAndroid Build Coastguard Worker    f = self.f
497*9e94795aSAndroid Build Coastguard Worker    f.seek(FAT_TABLE_START + head_cluster * 2)
498*9e94795aSAndroid Build Coastguard Worker
499*9e94795aSAndroid Build Coastguard Worker    cluster_count = 0
500*9e94795aSAndroid Build Coastguard Worker
501*9e94795aSAndroid Build Coastguard Worker    while head_cluster <= MAX_CLUSTER_ID:
502*9e94795aSAndroid Build Coastguard Worker      cluster_count += 1
503*9e94795aSAndroid Build Coastguard Worker      head_cluster = read_le_short(f)
504*9e94795aSAndroid Build Coastguard Worker      f.seek(FAT_TABLE_START + head_cluster * 2)
505*9e94795aSAndroid Build Coastguard Worker
506*9e94795aSAndroid Build Coastguard Worker    return cluster_count * self.bytes_per_cluster
507*9e94795aSAndroid Build Coastguard Worker
508*9e94795aSAndroid Build Coastguard Worker  def read_dentry(self, f=None):
509*9e94795aSAndroid Build Coastguard Worker    """
510*9e94795aSAndroid Build Coastguard Worker    Read and decode a dentry from the given file-like object at its current
511*9e94795aSAndroid Build Coastguard Worker    seek position.
512*9e94795aSAndroid Build Coastguard Worker    """
513*9e94795aSAndroid Build Coastguard Worker    f = f or self.f
514*9e94795aSAndroid Build Coastguard Worker    attributes = None
515*9e94795aSAndroid Build Coastguard Worker
516*9e94795aSAndroid Build Coastguard Worker    consumed = 1
517*9e94795aSAndroid Build Coastguard Worker
518*9e94795aSAndroid Build Coastguard Worker    lfn_entries = {}
519*9e94795aSAndroid Build Coastguard Worker
520*9e94795aSAndroid Build Coastguard Worker    while True:
521*9e94795aSAndroid Build Coastguard Worker      skip_bytes(f, 11)
522*9e94795aSAndroid Build Coastguard Worker      attributes = read_byte(f)
523*9e94795aSAndroid Build Coastguard Worker      rewind_bytes(f, 12)
524*9e94795aSAndroid Build Coastguard Worker
525*9e94795aSAndroid Build Coastguard Worker      if attributes & LFN_ATTRIBUTES != LFN_ATTRIBUTES:
526*9e94795aSAndroid Build Coastguard Worker        break
527*9e94795aSAndroid Build Coastguard Worker
528*9e94795aSAndroid Build Coastguard Worker      consumed += 1
529*9e94795aSAndroid Build Coastguard Worker
530*9e94795aSAndroid Build Coastguard Worker      seq = read_byte(f)
531*9e94795aSAndroid Build Coastguard Worker      chars = f.read(10)
532*9e94795aSAndroid Build Coastguard Worker      skip_bytes(f, 3) # Various hackish nonsense
533*9e94795aSAndroid Build Coastguard Worker      chars += f.read(12)
534*9e94795aSAndroid Build Coastguard Worker      skip_short(f) # Lots more nonsense
535*9e94795aSAndroid Build Coastguard Worker      chars += f.read(4)
536*9e94795aSAndroid Build Coastguard Worker
537*9e94795aSAndroid Build Coastguard Worker      chars = unicode(chars, "utf-16-le").encode("utf-8")
538*9e94795aSAndroid Build Coastguard Worker
539*9e94795aSAndroid Build Coastguard Worker      lfn_entries[seq] = chars
540*9e94795aSAndroid Build Coastguard Worker
541*9e94795aSAndroid Build Coastguard Worker    ind = read_byte(f)
542*9e94795aSAndroid Build Coastguard Worker
543*9e94795aSAndroid Build Coastguard Worker    if ind == 0 or ind == DEL_MARKER:
544*9e94795aSAndroid Build Coastguard Worker      skip_bytes(f, 31)
545*9e94795aSAndroid Build Coastguard Worker      return (None, consumed)
546*9e94795aSAndroid Build Coastguard Worker
547*9e94795aSAndroid Build Coastguard Worker    if ind == ESCAPE_DEL_MARKER:
548*9e94795aSAndroid Build Coastguard Worker      ind = DEL_MARKER
549*9e94795aSAndroid Build Coastguard Worker
550*9e94795aSAndroid Build Coastguard Worker    ind = str(unichr(ind))
551*9e94795aSAndroid Build Coastguard Worker
552*9e94795aSAndroid Build Coastguard Worker    if ind == '.':
553*9e94795aSAndroid Build Coastguard Worker      skip_bytes(f, 31)
554*9e94795aSAndroid Build Coastguard Worker      return (None, consumed)
555*9e94795aSAndroid Build Coastguard Worker
556*9e94795aSAndroid Build Coastguard Worker    shortname = ind + f.read(7).rstrip()
557*9e94795aSAndroid Build Coastguard Worker    ext = f.read(3).rstrip()
558*9e94795aSAndroid Build Coastguard Worker    skip_bytes(f, 15) # Assorted flags, ctime/atime/mtime, etc.
559*9e94795aSAndroid Build Coastguard Worker    first_cluster = read_le_short(f)
560*9e94795aSAndroid Build Coastguard Worker    size = read_le_long(f)
561*9e94795aSAndroid Build Coastguard Worker
562*9e94795aSAndroid Build Coastguard Worker    lfn = lfn_entries.items()
563*9e94795aSAndroid Build Coastguard Worker    lfn.sort(key=lambda x: x[0])
564*9e94795aSAndroid Build Coastguard Worker    lfn = reduce(lambda x, y: x + y[1], lfn, "")
565*9e94795aSAndroid Build Coastguard Worker
566*9e94795aSAndroid Build Coastguard Worker    if len(lfn) == 0:
567*9e94795aSAndroid Build Coastguard Worker      lfn = None
568*9e94795aSAndroid Build Coastguard Worker    else:
569*9e94795aSAndroid Build Coastguard Worker      lfn = lfn.split('\0', 1)[0]
570*9e94795aSAndroid Build Coastguard Worker
571*9e94795aSAndroid Build Coastguard Worker    return (dentry(self, attributes, shortname, ext, lfn, first_cluster,
572*9e94795aSAndroid Build Coastguard Worker      size), consumed)
573*9e94795aSAndroid Build Coastguard Worker
574*9e94795aSAndroid Build Coastguard Worker  def read_file(self, head_cluster, start_byte, size):
575*9e94795aSAndroid Build Coastguard Worker    """
576*9e94795aSAndroid Build Coastguard Worker    Read from a given FAT file.
577*9e94795aSAndroid Build Coastguard Worker    head_cluster: The first cluster in the file.
578*9e94795aSAndroid Build Coastguard Worker    start_byte: How many bytes in to the file to begin the read.
579*9e94795aSAndroid Build Coastguard Worker    size: How many bytes to read.
580*9e94795aSAndroid Build Coastguard Worker    """
581*9e94795aSAndroid Build Coastguard Worker    f = self.f
582*9e94795aSAndroid Build Coastguard Worker
583*9e94795aSAndroid Build Coastguard Worker    assert size >= 0, "Can't read a negative amount"
584*9e94795aSAndroid Build Coastguard Worker    if size == 0:
585*9e94795aSAndroid Build Coastguard Worker      return ""
586*9e94795aSAndroid Build Coastguard Worker
587*9e94795aSAndroid Build Coastguard Worker    got_data = ""
588*9e94795aSAndroid Build Coastguard Worker
589*9e94795aSAndroid Build Coastguard Worker    while True:
590*9e94795aSAndroid Build Coastguard Worker      size_now = size
591*9e94795aSAndroid Build Coastguard Worker      if start_byte + size > self.bytes_per_cluster:
592*9e94795aSAndroid Build Coastguard Worker        size_now = self.bytes_per_cluster - start_byte
593*9e94795aSAndroid Build Coastguard Worker
594*9e94795aSAndroid Build Coastguard Worker      if start_byte < self.bytes_per_cluster:
595*9e94795aSAndroid Build Coastguard Worker        size -= size_now
596*9e94795aSAndroid Build Coastguard Worker
597*9e94795aSAndroid Build Coastguard Worker        cluster_bytes_from_root = (head_cluster - 2) * \
598*9e94795aSAndroid Build Coastguard Worker            self.bytes_per_cluster
599*9e94795aSAndroid Build Coastguard Worker        bytes_from_root = cluster_bytes_from_root + start_byte
600*9e94795aSAndroid Build Coastguard Worker        bytes_from_data_start = bytes_from_root + self.root_entries * 32
601*9e94795aSAndroid Build Coastguard Worker
602*9e94795aSAndroid Build Coastguard Worker        f.seek(self.data_start() + bytes_from_data_start)
603*9e94795aSAndroid Build Coastguard Worker        line = f.read(size_now)
604*9e94795aSAndroid Build Coastguard Worker        got_data += line
605*9e94795aSAndroid Build Coastguard Worker
606*9e94795aSAndroid Build Coastguard Worker        if size == 0:
607*9e94795aSAndroid Build Coastguard Worker          return got_data
608*9e94795aSAndroid Build Coastguard Worker
609*9e94795aSAndroid Build Coastguard Worker      start_byte -= self.bytes_per_cluster
610*9e94795aSAndroid Build Coastguard Worker
611*9e94795aSAndroid Build Coastguard Worker      if start_byte < 0:
612*9e94795aSAndroid Build Coastguard Worker        start_byte = 0
613*9e94795aSAndroid Build Coastguard Worker
614*9e94795aSAndroid Build Coastguard Worker      f.seek(FAT_TABLE_START + head_cluster * 2)
615*9e94795aSAndroid Build Coastguard Worker      assert head_cluster <= MAX_CLUSTER_ID, "Out-of-bounds read"
616*9e94795aSAndroid Build Coastguard Worker      head_cluster = read_le_short(f)
617*9e94795aSAndroid Build Coastguard Worker      assert head_cluster > 0, "Read free cluster"
618*9e94795aSAndroid Build Coastguard Worker
619*9e94795aSAndroid Build Coastguard Worker    return got_data
620*9e94795aSAndroid Build Coastguard Worker
621*9e94795aSAndroid Build Coastguard Worker  def write_cluster_entry(self, entry):
622*9e94795aSAndroid Build Coastguard Worker    """
623*9e94795aSAndroid Build Coastguard Worker    Write a cluster entry to the FAT table. Assumes our backing file is already
624*9e94795aSAndroid Build Coastguard Worker    seeked to the correct entry in the first FAT table.
625*9e94795aSAndroid Build Coastguard Worker    """
626*9e94795aSAndroid Build Coastguard Worker    f = self.f
627*9e94795aSAndroid Build Coastguard Worker    f.write(struct.pack("<H", entry))
628*9e94795aSAndroid Build Coastguard Worker    skip_bytes(f, self.fat_size - 2)
629*9e94795aSAndroid Build Coastguard Worker    f.write(struct.pack("<H", entry))
630*9e94795aSAndroid Build Coastguard Worker    rewind_bytes(f, self.fat_size)
631*9e94795aSAndroid Build Coastguard Worker
632*9e94795aSAndroid Build Coastguard Worker  def allocate(self, amount):
633*9e94795aSAndroid Build Coastguard Worker    """
634*9e94795aSAndroid Build Coastguard Worker    Allocate a new cluster chain big enough to hold at least the given amount
635*9e94795aSAndroid Build Coastguard Worker    of bytes.
636*9e94795aSAndroid Build Coastguard Worker    """
637*9e94795aSAndroid Build Coastguard Worker    assert amount > 0, "Must allocate a non-zero amount."
638*9e94795aSAndroid Build Coastguard Worker
639*9e94795aSAndroid Build Coastguard Worker    f = self.f
640*9e94795aSAndroid Build Coastguard Worker    f.seek(FAT_TABLE_START + 4)
641*9e94795aSAndroid Build Coastguard Worker
642*9e94795aSAndroid Build Coastguard Worker    current = None
643*9e94795aSAndroid Build Coastguard Worker    current_size = 0
644*9e94795aSAndroid Build Coastguard Worker    free_zones = {}
645*9e94795aSAndroid Build Coastguard Worker
646*9e94795aSAndroid Build Coastguard Worker    pos = 2
647*9e94795aSAndroid Build Coastguard Worker    while pos < self.fat_size / 2:
648*9e94795aSAndroid Build Coastguard Worker      data = read_le_short(f)
649*9e94795aSAndroid Build Coastguard Worker
650*9e94795aSAndroid Build Coastguard Worker      if data == 0 and current is not None:
651*9e94795aSAndroid Build Coastguard Worker        current_size += 1
652*9e94795aSAndroid Build Coastguard Worker      elif data == 0:
653*9e94795aSAndroid Build Coastguard Worker        current = pos
654*9e94795aSAndroid Build Coastguard Worker        current_size = 1
655*9e94795aSAndroid Build Coastguard Worker      elif current is not None:
656*9e94795aSAndroid Build Coastguard Worker        free_zones[current] = current_size
657*9e94795aSAndroid Build Coastguard Worker        current = None
658*9e94795aSAndroid Build Coastguard Worker
659*9e94795aSAndroid Build Coastguard Worker      pos += 1
660*9e94795aSAndroid Build Coastguard Worker
661*9e94795aSAndroid Build Coastguard Worker    if current is not None:
662*9e94795aSAndroid Build Coastguard Worker      free_zones[current] = current_size
663*9e94795aSAndroid Build Coastguard Worker
664*9e94795aSAndroid Build Coastguard Worker    free_zones = free_zones.items()
665*9e94795aSAndroid Build Coastguard Worker    free_zones.sort(key=lambda x: x[1])
666*9e94795aSAndroid Build Coastguard Worker
667*9e94795aSAndroid Build Coastguard Worker    grabbed_zones = []
668*9e94795aSAndroid Build Coastguard Worker    grabbed = 0
669*9e94795aSAndroid Build Coastguard Worker
670*9e94795aSAndroid Build Coastguard Worker    while grabbed < amount and len(free_zones) > 0:
671*9e94795aSAndroid Build Coastguard Worker      zone = free_zones.pop()
672*9e94795aSAndroid Build Coastguard Worker      grabbed += zone[1] * self.bytes_per_cluster
673*9e94795aSAndroid Build Coastguard Worker      grabbed_zones.append(zone)
674*9e94795aSAndroid Build Coastguard Worker
675*9e94795aSAndroid Build Coastguard Worker    if grabbed < amount:
676*9e94795aSAndroid Build Coastguard Worker      return None
677*9e94795aSAndroid Build Coastguard Worker
678*9e94795aSAndroid Build Coastguard Worker    excess = (grabbed - amount) / self.bytes_per_cluster
679*9e94795aSAndroid Build Coastguard Worker
680*9e94795aSAndroid Build Coastguard Worker    grabbed_zones[-1] = (grabbed_zones[-1][0],
681*9e94795aSAndroid Build Coastguard Worker        grabbed_zones[-1][1] - excess)
682*9e94795aSAndroid Build Coastguard Worker
683*9e94795aSAndroid Build Coastguard Worker    out = None
684*9e94795aSAndroid Build Coastguard Worker    grabbed_zones.reverse()
685*9e94795aSAndroid Build Coastguard Worker
686*9e94795aSAndroid Build Coastguard Worker    for cluster, size in grabbed_zones:
687*9e94795aSAndroid Build Coastguard Worker      entries = range(cluster + 1, cluster + size)
688*9e94795aSAndroid Build Coastguard Worker      entries.append(out or 0xFFFF)
689*9e94795aSAndroid Build Coastguard Worker      out = cluster
690*9e94795aSAndroid Build Coastguard Worker      f.seek(FAT_TABLE_START + cluster * 2)
691*9e94795aSAndroid Build Coastguard Worker      for entry in entries:
692*9e94795aSAndroid Build Coastguard Worker        self.write_cluster_entry(entry)
693*9e94795aSAndroid Build Coastguard Worker
694*9e94795aSAndroid Build Coastguard Worker    return out
695*9e94795aSAndroid Build Coastguard Worker
696*9e94795aSAndroid Build Coastguard Worker  def extend_cluster(self, cluster, amount):
697*9e94795aSAndroid Build Coastguard Worker    """
698*9e94795aSAndroid Build Coastguard Worker    Given a cluster which is the *last* cluster in a chain, extend it to hold
699*9e94795aSAndroid Build Coastguard Worker    at least `amount` more bytes.
700*9e94795aSAndroid Build Coastguard Worker    """
701*9e94795aSAndroid Build Coastguard Worker    if amount == 0:
702*9e94795aSAndroid Build Coastguard Worker      return
703*9e94795aSAndroid Build Coastguard Worker    f = self.f
704*9e94795aSAndroid Build Coastguard Worker    entry_offset = FAT_TABLE_START + cluster * 2
705*9e94795aSAndroid Build Coastguard Worker    f.seek(entry_offset)
706*9e94795aSAndroid Build Coastguard Worker    assert read_le_short(f) == 0xFFFF, "Extending from middle of chain"
707*9e94795aSAndroid Build Coastguard Worker
708*9e94795aSAndroid Build Coastguard Worker    return_cluster = self.allocate(amount)
709*9e94795aSAndroid Build Coastguard Worker    f.seek(entry_offset)
710*9e94795aSAndroid Build Coastguard Worker    self.write_cluster_entry(return_cluster)
711*9e94795aSAndroid Build Coastguard Worker    return return_cluster
712*9e94795aSAndroid Build Coastguard Worker
713*9e94795aSAndroid Build Coastguard Worker  def write_file(self, head_cluster, start_byte, data):
714*9e94795aSAndroid Build Coastguard Worker    """
715*9e94795aSAndroid Build Coastguard Worker    Write to a given FAT file.
716*9e94795aSAndroid Build Coastguard Worker
717*9e94795aSAndroid Build Coastguard Worker    head_cluster: The first cluster in the file.
718*9e94795aSAndroid Build Coastguard Worker    start_byte: How many bytes in to the file to begin the write.
719*9e94795aSAndroid Build Coastguard Worker    data: The data to write.
720*9e94795aSAndroid Build Coastguard Worker    """
721*9e94795aSAndroid Build Coastguard Worker    f = self.f
722*9e94795aSAndroid Build Coastguard Worker    last_offset = start_byte + len(data)
723*9e94795aSAndroid Build Coastguard Worker    current_offset = 0
724*9e94795aSAndroid Build Coastguard Worker    current_cluster = head_cluster
725*9e94795aSAndroid Build Coastguard Worker
726*9e94795aSAndroid Build Coastguard Worker    while current_offset < last_offset:
727*9e94795aSAndroid Build Coastguard Worker      # Write everything that falls in the cluster starting at current_offset.
728*9e94795aSAndroid Build Coastguard Worker      data_begin = max(0, current_offset - start_byte)
729*9e94795aSAndroid Build Coastguard Worker      data_end = min(len(data),
730*9e94795aSAndroid Build Coastguard Worker                     current_offset + self.bytes_per_cluster - start_byte)
731*9e94795aSAndroid Build Coastguard Worker      if data_end > data_begin:
732*9e94795aSAndroid Build Coastguard Worker        cluster_file_offset = (self.data_start() + self.root_entries * 32 +
733*9e94795aSAndroid Build Coastguard Worker                               (current_cluster - 2) * self.bytes_per_cluster)
734*9e94795aSAndroid Build Coastguard Worker        f.seek(cluster_file_offset + max(0, start_byte - current_offset))
735*9e94795aSAndroid Build Coastguard Worker        f.write(data[data_begin:data_end])
736*9e94795aSAndroid Build Coastguard Worker
737*9e94795aSAndroid Build Coastguard Worker      # Advance to the next cluster in the chain or get a new cluster if needed.
738*9e94795aSAndroid Build Coastguard Worker      current_offset += self.bytes_per_cluster
739*9e94795aSAndroid Build Coastguard Worker      if last_offset > current_offset:
740*9e94795aSAndroid Build Coastguard Worker        f.seek(FAT_TABLE_START + current_cluster * 2)
741*9e94795aSAndroid Build Coastguard Worker        next_cluster = read_le_short(f)
742*9e94795aSAndroid Build Coastguard Worker        if next_cluster > MAX_CLUSTER_ID:
743*9e94795aSAndroid Build Coastguard Worker          next_cluster = self.extend_cluster(current_cluster, len(data))
744*9e94795aSAndroid Build Coastguard Worker        current_cluster = next_cluster
745*9e94795aSAndroid Build Coastguard Worker        assert current_cluster > 0, "Cannot write free cluster"
746*9e94795aSAndroid Build Coastguard Worker
747*9e94795aSAndroid Build Coastguard Worker
748*9e94795aSAndroid Build Coastguard Workerdef add_item(directory, item):
749*9e94795aSAndroid Build Coastguard Worker  """
750*9e94795aSAndroid Build Coastguard Worker  Copy a file into the given FAT directory. If the path given is a directory,
751*9e94795aSAndroid Build Coastguard Worker  copy recursively.
752*9e94795aSAndroid Build Coastguard Worker  directory: fat_dir to copy the file in to
753*9e94795aSAndroid Build Coastguard Worker  item: Path of local file to copy
754*9e94795aSAndroid Build Coastguard Worker  """
755*9e94795aSAndroid Build Coastguard Worker  if os.path.isdir(item):
756*9e94795aSAndroid Build Coastguard Worker    base = os.path.basename(item)
757*9e94795aSAndroid Build Coastguard Worker    if len(base) == 0:
758*9e94795aSAndroid Build Coastguard Worker      base = os.path.basename(item[:-1])
759*9e94795aSAndroid Build Coastguard Worker    sub = directory.open_subdirectory(base)
760*9e94795aSAndroid Build Coastguard Worker    for next_item in sorted(os.listdir(item)):
761*9e94795aSAndroid Build Coastguard Worker      add_item(sub, os.path.join(item, next_item))
762*9e94795aSAndroid Build Coastguard Worker  else:
763*9e94795aSAndroid Build Coastguard Worker    with open(item, 'rb') as f:
764*9e94795aSAndroid Build Coastguard Worker      directory.new_file(os.path.basename(item), f)
765*9e94795aSAndroid Build Coastguard Worker
766*9e94795aSAndroid Build Coastguard Workerif __name__ == "__main__":
767*9e94795aSAndroid Build Coastguard Worker  if len(sys.argv) < 3:
768*9e94795aSAndroid Build Coastguard Worker    print("Usage: fat16copy.py <image> <file> [<file> ...]")
769*9e94795aSAndroid Build Coastguard Worker    print("Files are copied into the root of the image.")
770*9e94795aSAndroid Build Coastguard Worker    print("Directories are copied recursively")
771*9e94795aSAndroid Build Coastguard Worker    sys.exit(1)
772*9e94795aSAndroid Build Coastguard Worker
773*9e94795aSAndroid Build Coastguard Worker  root = fat(sys.argv[1]).root
774*9e94795aSAndroid Build Coastguard Worker
775*9e94795aSAndroid Build Coastguard Worker  for p in sys.argv[2:]:
776*9e94795aSAndroid Build Coastguard Worker    add_item(root, p)
777