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