/* * 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; }