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