| /* | |
| * 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", (long long int)fileLength, kFooterMinSize); | |
| } | |
| return false; | |
| } | |
| ssize_t actual; | |
| size_t footerSize; | |
| { | |
| lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); | |
| char footer[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%08lld)\n", | |
| footerSize, (long long int)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", (long long int)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.c_str(), 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.c_str(), 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; | |
| } | |
| if (ftruncate(fd, mFooterStart) == -1) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| } |