blob: d66d59c8cdc904ec6fd22418ef2c75cf2f7b570d [file] [log] [blame]
The Android Open Source Project88b60792009-03-03 19:28:42 -08001/*
2 * Copyright (C) 2008 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 <stdio.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <string.h>
21
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <dirent.h>
25
26#include <stdarg.h>
27#include <fcntl.h>
28#include <termios.h>
29
30#include <zlib.h> // for adler32()
31
32static int verbose = 0;
33
34/*
35 * Android File Archive format:
36 *
37 * magic[5]: 'A' 'F' 'A' 'R' '\n'
38 * version[4]: 0x00 0x00 0x00 0x01
39 * for each file:
40 * file magic[4]: 'F' 'I' 'L' 'E'
41 * namelen[4]: Length of file name, including NUL byte (big-endian)
42 * name[*]: NUL-terminated file name
43 * datalen[4]: Length of file (big-endian)
44 * data[*]: Unencoded file data
45 * adler32[4]: adler32 of the unencoded file data (big-endian)
46 * file end magic[4]: 'f' 'i' 'l' 'e'
47 * end magic[4]: 'E' 'N' 'D' 0x00
48 *
49 * This format is about as simple as possible; it was designed to
50 * make it easier to transfer multiple files over an stdin/stdout
51 * pipe to another process, so word-alignment wasn't necessary.
52 */
53
54static void
55die(const char *why, ...)
56{
57 va_list ap;
58
59 va_start(ap, why);
60 fprintf(stderr, "error: ");
61 vfprintf(stderr, why, ap);
62 fprintf(stderr, "\n");
63 va_end(ap);
64 exit(1);
65}
66
67static void
68write_big_endian(size_t v)
69{
70 putchar((v >> 24) & 0xff);
71 putchar((v >> 16) & 0xff);
72 putchar((v >> 8) & 0xff);
73 putchar( v & 0xff);
74}
75
76static void
77_eject(struct stat *s, char *out, int olen, char *data, size_t datasize)
78{
79 unsigned long adler;
80
81 /* File magic.
82 */
83 printf("FILE");
84
85 /* Name length includes the NUL byte.
86 */
87 write_big_endian(olen + 1);
88
89 /* File name and terminating NUL.
90 */
91 printf("%s", out);
92 putchar('\0');
93
94 /* File length.
95 */
96 write_big_endian(datasize);
97
98 /* File data.
99 */
100 if (fwrite(data, 1, datasize, stdout) != datasize) {
101 die("Error writing file data");
102 }
103
104 /* Checksum.
105 */
106 adler = adler32(0, NULL, 0);
107 adler = adler32(adler, (unsigned char *)data, datasize);
108 write_big_endian(adler);
109
110 /* File end magic.
111 */
112 printf("file");
113}
114
115static void _archive(char *in, int ilen);
116
117static void
118_archive_dir(char *in, int ilen)
119{
120 int t;
121 DIR *d;
122 struct dirent *de;
123
124 if (verbose) {
125 fprintf(stderr, "_archive_dir('%s', %d)\n", in, ilen);
126 }
127
128 d = opendir(in);
129 if (d == 0) {
130 die("cannot open directory '%s'", in);
131 }
132
133 while ((de = readdir(d)) != 0) {
134 /* xxx: feature? maybe some dotfiles are okay */
135 if (strcmp(de->d_name, ".") == 0 ||
136 strcmp(de->d_name, "..") == 0)
137 {
138 continue;
139 }
140
141 t = strlen(de->d_name);
142 in[ilen] = '/';
143 memcpy(in + ilen + 1, de->d_name, t + 1);
144
145 _archive(in, ilen + t + 1);
146
147 in[ilen] = '\0';
148 }
149}
150
151static void
152_archive(char *in, int ilen)
153{
154 struct stat s;
155
156 if (verbose) {
157 fprintf(stderr, "_archive('%s', %d)\n", in, ilen);
158 }
159
160 if (lstat(in, &s)) {
161 die("could not stat '%s'\n", in);
162 }
163
164 if (S_ISREG(s.st_mode)) {
165 char *tmp;
166 int fd;
167
168 fd = open(in, O_RDONLY);
169 if (fd < 0) {
170 die("cannot open '%s' for read", in);
171 }
172
173 tmp = (char*) malloc(s.st_size);
174 if (tmp == 0) {
175 die("cannot allocate %d bytes", s.st_size);
176 }
177
178 if (read(fd, tmp, s.st_size) != s.st_size) {
179 die("cannot read %d bytes", s.st_size);
180 }
181
182 _eject(&s, in, ilen, tmp, s.st_size);
183
184 free(tmp);
185 close(fd);
186 } else if (S_ISDIR(s.st_mode)) {
187 _archive_dir(in, ilen);
188 } else {
189 /* We don't handle links, etc. */
190 die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
191 }
192}
193
194void archive(const char *start)
195{
196 char in[8192];
197
198 strcpy(in, start);
199
200 _archive_dir(in, strlen(in));
201}
202
203int
204main(int argc, char *argv[])
205{
206 struct termios old_termios;
207
208 if (argc == 1) {
209 die("usage: %s <dir-list>", argv[0]);
210 }
211 argc--;
212 argv++;
213
214 /* Force stdout into raw mode.
215 */
216 struct termios s;
217 if (tcgetattr(1, &s) < 0) {
218 die("Could not get termios for stdout");
219 }
220 old_termios = s;
221 cfmakeraw(&s);
222 if (tcsetattr(1, TCSANOW, &s) < 0) {
223 die("Could not set termios for stdout");
224 }
225
226 /* Print format magic and version.
227 */
228 printf("AFAR\n");
229 write_big_endian(1);
230
231 while (argc-- > 0) {
232 archive(*argv++);
233 }
234
235 /* Print end magic.
236 */
237 printf("END");
238 putchar('\0');
239
240 /* Restore stdout.
241 */
242 if (tcsetattr(1, TCSANOW, &old_termios) < 0) {
243 die("Could not restore termios for stdout");
244 }
245
246 return 0;
247}