ObbFile.cpp revision 6e7ac5f0bceddf51947fbf3b376e278df0735603
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <errno.h> 18#include <fcntl.h> 19#include <stdint.h> 20#include <stdlib.h> 21#include <string.h> 22#include <unistd.h> 23 24#define LOG_TAG "ObbFile" 25#include <utils/Log.h> 26#include <utils/ObbFile.h> 27 28//#define DEBUG 1 29 30#define kFooterTagSize 8 /* last two 32-bit integers */ 31 32#define kFooterMinSize 21 /* 32-bit signature version 33 * 32-bit package version 34 * 32-bit package name size 35 * 1-character package name 36 * 32-bit footer size 37 * 32-bit footer marker 38 */ 39 40#define kMaxBufSize 32768 /* Maximum file read buffer */ 41 42#define kSignature 0x01059983U /* ObbFile signature */ 43 44#define kSigVersion 1 /* We only know about signature version 1 */ 45 46/* offsets in version 1 of the header */ 47#define kPackageVersionOffset 4 48#define kPackageNameLenOffset 8 49#define kPackageNameOffset 12 50 51/* 52 * TEMP_FAILURE_RETRY is defined by some, but not all, versions of 53 * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's 54 * not already defined, then define it here. 55 */ 56#ifndef TEMP_FAILURE_RETRY 57/* Used to retry syscalls that can return EINTR. */ 58#define TEMP_FAILURE_RETRY(exp) ({ \ 59 typeof (exp) _rc; \ 60 do { \ 61 _rc = (exp); \ 62 } while (_rc == -1 && errno == EINTR); \ 63 _rc; }) 64#endif 65 66/* 67 * Work around situations where off_t is 64-bit and use off64_t in 68 * situations where it's 32-bit. 69 */ 70#ifdef OFF_T_IS_64_BIT 71#define my_lseek64 lseek 72typedef off_t my_off64_t; 73#else 74#define my_lseek64 lseek64 75typedef off64_t my_off64_t; 76#endif 77 78namespace android { 79 80ObbFile::ObbFile() : 81 mVersion(-1) { 82} 83 84ObbFile::~ObbFile() { 85} 86 87bool ObbFile::readFrom(const char* filename) 88{ 89 int fd; 90 bool success = false; 91 92 fd = ::open(filename, O_RDONLY); 93 if (fd < 0) { 94 LOGW("couldn't open file %s: %s", filename, strerror(errno)); 95 goto out; 96 } 97 success = readFrom(fd); 98 close(fd); 99 100 if (!success) { 101 LOGW("failed to read from %s (fd=%d)\n", filename, fd); 102 } 103 104out: 105 return success; 106} 107 108bool ObbFile::readFrom(int fd) 109{ 110 if (fd < 0) { 111 LOGW("attempt to read from invalid fd\n"); 112 return false; 113 } 114 115 return parseObbFile(fd); 116} 117 118bool ObbFile::parseObbFile(int fd) 119{ 120 my_off64_t fileLength = my_lseek64(fd, 0, SEEK_END); 121 122 if (fileLength < kFooterMinSize) { 123 if (fileLength < 0) { 124 LOGW("error seeking in ObbFile: %s\n", strerror(errno)); 125 } else { 126 LOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize); 127 } 128 return false; 129 } 130 131 ssize_t actual; 132 size_t footerSize; 133 134 { 135 my_lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); 136 137 char *footer = new char[kFooterTagSize]; 138 actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); 139 if (actual != kFooterTagSize) { 140 LOGW("couldn't read footer signature: %s\n", strerror(errno)); 141 return false; 142 } 143 144 unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t)); 145 if (fileSig != kSignature) { 146 LOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n", 147 kSignature, fileSig); 148 return false; 149 } 150 151 footerSize = get4LE((unsigned char*)footer); 152 if (footerSize > (size_t)fileLength - kFooterTagSize 153 || footerSize > kMaxBufSize) { 154 LOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n", 155 footerSize, fileLength); 156 return false; 157 } 158 159 if (footerSize < (kFooterMinSize - kFooterTagSize)) { 160 LOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n", 161 footerSize, kFooterMinSize - kFooterTagSize); 162 return false; 163 } 164 } 165 166 my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize; 167 if (my_lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { 168 LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); 169 return false; 170 } 171 172 mFooterStart = fileOffset; 173 174 char* scanBuf = (char*)malloc(footerSize); 175 if (scanBuf == NULL) { 176 LOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); 177 return false; 178 } 179 180 actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, footerSize)); 181 // readAmount is guaranteed to be less than kMaxBufSize 182 if (actual != (ssize_t)footerSize) { 183 LOGI("couldn't read ObbFile footer: %s\n", strerror(errno)); 184 free(scanBuf); 185 return false; 186 } 187 188#ifdef DEBUG 189 for (int i = 0; i < footerSize; ++i) { 190 LOGI("char: 0x%02x", scanBuf[i]); 191 } 192#endif 193 194 uint32_t sigVersion = get4LE((unsigned char*)scanBuf); 195 if (sigVersion != kSigVersion) { 196 LOGW("Unsupported ObbFile version %d\n", sigVersion); 197 free(scanBuf); 198 return false; 199 } 200 201 mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset); 202 203 uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); 204 if (packageNameLen <= 0 205 || packageNameLen > (footerSize - kPackageNameOffset)) { 206 LOGW("bad ObbFile package name length (0x%04x; 0x%04x possible)\n", 207 packageNameLen, footerSize - kPackageNameOffset); 208 free(scanBuf); 209 return false; 210 } 211 212 char* packageName = reinterpret_cast<char*>(scanBuf + kPackageNameOffset); 213 mPackageName = String8(const_cast<char*>(packageName), packageNameLen); 214 215 free(scanBuf); 216 217#ifdef DEBUG 218 LOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion); 219#endif 220 221 return true; 222} 223 224bool ObbFile::writeTo(const char* filename) 225{ 226 int fd; 227 bool success = false; 228 229 fd = ::open(filename, O_WRONLY); 230 if (fd < 0) { 231 goto out; 232 } 233 success = writeTo(fd); 234 close(fd); 235 236out: 237 if (!success) { 238 LOGW("failed to write to %s: %s\n", filename, strerror(errno)); 239 } 240 return success; 241} 242 243bool ObbFile::writeTo(int fd) 244{ 245 if (fd < 0) { 246 return false; 247 } 248 249 my_lseek64(fd, 0, SEEK_END); 250 251 if (mPackageName.size() == 0 || mVersion == -1) { 252 LOGW("tried to write uninitialized ObbFile data"); 253 return false; 254 } 255 256 unsigned char intBuf[sizeof(uint32_t)+1]; 257 memset(&intBuf, 0, sizeof(intBuf)); 258 259 put4LE(intBuf, kSigVersion); 260 if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { 261 LOGW("couldn't write signature version: %s", strerror(errno)); 262 return false; 263 } 264 265 put4LE(intBuf, mVersion); 266 if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { 267 LOGW("couldn't write package version"); 268 return false; 269 } 270 271 size_t packageNameLen = mPackageName.size(); 272 put4LE(intBuf, packageNameLen); 273 if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { 274 LOGW("couldn't write package name length: %s", strerror(errno)); 275 return false; 276 } 277 278 if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) { 279 LOGW("couldn't write package name: %s", strerror(errno)); 280 return false; 281 } 282 283 put4LE(intBuf, 3*sizeof(uint32_t) + packageNameLen); 284 if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { 285 LOGW("couldn't write footer size: %s", strerror(errno)); 286 return false; 287 } 288 289 put4LE(intBuf, kSignature); 290 if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { 291 LOGW("couldn't write footer magic signature: %s", strerror(errno)); 292 return false; 293 } 294 295 return true; 296} 297 298bool ObbFile::removeFrom(const char* filename) 299{ 300 int fd; 301 bool success = false; 302 303 fd = ::open(filename, O_RDWR); 304 if (fd < 0) { 305 goto out; 306 } 307 success = removeFrom(fd); 308 close(fd); 309 310out: 311 if (!success) { 312 LOGW("failed to remove signature from %s: %s\n", filename, strerror(errno)); 313 } 314 return success; 315} 316 317bool ObbFile::removeFrom(int fd) 318{ 319 if (fd < 0) { 320 return false; 321 } 322 323 if (!readFrom(fd)) { 324 return false; 325 } 326 327 ftruncate(fd, mFooterStart); 328 329 return true; 330} 331 332} 333