diff options
author | Mathias Agopian <mathias@google.com> | 2012-02-20 16:58:20 -0800 |
---|---|---|
committer | Mathias Agopian <mathias@google.com> | 2012-02-20 22:38:43 -0800 |
commit | 83c64e6b624a876436d2ef5d2f173b10407e27b4 (patch) | |
tree | c4effc3260ab399e8fdae23baf06b076cd87a604 /libs/androidfw/ObbFile.cpp | |
parent | 94d0024557f415b8463f085c9784a3e1d02b58c6 (diff) |
frameworks/base refactoring
create the new libandroidfw from parts of libui and libutils
Change-Id: I1584995616fff5d527a2aba63921b682a6194d58
Diffstat (limited to 'libs/androidfw/ObbFile.cpp')
-rw-r--r-- | libs/androidfw/ObbFile.cpp | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/libs/androidfw/ObbFile.cpp b/libs/androidfw/ObbFile.cpp new file mode 100644 index 000000000000..21e06c8db5cd --- /dev/null +++ b/libs/androidfw/ObbFile.cpp @@ -0,0 +1,345 @@ +/* + * 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; +} + +} |