/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <errno.h> #include <fcntl.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define LOG_TAG "ObbFile" #include <androidfw/ObbFile.h> #include <utils/Compat.h> #include <utils/Log.h> //#define DEBUG 1 #define kFooterTagSize 8 /* last two 32-bit integers */ #define kFooterMinSize 33 /* 32-bit signature version (4 bytes) * 32-bit package version (4 bytes) * 32-bit flags (4 bytes) * 64-bit salt (8 bytes) * 32-bit package name size (4 bytes) * >=1-character package name (1 byte) * 32-bit footer size (4 bytes) * 32-bit footer marker (4 bytes) */ #define kMaxBufSize 32768 /* Maximum file read buffer */ #define kSignature 0x01059983U /* ObbFile signature */ #define kSigVersion 1 /* We only know about signature version 1 */ /* offsets in version 1 of the header */ #define kPackageVersionOffset 4 #define kFlagsOffset 8 #define kSaltOffset 12 #define kPackageNameLenOffset 20 #define kPackageNameOffset 24 /* * TEMP_FAILURE_RETRY is defined by some, but not all, versions of * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's * not already defined, then define it here. */ #ifndef TEMP_FAILURE_RETRY /* Used to retry syscalls that can return EINTR. */ #define TEMP_FAILURE_RETRY(exp) ({ \ typeof (exp) _rc; \ do { \ _rc = (exp); \ } while (_rc == -1 && errno == EINTR); \ _rc; }) #endif namespace android { ObbFile::ObbFile() : mPackageName("") , mVersion(-1) , mFlags(0) { memset(mSalt, 0, sizeof(mSalt)); } ObbFile::~ObbFile() { } bool ObbFile::readFrom(const char* filename) { int fd; bool success = false; fd = ::open(filename, O_RDONLY); if (fd < 0) { ALOGW("couldn't open file %s: %s", filename, strerror(errno)); goto out; } success = readFrom(fd); close(fd); if (!success) { ALOGW("failed to read from %s (fd=%d)\n", filename, fd); } out: return success; } bool ObbFile::readFrom(int fd) { if (fd < 0) { ALOGW("attempt to read from invalid fd\n"); return false; } return parseObbFile(fd); } bool ObbFile::parseObbFile(int fd) { off64_t fileLength = lseek64(fd, 0, SEEK_END); if (fileLength < kFooterMinSize) { if (fileLength < 0) { ALOGW("error seeking in ObbFile: %s\n", strerror(errno)); } else { ALOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize); } return false; } ssize_t actual; size_t footerSize; { lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); char *footer = new char[kFooterTagSize]; actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); if (actual != kFooterTagSize) { ALOGW("couldn't read footer signature: %s\n", strerror(errno)); return false; } unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t)); if (fileSig != kSignature) { ALOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n", kSignature, fileSig); return false; } footerSize = get4LE((unsigned char*)footer); if (footerSize > (size_t)fileLength - kFooterTagSize || footerSize > kMaxBufSize) { ALOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n", footerSize, fileLength); return false; } if (footerSize < (kFooterMinSize - kFooterTagSize)) { ALOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n", footerSize, kFooterMinSize - kFooterTagSize); return false; } } off64_t fileOffset = fileLength - footerSize - kFooterTagSize; if (lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { ALOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); return false; } mFooterStart = fileOffset; char* scanBuf = (char*)malloc(footerSize); if (scanBuf == NULL) { ALOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); return false; } actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, footerSize)); // readAmount is guaranteed to be less than kMaxBufSize if (actual != (ssize_t)footerSize) { ALOGI("couldn't read ObbFile footer: %s\n", strerror(errno)); free(scanBuf); return false; } #ifdef DEBUG for (int i = 0; i < footerSize; ++i) { ALOGI("char: 0x%02x\n", scanBuf[i]); } #endif uint32_t sigVersion = get4LE((unsigned char*)scanBuf); if (sigVersion != kSigVersion) { ALOGW("Unsupported ObbFile version %d\n", sigVersion); free(scanBuf); return false; } mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset); mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset); memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt)); size_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); if (packageNameLen == 0 || packageNameLen > (footerSize - kPackageNameOffset)) { ALOGW("bad ObbFile package name length (0x%04zx; 0x%04zx possible)\n", packageNameLen, footerSize - kPackageNameOffset); free(scanBuf); return false; } char* packageName = reinterpret_cast<char*>(scanBuf + kPackageNameOffset); mPackageName = String8(const_cast<char*>(packageName), packageNameLen); free(scanBuf); #ifdef DEBUG ALOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion); #endif return true; } bool ObbFile::writeTo(const char* filename) { int fd; bool success = false; fd = ::open(filename, O_WRONLY); if (fd < 0) { goto out; } success = writeTo(fd); close(fd); out: if (!success) { ALOGW("failed to write to %s: %s\n", filename, strerror(errno)); } return success; } bool ObbFile::writeTo(int fd) { if (fd < 0) { return false; } lseek64(fd, 0, SEEK_END); if (mPackageName.size() == 0 || mVersion == -1) { ALOGW("tried to write uninitialized ObbFile data\n"); return false; } unsigned char intBuf[sizeof(uint32_t)+1]; memset(&intBuf, 0, sizeof(intBuf)); put4LE(intBuf, kSigVersion); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { ALOGW("couldn't write signature version: %s\n", strerror(errno)); return false; } put4LE(intBuf, mVersion); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { ALOGW("couldn't write package version\n"); return false; } put4LE(intBuf, mFlags); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { ALOGW("couldn't write package version\n"); return false; } if (write(fd, mSalt, sizeof(mSalt)) != (ssize_t)sizeof(mSalt)) { ALOGW("couldn't write salt: %s\n", strerror(errno)); return false; } size_t packageNameLen = mPackageName.size(); put4LE(intBuf, packageNameLen); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { ALOGW("couldn't write package name length: %s\n", strerror(errno)); return false; } if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) { ALOGW("couldn't write package name: %s\n", strerror(errno)); return false; } put4LE(intBuf, kPackageNameOffset + packageNameLen); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { ALOGW("couldn't write footer size: %s\n", strerror(errno)); return false; } put4LE(intBuf, kSignature); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { ALOGW("couldn't write footer magic signature: %s\n", strerror(errno)); return false; } return true; } bool ObbFile::removeFrom(const char* filename) { int fd; bool success = false; fd = ::open(filename, O_RDWR); if (fd < 0) { goto out; } success = removeFrom(fd); close(fd); out: if (!success) { ALOGW("failed to remove signature from %s: %s\n", filename, strerror(errno)); } return success; } bool ObbFile::removeFrom(int fd) { if (fd < 0) { return false; } if (!readFrom(fd)) { return false; } ftruncate(fd, mFooterStart); return true; } }