Home | History | Annotate | Download | only in cpio
      1 
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <unistd.h>
      5 #include <string.h>
      6 
      7 #include <sys/types.h>
      8 #include <sys/stat.h>
      9 #include <dirent.h>
     10 
     11 #include <stdarg.h>
     12 #include <fcntl.h>
     13 
     14 #include <private/android_filesystem_config.h>
     15 
     16 /* NOTES
     17 **
     18 ** - see buffer-format.txt from the linux kernel docs for
     19 **   an explanation of this file format
     20 ** - dotfiles are ignored
     21 ** - directories named 'root' are ignored
     22 ** - device notes, pipes, etc are not supported (error)
     23 */
     24 
     25 void die(const char *why, ...)
     26 {
     27     va_list ap;
     28 
     29     va_start(ap, why);
     30     fprintf(stderr,"error: ");
     31     vfprintf(stderr, why, ap);
     32     fprintf(stderr,"\n");
     33     va_end(ap);
     34     exit(1);
     35 }
     36 
     37 static int verbose = 0;
     38 static int total_size = 0;
     39 
     40 static void fix_stat(const char *path, struct stat *s)
     41 {
     42     fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode);
     43 }
     44 
     45 static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize)
     46 {
     47     // Nothing is special about this value, just picked something in the
     48     // approximate range that was being used already, and avoiding small
     49     // values which may be special.
     50     static unsigned next_inode = 300000;
     51 
     52     while(total_size & 3) {
     53         total_size++;
     54         putchar(0);
     55     }
     56 
     57     fix_stat(out, s);
     58 //    fprintf(stderr, "_eject %s: mode=0%o\n", out, s->st_mode);
     59 
     60     printf("%06x%08x%08x%08x%08x%08x%08x"
     61            "%08x%08x%08x%08x%08x%08x%08x%s%c",
     62            0x070701,
     63            next_inode++,  //  s.st_ino,
     64            s->st_mode,
     65            0, // s.st_uid,
     66            0, // s.st_gid,
     67            1, // s.st_nlink,
     68            0, // s.st_mtime,
     69            datasize,
     70            0, // volmajor
     71            0, // volminor
     72            0, // devmajor
     73            0, // devminor,
     74            olen + 1,
     75            0,
     76            out,
     77            0
     78            );
     79 
     80     total_size += 6 + 8*13 + olen + 1;
     81 
     82     if(strlen(out) != olen) die("ACK!");
     83 
     84     while(total_size & 3) {
     85         total_size++;
     86         putchar(0);
     87     }
     88 
     89     if(datasize) {
     90         fwrite(data, datasize, 1, stdout);
     91         total_size += datasize;
     92     }
     93 }
     94 
     95 static void _eject_trailer()
     96 {
     97     struct stat s;
     98     memset(&s, 0, sizeof(s));
     99     _eject(&s, "TRAILER!!!", 10, 0, 0);
    100 
    101     while(total_size & 0xff) {
    102         total_size++;
    103         putchar(0);
    104     }
    105 }
    106 
    107 static void _archive(char *in, char *out, int ilen, int olen);
    108 
    109 static int compare(const void* a, const void* b) {
    110   return strcmp(*(const char**)a, *(const char**)b);
    111 }
    112 
    113 static void _archive_dir(char *in, char *out, int ilen, int olen)
    114 {
    115     int i, t;
    116     DIR *d;
    117     struct dirent *de;
    118 
    119     if(verbose) {
    120         fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n",
    121                 in, out, ilen, olen);
    122     }
    123 
    124     d = opendir(in);
    125     if(d == 0) die("cannot open directory '%s'", in);
    126 
    127     int size = 32;
    128     int entries = 0;
    129     char** names = malloc(size * sizeof(char*));
    130     if (names == NULL) {
    131       fprintf(stderr, "failed to allocate dir names array (size %d)\n", size);
    132       exit(1);
    133     }
    134 
    135     while((de = readdir(d)) != 0){
    136             /* xxx: feature? maybe some dotfiles are okay */
    137         if(de->d_name[0] == '.') continue;
    138 
    139             /* xxx: hack. use a real exclude list */
    140         if(!strcmp(de->d_name, "root")) continue;
    141 
    142         if (entries >= size) {
    143           size *= 2;
    144           names = realloc(names, size * sizeof(char*));
    145           if (names == NULL) {
    146             fprintf(stderr, "failed to reallocate dir names array (size %d)\n",
    147                     size);
    148             exit(1);
    149           }
    150         }
    151         names[entries] = strdup(de->d_name);
    152         if (names[entries] == NULL) {
    153           fprintf(stderr, "failed to strdup name \"%s\"\n",
    154                   de->d_name);
    155           exit(1);
    156         }
    157         ++entries;
    158     }
    159 
    160     qsort(names, entries, sizeof(char*), compare);
    161 
    162     for (i = 0; i < entries; ++i) {
    163         t = strlen(names[i]);
    164         in[ilen] = '/';
    165         memcpy(in + ilen + 1, names[i], t + 1);
    166 
    167         if(olen > 0) {
    168             out[olen] = '/';
    169             memcpy(out + olen + 1, names[i], t + 1);
    170             _archive(in, out, ilen + t + 1, olen + t + 1);
    171         } else {
    172             memcpy(out, names[i], t + 1);
    173             _archive(in, out, ilen + t + 1, t);
    174         }
    175 
    176         in[ilen] = 0;
    177         out[olen] = 0;
    178 
    179         free(names[i]);
    180     }
    181     free(names);
    182 }
    183 
    184 static void _archive(char *in, char *out, int ilen, int olen)
    185 {
    186     struct stat s;
    187 
    188     if(verbose) {
    189         fprintf(stderr,"_archive('%s','%s',%d,%d)\n",
    190                 in, out, ilen, olen);
    191     }
    192 
    193     if(lstat(in, &s)) die("could not stat '%s'\n", in);
    194 
    195     if(S_ISREG(s.st_mode)){
    196         char *tmp;
    197         int fd;
    198 
    199         fd = open(in, O_RDONLY);
    200         if(fd < 0) die("cannot open '%s' for read", in);
    201 
    202         tmp = (char*) malloc(s.st_size);
    203         if(tmp == 0) die("cannot allocate %d bytes", s.st_size);
    204 
    205         if(read(fd, tmp, s.st_size) != s.st_size) {
    206             die("cannot read %d bytes", s.st_size);
    207         }
    208 
    209         _eject(&s, out, olen, tmp, s.st_size);
    210 
    211         free(tmp);
    212         close(fd);
    213     } else if(S_ISDIR(s.st_mode)) {
    214         _eject(&s, out, olen, 0, 0);
    215         _archive_dir(in, out, ilen, olen);
    216     } else if(S_ISLNK(s.st_mode)) {
    217         char buf[1024];
    218         int size;
    219         size = readlink(in, buf, 1024);
    220         if(size < 0) die("cannot read symlink '%s'", in);
    221         _eject(&s, out, olen, buf, size);
    222     } else {
    223         die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
    224     }
    225 }
    226 
    227 void archive(const char *start, const char *prefix)
    228 {
    229     char in[8192];
    230     char out[8192];
    231 
    232     strcpy(in, start);
    233     strcpy(out, prefix);
    234 
    235     _archive_dir(in, out, strlen(in), strlen(out));
    236 }
    237 
    238 int main(int argc, char *argv[])
    239 {
    240     argc--;
    241     argv++;
    242 
    243     if(argc == 0) die("no directories to process?!");
    244 
    245     while(argc-- > 0){
    246         char *x = strchr(*argv, '=');
    247         if(x != 0) {
    248             *x++ = 0;
    249         } else {
    250             x = "";
    251         }
    252 
    253         archive(*argv, x);
    254 
    255         argv++;
    256     }
    257 
    258     _eject_trailer();
    259 
    260     return 0;
    261 }
    262