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 #include <ctype.h>
      7 
      8 #include <sys/types.h>
      9 #include <sys/stat.h>
     10 #include <dirent.h>
     11 
     12 #include <stdarg.h>
     13 #include <fcntl.h>
     14 
     15 #include <private/android_filesystem_config.h>
     16 
     17 /* NOTES
     18 **
     19 ** - see buffer-format.txt from the linux kernel docs for
     20 **   an explanation of this file format
     21 ** - dotfiles are ignored
     22 ** - directories named 'root' are ignored
     23 ** - device notes, pipes, etc are not supported (error)
     24 */
     25 
     26 void die(const char *why, ...)
     27 {
     28     va_list ap;
     29 
     30     va_start(ap, why);
     31     fprintf(stderr,"error: ");
     32     vfprintf(stderr, why, ap);
     33     fprintf(stderr,"\n");
     34     va_end(ap);
     35     exit(1);
     36 }
     37 
     38 struct fs_config_entry {
     39     char* name;
     40     int uid, gid, mode;
     41 };
     42 
     43 static struct fs_config_entry* canned_config = NULL;
     44 
     45 /* Each line in the canned file should be a path plus three ints (uid,
     46  * gid, mode). */
     47 #ifdef PATH_MAX
     48 #define CANNED_LINE_LENGTH  (PATH_MAX+100)
     49 #else
     50 #define CANNED_LINE_LENGTH  (1024)
     51 #endif
     52 
     53 static int verbose = 0;
     54 static int total_size = 0;
     55 
     56 static void fix_stat(const char *path, struct stat *s)
     57 {
     58     if (canned_config) {
     59         // Use the list of file uid/gid/modes loaded from the file
     60         // given with -f.
     61 
     62         struct fs_config_entry* empty_path_config = NULL;
     63         struct fs_config_entry* p;
     64         for (p = canned_config; p->name; ++p) {
     65             if (!p->name[0]) {
     66                 empty_path_config = p;
     67             }
     68             if (strcmp(p->name, path) == 0) {
     69                 s->st_uid = p->uid;
     70                 s->st_gid = p->gid;
     71                 s->st_mode = p->mode | (s->st_mode & ~07777);
     72                 return;
     73             }
     74         }
     75         s->st_uid = empty_path_config->uid;
     76         s->st_gid = empty_path_config->gid;
     77         s->st_mode = empty_path_config->mode | (s->st_mode & ~07777);
     78     } else {
     79         // Use the compiled-in fs_config() function.
     80 
     81         fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &s->st_mode);
     82     }
     83 }
     84 
     85 static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize)
     86 {
     87     // Nothing is special about this value, just picked something in the
     88     // approximate range that was being used already, and avoiding small
     89     // values which may be special.
     90     static unsigned next_inode = 300000;
     91 
     92     while(total_size & 3) {
     93         total_size++;
     94         putchar(0);
     95     }
     96 
     97     fix_stat(out, s);
     98 //    fprintf(stderr, "_eject %s: mode=0%o\n", out, s->st_mode);
     99 
    100     printf("%06x%08x%08x%08x%08x%08x%08x"
    101            "%08x%08x%08x%08x%08x%08x%08x%s%c",
    102            0x070701,
    103            next_inode++,  //  s.st_ino,
    104            s->st_mode,
    105            0, // s.st_uid,
    106            0, // s.st_gid,
    107            1, // s.st_nlink,
    108            0, // s.st_mtime,
    109            datasize,
    110            0, // volmajor
    111            0, // volminor
    112            0, // devmajor
    113            0, // devminor,
    114            olen + 1,
    115            0,
    116            out,
    117            0
    118            );
    119 
    120     total_size += 6 + 8*13 + olen + 1;
    121 
    122     if(strlen(out) != (unsigned int)olen) die("ACK!");
    123 
    124     while(total_size & 3) {
    125         total_size++;
    126         putchar(0);
    127     }
    128 
    129     if(datasize) {
    130         fwrite(data, datasize, 1, stdout);
    131         total_size += datasize;
    132     }
    133 }
    134 
    135 static void _eject_trailer()
    136 {
    137     struct stat s;
    138     memset(&s, 0, sizeof(s));
    139     _eject(&s, "TRAILER!!!", 10, 0, 0);
    140 
    141     while(total_size & 0xff) {
    142         total_size++;
    143         putchar(0);
    144     }
    145 }
    146 
    147 static void _archive(char *in, char *out, int ilen, int olen);
    148 
    149 static int compare(const void* a, const void* b) {
    150   return strcmp(*(const char**)a, *(const char**)b);
    151 }
    152 
    153 static void _archive_dir(char *in, char *out, int ilen, int olen)
    154 {
    155     int i, t;
    156     DIR *d;
    157     struct dirent *de;
    158 
    159     if(verbose) {
    160         fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n",
    161                 in, out, ilen, olen);
    162     }
    163 
    164     d = opendir(in);
    165     if(d == 0) die("cannot open directory '%s'", in);
    166 
    167     int size = 32;
    168     int entries = 0;
    169     char** names = malloc(size * sizeof(char*));
    170     if (names == NULL) {
    171       fprintf(stderr, "failed to allocate dir names array (size %d)\n", size);
    172       exit(1);
    173     }
    174 
    175     while((de = readdir(d)) != 0){
    176             /* xxx: feature? maybe some dotfiles are okay */
    177         if(de->d_name[0] == '.') continue;
    178 
    179             /* xxx: hack. use a real exclude list */
    180         if(!strcmp(de->d_name, "root")) continue;
    181 
    182         if (entries >= size) {
    183           size *= 2;
    184           names = realloc(names, size * sizeof(char*));
    185           if (names == NULL) {
    186             fprintf(stderr, "failed to reallocate dir names array (size %d)\n",
    187                     size);
    188             exit(1);
    189           }
    190         }
    191         names[entries] = strdup(de->d_name);
    192         if (names[entries] == NULL) {
    193           fprintf(stderr, "failed to strdup name \"%s\"\n",
    194                   de->d_name);
    195           exit(1);
    196         }
    197         ++entries;
    198     }
    199 
    200     qsort(names, entries, sizeof(char*), compare);
    201 
    202     for (i = 0; i < entries; ++i) {
    203         t = strlen(names[i]);
    204         in[ilen] = '/';
    205         memcpy(in + ilen + 1, names[i], t + 1);
    206 
    207         if(olen > 0) {
    208             out[olen] = '/';
    209             memcpy(out + olen + 1, names[i], t + 1);
    210             _archive(in, out, ilen + t + 1, olen + t + 1);
    211         } else {
    212             memcpy(out, names[i], t + 1);
    213             _archive(in, out, ilen + t + 1, t);
    214         }
    215 
    216         in[ilen] = 0;
    217         out[olen] = 0;
    218 
    219         free(names[i]);
    220     }
    221     free(names);
    222 }
    223 
    224 static void _archive(char *in, char *out, int ilen, int olen)
    225 {
    226     struct stat s;
    227 
    228     if(verbose) {
    229         fprintf(stderr,"_archive('%s','%s',%d,%d)\n",
    230                 in, out, ilen, olen);
    231     }
    232 
    233     if(lstat(in, &s)) die("could not stat '%s'\n", in);
    234 
    235     if(S_ISREG(s.st_mode)){
    236         char *tmp;
    237         int fd;
    238 
    239         fd = open(in, O_RDONLY);
    240         if(fd < 0) die("cannot open '%s' for read", in);
    241 
    242         tmp = (char*) malloc(s.st_size);
    243         if(tmp == 0) die("cannot allocate %d bytes", s.st_size);
    244 
    245         if(read(fd, tmp, s.st_size) != s.st_size) {
    246             die("cannot read %d bytes", s.st_size);
    247         }
    248 
    249         _eject(&s, out, olen, tmp, s.st_size);
    250 
    251         free(tmp);
    252         close(fd);
    253     } else if(S_ISDIR(s.st_mode)) {
    254         _eject(&s, out, olen, 0, 0);
    255         _archive_dir(in, out, ilen, olen);
    256     } else if(S_ISLNK(s.st_mode)) {
    257         char buf[1024];
    258         int size;
    259         size = readlink(in, buf, 1024);
    260         if(size < 0) die("cannot read symlink '%s'", in);
    261         _eject(&s, out, olen, buf, size);
    262     } else {
    263         die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
    264     }
    265 }
    266 
    267 void archive(const char *start, const char *prefix)
    268 {
    269     char in[8192];
    270     char out[8192];
    271 
    272     strcpy(in, start);
    273     strcpy(out, prefix);
    274 
    275     _archive_dir(in, out, strlen(in), strlen(out));
    276 }
    277 
    278 static void read_canned_config(char* filename)
    279 {
    280     int allocated = 8;
    281     int used = 0;
    282 
    283     canned_config =
    284         (struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry));
    285 
    286     char line[CANNED_LINE_LENGTH];
    287     FILE* f = fopen(filename, "r");
    288     if (f == NULL) die("failed to open canned file");
    289 
    290     while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) {
    291         if (!line[0]) break;
    292         if (used >= allocated) {
    293             allocated *= 2;
    294             canned_config = (struct fs_config_entry*)realloc(
    295                 canned_config, allocated * sizeof(struct fs_config_entry));
    296         }
    297 
    298         struct fs_config_entry* cc = canned_config + used;
    299 
    300         if (isspace(line[0])) {
    301             cc->name = strdup("");
    302             cc->uid = atoi(strtok(line, " \n"));
    303         } else {
    304             cc->name = strdup(strtok(line, " \n"));
    305             cc->uid = atoi(strtok(NULL, " \n"));
    306         }
    307         cc->gid = atoi(strtok(NULL, " \n"));
    308         cc->mode = strtol(strtok(NULL, " \n"), NULL, 8);
    309         ++used;
    310     }
    311     if (used >= allocated) {
    312         ++allocated;
    313         canned_config = (struct fs_config_entry*)realloc(
    314             canned_config, allocated * sizeof(struct fs_config_entry));
    315     }
    316     canned_config[used].name = NULL;
    317 
    318     fclose(f);
    319 }
    320 
    321 
    322 int main(int argc, char *argv[])
    323 {
    324     argc--;
    325     argv++;
    326 
    327     if (argc > 1 && strcmp(argv[0], "-f") == 0) {
    328         read_canned_config(argv[1]);
    329         argc -= 2;
    330         argv += 2;
    331     }
    332 
    333     if(argc == 0) die("no directories to process?!");
    334 
    335     while(argc-- > 0){
    336         char *x = strchr(*argv, '=');
    337         if(x != 0) {
    338             *x++ = 0;
    339         } else {
    340             x = "";
    341         }
    342 
    343         archive(*argv, x);
    344 
    345         argv++;
    346     }
    347 
    348     _eject_trailer();
    349 
    350     return 0;
    351 }
    352