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