1 // Copyright 2014 The Android Open Source Project 2 // 3 // This software is licensed under the terms of the GNU General Public 4 // License version 2, as published by the Free Software Foundation, and 5 // may be copied, distributed, and modified under those terms. 6 // 7 // This program is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 // GNU General Public License for more details. 11 12 #include "android/filesystems/ramdisk_extractor.h" 13 14 #include "android/base/Compiler.h" 15 #include "android/base/Log.h" 16 #include "android/base/String.h" 17 18 #include <inttypes.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <string.h> 22 #include <zlib.h> 23 24 #define DEBUG 0 25 26 #if DEBUG 27 # define D(...) printf(__VA_ARGS__), fflush(stdout) 28 #else 29 # define D(...) ((void)0) 30 #endif 31 32 // Ramdisk images are gzipped cpio archives using the new ASCII 33 // format as described at [1]. Hence this source file first implements 34 // a gzip-based input stream class, then 35 // 36 // [1] http://people.freebsd.org/~kientzle/libarchive/man/cpio.5.txt 37 38 namespace { 39 40 // Helper class used to implement a gzip-based input stream. 41 // Usage is as follows: 42 // 43 // GZipInputStream input(filePath); 44 // if (input.error()) { 45 // fprintf(stderr, "Could not open file: %s\n", 46 // filePath, strerror(input.error())); 47 // } else { 48 // uint8_t header[32]; 49 // if (!doRead(&input, header, sizeof header)) { 50 // ... error, could not read header. 51 // } 52 // } 53 // .. stream is closed automatically on scope exit. 54 class GZipInputStream { 55 public: 56 // Open a new input stream to read from file |filePath|. 57 // The constructor can never fail, so call error() after 58 // this to see if an error occured. 59 explicit GZipInputStream(const char* filePath) { 60 mFile = gzopen(filePath, "rb"); 61 if (mFile == Z_NULL) { 62 mFile = NULL; 63 mError = errno; 64 } else { 65 mError = 0; 66 } 67 } 68 69 // Return the last error that occured on this stream, 70 // or 0 if everything's well. Note that as soon as an 71 // error occurs, the stream cannot be used anymore. 72 int error() const { return mError; } 73 74 // Close the stream, note that this is called automatically 75 // from the destructor, but clients might want to do this 76 // before. 77 void close() { 78 if (mFile) { 79 gzclose(mFile); 80 mFile = NULL; 81 } 82 } 83 84 ~GZipInputStream() { 85 close(); 86 } 87 88 // Try to read up to |len| bytes of data from the input 89 // stream into |buffer|. On success, return true and sets 90 // |*readBytes| to the number of bytes that were read. 91 // On failure, return false and set error(). 92 bool tryRead(void* buffer, size_t len, size_t* readBytes) { 93 *readBytes = 0; 94 95 if (mError) { 96 return false; 97 } 98 99 if (len == 0) { 100 return true; 101 } 102 103 // Warning, gzread() takes an unsigned int parameter for 104 // the length, but will return an error if its value 105 // exceeds INT_MAX anyway. 106 char* buff = reinterpret_cast<char*>(buffer); 107 108 while (len > 0) { 109 size_t avail = len; 110 if (avail > INT_MAX) 111 avail = INT_MAX; 112 113 int ret = gzread(mFile, buff, static_cast<unsigned int>(avail)); 114 if (ret < 0) { 115 gzerror(mFile, &mError); 116 break; 117 } 118 if (ret == 0) { 119 if (gzeof(mFile)) { 120 break; 121 } 122 gzerror(mFile, &mError); 123 break; 124 } 125 len -= ret; 126 buff += ret; 127 *readBytes += ret; 128 } 129 130 return (!mError && *readBytes > 0); 131 } 132 133 // Read exactly |len| bytes of data into |buffer|. On success, 134 // return true, on failure, return false and set error(). 135 bool doRead(void* buffer, size_t len) { 136 size_t readCount = 0; 137 if (!tryRead(buffer, len, &readCount)) { 138 return false; 139 } 140 if (readCount < len) { 141 mError = EIO; 142 return false; 143 } 144 return true; 145 } 146 147 bool doSkip(size_t len) { 148 if (mError) { 149 return false; 150 } 151 if (gzseek(mFile, len, SEEK_CUR) < 0) { 152 gzerror(mFile, &mError); 153 return false; 154 } 155 return true; 156 } 157 158 private: 159 DISALLOW_COPY_AND_ASSIGN(GZipInputStream); 160 161 gzFile mFile; 162 int mError; 163 }; 164 165 // Parse an hexadecimal string of 8 characters. On success, 166 // return true and sets |*value| to its value. On failure, 167 // return false. 168 bool parse_hex8(const char* input, uint32_t* value) { 169 uint32_t result = 0; 170 for (int n = 0; n < 8; ++n) { 171 int c = input[n]; 172 unsigned d = static_cast<unsigned>(c - '0'); 173 if (d >= 10) { 174 d = static_cast<unsigned>(c - 'a'); 175 if (d >= 6) { 176 d = static_cast<unsigned>(c - 'A'); 177 if (d >= 6) { 178 return false; 179 } 180 } 181 d += 10; 182 } 183 result = (result << 4) | d; 184 } 185 *value = result; 186 return true; 187 } 188 189 } // namespace 190 191 bool android_extractRamdiskFile(const char* ramdiskPath, 192 const char* fileName, 193 char** out, 194 size_t* outSize) { 195 *out = NULL; 196 *outSize = 0; 197 198 GZipInputStream input(ramdiskPath); 199 if (input.error()) { 200 errno = input.error(); 201 return false; 202 } 203 204 // Type of cpio new ASCII header. 205 struct cpio_newc_header { 206 char c_magic[6]; 207 char c_ino[8]; 208 char c_mode[8]; 209 char c_uid[8]; 210 char c_gid[8]; 211 char c_nlink[8]; 212 char c_mtime[8]; 213 char c_filesize[8]; 214 char c_devmajor[8]; 215 char c_devminor[8]; 216 char c_rdevmajor[8]; 217 char c_rdevminor[8]; 218 char c_namesize[8]; 219 char c_check[8]; 220 }; 221 222 size_t fileNameLen = strlen(fileName); 223 224 for (;;) { 225 // Read the header then check it. 226 cpio_newc_header header; 227 if (!input.doRead(&header, sizeof header)) { 228 // Assume end of input here. 229 D("Could not find %s in ramdisk image at %s\n", 230 fileName, ramdiskPath); 231 return false; 232 } 233 234 D("HEADER %.6s\n", header.c_magic); 235 if (memcmp(header.c_magic, "070701", 6) != 0) { 236 D("Not a valid ramdisk image file: %s\n", ramdiskPath); 237 errno = EINVAL; 238 return false; 239 } 240 241 // Compare file names, note that files with a size of 0 are 242 // hard links and should be ignored. 243 uint32_t nameSize; 244 uint32_t entrySize; 245 if (!parse_hex8(header.c_namesize, &nameSize) || 246 !parse_hex8(header.c_filesize, &entrySize)) { 247 D("Could not parse ramdisk file entry header!"); 248 break; 249 } 250 251 D("---- %d nameSize=%d entrySize=%d\n", __LINE__, nameSize, entrySize); 252 253 // The header is followed by the name, followed by 4-byte padding 254 // with NUL bytes. Compute the number of bytes to skip over the 255 // name. 256 size_t skipName = 257 ((sizeof header + nameSize + 3) & ~3) - sizeof header; 258 259 // The file data is 4-byte padded with NUL bytes too. 260 size_t skipFile = (entrySize + 3) & ~3; 261 size_t skipCount = 0; 262 263 // Last record is named 'TRAILER!!!' and indicates end of archive. 264 static const char kTrailer[] = "TRAILER!!!"; 265 static const size_t kTrailerSize = sizeof(kTrailer) - 1U; 266 267 if ((entrySize == 0 || nameSize != fileNameLen + 1U) && 268 nameSize != kTrailerSize + 1U) { 269 D("---- %d Skipping\n", __LINE__); 270 skipCount = skipName + skipFile; 271 } else { 272 // Read the name and compare it. 273 nameSize -= 1U; 274 android::base::String entryName; 275 entryName.resize(nameSize); 276 if (!input.doRead(&entryName[0], nameSize)) { 277 D("Could not read ramdisk file entry name!"); 278 break; 279 } 280 D("---- %d Name=[%s]\n", __LINE__, entryName.c_str()); 281 skipCount -= nameSize; 282 283 // Check for last entry. 284 if (nameSize == kTrailerSize && 285 !strcmp(entryName.c_str(), kTrailer)) { 286 D("End of archive reached. Could not find %s in ramdisk image at %s", 287 fileName, ramdiskPath); 288 return false; 289 } 290 291 // Check for the search file name. 292 if (nameSize == entryName.size() && 293 !strcmp(entryName.c_str(), fileName)) { 294 // Found it !! Skip over padding. 295 if (!input.doSkip(skipName - nameSize)) { 296 D("Could not skip ramdisk name entry!"); 297 break; 298 } 299 300 // Then read data into head-allocated buffer. 301 *out = reinterpret_cast<char*>(malloc(entrySize)); 302 *outSize = entrySize; 303 304 return input.doRead(*out, entrySize); 305 } 306 } 307 if (!input.doSkip(skipCount)) { 308 D("Could not skip ramdisk entry!"); 309 break; 310 } 311 } 312 313 errno = input.error(); 314 return false; 315 } 316