Home | History | Annotate | Download | only in filesystems
      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