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