1 // Copyright (c) 2008 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/data_pack.h" 6 7 #include <errno.h> 8 9 #include "base/file_util.h" 10 #include "base/logging.h" 11 #include "base/string_piece.h" 12 13 // For details of the file layout, see 14 // http://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedstrings 15 16 namespace { 17 18 // A word is four bytes. 19 static const size_t kWord = 4; 20 21 static const uint32 kFileFormatVersion = 1; 22 // Length of file header: version and entry count. 23 static const size_t kHeaderLength = 2 * sizeof(uint32); 24 25 #pragma pack(push,1) 26 struct DataPackEntry { 27 uint32 resource_id; 28 uint32 file_offset; 29 uint32 length; 30 31 static int CompareById(const void* void_key, const void* void_entry) { 32 uint32 key = *reinterpret_cast<const uint32*>(void_key); 33 const DataPackEntry* entry = 34 reinterpret_cast<const DataPackEntry*>(void_entry); 35 if (key < entry->resource_id) { 36 return -1; 37 } else if (key > entry->resource_id) { 38 return 1; 39 } else { 40 return 0; 41 } 42 } 43 }; 44 #pragma pack(pop) 45 46 COMPILE_ASSERT(sizeof(DataPackEntry) == 12, size_of_header_must_be_twelve); 47 48 } // anonymous namespace 49 50 namespace base { 51 52 // In .cc for MemoryMappedFile dtor. 53 DataPack::DataPack() : resource_count_(0) { 54 } 55 DataPack::~DataPack() { 56 } 57 58 bool DataPack::Load(const FilePath& path) { 59 mmap_.reset(new file_util::MemoryMappedFile); 60 if (!mmap_->Initialize(path)) { 61 DLOG(ERROR) << "Failed to mmap datapack"; 62 return false; 63 } 64 65 // Parse the header of the file. 66 // First uint32: version; second: resource count. 67 const uint32* ptr = reinterpret_cast<const uint32*>(mmap_->data()); 68 uint32 version = ptr[0]; 69 if (version != kFileFormatVersion) { 70 LOG(ERROR) << "Bad data pack version: got " << version << ", expected " 71 << kFileFormatVersion; 72 mmap_.reset(); 73 return false; 74 } 75 resource_count_ = ptr[1]; 76 77 // Sanity check the file. 78 // 1) Check we have enough entries. 79 if (kHeaderLength + resource_count_ * sizeof(DataPackEntry) > 80 mmap_->length()) { 81 LOG(ERROR) << "Data pack file corruption: too short for number of " 82 "entries specified."; 83 mmap_.reset(); 84 return false; 85 } 86 // 2) Verify the entries are within the appropriate bounds. 87 for (size_t i = 0; i < resource_count_; ++i) { 88 const DataPackEntry* entry = reinterpret_cast<const DataPackEntry*>( 89 mmap_->data() + kHeaderLength + (i * sizeof(DataPackEntry))); 90 if (entry->file_offset + entry->length > mmap_->length()) { 91 LOG(ERROR) << "Entry #" << i << " in data pack points off end of file. " 92 << "Was the file corrupted?"; 93 mmap_.reset(); 94 return false; 95 } 96 } 97 98 return true; 99 } 100 101 bool DataPack::GetStringPiece(uint32 resource_id, StringPiece* data) { 102 // It won't be hard to make this endian-agnostic, but it's not worth 103 // bothering to do right now. 104 #if defined(__BYTE_ORDER) 105 // Linux check 106 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, 107 datapack_assumes_little_endian); 108 #elif defined(__BIG_ENDIAN__) 109 // Mac check 110 #error DataPack assumes little endian 111 #endif 112 113 DataPackEntry* target = reinterpret_cast<DataPackEntry*>( 114 bsearch(&resource_id, mmap_->data() + kHeaderLength, resource_count_, 115 sizeof(DataPackEntry), DataPackEntry::CompareById)); 116 if (!target) { 117 return false; 118 } 119 120 data->set(mmap_->data() + target->file_offset, target->length); 121 return true; 122 } 123 124 RefCountedStaticMemory* DataPack::GetStaticMemory(uint32 resource_id) { 125 base::StringPiece piece; 126 if (!GetStringPiece(resource_id, &piece)) 127 return NULL; 128 129 return new RefCountedStaticMemory( 130 reinterpret_cast<const unsigned char*>(piece.data()), piece.length()); 131 } 132 133 // static 134 bool DataPack::WritePack(const FilePath& path, 135 const std::map<uint32, StringPiece>& resources) { 136 FILE* file = file_util::OpenFile(path, "wb"); 137 if (!file) 138 return false; 139 140 if (fwrite(&kFileFormatVersion, 1, kWord, file) != kWord) { 141 LOG(ERROR) << "Failed to write file version"; 142 file_util::CloseFile(file); 143 return false; 144 } 145 146 // Note: the python version of this function explicitly sorted keys, but 147 // std::map is a sorted associative container, we shouldn't have to do that. 148 uint32 entry_count = resources.size(); 149 if (fwrite(&entry_count, 1, kWord, file) != kWord) { 150 LOG(ERROR) << "Failed to write entry count"; 151 file_util::CloseFile(file); 152 return false; 153 } 154 155 // Each entry is 3 uint32s. 156 uint32 index_length = entry_count * 3 * kWord; 157 uint32 data_offset = kHeaderLength + index_length; 158 for (std::map<uint32, StringPiece>::const_iterator it = resources.begin(); 159 it != resources.end(); ++it) { 160 if (fwrite(&it->first, 1, kWord, file) != kWord) { 161 LOG(ERROR) << "Failed to write id for " << it->first; 162 file_util::CloseFile(file); 163 return false; 164 } 165 166 if (fwrite(&data_offset, 1, kWord, file) != kWord) { 167 LOG(ERROR) << "Failed to write offset for " << it->first; 168 file_util::CloseFile(file); 169 return false; 170 } 171 172 uint32 len = it->second.length(); 173 if (fwrite(&len, 1, kWord, file) != kWord) { 174 LOG(ERROR) << "Failed to write length for " << it->first; 175 file_util::CloseFile(file); 176 return false; 177 } 178 179 data_offset += len; 180 } 181 182 for (std::map<uint32, StringPiece>::const_iterator it = resources.begin(); 183 it != resources.end(); ++it) { 184 if (fwrite(it->second.data(), it->second.length(), 1, file) != 1) { 185 LOG(ERROR) << "Failed to write data for " << it->first; 186 file_util::CloseFile(file); 187 return false; 188 } 189 } 190 191 file_util::CloseFile(file); 192 193 return true; 194 } 195 196 } // namespace base 197