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