blob: 3a4a03a77185f6224943dd7cb73e70b0d1e8e080 [file] [log] [blame]
Kenny Root7cee34a2010-06-01 10:34:29 -07001/*
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 goto out;
95 }
96 success = readFrom(fd);
97 close(fd);
98
99out:
100 if (!success) {
101 LOGW("failed to read from %s\n", filename);
102 }
103 return success;
104}
105
106bool ObbFile::readFrom(int fd)
107{
108 if (fd < 0) {
109 LOGW("failed to read file\n");
110 return false;
111 }
112
113 return parseObbFile(fd);
114}
115
116bool ObbFile::parseObbFile(int fd)
117{
118 my_off64_t fileLength = my_lseek64(fd, 0, SEEK_END);
119
120 if (fileLength < kFooterMinSize) {
121 if (fileLength < 0) {
122 LOGW("error seeking in ObbFile: %s\n", strerror(errno));
123 } else {
124 LOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize);
125 }
126 return false;
127 }
128
129 ssize_t actual;
130 size_t footerSize;
131
132 {
133 my_lseek64(fd, fileLength - kFooterTagSize, SEEK_SET);
134
135 char *footer = new char[kFooterTagSize];
136 actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize));
137 if (actual != kFooterTagSize) {
138 LOGW("couldn't read footer signature: %s\n", strerror(errno));
139 return false;
140 }
141
142 unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t));
143 if (fileSig != kSignature) {
144 LOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n",
145 kSignature, fileSig);
146 return false;
147 }
148
149 footerSize = get4LE((unsigned char*)footer);
150 if (footerSize > (size_t)fileLength - kFooterTagSize
151 || footerSize > kMaxBufSize) {
152 LOGW("claimed footer size is too large (0x%08lx; file size is 0x%08llx)\n",
153 footerSize, fileLength);
154 return false;
155 }
156 }
157
158 my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize;
159 if (my_lseek64(fd, fileOffset, SEEK_SET) != fileOffset) {
160 LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno));
161 return false;
162 }
163
164 size_t readAmount = kMaxBufSize;
165 if (readAmount > footerSize)
166 readAmount = footerSize;
167
168 char* scanBuf = (char*)malloc(readAmount);
169 if (scanBuf == NULL) {
170 LOGW("couldn't allocate scanBuf: %s\n", strerror(errno));
171 return false;
172 }
173
174 actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount));
175 // readAmount is guaranteed to be less than kMaxBufSize
176 if (actual != (ssize_t)readAmount) {
177 LOGI("couldn't read ObbFile footer: %s\n", strerror(errno));
178 free(scanBuf);
179 return false;
180 }
181
182#ifdef DEBUG
183 for (int i = 0; i < readAmount; ++i) {
184 LOGI("char: 0x%02x", scanBuf[i]);
185 }
186#endif
187
188 uint32_t sigVersion = get4LE((unsigned char*)scanBuf);
189 if (sigVersion != kSigVersion) {
190 LOGW("Unsupported ObbFile version %d\n", sigVersion);
191 free(scanBuf);
192 return false;
193 }
194
195 mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset);
196
197 uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset);
198 if (packageNameLen <= 0
199 || packageNameLen > (footerSize - kPackageNameOffset)) {
200 LOGW("bad ObbFile package name length (0x%08x)\n", packageNameLen);
201 free(scanBuf);
202 return false;
203 }
204
205 char* packageName = reinterpret_cast<char*>(scanBuf + kPackageNameOffset);
206 mPackageName = String8(const_cast<char*>(packageName), packageNameLen);
207
208 free(scanBuf);
209 return true;
210}
211
212bool ObbFile::writeTo(const char* filename)
213{
214 int fd;
215 bool success = false;
216
217 fd = ::open(filename, O_WRONLY);
218 if (fd < 0) {
219 goto out;
220 }
221 success = writeTo(fd);
222 close(fd);
223
224out:
225 if (!success) {
226 LOGW("failed to write to %s: %s\n", filename, strerror(errno));
227 }
228 return success;
229}
230
231bool ObbFile::writeTo(int fd)
232{
233 if (fd < 0) {
234 return false;
235 }
236
237 if (mPackageName.size() == 0 || mVersion == -1) {
238 LOGW("tried to write uninitialized ObbFile data");
239 return false;
240 }
241
242 unsigned char intBuf[sizeof(uint32_t)+1];
243 memset(&intBuf, 0, sizeof(intBuf));
244
245 put4LE(intBuf, kSigVersion);
246 if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
247 LOGW("couldn't write signature version: %s", strerror(errno));
248 return false;
249 }
250
251 put4LE(intBuf, mVersion);
252 if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
253 LOGW("couldn't write package version");
254 return false;
255 }
256
257 size_t packageNameLen = mPackageName.size();
258 put4LE(intBuf, packageNameLen);
259 if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
260 LOGW("couldn't write package name length: %s", strerror(errno));
261 return false;
262 }
263
264 if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) {
265 LOGW("couldn't write package name: %s", strerror(errno));
266 return false;
267 }
268
269 put4LE(intBuf, 3*sizeof(uint32_t) + packageNameLen);
270 if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
271 LOGW("couldn't write footer size: %s", strerror(errno));
272 return false;
273 }
274
275 put4LE(intBuf, kSignature);
276 if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) {
277 LOGW("couldn't write footer magic signature: %s", strerror(errno));
278 return false;
279 }
280
281 return true;
282}
283
284}