248 lines
5.2 KiB
C
248 lines
5.2 KiB
C
/*
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
|
|
#include <zlib.h> // for adler32()
|
|
|
|
static int verbose = 0;
|
|
|
|
/*
|
|
* Android File Archive format:
|
|
*
|
|
* magic[5]: 'A' 'F' 'A' 'R' '\n'
|
|
* version[4]: 0x00 0x00 0x00 0x01
|
|
* for each file:
|
|
* file magic[4]: 'F' 'I' 'L' 'E'
|
|
* namelen[4]: Length of file name, including NUL byte (big-endian)
|
|
* name[*]: NUL-terminated file name
|
|
* datalen[4]: Length of file (big-endian)
|
|
* data[*]: Unencoded file data
|
|
* adler32[4]: adler32 of the unencoded file data (big-endian)
|
|
* file end magic[4]: 'f' 'i' 'l' 'e'
|
|
* end magic[4]: 'E' 'N' 'D' 0x00
|
|
*
|
|
* This format is about as simple as possible; it was designed to
|
|
* make it easier to transfer multiple files over an stdin/stdout
|
|
* pipe to another process, so word-alignment wasn't necessary.
|
|
*/
|
|
|
|
static void
|
|
die(const char *why, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, why);
|
|
fprintf(stderr, "error: ");
|
|
vfprintf(stderr, why, ap);
|
|
fprintf(stderr, "\n");
|
|
va_end(ap);
|
|
exit(1);
|
|
}
|
|
|
|
static void
|
|
write_big_endian(size_t v)
|
|
{
|
|
putchar((v >> 24) & 0xff);
|
|
putchar((v >> 16) & 0xff);
|
|
putchar((v >> 8) & 0xff);
|
|
putchar( v & 0xff);
|
|
}
|
|
|
|
static void
|
|
_eject(struct stat *s, char *out, int olen, char *data, size_t datasize)
|
|
{
|
|
unsigned long adler;
|
|
|
|
/* File magic.
|
|
*/
|
|
printf("FILE");
|
|
|
|
/* Name length includes the NUL byte.
|
|
*/
|
|
write_big_endian(olen + 1);
|
|
|
|
/* File name and terminating NUL.
|
|
*/
|
|
printf("%s", out);
|
|
putchar('\0');
|
|
|
|
/* File length.
|
|
*/
|
|
write_big_endian(datasize);
|
|
|
|
/* File data.
|
|
*/
|
|
if (fwrite(data, 1, datasize, stdout) != datasize) {
|
|
die("Error writing file data");
|
|
}
|
|
|
|
/* Checksum.
|
|
*/
|
|
adler = adler32(0, NULL, 0);
|
|
adler = adler32(adler, (unsigned char *)data, datasize);
|
|
write_big_endian(adler);
|
|
|
|
/* File end magic.
|
|
*/
|
|
printf("file");
|
|
}
|
|
|
|
static void _archive(char *in, int ilen);
|
|
|
|
static void
|
|
_archive_dir(char *in, int ilen)
|
|
{
|
|
int t;
|
|
DIR *d;
|
|
struct dirent *de;
|
|
|
|
if (verbose) {
|
|
fprintf(stderr, "_archive_dir('%s', %d)\n", in, ilen);
|
|
}
|
|
|
|
d = opendir(in);
|
|
if (d == 0) {
|
|
die("cannot open directory '%s'", in);
|
|
}
|
|
|
|
while ((de = readdir(d)) != 0) {
|
|
/* xxx: feature? maybe some dotfiles are okay */
|
|
if (strcmp(de->d_name, ".") == 0 ||
|
|
strcmp(de->d_name, "..") == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
t = strlen(de->d_name);
|
|
in[ilen] = '/';
|
|
memcpy(in + ilen + 1, de->d_name, t + 1);
|
|
|
|
_archive(in, ilen + t + 1);
|
|
|
|
in[ilen] = '\0';
|
|
}
|
|
}
|
|
|
|
static void
|
|
_archive(char *in, int ilen)
|
|
{
|
|
struct stat s;
|
|
|
|
if (verbose) {
|
|
fprintf(stderr, "_archive('%s', %d)\n", in, ilen);
|
|
}
|
|
|
|
if (lstat(in, &s)) {
|
|
die("could not stat '%s'\n", in);
|
|
}
|
|
|
|
if (S_ISREG(s.st_mode)) {
|
|
char *tmp;
|
|
int fd;
|
|
|
|
fd = open(in, O_RDONLY);
|
|
if (fd < 0) {
|
|
die("cannot open '%s' for read", in);
|
|
}
|
|
|
|
tmp = (char*) malloc(s.st_size);
|
|
if (tmp == 0) {
|
|
die("cannot allocate %d bytes", s.st_size);
|
|
}
|
|
|
|
if (read(fd, tmp, s.st_size) != s.st_size) {
|
|
die("cannot read %d bytes", s.st_size);
|
|
}
|
|
|
|
_eject(&s, in, ilen, tmp, s.st_size);
|
|
|
|
free(tmp);
|
|
close(fd);
|
|
} else if (S_ISDIR(s.st_mode)) {
|
|
_archive_dir(in, ilen);
|
|
} else {
|
|
/* We don't handle links, etc. */
|
|
die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
|
|
}
|
|
}
|
|
|
|
void archive(const char *start)
|
|
{
|
|
char in[8192];
|
|
|
|
strcpy(in, start);
|
|
|
|
_archive_dir(in, strlen(in));
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
struct termios old_termios;
|
|
|
|
if (argc == 1) {
|
|
die("usage: %s <dir-list>", argv[0]);
|
|
}
|
|
argc--;
|
|
argv++;
|
|
|
|
/* Force stdout into raw mode.
|
|
*/
|
|
struct termios s;
|
|
if (tcgetattr(1, &s) < 0) {
|
|
die("Could not get termios for stdout");
|
|
}
|
|
old_termios = s;
|
|
cfmakeraw(&s);
|
|
if (tcsetattr(1, TCSANOW, &s) < 0) {
|
|
die("Could not set termios for stdout");
|
|
}
|
|
|
|
/* Print format magic and version.
|
|
*/
|
|
printf("AFAR\n");
|
|
write_big_endian(1);
|
|
|
|
while (argc-- > 0) {
|
|
archive(*argv++);
|
|
}
|
|
|
|
/* Print end magic.
|
|
*/
|
|
printf("END");
|
|
putchar('\0');
|
|
|
|
/* Restore stdout.
|
|
*/
|
|
if (tcsetattr(1, TCSANOW, &old_termios) < 0) {
|
|
die("Could not restore termios for stdout");
|
|
}
|
|
|
|
return 0;
|
|
}
|